commit 340a21c1f14c29c67331cea09fa4ae62a81c2db5 Author: Zane Schaffer Date: Wed Oct 12 00:03:10 2022 -0700 init && basically all my changes diff --git a/.html.c.un~ b/.html.c.un~ new file mode 100644 index 0000000..7d307dd Binary files /dev/null and b/.html.c.un~ differ diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..f7625aa --- /dev/null +++ b/CHANGES @@ -0,0 +1,342 @@ +Version 2.0.4 (09/06/2022) + - Brown paper bag release: + - Fix missing comma in JSON output. (jogbear?) + +Version 2.0.3 (08/26/2022) + - Fix segfault when filelimit is used and tree encounters a directory it + cannot enter. (Kenta Arai) + - Use += when assigning CFLAGS and LDFLAGS in the Makefile allowing + them to be modified by environment variables during make. (Ben Brown) + Possibly assumes GNU make. + - Fixed broken -x option (stops recursing.) (balping) + - Fix use after free (causing segfault) for dir/subdir in list.c (Erik + Skultety / Ben Brown) + - Fixes for .gitignore functionality (Saniya Maheshwari / Mig-hub ? / Carlos + Pinto) + - Fixed * handing in patmatch. Worked almost like ** before, now properly + stops at /'s. These issues were the result of forgetting that patmatch() + was just to match filenames to patterns, not paths. + - Patterns starting with / are actually relative to the .gitignore file, + not the root of the filesystem, go figure. + - Patterns without /'s in .gitignore apply to any file in any directory + under the .gitignore, not just the .gitignore directory + - Remove "All rights reserved" from copyright statements. A left-over from + trees original artistic license. + - Add in --du and --prune to --help output (Nxueyamao?) + - Fixed segfault when an unknown directory is given with -X + - Fixed output up for -X and -J options. + - Remove one reference to strnlen which isn't necessary since it may not + be available on some OS's. + +Version 2.0.2 (02/16/2022) + - Okay, apparently the stddata addition is causing havoc (who knew how many + scripts just haphazardly hand programs random file descriptors, that's + surely not a problem.) Going forward the stddata option will only work + if the environment variable STDDATA_FD is present or set to the descriptor + to produce the JSON output on. + - Fix HTML url output issue. (Maxim Cournoyer) It was definitely broken in + the 2.0.0 release, and this should normalize it with respect to older + versions, however I think it needs to be updated to work better. + - Update MANPATH for OS X (Michiel Beijen) + - Fixed an error with * in the patchmatch code where *foo*bar would match + *foo alone. (Josey Smith) + +Version 2.0.1 (01/03/2022) + - Simplify Makefile and the following changes: prefix -> PREFIX, + BINDIR -> DESTDIR, -O4 -> -O3, mode 644 for man page installation + (Michal Vasilek) + - Make patterns ending in '/' match directories (but not files) for -I / -P + (Michiel Beijen) should also fix issues with --gitignore as well + (Taylor Faubion) + - Fix --gitignore not matching files relative to the path of the .gitignore + (Taylor Faubion) I did say it was hacked together. + - Refactored color.c a bit to simplify the code as a prelude to meta coloring. + +Version 2.0.0 (12/21/2021) + - This started out as a 1.9.0 release but then I got fed up with the + abundance of directory listers (8 in total, 2 each for each output mode). + Nothing is terribly well tested since there are a lot of changes and I would + like to get this out the door finally, please report breakage. This reduced + so much code that all the below additions only resulted in a code base that + is only 54 lines larger than 1.8.0. + - Rolled all the directory listers into 2 functions that call output specific + functions (removes one TODO). + - -R option now recursively calls the emit_tree() function rather than using + system() to re-call tree. Also removes a TODO. + - Adds --info to print information about files/directories from information + found in .info files (removes a maybe do) In HTML output, comments show as + mouse over tooltips, which I imagine will be the most useful use of this + "feature". + - Output un-indented JSON on file descriptor 3 ("stddata") automatically if + file descriptor 3 is present (currently Linux only.) Maybe switch to BSON. + - Always HTML escape filenames in HTML output even when -C is used. + (Eric Pruitt) + - Return a non-zero exit status if there is a failure to open any directory. + - Added --gitignore option to filter out files specified by .gitignore files. + (also reads $GIT_DIR/info/exclude if present.) To facilitate gitignore, + adds support for ** on pattern matching to allow /**/ to match a single /. + This is not well tested and kind of hacked together, so may not work + correctly. (Jake Zimmerman and others) + - Now also supports multiple -I and -P instances. (Michiel Beijen and others) + - Now prints meta data for the top level directory as well. + - Split spaghetti code in main into individual functions. + - Properly sort --fromfile input (Chentao Credungtao via Debian) + - Make tree colorization use reset (rs code in dir_colors,) not normal color + when resetting attributes (Filips Romāns via Debian). + - Honor -n (no color) even if the CLICOLOR_FORCE environment variable is set + (Paul Seyfert) + - Added --metafirst to print the metadata before the indentation lines + (suggested by Richard Mitchell) + - Fix --sort option to not require = + - Defer sorting for --du until the entire sub-directory tree has been + processed. + - Optimized makefile, HP/UX support (Osipov, Michael). Note that this changes + the prefix default to /usr/local, which is becoming required for many + systems now. + - Renamed (the by now very obsolete) doc/tree.1.fr to doc/tree.fr.1 (Jonas + Stein) + - Fix JSON string escaping such that it is not using the HTML escaping (Fox + & others) + - Add --filesfirst option (John A. Fedoruk). Cleaned up sorting code to make + --dirsfirst and --filesfirst top level meta-sorts. + - "arial" not "ariel" (Mark), HTML style-sheet cleaned up in any event. + - Deprecate using local -DLINUX / -DCYGWIN and use the OS provided + __linux__ or __CYGWIN__ (Jonas Stein) + - XML/HTML/JSON output needs to be mutually exclusive, last command line + switch wins. (Sergei Maximov) + - Make sure we use xmalloc instead of malloc in a number of places (Tomáš + Beránek) + +Version 1.8.0 (11/16/2018) + - Added an experimental --fromfile option (suggested by several people.) + This may eventually be replaced or supplimented by a --fromjson option. + - Added support for BSD's CLICOLOR and CLICOLOR_FORCE environment variables. + (Suggested by Alyssa Ross) + - Use strftime() exclusively when formatting date/time to respect locale. + - Some man page fixes and cleanups curtsey of Kirill Kolyshkin + - Update BINDIR in Makefile for MacOS X -- It is not allowed to install + programs to /usr/bin on MacOS X any longer due to System Integrity + Protection (SIP) (Shawn Mehan) + - Misc patches from Jacob Wahlgren: + - Improved command line switch error reporting. + - Symbolic links not displayed if a -P pattern is active + - Missing argument error reporting fixes on long format switches. + - Fixed JSON output hanging commas (John Lane, Tad, others) + - JSON size output ignored -h/--si flags (Wagner Camarao) + - Fixed issue with malformed multibyte string handling. (Mantas + Mikulėnas) + - Fixed issue where mbstowcs() fails to null terminate the string due to + improper UTF-8 encoding leading to garbage being printed. (Nick Craig-Wood) + - Found a bug where the wrong inode (and device) information would be printed + for symbolic links. (Stephan Gabert) + +Version 1.7.0 (04/23/2014) + - Allow user/group names up to 32 characters before clipping. + - Made -i compress XML and JSON output as much as possible by eliminating + extraneous whitespace. + - Added --caseinsensitive (renamed --ignore-case ala grep) flag so patterns + match without regard to case, courtesy of Jason A Donenfeld. + - Added --matchdirs option courtesy of Brian Mattern & Jason A. Donenfeld + . + - Fixed possible buffer overflow on large uid/gids w/o user names/group + names (Alexandre Wendling ) + - Added JSON support courtesy of Florian Sesser . + - Fixed formatting error with HTML output when -L 1 specified. (Sascha Zorn + ) + - Added file size sorting (Philipp M?ller ) + - Added '--sort[=]' option, ala ls. + - Fixed OS X makefile problems (Ryan Hollis ) + - Fixed possible memory overflow in read_dir (path/lbuf not equal in size + to pathsize/lbufsize.) (Han Hui ) + - Fix S_ISDOOR/S_IFDOOR spelling mistake for Solaris. (Tim Mooney + ) + - Make tree more reliably detect UTF-8 locales. (Mantas Mikulnas + and others.) + - Return non-zero exit status on option errors, print usage to stdout when + not an error, add the posix '--' option terminator, Change -S description + to mean CP437 (console) output codes, not ASCII. (Ivan Shmakov + ) + +Version 1.6.0 (05/24/11) + - Re-org of code into multiple files, split HTML and Unix listdir() into + separate functions, various code cleanups and optimizations. + - Fixed a memory leak in listdir() when memory was allocated early and not + freed before function exit. + - Fixed possible buffer overflow where symbolic links are followed. + - Fixed links printing "argetm" before the name of the link when the LINK + setting for DIR_COLORS is set to target (Markus Schnalke + ) + - More fully support dir colors -- added support for su, sg, tw, ow, & st + options (and "do" in theory). + - Use the environment variable "TREE_COLORS" instead of "LS_COLORS" for + color information if it exists. + - Added --si flag to print filesizes in SI (powers of 1000) units (Ulrich + Eckhardt) + - Added -Q to quote filenames in double quotes. Does not override -N or -q. + - Control characters are no longer printed in carrot notation, but as + backslashed octal, ala ls, except for codes 7-13 which are printed as + \a, \b, \t, \n, \v, \f and \r respectively. Spaces and backslashes are + also now backslashed as per ls, for better input to scripts unless -Q + is in use (where "'s are backslashed.) (Ujjwal Kumar) + - Added -U for unsorted listings (directory order). + - Added -c for sorting by last status change (ala ls -c). + - --dirsfirst is now a meta-sort and does not override -c, -v, -r or -t, but + is disabled by -U. + - After many requests, added the ability to process the entire tree before + emitting output. Used for the new options --du, which works like the du + command: sums the amount of space under each directory and prints a total + amount used in the report and the --prune option which will prune all empty + directories from the output (makes the -P option output much more readable.) + It should be noted that this will be slow to output when processing large + directory trees and can consume copious amounts of memory, use at your own + peril. + - Added -X option to emit the directory tree in XML format (turns colorization + off always.) + - Added --timefmt option to specify the format of time display (implies -D). + Uses the strftime format. + +Version 1.5.3 (11/24/09) + - Properly quote directories for the system command when tree is relaunched + using the -R option. + - Fixed possible indentation problem if dirs[*] is not properly zeroed + (Martin Nagy). + - Use strcoll() instead of strcmp() to sort files based on locale if set. + - Change "const static" to "static const" to remove some compiler warnings + for Solaris (Kamaraju Kusumanchi). + - Actually use TREE_CHARSET if it's defined. + - Automatically select UTF-8 charset if TREE_CHARSET is not set, and the + locale is set to *UTF-8 (overridden with --charset option.) + +Version 1.5.2.2 (01/22/09) + - Set locale before checking MB_CUR_MAX. + - Added HP-NonStop platform support (Craig McDaniel ) + - Fixed to support 32 bit UID/GIDs. + - Added Solaris build options to Makefile (edit and uncomment to use). + Provided by Wang Quanhong + +Version 1.5.2.1 (08/29/08) + - Added strverscmp.c file for os's without strverscmp. Source file is + attributed to: Jean-Franois Bignolles + - Try different approach to MB_CUR_MAX problem. + - Changed the argument to printit() to be signed char to avoid warnings. + +Version 1.5.2 (06/06/08) + - Added --filelimit X option to not descend directories that have more than + X number of files in them. + - Added -v option for version sorting (also called natural sorting) ala ls. + +Version 1.5.1.2 (06/04/08) + - Fixed compile issues related to MB_CUR_MAX on non-linux machines. + - Removed unecessary features.h + +Version 1.5.1.1 (06/11/07) + - Regression in HTML output, fixed formatting issues. + +Version 1.5.1 (?) + - Remove extraneous / at end of user input directory names when using -f + option (Zurd) + - List available charsets if --charset option is missing charset argument. + - Fixed --charset option processing bug. + - Fixed missing
's when -i option used with -H. + - Added -h option for human readable output. + - Colorization bugfix for special files and directories (make tree behave as + ls does) + +Version 1.5.0 (08/15/04) + - Added -T option to change title and H1 header in HTML output. + - Added -r option to reverse alpha sort output, ala. 'ls -r'. + - '|' wildcard support added by David MacMahon . + - Remove extraneous '/' at the end of dirs and dir-symlinks in HTML output. + - Removed several possible overflow problems by dynamically allocating + arrays in several places. + - Better support for Locales and printing utf-8 encoded characters in + filenames (still hackish). + - Fixed -t to alphasort files with same time-stamps. + - Fixed encoding of filenames in HTML output, Kyosuke and others. + - Patches by Kyosuke Tokoro : + - Now, runs OS/2 systems. + + Print the file attributes in 'adhrs' format for each file, instead + of the protections in 'drwxrwxrwx' format, when running the tree + on OS/2 with option -p. + - Added --charset option, to specify which character set is used for + output. + + You can specify any IANA registered character set name. But I have + tested only following character sets: + Shift_JIS EUC-JP IBM850 + UTF-8 ISO-8859-1 US-ASCII + + Now, `-S' option is equal to `--charset=IBM437'. + + When running on OS/2 systems, the default value of this option + is according to current codepage. On the other systems, no default. + - Change font-weight to font-size in CSS .VERSION. + - Change version to standard major.minor.patch format. + - Switch from artistic license to GPLv2. + +Version 1.4 (02/21/02 (b1), 03/24/02 (b2), 02/06/03 (b3)) + - Added large file support under Linux. + - Fixed crashing on missing command line arguments. + - Fixed several memory leaks + - Added --dirsfirst option to list directories first. + - Fixed formatting error when unable to open directories. + - Fixed bug in parse_dir_colors(). + - Changed -I to also ignore directories. + - Added --nolinks command to turn off hyperlinks with the HTML output. + - Fixed several memory leaks in listdir(). + - Some additional code cleanup in listdir(). + - Some systems may define TRUE/FALSE, so don't create the enums for TRUE + and FALSE if that's the case. + - Fixed over-allocation bug in read_dir(). + - Added crude beginnings of color output for HTML via CSS (Ted Tiberio + ttiberio@rochester.rr.com). + - Fixed buffer overflow problem in dircolors parsing. + - Fixed recursive symlink detection. + - Added --inodes and --device options. + - Added --noreport option. + +Version 1.3 (02/15/99) + - Fixed long pathname problem by dynamically allocating the path. + - Added recursive symlink detection. + - Added --help and --version options. + - When -C is used and LS_COLORS is undefined, tree uses a default color + scheme (thus -C always forces color output now). + - Added -S to show ASCII tree lines (Gerald Scheidl) + - Made tree more portable (Guido Socher and others) + + Following options courtesy of Francesc Rocher: + - Added -o to redirect the output. + - Added -H to print the tree in HTML format. + - Added -L to set the maximum level of directories to print. + - Added -R to recursively restart the search at the level given by `-L' + option (adding as well `-o 00Tree.html'). + +Version 1.2 (01/05/97) + - Added -D to print the date of the last modification. + - Added -t option to sort by last modification time (ala ls -t). + - Added -I , similar to the -P option except tree does not print + those files which match the pattern. + - Made tree print non-printable characters in filenames in standard unix + carrot notation. + - Added -N option to make tree print filenames without any processing. + - Added -q option to make tree print non-printable characters in filenames + as question marks. + - Added `|' to -F output and made it only print character type after the + link on sym-links, not on the symlink name itself. + - Added -u option to display username/uid, and -g option to display group + name/gid. + - Fully (pass the salt) implemented dircolors support. + +Version 1.1 (07/09/96) + - Made some changes to the Makefile to insure proper installation and for + multi-architecture support and a bug-fix. + - Made root directory colorized if dircolors is enabled. + - Put []'s around permission and size info, 'cause I think it looks better. + - Added -A option to enable ANSI-lines hack. + - Added some sanity checks for dircolors support. + - Added -P to list only those files that match the wildcard + given in . + - Fixed error where relative symbolic links to directories would not be + followed when the -l option was used. + - Made uid 0 the same as anyone else (-a was default for uid 0) + - Added -x directive to stay on one filesystem (ala find -xdev). + +Version 1.0 (??/??/90?) + - The original, a model of perfection... diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..671ad9d --- /dev/null +++ b/INSTALL @@ -0,0 +1,15 @@ +Installation instructions: + +1. Edit the Makefile for your OS. Comment out the Linux options and un-comment + the options for your OS. +2. Type: make +3. Type: make install +4. Enjoy colorful directory trees. + + I cannot test on non-Linux machines, so please feel free to contribute +porting information, bug reports, compile options, patches, etc for porting to +other OS's to ice@mama.indstate.edu. + + I would also welcome any localization efforts, particularly translating the +man page to other languages. And of course feel free to suggest options and +improvements you would like to see in tree. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4ca97b8 --- /dev/null +++ b/Makefile @@ -0,0 +1,115 @@ +# $Copyright: $ +# Copyright (c) 1996 - 2022 by Steve Baker +# All Rights reserved +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +PREFIX=/usr/local + +CC=gcc +INSTALL=install + +VERSION=2.0.4 +TREE_DEST=tree +DESTDIR=${PREFIX}/bin +MAN=tree.1 +# Probably needs to be ${PREFIX}/share/man for most systems now +MANDIR=${PREFIX}/man +OBJS=tree.o list.o hash.o color.o file.o filter.o info.o unix.o xml.o json.o html.o strverscmp.o + +# Uncomment options below for your particular OS: + +# Linux defaults: +#CFLAGS+=-ggdb -pedantic -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +CFLAGS+=-O3 -pedantic -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +#LDFLAGS+=-s + +# Uncomment for FreeBSD: +#CC=cc +#CFLAGS+=-O2 -Wall -fomit-frame-pointer +#LDFLAGS+=-s + +# Uncomment for OpenBSD: +#TREE_DEST=colortree +#MAN=colortree.1 +#CFLAGS+=-O2 -Wall -fomit-frame-pointer +#LDFLAGS+=-s + +# Uncomment for Solaris: +#CC=cc +#CFLAGS+=-xO0 -v +#LDFLAGS+= +#MANDIR=${prefix}/share/man + +# Uncomment for Cygwin: +#CFLAGS+=-O2 -Wall -fomit-frame-pointer +#LDFLAGS+=-s +#TREE_DEST=tree.exe + +# Uncomment for OS X: +# It is not allowed to install to /usr/bin on OS X any longer (SIP): +#CC=cc +#CFLAGS+=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp +#LDFLAGS+= +#MANDIR=${PREFIX}/share/man + +# Uncomment for HP/UX: +#prefix=/opt +#CC=cc +# manpage of mbsrtowcs() requires C99 and the two defines +#CFLAGS+=+w -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200112 -AC99 +#LDFLAGS+= +#MANDIR=${PREFIX}/share/man + +# Uncomment for OS/2: +#CFLAGS+=-02 -Wall -fomit-frame-pointer -Zomf -Zsmall-conv +#LDFLAGS+=-s -Zomf -Zsmall-conv + +# Uncomment for HP NonStop: +#prefix = /opt +#CC=c89 +#CFLAGS+=-Wextensions -WIEEE_float -g -Wnowarn=1506 -D_XOPEN_SOURCE_EXTENDED=1 \ +# -Wallow_cplusplus_comments +#LDFLAGS+= + +# AIX +#CC=cc_r -q64 +#LD=ld -d64 +#LDFLAGS+=-lc + +#------------------------------------------------------------ + +all: tree + +tree: $(OBJS) + $(CC) $(LDFLAGS) -o $(TREE_DEST) $(OBJS) + +$(OBJS): %.o: %.c tree.h + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(TREE_DEST) *.o *~ + +install: tree + $(INSTALL) -d $(DESTDIR) + $(INSTALL) -d $(MANDIR)/man1 + $(INSTALL) $(TREE_DEST) $(DESTDIR)/$(TREE_DEST); \ + $(INSTALL) -m 644 doc/$(MAN) $(MANDIR)/man1/$(MAN) + +distclean: + rm -f *.o *~ + +dist: distclean + tar zcf ../tree-$(VERSION).tgz -C .. `cat .tarball` diff --git a/README b/README new file mode 100644 index 0000000..016bbf7 --- /dev/null +++ b/README @@ -0,0 +1,286 @@ + Please read the INSTALL file for installation instructions, particularly if +you are installing on a non-Linux machine. + + This is a handy little utility to display a tree view of directories that +I wrote some time ago and just added color support to. I've decided that +since no one else has done something similar I would go ahead and release +it, even though it's barely a 1st year CS student hack. I've found it damn +handy to peruse a directory tree though, especially when someone is trying to +hide something from you. + + The main distribution site for tree is here: + http://oldmanprogrammer.net/code.php?src=tree + + Backup GIT sites are: + https://gitlab.com/OldManProgrammer/unix-tree + https://github.com/Old-Man-Programmer/tree + + Old site for as long as it lasts: + http://mama.indstate.edu/users/ice/tree/ + ftp://mama.indstate.edu/linux/tree/ + + If you don't like the way it looks let me know how you think it should be +formatted. Feel free to suggest modifications and additions. + + Thanks go out so the following people who have helped bring tree to the +pinnacle of perfection that it is: ;) + +Francesc Rocher + - Added HTML output (-H). + - Added options -o, -L and -R. + +Gerald Scheidl + - Added -S option to print ASCII graphics lines for use under linux + console when an alternate console font has been selected (might also + work under DOS telnet). + +Guido Socher (and others) + - Made tree more portable. Should compile under solaris. + +Mitja Lacen + - Discovered bug where tree will segmentation fault on long pathnames. + - Discovered in -L argument processing. + +Nathaniel Delage + - Discovered problem with recursive symlink detection + +A. Karthik + - Suggested option to remove file and directory report at end of tree + listing. + +Roger Luethi + - Spotted memory over-allocation bug in read_dir(). + - Submitted several patches to fix various memory leaks. + +Daniel Lee + - Reported that Tru64 defines TRUE/FALSE in sys/types.h (OSF1 standard?) + +Paolo Violini + - Found bug in tree that caused it to seg-fault if 50 file arguments where + given and directory coloring was turned on. + +Mitsuaki Masuhara + - Discovered tree crashed on missing arguments. + - Discovered that tree did not properly encode characters in filenames + when used as URLs when using the -H option. + - Fixed issue with --charset option processing. + +Johan Fredrik + - Pointed out that tree did not list large files. + +Ted Tiberio + - Submitted patch which fixed a compiler issue and cleaned up HTML and CSS + code, applied CSS to all output, and fixed up HTML to 4.01 strict + standards. + +David MacMahon + - Added '|' support to the pattern matching routines. + +Dan Jacobson + - Pointed out that -t did not sort properly for files with the same + timestamp. + - Suggested option to change HTML title and H1 string. + - Suggested -r option for reversed alphanumeric sort ala 'ls -r'. + +Kyosuke Tokoro + - Provided patch to support OS/2, fix HTML encoding, provide charset + support. Added to authors list. + +Florian Ernst + - Debian maintainer who pointed out problems and applied fire to feet to fix + stuff. + +Jack Cuyler + - Suggested -h option for human readable output for -s, ala ls -lh. + +Jonathon Cranford + - Supplied patch to make tree under cygwin. + +Richard Houser + - Provided patch to fix a colorization bug when dealing with special + files and directories that seem to have an extension. + +Zurd (?) + - Suggested removing trailing slash on user supplied directory names if -f + option is used. + +John Nintendud + - Pointed out broken HTML output in 1.5.1. + +Mark Braker + - Suggested --filelimit option. + +Michael Vogt + - Suggested -v option (version sort). + +Wang Quanhong + - Provided build options for Solaris. + +Craig McDaniel + - Provided build options and source mods for HP NonStop support. + +Christian Grigis + - Noted that setlocale() should come before MB_CUR_MAX check. + +Kamaraju Kusumanchi + - Submitted patch to remove compiler warnings for Solaris. + +Martin Nagy + - Provided patch which fixes issue where indent may output more than it + should when dirs[*] is not properly cleared before use. + +William C. Lathan III + - Showed that tree was not properly quoting arguments to recursively called + tree instances when using -R. + +Ulrich Eckhardt + - Submitted patch for --si option. + +Tim Waugh (redhat) + - Pointed out a potential memory leak in listdir(). + +Markus Schnalke + - Tracked down bug where tree would print "argetm" before the filename of a + symbolic link when the "target" option was specified for LINK in dircolors. + +Ujjwal Kumar + - Suggested that tree backslash spaces like ls does for script use. Made + output more like ls. + +Ivan Shmakov + - Pointed out multiple CLI defenciencies (via Debian) + +Mantas Mikulnas + - Provided patch to make tree more reliably detect the UTF-8 locale. + +Tim Mooney + - Noticed S_ISDOOR/S_IFDOOR spelling mistake for under Solaris. + +Han Hui + - Pointed out possible memory overflow in read_dir (path/lbuf not equal in size + to pathsize/lbufsize.) + +Ryan Hollis + - Pointed out problems with the Makefile w/ respect to OSX. + +Philipp M?ller + - Provided patch for filesize sorting. + +Sascha Zorn + - Pointed out that the HTML output was broken when -L 1 option was used. + +Alexandre Wendling + - Pointed out that modern systems may use 32 bit uid/gids which could lead + to a potential buffer overflow in the uid/gid to name mapping functions. + +Florian Sesser + - Provided patch to add JSON support. + +Brian Mattern & Jason A. Donenfeld + - Provided patch to add --matchdirs functionality. + +Jason A. Donenfeld + - Added --caseinsentive, renamed --ignore-case option. + - Bugged me a lot. + +Stephan Gabert + - Found a bug where the wrong inode (and device) information would be printed + for symbolic links. + +Nick Craig-Wood + - Fixed issue where mbstowcs() fails to null terminate the string due to + improper UTF-8 encoding leading to garbage being printed. + +Mantas Mikulėnas + - Fixed issue with malformed multibyte string handling. + +Wagner Camarao + - Pointed out that JSON size output ignored -h/--si flags + +John Lane, Tad, others + - Fixed JSON output hanging commas + +Jacob Wahlgren + - Improved command line switch error reporting. + - Symbolic links not displayed if a -P pattern is active + - Missing argument error reporting fixes on long format switches. + +Shawn Mehan + - Update BINDIR in Makefile for MacOS X -- It is not allowed to install + programs to /usr/bin on MacOS X any longer due to System Integrity + Protection (SIP) + +Kirill Kolyshkin + - Some man page fixes and cleanups + +Alyssa Ross + - Suggested adding support for BSD's CLICOLOR and CLICOLOR_FORCE environment + variables. + +Tomáš Beránek + - Make sure we always use xmalloc / xrealloc + +Sergei Maximov + - Make XML/HTML/JSON output mutually exclusive. + +Jonas Stein + - Deprecate using local -DLINUX / -DCYGWIN and use the OS provided defines + +John A. Fedoruk + - Suggested --filesfirst option. + +Michael Osipov + - Optimized makefile, HP/UX support. + +Richard Mitchell + - Suggested --metafirst option + +Paul Seyfert + - Honor -n (no color) even if the CLICOLOR_FORCE environment variable is set + +Filips Romāns via Debian + - Make tree colorization use reset (rs code in dir_colors,) not normal color + when resetting attributes. + +Chentao Credungtao via Debian + - Properly sort --fromfile input + +Jake Zimmerman (and others) + - Suggest support for .gitignore files (--gitignore option) + +Eric Pruitt + - Always HTML escape filenames in HTML output even when -C is used. + +Michiel Beijen (and others) + - Suggest Support multiple -I and -P instances. + - Suggest that / match directories in patterns (also Taylor Faubion) + - Suggested to update MANPATH for OS X + +Michal Vasilek + - Various Makefile fixes + +Josey Smith + - Reported an error with * in the patchmatch code where *foo*bar would match + *foo alone. + +Maxim Cournoyer + - Reported HTML url output issue w/ 2.0.0-2.0.1 + +Kenta Arai + - Reported Segfault with --filelimit option + +Ben Brown + - Updates to the Makefile + - Reported use after free error + +Erik Skultety + - Reported same use after error + +Saniya Maheshwari / Mig-hub ? / Carlos Pinto + - Reported various issues with --gitignore + +And many others whom I've failed to keep track of. I should have started +this list years ago. + + - Steve Baker + oldmanprogrammer.llc@gmail.com diff --git a/TODO b/TODO new file mode 100644 index 0000000..9429bbc --- /dev/null +++ b/TODO @@ -0,0 +1,27 @@ +Should do: + +- Use stdint.h and inttypes.h to standardize the int sizes and format strings. + Not sure how cross-platform this would be. + +- Add --DU option to fully report disk usage, taking into account files that + are not displayed in the output. + +- Make wide character support less of a hack. + +- Fully support HTML colorization properly and allow for an external stylesheet. + +- Might be nice to prune files by things like type, mode, access/modify/change + time, uid/gid, etc. ala find. + +- Just incorporate the stat structure into _info, since we now need most of + the structure anyway. + +Maybe do: + +- With the addition of TREE_COLORS, add some custom options to perhaps colorize + metadata like the permissions, date, username, etc, and change the color of + the tree lines and so on. + +- Refactor color.c. + +- Output BSON on stddata instead of JSON. diff --git a/color.c b/color.c new file mode 100644 index 0000000..6386fba --- /dev/null +++ b/color.c @@ -0,0 +1,464 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +/* + * Hacked in DIR_COLORS support for linux. ------------------------------ + */ +/* + * If someone asked me, I'd extend dircolors, to provide more generic + * color support so that more programs could take advantage of it. This + * is really just hacked in support. The dircolors program should: + * 1) Put the valid terms in a environment var, like: + * COLOR_TERMS=linux:console:xterm:vt100... + * 2) Put the COLOR and OPTIONS directives in a env var too. + * 3) Have an option to dircolors to silently ignore directives that it + * doesn't understand (directives that other programs would + * understand). + * 4) Perhaps even make those unknown directives environment variables. + * + * The environment is the place for cryptic crap no one looks at, but + * programs. No one is going to care if it takes 30 variables to do + * something. + */ +enum { + ERROR = -1, CMD_COLOR = 0, CMD_OPTIONS, CMD_TERM, CMD_EIGHTBIT, COL_RESET, + COL_NORMAL, COL_FILE, COL_DIR, COL_LINK, COL_FIFO, COL_DOOR, COL_BLK, COL_CHR, + COL_ORPHAN, COL_SOCK, COL_SETUID, COL_SETGID, COL_STICKY_OTHER_WRITABLE, + COL_OTHER_WRITABLE, COL_STICKY, COL_EXEC, COL_MISSING, + COL_LEFTCODE, COL_RIGHTCODE, COL_ENDCODE, +// Keep this one last, sets the size of the color_code array: + DOT_EXTENSION +}; + +enum { + MCOL_INODE, MCOL_PERMS, MCOL_USER, MCOL_GROUP, MCOL_SIZE, MCOL_DATE, + MCOL_INDENTLINES +}; + +bool colorize = FALSE, ansilines = FALSE, linktargetcolor = FALSE; +char *term, termmatch = FALSE, istty; + +char *color_code[DOT_EXTENSION+1] = {NULL}; + +char *vgacolor[] = { + "black", "red", "green", "yellow", "blue", "fuchsia", "aqua", "white", + NULL, NULL, + "transparent", "red", "green", "yellow", "blue", "fuchsia", "aqua", "black" +}; + +struct colortable colortable[11]; +struct extensions *ext = NULL; +const struct linedraw *linedraw; + +char **split(char *str, char *delim, int *nwrds); +int cmd(char *s); + +extern FILE *outfile; +extern bool Hflag, force_color, nocolor; +extern const char *charset; + +void parse_dir_colors() +{ + char buf[1025], **arg, **c, *colors, *s, *cc; + int i, n, col; + struct extensions *e; + + if (Hflag) return; + + if (getenv("TERM") == NULL) { + colorize = FALSE; + return; + } + + s = getenv("TREE_COLORS"); + if (s == NULL) s = getenv("LS_COLORS"); + cc = getenv("CLICOLOR"); + if (getenv("CLICOLOR_FORCE") != NULL && !nocolor) force_color=TRUE; + if ((s == NULL || strlen(s) == 0) && (force_color || cc != NULL)) 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:"; + + if (s == NULL || (!force_color && (nocolor || !isatty(1)))) { + colorize = FALSE; + return; + } + + colorize = TRUE; + + for(int i=0; i < DOT_EXTENSION; i++) color_code[i] = NULL; + + colors = scopy(s); + + arg = split(colors,":",&n); + + for(i=0;arg[i];i++) { + c = split(arg[i],"=",&n); + + switch(col = cmd(c[0])) { + case ERROR: + break; + case DOT_EXTENSION: + if (c[1]) { + e = xmalloc(sizeof(struct extensions)); + e->ext = scopy(c[0]+1); + e->term_flg = scopy(c[1]); + e->nxt = ext; + ext = e; + } + break; + case COL_LINK: + if (c[1] && strcasecmp("target",c[1]) == 0) { + linktargetcolor = TRUE; + color_code[COL_LINK] = "01;36"; /* Should never actually be used */ + break; + } + default: + if (c[1]) color_code[col] = scopy(c[1]); + break; + } + + free(c); + } + free(arg); + + /** + * Make sure at least reset (not normal) is defined. We're going to assume + * ANSI/vt100 support: + */ + if (!color_code[COL_LEFTCODE]) color_code[COL_LEFTCODE] = scopy("\033["); + if (!color_code[COL_RIGHTCODE]) color_code[COL_RIGHTCODE] = scopy("m"); + if (!color_code[COL_RESET]) color_code[COL_RESET] = scopy("0"); + if (!color_code[COL_ENDCODE]) { + sprintf(buf,"%s%s%s",color_code[COL_LEFTCODE],color_code[COL_RESET],color_code[COL_RIGHTCODE]); + color_code[COL_ENDCODE] = scopy(buf); + } + + free(colors); +} + +/* + * You must free the pointer that is allocated by split() after you + * are done using the array. + */ +char **split(char *str, char *delim, int *nwrds) +{ + int n = 128; + char **w = xmalloc(sizeof(char *) * n); + + w[*nwrds = 0] = strtok(str,delim); + + while (w[*nwrds]) { + if (*nwrds == (n-2)) w = xrealloc(w,sizeof(char *) * (n+=256)); + w[++(*nwrds)] = strtok(NULL,delim); + } + + w[*nwrds] = NULL; + return w; +} + +int cmd(char *s) +{ + static struct { + char *cmd; + char cmdnum; + } cmds[] = { + {"rs", COL_RESET}, {"no", COL_NORMAL}, {"fi", COL_FILE}, {"di", COL_DIR}, + {"ln", COL_LINK}, {"pi", COL_FIFO}, {"do", COL_DOOR}, {"bd", COL_BLK}, + {"cd", COL_CHR}, {"or", COL_ORPHAN}, {"so", COL_SOCK}, {"su", COL_SETUID}, + {"sg", COL_SETGID}, {"tw", COL_STICKY_OTHER_WRITABLE}, + {"ow", COL_OTHER_WRITABLE}, {"st", COL_STICKY}, {"ex", COL_EXEC}, + {"mi", COL_MISSING}, {"lc", COL_LEFTCODE}, {"rc", COL_RIGHTCODE}, + {"ec", COL_ENDCODE}, {NULL, 0} + }; + int i; + + if (s == NULL) return ERROR; // Probably can't happen + + if (s[0] == '*') return DOT_EXTENSION; + for(i=0;cmds[i].cmdnum;i++) { + if (!strcmp(cmds[i].cmd,s)) return cmds[i].cmdnum; + } + return ERROR; +} + +int print_color(int color) +{ + if (!color_code[color]) return FALSE; + + fputs(color_code[COL_LEFTCODE],outfile); + fputs(color_code[color],outfile); + fputs(color_code[COL_RIGHTCODE],outfile); + return TRUE; +} + +void endcolor(void) +{ + if (color_code[COL_ENDCODE]) + fputs(color_code[COL_ENDCODE],outfile); +} + +int color(u_short mode, char *name, bool orphan, bool islink) +{ + struct extensions *e; + int l, xl; + + if (orphan) { + if (islink) { + if (print_color(COL_MISSING)) return TRUE; + } else { + if (print_color(COL_ORPHAN)) return TRUE; + } + } + + // It's probably safe to assume short-circuit evaluation, but we'll do it this way: + switch(mode & S_IFMT) { + case S_IFIFO: + return print_color(COL_FIFO); + case S_IFCHR: + return print_color(COL_CHR); + case S_IFDIR: + if (mode & S_ISVTX) { + if ((mode & S_IWOTH)) + if (print_color(COL_STICKY_OTHER_WRITABLE)) return TRUE; + if (!(mode & S_IWOTH)) + if (print_color(COL_STICKY)) return TRUE; + } + if ((mode & S_IWOTH)) + if (print_color(COL_OTHER_WRITABLE)) return TRUE; + return print_color(COL_DIR); +#ifndef __EMX__ + case S_IFBLK: + return print_color(COL_BLK); + case S_IFLNK: + return print_color(COL_LINK); + #ifdef S_IFDOOR + case S_IFDOOR: + return print_color(COL_DOOR); + #endif +#endif + case S_IFSOCK: + return print_color(COL_SOCK); + case S_IFREG: + if ((mode & S_ISUID)) + if (print_color(COL_SETUID)) return TRUE; + if ((mode & S_ISGID)) + if (print_color(COL_SETGID)) return TRUE; + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + if (print_color(COL_EXEC)) return TRUE; + + /* not a directory, link, special device, etc, so check for extension match */ + l = strlen(name); + for(e=ext;e;e=e->nxt) { + xl = strlen(e->ext); + if (!strcmp((l>xl)?name+(l-xl):name,e->ext)) { + fputs(color_code[COL_LEFTCODE], outfile); + fputs(e->term_flg, outfile); + fputs(color_code[COL_RIGHTCODE], outfile); + return TRUE; + } + } + /* colorize just normal files too */ + return print_color(COL_FILE); + } + return print_color(COL_NORMAL); +} + +/* + * Charsets provided by Kyosuke Tokoro (NBG01720@nifty.ne.jp) + */ +const char *getcharset(void) +{ + #ifndef __EMX__ + return getenv("TREE_CHARSET"); + #else + static char buffer[13]; + ULONG aulCpList[3],ulListSize,codepage=0; + char*charset=getenv("TREE_CHARSET"); + if(charset) + return charset; + + if(!getenv("WINDOWID")) + if(!DosQueryCp(sizeof aulCpList,aulCpList,&ulListSize)) + if(ulListSize>=sizeof*aulCpList) + codepage=*aulCpList; + + switch(codepage){ + case 437: case 775: case 850: case 851: case 852: case 855: + case 857: case 860: case 861: case 862: case 863: case 864: + case 865: case 866: case 868: case 869: case 891: case 903: + case 904: + sprintf(buffer,"IBM%03lu",codepage); + break; + case 367: + return"US-ASCII"; + case 813: + return"ISO-8859-7"; + case 819: + return"ISO-8859-1"; + case 881: case 882: case 883: case 884: case 885: + sprintf(buffer,"ISO-8859-%lu",codepage-880); + break; + case 858: case 924: + sprintf(buffer,"IBM%05lu",codepage); + break; + case 874: + return"TIS-620"; + case 897: case 932: case 942: case 943: + return"Shift_JIS"; + case 912: + return"ISO-8859-2"; + case 915: + return"ISO-8859-5"; + case 916: + return"ISO-8859-8"; + case 949: case 970: + return"EUC-KR"; + case 950: + return"Big5"; + case 954: + return"EUC-JP"; + case 1051: + return"hp-roman8"; + case 1089: + return"ISO-8859-6"; + case 1250: case 1251: case 1253: case 1254: case 1255: case 1256: + case 1257: case 1258: + sprintf(buffer,"windows-%lu",codepage); + break; + case 1252: + return"ISO-8859-1-Windows-3.1-Latin-1"; + default: + return NULL; + } + #endif +} + +void initlinedraw(int flag) +{ + static const char*latin1_3[]={ + "ISO-8859-1", "ISO-8859-1:1987", "ISO_8859-1", "latin1", "l1", "IBM819", + "CP819", "csISOLatin1", "ISO-8859-3", "ISO_8859-3:1988", "ISO_8859-3", + "latin3", "ls", "csISOLatin3", NULL + }; + static const char*iso8859_789[]={ + "ISO-8859-7", "ISO_8859-7:1987", "ISO_8859-7", "ELOT_928", "ECMA-118", + "greek", "greek8", "csISOLatinGreek", "ISO-8859-8", "ISO_8859-8:1988", + "iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew", "ISO-8859-9", + "ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9", "latin5", "l5", + "csISOLatin5", NULL + }; + static const char*shift_jis[]={ + "Shift_JIS", "MS_Kanji", "csShiftJIS", NULL + }; + static const char*euc_jp[]={ + "EUC-JP", "Extended_UNIX_Code_Packed_Format_for_Japanese", + "csEUCPkdFmtJapanese", NULL + }; + static const char*euc_kr[]={ + "EUC-KR", "csEUCKR", NULL + }; + static const char*iso2022jp[]={ + "ISO-2022-JP", "csISO2022JP", "ISO-2022-JP-2", "csISO2022JP2", NULL + }; + static const char*ibm_pc[]={ + "IBM437", "cp437", "437", "csPC8CodePage437", "IBM852", "cp852", "852", + "csPCp852", "IBM863", "cp863", "863", "csIBM863", "IBM855", "cp855", + "855", "csIBM855", "IBM865", "cp865", "865", "csIBM865", "IBM866", + "cp866", "866", "csIBM866", NULL + }; + static const char*ibm_ps2[]={ + "IBM850", "cp850", "850", "csPC850Multilingual", "IBM00858", "CCSID00858", + "CP00858", "PC-Multilingual-850+euro", NULL + }; + static const char*ibm_gr[]={ + "IBM869", "cp869", "869", "cp-gr", "csIBM869", NULL + }; + static const char*gb[]={ + "GB2312", "csGB2312", NULL + }; + static const char*utf8[]={ + "UTF-8", "utf8", NULL + }; + static const char*big5[]={ + "Big5", "csBig5", NULL + }; + static const char*viscii[]={ + "VISCII", "csVISCII", NULL + }; + static const char*koi8ru[]={ + "KOI8-R", "csKOI8R", "KOI8-U", NULL + }; + static const char*windows[]={ + "ISO-8859-1-Windows-3.1-Latin-1", "csWindows31Latin1", + "ISO-8859-2-Windows-Latin-2", "csWindows31Latin2", "windows-1250", + "windows-1251", "windows-1253", "windows-1254", "windows-1255", + "windows-1256", "windows-1256", "windows-1257", NULL + }; + + static const struct linedraw cstable[]={ + { latin1_3, "| ", "|--", "·--", "©", + " [", " [", " [", " [", " [" }, + { iso8859_789, "| ", "|--", "·--", "(c)", + " [", " [", " [", " [", " [" }, + { shift_jis, "\204\240 ", "\204\245", "\204\244", "(c)", + " [", " [", " [", " [", " [" }, + { euc_jp, "\250\242 ", "\250\247", "\250\246", "(c)", + " [", " [", " [", " [", " [" }, + { euc_kr, "\246\242 ", "\246\247", "\246\246", "(c)", + " [", " [", " [", " [", " [" }, + { iso2022jp, "\033$B(\"\033(B ", "\033$B('\033(B", "\033$B(&\033(B", "(c)", + " [", " [", " [", " [", " [" }, + { ibm_pc, "\263 ", "\303\304\304", "\300\304\304", "(c)", + " [", " [", " [", " [", " [" }, + { ibm_ps2, "\263 ", "\303\304\304", "\300\304\304", "\227", + " [", " [", " [", " [", " [" }, + { ibm_gr, "\263 ", "\303\304\304", "\300\304\304", "\270", + " [", " [", " [", " [", " [" }, + { gb, "\251\246 ", "\251\300", "\251\270", "(c)", + " [", " [", " [", " [", " [" }, + { utf8, "\342\224\202\302\240\302\240", "\342\224\234\342\224\200\342\224\200", + "\342\224\224\342\224\200\342\224\200", "\302\251", + " \342\216\247", " \342\216\251", " \342\216\250", " \342\216\252", " {" }, + { big5, "\242x ", "\242u", "\242|", "(c)", + " [", " [", " [", " [", " [" }, + { viscii, "| ", "|--", "`--", "\371", + " [", " [", " [", " [", " [" }, + { koi8ru, "\201 ", "\206\200\200", "\204\200\200", "\277", + " [", " [", " [", " [", " [" }, + { windows, "| ", "|--", "`--", "\251", + " [", " [", " [", " [", " [" }, + { NULL, "| ", "|--", "`--", "(c)", + " [", " [", " [", " [", " [" }, + }; + const char**s; + + if (flag) { + fprintf(stderr,"tree: missing argument to --charset, valid charsets include:\n"); + for(linedraw=cstable;linedraw->name;++linedraw) { + for(s=linedraw->name;*s;++s) { + fprintf(stderr," %s\n",*s); + } + } + return; + } + if (charset) { + for(linedraw=cstable;linedraw->name;++linedraw) + for(s=linedraw->name;*s;++s) + if(!strcasecmp(charset,*s)) return; + } + linedraw=cstable+sizeof cstable/sizeof*cstable-1; +} diff --git a/color.o b/color.o new file mode 100644 index 0000000..6526487 Binary files /dev/null and b/color.o differ diff --git a/doc/global_info b/doc/global_info new file mode 100644 index 0000000..d0ef7a2 --- /dev/null +++ b/doc/global_info @@ -0,0 +1,96 @@ +# An example of a possible global info file. +/bin + Binaries needed before a /usr is mounted (single user mode). +/boot + Kernels / boot loader files +/dev + Device files +/etc + System-wide configuration files. +/etc/rc.d/ + Slackware init scripts +/etc/X11 + X windows system configuration files. +/home + Typically user home directories +*/lost+found + Contains files recovered by fsck in the event of file-system damage +/lib +/lib64 + System libraries (essential for binaries in /bin, /sbin) +/media +/mnt + Spare, usually temporary mount points, usually for removable media + (/media) +/opt + Optional local packages +/proc + System & process information virtual pseudo-filesystem (man 5 proc) +/root + Root's home directory +/run + System runtime files +/sbin + System (admin) binaries needed before /usr is mounted +/srv + Files served by the system (e.g. web service) +/sys + System information pseudo-filesystem +/tmp + System wide temporary files, not guaranteed to be preserved between + reboots +/usr + Read-only data, historically a secondary hierarchy to be mounted after + '/', so binaries / data that might not be available during boot. +/usr/bin + User binaries, not needed at boot. +/usr/include + Standard include libraries (C, C++) +/usr/lib +/usr/lib64 + Libraries (for binaries in /usr/bin, /usr/sbin) +/usr/local + A tertiary hierarchy for local system additions +/usr/sbin + Non-essential system binaries (e.g. daemons) +/usr/share + Shared data +/usr/src + Source code (e.g. kernel source) +/usr/X11R6 + X windows (Version 11, release 6) +/var + Variable files, files that will likely change during run-time. +/var/cache + Application cache data. +/var/lib + State information generated by programs such as databases and + package managers +/var/lib/pkgtools/packages + Slackware package database +/var/lib/pkgtools/setup + Slackware setup/configuration scripts +/var/lock + Lock files +/var/log + log files (syslogd, klogd, httpd, other daemons) +/var/mail + User mailboxes +/var/spool + Spool directories for tasks waiting to be processed (cron, + mail, print files) (depreciates /var/spool/mail) +/var/tmp + Temporary files to be preserved across reboots +/proc/1 + Each one of these subdirectories contains files and subdirectories exposing + information about the process with the corresponding process ID. Underneath + each of the /proc/[pid] directories, a task subdirectory contains subdirectories + of the form task/[tid], which contain corresponding information about each of the + threads in the process, where tid is the kernel thread ID of the thread. The + /proc/[pid] subdirectories are visible when iterating through /proc with getdents(2) + (and thus are visible when one uses ls(1) to view the contents of /proc). +/proc/self + When a process accesses this magic symbolic link, it resolves to the process's own /proc/[pid] directory. +/proc/thread-self + When a thread accesses this magic symbolic link, it resolves to the process's own /proc/self/task/[tid] directory. +# Could go on to add all of man 5 proc for example here. \ No newline at end of file diff --git a/doc/tree.1 b/doc/tree.1 new file mode 100644 index 0000000..c803e46 --- /dev/null +++ b/doc/tree.1 @@ -0,0 +1,470 @@ +.\" $Copyright: $ +.\" Copyright (c) 1996 - 2021 by Steve Baker +.\" All Rights reserved +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +.\" +... +.TH TREE 1 "" "Tree 2.0.0" +.SH NAME +tree \- list contents of directories in a tree-like format. +.SH SYNOPSIS +\fBtree\fP +[\fB-acdfghilnpqrstuvxACDFJQNSUX\fP] +[\fB-L\fP \fIlevel\fP [\fB-R\fP]] +[\fB-H\fP \fIbaseHREF\fP] +[\fB-T\fP \fItitle\fP] +[\fB-o\fP \fIfilename\fP] +[\fB-P\fP \fIpattern\fP] +[\fB-I\fP \fIpattern\fP] +[\fB--gitignore\fP] +[\fB--matchdirs\fP] +[\fB--metafirst\fP] +[\fB--ignore-case\fP] +[\fB--nolinks\fP] +[\fB--inodes\fP] +[\fB--device\fP] +[\fB--sort\fP[\fB=\fP]\fIname\fP] +[\fB--dirsfirst\fP] +[\fB--filesfirst\fP] +[\fB--filelimit\fP \fI#\fP] +[\fB--si\fP] +[\fB--du\fP] +[\fB--prune\fP] +[\fB--timefmt\fP[\fB=\fP]\fIformat\fP] +[\fB--fromfile\fP] +[\fB--info\fP] +[\fB--noreport\fP] +[\fB--version\fP] +[\fB--help\fP] +[\fB--\fP] [\fIdirectory\fP ...] + +.br +.SH DESCRIPTION +\fITree\fP is a recursive directory listing program that produces a depth +indented listing of files, which is colorized ala \fIdircolors\fP if the +\fBLS_COLORS\fP environment variable is set and output is to tty. With no +arguments, \fItree\fP lists the files in the current directory. When directory +arguments are given, \fItree\fP lists all the files and/or directories found in +the given directories each in turn. Upon completion of listing all +files/directories found, \fItree\fP returns the total number of files and/or +directories listed. + +By default, when a symbolic link is encountered, the path that the symbolic +link refers to is printed after the name of the link in the format: +.br + + name -> real-path +.br + +If the `\fB-l\fP' option is given and the symbolic link refers to an actual +directory, then \fItree\fP will follow the path of the symbolic link as if +it were a real directory. +.br + +.SH OPTIONS +\fITree\fP understands the following command line switches: + +.SH LISTING OPTIONS + +.TP +.B -a +All files are printed. By default tree does not print hidden files (those +beginning with a dot `.'). In no event does tree print the file system +constructs `.' (current directory) and `..' (previous directory). +.PP +.TP +.B -d +List directories only. +.PP +.TP +.B -l +Follows symbolic links if they point to directories, as if they were +directories. Symbolic links that will result in recursion are avoided when +detected. +.PP +.TP +.B -f +Prints the full path prefix for each file. +.PP +.TP +.B -x +Stay on the current file-system only. Ala \fBfind \fI-xdev\fP. +.PP +.TP +.B -L \fIlevel\fP +Max display depth of the directory tree. +.PP +.TP +.B -R +Recursively cross down the tree each \fIlevel\fP directories (see \fB-L\fP +option), and at each of them execute \fBtree\fP again adding `-o 00Tree.html' +as a new option. +.PP +.TP +.B -P \fIpattern\fP +List only those files that match the wild-card \fIpattern\fP. You may have +multiple -P options. Note: you must use the \fI-a\fP option to also consider +those files beginning with a dot `.' for matching. Valid wildcard operators +are `*' (any zero or more characters), `**` (any zero or more characters as +well as null /'s, i.e. /**/ may match a single /), `?' (any single character), +`[...]' (any single character listed between brackets (optional - (dash) for +character range may be used: ex: [A-Z]), and `[^...]' (any single character +not listed in brackets) and `|' separates alternate patterns. A '/' at the +end of the pattern matches directories, but not files. +.PP +.TP +.B -I \fIpattern\fP +Do not list those files that match the wild-card \fIpattern\fP. You may have +multiple -I options. See \fI-P\fP above for information on wildcard patterns. +.PP +.TP +.B --gitignore +Uses git \fB.gitignore\fP files for filtering files and directories. Also +uses \fB$GIT_DIR/info/exclude\fP if present. +.PP +.TP +.B --ignore-case +If a match pattern is specified by the \fB-P\fP or \fB-I\fP option, this will +cause the pattern to match without regards to the case of each letter. +.PP +.TP +.B --matchdirs +If a match pattern is specified by the \fB-P\fP option, this will cause the +pattern to be applied to directory names (in addition to filenames). In the +event of a match on the directory name, matching is disabled for the directory's +contents. If the \fB--prune\fP option is used, empty folders that match the +pattern will not be pruned. +.PP +.TP +.B --metafirst +Print the meta-data information at the beginning of the line rather than +after the indentation lines. +.PP +.TP +.B --prune +Makes tree prune empty directories from the output, useful when used in +conjunction with \fB-P\fP or \fB-I\fP. See \fBBUGS AND NOTES\fP below for +more information on this option. +.PP +.TP +.B --info +Prints file comments found in .info files. See \fB.INFO FILES\fP below +for more information on the format of .info files. +.PP +.TP +.B --noreport +Omits printing of the file and directory report at the end of the tree +listing. +.PP +.TP +.B --charset \fIcharset\fP +Set the character set to use when outputting HTML and for line drawing. +.PP +.TP +.B --filelimit \fI#\fP +Do not descend directories that contain more than \fI#\fP entries. +.PP +.TP +.B --timefmt \fIformat\fP +Prints (implies -D) and formats the date according to the format string +which uses the \fBstrftime\fP(3) syntax. +.PP +.TP +.B -o \fIfilename\fP +Send output to \fIfilename\fP. +.PP + +.SH FILE OPTIONS + +.TP +.B -q +Print non-printable characters in filenames as question marks instead of the +default. +.PP +.TP +.B -N +Print non-printable characters as is instead of as escaped octal numbers. +.PP +.TP +.B -Q +Quote the names of files in double quotes. +.PP +.TP +.B -p +Print the file type and permissions for each file (as per ls -l). +.PP +.TP +.B -u +Print the username, or UID # if no username is available, of the file. +.PP +.TP +.B -g +Print the group name, or GID # if no group name is available, of the file. +.PP +.TP +.B -s +Print the size of each file in bytes along with the name. +.PP +.TP +.B -h +Print the size of each file but in a more human readable way, e.g. appending a +size letter for kilobytes (K), megabytes (M), gigabytes (G), terabytes (T), +petabytes (P) and exabytes (E). +.PP +.TP +.B --si +Like \fB-h\fP but use SI units (powers of 1000) instead. +.PP +.TP +.B --du +For each directory report its size as the accumulation of sizes of all its files +and sub-directories (and their files, and so on). The total amount of used +space is also given in the final report (like the 'du -c' command.) This option +requires tree to read the entire directory tree before emitting it, see +\fBBUGS AND NOTES\fP below. Implies \fB-s\fP. +.PP +.TP +.B -D +Print the date of the last modification time or if \fB-c\fP is used, the last +status change time for the file listed. +.PP +.TP +.B -F +Append a `/' for directories, a `=' for socket files, a `*' for executable +files, a `>' for doors (Solaris) and a `|' for FIFO's, as per ls -F +.PP +.TP +.B --inodes +Prints the inode number of the file or directory +.PP +.TP +.B --device +Prints the device number to which the file or directory belongs +.PP + +.SH SORTING OPTIONS + +.TP +.B -v +Sort the output by version. +.PP +.TP +.B -t +Sort the output by last modification time instead of alphabetically. +.PP +.TP +.B -c +Sort the output by last status change instead of alphabetically. Modifies the +\fB-D\fP option (if used) to print the last status change instead of +modification time. +.PP +.TP +.B -U +Do not sort. Lists files in directory order. Disables \fB--dirsfirst\fP. +.PP +.TP +.B -r +Sort the output in reverse order. This is a meta-sort that alter the above sorts. +This option is disabled when \fB-U\fP is used. +.PP +.TP +.B --dirsfirst +List directories before files. This is a meta-sort that alters the above sorts. +This option is disabled when \fB-U\fP is used. +.PP +.TP +.B --filesfirst +List files before directories. This is a meta-sort that alters the above sorts. +This option is disabled when \fB-U\fP is used. +.PP +.TP +.B --sort\fR[\fB=\fR]\fItype\fR +Sort the output by \fItype\fR instead of name. Possible values are: +\fBctime\fR (\fB-c\fP), +\fBmtime\fR (\fB-t\fB), \fBsize\fR, or \fBversion\fR (\fB-v\fB). + +.SH GRAPHICS OPTIONS + +.TP +.B -i +Makes tree not print the indentation lines, useful when used in conjunction +with the \fB-f\fP option. Also removes as much whitespace as possible when used +with the \fB-J\fP or \fB-X\fP options. +.PP +.TP +.B -A +Turn on ANSI line graphics hack when printing the indentation lines. +.PP +.TP +.B -S +Turn on CP437 line graphics (useful when using Linux console mode fonts). This +option is now equivalent to `--charset=IBM437' and may eventually be depreciated. +.PP +.TP +.B -n +Turn colorization off always, over-ridden by the \fB-C\fP option, however +overrides CLICOLOR_FORCE if present. +.PP +.TP +.B -C +Turn colorization on always, using built-in color defaults if the LS_COLORS or +TREE_COLORS environment variables are not set. Useful to colorize output to a +pipe. +.PP + +.SH XML/JSON/HTML OPTIONS + +.TP +.B -X +Turn on XML output. Outputs the directory tree as an XML formatted file. +.PP +.TP +.B -J +Turn on JSON output. Outputs the directory tree as a JSON formatted array. +.PP +.TP +.B -H \fIbaseHREF\fP +Turn on HTML output, including HTTP references. Useful for ftp sites. +\fIbaseHREF\fP gives the base ftp location when using HTML output. That is, the +local directory may be `/local/ftp/pub', but it must be referenced as +`ftp://hostname.organization.domain/pub' (\fIbaseHREF\fP should be +`ftp://hostname.organization.domain'). Hint: don't use ANSI lines with this +option, and don't give more than one directory in the directory list. If you +wish to use colors via CSS style-sheet, use the -C option in addition to this +option to force color output. +.PP +.TP +.B -T \fItitle\fP +Sets the title and H1 header string in HTML output mode. +.PP +.TP +.B --nolinks +Turns off hyperlinks in HTML output. +.PP + +.SH INPUT OPTIONS + +.B --fromfile +Reads a directory listing from a file rather than the file-system. Paths +provided on the command line are files to read from rather than directories to +search. The dot (.) directory indicates that tree should read paths from +standard input. NOTE: this is only suitable for reading the output of a program +such as find, not 'tree -fi' as symlinks cannot (at least as yet) be distinguished +from files that simply contain ' -> ' as part of the filename. + +.SH MISC OPTIONS + +.TP +.B --help +Outputs a verbose usage listing. +.PP +.TP +.B --version +Outputs the version of tree. +.PP +.TP +.B -- +Option processing terminator. No further options will be processed after this. +.PP + +.SH .INFO FILES + +\fB.info\fP files are similiar to \.gitignore files, if a .info file is found +while scanning a directory it is read and added to a stack of .info +information. Each file is composed of comments (lines starting with hash marks +(#),) or wild-card patterns which may match a file relative to the directory +the \.info file is found in. If a file should match a pattern, the tab indented +comment that follows the pattern is used as the file comment. A comment is +terminated by a non-tab indented line. Multiple patterns, each to a line, may +share the same comment. + +.br +.SH FILES +\fB/etc/DIR_COLORS\fP System color database. +.br +\fB~/.dircolors\fP Users color database. +.br +\fB.gitignore\fP Git exclusion file +.br +\fB$GIT_DIR/info/exclude\fP Global git file exclusion list +.br +\fB.info\fP File comment file +.br +\fB/usr/share/finfo/global_info\fP Global file comment file + + +.SH ENVIRONMENT +\fBLS_COLORS\fP Color information created by dircolors +.br +\fBTREE_COLORS\fP Uses this for color information over LS_COLORS if it is set. +.br +\fBTREE_CHARSET\fP Character set for tree to use in HTML mode. +.br +\fBCLICOLOR\fP Enables colorization even if TREE_COLORS or LS_COLORS is not set. +.br +\fBCLICOLOR_FORCE\fP Always enables colorization (effectively -C) +.br +\fBLC_CTYPE\fP Locale for filename output. +.br +\fBLC_TIME\fP Locale for timefmt output, see \fBstrftime\fP(3). +.br +\fBTZ\fP Timezone for timefmt output, see \fBstrftime\fP(3). +.br +\fBSTDDATA_FD\fP Enable the stddata feature, optionally set descriptor to use. + +.SH AUTHOR +Steve Baker (ice@mama.indstate.edu) +.br +HTML output hacked by Francesc Rocher (rocher@econ.udg.es) +.br +Charsets and OS/2 support by Kyosuke Tokoro (NBG01720@nifty.ne.jp) + +.SH BUGS AND NOTES +Tree does not prune "empty" directories when the -P and -I options are used by +default. Use the --prune option. + +The -h and --si options round to the nearest whole number unlike the ls +implementations which rounds up always. + +Pruning files and directories with the -I, -P and --filelimit options will +lead to incorrect file/directory count reports. + +The --prune and --du options cause tree to accumulate the entire tree in memory +before emitting it. For large directory trees this can cause a significant delay +in output and the use of large amounts of memory. + +The timefmt expansion buffer is limited to a ridiculously large 255 characters. +Output of time strings longer than this will be undefined, but are guaranteed +to not exceed 255 characters. + +XML/JSON trees are not colored, which is a bit of a shame. + +Probably more. + +As of version 2.0.0, in Linux, tree will attempt to automatically output a +compact JSON tree on file descriptor 3 (what I call stddata,) if present and the +environment variable STDDATA_FD is defined or set to a positive non-zero file +descriptor value to use to output on. It is hoped that some day a better +Linux/Unix shell may take advantage of this feature, though BSON would probably +be a better format for this. + +.SH SEE ALSO +.BR dircolors (1), +.BR ls (1), +.BR find (1), +.BR du (1), +.BR strftime (3) +.BR gitignore (5) diff --git a/doc/tree.fr.1 b/doc/tree.fr.1 new file mode 100644 index 0000000..858b490 --- /dev/null +++ b/doc/tree.fr.1 @@ -0,0 +1,235 @@ +.\" $Copyright: $ +.\" Copyright (c) 1996 - 2001 by Steve Baker +.\" All Rights reserved +.\" +.\" This software is provided as is without any express or implied +.\" warranties, including, without limitation, the implied warranties +.\" of merchantability and fitness for a particular purpose. +.\" +... +.V= $Header: tree.1 1.4 1996 - 2001 $ +.TH TREE 1 "\*(V)" "Manuel du Programmeur UNIX" +.SH NOM +tree \- liste le contenu de rpertoires sous forme d'arborescence +.SH SYNOPSIS +\fBtree\fP [ \fB-adfgilnopqstuxACDFNS\fP ] [ \fB-L\fP \fIniveau\fP [ \fB-R\fP ]] [ \fB-H\fP \fIbaseHREF\fP ] [ \fB-P\fP \fImotif\fP ] [ \fB-I\fP \fImotif\fP ] [ \fIrepertoire\fP ... ] +.br +.SH DESCRIPTION +\fITree\fP est un programme de listage rcursif de rpertoires qui produit +un listage des fichiers indent en profondeur, qui est mis en couleurs la +manire de \fIdircolors\fP si la variable d'environnement \fBLS_COLORS\fP +est active et que la sortie est un tty. Sans arguments, \fItree\fP liste +les fichiers contenus dans le rpertoire courant. Lorsque des rpertoires +sont donns en argument, \fItree\fP liste tous les fichiers et/ou +rpertoires trouvs dans les rpertoires donns, chacun son tour. Aprs +avoir affich tous les fichiers et rpertoires trouvs, \fItree\fP renvoie +le nombre total de fichiers et/ou de rpertoires lists. + +Par dfaut, quand un lien symbolique est rencontr, la chemin auquel il +renvoie est affich aprs le nom du lien sous la forme suivante\ : + +.br + + nom -> chemin-reel +.br + +Si l'on donne l'option `\fB-l\fP' et que le lien symbolique renvoie un +rpertoire existant, \fItree\fP suivra le chemin du lien symbolique +comme s'il s'agissait d'un vritable rpertoire. + +.br + +.SH OPTIONS +\fITree\fP comprend les commutateurs suivants en ligne de commande\ : + +.TP +.B --help +Donne en sortie un listage verbeux des options d'utilisation +.PP +.TP +.B --version +Sort la version de tree +.PP +.TP +.B --inodes +Affiche le numro d'inode du fichier ou du rpertoire +.PP +.TP +.B --device +Affiche le numro de priphrique auquel le fichier ou le rpertoire +appartient +.PP +.TP +.B --noreport +Omet d'afficher le compte des fichiers et rpertoires la fin du listage +arborescent +.PP +.TP +.B --nolinks +Dsactive les hyperliens en sortie HTML +.PP +.TP +.B -a +Tous les fichiers sont affichs. Par dfaut tree n'affiche pas les fichiers +cachs (ceux qui commencent par un point `.'). En aucun cas, tree n'affiche +les constructeurs de systme de fichiers `.' (rpertoire courant) et `..' +(rpertoire parent). +.PP +.TP +.B -d +Liste uniquement les rpertoires +.PP +.TP +.B -f +Affiche le chemin complet en prfixe de chaque fichier +.PP +.TP +.B -i +Fait que tree n'affiche pas les lignes d'indentation, ce qui est utile en +conjonction avec l'option \fB-f\fP +.PP +.TP +.B -l +Suit les liens symboliques s'ils pointent sur des rpertoires, comme +s'ils taient des rpertoires. Les liens symboliques qui finiraient de +faon rcursive sont vits, une fois dtects +.PP +.TP +.B -x +Reste exclusivement dans le systme de fichiers courant, la manire de +\fBfind \fI-xdev\fP +.PP +.TP +\fB-P \fImotif\fP +Liste exclusivement les fichiers correspondant au joker +\fImotif\fP. Remarque\ : vous devez utiliser l'option \fI-a\fP pour prendre +galement en considration les fichiers commenant par un point `.'. Les +oprateurs de joker valides sont `*' (zro caractre ou plus), `?' +(n'importe quel caractre), `[...]' (n'importe quel caractre parmi ceux +qui sont lists entre crochets (un tiret `-' optionnel peut tre utilis +pour une plage de caractres\ ; par exemple\ : [A-Z])), et `[^...]' +(n'importe quel caractre l'exclusion de ceux qui sont lists entre +crochets). +.PP +.TP +.B -I \fImotif\fP +Ne pas lister les fichiers correspondant au joker \fImotif\fP +.PP +.TP +.B -p +Afficher les protections pour chaque fichier (comme avec ls -l) +.PP +.TP +.B -o \fInom-de-fichier\fP +Envoie la sortie vers \fInom-de-fichier\fP +.PP +.TP +.B -s +Affiche la taille de chaque fichier devant son nom +.PP +.TP +.B -u +Affiche le nom d'utilisateur du fichier, ou le numro d'UID si aucun nom +d'utilisateur n'est disponible +.PP +.TP +.B -g +Affiche le nom de groupe du fichier, ou le numro de GID si aucun nom de +groupe n'est disponible +.PP +.TP +.B -D +Affiche la date et l'heure de dernire modification du fichier +.PP +.TP +.B -F +Appose un `/' pour les rpertoires, un `=' pour les fichiers de socket, un +`*' pour les fichiers excutables et un `|' pour les FIFO, comme avec ls\ -F +.PP +.TP +.B -q +Affiche les caractres non affichables dans les noms de fichiers sous forme +de points d'interrogation au lieu de la notation carret par dfaut +.PP +.TP +.B -N +Affiche les caractres non affichables tels quels au lieu de la notation +carret par dfaut +.PP +.TP +.B -t +Trie la sortie par horaire de dernire modification, au lieu de trier +alphabtiquement +.PP +.TP +.B -n +Dsactive toujours la mise en couleurs, force par l'option \fB-C\fP +.PP +.TP +.B -C +Active toujours la mise en couleurs, au moyen des couleurs intgres par +dfaut si la variable d'environnement LS_COLORS n'est pas active. Utile +pour mettre en couleurs la sortie vers un tube. +.PP +.TP +.B -A +Active le graphisme ANSI de lignes pour l'affichage des lignes +d'indentation +.PP +.TP +.B -S +Active le graphisme ASCII de lignes (utile pour utiliser les polices du +mode console de Linux) +.PP +.TP +.B -L \fIniveau\fP +Maximum de profondeur d'affichage de l'arborescence des rpertoires +.PP +.TP +.B -R +Descendre rcursivement dans l'arborescence de chaque niveau de rpertoires +(voir l'option \fB-L\fP), et pour chacun d'eux excuter de nouveau +\fBtree\fP en y ajoutant `-o 00Tree.html' comme nouvelle option. +.PP +.TP +.B -H \fIbaseHREF\fP +Active la sortie HTML, en incluant des rfrences HTTP. Utile pour les +sites FTP. \fIbaseHREF\fP donne l'emplacement ftp de base lorsque l'on +utilise la sortie HTML. C'est--dire que le rpertoire local peut tre +`/local/ftp/pub', mais il doit tre rfrenc en tant que +`ftp://hostname.organization.domain/pub' (\fIbaseHREF\fP doit tre +`ftp://hostname.organization.domain'). Conseils\ : n'utilisez pas les lignes +ANSI avec cette option, et n'indiquez pas plus d'un rpertoire dans la +liste des rpertoires. Si vous voulez utiliser des couleurs via une feuille +de style CSS, utilisez l'option -C en plus de cette option pour forcer la +sortie en couleurs. +.PP +.br +.SH FICHIERS +/etc/DIR_COLORS Base de donnes des couleurs systme +.br +~/.dircolors Base de donnes des couleurs utilisateur +.br + +.SH ENVIRONNEMENT +\fBLS_COLORS\fP Informations sur les couleurs cres par dircolors + +.SH AUTEUR +Steve Baker (ice@mama.indstate.edu) +.br +La sortie HTML a t hacke par Francesc Rocher (rocher@econ.udg.es) + +.SH BOGUES +Aucun bogue connu. N'a pas t test en profondeur. Pourrait utiliser +quelques options supplmentaires, la manire de ls. + +.SH VOIR AUSSI +.BR dircolors (1L), +.BR ls (1L), +.BR find (1L) + +.SH TRADUCTEUR +Baptiste Mls + +N'hsitez pas reporter respectivement l'auteur ou au traducteur toute +erreur ou omission dans cette page de manuel. diff --git a/doc/xml.dtd b/doc/xml.dtd new file mode 100644 index 0000000..bee74bf --- /dev/null +++ b/doc/xml.dtd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/file.c b/file.c new file mode 100644 index 0000000..87db770 --- /dev/null +++ b/file.c @@ -0,0 +1,206 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern bool dflag, Fflag, aflag, fflag, pruneflag; +extern bool noindent, force_color, matchdirs; +extern bool reverse; +extern int pattern, ipattern; + +extern int (*topsort)(); +extern FILE *outfile; +extern int Level, *dirs, maxdirs; + +extern bool colorize; +extern char *endcode; + +extern char *file_comment, *file_pathsep; + +enum ftok { T_PATHSEP, T_DIR, T_FILE, T_EOP }; + +char *nextpc(char **p, int *tok) +{ + static char prev = 0; + char *s = *p; + if (!**p) { + *tok = T_EOP; // Shouldn't happen. + return NULL; + } + if (prev) { + prev = 0; + *tok = T_PATHSEP; + return NULL; + } + if (strchr(file_pathsep, **p) != NULL) { + (*p)++; + *tok = T_PATHSEP; + return NULL; + } + while(**p && strchr(file_pathsep,**p) == NULL) (*p)++; + + if (**p) { + *tok = T_DIR; + prev = **p; + *(*p)++ = '\0'; + } else *tok = T_FILE; + return s; +} + +struct _info *newent(char *name) { + struct _info *n = xmalloc(sizeof(struct _info)); + memset(n,0,sizeof(struct _info)); + n->name = scopy(name); + n->child = NULL; + n->tchild = n->next = NULL; + return n; +} + +// Should replace this with a Red-Black tree implementation or the like +struct _info *search(struct _info **dir, char *name) +{ + struct _info *ptr, *prev, *n; + int cmp; + + if (*dir == NULL) return (*dir = newent(name)); + + for(prev = ptr = *dir; ptr != NULL; ptr=ptr->next) { + cmp = strcmp(ptr->name,name); + if (cmp == 0) return ptr; + if (cmp > 0) break; + prev = ptr; + } + n = newent(name); + n->next = ptr; + if (prev == ptr) *dir = n; + else prev->next = n; + return n; +} + +void freefiletree(struct _info *ent) +{ + struct _info *ptr = ent, *t; + + while (ptr != NULL) { + if (ptr->tchild) freefiletree(ptr->tchild); + t = ptr; + ptr = ptr->next; + free(t); + } +} + +/** + * Recursively prune (unset show flag) files/directories of matches/ignored + * patterns: + */ +struct _info **fprune(struct _info *head, bool matched, bool root) +{ + struct _info **dir, *new = NULL, *end = NULL, *ent, *t; + int show, count = 0; + + for(ent = head; ent != NULL;) { + if (ent->tchild) ent->isdir = 1; + + show = 1; + if (dflag && !ent->isdir) show = 0; + if (!aflag && !root && ent->name[0] == '.') show = 0; + if (show && !matched) { + if (!ent->isdir) { + if (pattern && !patinclude(ent->name, 0)) show = 0; + if (ipattern && patignore(ent->name, 0)) show = 0; + } + if (ent->isdir && show && matchdirs && pattern) { + if (patinclude(ent->name, 1)) matched = TRUE; + } + } + if (pruneflag && !matched && ent->isdir && ent->tchild == NULL) show = 0; + if (show && ent->tchild != NULL) ent->child = fprune(ent->tchild, matched, FALSE); + + t = ent; + ent = ent->next; + if (show) { + if (end) end = end->next = t; + else new = end = t; + count++; + } else { + t->next = NULL; + freefiletree(t); + } + } + if (end) end->next = NULL; + + dir = xmalloc(sizeof(struct _info *) * (count+1)); + for(count = 0, ent = new; ent != NULL; ent = ent->next, count++) { + dir[count] = ent; + } + dir[count] = NULL; + + if (topsort) qsort(dir,count,sizeof(struct _info *),topsort); + + return dir; +} + +struct _info **file_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err) +{ + FILE *fp = (strcmp(d,".")? fopen(d,"r") : stdin); + char *path, *spath, *s; + long pathsize; + struct _info *root = NULL, **cwd, *ent; + int l, tok; + + size = 0; + if (fp == NULL) { + fprintf(stderr,"Error opening %s for reading.\n", d); + return NULL; + } + // 64K paths maximum + path = xmalloc(sizeof(char *) * (pathsize = (64 * 1024))); + + while(fgets(path, pathsize, fp) != NULL) { + if (file_comment != NULL && strcmp(path,file_comment) == 0) continue; + l = strlen(path); + while(l && isspace(path[l-1])) path[--l] = '\0'; + if (l == 0) continue; + + spath = path; + cwd = &root; + do { + s = nextpc(&spath, &tok); + if (tok == T_PATHSEP) continue; + switch(tok) { + case T_PATHSEP: continue; + case T_FILE: + case T_DIR: + // Should probably handle '.' and '..' entries here + ent = search(cwd, s); + // Might be empty, but should definitely be considered a directory: + if (tok == T_DIR) { + ent->isdir = 1; + ent->mode = S_IFDIR; + } else { + ent->mode = S_IFREG; + } + cwd = &(ent->tchild); + break; + } + } while (tok != T_FILE && tok != T_EOP); + } + if (fp != stdin) fclose(fp); + + // Prune accumulated directory tree: + return fprune(root, FALSE, TRUE); +} diff --git a/file.o b/file.o new file mode 100644 index 0000000..406f0bb Binary files /dev/null and b/file.o differ diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..76ebd2f --- /dev/null +++ b/filter.c @@ -0,0 +1,186 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +struct ignorefile *filterstack = NULL; + +static char fpattern[PATH_MAX]; + +void gittrim(char *s) +{ + int i, e = strlen(s)-1; + + if (s[e] == '\n') e--; + + for(i = e; i >= 0; i--) { + if (s[i] != ' ') break; + if (i && s[i-1] != '\\') e--; + } + s[e+1] = '\0'; + for(i = e = 0; s[i] != '\0';) { + if (s[i] == '\\') i++; + s[e++] = s[i++]; + } + s[e] = '\0'; +} + +struct pattern *new_pattern(char *pattern) +{ + struct pattern *p = xmalloc(sizeof(struct pattern)); + p->pattern = scopy(pattern + ((pattern[0] == '/')? 1 : 0)); + p->relative = (strchr(pattern,'/') == NULL); + p->next = NULL; + return p; +} + +struct ignorefile *new_ignorefile(char *path) +{ + char buf[PATH_MAX]; + struct ignorefile *ig; + struct pattern *remove = NULL, *remend, *p; + struct pattern *reverse = NULL, *revend; + int rev; + FILE *fp; + + snprintf(buf, PATH_MAX, "%s/.gitignore", path); + fp = fopen(buf, "r"); + if (fp == NULL) return NULL; + + while (fgets(buf, PATH_MAX, fp) != NULL) { + if (buf[0] == '#') continue; + rev = (buf[0] == '!'); + gittrim(buf); + if (strlen(buf) == 0) continue; + p = new_pattern(buf + (rev? 1 : 0)); + if (rev) { + if (reverse == NULL) reverse = revend = p; + else { + revend->next = p; + revend = p; + } + } else { + if (remove == NULL) remove = remend = p; + else { + remend->next = p; + remend = p; + } + } + } + + fclose(fp); + + ig = xmalloc(sizeof(struct ignorefile)); + ig->remove = remove; + ig->reverse = reverse; + ig->path = scopy(path); + ig->next = NULL; + + return ig; +} + +void push_filterstack(struct ignorefile *ig) +{ + if (ig == NULL) return; + ig->next = filterstack; + filterstack = ig; +} + +struct ignorefile *pop_filterstack(void) +{ + struct ignorefile *ig = filterstack; + struct pattern *p, *c; + filterstack = filterstack->next; + + for(p=c=ig->remove; p != NULL; c = p) { + p=p->next; + free(c->pattern); + } + for(p=c=ig->reverse; p != NULL; c = p) { + p=p->next; + free(c->pattern); + } + free(ig->path); + free(ig); + return NULL; +} + +/** + * true if remove filter matches and no reverse filter matches. + */ +int filtercheck(char *path, char *name, int isdir) +{ + int filter = 0; + struct ignorefile *ig; + struct pattern *p; + + for(ig = filterstack; !filter && ig; ig = ig->next) { + int fpos = sprintf(fpattern, "%s/", ig->path); + + for(p = ig->remove; p != NULL; p = p->next) { + if (p->relative) { + if (patmatch(name, p->pattern, isdir) == 1) { +// printf("> name: %s, pattern: %s\n", path, p->pattern); + filter = 1; + break; + } + } else { + sprintf(fpattern + fpos, "%s", p->pattern); +// printf("> path: %s, fpattern: %s\n", path, fpattern); + if (patmatch(path, fpattern, isdir) == 1) { +// printf("Matched path: %s, fpattern: %s\n", path, fpattern); + filter = 1; + break; + } + } + +// if (patmatch(path, p->pattern, isdir) == 1) { +// filter = 1; +// break; +// } +// if (p->pattern[0] == '/') continue; +// sprintf(fpattern + fpos, "%s", p->pattern); +// if (patmatch(path, fpattern, isdir) == 1) { +// filter = 1; +// break; +// } + } + } + if (!filter) return 0; + + for(ig = filterstack; ig; ig = ig->next) { + int fpos = sprintf(fpattern, "%s/", ig->path); + + for(p = ig->reverse; p != NULL; p = p->next) { + if (p->relative) { + if (patmatch(name, p->pattern, isdir) == 1) return 0; + } else { + sprintf(fpattern + fpos, "%s", p->pattern); + if (patmatch(path, fpattern, isdir) == 1) return 0; + } + +// if (patmatch(path, p->pattern, isdir) == 1) return 0; +// +// if (p->pattern[0] == '/') continue; +// sprintf(fpattern + fpos, "%s", p->pattern); +// +// if (patmatch(path, fpattern, isdir) == 1) return 0; + } + } + + return 1; +} diff --git a/filter.o b/filter.o new file mode 100644 index 0000000..a196932 Binary files /dev/null and b/filter.o differ diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..fc38f82 --- /dev/null +++ b/hash.c @@ -0,0 +1,114 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +/* Faster uid/gid -> name lookup with hash(tm)(r)(c) tables! */ +#define HASH(x) ((x)&255) +struct xtable *gtable[256], *utable[256]; + +#define inohash(x) ((x)&255) +struct inotable *itable[256]; + +char *uidtoname(uid_t uid) +{ + struct xtable *o, *p, *t; + struct passwd *ent; + char ubuf[32]; + int uent = HASH(uid); + + for(o = p = utable[uent]; p ; p=p->nxt) { + if (uid == p->xid) return p->name; + else if (uid < p->xid) break; + o = p; + } + /* Not found, do a real lookup and add to table */ + t = xmalloc(sizeof(struct xtable)); + if ((ent = getpwuid(uid)) != NULL) t->name = scopy(ent->pw_name); + else { + snprintf(ubuf,30,"%d",uid); + ubuf[31] = 0; + t->name = scopy(ubuf); + } + t->xid = uid; + t->nxt = p; + if (p == utable[uent]) utable[uent] = t; + else o->nxt = t; + return t->name; +} + +char *gidtoname(gid_t gid) +{ + struct xtable *o, *p, *t; + struct group *ent; + char gbuf[32]; + int gent = HASH(gid); + + for(o = p = gtable[gent]; p ; p=p->nxt) { + if (gid == p->xid) return p->name; + else if (gid < p->xid) break; + o = p; + } + /* Not found, do a real lookup and add to table */ + t = xmalloc(sizeof(struct xtable)); + if ((ent = getgrgid(gid)) != NULL) t->name = scopy(ent->gr_name); + else { + snprintf(gbuf,30,"%d",gid); + gbuf[31] = 0; + t->name = scopy(gbuf); + } + t->xid = gid; + t->nxt = p; + if (p == gtable[gent]) gtable[gent] = t; + else o->nxt = t; + return t->name; +} + +/* Record inode numbers of followed sym-links to avoid refollowing them */ +void saveino(ino_t inode, dev_t device) +{ + struct inotable *it, *ip, *pp; + int hp = inohash(inode); + + for(pp = ip = itable[hp];ip;ip = ip->nxt) { + if (ip->inode > inode) break; + if (ip->inode == inode && ip->device >= device) break; + pp = ip; + } + + if (ip && ip->inode == inode && ip->device == device) return; + + it = xmalloc(sizeof(struct inotable)); + it->inode = inode; + it->device = device; + it->nxt = ip; + if (ip == itable[hp]) itable[hp] = it; + else pp->nxt = it; +} + +int findino(ino_t inode, dev_t device) +{ + struct inotable *it; + + for(it=itable[inohash(inode)]; it; it=it->nxt) { + if (it->inode > inode) break; + if (it->inode == inode && it->device >= device) break; + } + + if (it && it->inode == inode && it->device == device) return TRUE; + return FALSE; +} diff --git a/hash.o b/hash.o new file mode 100644 index 0000000..bb9a54f Binary files /dev/null and b/hash.o differ diff --git a/html.c b/html.c new file mode 100644 index 0000000..35794e5 --- /dev/null +++ b/html.c @@ -0,0 +1,225 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern char *version, *hversion; +extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag; +extern bool Dflag, inodeflag, devflag, Rflag, duflag, hflag, siflag; +extern bool noindent, force_color, xdev, nolinks, metafirst, noreport; +extern char *host, *sp, *title; +extern const char *charset; + +extern FILE *outfile; +extern int Level, *dirs, maxdirs; + +extern bool colorize, linktargetcolor; +extern char *endcode; +extern const struct linedraw *linedraw; + +int htmldirlen = 0; + +char *class(struct _info *info) +{ + return + info->isdir ? "DIR" : + info->isexe ? "EXEC" : + info->isfifo ? "FIFO" : + info->issok ? "SOCK" : "NORM"; +} + +void html_encode(FILE *fd, char *s) +{ + for(;*s;s++) { + switch(*s) { + case '<': + fputs("<",fd); + break; + case '>': + fputs(">",fd); + break; + case '&': + fputs("&",fd); + break; + case '"': + fputs(""",fd); + break; + default: + fputc(*s,fd); + // fputc(isprint(*s)?*s:'?',fd); + break; + } + } +} + +void url_encode(FILE *fd, char *s) +{ + for(;*s;s++) { + switch(*s) { + case ' ': + case '"': + case '#': + case '%': + case '<': + case '>': + case '[': + case ']': + case '^': + case '\\': + case '?': + case '+': + fprintf(fd,"%%%02X",*s); + break; + case '&': + fprintf(fd,"&"); + break; + default: + fprintf(fd,isprint((u_int)*s)?"%c":"%%%02X",(u_char)*s); + break; + } + } +} + +void html_intro(void) +{ + fprintf(outfile, + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " %s\n" + " \n" + "\n" + "\n" + "\t

\n", title); +} + +void html_outtro(void) +{ + fprintf(outfile,"\n"); + fprintf(outfile,"\n"); +} + +void html_print(char *s) +{ + for(int i=0; s[i]; i++) { + if (s[i] == ' ') fprintf(outfile,"%s",sp); + else fprintf(outfile,"%c", s[i]); + } + fprintf(outfile,"%s%s", sp, sp); +} + +int html_printinfo(char *dirname, struct _info *file, int level) +{ + char info[512]; + + fillinfo(info,file); + if (metafirst) { + if (info[0] == '[') { + html_print(info); + fprintf(outfile,"%s%s", sp, sp); + } + if (!noindent) indent(level); + } else { + if (!noindent) indent(level); + if (info[0] == '[') { + html_print(info); + fprintf(outfile,"%s%s", sp, sp); + } + } + + return 0; +} + +// descend == add 00Tree.html to the link +int html_printfile(char *dirname, char *filename, struct _info *file, int descend) +{ + // Switch to using 'a' elements only. Omit href attribute if not a link + fprintf(outfile,"comment) { + fprintf(outfile," title=\""); + for(int i=0; file->comment[i]; i++) { + html_encode(outfile, file->comment[i]); + if (file->comment[i+1]) fprintf(outfile, "\n"); + } + fprintf(outfile, "\""); + } + + if (!nolinks) { + fprintf(outfile," href=\"%s",host); + if (dirname != NULL) { + int len = strlen(dirname); + int off = (len >= htmldirlen? htmldirlen : 0); + url_encode(outfile, dirname + off); + putc('/',outfile); + url_encode(outfile, filename); + fprintf(outfile,"%s%s\"",(descend > 1? "/00Tree.html" : ""), (file->isdir?"/":"")); + } else { + fprintf(outfile,"%s\"",(descend > 1? "/00Tree.html" : "")); + } + } + } + fprintf(outfile, ">"); + + if (dirname) html_encode(outfile,filename); + else html_encode(outfile, host); + + fprintf(outfile,""); + return 0; +} + +int html_error(char *error) +{ + fprintf(outfile, " [%s]", error); + return 0; +} + +void html_newline(struct _info *file, int level, int postdir, int needcomma) +{ + fprintf(outfile, "
\n"); +} + +void html_close(struct _info *file, int level, int needcomma) +{ + fprintf(outfile, "
\n", file->tag); +} + +void html_report(struct totals tot) +{ + char buf[256]; + + fprintf(outfile,"

\n\n"); + + if (duflag) { + psize(buf, tot.size); + fprintf(outfile,"%s%s used in ", buf, hflag || siflag? "" : " bytes"); + } + if (dflag) + fprintf(outfile,"%ld director%s\n",tot.dirs,(tot.dirs==1? "y":"ies")); + else + fprintf(outfile,"%ld director%s, %ld file%s\n",tot.dirs,(tot.dirs==1? "y":"ies"),tot.files,(tot.files==1? "":"s")); + + fprintf(outfile, "\n

\n"); +} diff --git a/html.o b/html.o new file mode 100644 index 0000000..4be55dd Binary files /dev/null and b/html.o differ diff --git a/info.c b/info.c new file mode 100644 index 0000000..4f88d89 --- /dev/null +++ b/info.c @@ -0,0 +1,175 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +/** + * TODO: Make a "filenote" command for info comments. + * maybe TODO: Support language extensions (i.e. .info.en, .info.gr, etc) + * # comments + * pattern + * pattern + * info messages + * more info + */ +extern FILE *outfile; +extern const struct linedraw *linedraw; + +struct infofile *infostack = NULL; + +struct comment *new_comment(struct pattern *phead, char **line, int lines) +{ + struct comment *com = xmalloc(sizeof(struct comment)); + com->pattern = phead; + com->desc = xmalloc(sizeof(char *) * (lines+1)); + int i; + for(i=0; i < lines; i++) com->desc[i] = line[i]; + com->desc[i] = NULL; + com->next = NULL; + return com; +} + +struct infofile *new_infofile(char *path) +{ + char buf[PATH_MAX]; + struct infofile *inf; + struct comment *chead = NULL, *cend = NULL, *com; + struct pattern *phead = NULL, *pend = NULL, *p; + char *line[PATH_MAX]; + FILE *fp; + int lines = 0; + + if (strcmp(path,INFO_PATH) == 0) fp = fopen(path, "r"); + else { + snprintf(buf, PATH_MAX, "%s/.info", path); + fp = fopen(buf, "r"); + } + if (fp == NULL) return NULL; + + while (fgets(buf, PATH_MAX, fp) != NULL) { + if (buf[0] == '#') continue; + gittrim(buf); + if (strlen(buf) < 1) continue; + + if (buf[0] == '\t') { + line[lines++] = scopy(buf+1); + } else { + if (lines) { + // Save previous pattern/message: + if (phead) { + com = new_comment(phead, line, lines); + if (!chead) chead = cend = com; + else cend = cend->next = com; + } else { + // Accumulated info message lines w/ no associated pattern? + for(int i=0; i < lines; i++) free(line[i]); + } + // Reset for next pattern/message: + phead = pend = NULL; + lines = 0; + } + p = new_pattern(buf); + if (phead == NULL) phead = pend = p; + else pend = pend->next = p; + } + } + if (phead) { + com = new_comment(phead, line, lines); + if (!chead) chead = cend = com; + else cend = cend->next = com; + } else { + for(int i=0; i < lines; i++) free(line[i]); + } + + fclose(fp); + + inf = xmalloc(sizeof(struct infofile)); + inf->comments = chead; + inf->path = scopy(path); + inf->next = NULL; + + return inf; +} + +void push_infostack(struct infofile *inf) +{ + if (inf == NULL) return; + inf->next = infostack; + infostack = inf; +} + +struct infofile *pop_infostack(void) +{ + struct infofile *inf = infostack; + struct comment *cn, *cc; + struct pattern *p, *c; + infostack = infostack->next; + + if (inf == NULL) return NULL; + + for(cn = cc = inf->comments; cn != NULL; cc = cn) { + cn = cn->next; + for(p=c=cc->pattern; p != NULL; c = p) { + p=p->next; + free(c->pattern); + } + for(int i=0; cc->desc[i] != NULL; i++) free(cc->desc[i]); + free(cc->desc); + free(cc); + } + free(inf->path); + free(inf); + return NULL; +} + +/** + * Returns an info pointer if a path matches a pattern. + * top == 1 if called in a directory with a .info file. + */ +struct comment *infocheck(char *path, char *name, int top, int isdir) +{ + struct infofile *inf = infostack; + struct comment *com; + struct pattern *p; + + if (inf == NULL) return NULL; + + for(inf = infostack; inf != NULL; inf = inf->next) { + for(com = inf->comments; com != NULL; com = com->next) { + for(p = com->pattern; p != NULL; p = p->next) { + if (patmatch(path, p->pattern, isdir) == 1) return com; + if (top && patmatch(name, p->pattern, isdir) == 1) return com; + } + } + top = 0; + } + return NULL; +} + +void printcomment(int line, int lines, char *s) +{ + if (lines == 1) fprintf(outfile, "%s ", linedraw->csingle); + else { + if (line == 0) fprintf(outfile, "%s ", linedraw->ctop); + else if (line < 2) { + fprintf(outfile, "%s ", (lines==2)? linedraw->cbot : linedraw->cmid); + } else { + fprintf(outfile, "%s ", (line == lines-1)? linedraw->cbot : linedraw->cext); + } + } + fprintf(outfile, "%s\n", s); +} diff --git a/info.o b/info.o new file mode 100644 index 0000000..1effa7b Binary files /dev/null and b/info.o differ diff --git a/json.c b/json.c new file mode 100644 index 0000000..c88bf37 --- /dev/null +++ b/json.c @@ -0,0 +1,188 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag; +extern bool Dflag, inodeflag, devflag, Rflag, cflag, hflag, siflag, duflag; +extern bool noindent, force_color, xdev, nolinks, noreport; + +extern const int ifmt[]; +extern const char fmt[], *ftype[]; + +extern FILE *outfile; +extern int Level, *dirs, maxdirs, errors; + +extern char *endcode; + +/* JSON code courtesy of Florian Sesser +[ + {"type": "directory", "name": "name", "mode": "0777", "user": "user", "group": "group", "inode": ###, "dev": ####, "time": "00:00 00-00-0000", "info": "", "contents": [ + {"type": "link", "name": "name", "target": "name", "info": "...", "contents": [... if link is followed, otherwise this is empty.]} + {"type": "file", "name": "name", "mode": "0777", "size": ###, "group": "group", "inode": ###, "dev": ###, "time": "00:00 00-00-0000", "info": "..."} + {"type": "socket", "name": "", "info": "...", "error": "some error" ...} + {"type": "block", "name": "" ...}, + {"type": "char", "name": "" ...}, + {"type": "fifo", "name": "" ...}, + {"type": "door", "name": "" ...}, + {"type": "port", "name": "" ...} + ]}, + {"type": "report", "size": ###, "files": ###, "directories": ###} +] +*/ + +/** + * JSON encoded strings are not HTML/XML strings: + * https://tools.ietf.org/html/rfc8259#section-7 + * FIXME: Still not UTF-8 + */ +void json_encode(FILE *fd, char *s) +{ + char *ctrl = "0-------btn-fr------------------"; + + for(;*s;s++) { + if ((unsigned char)*s < 32) { + if (ctrl[(unsigned char)*s] != '-') fprintf(fd, "\\%c", ctrl[(unsigned char)*s]); + else fprintf(fd, "\\u%04x", (unsigned char)*s); + } else if (*s == '"' || *s == '\\') fprintf(fd, "\\%c", *s); + else fprintf(fd, "%c", *s); + } +} + +void json_indent(int maxlevel) +{ + int i; + + fprintf(outfile, " "); + for(i=0; iinode); + #else + if (inodeflag) fprintf(outfile,",\"inode\":%ld",(long int)ent->inode); + #endif + if (devflag) fprintf(outfile, ",\"dev\":%d", (int)ent->dev); + #ifdef __EMX__ + if (pflag) fprintf(outfile, ",\"mode\":\"%04o\",\"prot\":\"%s\"",ent->attr, prot(ent->attr)); + #else + if (pflag) fprintf(outfile, ",\"mode\":\"%04o\",\"prot\":\"%s\"", ent->mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX), prot(ent->mode)); + #endif + if (uflag) fprintf(outfile, ",\"user\":\"%s\"", uidtoname(ent->uid)); + if (gflag) fprintf(outfile, ",\"group\":\"%s\"", gidtoname(ent->gid)); + if (sflag) { + if (hflag || siflag) { + char nbuf[64]; + int i; + psize(nbuf,ent->size); + for(i=0; isspace(nbuf[i]); i++); // trim() hack + fprintf(outfile, ",\"size\":\"%s\"", nbuf+i); + } else + fprintf(outfile, ",\"size\":%lld", (long long int)ent->size); + } + if (Dflag) fprintf(outfile, ",\"time\":\"%s\"", do_date(cflag? ent->ctime : ent->mtime)); +} + + +void json_intro(void) +{ + extern char *_nl; + fprintf(outfile, "[%s", noindent? "" : _nl); +} + +void json_outtro(void) +{ + extern char *_nl; + fprintf(outfile, "%s]\n", noindent? "" : _nl); +} + +int json_printinfo(char *dirname, struct _info *file, int level) +{ + mode_t mt; + int t; + + if (!noindent) json_indent(level); + + if (file->lnk) mt = file->mode & S_IFMT; + else mt = file->mode & S_IFMT; + + for(t=0;ifmt[t];t++) + if (ifmt[t] == mt) break; + fprintf(outfile,"{\"type\":\"%s\"", ftype[t]); + + return 0; +} + +int json_printfile(char *dirname, char *filename, struct _info *file, int descend) +{ + fprintf(outfile, ",\"name\":\""); + json_encode(outfile, filename); + fputc('"',outfile); + + if (file && file->comment) { + fprintf(outfile, ",\"info\":\""); + for(int i=0; file->comment[i]; i++) { + json_encode(outfile, file->comment[i]); + if (file->comment[i+1]) fprintf(outfile, "\\n"); + } + fprintf(outfile, "\""); + } + + if (file && file->lnk) { + fprintf(outfile, ",\"target\":\""); + json_encode(outfile, file->lnk); + fputc('"',outfile); + } + if (file) json_fillinfo(file); + + if (file && file->err) fprintf(outfile, ",\"error\": \"%s\"", file->err); + if (!descend) fputc('}',outfile); + else fprintf(outfile, ",\"contents\":["); + + return descend; +} + +int json_error(char *error) +{ + fprintf(outfile,"{\"error\": \"%s\"}%s",error, noindent?"":"\n"); + return 0; +} + +void json_newline(struct _info *file, int level, int postdir, int needcomma) +{ + extern char *_nl; + + fprintf(outfile, "%s%s", needcomma? "," : "", _nl); +} + +void json_close(struct _info *file, int level, int needcomma) +{ + if (!noindent) json_indent(level-1); + fprintf(outfile,"]}%s%s", needcomma? ",":"", noindent? "":"\n"); +} + +void json_report(struct totals tot) +{ + fprintf(outfile, ",%s{\"type\":\"report\"",noindent?"":"\n "); + if (duflag) fprintf(outfile,",\"size\":%lld", (long long int)tot.size); + fprintf(outfile,",\"directories\":%ld", tot.dirs); + if (!dflag) fprintf(outfile,",\"files\":%ld", tot.files); + fprintf(outfile, "}"); +} diff --git a/json.o b/json.o new file mode 100644 index 0000000..c5340c2 Binary files /dev/null and b/json.o differ diff --git a/list.c b/list.c new file mode 100644 index 0000000..57e5c34 --- /dev/null +++ b/list.c @@ -0,0 +1,254 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag; +extern bool Dflag, Hflag, inodeflag, devflag, Rflag, duflag, pruneflag, metafirst; +extern bool Jflag, hflag, siflag, noreport, noindent, force_color, xdev, nolinks; + +extern struct _info **(*getfulltree)(char *d, u_long lev, dev_t dev, off_t *size, char **err); +extern int (*topsort)(); +extern FILE *outfile; +extern int flimit, Level, *dirs, maxdirs, errors; +extern int htmldirlen; + +extern bool colorize, linktargetcolor; +extern char *endcode; +extern const struct linedraw *linedraw; + +static char errbuf[256]; + +/** + * Maybe TODO: Refactor the listing calls / when they are called. A more thorough + * analysis of the different outputs is required. This all is not as clean as I + * had hoped it to be. + */ + +extern struct listingcalls lc; + +void null_intro(void) +{ + return; +} + +void null_outtro(void) +{ + return; +} + +void null_close(struct _info *file, int level, int needcomma) +{ +} + +void emit_tree(char **dirname, bool needfulltree) +{ + struct totals tot = { 0 }; + struct ignorefile *ig = NULL; + struct infofile *inf = NULL; + struct _info **dir = NULL, *info = NULL; + char *err; + int i, j, n, needsclosed; + struct stat st; + + lc.intro(); + + for(i=0; dirname[i]; i++) { + if (fflag) { + j=strlen(dirname[i]); + do { + if (j > 1 && dirname[i][j-1] == '/') dirname[i][--j] = 0; + } while (j > 1 && dirname[i][j-1] == '/'); + } + if (Hflag) htmldirlen = strlen(dirname[i]); + + if ((n = lstat(dirname[i],&st)) >= 0) { + saveino(st.st_ino, st.st_dev); + info = stat2info(&st); + info->name = dirname[i]; + + if (needfulltree) { + dir = getfulltree(dirname[i], 0, st.st_dev, &(info->size), &err); + n = err? -1 : 0; + } else { + push_files(dirname[i], &ig, &inf); + dir = read_dir(dirname[i], &n, inf != NULL); + } + + lc.printinfo(dirname[i], info, 0); + } else info = NULL; + + needsclosed = lc.printfile(NULL, dirname[i], info, (dir != NULL) || (!dir && n)); + + if (!dir && n) { + lc.error("error opening dir"); + lc.newline(info, 0, 0, dirname[i+1] != NULL); + errors++; + } else if (flimit > 0 && n > flimit) { + sprintf(errbuf,"%d entries exceeds filelimit, not opening dir", n); + lc.error(errbuf); + lc.newline(info, 0, 0, dirname[i+1] != NULL); + errors++; + } else { + lc.newline(info, 0, 0, 0); + if (dir) { + tot = listdir(dirname[i], dir, 1, st.st_dev, needfulltree); + } else tot = (struct totals){0, 0}; + } + if (dir) { + free_dir(dir); + dir = NULL; + } + if (needsclosed) lc.close(info, 0, dirname[i+1] != NULL); + + if (duflag) tot.size = info->size; + else tot.size += st.st_size; + + if (ig != NULL) ig = pop_filterstack(); + if (inf != NULL) inf = pop_infostack(); + } + + if (!noreport) lc.report(tot); + + lc.outtro(); +} + +struct totals listdir(char *dirname, struct _info **dir, int lev, dev_t dev, bool hasfulltree) +{ + struct totals tot = {0}, subtotal; + struct ignorefile *ig = NULL; + struct infofile *inf = NULL; + struct _info **subdir = NULL; + int descend, htmldescend = 0, found, n, dirlen = strlen(dirname), pathlen = dirlen + 257; + int needsclosed; + char *path, *newpath, *filename, *err = NULL; + + int es = (dirname[strlen(dirname) - 1] == '/'); + + for(n=0; dir[n]; n++); + if (topsort) qsort(dir, n, sizeof(struct _info *), topsort); + + dirs[lev] = *(dir+1)? 1 : 2; + + path = xmalloc(sizeof(char) * pathlen); + + for (;*dir != NULL; dir++) { + lc.printinfo(dirname, *dir, lev); + + if (es) sprintf(path,"%s%s",dirname,(*dir)->name); + else sprintf(path,"%s/%s",dirname,(*dir)->name); + if (fflag) filename = path; + else filename = (*dir)->name; + + descend = 0; + err = NULL; + + if ((*dir)->isdir) { + tot.dirs++; + + found = findino((*dir)->inode,(*dir)->dev); + if (!found) saveino((*dir)->inode, (*dir)->dev); + + if (!(xdev && dev != (*dir)->dev) && (!(*dir)->lnk || ((*dir)->lnk && lflag))) { + descend = 1; + newpath = path; + + if ((*dir)->lnk) { + if (*(*dir)->lnk == '/') newpath = (*dir)->lnk; + else { + if (fflag && !strcmp(dirname,"/")) sprintf(path,"%s%s",dirname,(*dir)->lnk); + else sprintf(path,"%s/%s",dirname,(*dir)->lnk); + } + if (found) { + err = "recursive, not followed"; + descend = 0; + } + } + + if ((Level >= 0) && (lev > Level)) { + if (Rflag) { + FILE *outsave = outfile; + char *paths[2] = {newpath, NULL}, *output = xmalloc(strlen(newpath) + 13); + int *dirsave = xmalloc(sizeof(int) * (lev + 2)); + + memcpy(dirsave, dirs, sizeof(int) * (lev+1)); + sprintf(output, "%s/00Tree.html", newpath); + setoutput(output); + emit_tree(paths, hasfulltree); + + free(output); + fclose(outfile); + outfile = outsave; + + memcpy(dirs, dirsave, sizeof(int) * (lev+1)); + free(dirsave); + htmldescend = 10; + } else htmldescend = 0; + descend = 0; + } + + if (descend) { + if (hasfulltree) { + subdir = (*dir)->child; + err = (*dir)->err; + } else { + push_files(newpath, &ig, &inf); + subdir = read_dir(newpath, &n, inf != NULL); + if (!subdir && n) { + err = "error opening dir"; + errors++; + } if (flimit > 0 && n > flimit) { + sprintf(err = errbuf,"%d entries exceeds filelimit, not opening dir", n); + errors++; + free_dir(subdir); + subdir = NULL; + } + } + if (subdir == NULL) descend = 0; + } + } + } else tot.files++; + + needsclosed = lc.printfile(dirname, filename, *dir, descend + htmldescend + (Jflag && errors)); + if (err) lc.error(err); + + if (descend) { + lc.newline(*dir, lev, 0, 0); + + subtotal = listdir(newpath, subdir, lev+1, dev, hasfulltree); + tot.dirs += subtotal.dirs; + tot.files += subtotal.files; + tot.size += subtotal.size; + } else if (!needsclosed) lc.newline(*dir, lev, 0, *(dir+1)!=NULL); + + if (subdir) { + free_dir(subdir); + subdir = NULL; + } + if (needsclosed) lc.close(*dir, descend? lev : -1, *(dir+1)!=NULL); + + if (*(dir+1) && !*(dir+2)) dirs[lev] = 2; + tot.size += (*dir)->size; + + if (ig != NULL) ig = pop_filterstack(); + if (inf != NULL) inf = pop_infostack(); + } + + dirs[lev] = 0; + free(path); + return tot; +} diff --git a/list.o b/list.o new file mode 100644 index 0000000..22272fe Binary files /dev/null and b/list.o differ diff --git a/strverscmp.c b/strverscmp.c new file mode 100644 index 0000000..137701f --- /dev/null +++ b/strverscmp.c @@ -0,0 +1,160 @@ +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2002, 2005 Free Software Foundation, Inc. + This file is part of the libiberty library. + Contributed by Jean-Franois Bignolles , 1997. + + Libiberty is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Libiberty is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. */ + +/* #include "libiberty.h" */ +/* #include "safe-ctype.h" */ +#include +#ifndef __linux__ + +/* +@deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2}) +The @code{strverscmp} function compares the string @var{s1} against +@var{s2}, considering them as holding indices/version numbers. Return +value follows the same conventions as found in the @code{strverscmp} +function. In fact, if @var{s1} and @var{s2} contain no digits, +@code{strverscmp} behaves like @code{strcmp}. + +Basically, we compare strings normally (character by character), until +we find a digit in each string - then we enter a special comparison +mode, where each sequence of digits is taken as a whole. If we reach the +end of these two parts without noticing a difference, we return to the +standard comparison mode. There are two types of numeric parts: +"integral" and "fractional" (those begin with a '0'). The types +of the numeric parts affect the way we sort them: + +@itemize @bullet +@item +integral/integral: we compare values as you would expect. + +@item +fractional/integral: the fractional part is less than the integral one. +Again, no surprise. + +@item +fractional/fractional: the things become a bit more complex. +If the common prefix contains only leading zeroes, the longest part is less +than the other one; else the comparison behaves normally. +@end itemize + +@smallexample +strverscmp ("no digit", "no digit") + @result{} 0 // @r{same behavior as strcmp.} +strverscmp ("item#99", "item#100") + @result{} <0 // @r{same prefix, but 99 < 100.} +strverscmp ("alpha1", "alpha001") + @result{} >0 // @r{fractional part inferior to integral one.} +strverscmp ("part1_f012", "part1_f01") + @result{} >0 // @r{two fractional parts.} +strverscmp ("foo.009", "foo.0") + @result{} <0 // @r{idem, but with leading zeroes only.} +@end smallexample + +This function is especially useful when dealing with filename sorting, +because filenames frequently hold indices/version numbers. +@end deftypefun + +*/ + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractional parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the Glibc texinfo doc). */ + +int +strverscmp (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = + { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = + { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP, + +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (isdigit (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (isdigit (c1) != 0); + } + + state = result_type[state << 2 | (((c2 == '0') + (isdigit (c2) != 0)))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (isdigit (*p1++)) + if (!isdigit (*p2++)) + return 1; + + return isdigit (*p2) ? -1 : diff; + + default: + return state; + } +} +#endif \ No newline at end of file diff --git a/strverscmp.o b/strverscmp.o new file mode 100644 index 0000000..2dd6142 Binary files /dev/null and b/strverscmp.o differ diff --git a/tree b/tree new file mode 100755 index 0000000..9491ba1 Binary files /dev/null and b/tree differ diff --git a/tree.c b/tree.c new file mode 100644 index 0000000..a32b5a5 --- /dev/null +++ b/tree.c @@ -0,0 +1,1429 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tree.h" + +char *version ="$Version: $ tree v2.0.4 (c) 1996 - 2022 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $"; +char *hversion="\t\t tree v2.0.4 %s 1996 - 2022 by Steve Baker and Thomas Moore
\n" + "\t\t HTML output hacked and copyleft %s 1998 by Francesc Rocher
\n" + "\t\t JSON output hacked and copyleft %s 2014 by Florian Sesser
\n" + "\t\t Charsets / OS/2 support %s 2001 by Kyosuke Tokoro\n"; + +/* Globals */ +bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag; +bool qflag, Nflag, Qflag, Dflag, inodeflag, devflag, hflag, Rflag; +bool Hflag, siflag, cflag, Xflag, Jflag, duflag, pruneflag; +bool noindent, force_color, nocolor, xdev, noreport, nolinks; +bool ignorecase, matchdirs, fromfile, metafirst, gitignore, showinfo; +bool reverse; +int flimit; + +struct listingcalls lc; + +int pattern = 0, maxpattern = 0, ipattern = 0, maxipattern = 0; +char **patterns = NULL, **ipatterns = NULL; + +char *host = NULL, *title = "Directory Tree", *sp = " ", *_nl = "\n"; +char *file_comment = "#", *file_pathsep = "/"; +char *timefmt = NULL; +const char *charset = NULL; + +struct _info **(*getfulltree)(char *d, u_long lev, dev_t dev, off_t *size, char **err) = unix_getfulltree; +//off_t (*listdir)(char *, int *, int *, u_long, dev_t) = unix_listdir; +int (*basesort)() = alnumsort; +int (*topsort)() = NULL; + +char *sLevel, *curdir; +FILE *outfile = NULL; +int Level, *dirs, maxdirs; +int errors; + +int mb_cur_max; + +#ifdef __EMX__ +const u_short ifmt[]={ FILE_ARCHIVED, FILE_DIRECTORY, FILE_SYSTEM, FILE_HIDDEN, FILE_READONLY, 0}; +#else + #ifdef S_IFPORT + const u_int ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, S_IFDOOR, S_IFPORT, 0}; + const char fmt[] = "-dlcbspDP?"; + const char *ftype[] = {"file", "directory", "link", "char", "block", "socket", "fifo", "door", "port", "unknown", NULL}; + #else + const u_int ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, 0}; + const char fmt[] = "-dlcbsp?"; + const char *ftype[] = {"file", "directory", "link", "char", "block", "socket", "fifo", "unknown", NULL}; + #endif +#endif + +struct sorts { + char *name; + int (*cmpfunc)(); +} sorts[] = { + {"name", alnumsort}, + {"version", versort}, + {"size", fsizesort}, + {"mtime", mtimesort}, + {"ctime", ctimesort}, + {NULL, NULL} +}; + +/* Externs */ +/* hash.c */ +extern struct xtable *gtable[256], *utable[256]; +extern struct inotable *itable[256]; + +/* color.c */ +extern bool colorize, ansilines, linktargetcolor; +extern char *leftcode, *rightcode, *endcode; +extern const struct linedraw *linedraw; + + +int main(int argc, char **argv) +{ + char **dirname = NULL; + int i,j=0,k,n,optf,p = 0,q = 0; + char *stmp, *outfilename = NULL;; + bool needfulltree; + + aflag = dflag = fflag = lflag = pflag = sflag = Fflag = uflag = gflag = FALSE; + Dflag = qflag = Nflag = Qflag = Rflag = hflag = Hflag = siflag = cflag = FALSE; + noindent = force_color = nocolor = xdev = noreport = nolinks = reverse = FALSE; + ignorecase = matchdirs = inodeflag = devflag = Xflag = Jflag = FALSE; + duflag = pruneflag = metafirst = gitignore = FALSE; + + flimit = 0; + dirs = xmalloc(sizeof(int) * (maxdirs=PATH_MAX)); + memset(dirs, 0, sizeof(int) * maxdirs); + dirs[0] = 0; + Level = -1; + + setlocale(LC_CTYPE, ""); + setlocale(LC_COLLATE, ""); + + charset = getcharset(); + if (charset == NULL && + (strcmp(nl_langinfo(CODESET), "UTF-8") == 0 || + strcmp(nl_langinfo(CODESET), "utf8") == 0)) { + charset = "UTF-8"; + } + + lc = (struct listingcalls){ + null_intro, null_outtro, unix_printinfo, unix_printfile, unix_error, unix_newline, + null_close, unix_report + }; + +/* Still a hack, but assume that if the macro is defined, we can use it: */ +#ifdef MB_CUR_MAX + mb_cur_max = (int)MB_CUR_MAX; +#else + mb_cur_max = 1; +#endif + +#ifdef __linux__ + // Output JSON automatically to "stddata" if present: + char *stddata_fd = getenv(ENV_STDDATA_FD); + if (stddata_fd != NULL) { + int std_fd = atoi(stddata_fd); + if (std_fd <= 0) std_fd = STDDATA_FILENO; + if (fcntl(std_fd, F_GETFD) >= 0) { + Jflag = noindent = TRUE; + _nl = ""; + lc = (struct listingcalls){ + json_intro, json_outtro, json_printinfo, json_printfile, json_error, json_newline, + json_close, json_report + }; + outfile = fdopen(std_fd, "w"); + } + } +#endif + + memset(utable,0,sizeof(utable)); + memset(gtable,0,sizeof(gtable)); + memset(itable,0,sizeof(itable)); + + optf = TRUE; + for(n=i=1;i= maxpattern-1) patterns = xrealloc(patterns, sizeof(char *) * (maxpattern += 10)); + patterns[pattern++] = argv[n++]; + patterns[pattern] = NULL; + break; + case 'I': + if (argv[n] == NULL) { + fprintf(stderr,"tree: missing argument to -I option.\n"); + exit(1); + } + if (ipattern >= maxipattern-1) ipatterns = xrealloc(ipatterns, sizeof(char *) * (maxipattern += 10)); + ipatterns[ipattern++] = argv[n++]; + ipatterns[ipattern] = NULL; + break; + case 'A': + ansilines = TRUE; + break; + case 'S': + charset = "IBM437"; + break; + case 'D': + Dflag = TRUE; + break; + case 't': + basesort = mtimesort; + break; + case 'c': + basesort = ctimesort; + cflag = TRUE; + break; + case 'r': + reverse = TRUE; + break; + case 'v': + basesort = versort; + break; + case 'U': + basesort = NULL; + break; + case 'X': + Xflag = TRUE; + Hflag = Jflag = FALSE; + lc = (struct listingcalls){ + xml_intro, xml_outtro, xml_printinfo, xml_printfile, xml_error, xml_newline, + xml_close, xml_report + }; + break; + case 'J': + Jflag = TRUE; + Xflag = Hflag = FALSE; + lc = (struct listingcalls){ + json_intro, json_outtro, json_printinfo, json_printfile, json_error, json_newline, + json_close, json_report + }; + break; + case 'H': + Hflag = TRUE; + Xflag = Jflag = FALSE; + lc = (struct listingcalls){ + html_intro, html_outtro, html_printinfo, html_printfile, html_error, html_newline, + html_close, html_report + }; + if (argv[n] == NULL) { + fprintf(stderr,"tree: missing argument to -H option.\n"); + exit(1); + } + host = argv[n++]; + sp = " "; + break; + case 'T': + if (argv[n] == NULL) { + fprintf(stderr,"tree: missing argument to -T option.\n"); + exit(1); + } + title = argv[n++]; + break; + case 'R': + Rflag = TRUE; + break; + case 'L': + if ((sLevel = argv[n++]) == NULL) { + fprintf(stderr,"tree: Missing argument to -L option.\n"); + exit(1); + } + Level = strtoul(sLevel,NULL,0)-1; + if (Level < 0) { + fprintf(stderr,"tree: Invalid level, must be greater than 0.\n"); + exit(1); + } + break; + case 'o': + if (argv[n] == NULL) { + fprintf(stderr,"tree: missing argument to -o option.\n"); + exit(1); + } + outfilename = argv[n++]; + break; + case '-': + if (j == 1) { + if (!strcmp("--", argv[i])) { + optf = FALSE; + break; + } + if (!strcmp("--help",argv[i])) { + usage(2); + exit(0); + } + if (!strcmp("--version",argv[i])) { + char *v = version+12; + printf("%.*s\n",(int)strlen(v)-1,v); + exit(0); + } + if (!strcmp("--inodes",argv[i])) { + j = strlen(argv[i])-1; + inodeflag=TRUE; + break; + } + if (!strcmp("--device",argv[i])) { + j = strlen(argv[i])-1; + devflag=TRUE; + break; + } + if (!strcmp("--noreport",argv[i])) { + j = strlen(argv[i])-1; + noreport = TRUE; + break; + } + if (!strcmp("--nolinks",argv[i])) { + j = strlen(argv[i])-1; + nolinks = TRUE; + break; + } + if (!strcmp("--dirsfirst",argv[i])) { + j = strlen(argv[i])-1; + topsort = dirsfirst; + break; + } + if (!strcmp("--filesfirst",argv[i])) { + j = strlen(argv[i])-1; + topsort = filesfirst; + break; + } + if (!strncmp("--filelimit",argv[i],11)) { + j = 11; + if (*(argv[i]+11) == '=') { + if (*(argv[i]+12)) { + flimit=atoi(argv[i]+12); + j = strlen(argv[i])-1; + break; + } else { + fprintf(stderr,"tree: missing argument to --filelimit=\n"); + exit(1); + } + } + if (argv[n] != NULL) { + flimit = atoi(argv[n++]); + j = strlen(argv[i])-1; + } else { + fprintf(stderr,"tree: missing argument to --filelimit\n"); + exit(1); + } + break; + } + if (!strncmp("--charset",argv[i],9)){ + j = 9; + if (*(argv[i]+j) == '=') { + if (*(charset = (argv[i]+10))) { + j = strlen(argv[i])-1; + break; + } else { + fprintf(stderr,"tree: missing argument to --charset=\n"); + exit(1); + } + } + if (argv[n] != NULL) { + charset = argv[n++]; + j = strlen(argv[i])-1; + } else { + initlinedraw(1); + exit(1); + } + break; + } + if (!strncmp("--si", argv[i], 4)) { + j = strlen(argv[i])-1; + sflag = TRUE; + hflag = TRUE; + siflag = TRUE; + break; + } + if (!strncmp("--du",argv[i],4)) { + j = strlen(argv[i])-1; + sflag = TRUE; + duflag = TRUE; + break; + } + if (!strncmp("--prune",argv[i],7)) { + j = strlen(argv[i])-1; + pruneflag = TRUE; + break; + } + if (!strncmp("--timefmt",argv[i],9)) { + j = 9; + if (*(argv[i]+j) == '=') { + if (*(argv[i]+ (++j))) { + timefmt=scopy(argv[i]+j); + j = strlen(argv[i])-1; + break; + }else { + fprintf(stderr,"tree: missing argument to --timefmt=\n"); + exit(1); + } + } else if (argv[n] != NULL) { + timefmt = scopy(argv[n]); + n++; + j = strlen(argv[i])-1; + } else { + fprintf(stderr,"tree: missing argument to --timefmt\n"); + exit(1); + } + Dflag = TRUE; + break; + } + if (!strncmp("--ignore-case",argv[i],13)) { + j = strlen(argv[i])-1; + ignorecase = TRUE; + break; + } + if (!strncmp("--matchdirs",argv[i],11)) { + j = strlen(argv[i])-1; + matchdirs = TRUE; + break; + } + if (!strncmp("--sort",argv[i],6)) { + j = 6; + if (*(argv[i]+j) == '=') { + if (*(argv[i]+(++j))) { + stmp = argv[i]+j; + j = strlen(argv[i])-1; + } else { + fprintf(stderr,"tree: missing argument to --sort=\n"); + exit(1); + } + } else if (argv[n] != NULL) { + stmp = argv[n++]; + j = strlen(argv[i])-1; + } else { + fprintf(stderr,"tree: missing argument to --sort\n"); + exit(1); + } + basesort = NULL; + for(k=0;sorts[k].name;k++) { + if (strcasecmp(sorts[k].name,stmp) == 0) { + basesort = sorts[k].cmpfunc; + break; + } + } + if (basesort == NULL) { + fprintf(stderr,"tree: sort type '%s' not valid, should be one of: ", stmp); + for(k=0; sorts[k].name; k++) + printf("%s%c", sorts[k].name, sorts[k+1].name? ',': '\n'); + exit(1); + } + break; + } + if (!strncmp("--fromfile",argv[i],10)) { + j = strlen(argv[i])-1; + fromfile=TRUE; + getfulltree = file_getfulltree; + break; + } + if (!strncmp("--metafirst",argv[i],11)) { + j = strlen(argv[i])-1; + metafirst=TRUE; + break; + } + if (!strncmp("--gitignore",argv[i],11)) { + j = strlen(argv[i])-1; + gitignore=TRUE; + break; + } + if (!strncmp("--info",argv[i],6)) { + j = strlen(argv[i])-1; + showinfo=TRUE; + break; + } + fprintf(stderr,"tree: Invalid argument `%s'.\n",argv[i]); + usage(1); + exit(1); + } + default: + printf("here i = %d, n = %d\n", i, n); + fprintf(stderr,"tree: Invalid argument -`%c'.\n",argv[i][j]); + usage(1); + exit(1); + break; + } + } + } else { + if (!dirname) dirname = (char **)xmalloc(sizeof(char *) * (q=MINIT)); + else if (p == (q-2)) dirname = (char **)xrealloc(dirname,sizeof(char *) * (q+=MINC)); + dirname[p++] = scopy(argv[i]); + } + } + if (p) dirname[p] = NULL; + + setoutput(outfilename); + + parse_dir_colors(); + initlinedraw(0); + + /* Insure sensible defaults and sanity check options: */ + if (dirname == NULL) { + dirname = xmalloc(sizeof(char *) * 2); + dirname[0] = scopy("."); + dirname[1] = NULL; + } + if (topsort == NULL) topsort = basesort; + if (timefmt) setlocale(LC_TIME,""); + if (dflag) pruneflag = FALSE; /* You'll just get nothing otherwise. */ + if (Rflag && (Level == -1)) Rflag = FALSE; + + // Not going to implement git configs so no core.excludesFile support. + if (gitignore && (stmp = getenv("GIT_DIR"))) { + char *path = xmalloc(PATH_MAX); + snprintf(path, PATH_MAX, "%s/info/exclude", stmp); + push_filterstack(new_ignorefile(path)); + free(path); + } + if (showinfo) { + push_infostack(new_infofile(INFO_PATH)); + } + + needfulltree = duflag || pruneflag || matchdirs || fromfile; + + emit_tree(dirname, needfulltree); + + if (outfilename != NULL) fclose(outfile); + + return errors ? 2 : 0; +} + +void setoutput(char *filename) +{ + if (filename == NULL) { +#ifdef __EMX__ + _fsetmode(outfile=stdout,Hflag?"b":"t"); +#else + if (outfile == NULL) outfile = stdout; +#endif + } else { +#ifdef __EMX__ + outfile = fopen(filename, Hflag? "wb":"wt"); +#else + outfile = fopen(filename, "w"); +#endif + if (outfile == NULL) { + fprintf(stderr,"tree: invalid filename '%s'\n", filename); + exit(1); + } + } +} + +void usage(int n) +{ + /* 123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */ + /* \t9!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */ + fprintf(n < 2? stderr: stdout, + "usage: tree [-acdfghilnpqrstuvxACDFJQNSUX] [-L level [-R]] [-H baseHREF]\n" + "\t[-T title] [-o filename] [-P pattern] [-I pattern] [--gitignore]\n" + "\t[--matchdirs] [--metafirst] [--ignore-case] [--nolinks] [--inodes]\n" + "\t[--device] [--sort[=]] [--dirsfirst] [--filesfirst]\n" + "\t[--filelimit #] [--si] [--du] [--prune] [--charset X]\n" + "\t[--timefmt[=]format] [--fromfile] [--noreport] [--version] [--help]\n" + "\t[--] [directory ...]\n"); + + if (n < 2) return; + fprintf(stdout, + " ------- Listing options -------\n" + " -a All files are listed.\n" + " -d List directories only.\n" + " -l Follow symbolic links like directories.\n" + " -f Print the full path prefix for each file.\n" + " -x Stay on current filesystem only.\n" + " -L level Descend only level directories deep.\n" + " -R Rerun tree when max dir level reached.\n" + " -P pattern List only those files that match the pattern given.\n" + " -I pattern Do not list files that match the given pattern.\n" + " --gitignore Filter by using .gitignore files.\n" + " --ignore-case Ignore case when pattern matching.\n" + " --matchdirs Include directory names in -P pattern matching.\n" + " --metafirst Print meta-data at the beginning of each line.\n" + " --prune Prune empty directories from the output.\n" + " --info Print information about files found in .info files.\n" + " --noreport Turn off file/directory count at end of tree listing.\n" + " --charset X Use charset X for terminal/HTML and indentation line output.\n" + " --filelimit # Do not descend dirs with more than # files in them.\n" + " -o filename Output to file instead of stdout.\n" + " ------- File options -------\n" + " -q Print non-printable characters as '?'.\n" + " -N Print non-printable characters as is.\n" + " -Q Quote filenames with double quotes.\n" + " -p Print the protections for each file.\n" + " -u Displays file owner or UID number.\n" + " -g Displays file group owner or GID number.\n" + " -s Print the size in bytes of each file.\n" + " -h Print the size in a more human readable way.\n" + " --si Like -h, but use in SI units (powers of 1000).\n" + " --du Compute size of directories by their contents.\n" + " -D Print the date of last modification or (-c) status change.\n" + " --timefmt Print and format time according to the format .\n" + " -F Appends '/', '=', '*', '@', '|' or '>' as per ls -F.\n" + " --inodes Print inode number of each file.\n" + " --device Print device ID number to which each file belongs.\n" + " ------- Sorting options -------\n" + " -v Sort files alphanumerically by version.\n" + " -t Sort files by last modification time.\n" + " -c Sort files by last status change time.\n" + " -U Leave files unsorted.\n" + " -r Reverse the order of the sort.\n" + " --dirsfirst List directories before files (-U disables).\n" + " --filesfirst List files before directories (-U disables).\n" + " --sort X Select sort: name,version,size,mtime,ctime.\n" + " ------- Graphics options -------\n" + " -i Don't print indentation lines.\n" + " -A Print ANSI lines graphic indentation lines.\n" + " -S Print with CP437 (console) graphics indentation lines.\n" + " -n Turn colorization off always (-C overrides).\n" + " -C Turn colorization on always.\n" + " ------- XML/HTML/JSON options -------\n" + " -X Prints out an XML representation of the tree.\n" + " -J Prints out an JSON representation of the tree.\n" + " -H baseHREF Prints out HTML format with baseHREF as top directory.\n" + " -T string Replace the default HTML title and H1 header with string.\n" + " --nolinks Turn off hyperlinks in HTML output.\n" + " ------- Input options -------\n" + " --fromfile Reads paths from files (.=stdin)\n" + " ------- Miscellaneous options -------\n" + " --version Print version and exit.\n" + " --help Print usage and this help message and exit.\n" + " -- Options processing terminator.\n"); + exit(0); +} + +/** + * True if file matches an -I pattern + */ +int patignore(char *name, int isdir) +{ + for(int i=0; i < ipattern; i++) + if (patmatch(name, ipatterns[i], isdir)) return 1; + return 0; +} + +/** + * True if name matches a -P pattern + */ +int patinclude(char *name, int isdir) +{ +// printf("%s ", name); + for(int i=0; i < pattern; i++) + if (patmatch(name, patterns[i], isdir)) { +// printf("included\n"); + return 1; + } +// printf("failed include\n"); + return 0; +} + +/** + * Split out stat portion from read_dir as prelude to just using stat structure directly. + */ +struct _info *getinfo(char *name, char *path) +{ + static char *lbuf = NULL; + static int lbufsize = 0; + struct _info *ent; + struct stat st, lst; + int len, rs; + + if (lbuf == NULL) lbuf = xmalloc(lbufsize = PATH_MAX); + + if (lstat(path,&lst) < 0) return NULL; + + if ((lst.st_mode & S_IFMT) == S_IFLNK) { + if ((rs = stat(path,&st)) < 0) memset(&st, 0, sizeof(st)); + } else { + rs = 0; + st.st_mode = lst.st_mode; + st.st_dev = lst.st_dev; + st.st_ino = lst.st_ino; + } + + int isdir = (st.st_mode & S_IFMT) == S_IFDIR; + +#ifndef __EMX__ + if (gitignore && filtercheck(path, name, isdir)) return NULL; + + if ((lst.st_mode & S_IFMT) != S_IFDIR && !(lflag && ((st.st_mode & S_IFMT) == S_IFDIR))) { + if (pattern && !patinclude(name, isdir)) return NULL; + } + if (ipattern && patignore(name, isdir)) return NULL; +#endif + + if (dflag && ((st.st_mode & S_IFMT) != S_IFDIR)) return NULL; + +#ifndef __EMX__ +// if (pattern && ((lst.st_mode & S_IFMT) == S_IFLNK) && !lflag) continue; +#endif + + ent = (struct _info *)xmalloc(sizeof(struct _info)); + memset(ent, 0, sizeof(struct _info)); + + ent->name = scopy(name); + /* We should just incorporate struct stat into _info, and eliminate this unnecessary copying. + * Made sense long ago when we had fewer options and didn't need half of stat. + */ + ent->mode = lst.st_mode; + ent->uid = lst.st_uid; + ent->gid = lst.st_gid; + ent->size = lst.st_size; + ent->dev = st.st_dev; + ent->inode = st.st_ino; + ent->ldev = lst.st_dev; + ent->linode = lst.st_ino; + ent->lnk = NULL; + ent->orphan = FALSE; + ent->err = NULL; + ent->child = NULL; + + ent->atime = lst.st_atime; + ent->ctime = lst.st_ctime; + ent->mtime = lst.st_mtime; + +#ifdef __EMX__ + ent->attr = lst.st_attr; +#else + + /* These should be eliminated, as they're barely used: */ + ent->isdir = isdir; + ent->issok = ((st.st_mode & S_IFMT) == S_IFSOCK); + ent->isfifo = ((st.st_mode & S_IFMT) == S_IFIFO); + ent->isexe = (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? 1 : 0; + + if ((lst.st_mode & S_IFMT) == S_IFLNK) { + if (lst.st_size+1 > lbufsize) lbuf = xrealloc(lbuf,lbufsize=(lst.st_size+8192)); + if ((len=readlink(path,lbuf,lbufsize-1)) < 0) { + ent->lnk = scopy("[Error reading symbolic link information]"); + ent->isdir = FALSE; + ent->lnkmode = st.st_mode; + } else { + lbuf[len] = 0; + ent->lnk = scopy(lbuf); + if (rs < 0) ent->orphan = TRUE; + ent->lnkmode = st.st_mode; + } + } +#endif + + ent->comment = NULL; + + return ent; +} + +struct _info **read_dir(char *dir, int *n, int infotop) +{ + struct comment *com; + static char *path = NULL; + static long pathsize; + struct _info **dl, *info; + struct dirent *ent; + DIR *d; + int ne, p = 0, i; + int es = (dir[strlen(dir)-1] == '/'); + + if (path == NULL) { + path=xmalloc(pathsize = strlen(dir)+PATH_MAX); + } + + *n = -1; + if ((d=opendir(dir)) == NULL) return NULL; + + dl = (struct _info **)xmalloc(sizeof(struct _info *) * (ne = MINIT)); + + while((ent = (struct dirent *)readdir(d))) { + if (!strcmp("..",ent->d_name) || !strcmp(".",ent->d_name)) continue; + if (Hflag && !strcmp(ent->d_name,"00Tree.html")) continue; + if (!aflag && ent->d_name[0] == '.') continue; + + if (strlen(dir)+strlen(ent->d_name)+2 > pathsize) path = xrealloc(path,pathsize=(strlen(dir)+strlen(ent->d_name)+PATH_MAX)); + if (es) sprintf(path, "%s%s", dir, ent->d_name); + else sprintf(path,"%s/%s",dir,ent->d_name); + + info = getinfo(ent->d_name, path); + if (info) { + if (showinfo && (com = infocheck(path, ent->d_name, infotop, info->isdir))) { + for(i = 0; com->desc[i] != NULL; i++); + info->comment = xmalloc(sizeof(char *) * (i+1)); + for(i = 0; com->desc[i] != NULL; i++) info->comment[i] = scopy(com->desc[i]); + info->comment[i] = NULL; + } + if (p == (ne-1)) dl = (struct _info **)xrealloc(dl,sizeof(struct _info *) * (ne += MINC)); + dl[p++] = info; + } + } + closedir(d); + + if ((*n = p) == 0) { + free(dl); + return NULL; + } + + dl[p] = NULL; + return dl; +} + +void push_files(char *dir, struct ignorefile **ig, struct infofile **inf) +{ + if (gitignore) { + *ig = new_ignorefile(dir); + if (*ig != NULL) push_filterstack(*ig); + } + if (showinfo) { + *inf = new_infofile(dir); + if (*inf != NULL) push_infostack(*inf); + } +} + +/* This is for all the impossible things people wanted the old tree to do. + * This can and will use a large amount of memory for large directory trees + * and also take some time. + */ +struct _info **unix_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err) +{ + char *path; + long pathsize = 0; + struct ignorefile *ig = NULL; + struct infofile *inf = NULL; + struct _info **dir, **sav, **p, *sp; + struct stat sb; + int n; + u_long lev_tmp; + int tmp_pattern = 0; + char *start_rel_path; + + *err = NULL; + if (Level >= 0 && lev > Level) return NULL; + if (xdev && lev == 0) { + stat(d,&sb); + dev = sb.st_dev; + } + // if the directory name matches, turn off pattern matching for contents + if (matchdirs && pattern) { + lev_tmp = lev; + start_rel_path = d + strlen(d); + for (start_rel_path = d + strlen(d); start_rel_path != d; --start_rel_path) { + if (*start_rel_path == '/') + --lev_tmp; + if (lev_tmp <= 0) { + if (*start_rel_path) + ++start_rel_path; + break; + } + } + if (*start_rel_path && patinclude(start_rel_path, 1)) { + tmp_pattern = pattern; + pattern = 0; + } + } + + push_files(d, &ig, &inf); + + sav = dir = read_dir(d, &n, inf != NULL); + if (tmp_pattern) { + pattern = tmp_pattern; + tmp_pattern = 0; + } + if (dir == NULL && n) { + *err = scopy("error opening dir"); + errors++; + return NULL; + } + if (n == 0) { + if (sav != NULL) free_dir(sav); + return NULL; + } + path = xmalloc(pathsize=PATH_MAX); + + if (flimit > 0 && n > flimit) { + sprintf(path,"%d entries exceeds filelimit, not opening dir",n); + *err = scopy(path); + free_dir(sav); + free(path); + return NULL; + } + + if (lev >= maxdirs-1) { + dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024)); + } + + while (*dir) { + if ((*dir)->isdir && !(xdev && dev != (*dir)->dev)) { + if ((*dir)->lnk) { + if (lflag) { + if (findino((*dir)->inode,(*dir)->dev)) { + (*dir)->err = scopy("recursive, not followed"); + } else { + saveino((*dir)->inode, (*dir)->dev); + if (*(*dir)->lnk == '/') + (*dir)->child = unix_getfulltree((*dir)->lnk,lev+1,dev,&((*dir)->size),&((*dir)->err)); + else { + if (strlen(d)+strlen((*dir)->lnk)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024)); + if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->lnk); + else sprintf(path,"%s/%s",d,(*dir)->lnk); + (*dir)->child = unix_getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err)); + } + } + } + } else { + if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024)); + if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name); + else sprintf(path,"%s/%s",d,(*dir)->name); + saveino((*dir)->inode, (*dir)->dev); + (*dir)->child = unix_getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err)); + } + // prune empty folders, unless they match the requested pattern + if (pruneflag && (*dir)->child == NULL && + !(matchdirs && pattern && patinclude((*dir)->name, (*dir)->isdir))) { + sp = *dir; + for(p=dir;*p;p++) *p = *(p+1); + n--; + free(sp->name); + if (sp->lnk) free(sp->lnk); + free(sp); + continue; + } + } + if (duflag) *size += (*dir)->size; + dir++; + } + + // sorting needs to be deferred for --du: + if (topsort) qsort(sav,n,sizeof(struct _info *),topsort); + + free(path); + if (n == 0) { + free_dir(sav); + return NULL; + } + if (ig != NULL) pop_filterstack(); + if (inf != NULL) pop_infostack(); + return sav; +} + +/** + * filesfirst and dirsfirst are now top-level meta-sorts. + */ +int filesfirst(struct _info **a, struct _info **b) +{ + if ((*a)->isdir != (*b)->isdir) { + return (*a)->isdir ? 1 : -1; + } + return basesort(a, b); +} + +int dirsfirst(struct _info **a, struct _info **b) +{ + if ((*a)->isdir != (*b)->isdir) { + return (*a)->isdir ? -1 : 1; + } + return basesort(a, b); +} + +/* Sorting functions */ +int alnumsort(struct _info **a, struct _info **b) +{ + int v = strcoll((*a)->name,(*b)->name); + return reverse? -v : v; +} + +int versort(struct _info **a, struct _info **b) +{ + int v = strverscmp((*a)->name,(*b)->name); + return reverse? -v : v; +} + +int mtimesort(struct _info **a, struct _info **b) +{ + int v; + + if ((*a)->mtime == (*b)->mtime) { + v = strcoll((*a)->name,(*b)->name); + return reverse? -v : v; + } + v = (*a)->mtime == (*b)->mtime? 0 : ((*a)->mtime < (*b)->mtime ? -1 : 1); + return reverse? -v : v; +} + +int ctimesort(struct _info **a, struct _info **b) +{ + int v; + + if ((*a)->ctime == (*b)->ctime) { + v = strcoll((*a)->name,(*b)->name); + return reverse? -v : v; + } + v = (*a)->ctime == (*b)->ctime? 0 : ((*a)->ctime < (*b)->ctime? -1 : 1); + return reverse? -v : v; +} + +int sizecmp(off_t a, off_t b) +{ + return (a == b)? 0 : ((a < b)? 1 : -1); +} + +int fsizesort(struct _info **a, struct _info **b) +{ + int v = sizecmp((*a)->size, (*b)->size); + if (v == 0) v = strcoll((*a)->name,(*b)->name); + return reverse? -v : v; +} + +void *xmalloc (size_t size) +{ + register void *value = malloc (size); + if (value == NULL) { + fprintf(stderr,"tree: virtual memory exhausted.\n"); + exit(1); + } + return value; +} + +void *xrealloc (void *ptr, size_t size) +{ + register void *value = realloc (ptr,size); + if (value == NULL) { + fprintf(stderr,"tree: virtual memory exhausted.\n"); + exit(1); + } + return value; +} + +void free_dir(struct _info **d) +{ + int i; + + for(i=0;d[i];i++) { + free(d[i]->name); + if (d[i]->lnk) free(d[i]->lnk); + free(d[i]); + } + free(d); +} + +char *gnu_getcwd() +{ + int size = 100; + char *buffer = (char *) xmalloc (size); + + while (1) { + char *value = getcwd (buffer, size); + if (value != 0) return buffer; + size *= 2; + free (buffer); + buffer = (char *) xmalloc (size); + } +} + +static inline char cond_lower(char c) +{ + return ignorecase ? tolower(c) : c; +} + +/* + * Patmatch() code courtesy of Thomas Moore (dark@mama.indstate.edu) + * '|' support added by David MacMahon (davidm@astron.Berkeley.EDU) + * Case insensitive support added by Jason A. Donenfeld (Jason@zx2c4.com) + * returns: + * 1 on a match + * 0 on a mismatch + * -1 on a syntax error in the pattern + */ +int patmatch(char *buf, char *pat, int isdir) +{ + int match = 1,m,n; + char *bar = strchr(pat, '|'); + char pprev = 0; + + /* If a bar is found, call patmatch recursively on the two sub-patterns */ + if (bar) { + /* If the bar is the first or last character, it's a syntax error */ + if (bar == pat || !bar[1]) { + return -1; + } + /* Break pattern into two sub-patterns */ + *bar = '\0'; + match = patmatch(buf, pat, isdir); + if (!match) { + match = patmatch(buf, bar+1, isdir); + } + /* Join sub-patterns back into one pattern */ + *bar = '|'; + return match; + } + +// printf("> buf[%s], pat[%s]\n", buf, pat); + while(*pat && match) { + switch(*pat) { + case '[': + pat++; + if(*pat != '^') { + n = 1; + match = 0; + } else { + pat++; + n = 0; + } + while(*pat != ']'){ + if(*pat == '\\') pat++; + if(!*pat /* || *pat == '/' */ ) return -1; + if(pat[1] == '-'){ + m = *pat; + pat += 2; + if(*pat == '\\' && *pat) + pat++; + if(cond_lower(*buf) >= cond_lower(m) && cond_lower(*buf) <= cond_lower(*pat)) + match = n; + if(!*pat) + pat--; + } else if(cond_lower(*buf) == cond_lower(*pat)) match = n; + pat++; + } + buf++; + break; + case '*': + pat++; + if(!*pat) { + int f = (strchr(buf, '/') == NULL); +// printf("end *: buf = '%s', f = %d\n", buf, f); + return f; + } + match = 0; + /* "Support" ** for .gitignore support, mostly the same as *: */ + if (*pat == '*') { + pat++; + if(!*pat) return 1; + + while(*buf && !(match = patmatch(buf, pat, isdir))) { + // ../**/.. is allowed to match a null /: + if (pprev == '/' && *pat == '/' && *(pat+1) && (match = patmatch(buf, pat+1, isdir))) return match; + buf++; + while(*buf && *buf != '/') buf++; + } + } else { +// printf("* buf[%s], pat[%s]\n", buf, pat); + while(*buf && !(match = patmatch(buf++, pat, isdir))) + if (*buf == '/') break; +// if (!*buf && !match) match = patmatch(buf, pat, isdir); + } +// printf("*|%d buf[%s], pat[%s]\n", match, buf, pat); + if (!match && (!*buf || *buf == '/')) match = patmatch(buf, pat, isdir); + return match; + case '?': + if(!*buf) return 0; + buf++; + break; + case '/': + if (!*(pat+1) && !*buf) return isdir; + match = (*buf++ == *pat); + break; + case '\\': + if(*pat) + pat++; + default: + match = (cond_lower(*buf++) == cond_lower(*pat)); + break; + } + pprev = *pat++; + if(match<1) return match; + } + if(!*buf) return match; + return 0; +} + + +/** + * They cried out for ANSI-lines (not really), but here they are, as an option + * for the xterm and console capable among you, as a run-time option. + */ +void indent(int maxlevel) +{ + int i; + + if (ansilines) { + if (dirs[1]) fprintf(outfile,"\033(0"); + for(i=1; (i <= maxlevel) && dirs[i]; i++) { + if (dirs[i+1]) { + if (dirs[i] == 1) fprintf(outfile,"\170 "); + else printf(" "); + } else { + if (dirs[i] == 1) fprintf(outfile,"\164\161\161 "); + else fprintf(outfile,"\155\161\161 "); + } + } + if (dirs[1]) fprintf(outfile,"\033(B"); + } else { + if (Hflag) fprintf(outfile,"\t"); + for(i=1; (i <= maxlevel) && dirs[i]; i++) { + fprintf(outfile,"%s ", + dirs[i+1] ? (dirs[i]==1 ? linedraw->vert : (Hflag? "   " : " ") ) + : (dirs[i]==1 ? linedraw->vert_left:linedraw->corner)); + } + } +} + + +#ifdef __EMX__ +char *prot(long m) +#else +char *prot(mode_t m) +#endif +{ +#ifdef __EMX__ + const u_short *p; + static char buf[6]; + char*cp; + + for(p=ifmt,cp=strcpy(buf,"adshr");*cp;++p,++cp) + if(!(m&*p)) + *cp='-'; +#else + static char buf[11], perms[] = "rwxrwxrwx"; + int i, b; + + for(i=0;ifmt[i] && (m&S_IFMT) != ifmt[i];i++); + buf[0] = fmt[i]; + + /** + * Nice, but maybe not so portable, it is should be no less portable than the + * old code. + */ + for(b=S_IRUSR,i=0; i<9; b>>=1,i++) + buf[i+1] = (m & (b)) ? perms[i] : '-'; + if (m & S_ISUID) buf[3] = (buf[3]=='-')? 'S' : 's'; + if (m & S_ISGID) buf[6] = (buf[6]=='-')? 'S' : 's'; + if (m & S_ISVTX) buf[9] = (buf[9]=='-')? 'T' : 't'; + + buf[10] = 0; +#endif + return buf; +} + +#define SIXMONTHS (6*31*24*60*60) + +char *do_date(time_t t) +{ + static char buf[256]; + struct tm *tm; + + tm = localtime(&t); + + if (timefmt) { + strftime(buf,255,timefmt,tm); + buf[255] = 0; + } else { + time_t c = time(0); + /* Use strftime() so that locale is respected: */ + if (t > c || (t+SIXMONTHS) < c) + strftime(buf,255,"%b %e %Y",tm); + else + strftime(buf,255,"%b %e %R", tm); + } + return buf; +} + +/** + * Must fix this someday + */ +void printit(char *s) +{ + int c; + + if (Nflag) { + if (Qflag) fprintf(outfile, "\"%s\"",s); + else fprintf(outfile,"%s",s); + return; + } + if (mb_cur_max > 1) { + wchar_t *ws, *tp; + ws = xmalloc(sizeof(wchar_t)* (c=(strlen(s)+1))); + if (mbstowcs(ws,s,c) != (size_t)-1) { + if (Qflag) putc('"',outfile); + for(tp=ws;*tp && c > 1;tp++, c--) { + if (iswprint(*tp)) fprintf(outfile,"%lc",(wint_t)*tp); + else { + if (qflag) putc('?',outfile); + else fprintf(outfile,"\\%03o",(unsigned int)*tp); + } + } + if (Qflag) putc('"',outfile); + free(ws); + return; + } + free(ws); + } + if (Qflag) putc('"',outfile); + for(;*s;s++) { + c = (unsigned char)*s; +#ifdef __EMX__ + if(_nls_is_dbcs_lead(*(unsigned char*)s)){ + putc(*s,outfile); + putc(*++s,outfile); + continue; + } +#endif + if((c >= 7 && c <= 13) || c == '\\' || (c == '"' && Qflag) || (c == ' ' && !Qflag)) { + putc('\\',outfile); + if (c > 13) putc(c, outfile); + else putc("abtnvfr"[c-7], outfile); + } else if (isprint(c)) putc(c,outfile); + else { + if (qflag) { + if (mb_cur_max > 1 && c > 127) putc(c,outfile); + else putc('?',outfile); + } else fprintf(outfile,"\\%03o",c); + } + } + if (Qflag) putc('"',outfile); +} + +int psize(char *buf, off_t size) +{ + static char *iec_unit="BKMGTPEZY", *si_unit = "dkMGTPEZY"; + char *unit = siflag ? si_unit : iec_unit; + int idx, usize = siflag ? 1000 : 1024; + + if (hflag || siflag) { + for (idx=size= (usize*usize); idx++,size/=usize); + if (!idx) return sprintf(buf, " %4d", (int)size); + else return sprintf(buf, ((size/usize) >= 10)? " %3.0f%c" : " %3.1f%c" , (float)size/(float)usize,unit[idx]); + } else return sprintf(buf, sizeof(off_t) == sizeof(long long)? " %11lld" : " %9lld", (long long int)size); +} + +char Ftype(mode_t mode) +{ + int m = mode & S_IFMT; + if (!dflag && m == S_IFDIR) return '/'; + else if (m == S_IFSOCK) return '='; + else if (m == S_IFIFO) return '|'; + else if (m == S_IFLNK) return '@'; /* Here, but never actually used though. */ +#ifdef S_IFDOOR + else if (m == S_IFDOOR) return '>'; +#endif + else if ((m == S_IFREG) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; + return 0; +} + +struct _info *stat2info(struct stat *st) +{ + static struct _info info; + + info.linode = st->st_ino; + info.ldev = st->st_dev; +#ifdef __EMX__ + info.attr = st->st_attr +#endif + info.mode = st->st_mode; + info.uid = st->st_uid; + info.gid = st->st_gid; + info.size = st->st_size; + info.atime = st->st_atime; + info.ctime = st->st_ctime; + info.mtime = st->st_mtime; + + info.isdir = ((st->st_mode & S_IFMT) == S_IFDIR); + info.issok = ((st->st_mode & S_IFMT) == S_IFSOCK); + info.isfifo = ((st->st_mode & S_IFMT) == S_IFIFO); + info.isexe = (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? 1 : 0; + + return &info; +} + +char *fillinfo(char *buf, struct _info *ent) +{ + int n; + buf[n=0] = 0; + #ifdef __USE_FILE_OFFSET64 + if (inodeflag) n += sprintf(buf," %7lld",(long long)ent->linode); + #else + if (inodeflag) n += sprintf(buf," %7ld",(long int)ent->linode); + #endif + if (devflag) n += sprintf(buf+n, " %3d", (int)ent->ldev); + #ifdef __EMX__ + if (pflag) n += sprintf(buf+n, " %s",prot(ent->attr)); + #else + if (pflag) n += sprintf(buf+n, " %s", prot(ent->mode)); + #endif + if (uflag) n += sprintf(buf+n, " %-8.32s", uidtoname(ent->uid)); + if (gflag) n += sprintf(buf+n, " %-8.32s", gidtoname(ent->gid)); + if (sflag) n += psize(buf+n,ent->size); + if (Dflag) n += sprintf(buf+n, " %s", do_date(cflag? ent->ctime : ent->mtime)); + + if (buf[0] == ' ') { + buf[0] = '['; + sprintf(buf+n, "]"); + } + + return buf; +} diff --git a/tree.h b/tree.h new file mode 100644 index 0000000..9fcdb29 --- /dev/null +++ b/tree.h @@ -0,0 +1,311 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __EMX__ /* for OS/2 systems */ +# define INCL_DOSFILEMGR +# define INCL_DOSNLS +# include +# include +# include + /* On many systems stat() function is idential to lstat() function. + * But the OS/2 does not support symbolic links and doesn't have lstat() function. + */ +# define lstat stat +# define strcasecmp stricmp + /* Following two functions, getcwd() and chdir() don't support for drive letters. + * To implement support them, use _getcwd2() and _chdir2(). + */ +# define getcwd _getcwd2 +# define chdir _chdir2 +#endif + +#include +#include +#include +#include + +#ifdef __ANDROID +#define mbstowcs(w,m,x) mbsrtowcs(w,(const char**)(& #m),x,NULL) +#endif + +// Start using PATH_MAX instead of the magic number 4096 everywhere. +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifndef INFO_PATH +#define INFO_PATH "/usr/share/finfo/global_info" +#endif + +#ifdef __linux__ +#include +# define ENV_STDDATA_FD "STDDATA_FD" +# ifndef STDDATA_FILENO +# define STDDATA_FILENO 3 +# endif +#endif + +/* Should probably use strdup(), but we like our xmalloc() */ +#define scopy(x) strcpy(xmalloc(strlen(x)+1),(x)) +#define MINIT 30 /* number of dir entries to initially allocate */ +#define MINC 20 /* allocation increment */ + +#ifndef TRUE +typedef enum {FALSE=0, TRUE} bool; +#else +typedef int bool; +#endif + +struct _info { + char *name; + char *lnk; + bool isdir; + bool issok; + bool isfifo; + bool isexe; + bool orphan; + mode_t mode, lnkmode; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, ctime, mtime; + dev_t dev, ldev; + ino_t inode, linode; + #ifdef __EMX__ + long attr; + #endif + char *err; + const char *tag; + char **comment; + struct _info **child, *next, *tchild; +}; + +/* list.c */ +struct totals { + u_long files, dirs; + off_t size; +}; + +struct listingcalls { + void (*intro)(void); + void (*outtro)(void); + int (*printinfo)(char *dirname, struct _info *file, int level); + int (*printfile)(char *dirname, char *filename, struct _info *file, int descend); + int (*error)(char *error); + void (*newline)(struct _info *file, int level, int postdir, int needcomma); + void (*close)(struct _info *file, int level, int needcomma); + void (*report)(struct totals tot); +}; + + +/* hash.c */ +struct xtable { + unsigned int xid; + char *name; + struct xtable *nxt; +}; +struct inotable { + ino_t inode; + dev_t device; + struct inotable *nxt; +}; + +/* color.c */ +struct colortable { + char *term_flg, *CSS_name, *font_fg, *font_bg; +}; +struct extensions { + char *ext; + char *term_flg, *CSS_name, *web_fg, *web_bg, *web_extattr; + struct extensions *nxt; +}; +struct linedraw { + const char **name, *vert, *vert_left, *corner, *copy; + const char *ctop, *cbot, *cmid, *cext, *csingle; +}; +struct meta_ids { + char *name; + char *term_flg; +}; + +/* filter.c */ +struct pattern { + char *pattern; + int relative; + struct pattern *next; +}; + +struct ignorefile { + char *path; + struct pattern *remove, *reverse; + struct ignorefile *next; +}; + +/* info.c */ +struct comment { + struct pattern *pattern; + char **desc; + struct comment *next; +}; + +struct infofile { + char *path; + struct comment *comments; + struct infofile *next; +}; + + +/* Function prototypes: */ +/* tree.c */ +void setoutput(char *filename); +void usage(int); +void push_files(char *dir, struct ignorefile **ig, struct infofile **inf); +int patignore(char *name, int isdir); +int patinclude(char *name, int isdir); +struct _info **unix_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err); +struct _info **read_dir(char *dir, int *n, int infotop); + +int filesfirst(struct _info **, struct _info **); +int dirsfirst(struct _info **, struct _info **); +int alnumsort(struct _info **, struct _info **); +int versort(struct _info **a, struct _info **b); +int reversealnumsort(struct _info **, struct _info **); +int mtimesort(struct _info **, struct _info **); +int ctimesort(struct _info **, struct _info **); +int sizecmp(off_t a, off_t b); +int fsizesort(struct _info **a, struct _info **b); + +void *xmalloc(size_t), *xrealloc(void *, size_t); +char *gnu_getcwd(); +int patmatch(char *, char *, int); +void indent(int maxlevel); +void free_dir(struct _info **); +#ifdef __EMX__ +char *prot(long); +#else +char *prot(mode_t); +#endif +char *do_date(time_t); +void printit(char *); +int psize(char *buf, off_t size); +char Ftype(mode_t mode); +struct _info *stat2info(struct stat *st); +char *fillinfo(char *buf, struct _info *ent); + +/* list.c */ +void null_intro(void); +void null_outtro(void); +void null_close(struct _info *file, int level, int needcomma); +void emit_tree(char **dirname, bool needfulltree); +struct totals listdir(char *dirname, struct _info **dir, int lev, dev_t dev, bool hasfulltree); + +/* unix.c */ +int unix_printinfo(char *dirname, struct _info *file, int level); +int unix_printfile(char *dirname, char *filename, struct _info *file, int descend); +int unix_error(char *error); +void unix_newline(struct _info *file, int level, int postdir, int needcomma); +void unix_report(struct totals tot); + +/* html.c */ +void html_intro(void); +void html_outtro(void); +int html_printinfo(char *dirname, struct _info *file, int level); +int html_printfile(char *dirname, char *filename, struct _info *file, int descend); +int html_error(char *error); +void html_newline(struct _info *file, int level, int postdir, int needcomma); +void html_close(struct _info *file, int level, int needcomma); +void html_report(struct totals tot); +void html_encode(FILE *fd, char *s); + +/* xml.c */ +void xml_intro(void); +void xml_outtro(void); +int xml_printinfo(char *dirname, struct _info *file, int level); +int xml_printfile(char *dirname, char *filename, struct _info *file, int descend); +int xml_error(char *error); +void xml_newline(struct _info *file, int level, int postdir, int needcomma); +void xml_close(struct _info *file, int level, int needcomma); +void xml_report(struct totals tot); + +/* json.c */ +void json_indent(int maxlevel); +void json_fillinfo(struct _info *ent); +void json_intro(void); +void json_outtro(void); +int json_printinfo(char *dirname, struct _info *file, int level); +int json_printfile(char *dirname, char *filename, struct _info *file, int descend); +int json_error(char *error); +void json_newline(struct _info *file, int level, int postdir, int needcomma); +void json_close(struct _info *file, int level, int needcomma); +void json_report(struct totals tot); + +/* color.c */ +void parse_dir_colors(); +int color(u_short mode, char *name, bool orphan, bool islink); +void endcolor(void); +const char *getcharset(void); +void initlinedraw(int); + +/* hash.c */ +char *uidtoname(uid_t uid); +char *gidtoname(gid_t gid); +int findino(ino_t, dev_t); +void saveino(ino_t, dev_t); + +/* file.c */ +struct _info **file_getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err); + +/* filter.c */ +void gittrim(char *s); +struct pattern *new_pattern(char *pattern); +int filtercheck(char *path, char *name, int isdir); +struct ignorefile *new_ignorefile(char *path); +void push_filterstack(struct ignorefile *ig); +struct ignorefile *pop_filterstack(void); + +/* info.c */ +struct infofile *new_infofile(char *path); +void push_infostack(struct infofile *inf); +struct infofile *pop_infostack(void); +struct comment *infocheck(char *path, char *name, int top, int isdir); +void printcomment(int line, int lines, char *s); + +/* list.c */ +void new_emit_unix(char **dirname, bool needfulltree); + + +/* We use the strverscmp.c file if we're not linux: */ +#ifndef __linux__ +int strverscmp (const char *s1, const char *s2); +#endif diff --git a/tree.o b/tree.o new file mode 100644 index 0000000..e00dad3 Binary files /dev/null and b/tree.o differ diff --git a/unix.c b/unix.c new file mode 100644 index 0000000..5dbcbdf --- /dev/null +++ b/unix.c @@ -0,0 +1,111 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern FILE *outfile; +extern bool dflag, Fflag, duflag, metafirst, hflag, siflag, noindent; +extern bool colorize, linktargetcolor; +extern const struct linedraw *linedraw; +extern int *dirs; + +static char info[512] = {0}; + +int unix_printinfo(char *dirname, struct _info *file, int level) +{ + fillinfo(info, file); + if (metafirst) { + if (info[0] == '[') fprintf(outfile, "%s ",info); + if (!noindent) indent(level); + } else { + if (!noindent) indent(level); + if (info[0] == '[') fprintf(outfile, "%s ",info); + } + return 0; +} + +int unix_printfile(char *dirname, char *filename, struct _info *file, int descend) +{ + int colored = FALSE, c; + + if (file && colorize) { + if (file->lnk && linktargetcolor) colored = color(file->lnkmode,file->name,file->orphan,FALSE); + else colored = color(file->mode,file->name,file->orphan,FALSE); + } + + printit(filename); + + if (colored) endcolor(); + + if (file) { + if (Fflag && !file->lnk) { + if ((c = Ftype(file->mode))) fputc(c, outfile); + } + + if (file->lnk) { + fprintf(outfile," -> "); + if (colorize) colored = color(file->lnkmode,file->lnk,file->orphan,TRUE); + printit(file->lnk); + if (colored) endcolor(); + if (Fflag) { + if ((c = Ftype(file->lnkmode))) fputc(c, outfile); + } + } + } + return 0; +} + +int unix_error(char *error) +{ + fprintf(outfile, " [%s]", error); + return 0; +} + +void unix_newline(struct _info *file, int level, int postdir, int needcomma) +{ + if (postdir <= 0) fprintf(outfile, "\n"); + if (file && file->comment) { + int infosize = 0, line, lines; + if (metafirst) infosize = info[0] == '['? strlen(info)+2 : 0; + + for(lines = 0; file->comment[lines]; lines++); + dirs[level+1] = 1; + for(line = 0; line < lines; line++) { + if (metafirst) { + printf("%*s", infosize, ""); + } + indent(level); + printcomment(line, lines, file->comment[line]); + } + dirs[level+1] = 0; + } +} + +void unix_report(struct totals tot) +{ + char buf[256]; + + fputc('\n', outfile); + if (duflag) { + psize(buf, tot.size); + fprintf(outfile,"%s%s used in ", buf, hflag || siflag? "" : " bytes"); + } + if (dflag) + fprintf(outfile,"%ld director%s\n",tot.dirs,(tot.dirs==1? "y":"ies")); + else + fprintf(outfile,"%ld director%s, %ld file%s\n",tot.dirs,(tot.dirs==1? "y":"ies"),tot.files,(tot.files==1? "":"s")); +} diff --git a/unix.o b/unix.o new file mode 100644 index 0000000..b7752b0 Binary files /dev/null and b/unix.o differ diff --git a/xml.c b/xml.c new file mode 100644 index 0000000..e8d1e61 --- /dev/null +++ b/xml.c @@ -0,0 +1,181 @@ +/* $Copyright: $ + * Copyright (c) 1996 - 2022 by Steve Baker (ice@mama.indstate.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "tree.h" + +extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag; +extern bool Dflag, inodeflag, devflag, Rflag, cflag, duflag, siflag; +extern bool noindent, force_color, xdev, nolinks, noreport; +extern const char *charset; + +extern const int ifmt[]; +extern const char fmt[], *ftype[]; + +extern FILE *outfile; +extern int Level, *dirs, maxdirs, errors; + +extern char *endcode; + +extern struct listingcalls lc; + +/* + + + + ... if link is followed, otherwise this is empty. + + + some error + + + + + + ... + + + # + # + # + + +*/ + +void xml_indent(int maxlevel) +{ + int i; + + fprintf(outfile, " "); + for(i=0; iinode); + #else + if (inodeflag) fprintf(outfile," inode=\"%ld\"",(long int)ent->inode); + #endif + if (devflag) fprintf(outfile, " dev=\"%d\"", (int)ent->dev); + #ifdef __EMX__ + if (pflag) fprintf(outfile, " mode=\"%04o\" prot=\"%s\"",ent->attr, prot(ent->attr)); + #else + if (pflag) fprintf(outfile, " mode=\"%04o\" prot=\"%s\"", ent->mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX), prot(ent->mode)); + #endif + if (uflag) fprintf(outfile, " user=\"%s\"", uidtoname(ent->uid)); + if (gflag) fprintf(outfile, " group=\"%s\"", gidtoname(ent->gid)); + if (sflag) fprintf(outfile, " size=\"%lld\"", (long long int)(ent->size)); + if (Dflag) fprintf(outfile, " time=\"%s\"", do_date(cflag? ent->ctime : ent->mtime)); +} + +void xml_intro(void) +{ + extern char *_nl; + + fprintf(outfile,"%s%s",_nl,_nl); +} + +void xml_outtro(void) +{ + fprintf(outfile,"\n"); +} + +int xml_printinfo(char *dirname, struct _info *file, int level) +{ + mode_t mt; + int t; + + if (!noindent) xml_indent(level); + + if (file->lnk) mt = file->mode & S_IFMT; + else mt = file->mode & S_IFMT; + + for(t=0;ifmt[t];t++) + if (ifmt[t] == mt) break; + fprintf(outfile,"<%s", (file->tag = ftype[t])); + + return 0; +} + +int xml_printfile(char *dirname, char *filename, struct _info *file, int descend) +{ + int t, mt; + + if (file) { + if (file->lnk) mt = file->mode & S_IFMT; + else mt = file->mode & S_IFMT; + } else mt = 0; + for(t=0;ifmt[t];t++) + if (ifmt[t] == mt) break; + fprintf(outfile,"<%s", ftype[t]); + + fprintf(outfile, " name=\""); + html_encode(outfile, filename); + fputc('"',outfile); + + if (file && file->comment) { + fprintf(outfile, " info=\""); + for(int i=0; file->comment[i]; i++) { + html_encode(outfile, file->comment[i]); + if (file->comment[i+1]) fprintf(outfile, "\n"); + } + fputc('"', outfile); + } + + if (file && file->lnk) { + fprintf(outfile, " target=\""); + html_encode(outfile,file->lnk); + fputc('"',outfile); + } + if (file) xml_fillinfo(file); + fputc('>',outfile); + + return 1; +} + +int xml_error(char *error) +{ + fprintf(outfile,"%s", error); + + return 0; +} + +void xml_newline(struct _info *file, int level, int postdir, int needcomma) +{ + if (postdir >= 0) fprintf(outfile, "\n"); +} + +void xml_close(struct _info *file, int level, int needcomma) +{ + if (!noindent && level >= 0) xml_indent(level-1); + fprintf(outfile,"%s", file? file->tag : "unknown", noindent? "" : "\n"); +} + + +void xml_report(struct totals tot) +{ + extern char *_nl; + + fprintf(outfile,"%s%s",noindent?"":" ", _nl); + if (duflag) fprintf(outfile,"%s%lld%s", noindent?"":" ", (long long int)tot.size, _nl); + fprintf(outfile,"%s%ld%s", noindent?"":" ", tot.dirs, _nl); + if (!dflag) fprintf(outfile,"%s%ld%s", noindent?"":" ", tot.files, _nl); + fprintf(outfile,"%s%s",noindent?"":" ", _nl); +} diff --git a/xml.o b/xml.o new file mode 100644 index 0000000..0edbaf6 Binary files /dev/null and b/xml.o differ