inode);
75 | #else
76 | if (inodeflag) fprintf(outfile,",\"inode\":%ld",(long int)ent->inode);
77 | #endif
78 | if (devflag) fprintf(outfile, ",\"dev\":%d", (int)ent->dev);
79 | #ifdef __EMX__
80 | if (pflag) fprintf(outfile, ",\"mode\":\"%04o\",\"prot\":\"%s\"",ent->attr, prot(ent->attr));
81 | #else
82 | 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));
83 | #endif
84 | if (uflag) fprintf(outfile, ",\"user\":\"%s\"", uidtoname(ent->uid));
85 | if (gflag) fprintf(outfile, ",\"group\":\"%s\"", gidtoname(ent->gid));
86 | if (sflag) {
87 | if (hflag || siflag) {
88 | char nbuf[64];
89 | int i;
90 | psize(nbuf,ent->size);
91 | for(i=0; isspace(nbuf[i]); i++); /* trim() hack */
92 | fprintf(outfile, ",\"size\":\"%s\"", nbuf+i);
93 | } else
94 | fprintf(outfile, ",\"size\":%lld", (long long int)ent->size);
95 | }
96 | if (Dflag) fprintf(outfile, ",\"time\":\"%s\"", do_date(cflag? ent->ctime : ent->mtime));
97 | }
98 |
99 |
100 | void json_intro(void)
101 | {
102 | extern char *_nl;
103 | fprintf(outfile, "[%s", noindent? "" : _nl);
104 | }
105 |
106 | void json_outtro(void)
107 | {
108 | extern char *_nl;
109 | fprintf(outfile, "%s]\n", noindent? "" : _nl);
110 | }
111 |
112 | int json_printinfo(char *dirname, struct _info *file, int level)
113 | {
114 | UNUSED(dirname);
115 | mode_t mt;
116 | int t;
117 |
118 | if (!noindent) json_indent(level);
119 |
120 | if (file != NULL) {
121 | if (file->lnk) mt = file->mode & S_IFMT;
122 | else mt = file->mode & S_IFMT;
123 | } else mt = 0;
124 |
125 | for(t=0;ifmt[t];t++)
126 | if (ifmt[t] == mt) break;
127 | fprintf(outfile,"{\"type\":\"%s\"", ftype[t]);
128 |
129 | return 0;
130 | }
131 |
132 | int json_printfile(char *dirname, char *filename, struct _info *file, int descend)
133 | {
134 | UNUSED(dirname);
135 | int i;
136 |
137 | fprintf(outfile, ",\"name\":\"");
138 | json_encode(outfile, filename);
139 | fputc('"',outfile);
140 |
141 | if (file && file->comment) {
142 | fprintf(outfile, ",\"info\":\"");
143 | for(i=0; file->comment[i]; i++) {
144 | json_encode(outfile, file->comment[i]);
145 | if (file->comment[i+1]) fprintf(outfile, "\\n");
146 | }
147 | fprintf(outfile, "\"");
148 | }
149 |
150 | if (file && file->lnk) {
151 | fprintf(outfile, ",\"target\":\"");
152 | json_encode(outfile, file->lnk);
153 | fputc('"',outfile);
154 | }
155 | if (file) json_fillinfo(file);
156 |
157 | // if (file && file->err) fprintf(outfile, ",\"error\": \"%s\"", file->err);
158 | if (descend || (file->isdir && file->err)) fprintf(outfile, ",\"contents\":[");
159 | else fputc('}',outfile);
160 |
161 | return descend || (file->isdir && file->err);
162 | }
163 |
164 | int json_error(char *error)
165 | {
166 | fprintf(outfile,"{\"error\": \"%s\"}%s",error, noindent?"":"");
167 | return 0;
168 | }
169 |
170 | void json_newline(struct _info *file, int level, int postdir, int needcomma)
171 | {
172 | UNUSED(file);UNUSED(level);UNUSED(postdir);
173 | extern char *_nl;
174 |
175 | fprintf(outfile, "%s%s", needcomma? "," : "", _nl);
176 | }
177 |
178 | void json_close(struct _info *file, int level, int needcomma)
179 | {
180 | UNUSED(file);
181 | if (!noindent) json_indent(level);
182 | fprintf(outfile,"]}%s%s", needcomma? ",":"", noindent? "":"\n");
183 | }
184 |
185 | void json_report(struct totals tot)
186 | {
187 | fprintf(outfile, ",%s{\"type\":\"report\"",noindent?"":"\n ");
188 | if (duflag) fprintf(outfile,",\"size\":%lld", (long long int)tot.size);
189 | fprintf(outfile,",\"directories\":%ld", tot.dirs);
190 | if (!dflag) fprintf(outfile,",\"files\":%ld", tot.files);
191 | fprintf(outfile, "}");
192 | }
193 |
--------------------------------------------------------------------------------
/html.c:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 | #include "tree.h"
19 |
20 |
21 | extern bool duflag, dflag, hflag, siflag;
22 | extern bool metafirst, noindent, force_color, nolinks, htmloffset;
23 |
24 | extern char *hversion;
25 | extern char *host, *sp, *title, *Hintro, *Houtro;
26 | extern const char *charset;
27 |
28 | extern FILE *outfile;
29 |
30 | extern const struct linedraw *linedraw;
31 |
32 | size_t htmldirlen = 0;
33 |
34 | char *class(struct _info *info)
35 | {
36 | return
37 | info->isdir ? "DIR" :
38 | info->isexe ? "EXEC" :
39 | info->isfifo ? "FIFO" :
40 | info->issok ? "SOCK" : "NORM";
41 | }
42 |
43 | void html_encode(FILE *fd, char *s)
44 | {
45 | for(;*s;s++) {
46 | switch(*s) {
47 | case '<':
48 | fputs("<",fd);
49 | break;
50 | case '>':
51 | fputs(">",fd);
52 | break;
53 | case '&':
54 | fputs("&",fd);
55 | break;
56 | case '"':
57 | fputs(""",fd);
58 | break;
59 | default:
60 | fputc(*s,fd);
61 | break;
62 | }
63 | }
64 | }
65 |
66 | void url_encode(FILE *fd, char *s)
67 | {
68 | // Removes / from the reserved list:
69 | static const char *reserved = "!#$&'()*+,:;=?@[]";
70 |
71 | for(;*s;s++) {
72 | fprintf(fd, (isprint((u_int)*s) && (strchr(reserved, *s) == NULL))? "%c":"%%%02X", *s);
73 | }
74 | }
75 |
76 | void fcat(const char *filename)
77 | {
78 | FILE *fp;
79 | char buf[PATH_MAX];
80 | size_t n;
81 |
82 | if ((fp = fopen(filename, "r")) == NULL) return;
83 | while((n = fread(buf, sizeof(char), PATH_MAX, fp)) > 0) {
84 | fwrite(buf, sizeof(char), n, outfile);
85 | }
86 | fclose(fp);
87 | }
88 |
89 | void html_intro(void)
90 | {
91 | if (Hintro) fcat(Hintro);
92 | else {
93 | fprintf(outfile,
94 | "\n"
95 | "\n"
96 | "\n"
97 | " \n"
98 | " \n"
99 | " \n"
102 | " %s\n"
103 | " \n"
120 | "\n"
121 | "\n"
122 | "\t%s
\n", title, title);
123 | }
124 | }
125 |
126 | void html_outtro(void)
127 | {
128 | if (Houtro) fcat(Houtro);
129 | else {
130 | fprintf(outfile,"\t
\n");
131 | fprintf(outfile,"\t\n");
132 | fprintf(outfile,hversion,linedraw->copy, linedraw->copy, linedraw->copy, linedraw->copy);
133 | fprintf(outfile,"\t
\n");
134 | fprintf(outfile,"\n");
135 | fprintf(outfile,"\n");
136 | }
137 | }
138 |
139 | void html_print(char *s)
140 | {
141 | int i;
142 | for(i=0; s[i]; i++) {
143 | if (s[i] == ' ') fprintf(outfile,"%s",sp);
144 | else fprintf(outfile,"%c", s[i]);
145 | }
146 | fprintf(outfile,"%s%s", sp, sp);
147 | }
148 |
149 | int html_printinfo(char *dirname, struct _info *file, int level)
150 | {
151 | UNUSED(dirname);
152 |
153 | char info[512];
154 |
155 | fillinfo(info,file);
156 | if (metafirst) {
157 | if (info[0] == '[') {
158 | html_print(info);
159 | fprintf(outfile,"%s%s", sp, sp);
160 | }
161 | if (!noindent) indent(level);
162 | } else {
163 | if (!noindent) indent(level);
164 | if (info[0] == '[') {
165 | html_print(info);
166 | fprintf(outfile,"%s%s", sp, sp);
167 | }
168 | }
169 |
170 | return 0;
171 | }
172 |
173 | /* descend == add 00Tree.html to the link */
174 | int html_printfile(char *dirname, char *filename, struct _info *file, int descend)
175 | {
176 | int i;
177 | /* Switch to using 'a' elements only. Omit href attribute if not a link */
178 | fprintf(outfile,"comment) {
182 | fprintf(outfile," title=\"");
183 | for(i=0; file->comment[i]; i++) {
184 | html_encode(outfile, file->comment[i]);
185 | if (file->comment[i+1]) fprintf(outfile, "\n");
186 | }
187 | fprintf(outfile, "\"");
188 | }
189 |
190 | if (!nolinks) {
191 | fprintf(outfile," href=\"%s",host);
192 | if (dirname != NULL) {
193 | size_t len = strlen(dirname);
194 | size_t off = (len >= htmldirlen? htmldirlen : 0);
195 | url_encode(outfile, dirname + (htmloffset? off : 0));
196 | if (strcmp(dirname, filename) != 0) {
197 | if (dirname[strlen(dirname)-1] != '/') putc('/', outfile);
198 | url_encode(outfile, filename);
199 | }
200 | fprintf(outfile,"%s%s\"",(descend > 1? "/00Tree.html" : ""), (file->isdir && descend < 2?"/":""));
201 | } else {
202 | if (host[strlen(host)-1] != '/') putc('/', outfile);
203 | url_encode(outfile, filename);
204 | fprintf(outfile,"%s\"",(descend > 1? "/00Tree.html" : ""));
205 | }
206 | }
207 | }
208 | fprintf(outfile, ">");
209 |
210 | if (dirname) html_encode(outfile,filename);
211 | else html_encode(outfile, host);
212 |
213 | fprintf(outfile,"");
214 | return 0;
215 | }
216 |
217 | int html_error(char *error)
218 | {
219 | fprintf(outfile, " [%s]", error);
220 | return 0;
221 | }
222 |
223 | void html_newline(struct _info *file, int level, int postdir, int needcomma)
224 | {
225 | UNUSED(file);UNUSED(level);UNUSED(postdir);UNUSED(needcomma);
226 |
227 | fprintf(outfile, "
\n");
228 | }
229 |
230 | void html_close(struct _info *file, int level, int needcomma)
231 | {
232 | UNUSED(level);UNUSED(needcomma);
233 |
234 | fprintf(outfile, "%s>
\n", file->tag);
235 | }
236 |
237 | void html_report(struct totals tot)
238 | {
239 | char buf[256];
240 |
241 | fprintf(outfile,"
\n\n");
242 |
243 | if (duflag) {
244 | psize(buf, tot.size);
245 | fprintf(outfile,"%s%s used in ", buf, hflag || siflag? "" : " bytes");
246 | }
247 | if (dflag)
248 | fprintf(outfile,"%ld director%s\n",tot.dirs,(tot.dirs==1? "y":"ies"));
249 | else
250 | fprintf(outfile,"%ld director%s, %ld file%s\n",tot.dirs,(tot.dirs==1? "y":"ies"),tot.files,(tot.files==1? "":"s"));
251 |
252 | fprintf(outfile, "\n
\n");
253 | }
254 |
--------------------------------------------------------------------------------
/list.c:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 | #include "tree.h"
19 |
20 | extern bool fflag, duflag, lflag, Rflag, Jflag, xdev, noreport, hyperflag, Hflag;
21 |
22 | extern struct _info **(*getfulltree)(char *d, u_long lev, dev_t dev, off_t *size, char **err);
23 | extern int (*topsort)(struct _info **, struct _info **);
24 | extern FILE *outfile;
25 | extern int flimit, *dirs, errors;
26 | extern ssize_t Level;
27 | extern size_t htmldirlen;
28 |
29 | static char errbuf[256];
30 | char realbasepath[PATH_MAX];
31 | size_t dirpathoffset = 0;
32 |
33 | /**
34 | * Maybe TODO: Refactor the listing calls / when they are called. A more thorough
35 | * analysis of the different outputs is required. This all is not as clean as I
36 | * had hoped it to be.
37 | */
38 |
39 | extern struct listingcalls lc;
40 |
41 | void null_intro(void)
42 | {
43 | return;
44 | }
45 |
46 | void null_outtro(void)
47 | {
48 | return;
49 | }
50 |
51 | void null_close(struct _info *file, int level, int needcomma)
52 | {
53 | UNUSED(file);UNUSED(level);UNUSED(needcomma);
54 | }
55 |
56 | void emit_tree(char **dirname, bool needfulltree)
57 | {
58 | struct totals tot = { 0 }, subtotal;
59 | struct ignorefile *ig = NULL;
60 | struct infofile *inf = NULL;
61 | struct _info **dir = NULL, *info = NULL;
62 | char *err;
63 | ssize_t n;
64 | int i, needsclosed;
65 | size_t j;
66 | struct stat st;
67 |
68 | lc.intro();
69 |
70 | for(i=0; dirname[i]; i++) {
71 | if (hyperflag) {
72 | if (realpath(dirname[i], realbasepath) == NULL) { realbasepath[0] = '\0'; dirpathoffset = 0; }
73 | else dirpathoffset = strlen(dirname[i]);
74 | }
75 |
76 | if (fflag) {
77 | j=strlen(dirname[i]);
78 | do {
79 | if (j > 1 && dirname[i][j-1] == '/') dirname[i][--j] = 0;
80 | } while (j > 1 && dirname[i][j-1] == '/');
81 | }
82 | if (Hflag) htmldirlen = strlen(dirname[i]);
83 |
84 | if ((n = lstat(dirname[i],&st)) >= 0) {
85 | saveino(st.st_ino, st.st_dev);
86 | info = stat2info(&st);
87 | info->name = ""; //dirname[i];
88 |
89 | if (needfulltree) {
90 | dir = getfulltree(dirname[i], 0, st.st_dev, &(info->size), &err);
91 | n = err? -1 : 0;
92 | } else {
93 | push_files(dirname[i], &ig, &inf, true);
94 | dir = read_dir(dirname[i], &n, inf != NULL);
95 | }
96 |
97 | lc.printinfo(dirname[i], info, 0);
98 | } else info = NULL;
99 |
100 | needsclosed = lc.printfile(dirname[i], dirname[i], info, (dir != NULL) || (!dir && n));
101 | subtotal = (struct totals){0, 0, 0};
102 |
103 | if (!dir && n) {
104 | lc.error("error opening dir");
105 | lc.newline(info, 0, 0, dirname[i+1] != NULL);
106 | if (!info) errors++;
107 | else subtotal.files++;
108 | } else if (flimit > 0 && n > flimit) {
109 | sprintf(errbuf,"%ld entries exceeds filelimit, not opening dir", n);
110 | lc.error(errbuf);
111 | lc.newline(info, 0, 0, dirname[i+1] != NULL);
112 | subtotal.dirs++;
113 | } else {
114 | lc.newline(info, 0, 0, 0);
115 | if (dir) {
116 | subtotal = listdir(dirname[i], dir, 1, st.st_dev, needfulltree);
117 | subtotal.dirs++;
118 | }
119 | }
120 | if (dir) {
121 | free_dir(dir);
122 | dir = NULL;
123 | }
124 | if (needsclosed) lc.close(info, 0, dirname[i+1] != NULL);
125 |
126 | tot.files += subtotal.files;
127 | tot.dirs += subtotal.dirs;
128 | // Do not bother to accumulate tot.size in listdir.
129 | // This is already done in getfulltree()
130 | if (duflag) tot.size += info? info->size : 0;
131 |
132 | if (ig != NULL) ig = pop_filterstack();
133 | if (inf != NULL) inf = pop_infostack();
134 | }
135 |
136 | if (!noreport) lc.report(tot);
137 |
138 | lc.outtro();
139 | }
140 |
141 | struct totals listdir(char *dirname, struct _info **dir, int lev, dev_t dev, bool hasfulltree)
142 | {
143 | struct totals tot = {0}, subtotal;
144 | struct ignorefile *ig = NULL;
145 | struct infofile *inf = NULL;
146 | struct _info **subdir = NULL;
147 | size_t namemax = 257, namelen;
148 | int descend, htmldescend = 0;
149 | int needsclosed;
150 | ssize_t n;
151 | size_t dirlen = strlen(dirname)+2, pathlen = dirlen + 257;
152 | bool found;
153 | char *path, *newpath, *filename, *err = NULL;
154 |
155 | int es = (dirname[strlen(dirname) - 1] == '/');
156 |
157 | // Sanity check on dir, may or may not be necessary when using --fromfile:
158 | if (dir == NULL || *dir == NULL) return tot;
159 |
160 | for(n=0; dir[n]; n++);
161 | if (topsort) qsort(dir, (size_t)n, sizeof(struct _info *), (int (*)(const void *, const void *))topsort);
162 |
163 | dirs[lev] = *(dir+1)? 1 : 2;
164 |
165 | path = xmalloc(sizeof(char) * pathlen);
166 |
167 | for (;*dir != NULL; dir++) {
168 | lc.printinfo(dirname, *dir, lev);
169 |
170 | namelen = strlen((*dir)->name) + 1;
171 | if (namemax < namelen)
172 | path = xrealloc(path, dirlen + (namemax = namelen));
173 | if (es) sprintf(path,"%s%s",dirname,(*dir)->name);
174 | else sprintf(path,"%s/%s",dirname,(*dir)->name);
175 | if (fflag) filename = path;
176 | else filename = (*dir)->name;
177 |
178 | descend = 0;
179 | err = NULL;
180 | newpath = path;
181 |
182 | if ((*dir)->isdir) {
183 | tot.dirs++;
184 |
185 | if (!hasfulltree) {
186 | found = findino((*dir)->inode,(*dir)->dev);
187 | if (!found) {
188 | saveino((*dir)->inode, (*dir)->dev);
189 | }
190 | } else found = false;
191 |
192 | if (!(xdev && dev != (*dir)->dev) && (!(*dir)->lnk || ((*dir)->lnk && lflag))) {
193 | descend = 1;
194 |
195 | if ((*dir)->lnk) {
196 | if (*(*dir)->lnk == '/') newpath = (*dir)->lnk;
197 | else {
198 | if (fflag && !strcmp(dirname,"/")) sprintf(path,"%s%s",dirname,(*dir)->lnk);
199 | else sprintf(path,"%s/%s",dirname,(*dir)->lnk);
200 | }
201 | if (found) {
202 | err = "recursive, not followed";
203 | /* Not actually a problem if we weren't going to descend anyway: */
204 | if (Level >= 0 && lev > Level) err = NULL;
205 | descend = -1;
206 | }
207 | }
208 |
209 | if ((Level >= 0) && (lev > Level)) {
210 | if (Rflag) {
211 | FILE *outsave = outfile;
212 | char *paths[2] = {newpath, NULL}, *output = xmalloc(strlen(newpath) + 13);
213 | int *dirsave = xmalloc(sizeof(int) * (size_t)(lev + 2));
214 |
215 | memcpy(dirsave, dirs, sizeof(int) * (size_t)(lev+1));
216 | sprintf(output, "%s/00Tree.html", newpath);
217 | setoutput(output);
218 | emit_tree(paths, hasfulltree);
219 |
220 | free(output);
221 | fclose(outfile);
222 | outfile = outsave;
223 |
224 | memcpy(dirs, dirsave, sizeof(int) * (size_t)(lev+1));
225 | free(dirsave);
226 | htmldescend = 10;
227 | } else htmldescend = 0;
228 | descend = 0;
229 | }
230 |
231 | if (descend > 0) {
232 | if (hasfulltree) {
233 | subdir = (*dir)->child;
234 | err = (*dir)->err;
235 | } else {
236 | push_files(newpath, &ig, &inf, false);
237 | subdir = read_dir(newpath, &n, inf != NULL);
238 | if (!subdir && n) {
239 | err = "error opening dir";
240 | errors++;
241 | } if (flimit > 0 && n > flimit) {
242 | sprintf(err = errbuf,"%ld entries exceeds filelimit, not opening dir", n);
243 | errors++;
244 | free_dir(subdir);
245 | subdir = NULL;
246 | }
247 | }
248 | if (subdir == NULL) descend = 0;
249 | }
250 | }
251 | } else tot.files++;
252 |
253 | needsclosed = lc.printfile(dirname, filename, *dir, descend + htmldescend + (Jflag && errors));
254 | if (err) lc.error(err);
255 |
256 | if (descend > 0) {
257 | lc.newline(*dir, lev, 0, 0);
258 |
259 | subtotal = listdir(newpath, subdir, lev+1, dev, hasfulltree);
260 | tot.dirs += subtotal.dirs;
261 | tot.files += subtotal.files;
262 | } else if (!needsclosed) lc.newline(*dir, lev, 0, *(dir+1)!=NULL);
263 |
264 | if (subdir) {
265 | free_dir(subdir);
266 | subdir = NULL;
267 | }
268 | if (needsclosed) lc.close(*dir, descend? lev : -1, *(dir+1)!=NULL);
269 |
270 | if (*(dir+1) && !*(dir+2)) dirs[lev] = 2;
271 |
272 | if (ig != NULL) ig = pop_filterstack();
273 | if (inf != NULL) inf = pop_infostack();
274 | }
275 |
276 | dirs[lev] = 0;
277 | free(path);
278 | return tot;
279 | }
280 |
--------------------------------------------------------------------------------
/file.c:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 | #include "tree.h"
19 |
20 | extern bool dflag, aflag, pruneflag, gitignore, showinfo;
21 | extern bool matchdirs, fflinks;
22 | extern int pattern, ipattern;
23 |
24 | extern int (*topsort)(const struct _info **, const struct _info **);
25 | extern FILE *outfile;
26 |
27 | extern char *file_comment, *file_pathsep;
28 |
29 | /* 64K paths maximum */
30 | #define MAXPATH 64*1024
31 |
32 | enum ftok { T_PATHSEP, T_DIR, T_FILE, T_EOP };
33 |
34 | char *nextpc(char **p, int *tok)
35 | {
36 | static char prev = 0;
37 | char *s = *p;
38 | if (!**p) {
39 | *tok = T_EOP; /* Shouldn't happen. */
40 | return NULL;
41 | }
42 | if (prev) {
43 | prev = 0;
44 | *tok = T_PATHSEP;
45 | return NULL;
46 | }
47 | if (strchr(file_pathsep, **p) != NULL) {
48 | (*p)++;
49 | *tok = T_PATHSEP;
50 | return NULL;
51 | }
52 | while(**p && strchr(file_pathsep,**p) == NULL) (*p)++;
53 |
54 | if (**p) {
55 | *tok = T_DIR;
56 | prev = **p;
57 | *(*p)++ = '\0';
58 | } else *tok = T_FILE;
59 | return s;
60 | }
61 |
62 | struct _info *newent(const char *name) {
63 | struct _info *n = xmalloc(sizeof(struct _info));
64 | memset(n,0,sizeof(struct _info));
65 | n->name = scopy(name);
66 | n->child = NULL;
67 | n->tchild = n->next = NULL;
68 | return n;
69 | }
70 |
71 | /* Don't insertion sort, let fprune() do the sort if necessary */
72 | struct _info *search(struct _info **dir, const char *name)
73 | {
74 | struct _info *ptr, *prev, *n;
75 | int cmp;
76 |
77 | if (*dir == NULL) return (*dir = newent(name));
78 |
79 | for(prev = ptr = *dir; ptr != NULL; ptr=ptr->next) {
80 | cmp = strcmp(ptr->name,name);
81 | if (cmp == 0) return ptr;
82 | prev = ptr;
83 | }
84 | n = newent(name);
85 | n->next = ptr;
86 | if (prev == ptr) *dir = n;
87 | else prev->next = n;
88 | return n;
89 | }
90 |
91 | void freefiletree(struct _info *ent)
92 | {
93 | struct _info *ptr = ent, *t;
94 |
95 | while (ptr != NULL) {
96 | if (ptr->tchild) freefiletree(ptr->tchild);
97 | t = ptr;
98 | ptr = ptr->next;
99 | free(t);
100 | }
101 | }
102 |
103 | /**
104 | * Recursively prune (unset show flag) files/directories of matches/ignored
105 | * patterns:
106 | */
107 | struct _info **fprune(struct _info *head, const char *path, bool matched, bool root)
108 | {
109 | struct _info **dir, *new = NULL, *end = NULL, *ent, *t;
110 | struct comment *com;
111 | struct ignorefile *ig = NULL;
112 | struct infofile *inf = NULL;
113 | char *cur, *fpath = xmalloc(sizeof(char) * MAXPATH);
114 | size_t i, count = 0;
115 | bool show;
116 |
117 | strcpy(fpath, path);
118 | cur = fpath + strlen(fpath);
119 | *(cur++) = '/';
120 |
121 | push_files(path, &ig, &inf, root);
122 |
123 | for(ent = head; ent != NULL;) {
124 | strcpy(cur, ent->name);
125 | if (ent->tchild) ent->isdir = 1;
126 |
127 | show = true;
128 | if (dflag && !ent->isdir) show = false;
129 | if (!aflag && !root && ent->name[0] == '.') show = false;
130 | if (show && !matched) {
131 | if (!ent->isdir) {
132 | if (pattern && !patinclude(ent->name, 0)) show = false;
133 | if (ipattern && patignore(ent->name, 0)) show = false;
134 | }
135 | if (ent->isdir && show && matchdirs && pattern) {
136 | if (patinclude(ent->name, 1)) matched = true;
137 | }
138 | }
139 | if (pruneflag && !matched && ent->isdir && ent->tchild == NULL) show = false;
140 | if (gitignore && filtercheck(path, ent->name, ent->isdir)) show = false;
141 | if (show && showinfo && (com = infocheck(path, ent->name, inf != NULL, ent->isdir))) {
142 | for(i = 0; com->desc[i] != NULL; i++);
143 | ent->comment = xmalloc(sizeof(char *) * (i+1));
144 | for(i = 0; com->desc[i] != NULL; i++) ent->comment[i] = scopy(com->desc[i]);
145 | ent->comment[i] = NULL;
146 | }
147 | if (show && ent->tchild != NULL) ent->child = fprune(ent->tchild, fpath, matched, false);
148 |
149 |
150 | t = ent;
151 | ent = ent->next;
152 | if (show) {
153 | if (end) end = end->next = t;
154 | else new = end = t;
155 | count++;
156 | } else {
157 | t->next = NULL;
158 | freefiletree(t);
159 | }
160 | }
161 | if (end) end->next = NULL;
162 |
163 | dir = xmalloc(sizeof(struct _info *) * (count+1));
164 | for(count = 0, ent = new; ent != NULL; ent = ent->next, count++) {
165 | dir[count] = ent;
166 | }
167 | dir[count] = NULL;
168 |
169 | if (topsort) qsort(dir,count,sizeof(struct _info *), (int (*)(const void *, const void *))topsort);
170 |
171 | if (ig != NULL) ig = pop_filterstack();
172 | if (inf != NULL) inf = pop_infostack();
173 | free(fpath);
174 |
175 | return dir;
176 | }
177 |
178 | struct _info **file_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err)
179 | {
180 | UNUSED(lev);UNUSED(dev);UNUSED(err);
181 | FILE *fp = (strcmp(d,".")? fopen(d,"r") : stdin);
182 | char *path, *spath, *s, *link;
183 | struct _info *root = NULL, **cwd, *ent;
184 | int tok;
185 | size_t l;
186 |
187 | *size = 0;
188 | if (fp == NULL) {
189 | fprintf(stderr,"tree: Error opening %s for reading.\n", d);
190 | return NULL;
191 | }
192 | path = xmalloc(sizeof(char) * MAXPATH);
193 |
194 | while(fgets(path, MAXPATH, fp) != NULL) {
195 | if (file_comment != NULL && strncmp(path,file_comment,strlen(file_comment)) == 0) continue;
196 | l = strlen(path);
197 | while(l && (path[l-1] == '\n' || path[l-1] == '\r')) path[--l] = '\0';
198 | if (l == 0) continue;
199 |
200 | spath = path;
201 | cwd = &root;
202 |
203 | link = fflinks? strstr(path, " -> ") : NULL;
204 | if (link) {
205 | *link = '\0';
206 | link += 4;
207 | }
208 | ent = NULL;
209 | do {
210 | s = nextpc(&spath, &tok);
211 | switch(tok) {
212 | case T_PATHSEP: continue;
213 | case T_FILE:
214 | case T_DIR:
215 | /* Should probably handle '.' and '..' entries here */
216 | ent = search(cwd, s);
217 | /* Might be empty, but should definitely be considered a directory: */
218 | if (tok == T_DIR) {
219 | ent->isdir = 1;
220 | ent->mode = S_IFDIR;
221 | } else {
222 | ent->mode = S_IFREG;
223 | }
224 |
225 | cwd = &(ent->tchild);
226 | break;
227 | }
228 | } while (tok != T_FILE && tok != T_EOP);
229 |
230 | if (ent && link) {
231 | ent->isdir = 0;
232 | ent->mode = S_IFLNK;
233 | ent->lnk = scopy(link);
234 | }
235 | }
236 | if (fp != stdin) fclose(fp);
237 |
238 | free(path);
239 |
240 | /* Prune accumulated directory tree: */
241 | return fprune(root, "", false, true);
242 | }
243 |
244 | struct _info **tabedfile_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err)
245 | {
246 | UNUSED(lev);UNUSED(dev);UNUSED(err);
247 | FILE *fp = (strcmp(d,".")? fopen(d,"r") : stdin);
248 | char *path, *spath, *link;
249 | struct _info *root = NULL, **istack, *ent;
250 | size_t line = 0, l, tabs, top = 0, maxstack = 2048;
251 |
252 | *size = 0;
253 | if (fp == NULL) {
254 | fprintf(stderr,"tree: Error opening %s for reading.\n", d);
255 | return NULL;
256 | }
257 | path = xmalloc(sizeof(char) * MAXPATH);
258 | istack = xmalloc(sizeof(struct _info *) * maxstack);
259 | memset(istack, 0, sizeof(struct _info *) * maxstack);
260 |
261 | while(fgets(path, MAXPATH, fp) != NULL) {
262 | line++;
263 | if (file_comment != NULL && strncmp(path,file_comment,strlen(file_comment)) == 0) continue;
264 | l = strlen(path);
265 | while(l && (path[l-1] == '\n' || path[l-1] == '\r')) path[--l] = '\0';
266 | if (l == 0) continue;
267 |
268 | for(tabs=0; path[tabs] == '\t'; tabs++);
269 | if (tabs >= maxstack) {
270 | fprintf(stderr, "tree: Tab depth exceeds maximum path depth (%ld >= %ld) on line %ld\n", tabs, maxstack, line);
271 | continue;
272 | }
273 |
274 | spath = path+tabs;
275 |
276 | link = fflinks? strstr(spath, " -> ") : NULL;
277 | if (link) {
278 | *link = '\0';
279 | link += 4;
280 | }
281 | if (tabs > 0 && ((tabs-1 > top) || (istack[tabs-1] == NULL))) {
282 | fprintf(stderr, "tree: Orphaned file [%s] on line %ld, check tab depth in file.\n", spath, line);
283 | continue;
284 | }
285 | ent = istack[tabs] = search(tabs? &(istack[tabs-1]->tchild) : &root, spath);
286 | ent->mode = S_IFREG;
287 | if (tabs) {
288 | istack[tabs-1]->isdir = 1;
289 | istack[tabs-1]->mode = S_IFDIR;
290 | }
291 | if (link) {
292 | ent->isdir = 0;
293 | ent->mode = S_IFLNK;
294 | ent->lnk = scopy(link);
295 | }
296 | top = tabs;
297 | }
298 | if (fp != stdin) fclose(fp);
299 |
300 | free(path);
301 | free(istack);
302 |
303 | /* Prune accumulated directory tree: */
304 | return fprune(root, "", false, true);
305 | }
306 |
--------------------------------------------------------------------------------
/tree.h:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 |
19 | #define _GNU_SOURCE
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #ifdef __EMX__ /* for OS/2 systems */
36 | # define INCL_DOSFILEMGR
37 | # define INCL_DOSNLS
38 | # include
39 | # include
40 | # include
41 | /* On many systems stat() function is identical to lstat() function.
42 | * But the OS/2 does not support symbolic links and doesn't have lstat() function.
43 | */
44 | # define lstat stat
45 | # define strcasecmp stricmp
46 | /* Following two functions, getcwd() and chdir() don't support for drive letters.
47 | * To implement support them, use _getcwd2() and _chdir2().
48 | */
49 | # define getcwd _getcwd2
50 | # define chdir _chdir2
51 | #endif
52 |
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 |
59 | #ifdef __ANDROID
60 | #define mbstowcs(w,m,x) mbsrtowcs(w,(const char**)(& #m),x,NULL)
61 | #endif
62 |
63 | /* Start using PATH_MAX instead of the magic number 4096 everywhere. */
64 | #ifndef PATH_MAX
65 | #define PATH_MAX 4096
66 | #endif
67 |
68 | #ifndef INFO_PATH
69 | #define INFO_PATH "/usr/share/finfo/global_info"
70 | #endif
71 |
72 | #ifdef __linux__
73 | #include
74 | # define ENV_STDDATA_FD "STDDATA_FD"
75 | # ifndef STDDATA_FILENO
76 | # define STDDATA_FILENO 3
77 | # endif
78 | #endif
79 |
80 | /* Should probably use strdup(), but we like our xmalloc() */
81 | #define scopy(x) strcpy(xmalloc(strlen(x)+1),(x))
82 | #define MINIT 30 /* number of dir entries to initially allocate */
83 | #define MINC 20 /* allocation increment */
84 |
85 | #define UNUSED(x) ((void)x)
86 |
87 | struct _info {
88 | char *name;
89 | char *lnk;
90 | bool isdir;
91 | bool issok;
92 | bool isfifo;
93 | bool isexe;
94 | bool orphan;
95 | mode_t mode, lnkmode;
96 | uid_t uid;
97 | gid_t gid;
98 | off_t size;
99 | time_t atime, ctime, mtime;
100 | dev_t dev, ldev;
101 | ino_t inode, linode;
102 | #ifdef __EMX__
103 | long attr;
104 | #endif
105 | char *err;
106 | const char *tag;
107 | char **comment;
108 | struct _info **child, *next, *tchild;
109 | };
110 |
111 | /* list.c */
112 | struct totals {
113 | size_t files, dirs;
114 | off_t size;
115 | };
116 |
117 | struct listingcalls {
118 | void (*intro)(void);
119 | void (*outtro)(void);
120 | int (*printinfo)(char *dirname, struct _info *file, int level);
121 | int (*printfile)(char *dirname, char *filename, struct _info *file, int descend);
122 | int (*error)(char *error);
123 | void (*newline)(struct _info *file, int level, int postdir, int needcomma);
124 | void (*close)(struct _info *file, int level, int needcomma);
125 | void (*report)(struct totals tot);
126 | };
127 |
128 |
129 | /* hash.c */
130 | struct xtable {
131 | unsigned int xid;
132 | char *name;
133 | struct xtable *nxt;
134 | };
135 | struct inotable {
136 | ino_t inode;
137 | dev_t device;
138 | struct inotable *nxt;
139 | };
140 |
141 | /* color.c */
142 | struct extensions {
143 | char *ext;
144 | char *term_flg;
145 | //char *CSS_name, *web_fg, *web_bg, *web_extattr;
146 | struct extensions *nxt;
147 | };
148 | struct linedraw {
149 | const char **name, *vert, *vert_left, *corner, *copy;
150 | const char *ctop, *cbot, *cmid, *cext, *csingle;
151 | };
152 | struct meta_ids {
153 | char *name;
154 | char *term_flg;
155 | };
156 |
157 | /* filter.c */
158 | struct pattern {
159 | char *pattern;
160 | int relative;
161 | struct pattern *next;
162 | };
163 |
164 | struct ignorefile {
165 | char *path;
166 | struct pattern *remove, *reverse;
167 | struct ignorefile *next;
168 | };
169 |
170 | /* info.c */
171 | struct comment {
172 | struct pattern *pattern;
173 | char **desc;
174 | struct comment *next;
175 | };
176 |
177 | struct infofile {
178 | char *path;
179 | struct comment *comments;
180 | struct infofile *next;
181 | };
182 |
183 |
184 | /* Function prototypes: */
185 | /* tree.c */
186 | void setoutput(const char *filename);
187 | void print_version(int nl);
188 | void usage(int);
189 | void push_files(const char *dir, struct ignorefile **ig, struct infofile **inf, bool top);
190 | int patignore(const char *name, bool isdir);
191 | int patinclude(const char *name, bool isdir);
192 | struct _info **unix_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err);
193 | struct _info **read_dir(char *dir, ssize_t *n, int infotop);
194 |
195 | int filesfirst(struct _info **, struct _info **);
196 | int dirsfirst(struct _info **, struct _info **);
197 | int alnumsort(struct _info **, struct _info **);
198 | int versort(struct _info **a, struct _info **b);
199 | int reversealnumsort(struct _info **, struct _info **);
200 | int mtimesort(struct _info **, struct _info **);
201 | int ctimesort(struct _info **, struct _info **);
202 | int sizecmp(off_t a, off_t b);
203 | int fsizesort(struct _info **a, struct _info **b);
204 |
205 | void *xmalloc(size_t), *xrealloc(void *, size_t);
206 | char *gnu_getcwd(void);
207 | int patmatch(const char *buf, const char *pat, bool isdir);
208 | void indent(int maxlevel);
209 | void free_dir(struct _info **);
210 | #ifdef __EMX__
211 | char *prot(long);
212 | #else
213 | char *prot(mode_t);
214 | #endif
215 | char *do_date(time_t);
216 | void printit(const char *);
217 | int psize(char *buf, off_t size);
218 | char Ftype(mode_t mode);
219 | struct _info *stat2info(const struct stat *st);
220 | char *fillinfo(char *buf, const struct _info *ent);
221 |
222 | /* list.c */
223 | void null_intro(void);
224 | void null_outtro(void);
225 | void null_close(struct _info *file, int level, int needcomma);
226 | void emit_tree(char **dirname, bool needfulltree);
227 | struct totals listdir(char *dirname, struct _info **dir, int lev, dev_t dev, bool hasfulltree);
228 |
229 | /* unix.c */
230 | int unix_printinfo(char *dirname, struct _info *file, int level);
231 | int unix_printfile(char *dirname, char *filename, struct _info *file, int descend);
232 | int unix_error(char *error);
233 | void unix_newline(struct _info *file, int level, int postdir, int needcomma);
234 | void unix_report(struct totals tot);
235 |
236 | /* html.c */
237 | void url_encode(FILE *fd, char *s);
238 | void html_intro(void);
239 | void html_outtro(void);
240 | int html_printinfo(char *dirname, struct _info *file, int level);
241 | int html_printfile(char *dirname, char *filename, struct _info *file, int descend);
242 | int html_error(char *error);
243 | void html_newline(struct _info *file, int level, int postdir, int needcomma);
244 | void html_close(struct _info *file, int level, int needcomma);
245 | void html_report(struct totals tot);
246 | void html_encode(FILE *fd, char *s);
247 |
248 | /* xml.c */
249 | void xml_intro(void);
250 | void xml_outtro(void);
251 | int xml_printinfo(char *dirname, struct _info *file, int level);
252 | int xml_printfile(char *dirname, char *filename, struct _info *file, int descend);
253 | int xml_error(char *error);
254 | void xml_newline(struct _info *file, int level, int postdir, int needcomma);
255 | void xml_close(struct _info *file, int level, int needcomma);
256 | void xml_report(struct totals tot);
257 |
258 | /* json.c */
259 | void json_indent(int maxlevel);
260 | void json_fillinfo(struct _info *ent);
261 | void json_intro(void);
262 | void json_outtro(void);
263 | int json_printinfo(char *dirname, struct _info *file, int level);
264 | int json_printfile(char *dirname, char *filename, struct _info *file, int descend);
265 | int json_error(char *error);
266 | void json_newline(struct _info *file, int level, int postdir, int needcomma);
267 | void json_close(struct _info *file, int level, int needcomma);
268 | void json_report(struct totals tot);
269 |
270 | /* color.c */
271 | void parse_dir_colors(void);
272 | bool color(mode_t mode, const char *name, bool orphan, bool islink);
273 | void endcolor(void);
274 | void fancy(FILE *out, char *s);
275 | const char *getcharset(void);
276 | void initlinedraw(bool flag);
277 |
278 | /* hash.c */
279 | char *uidtoname(uid_t uid);
280 | char *gidtoname(gid_t gid);
281 | bool findino(ino_t, dev_t);
282 | void saveino(ino_t, dev_t);
283 |
284 | /* file.c */
285 | struct _info **file_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err);
286 | struct _info **tabedfile_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err);
287 |
288 | /* filter.c */
289 | void gittrim(char *s);
290 | struct pattern *new_pattern(char *pattern);
291 | bool filtercheck(const char *path, const char *name, int isdir);
292 | struct ignorefile *new_ignorefile(const char *path, bool checkparents);
293 | void push_filterstack(struct ignorefile *ig);
294 | struct ignorefile *pop_filterstack(void);
295 |
296 | /* info.c */
297 | struct infofile *new_infofile(const char *path, bool checkparents);
298 | void push_infostack(struct infofile *inf);
299 | struct infofile *pop_infostack(void);
300 | struct comment *infocheck(const char *path, const char *name, int top, bool isdir);
301 | void printcomment(size_t line, size_t lines, char *s);
302 |
303 | /* list.c */
304 | void new_emit_unix(char **dirname, bool needfulltree);
305 |
306 |
307 | /* We use the strverscmp.c file if we're not linux: */
308 | #if !defined(__linux__) || defined(__ANDROID__)
309 | int strverscmp (const char *s1, const char *s2);
310 | #endif
311 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Please read the INSTALL file for installation instructions, particularly if
2 | you are installing on a non-Linux machine.
3 |
4 | This is a handy little utility to display a tree view of directories that
5 | I wrote some time ago and just added color support to. I've decided that
6 | since no one else has done something similar I would go ahead and release
7 | it, even though it's barely a 1st year CS student hack. I've found it damn
8 | handy to peruse a directory tree though, especially when someone is trying to
9 | hide something from you.
10 |
11 | The main distribution site for tree is here:
12 | http://oldmanprogrammer.net/source.php?dir=projects/tree
13 |
14 | Backup GIT sites are:
15 | https://gitlab.com/OldManProgrammer/unix-tree
16 | https://github.com/Old-Man-Programmer/tree
17 |
18 | Current e-mail address to reach me at: steve.baker.llc@gmail.com
19 |
20 | If you don't like the way it looks let me know how you think it should be
21 | formatted. Feel free to suggest modifications and additions.
22 |
23 | Thanks go out so the following people who have helped bring tree to the
24 | pinnacle of perfection that it is: ;)
25 |
26 | Francesc Rocher
27 | - Added HTML output (-H).
28 | - Added options -o, -L and -R.
29 |
30 | Gerald Scheidl
31 | - Added -S option to print ASCII graphics lines for use under linux
32 | console when an alternate console font has been selected (might also
33 | work under DOS telnet).
34 |
35 | Guido Socher (and others)
36 | - Made tree more portable. Should compile under solaris.
37 |
38 | Mitja Lacen
39 | - Discovered bug where tree will segmentation fault on long pathnames.
40 | - Discovered in -L argument processing.
41 |
42 | Nathaniel Delage
43 | - Discovered problem with recursive symlink detection
44 |
45 | A. Karthik
46 | - Suggested option to remove file and directory report at end of tree
47 | listing.
48 |
49 | Roger Luethi
50 | - Spotted memory over-allocation bug in read_dir().
51 | - Submitted several patches to fix various memory leaks.
52 |
53 | Daniel Lee
54 | - Reported that Tru64 defines TRUE/FALSE in sys/types.h (OSF1 standard?)
55 |
56 | Paolo Violini
57 | - Found bug in tree that caused it to seg-fault if 50 file arguments where
58 | given and directory coloring was turned on.
59 |
60 | Mitsuaki Masuhara
61 | - Discovered tree crashed on missing arguments.
62 | - Discovered that tree did not properly encode characters in filenames
63 | when used as URLs when using the -H option.
64 | - Fixed issue with --charset option processing.
65 |
66 | Johan Fredrik
67 | - Pointed out that tree did not list large files.
68 |
69 | Ted Tiberio
70 | - Submitted patch which fixed a compiler issue and cleaned up HTML and CSS
71 | code, applied CSS to all output, and fixed up HTML to 4.01 strict
72 | standards.
73 |
74 | David MacMahon
75 | - Added '|' support to the pattern matching routines.
76 |
77 | Dan Jacobson
78 | - Pointed out that -t did not sort properly for files with the same
79 | timestamp.
80 | - Suggested option to change HTML title and H1 string.
81 | - Suggested -r option for reversed alphanumeric sort ala 'ls -r'.
82 |
83 | Kyosuke Tokoro
84 | - Provided patch to support OS/2, fix HTML encoding, provide charset
85 | support. Added to authors list.
86 |
87 | Florian Ernst
88 | - Debian maintainer who pointed out problems and applied fire to feet to fix
89 | stuff.
90 |
91 | Jack Cuyler
92 | - Suggested -h option for human readable output for -s, ala ls -lh.
93 |
94 | Jonathon Cranford
95 | - Supplied patch to make tree under cygwin.
96 |
97 | Richard Houser
98 | - Provided patch to fix a colorization bug when dealing with special
99 | files and directories that seem to have an extension.
100 |
101 | Zurd (?)
102 | - Suggested removing trailing slash on user supplied directory names if -f
103 | option is used.
104 |
105 | John Nintendud
106 | - Pointed out broken HTML output in 1.5.1.
107 |
108 | Mark Braker
109 | - Suggested --filelimit option.
110 |
111 | Michael Vogt
112 | - Suggested -v option (version sort).
113 |
114 | Wang Quanhong
115 | - Provided build options for Solaris.
116 |
117 | Craig McDaniel
118 | - Provided build options and source mods for HP NonStop support.
119 |
120 | Christian Grigis
121 | - Noted that setlocale() should come before MB_CUR_MAX check.
122 |
123 | Kamaraju Kusumanchi
124 | - Submitted patch to remove compiler warnings for Solaris.
125 |
126 | Martin Nagy
127 | - Provided patch which fixes issue where indent may output more than it
128 | should when dirs[*] is not properly cleared before use.
129 |
130 | William C. Lathan III
131 | - Showed that tree was not properly quoting arguments to recursively called
132 | tree instances when using -R.
133 |
134 | Ulrich Eckhardt
135 | - Submitted patch for --si option.
136 |
137 | Tim Waugh (redhat)
138 | - Pointed out a potential memory leak in listdir().
139 |
140 | Markus Schnalke
141 | - Tracked down bug where tree would print "argetm" before the filename of a
142 | symbolic link when the "target" option was specified for LINK in dircolors.
143 |
144 | Ujjwal Kumar
145 | - Suggested that tree backslash spaces like ls does for script use. Made
146 | output more like ls.
147 |
148 | Ivan Shmakov
149 | - Pointed out multiple CLI defenciencies (via Debian)
150 |
151 | Mantas Mikulnas
152 | - Provided patch to make tree more reliably detect the UTF-8 locale.
153 |
154 | Tim Mooney
155 | - Noticed S_ISDOOR/S_IFDOOR spelling mistake for under Solaris.
156 |
157 | Han Hui
158 | - Pointed out possible memory overflow in read_dir (path/lbuf not equal in size
159 | to pathsize/lbufsize.)
160 |
161 | Ryan Hollis
162 | - Pointed out problems with the Makefile w/ respect to OSX.
163 |
164 | Philipp M?ller
165 | - Provided patch for filesize sorting.
166 |
167 | Sascha Zorn
168 | - Pointed out that the HTML output was broken when -L 1 option was used.
169 |
170 | Alexandre Wendling
171 | - Pointed out that modern systems may use 32 bit uid/gids which could lead
172 | to a potential buffer overflow in the uid/gid to name mapping functions.
173 |
174 | Florian Sesser
175 | - Provided patch to add JSON support.
176 |
177 | Brian Mattern & Jason A. Donenfeld
178 | - Provided patch to add --matchdirs functionality.
179 |
180 | Jason A. Donenfeld
181 | - Added --caseinsentive, renamed --ignore-case option.
182 | - Bugged me a lot.
183 |
184 | Stephan Gabert
185 | - Found a bug where the wrong inode (and device) information would be printed
186 | for symbolic links.
187 |
188 | Nick Craig-Wood
189 | - Fixed issue where mbstowcs() fails to null terminate the string due to
190 | improper UTF-8 encoding leading to garbage being printed.
191 |
192 | Mantas Mikulėnas
193 | - Fixed issue with malformed multibyte string handling.
194 |
195 | Wagner Camarao
196 | - Pointed out that JSON size output ignored -h/--si flags
197 |
198 | John Lane, Tad, others
199 | - Fixed JSON output hanging commas
200 |
201 | Jacob Wahlgren
202 | - Improved command line switch error reporting.
203 | - Symbolic links not displayed if a -P pattern is active
204 | - Missing argument error reporting fixes on long format switches.
205 |
206 | Shawn Mehan
207 | - Update BINDIR in Makefile for MacOS X -- It is not allowed to install
208 | programs to /usr/bin on MacOS X any longer due to System Integrity
209 | Protection (SIP)
210 |
211 | Kirill Kolyshkin
212 | - Some man page fixes and cleanups
213 |
214 | Alyssa Ross
215 | - Suggested adding support for BSD's CLICOLOR and CLICOLOR_FORCE environment
216 | variables.
217 |
218 | Tomáš Beránek
219 | - Make sure we always use xmalloc / xrealloc
220 |
221 | Sergei Maximov
222 | - Make XML/HTML/JSON output mutually exclusive.
223 |
224 | Jonas Stein
225 | - Deprecate using local -DLINUX / -DCYGWIN and use the OS provided defines
226 |
227 | John A. Fedoruk
228 | - Suggested --filesfirst option.
229 |
230 | Michael Osipov
231 | - Optimized makefile, HP/UX support.
232 |
233 | Richard Mitchell
234 | - Suggested --metafirst option
235 |
236 | Paul Seyfert
237 | - Honor -n (no color) even if the CLICOLOR_FORCE environment variable is set
238 |
239 | Filips Romāns via Debian
240 | - Make tree colorization use reset (rs code in dir_colors,) not normal color
241 | when resetting attributes.
242 |
243 | Chentao Credungtao via Debian
244 | - Properly sort --fromfile input
245 |
246 | Jake Zimmerman (and others)
247 | - Suggest support for .gitignore files (--gitignore option)
248 |
249 | Eric Pruitt
250 | - Always HTML escape filenames in HTML output even when -C is used.
251 |
252 | Michiel Beijen (and others)
253 | - Suggest Support multiple -I and -P instances.
254 | - Suggest that / match directories in patterns (also Taylor Faubion)
255 | - Suggested to update MANPATH for OS X
256 |
257 | Michal Vasilek
258 | - Various Makefile fixes
259 |
260 | Josey Smith
261 | - Reported an error with * in the patchmatch code where *foo*bar would match
262 | *foo alone.
263 |
264 | Maxim Cournoyer
265 | - Reported HTML url output issue w/ 2.0.0-2.0.1
266 |
267 | Ben Brown
268 | - Updates to the Makefile
269 | - Reported use after free error
270 |
271 | Erik Skultety
272 | - Reported same use after error
273 |
274 | Saniya Maheshwari / Mig-hub ? / Carlos Pinto
275 | - Reported various issues with --gitignore
276 |
277 | Piotr Andruszkow
278 | - Suggested adding support for --info and --gitignore for the --fromfile
279 | option.
280 |
281 | Sebastian Rose
282 | - Another attempt at fixing extraneous /'s in HTML URLs/output.
283 |
284 | Dave Rice
285 | - Fixed XML output
286 |
287 | Timm Fitschen
288 | - Suggest adding support for the NO_COLOR environment variable.
289 |
290 | Chentao Credungtao
291 | - Suggested supporting symbolic links in --fromfile (--fflinks option)
292 |
293 | Sith Wijesinghe and Matthew Sessions
294 | - Remove many C90 isms to make compiling with C90 compilers easier.
295 |
296 | simonpmind (gitlab)
297 | - Reported issue where following links while doing JSON output would lead to
298 | incorrect JSON output.
299 | - Suggested suppressing 'recursive, not followed' when using -L, fixing JSON
300 | error.
301 |
302 | German Lashevich
303 | - Reported an issue where .info patterns relative to the .info file that did
304 | not use a wildcard for matching the prefix were not matching files properly.
305 |
306 | Javier Jaramago Fernández
307 | - Reported a buffer overflow in listdir() when file names are allowed to be
308 | longer than 256 characters (like when using fromfile.)
309 |
310 | 6ramr (gitlab)
311 | - Reported issue with following symbolic links when a full tree was gathered.
312 |
313 | Clinton
314 | - Reported issue where --gitignore does not think a pattern with a singular
315 | terminal '/' (indicating it matches only directories,) is a relative path.
316 |
317 | jack6th (gitlab)
318 | - Don't prematurely sort files/directories with --from*file.
319 |
320 | Hanqin Guan (The OSLab of Peking University):
321 | - Fuzzing testing that identified several problems in --fromfile and
322 | --fromtabfile processing.
323 | - -U should disable --dirsfirst / --filesfirst sorting.
324 | - Make sure gittrim() function can handle a null string.
325 |
326 | Trevor Gross
327 | - Suggested adding support for immediate values to -L with no spacing.
328 |
329 | Christoph Anton Mitterer
330 | - Suggested option negation, which led to --opt-toggle.
331 |
332 | Nicolai Dagestad
333 | - Suggested --hyperlink option.
334 |
335 | Alchemyst (github)
336 | - Reported JSON error when using the --du option and unable to open a
337 | directory.
338 | - Reported that the reported total was incorrect when using the --du option.
339 |
340 | Ivan Ivanovich
341 | - Reported small rounding error in human readable size output.
342 |
343 | Kenta Arai
344 | - Reported Segfault with --filelimit option
345 | - Add NULL guard for json_printinfo() and xml_printinfo() (and fix ftype
346 | printing for XML)
347 | - Fix getcharset() to not return a getenv() pointer.
348 | - Suggest adding .gitignore file to the distribution.
349 | - Clean up some warnings issued by -Wextra
350 |
351 | freemedom (github)
352 | - Provided Makefile information for cross compiling for Android.
353 |
354 | David Seifert
355 | - Provided updates for Makefile
356 | - Suggested move to stdbool.h to avoid problems with newer compilers.
357 |
358 | Daniel Li / Landon Bourma
359 | - Reported segfault from incorrect free.
360 |
361 | And many others whom I've failed to keep track of. I should have started
362 | this list years ago.
363 |
364 | - Steve Baker
365 | Steve.Baker.llc@gmail.com
366 | or
367 | oldmanprogrammer.llc@gmail.com
368 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License
307 | along with this program; if not, write to the Free Software
308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309 |
310 |
311 | Also add information on how to contact you by electronic and paper mail.
312 |
313 | If the program is interactive, make it output a short notice like this
314 | when it starts in an interactive mode:
315 |
316 | Gnomovision version 69, Copyright (C) year name of author
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 | This is free software, and you are welcome to redistribute it
319 | under certain conditions; type `show c' for details.
320 |
321 | The hypothetical commands `show w' and `show c' should show the appropriate
322 | parts of the General Public License. Of course, the commands you use may
323 | be called something other than `show w' and `show c'; they could even be
324 | mouse-clicks or menu items--whatever suits your program.
325 |
326 | You should also get your employer (if you work as a programmer) or your
327 | school, if any, to sign a "copyright disclaimer" for the program, if
328 | necessary. Here is a sample; alter the names:
329 |
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
332 |
333 | , 1 April 1989
334 | Ty Coon, President of Vice
335 |
336 | This General Public License does not permit incorporating your program into
337 | proprietary programs. If your program is a subroutine library, you may
338 | consider it more useful to permit linking proprietary applications with the
339 | library. If this is what you want to do, use the GNU Library General
340 | Public License instead of this License.
341 |
--------------------------------------------------------------------------------
/color.c:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 | #include "tree.h"
19 |
20 | /*
21 | * Hacked in DIR_COLORS support for linux. ------------------------------
22 | */
23 | /*
24 | * If someone asked me, I'd extend dircolors, to provide more generic
25 | * color support so that more programs could take advantage of it. This
26 | * is really just hacked in support. The dircolors program should:
27 | * 1) Put the valid terms in a environment var, like:
28 | * COLOR_TERMS=linux:console:xterm:vt100...
29 | * 2) Put the COLOR and OPTIONS directives in a env var too.
30 | * 3) Have an option to dircolors to silently ignore directives that it
31 | * doesn't understand (directives that other programs would
32 | * understand).
33 | * 4) Perhaps even make those unknown directives environment variables.
34 | *
35 | * The environment is the place for cryptic crap no one looks at, but
36 | * programs. No one is going to care if it takes 30 variables to do
37 | * something.
38 | */
39 | enum {
40 | ERROR = -1, CMD_COLOR = 0, CMD_OPTIONS, CMD_TERM, CMD_EIGHTBIT, COL_RESET,
41 | COL_NORMAL, COL_FILE, COL_DIR, COL_LINK, COL_FIFO, COL_DOOR, COL_BLK, COL_CHR,
42 | COL_ORPHAN, COL_SOCK, COL_SETUID, COL_SETGID, COL_STICKY_OTHER_WRITABLE,
43 | COL_OTHER_WRITABLE, COL_STICKY, COL_EXEC, COL_MISSING,
44 | COL_LEFTCODE, COL_RIGHTCODE, COL_ENDCODE, COL_BOLD, COL_ITALIC,
45 | /* Keep this one last, sets the size of the color_code array: */
46 | DOT_EXTENSION
47 | };
48 |
49 | enum {
50 | MCOL_INODE, MCOL_PERMS, MCOL_USER, MCOL_GROUP, MCOL_SIZE, MCOL_DATE,
51 | MCOL_INDENTLINES
52 | };
53 |
54 | bool colorize = false, ansilines = false, linktargetcolor = false;
55 |
56 | char *color_code[DOT_EXTENSION+1] = {NULL};
57 |
58 | /*
59 | char *vgacolor[] = {
60 | "black", "red", "green", "yellow", "blue", "fuchsia", "aqua", "white",
61 | NULL, NULL,
62 | "transparent", "red", "green", "yellow", "blue", "fuchsia", "aqua", "black"
63 | };
64 | struct colortable {
65 | char *term_flg, *CSS_name, *font_fg, *font_bg;
66 | } colortable[11];
67 | */
68 |
69 | struct extensions *ext = NULL;
70 | const struct linedraw *linedraw;
71 |
72 | char **split(char *str, const char *delim, size_t *nwrds);
73 | int cmd(char *s);
74 |
75 | extern FILE *outfile;
76 | extern bool Hflag, force_color, nocolor;
77 | extern const char *charset;
78 |
79 | void parse_dir_colors(void)
80 | {
81 | char **arg, **c, *colors, *s;
82 | int i, col, cc;
83 | size_t n;
84 | struct extensions *e;
85 |
86 | if (Hflag) return;
87 |
88 | s = getenv("NO_COLOR");
89 | if (s && s[0]) nocolor = true;
90 |
91 | if (getenv("TERM") == NULL) {
92 | colorize = false;
93 | return;
94 | }
95 |
96 | cc = getenv("CLICOLOR") != NULL;
97 | if (getenv("CLICOLOR_FORCE") != NULL && !nocolor) force_color=true;
98 | s = getenv("TREE_COLORS");
99 | if (s == NULL) s = getenv("LS_COLORS");
100 | if ((s == NULL || strlen(s) == 0) && (force_color || cc)) s = ":no=00:rs=0:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.bat=01;32:*.BAT=01;32:*.btm=01;32:*.BTM=01;32:*.cmd=01;32:*.CMD=01;32:*.com=01;32:*.COM=01;32:*.dll=01;32:*.DLL=01;32:*.exe=01;32:*.EXE=01;32:*.arj=01;31:*.bz2=01;31:*.deb=01;31:*.gz=01;31:*.lzh=01;31:*.rpm=01;31:*.tar=01;31:*.taz=01;31:*.tb2=01;31:*.tbz2=01;31:*.tbz=01;31:*.tgz=01;31:*.tz2=01;31:*.z=01;31:*.Z=01;31:*.zip=01;31:*.ZIP=01;31:*.zoo=01;31:*.asf=01;35:*.ASF=01;35:*.avi=01;35:*.AVI=01;35:*.bmp=01;35:*.BMP=01;35:*.flac=01;35:*.FLAC=01;35:*.gif=01;35:*.GIF=01;35:*.jpg=01;35:*.JPG=01;35:*.jpeg=01;35:*.JPEG=01;35:*.m2a=01;35:*.M2a=01;35:*.m2v=01;35:*.M2V=01;35:*.mov=01;35:*.MOV=01;35:*.mp3=01;35:*.MP3=01;35:*.mpeg=01;35:*.MPEG=01;35:*.mpg=01;35:*.MPG=01;35:*.ogg=01;35:*.OGG=01;35:*.ppm=01;35:*.rm=01;35:*.RM=01;35:*.tga=01;35:*.TGA=01;35:*.tif=01;35:*.TIF=01;35:*.wav=01;35:*.WAV=01;35:*.wmv=01;35:*.WMV=01;35:*.xbm=01;35:*.xpm=01;35:";
101 |
102 | if (s == NULL || (!force_color && (nocolor || !isatty(1)))) {
103 | colorize = false;
104 | return;
105 | }
106 |
107 | colorize = true;
108 |
109 | for(i=0; i < DOT_EXTENSION; i++) color_code[i] = NULL;
110 |
111 | colors = scopy(s);
112 |
113 | arg = split(colors,":",&n);
114 |
115 | for(i=0;arg[i];i++) {
116 | c = split(arg[i],"=",&n);
117 |
118 | switch(col = cmd(c[0])) {
119 | case ERROR:
120 | break;
121 | case DOT_EXTENSION:
122 | if (c[1]) {
123 | e = xmalloc(sizeof(struct extensions));
124 | e->ext = scopy(c[0]+1);
125 | e->term_flg = scopy(c[1]);
126 | e->nxt = ext;
127 | ext = e;
128 | }
129 | break;
130 | case COL_LINK:
131 | if (c[1] && (strcasecmp("target",c[1]) == 0)) {
132 | linktargetcolor = true;
133 | color_code[COL_LINK] = "01;36"; /* Should never actually be used */
134 | break;
135 | }
136 | /* Falls through */
137 | default:
138 | if (c[1]) color_code[col] = scopy(c[1]);
139 | break;
140 | }
141 |
142 | free(c);
143 | }
144 | free(arg);
145 |
146 | /**
147 | * Make sure at least reset (not normal) is defined. We're going to assume
148 | * ANSI/vt100 support:
149 | */
150 | if (!color_code[COL_LEFTCODE]) color_code[COL_LEFTCODE] = scopy("\033[");
151 | if (!color_code[COL_RIGHTCODE]) color_code[COL_RIGHTCODE] = scopy("m");
152 | if (!color_code[COL_RESET]) color_code[COL_RESET] = scopy("0");
153 | if (!color_code[COL_BOLD]) {
154 | color_code[COL_BOLD] = xmalloc(strlen(color_code[COL_LEFTCODE])+strlen(color_code[COL_RIGHTCODE])+2);
155 | sprintf(color_code[COL_BOLD],"%s1%s",color_code[COL_LEFTCODE],color_code[COL_RIGHTCODE]);
156 | }
157 | if (!color_code[COL_ITALIC]) {
158 | color_code[COL_ITALIC] = xmalloc(strlen(color_code[COL_LEFTCODE])+strlen(color_code[COL_RIGHTCODE])+2);
159 | sprintf(color_code[COL_ITALIC],"%s3%s",color_code[COL_LEFTCODE],color_code[COL_RIGHTCODE]);
160 | }
161 | if (!color_code[COL_ENDCODE]) {
162 | color_code[COL_ENDCODE] = xmalloc(strlen(color_code[COL_LEFTCODE])+strlen(color_code[COL_RESET])+strlen(color_code[COL_RIGHTCODE])+1);
163 | sprintf(color_code[COL_ENDCODE],"%s%s%s",color_code[COL_LEFTCODE],color_code[COL_RESET],color_code[COL_RIGHTCODE]);
164 | }
165 |
166 | free(colors);
167 | }
168 |
169 | /*
170 | * You must free the pointer that is allocated by split() after you
171 | * are done using the array.
172 | */
173 | char **split(char *str, const char *delim, size_t *nwrds)
174 | {
175 | size_t n = 128;
176 | char **w = xmalloc(sizeof(char *) * n);
177 |
178 | w[*nwrds = 0] = strtok(str,delim);
179 |
180 | while (w[*nwrds]) {
181 | if (*nwrds == (n-2)) w = xrealloc(w,sizeof(char *) * (n+=256));
182 | w[++(*nwrds)] = strtok(NULL,delim);
183 | }
184 |
185 | w[*nwrds] = NULL;
186 | return w;
187 | }
188 |
189 | int cmd(char *s)
190 | {
191 | static struct {
192 | char *cmd;
193 | char cmdnum;
194 | } cmds[] = {
195 | {"rs", COL_RESET}, {"no", COL_NORMAL}, {"fi", COL_FILE}, {"di", COL_DIR},
196 | {"ln", COL_LINK}, {"pi", COL_FIFO}, {"do", COL_DOOR}, {"bd", COL_BLK},
197 | {"cd", COL_CHR}, {"or", COL_ORPHAN}, {"so", COL_SOCK}, {"su", COL_SETUID},
198 | {"sg", COL_SETGID}, {"tw", COL_STICKY_OTHER_WRITABLE},
199 | {"ow", COL_OTHER_WRITABLE}, {"st", COL_STICKY}, {"ex", COL_EXEC},
200 | {"mi", COL_MISSING}, {"lc", COL_LEFTCODE}, {"rc", COL_RIGHTCODE},
201 | {"ec", COL_ENDCODE}, {NULL, 0}
202 | };
203 | int i;
204 |
205 | if (s == NULL) return ERROR; /* Probably can't happen */
206 |
207 | if (s[0] == '*') return DOT_EXTENSION;
208 | for(i=0;cmds[i].cmdnum;i++) {
209 | if (!strcmp(cmds[i].cmd,s)) return cmds[i].cmdnum;
210 | }
211 | return ERROR;
212 | }
213 |
214 | bool print_color(int color)
215 | {
216 | if (!color_code[color]) return false;
217 |
218 | fputs(color_code[COL_LEFTCODE],outfile);
219 | fputs(color_code[color],outfile);
220 | fputs(color_code[COL_RIGHTCODE],outfile);
221 | return true;
222 | }
223 |
224 | void endcolor(void)
225 | {
226 | if (color_code[COL_ENDCODE])
227 | fputs(color_code[COL_ENDCODE],outfile);
228 | }
229 |
230 |
231 | void fancy(FILE *out, char *s)
232 | {
233 | for (; *s; s++) {
234 | switch(*s) {
235 | case '\b': if (colorize && color_code[COL_BOLD]) fputs(color_code[COL_BOLD] , out); break;
236 | case '\f': if (colorize && color_code[COL_ITALIC]) fputs(color_code[COL_ITALIC] , out); break;
237 | case '\r': if (colorize && color_code[COL_ENDCODE]) fputs(color_code[COL_ENDCODE], out); break;
238 | default:
239 | fputc(*s,out);
240 | }
241 | }
242 | }
243 |
244 | bool color(mode_t mode, const char *name, bool orphan, bool islink)
245 | {
246 | struct extensions *e;
247 | size_t l, xl;
248 |
249 | if (orphan) {
250 | if (islink) {
251 | if (print_color(COL_MISSING)) return true;
252 | } else {
253 | if (print_color(COL_ORPHAN)) return true;
254 | }
255 | }
256 |
257 | /* It's probably safe to assume short-circuit evaluation, but we'll do it this way: */
258 | switch(mode & S_IFMT) {
259 | case S_IFIFO:
260 | return print_color(COL_FIFO);
261 | case S_IFCHR:
262 | return print_color(COL_CHR);
263 | case S_IFDIR:
264 | if (mode & S_ISVTX) {
265 | if ((mode & S_IWOTH))
266 | if (print_color(COL_STICKY_OTHER_WRITABLE)) return true;
267 | if (!(mode & S_IWOTH))
268 | if (print_color(COL_STICKY)) return true;
269 | }
270 | if ((mode & S_IWOTH))
271 | if (print_color(COL_OTHER_WRITABLE)) return true;
272 | return print_color(COL_DIR);
273 | #ifndef __EMX__
274 | case S_IFBLK:
275 | return print_color(COL_BLK);
276 | case S_IFLNK:
277 | return print_color(COL_LINK);
278 | #ifdef S_IFDOOR
279 | case S_IFDOOR:
280 | return print_color(COL_DOOR);
281 | #endif
282 | #endif
283 | case S_IFSOCK:
284 | return print_color(COL_SOCK);
285 | case S_IFREG:
286 | if ((mode & S_ISUID))
287 | if (print_color(COL_SETUID)) return true;
288 | if ((mode & S_ISGID))
289 | if (print_color(COL_SETGID)) return true;
290 | if (mode & (S_IXUSR | S_IXGRP | S_IXOTH))
291 | if (print_color(COL_EXEC)) return true;
292 |
293 | /* not a directory, link, special device, etc, so check for extension match */
294 | l = strlen(name);
295 | for(e=ext;e;e=e->nxt) {
296 | xl = strlen(e->ext);
297 | if (!strcmp((l>xl)?name+(l-xl):name,e->ext)) {
298 | fputs(color_code[COL_LEFTCODE], outfile);
299 | fputs(e->term_flg, outfile);
300 | fputs(color_code[COL_RIGHTCODE], outfile);
301 | return true;
302 | }
303 | }
304 | /* colorize just normal files too */
305 | return print_color(COL_FILE);
306 | }
307 | return print_color(COL_NORMAL);
308 | }
309 |
310 | /*
311 | * Charsets provided by Kyosuke Tokoro (NBG01720@nifty.ne.jp)
312 | */
313 | const char *getcharset(void)
314 | {
315 | char *cs;
316 | static char buffer[256];
317 |
318 | cs = getenv("TREE_CHARSET");
319 | if (cs) return strncpy(buffer,cs,255);
320 |
321 | #ifndef __EMX__
322 | return NULL;
323 | #else
324 | ULONG aulCpList[3],ulListSize,codepage=0;
325 |
326 | if(!getenv("WINDOWID"))
327 | if(!DosQueryCp(sizeof aulCpList,aulCpList,&ulListSize))
328 | if(ulListSize>=sizeof*aulCpList)
329 | codepage=*aulCpList;
330 |
331 | switch(codepage) {
332 | case 437: case 775: case 850: case 851: case 852: case 855:
333 | case 857: case 860: case 861: case 862: case 863: case 864:
334 | case 865: case 866: case 868: case 869: case 891: case 903:
335 | case 904:
336 | sprintf(buffer,"IBM%03lu",codepage);
337 | break;
338 | case 367:
339 | return"US-ASCII";
340 | case 813:
341 | return"ISO-8859-7";
342 | case 819:
343 | return"ISO-8859-1";
344 | case 881: case 882: case 883: case 884: case 885:
345 | sprintf(buffer,"ISO-8859-%lu",codepage-880);
346 | break;
347 | case 858: case 924:
348 | sprintf(buffer,"IBM%05lu",codepage);
349 | break;
350 | case 874:
351 | return"TIS-620";
352 | case 897: case 932: case 942: case 943:
353 | return"Shift_JIS";
354 | case 912:
355 | return"ISO-8859-2";
356 | case 915:
357 | return"ISO-8859-5";
358 | case 916:
359 | return"ISO-8859-8";
360 | case 949: case 970:
361 | return"EUC-KR";
362 | case 950:
363 | return"Big5";
364 | case 954:
365 | return"EUC-JP";
366 | case 1051:
367 | return"hp-roman8";
368 | case 1089:
369 | return"ISO-8859-6";
370 | case 1250: case 1251: case 1253: case 1254: case 1255: case 1256:
371 | case 1257: case 1258:
372 | sprintf(buffer,"windows-%lu",codepage);
373 | break;
374 | case 1252:
375 | return"ISO-8859-1-Windows-3.1-Latin-1";
376 | default:
377 | return NULL;
378 | }
379 | return buffer;
380 | #endif
381 | }
382 |
383 | void initlinedraw(bool flag)
384 | {
385 | static const char*latin1_3[]={
386 | "ISO-8859-1", "ISO-8859-1:1987", "ISO_8859-1", "latin1", "l1", "IBM819",
387 | "CP819", "csISOLatin1", "ISO-8859-3", "ISO_8859-3:1988", "ISO_8859-3",
388 | "latin3", "ls", "csISOLatin3", NULL
389 | };
390 | static const char*iso8859_789[]={
391 | "ISO-8859-7", "ISO_8859-7:1987", "ISO_8859-7", "ELOT_928", "ECMA-118",
392 | "greek", "greek8", "csISOLatinGreek", "ISO-8859-8", "ISO_8859-8:1988",
393 | "iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew", "ISO-8859-9",
394 | "ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9", "latin5", "l5",
395 | "csISOLatin5", NULL
396 | };
397 | static const char*shift_jis[]={
398 | "Shift_JIS", "MS_Kanji", "csShiftJIS", NULL
399 | };
400 | static const char*euc_jp[]={
401 | "EUC-JP", "Extended_UNIX_Code_Packed_Format_for_Japanese",
402 | "csEUCPkdFmtJapanese", NULL
403 | };
404 | static const char*euc_kr[]={
405 | "EUC-KR", "csEUCKR", NULL
406 | };
407 | static const char*iso2022jp[]={
408 | "ISO-2022-JP", "csISO2022JP", "ISO-2022-JP-2", "csISO2022JP2", NULL
409 | };
410 | static const char*ibm_pc[]={
411 | "IBM437", "cp437", "437", "csPC8CodePage437", "IBM852", "cp852", "852",
412 | "csPCp852", "IBM863", "cp863", "863", "csIBM863", "IBM855", "cp855",
413 | "855", "csIBM855", "IBM865", "cp865", "865", "csIBM865", "IBM866",
414 | "cp866", "866", "csIBM866", NULL
415 | };
416 | static const char*ibm_ps2[]={
417 | "IBM850", "cp850", "850", "csPC850Multilingual", "IBM00858", "CCSID00858",
418 | "CP00858", "PC-Multilingual-850+euro", NULL
419 | };
420 | static const char*ibm_gr[]={
421 | "IBM869", "cp869", "869", "cp-gr", "csIBM869", NULL
422 | };
423 | static const char*gb[]={
424 | "GB2312", "csGB2312", NULL
425 | };
426 | static const char*utf8[]={
427 | "UTF-8", "utf8", NULL
428 | };
429 | static const char*big5[]={
430 | "Big5", "csBig5", NULL
431 | };
432 | static const char*viscii[]={
433 | "VISCII", "csVISCII", NULL
434 | };
435 | static const char*koi8ru[]={
436 | "KOI8-R", "csKOI8R", "KOI8-U", NULL
437 | };
438 | static const char*windows[]={
439 | "ISO-8859-1-Windows-3.1-Latin-1", "csWindows31Latin1",
440 | "ISO-8859-2-Windows-Latin-2", "csWindows31Latin2", "windows-1250",
441 | "windows-1251", "windows-1253", "windows-1254", "windows-1255",
442 | "windows-1256", "windows-1256", "windows-1257", NULL
443 | };
444 |
445 | static const struct linedraw cstable[]={
446 | { latin1_3, "| ", "|--", "·--", "©",
447 | " [", " [", " [", " [", " [" },
448 | { iso8859_789, "| ", "|--", "·--", "(c)",
449 | " [", " [", " [", " [", " [" },
450 | { shift_jis, "\204\240 ", "\204\245", "\204\244", "(c)",
451 | " [", " [", " [", " [", " [" },
452 | { euc_jp, "\250\242 ", "\250\247", "\250\246", "(c)",
453 | " [", " [", " [", " [", " [" },
454 | { euc_kr, "\246\242 ", "\246\247", "\246\246", "(c)",
455 | " [", " [", " [", " [", " [" },
456 | { iso2022jp, "\033$B(\"\033(B ", "\033$B('\033(B", "\033$B(&\033(B", "(c)",
457 | " [", " [", " [", " [", " [" },
458 | { ibm_pc, "\263 ", "\303\304\304", "\300\304\304", "(c)",
459 | " [", " [", " [", " [", " [" },
460 | { ibm_ps2, "\263 ", "\303\304\304", "\300\304\304", "\227",
461 | " [", " [", " [", " [", " [" },
462 | { ibm_gr, "\263 ", "\303\304\304", "\300\304\304", "\270",
463 | " [", " [", " [", " [", " [" },
464 | { gb, "\251\246 ", "\251\300", "\251\270", "(c)",
465 | " [", " [", " [", " [", " [" },
466 | { utf8, "\342\224\202\302\240\302\240", "\342\224\234\342\224\200\342\224\200",
467 | "\342\224\224\342\224\200\342\224\200", "\302\251",
468 | " \342\216\247", " \342\216\251", " \342\216\250", " \342\216\252", " {" },
469 | { big5, "\242x ", "\242u", "\242|", "(c)",
470 | " [", " [", " [", " [", " [" },
471 | { viscii, "| ", "|--", "`--", "\371",
472 | " [", " [", " [", " [", " [" },
473 | { koi8ru, "\201 ", "\206\200\200", "\204\200\200", "\277",
474 | " [", " [", " [", " [", " [" },
475 | { windows, "| ", "|--", "`--", "\251",
476 | " [", " [", " [", " [", " [" },
477 | { NULL, "| ", "|--", "`--", "(c)",
478 | " [", " [", " [", " [", " [" },
479 | };
480 | const char**s;
481 |
482 | if (flag) {
483 | fprintf(stderr,"Valid charsets include:\n");
484 | for(linedraw=cstable;linedraw->name;++linedraw) {
485 | for(s=linedraw->name;*s;++s) {
486 | fprintf(stderr," %s\n",*s);
487 | }
488 | }
489 | return;
490 | }
491 | if (charset) {
492 | for(linedraw=cstable;linedraw->name;++linedraw)
493 | for(s=linedraw->name;*s;++s)
494 | if(!strcasecmp(charset,*s)) return;
495 | }
496 | linedraw=cstable+sizeof cstable/sizeof*cstable-1;
497 | }
498 |
--------------------------------------------------------------------------------
/doc/tree.1:
--------------------------------------------------------------------------------
1 | .\" $Copyright: $
2 | .\" Copyright (c) 1996 - 2024 by Steve Baker
3 | .\" All Rights reserved
4 | .\"
5 | .\" This program is free software; you can redistribute it and/or modify
6 | .\" it under the terms of the GNU General Public License as published by
7 | .\" the Free Software Foundation; either version 2 of the License, or
8 | .\" (at your option) any later version.
9 | .\"
10 | .\" This program is distributed in the hope that it will be useful,
11 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | .\" GNU General Public License for more details.
14 | .\"
15 | .\" You should have received a copy of the GNU General Public License
16 | .\" along with this program; if not, write to the Free Software
17 | .\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 | .\"
19 | ...
20 | .TH TREE 1 "" "Tree 2.2.1"
21 | .SH NAME
22 | tree \- list contents of directories in a tree-like format.
23 | .SH SYNOPSIS
24 | \fBtree\fP
25 | [\fB-acdfghilnpqrstuvxACDFJQNSUX\fP]
26 | [\fB-L\fP \fIlevel\fP [\fB-R\fP]]
27 | [\fB-H\fP [-]\fIbaseHREF\fP]
28 | [\fB-T\fP \fItitle\fP]
29 | [\fB-o\fP \fIfilename\fP]
30 | [\fB-P\fP \fIpattern\fP]
31 | [\fB-I\fP \fIpattern\fP]
32 | [\fB--gitignore\fP]
33 | [\fB--gitfile\fP[\fB=\fP]\fIfile\fP]
34 | [\fB--matchdirs\fP]
35 | [\fB--metafirst\fP]
36 | [\fB--ignore-case\fP]
37 | [\fB--nolinks\fP]
38 | [\fB--hintro\fP[\fB=\fP]\fIfile\fP]
39 | [\fB--houtro\fP[\fB=\fP]\fIfile\fP]
40 | [\fB--inodes\fP]
41 | [\fB--device\fP]
42 | [\fB--sort\fP[\fB=\fP]\fIname\fP]
43 | [\fB--dirsfirst\fP]
44 | [\fB--filesfirst\fP]
45 | [\fB--filelimit\fP[\fB=\fP]\fI#\fP]
46 | [\fB--si\fP]
47 | [\fB--du\fP]
48 | [\fB--prune\fP]
49 | [\fB--charset\fP[\fB=\fP]\fIX\fP]
50 | [\fB--timefmt\fP[\fB=\fP]\fIformat\fP]
51 | [\fB--fromfile\fP]
52 | [\fB--fromtabfile\fP]
53 | [\fB--fflinks\fP]
54 | [\fB--info\fP]
55 | [\fB--infofile\fP[\fB=\fP]\fIfile\fP]
56 | [\fB--noreport\fP]
57 | [\fB--hyperlink\fP]
58 | [\fB--scheme\fP[\fB=\fP]\fIschema\fP]
59 | [\fB--authority\fP[\fB=\fP]\fIhostname\fP]
60 | [\fB--opt-toggle\fP]
61 | [\fB--version\fP]
62 | [\fB--help\fP]
63 | [\fB--\fP] [\fIdirectory\fP ...]
64 |
65 | .br
66 | .SH DESCRIPTION
67 | \fITree\fP is a recursive directory listing program that produces a depth
68 | indented listing of files, which is colorized ala \fIdircolors\fP if the
69 | \fBLS_COLORS\fP environment variable is set and output is to tty. With no
70 | arguments, \fItree\fP lists the files in the current directory. When directory
71 | arguments are given, \fItree\fP lists all the files and/or directories found in
72 | the given directories each in turn. Upon completion of listing all
73 | files/directories found, \fItree\fP returns the total number of files and/or
74 | directories listed.
75 |
76 | By default, when a symbolic link is encountered, the path that the symbolic
77 | link refers to is printed after the name of the link in the format:
78 | .br
79 |
80 | name -> real-path
81 | .br
82 |
83 | If the `\fB-l\fP' option is given and the symbolic link refers to an actual
84 | directory, then \fItree\fP will follow the path of the symbolic link as if
85 | it were a real directory.
86 | .br
87 |
88 | .SH OPTIONS
89 | \fITree\fP understands the following command line switches:
90 |
91 | .SH LISTING OPTIONS
92 |
93 | .TP
94 | .B -a
95 | All files are printed. By default tree does not print hidden files (those
96 | beginning with a dot `.'). In no event does tree print the file system
97 | constructs `.' (current directory) and `..' (previous directory).
98 | .PP
99 | .TP
100 | .B -d
101 | List directories only.
102 | .PP
103 | .TP
104 | .B -l
105 | Follows symbolic links if they point to directories, as if they were
106 | directories. Symbolic links that will result in recursion are avoided when
107 | detected.
108 | .PP
109 | .TP
110 | .B -f
111 | Prints the full path prefix for each file.
112 | .PP
113 | .TP
114 | .B -x
115 | Stay on the current file-system only. Ala \fBfind \fI-xdev\fP.
116 | .PP
117 | .TP
118 | .B -L \fIlevel\fP
119 | Max display depth of the directory tree.
120 | .PP
121 | .TP
122 | .B -R
123 | Recursively cross down the tree each \fIlevel\fP directories (see \fB-L\fP
124 | option), and at each level outputting to a file named \fB00Tree.html\fP
125 | (ala \fB-o\fP).
126 | .PP
127 | .TP
128 | .B -P \fIpattern\fP
129 | List only those files that match the wild-card \fIpattern\fP. You may have
130 | multiple -P options. Note: you must use the \fI-a\fP option to also consider
131 | those files beginning with a dot `.' for matching. Valid wildcard operators
132 | are `*' (any zero or more characters), `**` (any zero or more characters as
133 | well as null /'s, i.e. /**/ may match a single /), `?' (any single character),
134 | `[...]' (any single character listed between brackets (optional - (dash) for
135 | character range may be used: ex: [A-Z]), and `[^...]' (any single character
136 | not listed in brackets) and `|' separates alternate patterns. A '/' at the
137 | end of the pattern matches directories, but not files.
138 | .PP
139 | .TP
140 | .B -I \fIpattern\fP
141 | Do not list those files that match the wild-card \fIpattern\fP. You may have
142 | multiple -I options. See \fI-P\fP above for information on wildcard patterns.
143 | .PP
144 | .TP
145 | .B --gitignore
146 | Uses git \fB.gitignore\fP files for filtering files and directories. Also
147 | uses \fB$GIT_DIR/info/exclude\fP if present.
148 | .PP
149 | .TP
150 | .B --gitfile\fR[\fB=\fR]\fIfile\fP
151 | Use \fIfile\fP explicitly as a gitignore file.
152 | .PP
153 | .TP
154 | .B --ignore-case
155 | If a match pattern is specified by the \fB-P\fP or \fB-I\fP option, this will
156 | cause the pattern to match without regard to the case of each letter.
157 | .PP
158 | .TP
159 | .B --matchdirs
160 | If a match pattern is specified by the \fB-P\fP option, this will cause the
161 | pattern to be applied to directory names (in addition to filenames). In the
162 | event of a match on the directory name, matching is disabled for the directory's
163 | contents. If the \fB--prune\fP option is used, empty folders that match the
164 | pattern will not be pruned.
165 | .PP
166 | .TP
167 | .B --metafirst
168 | Print the meta-data information at the beginning of the line rather than
169 | after the indentation lines.
170 | .PP
171 | .TP
172 | .B --prune
173 | Makes tree prune empty directories from the output, useful when used in
174 | conjunction with \fB-P\fP or \fB-I\fP. See \fBBUGS AND NOTES\fP below for
175 | more information on this option.
176 | .PP
177 | .TP
178 | .B --info
179 | Prints file comments found in .info files. See \fB.INFO FILES\fP below
180 | for more information on the format of .info files.
181 | .PP
182 | .TP
183 | .B --infofile\fR[\fB=\fR]\fIfile\fP
184 | Use \fIfile\fP explicitly as a info file.
185 | .PP
186 | .TP
187 | .B --noreport
188 | Omits printing of the file and directory report at the end of the tree
189 | listing.
190 | .PP
191 | .TP
192 | .B --charset\fR[\fB=\fR]\fIcharset\fP
193 | Set the character set to use when outputting HTML and for line drawing.
194 | .PP
195 | .TP
196 | .B --filelimit\fR[\fB=\fR]\fI#\fP
197 | Do not descend directories that contain more than \fI#\fP entries.
198 | .PP
199 | .TP
200 | .B --timefmt\fR[\fB=\fR]\fIformat\fP
201 | Prints (implies -D) and formats the date according to the format string
202 | which uses the \fBstrftime\fP(3) syntax.
203 | .PP
204 | .TP
205 | .B -o \fIfilename\fP
206 | Send output to \fIfilename\fP.
207 | .PP
208 |
209 | .SH FILE OPTIONS
210 |
211 | .TP
212 | .B -q
213 | Print non-printable characters in filenames as question marks instead of the
214 | default.
215 | .PP
216 | .TP
217 | .B -N
218 | Print non-printable characters as is instead of as escaped octal numbers.
219 | .PP
220 | .TP
221 | .B -Q
222 | Quote the names of files in double quotes.
223 | .PP
224 | .TP
225 | .B -p
226 | Print the file type and permissions for each file (as per ls -l).
227 | .PP
228 | .TP
229 | .B -u
230 | Print the username, or UID # if no username is available, of the file.
231 | .PP
232 | .TP
233 | .B -g
234 | Print the group name, or GID # if no group name is available, of the file.
235 | .PP
236 | .TP
237 | .B -s
238 | Print the size of each file in bytes along with the name.
239 | .PP
240 | .TP
241 | .B -h
242 | Print the size of each file but in a more human readable way, e.g. appending a
243 | size letter for kilobytes (K), megabytes (M), gigabytes (G), terabytes (T),
244 | petabytes (P) and exabytes (E).
245 | .PP
246 | .TP
247 | .B --si
248 | Like \fB-h\fP but use SI units (powers of 1000) instead.
249 | .PP
250 | .TP
251 | .B --du
252 | For each directory report its size as the accumulation of sizes of all its files
253 | and sub-directories (and their files, and so on). The total amount of used
254 | space is also given in the final report (like the 'du -c' command.) This option
255 | requires tree to read the entire directory tree before emitting it, see
256 | \fBBUGS AND NOTES\fP below. Implies \fB-s\fP.
257 | .PP
258 | .TP
259 | .B -D
260 | Print the date of the last modification time or if \fB-c\fP is used, the last
261 | status change time for the file listed.
262 | .PP
263 | .TP
264 | .B -F
265 | Append a `/' for directories, a `=' for socket files, a `*' for executable
266 | files, a `>' for doors (Solaris) and a `|' for FIFO's, as per ls -F
267 | .PP
268 | .TP
269 | .B --inodes
270 | Prints the inode number of the file or directory
271 | .PP
272 | .TP
273 | .B --device
274 | Prints the device number to which the file or directory belongs
275 | .PP
276 |
277 | .SH SORTING OPTIONS
278 |
279 | .TP
280 | .B -v
281 | Sort the output by version.
282 | .PP
283 | .TP
284 | .B -t
285 | Sort the output by last modification time instead of alphabetically.
286 | .PP
287 | .TP
288 | .B -c
289 | Sort the output by last status change instead of alphabetically. Modifies the
290 | \fB-D\fP option (if used) to print the last status change instead of
291 | modification time.
292 | .PP
293 | .TP
294 | .B -U
295 | Do not sort. Lists files in directory order. Disables \fB--dirsfirst\fP.
296 | .PP
297 | .TP
298 | .B -r
299 | Sort the output in reverse order. This is a meta-sort that alters the above sorts.
300 | This option is disabled when \fB-U\fP is used.
301 | .PP
302 | .TP
303 | .B --dirsfirst
304 | List directories before files. This is a meta-sort that alters the above sorts.
305 | This option is disabled when \fB-U\fP is used.
306 | .PP
307 | .TP
308 | .B --filesfirst
309 | List files before directories. This is a meta-sort that alters the above sorts.
310 | This option is disabled when \fB-U\fP is used.
311 | .PP
312 | .TP
313 | .B --sort\fR[\fB=\fR]\fItype\fR
314 | Sort the output by \fItype\fR instead of name. Possible values are:
315 | \fBctime\fR (\fB-c\fP),
316 | \fBmtime\fR (\fB-t\fB), \fBsize\fR, \fBversion\fR (\fB-v\fR) or \fBnone\fR
317 | (\fB-U\fR).
318 |
319 | .SH GRAPHICS OPTIONS
320 |
321 | .TP
322 | .B -i
323 | Makes tree not print the indentation lines, useful when used in conjunction
324 | with the \fB-f\fP option. Also removes as much whitespace as possible when used
325 | with the \fB-J\fP or \fB-X\fP options.
326 | .PP
327 | .TP
328 | .B -A
329 | Turn on ANSI line graphics hack when printing the indentation lines.
330 | .PP
331 | .TP
332 | .B -S
333 | Turn on CP437 line graphics (useful when using Linux console mode fonts). This
334 | option is now equivalent to `\fB--charset=IBM437\fP' and may eventually be depreciated.
335 | .PP
336 | .TP
337 | .B -n
338 | Turn colorization off always, over-ridden by the \fB-C\fP option, however
339 | overrides CLICOLOR_FORCE if present.
340 | .PP
341 | .TP
342 | .B -C
343 | Turn colorization on always, using built-in color defaults if the LS_COLORS or
344 | TREE_COLORS environment variables are not set. Useful to colorize output to a
345 | pipe.
346 | .PP
347 |
348 | .SH XML/JSON/HTML/HYPERLINKS OPTIONS
349 |
350 | .TP
351 | .B -X
352 | Turn on XML output. Outputs the directory tree as an XML formatted file.
353 | .PP
354 | .TP
355 | .B -J
356 | Turn on JSON output. Outputs the directory tree as a JSON formatted array.
357 | .PP
358 | .TP
359 | .B -H [-]\fIbaseHREF\fP
360 | Turn on HTML output, including HTTP references. Useful for ftp sites.
361 | \fIbaseHREF\fP gives the base ftp location when using HTML output. That is, the
362 | local directory may be `/local/ftp/pub', but it must be referenced as
363 | `ftp://hostname.organization.domain/pub' (\fIbaseHREF\fP should be
364 | `ftp://hostname.organization.domain'). If \fIbaseHREF\fP begins with a dash
365 | (\fB-\fP), then the dash is removed and indicates that tree should remove the
366 | first directory name from the href URL. Hint: don't use ANSI lines with this
367 | option, and don't give more than one directory in the directory list. If you
368 | wish to use colors via CSS style-sheet, use the -C option in addition to this
369 | option to force color output.
370 | .PP
371 | .TP
372 | .B --hintro\fR[\fB=\fR]\fIfile\fP
373 | Use \fIfile\fP as the HTML intro in place of the default one. Use an empty
374 | file or \fI/dev/null\fP to eliminate the intro altogether.
375 | .PP
376 | .TP
377 | .B --houtro\fR[\fB=\fR]\fIfile\fP
378 | Use \fIfile\fP as the HTML outro in place of the default one. Use an empty
379 | file or \fI/dev/null\fP to eliminate the outro altogether.
380 | .PP
381 | .TP
382 | .B -T \fItitle\fP
383 | Sets the title and H1 header string in HTML output mode.
384 | .PP
385 | .TP
386 | .B --nolinks
387 | Turns off hyperlinks in HTML output.
388 | .PP
389 | .TP
390 | .B --hyperlink
391 | Enable OSC 8 terminal hyperlinks for terminals that support them. See \fBBUGS
392 | AND NOTES\fR below.
393 | .PP
394 | .TP
395 | .B --scheme\fR[\fB=\fR]\fIschema\fP
396 | Sets the schema used in the OSC 8 hyperlinks. The default schema is '\fBfile://\fR'.
397 | If the schema omits the colon (:), then \fB://\fR will be appended to the schema.
398 | .TP
399 | .B --authority\fR[\fB=\fR]\fIhostname|authority\fP
400 | Sets the authority (hostname) to use for the OSC 8 hyperlinks. By default the
401 | local hostname of the machine as returned by gethostname() is used as the
402 | authority. A dot (\fB.\fP) or a set of ""'s, sans '=', (i.e. the empty string)
403 | can be used to indicate a null authority.
404 | .PP
405 |
406 | .SH INPUT OPTIONS
407 |
408 | .TP
409 | .B --fromfile
410 | Reads a directory listing from a file rather than the file-system. Paths
411 | provided on the command line are files to read from rather than directories to
412 | search. The dot (.) directory indicates that tree should read paths from
413 | standard input. NOTE: this is only suitable for reading the output of a program
414 | such as find, not 'tree -fi' as symlinks are not distinguished from files that
415 | simply contain ' -> ' as part of the filename unless the \fB--fflinks\fP option
416 | is used.
417 | .PP
418 | .TP
419 | .B --fromtabfile
420 | Like \fB--fromfile\fP, tree reads a directory tree from a text file where the
421 | files are tab indented in a tree like format to indicate the directory nesting
422 | level.
423 | .PP
424 | .TP
425 | .B --fflinks
426 | Processes symbolic link information found in a file, as from the output of
427 | \fB'tree -fi --noreport'\fP. Only the first occurrence of the string \fB' -> '\fP
428 | is used to denote the separation of the filename from the link.
429 | .PP
430 |
431 | .SH MISC OPTIONS
432 |
433 | .TP
434 | .B --opt-toggle
435 | Enables option "toggling". Turns on the ability to toggle options such as -a,
436 | -h, etc. Useful to add to an alias when you wish to disable options enabled in
437 | the alias.
438 | .PP
439 | .TP
440 | .B --help
441 | Outputs a verbose usage listing.
442 | .PP
443 | .TP
444 | .B --version
445 | Outputs the version of tree.
446 | .PP
447 | .TP
448 | .B --
449 | Option processing terminator. No further options will be processed after this.
450 | .PP
451 |
452 | .SH .INFO FILES
453 |
454 | \fB.info\fP files are similar to \.gitignore files, if a .info file is found
455 | while scanning a directory it is read and added to a stack of .info
456 | information. Each file is composed of comments (lines starting with hash marks
457 | (#),) or wild-card patterns which may match a file relative to the directory
458 | the \.info file is found in. If a file should match a pattern, the tab indented
459 | comment that follows the pattern is used as the file comment. A comment is
460 | terminated by a non-tab indented line. Multiple patterns, each to a line, may
461 | share the same comment.
462 |
463 | .br
464 | .SH FILES
465 | \fB/etc/DIR_COLORS\fP System color database.
466 | .br
467 | \fB~/.dircolors\fP Users color database.
468 | .br
469 | \fB.gitignore\fP Git exclusion file
470 | .br
471 | \fB$GIT_DIR/info/exclude\fP Global git file exclusion list
472 | .br
473 | \fB.info\fP File comment file
474 | .br
475 | \fB/usr/share/finfo/global_info\fP Global file comment file
476 |
477 |
478 | .SH ENVIRONMENT
479 | \fBLS_COLORS\fP Color information created by dircolors
480 | .br
481 | \fBTREE_COLORS\fP Uses this for color information over LS_COLORS if it is set.
482 | .br
483 | \fBTREE_CHARSET\fP Character set for tree to use in HTML mode.
484 | .br
485 | \fBCLICOLOR\fP Enables colorization even if TREE_COLORS or LS_COLORS is not set.
486 | .br
487 | \fBCLICOLOR_FORCE\fP Always enables colorization (effectively -C)
488 | .br
489 | \fBNO_COLOR\fP Disable colorization (effectively -n) (see \fBhttps://no-color.org/\fP)
490 | .br
491 | \fBLC_CTYPE\fP Locale for filename output.
492 | .br
493 | \fBLC_TIME\fP Locale for timefmt output, see \fBstrftime\fP(3).
494 | .br
495 | \fBTZ\fP Timezone for timefmt output, see \fBstrftime\fP(3).
496 | .br
497 | \fBSTDDATA_FD\fP Enable the stddata feature, optionally set descriptor to use.
498 |
499 | .SH AUTHOR
500 | Steve Baker (Steve.Baker.llc@gmail.com)
501 | .br
502 | HTML output hacked by Francesc Rocher (rocher@econ.udg.es)
503 | .br
504 | Charsets and OS/2 support by Kyosuke Tokoro (NBG01720@nifty.ne.jp)
505 |
506 | .SH BUGS AND NOTES
507 | Tree does not prune "empty" directories when the -P and -I options are used by
508 | default. Use the --prune option.
509 |
510 | The -h and --si options round to the nearest whole number unlike the ls
511 | implementations which rounds up always.
512 |
513 | Pruning files and directories with the -I, -P and --filelimit options will
514 | lead to incorrect file/directory count reports.
515 |
516 | The --prune and --du options cause tree to accumulate the entire tree in memory
517 | before emitting it. For large directory trees this can cause a significant delay
518 | in output and the use of large amounts of memory.
519 |
520 | The timefmt expansion buffer is limited to a ridiculously large 255 characters.
521 | Output of time strings longer than this will be undefined, but are guaranteed
522 | to not exceed 255 characters.
523 |
524 | XML/JSON trees are not colored, which is a bit of a shame. The jq utility
525 | can colorize the JSON however, just not the filenames by file-type ala LS_COLORS.
526 |
527 | OSC 8 hyperlinks may be poorly supported by your terminal. For my version of
528 | Konsole it is necessary to set the schema to file: (no //) and use a null
529 | authority. It may also be necessary to spend 3.5 hours finding the option to
530 | turn on hyperlinks.
531 |
532 | Probably more.
533 |
534 | As of version 2.0.0, in Linux, tree will attempt to automatically output a
535 | compact JSON tree on file descriptor 3 (what I call stddata,) if present and the
536 | environment variable STDDATA_FD is defined or set to a positive non-zero file
537 | descriptor value to use to output on. It is hoped that some day a better
538 | Linux/Unix shell may take advantage of this feature, though BSON would probably
539 | be a better format for this.
540 |
541 | .SH SEE ALSO
542 | .BR dircolors (1),
543 | .BR ls (1),
544 | .BR find (1),
545 | .BR du (1),
546 | .BR jq (1),
547 | .BR strftime (3),
548 | .BR gitignore (5)
549 |
--------------------------------------------------------------------------------
/CHANGES:
--------------------------------------------------------------------------------
1 | Version 2.2.1 (11/25/2024)
2 | - Yet another brown-paper bag release. I seem to like doing these.
3 | - Fix regression where I free a pointer that should not have been freed due
4 | to a variable renaming that wasn't completed. (Daniel Li / Landon Bourma)
5 | - Put back, in a modified form, HTML href directory path fixing that was
6 | removed in 2.1.2. If the "baseHREF" part after the -H begins with a dash
7 | (-), the dash is removed and the lead directory name is removed from the
8 | href, otherwise it is left as-is. It can be very difficult to know how to
9 | handle the host and directory part given the plethora of protocols and
10 | what-not, so hopefully this should give enough control over that to suit
11 | most requirements.
12 |
13 | Version 2.2.0 (11/24/2024)
14 | - Add option --opt-toggle which turns on the ability to toggle options such
15 | as -a, -p, etc. Useful to add to an alias for turning an option off when
16 | using said alias. (Christoph Anton Mitterer)
17 | - Add --hyperlink option to print OSC 8 terminal hyperlinks for files. Also
18 | adds the --scheme and --authority options to modify the schema and hostname/
19 | authority of the links. (Nicolai Dagestad)
20 | OSC 8 Terminal hyperlinks:
21 | https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
22 | - Maybe finally fix JSON error reporting when unable to open a directory and a
23 | full tree is required, such as when using --du. (Alchemyst@github)
24 | - Fix small rounding error in human readable size (-h) output, where 9.99K is
25 | rounded to 10.0K rather than 10K (Ivan Ivanovich)
26 | - Fix the totals report for sizes when --du option is used. The directory
27 | size total was correct, but the final report was an accumulation of all
28 | the directory totals rather than just the top most directory total.
29 | (Alchemyst@github)
30 | - Add .gitignore file to distribution for those wanting that. (Kenta Arai)
31 | - Add 'none' as a valid --sort option (i.e. -U).
32 | - Add ability to cross compile for Android (freemedom@github)
33 | - List charsets again if --charsets is not given an argument.
34 | - Allow --help and usage to use ANSI bold and italic if colorization is
35 | enabled.
36 | - General code cleanups:
37 | - Removed unused externs where possible.
38 | - Clean up some warnings issued by -Wextra (Kenta Arai)
39 | - Update Makefile to allow CC and the CFLAGS -O3 option to be overridden,
40 | move CPPFLAGS into their own variable, add -Wstrict-prototypes
41 | (David Seifert)
42 | - Long over-due move to stdbool.h, removes custom bool type and changes all
43 | occurrences of TRUE/FALSE to true/false. (David Seifert / others) This
44 | likely makes C99+ even more of a requirement now. Please let me know if
45 | this requires a work-around for your system.
46 | - Went ahead and added -Wconversion to the Makefile as well. This required a
47 | large number of type conversion fixing which may have unexpected side
48 | effects, but should hopefully help with tree safely dealing with absurd
49 | sizes/number of things in the future as this promotes using size_t more.
50 | This probably needs more work to do properly however.
51 | - Apply the const constraint on parameter strings wherever possible.
52 |
53 | Version 2.1.3 (07/09/2024)
54 | - Mostly a brown-paper bag release to fix the below regression and add a
55 | feature I forgot to add.
56 | - Fix regression in search() function that broke --fromfile (Florian Ernst)
57 | (caused by removing too much code while fixing premature sort for
58 | --fromfile)
59 | - Allow the -L option to accept its parameter immediately (with no space)
60 | instead of requiring it be the next option word. (Trevor Gross)
61 |
62 | Version 2.1.2 (07/01/2024)
63 | - Fix issue where --gitignore does not think a pattern with a singular
64 | terminal '/' (indicating it matches only directories,) is a relative path.
65 | (Clinton)
66 | - Don't emit the error 'recursive, not followed' if when using -L, the depth
67 | would prevent descending anyway. This also fixes up a JSON output error
68 | (missing comma) when this happens. (simonpmind)
69 | - Don't prematurely sort files/directories with --from*file. (gitlab @jack6th)
70 | - Various seg-faults fixed (Hanqin Guan (The OSLab of Peking University)):
71 | - Make doubly sure that there is actually a previous path entry when reading
72 | from a tabbed file.
73 | - Make sure there is actually a file entity when applying the link info to
74 | it when reading fromfile using --fflinks.
75 | - Increase space for the path a little in listdir(), just to be sure.
76 | - Make sure that there is no topsort (--dirsfirst / --filesfirst) if there
77 | is no basesort (-U).
78 | - Make sure gittrim() function can handle a null string.
79 | - Update email address to steve.baker.llc@gmail.com from ice@mama.indstate.edu
80 | which has been permanently disabled.
81 |
82 | Version 2.1.1 (05/31/2023)
83 | - Various spelling corrections.
84 | - Fix issue where following links while doing JSON output would lead to
85 | incorrect JSON output. (simonpmind)
86 | - Fix issue where .info patterns relative to the .info file that did not use
87 | a wildcard for matching the prefix were not matching files properly.
88 | (German Lashevich)
89 | - Added support for making trees from tab indented files (--fromtabfile)
90 | (gitlab @AvidSeeker), also cleaned up some other issues in the fromfile
91 | code.
92 | - Fix buffer overflow in listdir() when file names are allowed to be longer
93 | than 256 characters (like when using fromfile.) (Javier Jaramago Fernández)
94 | - If when attempting to open a .gitignore or .info file from a top level
95 | directory and failing, recursively check the parents for such a file. This
96 | stops when successful at opening such a file. This behavior might in the
97 | future be modified to open all such files in all parents to until root is
98 | reached. (Damien Bezborodov) Note that this requires the use of realpath()
99 | which I think may be an issue for some OSes.
100 | - Fix issue where tree would never descend (-l) a symbolic link when a full
101 | tree is gathered (--du/matchdirs/prune) (gitlab @6ramr)
102 |
103 | Version 2.1.0 (12/26/2022)
104 | This is a bit bigger release, due to not realizing that gitlab/github was
105 | not sending me email and ice+tree@mama.indstate.edu was broken. This has
106 | been fixed and I think I have gone through most all of the outstanding issues
107 | that were submitted over the last few months. Note to those that are sending
108 | me git merge requests: because I distribute this in multiple ways and places,
109 | and I tend to manually go over patches and often re-work them in any event, I
110 | will not likely ever directly accept any merge requests. I will still use the
111 | patch information from them for inclusion into the code, so you are more than
112 | welcome to still submit such merges.
113 | - Add support for --info and --gitignore for the --fromfile option.
114 | (Suggested by Piotr Andruszkow)
115 | - Add options --infofile and --gitfile to load .info and .gitignore files
116 | explicitly. Each implies --info or --gitignore respectively.
117 | - Add NULL guard for json_printinfo() and xml_printinfo() (and fix ftype
118 | printing for XML) (Kenta Arai)
119 | - Fix getcharset() to not return a getenv() pointer (fix for ENV34-C issue.)
120 | (Kenta Arai)
121 | - Another attempt at fixing extraneous /'s in HTML URLs/output. (Sebastian
122 | Rose)
123 | - Fixed XML output (Dave Rice)
124 | - Remove the (very outdated) French version of the manpage. Look to
125 | localization projects such as Debian's 'manpages-l10n' for localized
126 | translations. (hmartink)
127 | - Add support for the NO_COLOR environment variable (https://no-color.org/).
128 | Equivalent to the -n option (can be still be overridden with -C).
129 | (Timm Fitschen)
130 | - Removed many C99isms to enable compiling on C90 compilers with fewer
131 | warnings. (Sith Wijesinghe and Matthew Sessions) It should not be
132 | necessary to avoid using a standard that is old enough to drink,
133 | furthermore it is all but impossible to remove the remaining warnings and
134 | have modern features like compound literals. In the meantime I've added
135 | -std=c11 to the default CFLAGS for Linux and will likely not worry about
136 | C90 compatibility going forward unless there is some other reason for it.
137 | - Added a helper function for long command line arguments to clean up option
138 | processing (and fixes the processing for a few of the options such as
139 | --timefmt= (наб?).)
140 | - Added --hintro and --houtro options to select files to use as the HTML
141 | intro and outro. Use /dev/null or an empty file to eliminate them
142 | entirely. This should make it much easier to create your own custom CSS
143 | or embed one or more trees into a web page.
144 | - Defer printing the version until the character set is known so we can use
145 | the linedraw copyright symbol.
146 | - Revert change to the error code to not return an error (code 2) when
147 | attempting to list a non-directory that actually exists. Tree will still
148 | return an error when attempting to list a non-existing directory/file.
149 | - Added option --fflinks which will process symbolic link information from
150 | a file generated with 'tree -if --noreport' when using --fromfile.
151 | (Suggested by Chentao Credungtao)
152 | - Updated the totals reporting code to also include in the total the file or
153 | directory that is being listed. This should make a correct report when
154 | doing something like 'tree *'.
155 |
156 | Version 2.0.4 (09/06/2022)
157 | - Brown paper bag release:
158 | - Fix missing comma in JSON output. (jogbear?)
159 |
160 | Version 2.0.3 (08/26/2022)
161 | - Fix segfault when filelimit is used and tree encounters a directory it
162 | cannot enter. (Kenta Arai)
163 | - Use += when assigning CFLAGS and LDFLAGS in the Makefile allowing
164 | them to be modified by environment variables during make. (Ben Brown)
165 | Possibly assumes GNU make.
166 | - Fixed broken -x option (stops recursing.) (balping)
167 | - Fix use after free (causing segfault) for dir/subdir in list.c (Erik
168 | Skultety / Ben Brown)
169 | - Fixes for .gitignore functionality (Saniya Maheshwari / Mig-hub ? / Carlos
170 | Pinto)
171 | - Fixed * handing in patmatch. Worked almost like ** before, now properly
172 | stops at /'s. These issues were the result of forgetting that patmatch()
173 | was just to match filenames to patterns, not paths.
174 | - Patterns starting with / are actually relative to the .gitignore file,
175 | not the root of the filesystem, go figure.
176 | - Patterns without /'s in .gitignore apply to any file in any directory
177 | under the .gitignore, not just the .gitignore directory
178 | - Remove "All rights reserved" from copyright statements. A left-over from
179 | trees original artistic license.
180 | - Add in --du and --prune to --help output (Nxueyamao?)
181 | - Fixed segfault when an unknown directory is given with -X
182 | - Fixed output up for -X and -J options.
183 | - Remove one reference to strnlen which isn't necessary since it may not
184 | be available on some OS's.
185 |
186 | Version 2.0.2 (02/16/2022)
187 | - Okay, apparently the stddata addition is causing havoc (who knew how many
188 | scripts just haphazardly hand programs random file descriptors, that's
189 | surely not a problem.) Going forward the stddata option will only work
190 | if the environment variable STDDATA_FD is present or set to the descriptor
191 | to produce the JSON output on.
192 | - Fix HTML url output issue. (Maxim Cournoyer) It was definitely broken in
193 | the 2.0.0 release, and this should normalize it with respect to older
194 | versions, however I think it needs to be updated to work better.
195 | - Update MANPATH for OS X (Michiel Beijen)
196 | - Fixed an error with * in the patchmatch code where *foo*bar would match
197 | *foo alone. (Josey Smith)
198 |
199 | Version 2.0.1 (01/03/2022)
200 | - Simplify Makefile and the following changes: prefix -> PREFIX,
201 | BINDIR -> DESTDIR, -O4 -> -O3, mode 644 for man page installation
202 | (Michal Vasilek)
203 | - Make patterns ending in '/' match directories (but not files) for -I / -P
204 | (Michiel Beijen) should also fix issues with --gitignore as well
205 | (Taylor Faubion)
206 | - Fix --gitignore not matching files relative to the path of the .gitignore
207 | (Taylor Faubion) I did say it was hacked together.
208 | - Refactored color.c a bit to simplify the code as a prelude to meta coloring.
209 |
210 | Version 2.0.0 (12/21/2021)
211 | - This started out as a 1.9.0 release but then I got fed up with the
212 | abundance of directory listers (8 in total, 2 each for each output mode).
213 | Nothing is terribly well tested since there are a lot of changes and I would
214 | like to get this out the door finally, please report breakage. This reduced
215 | so much code that all the below additions only resulted in a code base that
216 | is only 54 lines larger than 1.8.0.
217 | - Rolled all the directory listers into 2 functions that call output specific
218 | functions (removes one TODO).
219 | - -R option now recursively calls the emit_tree() function rather than using
220 | system() to re-call tree. Also removes a TODO.
221 | - Adds --info to print information about files/directories from information
222 | found in .info files (removes a maybe do) In HTML output, comments show as
223 | mouse over tooltips, which I imagine will be the most useful use of this
224 | "feature".
225 | - Output un-indented JSON on file descriptor 3 ("stddata") automatically if
226 | file descriptor 3 is present (currently Linux only.) Maybe switch to BSON.
227 | - Always HTML escape filenames in HTML output even when -C is used.
228 | (Eric Pruitt)
229 | - Return a non-zero exit status if there is a failure to open any directory.
230 | - Added --gitignore option to filter out files specified by .gitignore files.
231 | (also reads $GIT_DIR/info/exclude if present.) To facilitate gitignore,
232 | adds support for ** on pattern matching to allow /**/ to match a single /.
233 | This is not well tested and kind of hacked together, so may not work
234 | correctly. (Jake Zimmerman and others)
235 | - Now also supports multiple -I and -P instances. (Michiel Beijen and others)
236 | - Now prints meta data for the top level directory as well.
237 | - Split spaghetti code in main into individual functions.
238 | - Properly sort --fromfile input (Chentao Credungtao via Debian)
239 | - Make tree colorization use reset (rs code in dir_colors,) not normal color
240 | when resetting attributes (Filips Romāns via Debian).
241 | - Honor -n (no color) even if the CLICOLOR_FORCE environment variable is set
242 | (Paul Seyfert)
243 | - Added --metafirst to print the metadata before the indentation lines
244 | (suggested by Richard Mitchell)
245 | - Fix --sort option to not require =
246 | - Defer sorting for --du until the entire sub-directory tree has been
247 | processed.
248 | - Optimized makefile, HP/UX support (Osipov, Michael). Note that this changes
249 | the prefix default to /usr/local, which is becoming required for many
250 | systems now.
251 | - Renamed (the by now very obsolete) doc/tree.1.fr to doc/tree.fr.1 (Jonas
252 | Stein)
253 | - Fix JSON string escaping such that it is not using the HTML escaping (Fox
254 | & others)
255 | - Add --filesfirst option (John A. Fedoruk). Cleaned up sorting code to make
256 | --dirsfirst and --filesfirst top level meta-sorts.
257 | - "arial" not "ariel" (Mark), HTML style-sheet cleaned up in any event.
258 | - Deprecate using local -DLINUX / -DCYGWIN and use the OS provided
259 | __linux__ or __CYGWIN__ (Jonas Stein)
260 | - XML/HTML/JSON output needs to be mutually exclusive, last command line
261 | switch wins. (Sergei Maximov)
262 | - Make sure we use xmalloc instead of malloc in a number of places (Tomáš
263 | Beránek)
264 |
265 | Version 1.8.0 (11/16/2018)
266 | - Added an experimental --fromfile option (suggested by several people.)
267 | This may eventually be replaced or supplimented by a --fromjson option.
268 | - Added support for BSD's CLICOLOR and CLICOLOR_FORCE environment variables.
269 | (Suggested by Alyssa Ross)
270 | - Use strftime() exclusively when formatting date/time to respect locale.
271 | - Some man page fixes and cleanups curtsey of Kirill Kolyshkin
272 | - Update BINDIR in Makefile for MacOS X -- It is not allowed to install
273 | programs to /usr/bin on MacOS X any longer due to System Integrity
274 | Protection (SIP) (Shawn Mehan)
275 | - Misc patches from Jacob Wahlgren:
276 | - Improved command line switch error reporting.
277 | - Symbolic links not displayed if a -P pattern is active
278 | - Missing argument error reporting fixes on long format switches.
279 | - Fixed JSON output hanging commas (John Lane, Tad, others)
280 | - JSON size output ignored -h/--si flags (Wagner Camarao)
281 | - Fixed issue with malformed multibyte string handling. (Mantas
282 | Mikulėnas)
283 | - Fixed issue where mbstowcs() fails to null terminate the string due to
284 | improper UTF-8 encoding leading to garbage being printed. (Nick Craig-Wood)
285 | - Found a bug where the wrong inode (and device) information would be printed
286 | for symbolic links. (Stephan Gabert)
287 |
288 | Version 1.7.0 (04/23/2014)
289 | - Allow user/group names up to 32 characters before clipping.
290 | - Made -i compress XML and JSON output as much as possible by eliminating
291 | extraneous whitespace.
292 | - Added --caseinsensitive (renamed --ignore-case ala grep) flag so patterns
293 | match without regard to case, courtesy of Jason A Donenfeld.
294 | - Added --matchdirs option courtesy of Brian Mattern & Jason A. Donenfeld
295 | .
296 | - Fixed possible buffer overflow on large uid/gids w/o user names/group
297 | names (Alexandre Wendling )
298 | - Added JSON support courtesy of Florian Sesser .
299 | - Fixed formatting error with HTML output when -L 1 specified. (Sascha Zorn
300 | )
301 | - Added file size sorting (Philipp M?ller )
302 | - Added '--sort[=]' option, ala ls.
303 | - Fixed OS X makefile problems (Ryan Hollis )
304 | - Fixed possible memory overflow in read_dir (path/lbuf not equal in size
305 | to pathsize/lbufsize.) (Han Hui )
306 | - Fix S_ISDOOR/S_IFDOOR spelling mistake for Solaris. (Tim Mooney
307 | )
308 | - Make tree more reliably detect UTF-8 locales. (Mantas Mikulnas
309 | and others.)
310 | - Return non-zero exit status on option errors, print usage to stdout when
311 | not an error, add the posix '--' option terminator, Change -S description
312 | to mean CP437 (console) output codes, not ASCII. (Ivan Shmakov
313 | )
314 |
315 | Version 1.6.0 (05/24/11)
316 | - Re-org of code into multiple files, split HTML and Unix listdir() into
317 | separate functions, various code cleanups and optimizations.
318 | - Fixed a memory leak in listdir() when memory was allocated early and not
319 | freed before function exit.
320 | - Fixed possible buffer overflow where symbolic links are followed.
321 | - Fixed links printing "argetm" before the name of the link when the LINK
322 | setting for DIR_COLORS is set to target (Markus Schnalke
323 | )
324 | - More fully support dir colors -- added support for su, sg, tw, ow, & st
325 | options (and "do" in theory).
326 | - Use the environment variable "TREE_COLORS" instead of "LS_COLORS" for
327 | color information if it exists.
328 | - Added --si flag to print filesizes in SI (powers of 1000) units (Ulrich
329 | Eckhardt)
330 | - Added -Q to quote filenames in double quotes. Does not override -N or -q.
331 | - Control characters are no longer printed in carrot notation, but as
332 | backslashed octal, ala ls, except for codes 7-13 which are printed as
333 | \a, \b, \t, \n, \v, \f and \r respectively. Spaces and backslashes are
334 | also now backslashed as per ls, for better input to scripts unless -Q
335 | is in use (where "'s are backslashed.) (Ujjwal Kumar)
336 | - Added -U for unsorted listings (directory order).
337 | - Added -c for sorting by last status change (ala ls -c).
338 | - --dirsfirst is now a meta-sort and does not override -c, -v, -r or -t, but
339 | is disabled by -U.
340 | - After many requests, added the ability to process the entire tree before
341 | emitting output. Used for the new options --du, which works like the du
342 | command: sums the amount of space under each directory and prints a total
343 | amount used in the report and the --prune option which will prune all empty
344 | directories from the output (makes the -P option output much more readable.)
345 | It should be noted that this will be slow to output when processing large
346 | directory trees and can consume copious amounts of memory, use at your own
347 | peril.
348 | - Added -X option to emit the directory tree in XML format (turns colorization
349 | off always.)
350 | - Added --timefmt option to specify the format of time display (implies -D).
351 | Uses the strftime format.
352 |
353 | Version 1.5.3 (11/24/09)
354 | - Properly quote directories for the system command when tree is relaunched
355 | using the -R option.
356 | - Fixed possible indentation problem if dirs[*] is not properly zeroed
357 | (Martin Nagy).
358 | - Use strcoll() instead of strcmp() to sort files based on locale if set.
359 | - Change "const static" to "static const" to remove some compiler warnings
360 | for Solaris (Kamaraju Kusumanchi).
361 | - Actually use TREE_CHARSET if it's defined.
362 | - Automatically select UTF-8 charset if TREE_CHARSET is not set, and the
363 | locale is set to *UTF-8 (overridden with --charset option.)
364 |
365 | Version 1.5.2.2 (01/22/09)
366 | - Set locale before checking MB_CUR_MAX.
367 | - Added HP-NonStop platform support (Craig McDaniel )
368 | - Fixed to support 32 bit UID/GIDs.
369 | - Added Solaris build options to Makefile (edit and uncomment to use).
370 | Provided by Wang Quanhong
371 |
372 | Version 1.5.2.1 (08/29/08)
373 | - Added strverscmp.c file for os's without strverscmp. Source file is
374 | attributed to: Jean-Franois Bignolles
375 | - Try different approach to MB_CUR_MAX problem.
376 | - Changed the argument to printit() to be signed char to avoid warnings.
377 |
378 | Version 1.5.2 (06/06/08)
379 | - Added --filelimit X option to not descend directories that have more than
380 | X number of files in them.
381 | - Added -v option for version sorting (also called natural sorting) ala ls.
382 |
383 | Version 1.5.1.2 (06/04/08)
384 | - Fixed compile issues related to MB_CUR_MAX on non-linux machines.
385 | - Removed unecessary features.h
386 |
387 | Version 1.5.1.1 (06/11/07)
388 | - Regression in HTML output, fixed formatting issues.
389 |
390 | Version 1.5.1 (?)
391 | - Remove extraneous / at end of user input directory names when using -f
392 | option (Zurd)
393 | - List available charsets if --charset option is missing charset argument.
394 | - Fixed --charset option processing bug.
395 | - Fixed missing
's when -i option used with -H.
396 | - Added -h option for human readable output.
397 | - Colorization bugfix for special files and directories (make tree behave as
398 | ls does)
399 |
400 | Version 1.5.0 (08/15/04)
401 | - Added -T option to change title and H1 header in HTML output.
402 | - Added -r option to reverse alpha sort output, ala. 'ls -r'.
403 | - '|' wildcard support added by David MacMahon .
404 | - Remove extraneous '/' at the end of dirs and dir-symlinks in HTML output.
405 | - Removed several possible overflow problems by dynamically allocating
406 | arrays in several places.
407 | - Better support for Locales and printing utf-8 encoded characters in
408 | filenames (still hackish).
409 | - Fixed -t to alphasort files with same time-stamps.
410 | - Fixed encoding of filenames in HTML output, Kyosuke and others.
411 | - Patches by Kyosuke Tokoro :
412 | - Now, runs OS/2 systems.
413 | + Print the file attributes in 'adhrs' format for each file, instead
414 | of the protections in 'drwxrwxrwx' format, when running the tree
415 | on OS/2 with option -p.
416 | - Added --charset option, to specify which character set is used for
417 | output.
418 | + You can specify any IANA registered character set name. But I have
419 | tested only following character sets:
420 | Shift_JIS EUC-JP IBM850
421 | UTF-8 ISO-8859-1 US-ASCII
422 | + Now, `-S' option is equal to `--charset=IBM437'.
423 | + When running on OS/2 systems, the default value of this option
424 | is according to current codepage. On the other systems, no default.
425 | - Change font-weight to font-size in CSS .VERSION.
426 | - Change version to standard major.minor.patch format.
427 | - Switch from artistic license to GPLv2.
428 |
429 | Version 1.4 (02/21/02 (b1), 03/24/02 (b2), 02/06/03 (b3))
430 | - Added large file support under Linux.
431 | - Fixed crashing on missing command line arguments.
432 | - Fixed several memory leaks
433 | - Added --dirsfirst option to list directories first.
434 | - Fixed formatting error when unable to open directories.
435 | - Fixed bug in parse_dir_colors().
436 | - Changed -I to also ignore directories.
437 | - Added --nolinks command to turn off hyperlinks with the HTML output.
438 | - Fixed several memory leaks in listdir().
439 | - Some additional code cleanup in listdir().
440 | - Some systems may define TRUE/FALSE, so don't create the enums for TRUE
441 | and FALSE if that's the case.
442 | - Fixed over-allocation bug in read_dir().
443 | - Added crude beginnings of color output for HTML via CSS (Ted Tiberio
444 | ttiberio@rochester.rr.com).
445 | - Fixed buffer overflow problem in dircolors parsing.
446 | - Fixed recursive symlink detection.
447 | - Added --inodes and --device options.
448 | - Added --noreport option.
449 |
450 | Version 1.3 (02/15/99)
451 | - Fixed long pathname problem by dynamically allocating the path.
452 | - Added recursive symlink detection.
453 | - Added --help and --version options.
454 | - When -C is used and LS_COLORS is undefined, tree uses a default color
455 | scheme (thus -C always forces color output now).
456 | - Added -S to show ASCII tree lines (Gerald Scheidl)
457 | - Made tree more portable (Guido Socher and others)
458 |
459 | Following options courtesy of Francesc Rocher:
460 | - Added -o to redirect the output.
461 | - Added -H to print the tree in HTML format.
462 | - Added -L to set the maximum level of directories to print.
463 | - Added -R to recursively restart the search at the level given by `-L'
464 | option (adding as well `-o 00Tree.html').
465 |
466 | Version 1.2 (01/05/97)
467 | - Added -D to print the date of the last modification.
468 | - Added -t option to sort by last modification time (ala ls -t).
469 | - Added -I , similar to the -P option except tree does not print
470 | those files which match the pattern.
471 | - Made tree print non-printable characters in filenames in standard unix
472 | carrot notation.
473 | - Added -N option to make tree print filenames without any processing.
474 | - Added -q option to make tree print non-printable characters in filenames
475 | as question marks.
476 | - Added `|' to -F output and made it only print character type after the
477 | link on sym-links, not on the symlink name itself.
478 | - Added -u option to display username/uid, and -g option to display group
479 | name/gid.
480 | - Fully (pass the salt) implemented dircolors support.
481 |
482 | Version 1.1 (07/09/96)
483 | - Made some changes to the Makefile to insure proper installation and for
484 | multi-architecture support and a bug-fix.
485 | - Made root directory colorized if dircolors is enabled.
486 | - Put []'s around permission and size info, 'cause I think it looks better.
487 | - Added -A option to enable ANSI-lines hack.
488 | - Added some sanity checks for dircolors support.
489 | - Added -P to list only those files that match the wildcard
490 | given in .
491 | - Fixed error where relative symbolic links to directories would not be
492 | followed when the -l option was used.
493 | - Made uid 0 the same as anyone else (-a was default for uid 0)
494 | - Added -x directive to stay on one filesystem (ala find -xdev).
495 |
496 | Version 1.0 (??/??/90?)
497 | - The original, a model of perfection...
498 |
--------------------------------------------------------------------------------
/tree.c:
--------------------------------------------------------------------------------
1 | /* $Copyright: $
2 | * Copyright (c) 1996 - 2024 by Steve Baker (steve.baker.llc@gmail.com)
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 | */
18 |
19 | #include "tree.h"
20 |
21 | char *version = "$Version: $ tree v2.2.1 %s 1996 - 2024 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $";
22 | char *hversion= "\t\t tree v2.2.1 %s 1996 - 2024 by Steve Baker and Thomas Moore
\n"
23 | "\t\t HTML output hacked and copyleft %s 1998 by Francesc Rocher
\n"
24 | "\t\t JSON output hacked and copyleft %s 2014 by Florian Sesser
\n"
25 | "\t\t Charsets / OS/2 support %s 2001 by Kyosuke Tokoro\n";
26 |
27 | /* Globals */
28 | bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
29 | bool qflag, Nflag, Qflag, Dflag, inodeflag, devflag, hflag, Rflag;
30 | bool Hflag, siflag, cflag, Xflag, Jflag, duflag, pruneflag, hyperflag;
31 | bool noindent, force_color, nocolor, xdev, noreport, nolinks;
32 | bool ignorecase, matchdirs, fromfile, metafirst, gitignore, showinfo;
33 | bool reverse, fflinks, htmloffset;
34 | int flimit;
35 |
36 | struct listingcalls lc;
37 |
38 | int pattern = 0, maxpattern = 0, ipattern = 0, maxipattern = 0;
39 | char **patterns = NULL, **ipatterns = NULL;
40 |
41 | char *host = NULL, *title = "Directory Tree", *sp = " ", *_nl = "\n";
42 | char *Hintro = NULL, *Houtro = NULL, *scheme = "file://", *authority = NULL;
43 | char *file_comment = "#", *file_pathsep = "/";
44 | char *timefmt = NULL;
45 | const char *charset = NULL;
46 |
47 | struct _info **(*getfulltree)(char *d, u_long lev, dev_t dev, off_t *size, char **err) = unix_getfulltree;
48 | /* off_t (*listdir)(char *, int *, int *, u_long, dev_t) = unix_listdir; */
49 | int (*basesort)(struct _info **, struct _info **) = alnumsort;
50 | int (*topsort)(struct _info **, struct _info **) = NULL;
51 |
52 | char *sLevel, *curdir;
53 | FILE *outfile = NULL;
54 | int *dirs;
55 | ssize_t Level;
56 | size_t maxdirs;
57 | int errors;
58 |
59 | char xpattern[PATH_MAX];
60 |
61 | int mb_cur_max;
62 |
63 | #ifdef __EMX__
64 | const u_short ifmt[]={ FILE_ARCHIVED, FILE_DIRECTORY, FILE_SYSTEM, FILE_HIDDEN, FILE_READONLY, 0};
65 | #else
66 | #ifdef S_IFPORT
67 | const mode_t ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, S_IFDOOR, S_IFPORT, 0};
68 | const char fmt[] = "-dlcbspDP?";
69 | const char *ftype[] = {"file", "directory", "link", "char", "block", "socket", "fifo", "door", "port", "unknown", NULL};
70 | #else
71 | const mode_t ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, 0};
72 | const char fmt[] = "-dlcbsp?";
73 | const char *ftype[] = {"file", "directory", "link", "char", "block", "socket", "fifo", "unknown", NULL};
74 | #endif
75 | #endif
76 |
77 | struct sorts {
78 | char *name;
79 | int (*cmpfunc)(struct _info **, struct _info **);
80 | } sorts[] = {
81 | {"name", alnumsort},
82 | {"version", versort},
83 | {"size", fsizesort},
84 | {"mtime", mtimesort},
85 | {"ctime", ctimesort},
86 | {"none", NULL},
87 | {NULL, NULL}
88 | };
89 |
90 | /* Externs */
91 | /* hash.c */
92 | extern struct xtable *gtable[256], *utable[256];
93 | extern struct inotable *itable[256];
94 |
95 | /* color.c */
96 | extern bool colorize, ansilines, linktargetcolor;
97 | extern char *leftcode, *rightcode, *endcode;
98 | extern const struct linedraw *linedraw;
99 |
100 | /* Time to switch to getopt()? */
101 | char *long_arg(char *argv[], size_t i, size_t *j, size_t *n, char *prefix) {
102 | char *ret = NULL;
103 | size_t len = strlen(prefix);
104 |
105 | if (!strncmp(prefix,argv[i], len)) {
106 | *j = len;
107 | if (*(argv[i]+(*j)) == '=') {
108 | if (*(argv[i]+ (++(*j)))) {
109 | ret=(argv[i] + (*j));
110 | *j = strlen(argv[i])-1;
111 | } else {
112 | fprintf(stderr,"tree: Missing argument to %s=\n", prefix);
113 | if (strcmp(prefix, "--charset=") == 0) initlinedraw(true);
114 | exit(1);
115 | }
116 | } else if (argv[*n] != NULL) {
117 | ret = argv[*n];
118 | (*n)++;
119 | *j = strlen(argv[i])-1;
120 | } else {
121 | fprintf(stderr,"tree: Missing argument to %s\n", prefix);
122 | if (strcmp(prefix, "--charset") == 0) initlinedraw(true);
123 | exit(1);
124 | }
125 | }
126 | return ret;
127 | }
128 |
129 | int main(int argc, char **argv)
130 | {
131 | struct ignorefile *ig;
132 | struct infofile *inf;
133 | char **dirname = NULL;
134 | size_t i, j=0, k, n, p = 0, q = 0;
135 | bool optf = true;
136 | char *stmp, *outfilename = NULL, *arg;
137 | char *stddata_fd;
138 | bool needfulltree, showversion = false, opt_toggle = false;
139 |
140 | aflag = dflag = fflag = lflag = pflag = sflag = Fflag = uflag = gflag = false;
141 | Dflag = qflag = Nflag = Qflag = Rflag = hflag = Hflag = siflag = cflag = false;
142 | noindent = force_color = nocolor = xdev = noreport = nolinks = reverse = false;
143 | ignorecase = matchdirs = inodeflag = devflag = Xflag = Jflag = fflinks = false;
144 | duflag = pruneflag = metafirst = gitignore = hyperflag = htmloffset = false;
145 |
146 | flimit = 0;
147 | dirs = xmalloc(sizeof(int) * (size_t)(maxdirs=PATH_MAX));
148 | memset(dirs, 0, sizeof(int) * (size_t)maxdirs);
149 | dirs[0] = 0;
150 | Level = -1;
151 |
152 | setlocale(LC_CTYPE, "");
153 | setlocale(LC_COLLATE, "");
154 |
155 | charset = getcharset();
156 | if (charset == NULL &&
157 | (strcmp(nl_langinfo(CODESET), "UTF-8") == 0 ||
158 | strcmp(nl_langinfo(CODESET), "utf8") == 0)) {
159 | charset = "UTF-8";
160 | }
161 |
162 | lc = (struct listingcalls){
163 | null_intro, null_outtro, unix_printinfo, unix_printfile, unix_error, unix_newline,
164 | null_close, unix_report
165 | };
166 |
167 | /* Still a hack, but assume that if the macro is defined, we can use it: */
168 | #ifdef MB_CUR_MAX
169 | mb_cur_max = (int)MB_CUR_MAX;
170 | #else
171 | mb_cur_max = 1;
172 | #endif
173 |
174 | #ifdef __linux__
175 | /* Output JSON automatically to "stddata" if present: */
176 | stddata_fd = getenv(ENV_STDDATA_FD);
177 | if (stddata_fd != NULL) {
178 | int std_fd = atoi(stddata_fd);
179 | if (std_fd <= 0) std_fd = STDDATA_FILENO;
180 | if (fcntl(std_fd, F_GETFD) >= 0) {
181 | Jflag = noindent = true;
182 | _nl = "";
183 | lc = (struct listingcalls){
184 | json_intro, json_outtro, json_printinfo, json_printfile, json_error, json_newline,
185 | json_close, json_report
186 | };
187 | outfile = fdopen(std_fd, "w");
188 | }
189 | }
190 | #endif
191 |
192 | memset(utable,0,sizeof(utable));
193 | memset(gtable,0,sizeof(gtable));
194 | memset(itable,0,sizeof(itable));
195 |
196 | for(n=i=1;i<(size_t)argc;i=n) {
197 | n++;
198 | if (optf && argv[i][0] == '-' && argv[i][1]) {
199 | for(j=1;argv[i][j];j++) {
200 | switch(argv[i][j]) {
201 | case 'N':
202 | Nflag = (opt_toggle? !Nflag : true);
203 | break;
204 | case 'q':
205 | qflag = (opt_toggle? !qflag : true);
206 | break;
207 | case 'Q':
208 | Qflag = (opt_toggle? !Qflag : true);
209 | break;
210 | case 'd':
211 | dflag = (opt_toggle? !dflag : true);
212 | break;
213 | case 'l':
214 | lflag = (opt_toggle? !lflag : true);
215 | break;
216 | case 's':
217 | sflag = (opt_toggle? !sflag : true);
218 | break;
219 | case 'h':
220 | /* Assume they also want -s */
221 | sflag = (hflag = (opt_toggle? !hflag : true));
222 | break;
223 | case 'u':
224 | uflag = (opt_toggle? !uflag : true);
225 | break;
226 | case 'g':
227 | gflag = (opt_toggle? !gflag : true);
228 | break;
229 | case 'f':
230 | fflag = (opt_toggle? !fflag : true);
231 | break;
232 | case 'F':
233 | Fflag = (opt_toggle? !Fflag : true);
234 | break;
235 | case 'a':
236 | aflag = (opt_toggle? !aflag : true);
237 | break;
238 | case 'p':
239 | pflag = (opt_toggle? !pflag : true);
240 | break;
241 | case 'i':
242 | noindent = (opt_toggle? !noindent : true);
243 | _nl = "";
244 | break;
245 | case 'C':
246 | force_color = (opt_toggle? !force_color : true);
247 | break;
248 | case 'n':
249 | nocolor = (opt_toggle? !nocolor : true);
250 | break;
251 | case 'x':
252 | xdev = (opt_toggle? !xdev : true);
253 | break;
254 | case 'P':
255 | if (argv[n] == NULL) {
256 | fprintf(stderr,"tree: Missing argument to -P option.\n");
257 | exit(1);
258 | }
259 | if (pattern >= maxpattern-1) patterns = xrealloc(patterns, sizeof(char *) * (size_t)(maxpattern += 10));
260 | patterns[pattern++] = argv[n++];
261 | patterns[pattern] = NULL;
262 | break;
263 | case 'I':
264 | if (argv[n] == NULL) {
265 | fprintf(stderr,"tree: Missing argument to -I option.\n");
266 | exit(1);
267 | }
268 | if (ipattern >= maxipattern-1) ipatterns = xrealloc(ipatterns, sizeof(char *) * (size_t)(maxipattern += 10));
269 | ipatterns[ipattern++] = argv[n++];
270 | ipatterns[ipattern] = NULL;
271 | break;
272 | case 'A':
273 | ansilines = (opt_toggle? !ansilines : true);
274 | break;
275 | case 'S':
276 | charset = "IBM437";
277 | break;
278 | case 'D':
279 | Dflag = (opt_toggle? !Dflag : true);
280 | break;
281 | case 't':
282 | basesort = mtimesort;
283 | break;
284 | case 'c':
285 | basesort = ctimesort;
286 | cflag = true;
287 | break;
288 | case 'r':
289 | reverse = (opt_toggle? !reverse : true);
290 | break;
291 | case 'v':
292 | basesort = versort;
293 | break;
294 | case 'U':
295 | basesort = NULL;
296 | break;
297 | case 'X':
298 | Xflag = true;
299 | Hflag = Jflag = false;
300 | lc = (struct listingcalls){
301 | xml_intro, xml_outtro, xml_printinfo, xml_printfile, xml_error, xml_newline,
302 | xml_close, xml_report
303 | };
304 | break;
305 | case 'J':
306 | Jflag = true;
307 | Xflag = Hflag = false;
308 | lc = (struct listingcalls){
309 | json_intro, json_outtro, json_printinfo, json_printfile, json_error, json_newline,
310 | json_close, json_report
311 | };
312 | break;
313 | case 'H':
314 | Hflag = true;
315 | Xflag = Jflag = false;
316 | lc = (struct listingcalls){
317 | html_intro, html_outtro, html_printinfo, html_printfile, html_error, html_newline,
318 | html_close, html_report
319 | };
320 | if (argv[n] == NULL) {
321 | fprintf(stderr,"tree: Missing argument to -H option.\n");
322 | exit(1);
323 | }
324 | host = argv[n++];
325 | k = strlen(host)-1;
326 | if (host[0] == '-') {
327 | htmloffset = true;
328 | host++;
329 | }
330 | /* Allows a / if that is the only character as the 'host': */
331 | // if (k && host[k] == '/') host[k] = '\0';
332 | sp = " ";
333 | break;
334 | case 'T':
335 | if (argv[n] == NULL) {
336 | fprintf(stderr,"tree: Missing argument to -T option.\n");
337 | exit(1);
338 | }
339 | title = argv[n++];
340 | break;
341 | case 'R':
342 | Rflag = (opt_toggle? !Rflag : true);
343 | break;
344 | case 'L':
345 | if (isdigit(argv[i][j+1])) {
346 | for(k=0; (argv[i][j+1+k] != '\0') && (isdigit(argv[i][j+1+k])) && (k < PATH_MAX-1); k++) {
347 | xpattern[k] = argv[i][j+1+k];
348 | }
349 | xpattern[k] = '\0';
350 | j += k;
351 | sLevel = xpattern;
352 | } else {
353 | if ((sLevel = argv[n++]) == NULL) {
354 | fprintf(stderr,"tree: Missing argument to -L option.\n");
355 | exit(1);
356 | }
357 | }
358 | Level = (int)strtoul(sLevel,NULL,0)-1;
359 | if (Level < 0) {
360 | fprintf(stderr,"tree: Invalid level, must be greater than 0.\n");
361 | exit(1);
362 | }
363 | break;
364 | case 'o':
365 | if (argv[n] == NULL) {
366 | fprintf(stderr,"tree: Missing argument to -o option.\n");
367 | exit(1);
368 | }
369 | outfilename = argv[n++];
370 | break;
371 | case '-':
372 | if (j == 1) {
373 | if (!strcmp("--", argv[i])) {
374 | optf = false;
375 | break;
376 | }
377 | /* Long options that don't take parameters should just use strcmp: */
378 | if (!strcmp("--help",argv[i])) {
379 | usage(2);
380 | exit(0);
381 | }
382 | if (!strcmp("--version",argv[i])) {
383 | j = strlen(argv[i])-1;
384 | showversion = true;
385 | break;
386 | }
387 | if (!strcmp("--inodes",argv[i])) {
388 | j = strlen(argv[i])-1;
389 | inodeflag = (opt_toggle? !inodeflag : true);
390 | break;
391 | }
392 | if (!strcmp("--device",argv[i])) {
393 | j = strlen(argv[i])-1;
394 | devflag = (opt_toggle? !devflag : true);
395 | break;
396 | }
397 | if (!strcmp("--noreport",argv[i])) {
398 | j = strlen(argv[i])-1;
399 | noreport = (opt_toggle? !noreport : true);
400 | break;
401 | }
402 | if (!strcmp("--nolinks",argv[i])) {
403 | j = strlen(argv[i])-1;
404 | nolinks = (opt_toggle? !nolinks : true);
405 | break;
406 | }
407 | if (!strcmp("--dirsfirst",argv[i])) {
408 | j = strlen(argv[i])-1;
409 | topsort = dirsfirst;
410 | break;
411 | }
412 | if (!strcmp("--filesfirst",argv[i])) {
413 | j = strlen(argv[i])-1;
414 | topsort = filesfirst;
415 | break;
416 | }
417 | if ((arg = long_arg(argv, i, &j, &n, "--filelimit")) != NULL) {
418 | flimit = atoi(arg);
419 | break;
420 | }
421 | if ((arg = long_arg(argv, i, &j, &n, "--charset")) != NULL) {
422 | charset = arg;
423 | break;
424 | }
425 | if (!strcmp("--si", argv[i])) {
426 | j = strlen(argv[i])-1;
427 | sflag = hflag = siflag = (opt_toggle? !siflag : true);
428 | break;
429 | }
430 | if (!strcmp("--du",argv[i])) {
431 | j = strlen(argv[i])-1;
432 | sflag = duflag = (opt_toggle? !duflag : true);
433 | break;
434 | }
435 | if (!strcmp("--prune",argv[i])) {
436 | j = strlen(argv[i])-1;
437 | pruneflag = (opt_toggle? !pruneflag : true);
438 | break;
439 | }
440 | if ((arg = long_arg(argv, i, &j, &n, "--timefmt")) != NULL) {
441 | timefmt = scopy(arg);
442 | Dflag = true;
443 | break;
444 | }
445 | if (!strcmp("--ignore-case",argv[i])) {
446 | j = strlen(argv[i])-1;
447 | ignorecase = (opt_toggle? !ignorecase : true);
448 | break;
449 | }
450 | if (!strcmp("--matchdirs",argv[i])) {
451 | j = strlen(argv[i])-1;
452 | matchdirs = (opt_toggle? !matchdirs : true);
453 | break;
454 | }
455 | if ((arg = long_arg(argv, i, &j, &n, "--sort")) != NULL) {
456 | basesort = NULL;
457 | for(k=0;sorts[k].name;k++) {
458 | if (strcasecmp(sorts[k].name,arg) == 0) {
459 | basesort = sorts[k].cmpfunc;
460 | break;
461 | }
462 | }
463 | if (sorts[k].name == NULL) {
464 | fprintf(stderr,"tree: Sort type '%s' not valid, should be one of: ", arg);
465 | for(k=0; sorts[k].name; k++)
466 | printf("%s%c", sorts[k].name, sorts[k+1].name? ',': '\n');
467 | exit(1);
468 | }
469 | break;
470 | }
471 | if (!strcmp("--fromtabfile", argv[i])) {
472 | j = strlen(argv[i])-1;
473 | fromfile=true;
474 | getfulltree = tabedfile_getfulltree;
475 | break;
476 | }
477 | if (!strcmp("--fromfile",argv[i])) {
478 | j = strlen(argv[i])-1;
479 | fromfile=true;
480 | getfulltree = file_getfulltree;
481 | break;
482 | }
483 | if (!strcmp("--metafirst",argv[i])) {
484 | j = strlen(argv[i])-1;
485 | metafirst = (opt_toggle? !metafirst : true);
486 | break;
487 | }
488 | if ((arg = long_arg(argv, i, &j, &n, "--gitfile")) != NULL) {
489 | gitignore=true;
490 | ig = new_ignorefile(arg, false);
491 | if (ig != NULL) push_filterstack(ig);
492 | else {
493 | fprintf(stderr,"tree: Could not load gitignore file\n");
494 | exit(1);
495 | }
496 | break;
497 | }
498 | if (!strcmp("--gitignore",argv[i])) {
499 | j = strlen(argv[i])-1;
500 | gitignore = (opt_toggle? !gitignore : true);
501 | break;
502 | }
503 | if (!strcmp("--info",argv[i])) {
504 | j = strlen(argv[i])-1;
505 | showinfo = (opt_toggle? !showinfo : true);
506 | break;
507 | }
508 | if ((arg = long_arg(argv, i, &j, &n, "--infofile")) != NULL) {
509 | showinfo = true;
510 | inf = new_infofile(arg, false);
511 | if (inf != NULL) push_infostack(inf);
512 | else {
513 | fprintf(stderr,"tree: Could not load infofile\n");
514 | exit(1);
515 | }
516 | break;
517 | }
518 | if ((arg = long_arg(argv, i, &j, &n, "--hintro")) != NULL) {
519 | Hintro = scopy(arg);
520 | break;
521 | }
522 | if ((arg = long_arg(argv, i, &j, &n, "--houtro")) != NULL) {
523 | Houtro = scopy(arg);
524 | break;
525 | }
526 | if (!strcmp("--fflinks",argv[i])) {
527 | j = strlen(argv[i])-1;
528 | fflinks = (opt_toggle? !fflinks : true);
529 | break;
530 | }
531 | if (!strcmp("--hyperlink", argv[i])) {
532 | j = strlen(argv[i])-1;
533 | hyperflag = (opt_toggle? !hyperflag : true);
534 | break;
535 | }
536 | if ((arg = long_arg(argv, i, &j, &n, "--scheme")) != NULL) {
537 | if (strchr(arg, ':') == NULL) {
538 | sprintf(xpattern, "%s://", arg);
539 | arg = scopy(xpattern);
540 | } else scheme = scopy(arg);
541 | break;
542 | }
543 | if ((arg = long_arg(argv, i, &j, &n, "--authority")) != NULL) {
544 | // I don't believe that . by itself can be a valid hostname,
545 | // so it will do as a null authority.
546 | if (strcmp(arg, ".") == 0) authority = scopy("");
547 | else authority = scopy(arg);
548 | break;
549 | }
550 | if (!strcmp("--opt-toggle", argv[i])) {
551 | j = strlen(argv[i])-1;
552 | opt_toggle = !opt_toggle;
553 | break;
554 | }
555 |
556 | fprintf(stderr,"tree: Invalid argument `%s'.\n",argv[i]);
557 | usage(1);
558 | exit(1);
559 | }
560 | /* Falls through */
561 | default:
562 | /* printf("here i = %d, n = %d\n", i, n); */
563 | fprintf(stderr,"tree: Invalid argument -`%c'.\n",argv[i][j]);
564 | usage(1);
565 | exit(1);
566 | break;
567 | }
568 | }
569 | } else {
570 | if (!dirname) dirname = (char **)xmalloc(sizeof(char *) * (q=MINIT));
571 | else if (p == (q-2)) dirname = (char **)xrealloc(dirname,sizeof(char *) * (q+=MINC));
572 | dirname[p++] = scopy(argv[i]);
573 | }
574 | }
575 | if (p) dirname[p] = NULL;
576 |
577 | setoutput(outfilename);
578 |
579 | parse_dir_colors();
580 | initlinedraw(false);
581 |
582 | if (showversion) {
583 | print_version(true);
584 | exit(0);
585 | }
586 |
587 | /* Insure sensible defaults and sanity check options: */
588 | if (dirname == NULL) {
589 | dirname = xmalloc(sizeof(char *) * 2);
590 | dirname[0] = scopy(".");
591 | dirname[1] = NULL;
592 | }
593 | if (topsort == NULL) topsort = basesort;
594 | if (basesort == NULL) topsort = NULL;
595 | if (timefmt) setlocale(LC_TIME,"");
596 | if (dflag) pruneflag = false; /* You'll just get nothing otherwise. */
597 | if (Rflag && (Level == -1)) Rflag = false;
598 |
599 | if (hyperflag && authority == NULL) {
600 | // If the hostname is longer than PATH_MAX, maybe it's just as well we don't
601 | // try to use it.
602 | if (gethostname(xpattern,PATH_MAX) < 0) {
603 | fprintf(stderr,"Unable to get hostname, using 'localhost'.\n");
604 | authority = "localhost";
605 | } else authority = scopy(xpattern);
606 | }
607 |
608 | /* Not going to implement git configs so no core.excludesFile support. */
609 | if (gitignore && (stmp = getenv("GIT_DIR"))) {
610 | char *path = xmalloc(PATH_MAX);
611 | snprintf(path, PATH_MAX, "%s/info/exclude", stmp);
612 | push_filterstack(new_ignorefile(path, false));
613 | free(path);
614 | }
615 | if (showinfo) {
616 | push_infostack(new_infofile(INFO_PATH, false));
617 | }
618 |
619 | needfulltree = duflag || pruneflag || matchdirs || fromfile;
620 |
621 | emit_tree(dirname, needfulltree);
622 |
623 | if (outfilename != NULL) fclose(outfile);
624 |
625 | return errors ? 2 : 0;
626 | }
627 |
628 | void print_version(int nl)
629 | {
630 | char buf[PATH_MAX], *v;
631 | v = version+12;
632 | sprintf(buf, "%.*s%s", (int)strlen(v)-2, v, nl?"\n":"");
633 | fprintf(outfile, buf, linedraw->copy);
634 | }
635 |
636 | void setoutput(const char *filename)
637 | {
638 | if (filename == NULL) {
639 | #ifdef __EMX__
640 | _fsetmode(outfile=stdout,Hflag?"b":"t");
641 | #else
642 | if (outfile == NULL) outfile = stdout;
643 | #endif
644 | } else {
645 | #ifdef __EMX__
646 | outfile = fopen(filename, Hflag? "wb":"wt");
647 | #else
648 | outfile = fopen(filename, "w");
649 | #endif
650 | if (outfile == NULL) {
651 | fprintf(stderr,"tree: invalid filename '%s'\n", filename);
652 | exit(1);
653 | }
654 | }
655 | }
656 |
657 | void usage(int n)
658 | {
659 | parse_dir_colors();
660 | initlinedraw(false);
661 |
662 | /* 123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */
663 | /* \t9!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */
664 | fancy(n < 2? stderr: stdout,
665 | "usage: \btree\r [\b-acdfghilnpqrstuvxACDFJQNSUX\r] [\b-L\r \flevel\r [\b-R\r]] [\b-H\r [-]\fbaseHREF\r]\n"
666 | "\t[\b-T\r \ftitle\r] [\b-o\r \ffilename\r] [\b-P\r \fpattern\r] [\b-I\r \fpattern\r] [\b--gitignore\r]\n"
667 | "\t[\b--gitfile\r[\b=\r]\ffile\r] [\b--matchdirs\r] [\b--metafirst\r] [\b--ignore-case\r]\n"
668 | "\t[\b--nolinks\r] [\b--hintro\r[\b=\r]\ffile\r] [\b--houtro\r[\b=\r]\ffile\r] [\b--inodes\r] [\b--device\r]\n"
669 | "\t[\b--sort\r[\b=\r]\fname\r] [\b--dirsfirst\r] [\b--filesfirst\r] [\b--filelimit\r[\b=\r]\f#\r] [\b--si\r]\n"
670 | "\t[\b--du\r] [\b--prune\r] [\b--charset\r[\b=\r]\fX\r] [\b--timefmt\r[\b=\r]\fformat\r] [\b--fromfile\r]\n"
671 | "\t[\b--fromtabfile\r] [\b--fflinks\r] [\b--info\r] [\b--infofile\r[\b=\r]\ffile\r] [\b--noreport\r]\n"
672 | "\t[\b--hyperlink\r] [\b--scheme\r[\b=\r]\fschema\r] [\b--authority\r[\b=\r]\fhost\r] [\b--opt-toggle\r]\n"
673 | "\t[\b--version\r] [\b--help\r] [\b--\r] [\fdirectory\r \b...\r]\n");
674 |
675 | if (n < 2) return;
676 | fancy(stdout,
677 | " \b------- Listing options -------\r\n"
678 | " \b-a\r All files are listed.\n"
679 | " \b-d\r List directories only.\n"
680 | " \b-l\r Follow symbolic links like directories.\n"
681 | " \b-f\r Print the full path prefix for each file.\n"
682 | " \b-x\r Stay on current filesystem only.\n"
683 | " \b-L\r \flevel\r Descend only \flevel\r directories deep.\n"
684 | " \b-R\r Rerun tree when max dir level reached.\n"
685 | " \b-P\r \fpattern\r List only those files that match the pattern given.\n"
686 | " \b-I\r \fpattern\r Do not list files that match the given pattern.\n"
687 | " \b--gitignore\r Filter by using \b.gitignore\r files.\n"
688 | " \b--gitfile\r \fX\r Explicitly read a gitignore file.\n"
689 | " \b--ignore-case\r Ignore case when pattern matching.\n"
690 | " \b--matchdirs\r Include directory names in \b-P\r pattern matching.\n"
691 | " \b--metafirst\r Print meta-data at the beginning of each line.\n"
692 | " \b--prune\r Prune empty directories from the output.\n"
693 | " \b--info\r Print information about files found in \b.info\r files.\n"
694 | " \b--infofile\r \fX\r Explicitly read info file.\n"
695 | " \b--noreport\r Turn off file/directory count at end of tree listing.\n"
696 | " \b--charset\r \fX\r Use charset \fX\r for terminal/HTML and indentation line output.\n"
697 | " \b--filelimit\r \f#\r Do not descend dirs with more than \f#\r files in them.\n"
698 | " \b-o\r \ffilename\r Output to file instead of stdout.\n"
699 | " \b------- File options -------\r\n"
700 | " \b-q\r Print non-printable characters as '\b?\r'.\n"
701 | " \b-N\r Print non-printable characters as is.\n"
702 | " \b-Q\r Quote filenames with double quotes.\n"
703 | " \b-p\r Print the protections for each file.\n"
704 | " \b-u\r Displays file owner or UID number.\n"
705 | " \b-g\r Displays file group owner or GID number.\n"
706 | " \b-s\r Print the size in bytes of each file.\n"
707 | " \b-h\r Print the size in a more human readable way.\n"
708 | " \b--si\r Like \b-h\r, but use in SI units (powers of 1000).\n"
709 | " \b--du\r Compute size of directories by their contents.\n"
710 | " \b-D\r Print the date of last modification or (-c) status change.\n"
711 | " \b--timefmt\r \ffmt\r Print and format time according to the format \ffmt\r.\n"
712 | " \b-F\r Appends '\b/\r', '\b=\r', '\b*\r', '\b@\r', '\b|\r' or '\b>\r' as per \bls -F\r.\n"
713 | " \b--inodes\r Print inode number of each file.\n"
714 | " \b--device\r Print device ID number to which each file belongs.\n");
715 | fancy(stdout,
716 | " \b------- Sorting options -------\r\n"
717 | " \b-v\r Sort files alphanumerically by version.\n"
718 | " \b-t\r Sort files by last modification time.\n"
719 | " \b-c\r Sort files by last status change time.\n"
720 | " \b-U\r Leave files unsorted.\n"
721 | " \b-r\r Reverse the order of the sort.\n"
722 | " \b--dirsfirst\r List directories before files (\b-U\r disables).\n"
723 | " \b--filesfirst\r List files before directories (\b-U\r disables).\n"
724 | " \b--sort\r \fX\r Select sort: \b\fname\r,\b\fversion\r,\b\fsize\r,\b\fmtime\r,\b\fctime\r,\b\fnone\r.\n"
725 | " \b------- Graphics options -------\r\n"
726 | " \b-i\r Don't print indentation lines.\n"
727 | " \b-A\r Print ANSI lines graphic indentation lines.\n"
728 | " \b-S\r Print with CP437 (console) graphics indentation lines.\n"
729 | " \b-n\r Turn colorization off always (\b-C\r overrides).\n"
730 | " \b-C\r Turn colorization on always.\n"
731 | " \b------- XML/HTML/JSON/HYPERLINK options -------\r\n"
732 | " \b-X\r Prints out an XML representation of the tree.\n"
733 | " \b-J\r Prints out an JSON representation of the tree.\n"
734 | " \b-H\r \fbaseHREF\r Prints out HTML format with \fbaseHREF\r as top directory.\n"
735 | " \b-T\r \fstring\r Replace the default HTML title and H1 header with \fstring\r.\n"
736 | " \b--nolinks\r Turn off hyperlinks in HTML output.\n"
737 | " \b--hintro\r \fX\r Use file \fX\r as the HTML intro.\n"
738 | " \b--houtro\r \fX\r Use file \fX\r as the HTML outro.\n"
739 | " \b--hyperlink\r Turn on OSC 8 terminal hyperlinks.\n"
740 | " \b--scheme\r \fX\r Set OSC 8 hyperlink scheme, default \b\ffile://\r\n"
741 | " \b--authority\r \fX\r Set OSC 8 hyperlink authority/hostname.\n"
742 | " \b------- Input options -------\r\n"
743 | " \b--fromfile\r Reads paths from files (\b.\r=stdin)\n"
744 | " \b--fromtabfile\r Reads trees from tab indented files (\b.\r=stdin)\n"
745 | " \b--fflinks\r Process link information when using \b--fromfile\r.\n"
746 | " \b------- Miscellaneous options -------\r\n"
747 | " \b--opt-toggle\r Enable option toggling.\n"
748 | " \b--version\r Print version and exit.\n"
749 | " \b--help\r Print usage and this help message and exit.\n"
750 | " \b--\r Options processing terminator.\n");
751 | exit(0);
752 | }
753 |
754 | /**
755 | * True if file matches an -I pattern
756 | */
757 | int patignore(const char *name, bool isdir)
758 | {
759 | int i;
760 | for(i=0; i < ipattern; i++)
761 | if (patmatch(name, ipatterns[i], isdir)) return 1;
762 | return 0;
763 | }
764 |
765 | /**
766 | * True if name matches a -P pattern
767 | */
768 | int patinclude(const char *name, bool isdir)
769 | {
770 | int i;
771 | for(i=0; i < pattern; i++) {
772 | if (patmatch(name, patterns[i], isdir)) {
773 | return 1;
774 | }
775 | }
776 | return 0;
777 | }
778 |
779 | /**
780 | * Split out stat portion from read_dir as prelude to just using stat structure directly.
781 | */
782 | struct _info *getinfo(const char *name, char *path)
783 | {
784 | static char *lbuf = NULL;
785 | static size_t lbufsize = 0;
786 | struct _info *ent;
787 | struct stat st, lst;
788 | ssize_t len;
789 | int rs;
790 | bool isdir;
791 |
792 | if (lbuf == NULL) lbuf = xmalloc(lbufsize = PATH_MAX);
793 |
794 | if (lstat(path,&lst) < 0) return NULL;
795 |
796 | if ((lst.st_mode & S_IFMT) == S_IFLNK) {
797 | if ((rs = stat(path,&st)) < 0) memset(&st, 0, sizeof(st));
798 | } else {
799 | rs = 0;
800 | st.st_mode = lst.st_mode;
801 | st.st_dev = lst.st_dev;
802 | st.st_ino = lst.st_ino;
803 | }
804 |
805 | isdir = (st.st_mode & S_IFMT) == S_IFDIR;
806 |
807 | #ifndef __EMX__
808 | if (gitignore && filtercheck(path, name, isdir)) return NULL;
809 |
810 | if ((lst.st_mode & S_IFMT) != S_IFDIR && !(lflag && ((st.st_mode & S_IFMT) == S_IFDIR))) {
811 | if (pattern && !patinclude(name, isdir)) return NULL;
812 | }
813 | if (ipattern && patignore(name, isdir)) return NULL;
814 | #endif
815 |
816 | if (dflag && ((st.st_mode & S_IFMT) != S_IFDIR)) return NULL;
817 |
818 | #ifndef __EMX__
819 | /* if (pattern && ((lst.st_mode & S_IFMT) == S_IFLNK) && !lflag) continue; */
820 | #endif
821 |
822 | ent = (struct _info *)xmalloc(sizeof(struct _info));
823 | memset(ent, 0, sizeof(struct _info));
824 |
825 | ent->name = scopy(name);
826 | /* We should just incorporate struct stat into _info, and eliminate this unnecessary copying.
827 | * Made sense long ago when we had fewer options and didn't need half of stat.
828 | */
829 | ent->mode = lst.st_mode;
830 | ent->uid = lst.st_uid;
831 | ent->gid = lst.st_gid;
832 | ent->size = lst.st_size;
833 | ent->dev = st.st_dev;
834 | ent->inode = st.st_ino;
835 | ent->ldev = lst.st_dev;
836 | ent->linode = lst.st_ino;
837 | ent->lnk = NULL;
838 | ent->orphan = false;
839 | ent->err = NULL;
840 | ent->child = NULL;
841 |
842 | ent->atime = lst.st_atime;
843 | ent->ctime = lst.st_ctime;
844 | ent->mtime = lst.st_mtime;
845 |
846 | #ifdef __EMX__
847 | ent->attr = lst.st_attr;
848 | #else
849 |
850 | /* These should be eliminated, as they're barely used: */
851 | ent->isdir = isdir;
852 | ent->issok = ((st.st_mode & S_IFMT) == S_IFSOCK);
853 | ent->isfifo = ((st.st_mode & S_IFMT) == S_IFIFO);
854 | ent->isexe = (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? 1 : 0;
855 |
856 | if ((lst.st_mode & S_IFMT) == S_IFLNK) {
857 | if ((size_t)lst.st_size+1 > lbufsize) lbuf = xrealloc(lbuf,lbufsize=((size_t)lst.st_size+8192));
858 | if ((len=readlink(path,lbuf,lbufsize-1)) < 0) {
859 | ent->lnk = scopy("[Error reading symbolic link information]");
860 | ent->isdir = false;
861 | ent->lnkmode = st.st_mode;
862 | } else {
863 | lbuf[len] = 0;
864 | ent->lnk = scopy(lbuf);
865 | if (rs < 0) ent->orphan = true;
866 | ent->lnkmode = st.st_mode;
867 | }
868 | }
869 | #endif
870 |
871 | ent->comment = NULL;
872 |
873 | return ent;
874 | }
875 |
876 | struct _info **read_dir(char *dir, ssize_t *n, int infotop)
877 | {
878 | struct comment *com;
879 | static char *path = NULL;
880 | static size_t pathsize;
881 | struct _info **dl, *info;
882 | struct dirent *ent;
883 | DIR *d;
884 | size_t ne, p = 0, i;
885 | bool es = (dir[strlen(dir)-1] == '/');
886 |
887 | if (path == NULL) {
888 | path=xmalloc(pathsize = strlen(dir)+PATH_MAX);
889 | }
890 |
891 | *n = -1;
892 | if ((d=opendir(dir)) == NULL) return NULL;
893 |
894 | dl = (struct _info **)xmalloc(sizeof(struct _info *) * (ne = MINIT));
895 |
896 | while((ent = (struct dirent *)readdir(d))) {
897 | if (!strcmp("..",ent->d_name) || !strcmp(".",ent->d_name)) continue;
898 | if (Hflag && !strcmp(ent->d_name,"00Tree.html")) continue;
899 | if (!aflag && ent->d_name[0] == '.') continue;
900 |
901 | if (strlen(dir)+strlen(ent->d_name)+2 > pathsize) path = xrealloc(path,pathsize=(strlen(dir)+strlen(ent->d_name)+PATH_MAX));
902 | if (es) sprintf(path, "%s%s", dir, ent->d_name);
903 | else sprintf(path,"%s/%s",dir,ent->d_name);
904 |
905 | info = getinfo(ent->d_name, path);
906 | if (info) {
907 | if (showinfo && (com = infocheck(path, ent->d_name, infotop, info->isdir))) {
908 | for(i = 0; com->desc[i] != NULL; i++);
909 | info->comment = xmalloc(sizeof(char *) * (i+1));
910 | for(i = 0; com->desc[i] != NULL; i++) info->comment[i] = scopy(com->desc[i]);
911 | info->comment[i] = NULL;
912 | }
913 | if (p == (ne-1)) dl = (struct _info **)xrealloc(dl,sizeof(struct _info *) * (ne += MINC));
914 | dl[p++] = info;
915 | }
916 | }
917 | closedir(d);
918 |
919 | if ((*n = (ssize_t)p) == 0) {
920 | free(dl);
921 | return NULL;
922 | }
923 |
924 | dl[p] = NULL;
925 | return dl;
926 | }
927 |
928 | void push_files(const char *dir, struct ignorefile **ig, struct infofile **inf, bool top)
929 | {
930 | if (gitignore) {
931 | *ig = new_ignorefile(dir, top);
932 | if (*ig != NULL) push_filterstack(*ig);
933 | }
934 | if (showinfo) {
935 | *inf = new_infofile(dir, top);
936 | if (*inf != NULL) push_infostack(*inf);
937 | }
938 | }
939 |
940 | /* This is for all the impossible things people wanted the old tree to do.
941 | * This can and will use a large amount of memory for large directory trees
942 | * and also take some time.
943 | */
944 | struct _info **unix_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err)
945 | {
946 | char *path;
947 | size_t pathsize = 0;
948 | struct ignorefile *ig = NULL;
949 | struct infofile *inf = NULL;
950 | struct _info **dir, **sav, **p, *xp;
951 | struct stat sb;
952 | ssize_t n;
953 | u_long lev_tmp;
954 | int tmp_pattern = 0;
955 | char *start_rel_path;
956 |
957 | *err = NULL;
958 | if (Level >= 0 && lev > (u_long)Level) return NULL;
959 | if (xdev && lev == 0) {
960 | stat(d,&sb);
961 | dev = sb.st_dev;
962 | }
963 | /* if the directory name matches, turn off pattern matching for contents */
964 | if (matchdirs && pattern) {
965 | lev_tmp = lev;
966 | start_rel_path = d + strlen(d);
967 | for (start_rel_path = d + strlen(d); start_rel_path != d; --start_rel_path) {
968 | if (*start_rel_path == '/')
969 | --lev_tmp;
970 | if (lev_tmp <= 0) {
971 | if (*start_rel_path)
972 | ++start_rel_path;
973 | break;
974 | }
975 | }
976 | if (*start_rel_path && patinclude(start_rel_path, 1)) {
977 | tmp_pattern = pattern;
978 | pattern = 0;
979 | }
980 | }
981 |
982 | push_files(d, &ig, &inf, lev==0);
983 |
984 | sav = dir = read_dir(d, &n, inf != NULL);
985 | if (tmp_pattern) {
986 | pattern = tmp_pattern;
987 | tmp_pattern = 0;
988 | }
989 | if (dir == NULL && n) {
990 | *err = scopy("error opening dir");
991 | return NULL;
992 | }
993 | if (n == 0) {
994 | if (sav != NULL) free_dir(sav);
995 | return NULL;
996 | }
997 | path = xmalloc(pathsize=PATH_MAX);
998 |
999 | if (flimit > 0 && n > flimit) {
1000 | sprintf(path,"%ld entries exceeds filelimit, not opening dir",n);
1001 | *err = scopy(path);
1002 | free_dir(sav);
1003 | free(path);
1004 | return NULL;
1005 | }
1006 |
1007 | if (lev >= (u_long)maxdirs-1) {
1008 | dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
1009 | }
1010 |
1011 | while (*dir) {
1012 | if ((*dir)->isdir && !(xdev && dev != (*dir)->dev)) {
1013 | if ((*dir)->lnk) {
1014 | if (lflag) {
1015 | if (findino((*dir)->inode,(*dir)->dev)) {
1016 | (*dir)->err = scopy("recursive, not followed");
1017 | } else {
1018 | saveino((*dir)->inode, (*dir)->dev);
1019 | if (*(*dir)->lnk == '/')
1020 | (*dir)->child = unix_getfulltree((*dir)->lnk,lev+1,dev,&((*dir)->size),&((*dir)->err));
1021 | else {
1022 | if (strlen(d)+strlen((*dir)->lnk)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
1023 | if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->lnk);
1024 | else sprintf(path,"%s/%s",d,(*dir)->lnk);
1025 | (*dir)->child = unix_getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err));
1026 | }
1027 | }
1028 | }
1029 | } else {
1030 | if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
1031 | if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
1032 | else sprintf(path,"%s/%s",d,(*dir)->name);
1033 | saveino((*dir)->inode, (*dir)->dev);
1034 | (*dir)->child = unix_getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err));
1035 | }
1036 | /* prune empty folders, unless they match the requested pattern */
1037 | if (pruneflag && (*dir)->child == NULL &&
1038 | !(matchdirs && pattern && patinclude((*dir)->name, (*dir)->isdir))) {
1039 | xp = *dir;
1040 | for(p=dir;*p;p++) *p = *(p+1);
1041 | n--;
1042 | free(xp->name);
1043 | if (xp->lnk) free(xp->lnk);
1044 | free(xp);
1045 | continue;
1046 | }
1047 | }
1048 | if (duflag) *size += (*dir)->size;
1049 | dir++;
1050 | }
1051 |
1052 | /* sorting needs to be deferred for --du: */
1053 | if (topsort) qsort(sav,(size_t)n,sizeof(struct _info *), (int (*)(const void *, const void *))topsort);
1054 |
1055 | free(path);
1056 | if (n == 0) {
1057 | free_dir(sav);
1058 | return NULL;
1059 | }
1060 | if (ig != NULL) pop_filterstack();
1061 | if (inf != NULL) pop_infostack();
1062 | return sav;
1063 | }
1064 |
1065 | /**
1066 | * filesfirst and dirsfirst are now top-level meta-sorts.
1067 | */
1068 | int filesfirst(struct _info **a, struct _info **b)
1069 | {
1070 | if ((*a)->isdir != (*b)->isdir) {
1071 | return (*a)->isdir ? 1 : -1;
1072 | }
1073 | return basesort(a, b);
1074 | }
1075 |
1076 | int dirsfirst(struct _info **a, struct _info **b)
1077 | {
1078 | if ((*a)->isdir != (*b)->isdir) {
1079 | return (*a)->isdir ? -1 : 1;
1080 | }
1081 | return basesort(a, b);
1082 | }
1083 |
1084 | /* Sorting functions */
1085 | int alnumsort(struct _info **a, struct _info **b)
1086 | {
1087 | int v = strcoll((*a)->name,(*b)->name);
1088 | return reverse? -v : v;
1089 | }
1090 |
1091 | int versort(struct _info **a, struct _info **b)
1092 | {
1093 | int v = strverscmp((*a)->name,(*b)->name);
1094 | return reverse? -v : v;
1095 | }
1096 |
1097 | int mtimesort(struct _info **a, struct _info **b)
1098 | {
1099 | int v;
1100 |
1101 | if ((*a)->mtime == (*b)->mtime) {
1102 | v = strcoll((*a)->name,(*b)->name);
1103 | return reverse? -v : v;
1104 | }
1105 | v = (*a)->mtime == (*b)->mtime? 0 : ((*a)->mtime < (*b)->mtime ? -1 : 1);
1106 | return reverse? -v : v;
1107 | }
1108 |
1109 | int ctimesort(struct _info **a, struct _info **b)
1110 | {
1111 | int v;
1112 |
1113 | if ((*a)->ctime == (*b)->ctime) {
1114 | v = strcoll((*a)->name,(*b)->name);
1115 | return reverse? -v : v;
1116 | }
1117 | v = (*a)->ctime == (*b)->ctime? 0 : ((*a)->ctime < (*b)->ctime? -1 : 1);
1118 | return reverse? -v : v;
1119 | }
1120 |
1121 | int sizecmp(off_t a, off_t b)
1122 | {
1123 | return (a == b)? 0 : ((a < b)? 1 : -1);
1124 | }
1125 |
1126 | int fsizesort(struct _info **a, struct _info **b)
1127 | {
1128 | int v = sizecmp((*a)->size, (*b)->size);
1129 | if (v == 0) v = strcoll((*a)->name,(*b)->name);
1130 | return reverse? -v : v;
1131 | }
1132 |
1133 | void *xmalloc (size_t size)
1134 | {
1135 | register void *value = malloc (size);
1136 | if (value == NULL) {
1137 | fprintf(stderr,"tree: virtual memory exhausted.\n");
1138 | exit(1);
1139 | }
1140 | return value;
1141 | }
1142 |
1143 | void *xrealloc (void *ptr, size_t size)
1144 | {
1145 | register void *value = realloc (ptr,size);
1146 | if (value == NULL) {
1147 | fprintf(stderr,"tree: virtual memory exhausted.\n");
1148 | exit(1);
1149 | }
1150 | return value;
1151 | }
1152 |
1153 | void free_dir(struct _info **d)
1154 | {
1155 | int i;
1156 |
1157 | for(i=0;d[i];i++) {
1158 | free(d[i]->name);
1159 | if (d[i]->lnk) free(d[i]->lnk);
1160 | free(d[i]);
1161 | }
1162 | free(d);
1163 | }
1164 |
1165 | char *gnu_getcwd(void)
1166 | {
1167 | size_t size = 100;
1168 | char *buffer = (char *) xmalloc (size);
1169 |
1170 | while (1) {
1171 | char *value = getcwd (buffer, size);
1172 | if (value != 0) return buffer;
1173 | size *= 2;
1174 | free (buffer);
1175 | buffer = (char *) xmalloc (size);
1176 | }
1177 | }
1178 |
1179 | static char cond_lower(char c)
1180 | {
1181 | return ignorecase ? (char)tolower(c) : c;
1182 | }
1183 |
1184 | /*
1185 | * Patmatch() code courtesy of Thomas Moore (dark@mama.indstate.edu)
1186 | * '|' support added by David MacMahon (davidm@astron.Berkeley.EDU)
1187 | * Case insensitive support added by Jason A. Donenfeld (Jason@zx2c4.com)
1188 | * returns:
1189 | * 1 on a match
1190 | * 0 on a mismatch
1191 | * -1 on a syntax error in the pattern
1192 | */
1193 | int patmatch(const char *buf, const char *pat, bool isdir)
1194 | {
1195 | int match = 1, n;
1196 | char *bar = strchr(pat, '|');
1197 | char m, pprev = 0;
1198 |
1199 | /* If a bar is found, call patmatch recursively on the two sub-patterns */
1200 | if (bar) {
1201 | /* If the bar is the first or last character, it's a syntax error */
1202 | if (bar == pat || !bar[1]) {
1203 | return -1;
1204 | }
1205 | /* Break pattern into two sub-patterns */
1206 | *bar = '\0';
1207 | match = patmatch(buf, pat, isdir);
1208 | if (!match) {
1209 | match = patmatch(buf, bar+1, isdir);
1210 | }
1211 | /* Join sub-patterns back into one pattern */
1212 | *bar = '|';
1213 | return match;
1214 | }
1215 |
1216 | while(*pat && match) {
1217 | switch(*pat) {
1218 | case '[':
1219 | pat++;
1220 | if(*pat != '^') {
1221 | n = 1;
1222 | match = 0;
1223 | } else {
1224 | pat++;
1225 | n = 0;
1226 | }
1227 | while(*pat != ']'){
1228 | if(*pat == '\\') pat++;
1229 | if(!*pat /* || *pat == '/' */ ) return -1;
1230 | if(pat[1] == '-'){
1231 | m = *pat;
1232 | pat += 2;
1233 | if(*pat == '\\' && *pat)
1234 | pat++;
1235 | if(cond_lower(*buf) >= cond_lower(m) && cond_lower(*buf) <= cond_lower(*pat))
1236 | match = n;
1237 | if(!*pat)
1238 | pat--;
1239 | } else if(cond_lower(*buf) == cond_lower(*pat)) match = n;
1240 | pat++;
1241 | }
1242 | buf++;
1243 | break;
1244 | case '*':
1245 | pat++;
1246 | if(!*pat) {
1247 | int f = (strchr(buf, '/') == NULL);
1248 | return f;
1249 | }
1250 | match = 0;
1251 | /* "Support" ** for .gitignore support, mostly the same as *: */
1252 | if (*pat == '*') {
1253 | pat++;
1254 | if(!*pat) return 1;
1255 |
1256 | while(*buf && !(match = patmatch(buf, pat, isdir))) {
1257 | /* ** between two /'s is allowed to match a null /: */
1258 | if (pprev == '/' && *pat == '/' && *(pat+1) && (match = patmatch(buf, pat+1, isdir))) return match;
1259 | buf++;
1260 | while(*buf && *buf != '/') buf++;
1261 | }
1262 | } else {
1263 | while(*buf && !(match = patmatch(buf++, pat, isdir)))
1264 | if (*buf == '/') break;
1265 | }
1266 | if (!match && (!*buf || *buf == '/')) match = patmatch(buf, pat, isdir);
1267 | return match;
1268 | case '?':
1269 | if(!*buf) return 0;
1270 | buf++;
1271 | break;
1272 | case '/':
1273 | if (!*(pat+1) && !*buf) return isdir;
1274 | match = (*buf++ == *pat);
1275 | break;
1276 | case '\\':
1277 | if(*pat)
1278 | pat++;
1279 | /* Falls through */
1280 | default:
1281 | match = (cond_lower(*buf++) == cond_lower(*pat));
1282 | break;
1283 | }
1284 | pprev = *pat++;
1285 | if(match<1) return match;
1286 | }
1287 | if(!*buf) return match;
1288 | return 0;
1289 | }
1290 |
1291 |
1292 | /**
1293 | * They cried out for ANSI-lines (not really), but here they are, as an option
1294 | * for the xterm and console capable among you, as a run-time option.
1295 | */
1296 | void indent(int maxlevel)
1297 | {
1298 | int i;
1299 |
1300 | if (ansilines) {
1301 | if (dirs[1]) fprintf(outfile,"\033(0");
1302 | for(i=1; (i <= maxlevel) && dirs[i]; i++) {
1303 | if (dirs[i+1]) {
1304 | if (dirs[i] == 1) fprintf(outfile,"\170 ");
1305 | else printf(" ");
1306 | } else {
1307 | if (dirs[i] == 1) fprintf(outfile,"\164\161\161 ");
1308 | else fprintf(outfile,"\155\161\161 ");
1309 | }
1310 | }
1311 | if (dirs[1]) fprintf(outfile,"\033(B");
1312 | } else {
1313 | if (Hflag) fprintf(outfile,"\t");
1314 | for(i=1; (i <= maxlevel) && dirs[i]; i++) {
1315 | fprintf(outfile,"%s ",
1316 | dirs[i+1] ? (dirs[i]==1 ? linedraw->vert : (Hflag? " " : " ") )
1317 | : (dirs[i]==1 ? linedraw->vert_left:linedraw->corner));
1318 | }
1319 | }
1320 | }
1321 |
1322 |
1323 | #ifdef __EMX__
1324 | char *prot(long m)
1325 | #else
1326 | char *prot(mode_t m)
1327 | #endif
1328 | {
1329 | #ifdef __EMX__
1330 | const u_short *p;
1331 | static char buf[6];
1332 | char*cp;
1333 |
1334 | for(p=ifmt,cp=strcpy(buf,"adshr");*cp;++p,++cp)
1335 | if(!(m&*p))
1336 | *cp='-';
1337 | #else
1338 | static char buf[11], perms[] = "rwxrwxrwx";
1339 | int i;
1340 | mode_t b;
1341 |
1342 | for(i=0;ifmt[i] && (m&S_IFMT) != ifmt[i];i++);
1343 | buf[0] = fmt[i];
1344 |
1345 | /**
1346 | * Nice, but maybe not so portable, it is should be no less portable than the
1347 | * old code.
1348 | */
1349 | for(b=S_IRUSR,i=0; i<9; b>>=1,i++)
1350 | buf[i+1] = (m & (b)) ? perms[i] : '-';
1351 | if (m & S_ISUID) buf[3] = (buf[3]=='-')? 'S' : 's';
1352 | if (m & S_ISGID) buf[6] = (buf[6]=='-')? 'S' : 's';
1353 | if (m & S_ISVTX) buf[9] = (buf[9]=='-')? 'T' : 't';
1354 |
1355 | buf[10] = 0;
1356 | #endif
1357 | return buf;
1358 | }
1359 |
1360 | #define SIXMONTHS (6*31*24*60*60)
1361 |
1362 | char *do_date(time_t t)
1363 | {
1364 | static char buf[256];
1365 | struct tm *tm;
1366 |
1367 | tm = localtime(&t);
1368 |
1369 | if (timefmt) {
1370 | strftime(buf,255,timefmt,tm);
1371 | buf[255] = 0;
1372 | } else {
1373 | time_t c = time(0);
1374 | /* Use strftime() so that locale is respected: */
1375 | if (t > c || (t+SIXMONTHS) < c)
1376 | strftime(buf,255,"%b %e %Y",tm);
1377 | else
1378 | strftime(buf,255,"%b %e %R", tm);
1379 | }
1380 | return buf;
1381 | }
1382 |
1383 | /**
1384 | * Must fix this someday
1385 | */
1386 | void printit(const char *s)
1387 | {
1388 | int c;
1389 | size_t cs;
1390 |
1391 | if (Nflag) {
1392 | if (Qflag) fprintf(outfile, "\"%s\"",s);
1393 | else fprintf(outfile,"%s",s);
1394 | return;
1395 | }
1396 | if (mb_cur_max > 1) {
1397 | wchar_t *ws, *tp;
1398 | ws = xmalloc(sizeof(wchar_t)* (cs=(strlen(s)+1)));
1399 | if (mbstowcs(ws,s,cs) != (size_t)-1) {
1400 | if (Qflag) putc('"',outfile);
1401 | for(tp=ws;*tp && cs > 1;tp++, cs--) {
1402 | if (iswprint((wint_t)*tp)) fprintf(outfile,"%lc",(wint_t)*tp);
1403 | else {
1404 | if (qflag) putc('?',outfile);
1405 | else fprintf(outfile,"\\%03o",(unsigned int)*tp);
1406 | }
1407 | }
1408 | if (Qflag) putc('"',outfile);
1409 | free(ws);
1410 | return;
1411 | }
1412 | free(ws);
1413 | }
1414 | if (Qflag) putc('"',outfile);
1415 | for(;*s;s++) {
1416 | c = (unsigned char)*s;
1417 | #ifdef __EMX__
1418 | if(_nls_is_dbcs_lead(*(unsigned char*)s)){
1419 | putc(*s,outfile);
1420 | putc(*++s,outfile);
1421 | continue;
1422 | }
1423 | #endif
1424 | if((c >= 7 && c <= 13) || c == '\\' || (c == '"' && Qflag) || (c == ' ' && !Qflag)) {
1425 | putc('\\',outfile);
1426 | if (c > 13) putc(c, outfile);
1427 | else putc("abtnvfr"[c-7], outfile);
1428 | } else if (isprint(c)) putc(c,outfile);
1429 | else {
1430 | if (qflag) {
1431 | if (mb_cur_max > 1 && c > 127) putc(c,outfile);
1432 | else putc('?',outfile);
1433 | } else fprintf(outfile,"\\%03o",c);
1434 | }
1435 | }
1436 | if (Qflag) putc('"',outfile);
1437 | }
1438 |
1439 | int psize(char *buf, off_t size)
1440 | {
1441 | static char *iec_unit="BKMGTPEZY", *si_unit = "dkMGTPEZY";
1442 | char *unit = siflag ? si_unit : iec_unit;
1443 | int idx, usize = siflag ? 1000 : 1024;
1444 |
1445 | if (hflag || siflag) {
1446 | for (idx=size= (usize*usize); idx++,size/=usize);
1447 | if (!idx) return sprintf(buf, " %4d", (int)size);
1448 | else return sprintf(buf, (((size+52)/usize) >= 10)? " %3.0f%c" : " %3.1f%c" , (float)size/(float)usize,unit[idx]);
1449 | } else return sprintf(buf, sizeof(off_t) == sizeof(long long)? " %11lld" : " %9lld", (long long int)size);
1450 | }
1451 |
1452 | char Ftype(mode_t mode)
1453 | {
1454 | int m = mode & S_IFMT;
1455 | if (!dflag && m == S_IFDIR) return '/';
1456 | else if (m == S_IFSOCK) return '=';
1457 | else if (m == S_IFIFO) return '|';
1458 | else if (m == S_IFLNK) return '@'; /* Here, but never actually used though. */
1459 | #ifdef S_IFDOOR
1460 | else if (m == S_IFDOOR) return '>';
1461 | #endif
1462 | else if ((m == S_IFREG) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*';
1463 | return 0;
1464 | }
1465 |
1466 | struct _info *stat2info(const struct stat *st)
1467 | {
1468 | static struct _info info;
1469 |
1470 | info.linode = st->st_ino;
1471 | info.ldev = st->st_dev;
1472 | #ifdef __EMX__
1473 | info.attr = st->st_attr
1474 | #endif
1475 | info.mode = st->st_mode;
1476 | info.uid = st->st_uid;
1477 | info.gid = st->st_gid;
1478 | info.size = st->st_size;
1479 | info.atime = st->st_atime;
1480 | info.ctime = st->st_ctime;
1481 | info.mtime = st->st_mtime;
1482 |
1483 | info.isdir = ((st->st_mode & S_IFMT) == S_IFDIR);
1484 | info.issok = ((st->st_mode & S_IFMT) == S_IFSOCK);
1485 | info.isfifo = ((st->st_mode & S_IFMT) == S_IFIFO);
1486 | info.isexe = (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? 1 : 0;
1487 |
1488 | return &info;
1489 | }
1490 |
1491 | char *fillinfo(char *buf, const struct _info *ent)
1492 | {
1493 | int n;
1494 | buf[n=0] = 0;
1495 | #ifdef __USE_FILE_OFFSET64
1496 | if (inodeflag) n += sprintf(buf," %7lld",(long long)ent->linode);
1497 | #else
1498 | if (inodeflag) n += sprintf(buf," %7ld",(long int)ent->linode);
1499 | #endif
1500 | if (devflag) n += sprintf(buf+n, " %3d", (int)ent->ldev);
1501 | #ifdef __EMX__
1502 | if (pflag) n += sprintf(buf+n, " %s",prot(ent->attr));
1503 | #else
1504 | if (pflag) n += sprintf(buf+n, " %s", prot(ent->mode));
1505 | #endif
1506 | if (uflag) n += sprintf(buf+n, " %-8.32s", uidtoname(ent->uid));
1507 | if (gflag) n += sprintf(buf+n, " %-8.32s", gidtoname(ent->gid));
1508 | if (sflag) n += psize(buf+n,ent->size);
1509 | if (Dflag) n += sprintf(buf+n, " %s", do_date(cflag? ent->ctime : ent->mtime));
1510 |
1511 | if (buf[0] == ' ') {
1512 | buf[0] = '[';
1513 | sprintf(buf+n, "]");
1514 | }
1515 |
1516 | return buf;
1517 | }
1518 |
--------------------------------------------------------------------------------