fileutils/COPYING 644 473 0 30310 4702305040 7125 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our 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. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, 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 a 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 tell them 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. 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 Agreement 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 work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 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 General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual 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 General Public License. d) 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. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 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 Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying 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. 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. 7. 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 the 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 the license, you may choose any version ever published by the Free Software Foundation. 8. 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 9. 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. 10. 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 Appendix: 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 humanity, 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) 19yy 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19xx 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 a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! 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 a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the programfileutils/ChangeLog 644 473 0 124731 4702305040 7677 Mon Feb 5 17:29:20 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * Version 1.0 released. * Move discussion of missing tail -r option from tail.c to README. Sun Feb 4 02:23:01 1990 David J. MacKenzie (djm at rice-chex) * Makefile: Note that _doprnt seems to be broken on the sparc. Get rid of separate defaults for USG and BSD because the distinction is blurry. Sat Feb 3 04:03:32 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * cp.c (do_copy, copy, copy_dir): Rename `new_structure' to `new_dst'. (copy): If unlink because of flag_force succeeds, make new_dst nonzero. Fix wrong comment. * mkdir.c, rmdir.c: Replace functions fatal, nonfatal, and nonfatal_perror with function error, and rename global var `status' to `errors'. Fri Feb 2 11:15:18 1990 David J. MacKenzie (djm at rice-chex) * rm.c (main): Recognize new -o option to attempt to override protection of unwritable files, bypassing the query while still providing error messages. (rm): Don't ask question if -o was given. (usage): Mention -o +override option. * chmod.c (main): Recognize new -d option to dereference symlinks, setting xstat to stat or lstat appropriately. (change_file_mode): Use *xstat instead of lstat. (usage): Mention -d option. * chmod.c (nonfatal_perror): Set `errors' to 1 instead of incrementing it. * mv.c (error): New function to print message and set `errors'. (do_move): Call error instead of fprintf and perror. (perror): Function definition (for USG) removed. (main): Use new global var `errors' for exit status. (Makefile): Mention that -DVPRINTF might be needed for USG. Thu Feb 1 15:50:12 1990 David J. MacKenzie (djm at rice-chex) * chmod.c (main): Use new global var `errors' for exit status. (nonfatal_perror): Increment `errors'. Wed Jan 31 18:59:03 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * ls.c (decode_switches, usage): Change -z option to -S (to sort by file size), removing the old meaning of -S (don't sort) since it will be wanted rarely enough that +sort=none will suffice. If I think of a good letter for it I might put the short option for it back. (fatal, error): Change type of arg1 and arg2 to char * from int. (decode_switches, xmalloc, xrealloc): Add casts to calls of fatal. * Makefile (mkdir): Had CFLAGS, wanted LDFLAGS. Tue Jan 30 00:48:46 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * head.c, tail.c, mkdir.c, rmdir.c, dd.c, ls.c: Start all error messages with lowercase, for consistency. * rm.c (rm, clear_directory): Use the existing global var `pathname' instead of passing the path to remove as an argument, so that if `pathname' is reallocated, the old copy of `pathname' does not have to be left around for instances of the rm function that have not returned yet, and eventually lost and never freed. (rm): Pass pointer to stat structure as arg to clear_directory. (clear_directory): Take a pointer to a stat structure as an arg. Copy the directory contents into two buffers before processing them, so excessive file descriptors are not consumed. Move the call to check_stack to after the new pathname has been constructed, so any error message printed by check_stack is accurate. (check_stack): Take an i-number instead of a struct direct * as an argument. Remove the check for 'this should never happen'. (stp_cpy): New function. * ls.c: Add -S +sort=none option to disable sorting of filenames. Remove long options that are redundant with +sort, +format, or +time to make the option set easier to comprehend. (decode_switches): Handle -S option. (sort_files): Handle case sort_none. (usage): Update message. Mon Jan 29 14:25:47 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * chmod.c (nonfatal_perror): Add arg for describing the circumstances in which the error occurred. (change_file_mode, change_dir_mode): Add new arg to calls of nonfatal_perror. (change_dir_mode): Add arg for results of stat on the directory. Read the directory into a buffer before processing its contents so excessive file descriptors are not consumed. (change_file_mode): Add new arg to call of change_dir_mode. (stp_cpy): New function. * rm.c (clear_directory): If errno is set after failed opendir, print the system error message. Fri Jan 26 14:03:12 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * dd.c (pfatal_with_name): Add program name to error message, and rearrange message. (main, copy): Make error messages clearer. * rm.c (perror_with_name): Add an argument for describing the circumstances in which the error occurred. (rm): Add the new argument to perror_with_name calls. Thu Jan 25 15:11:16 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * ls.c (usage): Mention +format=long and +time=use options. * ln.c (do_link): Use lstat instead of access when testing for the existance of the target file, in case the target is a symlink. * dd.c (main, usage): Remove conv=debug option. gdb is more useful. * du.c (usage, main): Alphabetize the options. Wed Jan 24 10:34:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * dd.c, du.c, mv.c, rm.c: Change some functions' return types to void. * cp.c, cp-aux.c, cp-hash.c: Remove void casts and indent. * dd.c: Un-nest some statements, make comments into full sentences, indent. (copy): Add program name and explanations to perror messages. (pfatal_with_name): Print filename on stderr, not stdout. * cmp.c: Un-nest some statements, indent. (main): Change an error message. * dirlib.c: Make compilation conditional on NEED_MKDIR instead of USG. * Makefile: Document NEED_MKDIR. * rm.c (clear_directory): Account for '/' and '\0' when checking whether length of `pathname' is sufficient. Mon Jan 22 00:32:09 1990 David J. MacKenzie (djm at albert.ai.mit.edu) * README: New file. * Makefile: Include README in distribution. Sun Jan 21 00:40:56 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * system.h [USGr3]: If DIRSIZ is already defined, don't redefine it. * Makefile: Add linking rules for cat, cmp, du, head, ln, mv, and tail, for the sake of old makes that don't understand ".c:" default rules. Alphabetize the linking rules. Add INSTALL macro for program to install binaries in BIN directory. * cat.c, cp.c, du.c, ln.c, ls.c, mv.c: Remove declarations of alloca; already done in system.h. Tue Jan 16 23:10:17 1990 David J. MacKenzie (djm at rice-chex) * Makefile: Fix a couple of typos. Mon Jan 15 01:22:31 1990 David J. MacKenzie (djm at rice-chex) * rmdir.c: New file, including -p option for POSIX. Sun Jan 14 21:04:11 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * Makefile: Remove rules for making rmdir.o from rm.c. Add rmdir.c to distribution. * rm.c: Remove support for compiling as rmdir. It was messy to begin with, and would have gotten worse when the POSIX -p option was added to rmdir. Remove -D, +debug option and add -v, +verbose option. Change some error messages. Sat Jan 13 00:14:05 1990 David J. MacKenzie (djm at rice-chex) * dirlib.c: New file containing mkdir and rmdir functions for old USG systems (empty for BSD). * Makefile: Ensure that /bin/sh is the shell used. Omit references to defining USG2, as it is no longer used. Break down LIBS into LIBOBJS and LIBS, for versions of make that don't correctly handle dependencies on -lfoo. (dir.o, vdir.o, rmdir.o): Add commented out alternate rules for compilers that can't do -c -o. (install): Use cp -f instead of cp. Instead of copying d and v, link them. (mkdir, cp, rmdir): Depend on dirlib.o. (dist): Add dirlib.c to list of files to distribute. Fri Jan 12 00:58:18 1990 David J. MacKenzie (djm at rice-chex) * chmod.c (describe_change): Remove the word `file' from the messages, since it could be misleading when describing directories and other non-regular files. * mv.c (do_move): Don't use access to determine writability of symlinks, as it will return invalid results. Thu Jan 11 00:51:17 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * chmod.c, head.c, ls.c, tail.c (main, usage): Remove version numbers for individual programs, as they have become fairly meaningless. Wed Jan 10 02:34:19 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * chmod.c (change_file_mode, nonfatal_perror): Fix enum type clashes. * cat.c: Rename swallow_empty_lines_after_empty_lines to squeeze_empty_lines for old compilers (sheesh!). * cp.c (copy): Change xstat () to (*xstat) () for old compilers. * system.h: Declare getcwd if USG. Use USGr3 and SYSNDIR instead of NDIR macro to determine directory routines header file, for compatibility with glob.c. * Makefile: Revise comments for USG. * mv.c (perror): Declare external errno vars. (do_move): Use lstat rather than stat and access. * ls.c (usage): Split message into two for old compilers. Thu Jan 4 01:02:59 1990 David J. MacKenzie (djm at gnu) * cp.h: Do not declare functions that are declared in . Wed Jan 3 23:42:26 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * system.h: Include , or if USG. * cmp.c, du.c, ls.c: Do not directly include . * du.c (blocks_to_kb): If BLKSIZE is defined and equal to 1024, just return the number of blocks. * mv.c (perror): Slight change in "unknown error" message. * Makefile: Mention extra HP/UX flag in comment. Tue Jan 2 01:10:44 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu) * chmod.c (change_dir_mode): Call xmalloc and free less often, and use strcpy instead of sprintf, for speed. * mkdir.c: New program. * Makefile: Add rules for mkdir. * chmod.c: Move several functions into modechange.c and declarations into modechange.h. (change_file_mode): Rename from xchmod. (change_dir_mode): Rename from xchmoddir. * modechange.c: New file of library functions to manipulate file modes. * modechange.h: New header file for modechange.c and programs that use it. * Makefile: Link chmod with modechange.o and include modechange.[ch] in distribution. Sun Dec 31 17:42:55 1989 David J. MacKenzie (djm at rice-chex) * rm.c (rm): Move misplaced quote. Fri Dec 29 03:10:51 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ln.c (main): Recognize new -f and -i POSIX options, and new -v option for consistency with cp and mv. (do_link): Remove an existing file if -f given, or if -i given and confirmed by user. List files being linked if -v given. (confirm): New function. (usage): Mention new options. * mv.c (do_move): Slight change in query message. (usage): Alphabetize options listing. * rm.c (yesno): Allow 'Y' as well as 'y' to confirm, for consistency with cp and mv. (rm): Flush output before reading. Don't print the program name in the interactive queries, for consistency with cp and mv. * ls.c (get_link_name): Instead of guessing the size of the link name, use the file size from stat. * cp.c (main): exit instead of returning, for the benefit of some C compilers that don't handle a return from main correctly. Recognize new POSIX -f option, `flag_force'. (copy): Use flag_force. Fix typo; had S_IFLINK, wanted S_IFLNK. * cp-aux.c (usage): Mention -f in message and alphabetize the options lists. * rm.c (rm): Make -f option cause -i option to be ignored, for POSIX compatibility. Thu Dec 28 03:25:28 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * Makefile: Change default binary directory from /usr/local/bin to /usr/local/gnubin. * rm.c (xmalloc): Print memory exhausted message even if -f option is given. * cat.c, cp.c, du.c, ln.c, ls.c, mv.c: Declare alloca, with special cases for GNU C and sparc. * du.c, rm.c (xmalloc, xrealloc): Don't pass a null pointer from malloc through if 0 bytes are requested; print an error message, on the assumption that the request for 0 bytes is a bug. Fri Dec 22 23:08:33 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * tail.c (tail_lines): Don't run file_lines on an empty file, to avoid spurious error message. Wed Dec 20 00:33:10 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * du.c (main): Don't truncate a single letter directory name if it is not '/'. * cp.c: Use int instead of uid_t and gid_t; too much trouble to figure out which systems have the typedefs. * system.h: Remove uid_t and gid_t typedefs. Declare alloca if not GNU C or sparc. * mv.c: Move exit (1) from main to usage. (rename): Define if USG, not USG2 (at least this is correct for 386/ix). (perror): New function if USG defined, because System V perror chokes on perror (0). * cat.c, cmp.c, cp-aux.c, dd.c, du.c, head.c, ln.c, ls.c, mv.c, rm.c, tail.c: Combine multiple fprintf calls for usage messages into single calls, to make editing the messages easier. Sat Dec 16 16:06:03 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * Makefile: Add "CC = gcc". * du.c: Change +count-all option to +count-links. Fri Dec 8 21:01:29 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * du.c: Change +combine option to +total. Define INITIAL_PATH_SIZE. Global: fix up some comments and add comments on global variables that lacked them. (usage): Reflect argument change. (main): Use INITIAL_PATH_SIZE. (count_entry): Rename from durec (less cryptic). Use simpler but functionally equivalent code to determine whether to print the entry's size. Mon Dec 4 14:14:22 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cat.c (cat): Only number blank lines if nonblank lines are also being numbered. Sat Dec 2 13:52:15 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * du.c: Add descriptive comments to global variable definitions. Fri Dec 1 19:10:33 1989 David J. MacKenzie (djm at trix) * cat.c (main): Make numbers_at_empty_lines default to 1 so -n option works. * Makefile: Add -g to default LDFLAGS. (clean): Use /bin/rm so we don't get ./rm, in case it was compiled for a different architecture. Wed Nov 29 10:21:53 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * du.c: Remove #include because system.h now does it. Tue Nov 28 00:33:22 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ls.c (decode_switches): Add new +format, +time, and +sort options that take strings as args to select output style. Decide name printed in version message at compile time instead of run time. (argmatch): New function to check if a long option arg is valid. (invalid_arg): New function to complain about an invalid arg to a long option and exit. (usage): Document new options. * Makefile (install): Make install depend on all. Mon Nov 27 13:09:46 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * chmod.c (xchmod): stat -> lstat. Sun Nov 26 01:28:21 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cp-aux.c (user_confirm_overwriting): Print message on stdout instead of stderr to be consistent with mv. Remove accidental second "Usage:" from string. * mv.c (yes): Put getchar value into an int, not a char. * Makefile (dist): Include glob.c in tar file. Sat Nov 25 23:36:54 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cp-aux.c (usage), cat.c (usage), cmp.c (usage), du.c (usage), head.c (usage), tail.c (usage), ls.c (usage): Combine separate short and long option usage messages into single messages. * mv.c: Add -v (+verbose) function like cp's to list files as they are moved. Indent to make spacing consistent. (main): Handle -v option. (movefile): Remove unneccessary temporary variable. (yes): Flush stdout before reading, as a precaution. (do_move): Stat the source file, then if -v was specified, print the from and to names. Eliminate later stat. (usage): Document -v (+verbose). Fri Nov 24 02:41:27 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cp-hash.c: Fix spelling in comments. * system.h: For USG, define getwd in terms of getcwd. Try to get MAXPATHLEN from sys/param.h. * du.c: Indent to standardize spacing. Don't declare getwd because system.h now does it. Fix spelling in comments. (xmalloc, xrealloc): New functions to allocate memory with error check. Global: Use xmalloc and xrealloc instead of checking the return value every time. Global: Remove casts to void. (main): Fix apparent typo: had &, wants &&. (durec): Don't simply skip first two directory entries, but check for "." and ".." explicitly. Thu Nov 23 16:41:17 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * rm.c: Rename `no_rmdir_long_opts' to `rm_long_opts'. (main): Merge tests for rmdirflag to determine options into one if statement. Call usage to print usage message. (usage): New function. Mention long options. (xmalloc): Change error message. Return of 0 is ok if 0 bytes requested (ANSI C). * chmod.c (describe_change): Display different messages depending on whether the file's mode has changed or not. (xchmod): Call describe_change with flag telling it whether the file's mode has changed. * cp.c (copy_dir): Instead of always skipping the first two entries of a directory to recursively copy, explicitly skip "." and ".." when they are encountered, because some NFS filesystems have directories without "." and "..". (copy_reg): If less than sizeof(int) bytes have been read, check for blocks of nulls as chars intead of ints; otherwise, the files are zeroed. (But normally use int comparisons for speed.) * cp-aux.c (user_confirm_overwriting): Allow a single RET as a negative response. Flush prompt message in case stderr is line buffered. Use getchar instead of fgetc. * cp-hash.c: Include stdio.h instead of defining NULL. * cp.h: Don't declare fgetc (no longer used). * Makefile: Move BSD macro defs next to USG defs. Add COPYING to tar file, and add release version to tar file and directory names. Change LDLAGS typo to LDFLAGS. Tue Nov 14 09:43:43 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cat.c (main): Make options more flexible. Add: -E (+show-ends), which is like -e but doesn't imply -v; -T (+show-tabs), which is like -t but doesn't imply -v; -A (+show-all), which is like -ETv. Remove complaints about (needlessly) mutually exclusive options that were there for BSD compatibility, so options accumulate (which is more useful). (usage): Document new option syntax. (cat): Allow tabs to be shown as ^I even if other control and meta characters are not being quoted (so -T works). * Makefile: Add rules for v, vdir, d, and dir. Remove extra rule for cp that defines uid_t and gid_t; now handled in system.h. Remove README-cp from tar files; it was replaced by cp.texinfo. Change CPPFLAGS to LDFLAGS in default rule for making an executable from a .c file. Don't load getopt into dd. Mon Nov 13 10:08:35 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ls.c (get_link_name): Take code to prepend leading path back out; long output format needs the original link path. (make_link_path): New function to return the full path of a symlink. (gobble_file): Call make_link_path. Use the full path for non-long output formats, the original link path for long format. Sun Nov 12 15:42:43 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ls.c (gobble_file): Don't exchange the name of a symlink to a directory named on the command line with the name of the directory linked to. (`ls -d /usr/tmp' where /usr/tmp was a symlink to /var/tmp printed `/var/tmp' instead of `/usr/tmp'). (get_link_name): For symlinks that are relative paths, prepend the leading path from the link name. Fri Nov 10 18:13:52 1989 Torbjoern Granlund (tege at echnaton.sics.se) * cp.c: (copy) Don't modify the times for symlinks. The result would be to modify the file which the link points to. (do_copy) Use stat, never lstat, for the target, even if `-d' was specified. Otherwise, it would be forbidden to copy some files to a directory via a symlink pointing to the directory. Sat Nov 11 00:21:32 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * chmod.c (basename): strrchr => rindex. Thu Nov 9 11:46:51 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * rm.c (rm): Make check for symbolic link conditional on S_IFLNK being defined. Wed Nov 8 12:23:33 1989 David J. MacKenzie (djm at spiff) * ls.c: Rename +numerid-id option to +numeric-uid-gid. Thu Nov 9 00:13:03 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ls.c: Rename +directories-as-files option to +directory. Wed Nov 8 23:14:10 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * ls.c: Rename more long options and adjust usage message. Rename all-files to all. Rename almost-all-files to almost-all. Rename question-mark to hide-control-chars. Rename mark-file to classify. Rename file-size to size. Make file-type an alias for classify. Remove long name for -p. Tue Nov 7 21:04:04 1989 David J. MacKenzie (djm at spiff) * ls.c: Make +long a synonym for +verbose and +across an alias for +horizontal. Rename +follow-symbolic-links to +dereference, and +reverse-sort to +reverse. * cmp.c: Rename +byte-info to +verbose and make +quiet an alias for +silent. * du.c: Rename +combined-args to +combine. * head.c, tail.c: Make +silent an alias for +quiet. Sat Nov 4 00:34:00 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * cp.c: Reorder #includes so it will compile on systems that do special things in stdio.h and need it included early. * cp-aux.c (usage): Update message to reflect current options. * cp.h: Remove duplicate function declarations. * system.h: Define uid_t and gid_t, used by cp. Fri Nov 3 17:37:48 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * head.c, tail.c (main): Add long options using getopt_long and modify short option syntax for Posix conformance. Exit with status 1 if any system errors occurred. Global: Mention the operation that caused system errors in error messages. (nonfatal_perror, fatal_perror): Modify to take 2 args. (atou): New function for parsing arg to -n option. (usage): Describe new argument syntaxes. Tue Oct 31 02:03:32 1989 David J. MacKenzie (djm at spiff) * ls.c (decode_switches): Add long options, using getopt_long instead of getopt. (usage): New function to print usage message, mentioning long options. * tail.c (start_chars, start_lines, file_lines): Make return type int and return an error indicator to avoid duplicate error messages from `dump_remainder'. * cmp.c, du.c, mv.c, rm.c: Mention long options in usage messages. * ln.c: Change `+soft' option to `+symbolic'. (usage): Mention long option. * cat.c (main): Add long options. (usage): Mention long options. Mon Oct 30 03:02:47 1989 David J. MacKenzie (djm at spiff) * head.c, tail.c: Make an open or read error a nonfatal error instead of fatal by globally replacing function xread with read and checking the return value. Pass the input file name to most functions for error messages. * ln.c: Move variable soft_link outside of main so ln will compile. Mon Oct 23 12:19:40 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu) * chmod.c: Clarified some comments, indented file. (nonfatal_perror): Renamed from pnonfatal. (main): Initialize options (formerly done statically). (xchmod): Only call chmod if the mode will actually change. Wed Oct 18 22:51:29 1989 David J. MacKenzie (djm at spiff) * cmp.c, cp.c, du.c, ln.c, rm.c: Add terminating element to long_options. Make it static in ln.c. Tue Oct 17 11:53:14 1989 David MacKenzie (djm at wam.umd.edu) * ls.c: Added -z option to sort by file size. Indented file. (compare_size), (rev_cmp_size): New functions to sort by size. (compare_name), (rev_cmp_name): Removed 's' from end of the function names for consistency. (sort_type): Enumerated variable replaces int sort_by_time. (format_needs_stat): New variable for gobble_file. (decode_switches): Recognize -z option. (gobble_file): Replace long calculation with format_needs_stat. (sort_type): Use switch statement to choose sort function. (copystring): Removed unneeded code. Tue Oct 17 03:01:17 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * mv.c (main): Typo: had getopt, wants getopt_long. Make long_options static, and add terminating element. Accept EACCES as alternative to EACCESS. Sun Oct 15 20:06:33 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * mv.c (do_move): Distinguish lack of write bits from other reasons why `access' would fail for writing. Mon Sep 11 11:09:08 1989 David MacKenzie (djm at wam.umd.edu) * ls.c: Improved sort speed when -r is given. * (sort_files): For reverse sort, instead of calling reverse_files (), call the appropriate reverse compare function. * (reverse_files): Function removed. * (rev_cmp_ctime), (rev_cmp_mtime), (rev_cmp_atime), (rev_cmp_names): New functions that compare files in reverse order. Sat Sep 9 12:24:20 1989 David MacKenzie (djm at wam.umd.edu) * ls.c: (decode_switches): Get screen width from kernel if available. (gobble_file): Clear file[files_index].linkmode whether or not S_IFLNK is defined. (print_long_format): Remove code to set file type character for symbolic links; filemodestring does that. Fri Aug 11 19:46:19 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * tail.c: Added support for giving multiple filenames. Renamed -h `head of file' switch to -s `start of file', to avoid conflict with possible future -h `help' switch. Added -q `quiet' and -v `verbose' switches to control printing of filename banners. Let system.h handle USG adaptations. Removed all register declarations, as gcc can figure that out ok. Various functions: `number' is no longer negative if tailing from the end of the file. Changed variable `nread' to `chars_read'. (start_chars): Renamed from head_chars. (start_lines): Renamed from head_lines. (write_header): New function to print filename banner. (tail_file): New function to open a file and tail it, and maybe print a filename banner. (main): Moved file opening code to tail_file. Added `header_mode' variable and other support for -v and -q and multiple input files. (file_lines): Removed variable is_end_buffer and associated needless convolution. (pipe_lines): Renamed `struct buffer' to `struct linebuffer', and `BUFFER' to `LBUFFER'. Free all buffers before returning. (pipe_chars): Renamed `struct buffer' to `struct charbuffer', and `BUFFER' to `CBUFFER'. Free all buffers before returning. (start_lines): Renamed `i' to `chars_to_skip' and simplified inner loop. (usage): Udated to reflect new switches. Sun Aug 13 22:53:01 1989 Joy Kendall (jak at hobbes.ai.mit.edu) * ln.c, mv.c, cmp.c, cp.c, du.c, rm.c: Added long-named option capabilities by changing calls to "getopt" to be calls to "getopt_long", added option structures to each program. (didn't modify Makefile yet ...) Sat Aug 12 11:25:54 1989 Richard Stallman (rms at hobbes.ai.mit.edu) * ln.c: Delete #include of stat.h. System.h does it. Fri Aug 11 00:39:53 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ln.c: Major cleanup of comments and code. Moved function declarations to top of file. Added #ifdefs for systems without symlinks. (program): Renamed from `pgm'. (linkfunc): Pointer to function returning an int replaces int `dosoft'. (main): Renamed `ac' and `av' to `argc' and `argv'. Replaced argument parser with one that uses getopt. Moved usage message to usage function. Take base of program name. Renamed `where' to `to'. Exit with status 1 if any links failed. (do_link): Renamed from dolink. Removed unneeded `l' variable. Call `linkfunc' instead of checking `dosoft'. Changed error message. Renamed `cp' to `old_base'. (isdir): Renamed `fn' to `file' and `stb' to `stats'. (basename): New function to return base of given path. (usage): New function to print usage message and die. Fri Aug 11 19:46:19 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * head.c: Renamed -p `plain' switch to -q `quiet' (more standard meaning), added -v `verbose' switch. (main): Added variable `header_mode' and other support for -v. Moved the calling of write_header to head_file. (usage): Updated to mention -q and -v; added missing ellipsis. Fri Aug 11 19:46:19 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * system.h: Defined L_SET and L_XTND (used by lseek in tail). Thu Aug 10 20:03:27 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * head.c: Let system.h figure out the USG adaptations. Revise some comments. (write_header): Rename variable `first_time' to `first_file'. (head_lines): Simplify inner loop to remove redundant tests. Wed Aug 9 16:17:32 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * mv.c: Let system.h include sys/file.h if needed. Declare rindex at top of file. (main): Print usage message for unknown option. (usage): Clean up message. Tue Aug 1 11:42:20 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) * cat.c (cat): Added "ch = *bpin++;" to test case for swallow_empty_lines_after_empty_lines so that cat -s would work. I suspect that -s was never tested. Wed Jul 12 19:51:13 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * cat.c (cat): Move `#ifdef FIONREAD' to prevent an uninitialized variable `n_to_read' from being checked if FIONREAD undefined. Fri Jul 7 19:53:21 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * chmod.c: New program. Makefile: Added rules for chmod. GNUmakefile: Added chmod to list of executables. Tue Jun 27 14:55:04 1989 Roland McGrath (roland at hobbes.ai.mit.edu) * rm.c (rm): Don't confirm to remove symlinks for which access (, W_OK) fails. Thu Jun 22 17:18:05 1989 Roland McGrath (roland at hobbes.ai.mit.edu) * cp.c (copy): When copying directories recursively, recreate all file types. When not copying directories recursively, copy the contents of all files but directories. Wed Jun 21 22:46:46 1989 Roland McGrath (roland at hobbes.ai.mit.edu) * cp.c (copy): When not copying directories recursively, allow copying between mismatched file types. Sat Jun 10 00:19:11 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * ls.c: Don't include sys/file.h; done in system.h. * cp.c (copy): Shift S_IEXEC to bits for "others". S_WRITE also. * system.h [BSD]: sys/dir.h, not dir.h. Thu Jun 8 16:32:26 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * system.h: Include stat.h, so we can test S_IFLNK. * *.c: Don't include stat.h redundantly. Wed Jun 7 14:27:17 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Makefile: Don't use $^. * system.h (NLENGTH): New macro for length of name of dir entry. (DIRSIZ): Define this on systems that don't. (DIRECT): This flag is eliminated. * ls.c: Conditionalize all code using symlinks on S_IFLNK. Let system.h bring in sys/file.h. Reinstall new definition of nblocks. * du.c (durec): Use NLENGTH. * system.h [no S_IFLNK] : define lstat as stat. * cp.c (copy): Don't check for symlinks if there are none. (copy_dir): Use `struct direct'. * Makefile (clean): Fix typo. * dd.c: Include sys/types.h. * mv.c (do_move): Eliminate busted USG conditional on value of rename. * mv.c (do_move): Declare local `val'. Sun Jun 4 22:36:08 EDT 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c: Advanced version to 1.6. Automatically trace symbolic links to directories named on command line. Removed useless help message and -h switch. (struct file): Added `linkmode' field for printing the filetype of files that are symbolically linked to. (struct pending): Added `realname' field to allow listing of directories that are symbolically linked to under the name of the symbolic link. (help_message): Removed unhelpful help message. Might be reinstated later in a more useful form. (trace_links_to_dirs): Removed old variable controlled by -D. (main): Removed unneeded `thisdir' variable. Reorganized listing of pending directories. (decode_switches): Removed -h and -D, changed behavior of -L. Broke usage message into two strings to fit into code indentation. (queue_directory): Added `realname' argument for symbolic links to directories. Also added it to linked list. (print_dir): Added `realname' argument for symbolic links to directories. Use it for printing directory name if nonzero. (file_interesting): Renamed from file_interesting_p -- C's not Lisp. (gobble_file): Moved code that reads symbolic link name into get_link_name. Reorganized so it automatically traces symbolic links to directories. Renamed `concat' variable to `path'. Stat files that are symbolically linked to for later filetype indicator in long listing format. (get_link_name): Created a new function with some code taken from gobble_file. (extract_dirs_from_files): Renamed `concat' variable to `path'. Pass linkname to queue_directory. Use malloc and free in loop instead of alloca to avoid wasting lots of stack space. (print_long_format): Renamed `mbuf' to `modebuf' and `tbuf' to `timebuf'. Print filetype indicator on name of file symbolically linked to rather than on the link. This is what the SunOS 4.0 ls does. (print_type_indicator): Changed argument type from `struct file *' to `unsigned int' so it can be used to print the mode of files that are symbolically linked to. (getgroup): Changed argument name from `uid' to `gid'. Changed name of `usernum_string' variable to `groupnum_string'. (xrealloc): Added "virtual " to error message. (xmalloc): Added "virtual " to error message. (perror_with_name): Removed unneeded `err' variable. Tue May 30 18:24:12 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * Makefile (rm, du): Add linking rules to use $(LIBS). Mon May 29 22:03:19 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * ln.c (dolink): Make arg of perror a null string. * mv.c (rename): If USG2, define using link and unlink. * cp.h: Include system.h. Don't declare alloca. * cp.c: Use ST_BLKSIZE. Cast all calls to alloca. (main) [USG]: Alternate code for getting group id. (copy): Don't use S_IFSOCK if not defined. Alternate code for setting file's time. (copy_reg): Don't use ftruncate on USG. * system.h: Define macros for system-independence. Define alloca. * du.c: Include system.h. Don't declare alloca, cast its calls. (durec): Compensate on USG for lack of st_blksize. * cat.c: Include system.h. Use new macro ST_BLKSIZE. (cat): Error if need FIONREAD and don't have it. * cmp.c, dd.c, ls.c, rm.c: Use system.h. Sat May 27 01:06:51 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * cat.c: Clean up assignments nested within other things. Thu May 25 18:54:34 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * cmp.c (cmp): Use POSIX standard for no-options output format. Tue May 23 12:13:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) * mv.c (main), ln.c (main): Added exit(0)'s at the end of each to guarantee sucessful (for shell scripts). Thu May 11 16:42:51 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) * tail.c (file_lines): Output the entire file if the file fits in a single buffer and contains EXACTLY the number of lines asked for. Fri May 5 18:17:46 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * cmp.c (cmp): Output discrepancies to stdout, not stderr. Wed May 3 10:43:15 1989 Jay Fenlason (hack at spiff) * du.c, ln.c, ls.c, mv.c: added #ifdef sparc for alloca stuff. * Makefile: added 'clean', 'dist' and 'getopt.o' entries. Thu Mar 2 1989 23:31:27 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c: Moved some non-option variable definitions from amidst the option variables to before them. Added -k and -I options. Advanced version to 1.4. (nblocks): Adjust number of blocks if -k given. (indicator_style): An enum, replacing the int print_filetype. (kilobyte_blocks): Added variable to show block sizes as 1024-bytes instead of 512-bytes. (ignore_patterns): linked list replaces the int ignore_backup_files with a more general mechanism. (main): Moved the clearing of dir_defaulted out of a loop. (decode_options): Added support for -k and -I and reimplemented -B in terms of -I (calls to add_ignore_pattern). (add_ignore_pattern): New function to add an element to the list of patterns for files to ignore. (file_interesting_p): Replaced check for backup file suffix with calls to glob_match on the files in ignore_patterns. ls -aB now doesn't display files that match `.*~'. Mon Feb 20 1989 01:23:52 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c: Modified -C format. Advanced version to 1.3. (version_string): Moved comments up to top of file and version string into decode_switches (). (program_name): Added variable to contain base of name program was actually run with (could be one of `ls, dir, d, vdir, v'). (main): Set program_name. Don't check for null arguments, as decode_switches no longer creates them (and hasn't for awhile). (decode_switches): Don't decrement line_length by 1. Print actual program name in version message and exit afterward. Check -w argument for valid values. (print_many_per_line): Removed variables longcols and start_shortcol and renamed lineno to row and nlines to rows. Rewrote algorithm to more closely mimic Unix ls. (print_horizontal): Renamed perline to cols and added error checks on line length. (error): Print actual program name in error message. (basename): New function to return name stripped of leading path. Mon Feb 13 22:50:45 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * chmod.c: Added -c option. Advanced version to 1.2. (changes_only): Added new variable to determine whether to verbosely show all new modes or only those that actually change. Set by -c. (xchmod): If changes_only is true, check that the new mode is different from the old mode before describing the change. Sun Feb 12 08:41:20 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * cat.c: Don't include strings.h. (itoa): Deleted. (error): Take error code as extra arg; all calls changed. Use fprintf, not itoa. (main): No error for repeated option. Sat Feb 11 03:55:44 1989 Torbjorn Granlund (rms at sugar-bombs.ai.mit.edu) * cat.c (main): Fix how ARGBAD elts are set. Leave one extra byte at end of ARGBAD. Close the input descriptors, and report errors on close. Fri Feb 10 05:15:17 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * du.c: Change dirent.h and `struct dirent' to sys/dir.h and struct direct. (struct htab): Make `hash' field length 1, not 0. (hash_insert): Check realloc even if it's to make the block smaller. Wed Feb 8 07:52:39 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * chmod.mackenzie (main): Added modeind variable to allow options to be specified after the mode. Wed Feb 8 07:52:39 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c (attach): New fn to concat dirname and filename cleanly. Called in various places. (main): Set EXPLICIT_ARG when calling gobble_file for `.'. * dd.c: Moved function decl's to top of file. Removed commas after last elements in tables. (scanargs): changed "bad arg: %s" messages to GNU-getopt-like "unrecognized option `%s'". (parse_conversion): changed "bad conversion" message to "invalid conversion". (usage): Added function to print usage message. Sat Feb 4 00:44:45 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c (enum filetype): Added arg_directory. (gobble_file): Give command line dir args type arg_directory unless -d selected. (extract_dirs_from_files): Only delete command line dir args from listing, for Unix compatibility. (print_many_per_line): Renamed variables: i to filesno, i1 to lineno, max to max_name_length, tem to name_length. (print_horizontal): Renamed variables: i to filesno, max to max_name_length, tem to name_length. (print_with_commas): Renamed variables: i to filesno, col to pos, ocol to oldpos. Fri Feb 3 23:06:33 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * tail.c: moved function decl's to separate lines. moved function def'n types to their own lines. advanced version to 2.2. Fri Feb 3 23:02:40 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * head.c: moved function decl's to one per line. put function def'n types on their own lines. (main): changed to version 1.1. (write_header): changed "<<==" to "<==". Fri Feb 3 01:32:39 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * ls.c (decode_switches): Default -b for `dir' and `vdir'. -q turns off -b. New option -N turns off -q and -b. * ls.c (print_name_with_quoting): Handle -q flag; don't print ctl chars as self unless -q. (decode_switches): Set qmark_funny_chars according to -q. Default it to 1 if output to tty. Thu Feb 2 23:38:13 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c (getgroup): changed user_alist to group_alist. Wed Feb 1 01:04:22 EST 1989 David MacKenzie (edf at rocky2.rockefeller.edu) * ls.c (print_dir_name): print directory name if 1. (main): added code to set print_dir_name, then clear it if exactly one dir arg is given or if default dir is used, then set it after first dir is listed. (print_dir): removed code that sets dir_defaulted and changed the variable that controls whether dir names are printed from dir_defaulted to print_dir_name. (decode_switches): redirected version message to stderr. Sun Jan 29 00:34:42 1989 David MacKenzie (rms at sugar-bombs.ai.mit.edu) * ls.c: More include files. Forward-declare more fcns at start. (nblock, nlength): Macros to extract file size from `stat' and file name length from dir entry. Called where appropriate. (numeric_users): 1 if -n spec'd. (help_message): help_msg renamed. (main): Print blank line between spec'd files and spec'd dirs. Variable `dir_defaulted' always set to 1 if no files spec'd. Exit instead of returning. (decode_switches): Use `isatty', not `TIOCGETP'. Initialize numeric_users. Recognize `n' option. `V' is alias for `v'. Print help for unrecognized option. (print_dir): Don't copystring the file names. (clear_files): Don't free linkname if null (gobble_file): copystring the name. Use a switch to decode the type of file. (extract_dirs_from_files): precompute length of DIRNAME. Other minor rearrangements. (print_type_indicator): fn split out from print_file_name_and_frills. (print_long_format): Rename `time' to `when'. Print userid and group numerically if `-n'. Call print_type_indicator if -F specified. Wed Jan 25 21:16:43 1989 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * du.c (durec, main): fflush if anything is printed. Fri Dec 16 13:54:03 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu) * tail.c: New version from David MacKenzie uses getopt for new option syntax, but also supports the old one. Other cleanups too. Unk May 0 ........ 1988 Roland McGrath (roland at wheaties.ai.mit.edu) * ls.c: Implement -g. -B -A does not display .*~. Other cleanups. Unk Jan 0 ........ 1988 Jay Fenlason (hack at wheaties.ai.mit.edu) * ls.c: Make ls -Ra not loop in ././././... Local Variables: mode: indented-text left-margin: 8 version-control: never End: n (rms at sugar-bombs.ai.mit.edu) * fileutils/Makefile 644 473 0 7717 4702312176 7541 # Makefile for fileutils # Copyright (C) 1986, 1988, 1989 Free Software Foundation, Inc. # 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. SHELL = /bin/sh CC = gcc # For System V, add -DUSG to CFLAGS, and -DBLKSIZE=1024 # (or some other number which is the optimal blocksize for filesys i/o ops). # On V.3, add -DUSGr3 as well (this selects dirent.h instead of ndir.h). # Add -DSYSNDIR if your system uses sys/ndir.h instead of ndir.h. # On HP/UX, add -DDIRSIZ_MACRO (for ndir.h). # Add -DNEED_MKDIR if your system lacks the mkdir and rmdir system calls. # Add -DVPRINTF to use vfprintf instead of _doprnt (which seems to be # broken on the sparc). # Use cp -f for INSTALL if you don't have install. # Add -lndir to LIBS if you use libndir.a. CFLAGS = -O -g -DUSG -DSYSNDIR -DNEED_MKDIR -DBLKSIZE=1024 LDFLAGS = -g LIBOBJS = getopt.o getopt1.o LIBS = $(LIBOBJS) -lx INSTALL = install -c -s # Where to install the executables BIN = /usr/local/bin/gnu # Executable files in this directory PROGS = cat chmod cmp cp dd dir du head ln ls mkdir mv rm rmdir tail vdir # Version of fileutils release VERSION = 1.0 all: $(PROGS) .PHONY: all $(PROGS): $(LIBOBJS) # Linking rules cat: cat.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ cat.c $(LIBS) chmod: chmod.o modechange.o filemode.o $(CC) $(LDFLAGS) -o $@ chmod.o modechange.o filemode.o $(LIBS) cmp: cmp.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ cmp.c $(LIBS) cp: cp.o cp-hash.o cp-aux.o dirlib.o $(CC) $(LDFLAGS) -o $@ cp.o cp-hash.o cp-aux.o dirlib.o $(LIBS) dd: dd.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ dd.c dir: dir.o filemode.o glob.o $(CC) $(LDFLAGS) -o $@ dir.o filemode.o glob.o $(LIBS) du: du.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ du.c $(LIBS) head: head.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ head.c $(LIBS) ln: ln.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ ln.c $(LIBS) ls: ls.o filemode.o glob.o $(CC) $(LDFLAGS) -o $@ ls.o filemode.o glob.o $(LIBS) mkdir: mkdir.o modechange.o dirlib.o $(CC) $(LDFLAGS) -o $@ mkdir.o modechange.o dirlib.o $(LIBS) mv: mv.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ mv.c $(LIBS) rm: rm.o dirlib.o $(CC) $(LDFLAGS) -o $@ rm.o dirlib.o $(LIBS) rmdir: rmdir.o dirlib.o $(CC) $(LDFLAGS) -o $@ rmdir.o dirlib.o $(LIBS) tail: tail.c $(CC) $(LDFLAGS) $(CFLAGS) -o $@ tail.c $(LIBS) vdir: vdir.o filemode.o glob.o $(CC) $(LDFLAGS) -o $@ vdir.o filemode.o glob.o $(LIBS) # Compilation rules chmod.o mkdir.o modechange.o: modechange.h cp.o cp-hash.o cp-aux.o: cp.h # C compilers that can't handle -c and -o together need the following: #dir.o: ls.c # $(CC) $(CFLAGS) -DMULTI_COL -c ls.c # mv ls.o dir.o # #vdir.o: ls.c # $(CC) $(CFLAGS) -DLONG_FORMAT -c ls.c # mv ls.o vdir.o dir.o: ls.c $(CC) $(CFLAGS) -DMULTI_COL -o dir.o -c ls.c vdir.o: ls.c $(CC) $(CFLAGS) -DLONG_FORMAT -o vdir.o -c ls.c clean: /bin/rm -f $(PROGS) *.o install: all for i in $(PROGS); do $(INSTALL) $$i $(BIN)$$i; done # ln -f $(BIN)/dir $(BIN)/d; ln -f $(BIN)/vdir $(BIN)/v dist: rm -rf fileutils-$(VERSION) mkdir fileutils-$(VERSION) ln COPYING ChangeLog Makefile README cat.c chmod.c cmp.c cp-aux.c \ cp-hash.c cp.c cp.h dd.c dirlib.c du.c filemode.c getopt.c getopt1.c \ glob.c head.c ln.c ls.c mkdir.c modechange.c modechange.h \ mv.c rm.c rmdir.c tail.c getopt.h system.h \ fileutils-$(VERSION) tar cohf fileutils-$(VERSION).tar fileutils-$(VERSION) compress < fileutils-$(VERSION).tar > fileutils-$(VERSION).tar.Z rm -rf fileutils-$(VERSION) ln -f $(BIN)/vdir $(BIN)/v dist: rm -rf fileutifileutils/README 644 473 0 4131 4702305041 6735 This is the GNU file manipulation utilities package. Most of these programs have significant advantages over their Unix counterparts, such as greater speed or additional flexibility. The documentation for these programs is not finished yet; it will be added in a later release. Most of them accept descriptive long-named options in addition to Unix-style short-named options, which should help you figure out their additional features. Giving a `-h' option (or any invalid option) will produce a usage message. Some of the programs also have explanatory comments at the top of the source code. The fileutils are intended to be POSIX compliant, like the rest of the GNU system. They were mostly written on BSD Unix and later ported to System V. Known bugs: * mv cannot rename directories on systems that lack the rename system call, except when run by root. I think the System V mv runs a setuid root program called /etc/mvdir to do this. No one has bothered to hack that into the rename emulator in GNU mv. * When copying sparse files that end in blocks of nulls, cp truncates the trailing nulls on systems that lack the ftruncate system call. ftruncate emulations have been written using the fcntl and chsize system calls on System V release 3 and Xenix. * On some versions of System V, du sometimes reports that it cannot stat the last few entries in some directories. This is probably due to bugs in those systems' portable directory libraries. * tail has no -r option (print backwards). Printing the entire file, as the Unix manual says, could run out of memory when tailing a pipe. Reversing a file is really a different job from printing the end of a file; the BSD tail can get away with kludging it in because of its limited size buffer. A more versatile way than tail -r to reverse files is the `tac' command which comes in the 4.3BSD user-contributed code distribution. An enhanced GNU version of `tac' might be included in a future distribution of the fileutils. Bug reports and discussion about these programs should go to bug-gnu-utils@prep.ai.mit.edu. David MacKenzie eally a different job from printing the end of a file; the BSD tail can get away with kludging it in because of its limited size buffer. A more versatile way than tail -r to reverse files is the `tac' command which comes in the 4.3BSD user-contributed code distribution. An enhanced GNU version of `tac' might be included in a future distribution of the fileutils. Bug reports and discussion about these programs should fileutils/cat.c 644 473 0 40046 4702305041 7015 /* cat -- concatenate files and print on the standard output. Copyright (C) 1988 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Differences from the Unix cat: * Always unbuffered, -u is ignored. * 100 times faster with -v -u. * 20 times faster with -v. * Warnings about nonexisting files are delayed until everything has been written out. This is nice if output is to a terminal. By tege@nada.kth.se, Torbjorn Granlund, advised by rms, Richard Stallman. */ #include #include #include #include "system.h" #include #include #include "getopt.h" extern char *malloc (); extern int free (); extern int errno; extern char *sys_errlist[]; extern int sys_nerr; extern char *copystring (); #define max(h,i) ((h) > (i) ? (h) : (i)) typedef unsigned char uchar; void simple_cat (); void cat (); void next_line_num (); /* Name under which this program was invoked. */ char *prog_name; /* Name of input file. May be "-". */ char *infile; /* Name of output file. Always is "-". Ready for more options... */ char *outfile; /* Descriptor on which input file is open. */ int input_desc; /* Descriptor on which output file is open. Always is 1. */ int output_desc; /* Buffer for line numbers. */ char line_buf[13] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','0','\t','\0'}; /* Position in `line_buf' where printing starts. This will not change unless the number of lines are more than 999999. */ char *line_num_print = line_buf + 5; /* Position of the first digit in `line_buf'. */ char *line_num_start = line_buf + 10; /* Position of the last digit in `line_buf'. */ char *line_num_end = line_buf + 10; /* Preserves the `cat' function's local `newlines' between invocations. */ int newlines2 = 0; void usage (reason) char *reason; { if (reason != NULL) fprintf (stderr, "%s: %s\n", prog_name, reason); fprintf (stderr, "\ Usage: %s [-unbsvetETA] [+number] [+number-nonblank] [+squeeze-blank]\n\ [+show-nonprinting] [+show-ends] [+show-tabs] [+show-all] [file...]\n", prog_name); exit (2); } /* Error handler. ERR is a short description of what operation failed, CODE is the error code, IN is the argument to the failed command or NULL. RET is the exit status, or 0 if the error wasn't fatal. */ void error (err, code, in, ret) char *err; int code; char *in; int ret; { fputs (prog_name, stderr); fputs (": ", stderr); if (in != NULL) { fputs (in, stderr); fputs (": ", stderr); } fputs (err, stderr); if (code > 0) { if (code < sys_nerr) { fputs (", ", stderr); fputs (sys_errlist[code], stderr); } else fprintf (stderr, ", error code %d", code); } fputs ("\n", stderr); if (ret) exit (ret); } void main (argc, argv) int argc; char *argv[]; { /* Optimal size of i/o operations of output. */ int outsize; /* Optimal size of i/o operations of input. */ int insize; /* Pointer to the input buffer. */ uchar *inbuf; /* Pointer to the output buffer. */ uchar *outbuf; extern int optind; int c; /* Index in argv to processed argument. */ int argind; /* Array of boolean values telling whether an input file exists and is readable. */ char *argbad; /* Device number of the output (file or whatever). */ int out_dev; /* I-node number of the output. */ int out_ino; struct stat stat_buf; /* Count of non-fatal error conditions. */ int exit_stat = 0; /* Variables that are set according to the specified options. */ int numbers = 0; int numbers_at_empty_lines = 1; int squeeze_empty_lines = 0; int mark_line_ends = 0; int quote = 0; int output_tabs = 1; int options = 0; int longind; static struct option long_options[] = { {"number", 0, NULL, 'n'}, {"number-nonblank", 0, NULL, 'b'}, {"squeeze-blank", 0, NULL, 's'}, {"show-nonprinting", 0, NULL, 'v'}, {"show-ends", 0, NULL, 'E'}, {"show-tabs", 0, NULL, 'T'}, {"show-all", 0, NULL, 'A'}, {NULL, 0, NULL, 0} }; prog_name = argv[0]; /* Parse command line options. */ while ((c = getopt_long (argc, argv, "unbsvetETA", long_options, &longind)) != EOF) { options++; if (c == 0) c = long_options[longind].val; switch (c) { case 'u': /* We provide the -u feature unconditionally. */ options--; break; case 'n': numbers = 1; break; case 'b': numbers = 1; numbers_at_empty_lines = 0; break; case 's': squeeze_empty_lines = 1; break; case 'v': quote = 1; break; case 'e': mark_line_ends = 1; quote = 1; break; case 't': output_tabs = 0; quote = 1; break; case 'E': mark_line_ends = 1; break; case 'T': output_tabs = 0; break; case 'A': quote = 1; mark_line_ends = 1; output_tabs = 0; break; default: usage ((char *)0); } } output_desc = fileno (stdout); outfile = "-"; /* Get device, i-node number, and optimal blocksize of output. */ if (fstat (output_desc, &stat_buf) < 0) error ("cannot stat", errno, outfile, 1); outsize = ST_BLKSIZE (stat_buf); out_dev = stat_buf.st_dev; out_ino = stat_buf.st_ino; /* Check if any of the input files are the same as the output file. */ argbad = (char *) alloca (argc + 1); infile = "-"; /* Main loop. */ argind = optind; do { argbad[argind] = 0; if (argind < argc) infile = argv[argind]; if (infile[0] == '-' && infile[1] == 0) input_desc = fileno (stdin); else { /* Open a file for input. If the file cannot be opened, remember the error number in argbad. */ input_desc = open (infile, O_RDONLY); if (input_desc < 0) { argbad[argind] = errno; exit_stat++; continue; } } if (fstat (input_desc, &stat_buf) < 0) error ("cannot stat", errno, infile, 1); insize = ST_BLKSIZE (stat_buf); /* If the input isn't the standard input, compare the device and i-node numbers of this input file with the corresponding values of the (output file associated with) stdout, and skip this input file if they coincide. Input files cannot be redirected to themselves. A warning will be printed after all files has been processed. This condition is remembered in argbad. */ if (input_desc != 0 && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino) { argbad[argind] = -1; exit_stat++; continue; } /* Select which version of `cat' to use. If any options (more than -u) were specified, use `cat', otherwise use `simple_cat'. */ if (options == 0) { insize = max (insize, outsize); inbuf = (uchar *)malloc (insize); if (inbuf == NULL) error ("virtual memory exhausted", errno, (char *)0, 1); simple_cat (inbuf, insize); } else { inbuf = (uchar *)malloc (insize + 1); if (inbuf == NULL) error ("virtual memory exhausted", errno, (char *)0, 1); /* Why are (OUTSIZE - 1 + INSIZE * 4 + 13) bytes allocated for the output buffer? A test whether output needs to be written is done when the input buffer empties or when a newline appears in the input. After output is written, at most (OUTSIZE - 1) bytes will remain in the buffer. Now INSIZE bytes of input is read. Each input character may grow by a factor of 4 (by the prepension of M-^). If all characters do, and no newlines appear in this block of input, we will have at most (OUTSIZE - 1 + INSIZE) bytes in the buffer. If the last character in the preceeding block of input was a newline, a line number may be written (according to the given options) as the first thing in the output buffer. (Done after the new input is read, but before processing of the input begins.) A line number requires seldom more than 13 positions. */ outbuf = (uchar *)malloc (outsize - 1 + insize * 4 + 13); if (outbuf == NULL) error ("virtual memory exhausted", errno, (char *)0, 1); cat (inbuf, insize, outbuf, outsize, quote, output_tabs, numbers, numbers_at_empty_lines, mark_line_ends, squeeze_empty_lines); free (outbuf); } free (inbuf); if (input_desc != 0 && close (input_desc) < 0) error ("cannot close", errno, infile, 1); } while (++argind < argc); /* Print delayed warnings. */ for (argind = optind; argind < argc; argind++) if (argbad[argind]) { if (argbad[argind] < 0) error ("input file is output file", 0, argv[argind], 0); else error ("cannot open", argbad[argind], argv[argind], 0); } exit (exit_stat != 0); } /* Plain cat. Copies the file behind `input_desc' to the file behind `output_desc'. */ void simple_cat (buf, bufsize) /* Pointer to the buffer, used by reads and writes. */ uchar *buf; /* Number of characters preferably read or written by each read and write call. */ int bufsize; { /* Actual number of characters read, and therefore written. */ int n_read; /* Loop until the end of the file. */ for (;;) { /* Read a block of input. */ n_read = read (input_desc, buf, bufsize); if (n_read < 0) error ("cannot read", errno, infile, 1); /* End of this file? */ if (n_read == 0) break; /* Write this block out. */ if (write (output_desc, buf, n_read) != n_read) error ("cannot write", errno, outfile, 1); } } /* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC. Called if any option more than -u was specified. A newline character is always put at the end of the buffer, to make an explicit test for buffer end unnecessary. */ void cat (inbuf, insize, outbuf, outsize, quote, output_tabs, numbers, numbers_at_empty_lines, mark_line_ends, squeeze_empty_lines) /* Pointer to the beginning of the input buffer. */ uchar *inbuf; /* Number of characters read in each read call. */ int insize; /* Pointer to the beginning of the output buffer. */ uchar *outbuf; /* Number of characters written by each write call. */ int outsize; /* Variables that have values according to the specified options. */ int quote; int output_tabs; int numbers; int numbers_at_empty_lines; int mark_line_ends; int squeeze_empty_lines; { /* Last character read from the input buffer. */ uchar ch; /* Pointer to the next character in the input buffer. */ uchar *bpin; /* Pointer to the first non-valid byte in the input buffer, i.e. the current end of the buffer. */ uchar *eob; /* Pointer to the position where the next character shall be written. */ uchar *bpout; /* Number of characters read by the last read call. */ int n_read; /* The variable NEWLINES determines how many consequtive newlines there have been in the input. 0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1, etc. Initially 0 to indicate that we are at the beginning of a new line. The "state" of the procedure is determined by NEWLINES. */ int newlines = newlines2; /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input is read immediately. */ eob = inbuf; bpin = eob + 1; bpout = outbuf; for (;;) { do { /* Write if there are at least OUTSIZE bytes in OUTBUF. */ if (bpout - outbuf >= outsize) { uchar *wp = outbuf; do { if (write (output_desc, wp, outsize) != outsize) error ("cannot write", errno, outfile, 1); wp += outsize; } while (bpout - wp >= outsize); /* Move the remaining bytes to the beginning of the buffer. */ bcopy (wp, outbuf, bpout - wp); bpout = outbuf + (bpout - wp); } /* Is INBUF empty? */ if (bpin > eob) { #ifdef FIONREAD int n_to_read; /* Is there any input to read immediately? If not, we are about to wait, so write all buffered output before waiting. */ if (ioctl (input_desc, FIONREAD, &n_to_read) < 0) error ("cannot do ioctl", errno, infile, 1); if (n_to_read == 0) #endif { int n_write = bpout - outbuf; if (write (output_desc, outbuf, n_write) != n_write) error ("cannot write", errno, outfile, 1); bpout = outbuf; } /* Read more input into INBUF. */ n_read = read (input_desc, inbuf, insize); if (n_read < 0) error ("cannot read", errno, infile, 1); if (n_read == 0) { newlines2 = newlines; return; } /* Update the pointers and insert a sentinel at the buffer end. */ bpin = inbuf; eob = bpin + n_read; *eob = '\n'; } else { /* It was a real (not a sentinel) newline. */ /* Was the last line empty? (i.e. have two or more consecutive newlines been read?) */ if (++newlines > 0) { /* Are multiple adjacent empty lines to be substituted by single ditto (-s), and this was the second empty line? */ if (squeeze_empty_lines && newlines >= 2) { ch= *bpin++; continue; } /* Are line numbers to be written at empty lines (-n)? */ if (numbers && numbers_at_empty_lines) { next_line_num (); bpout = (uchar *)copystring (bpout, line_num_print); } } /* Output a currency symbol if requested (-e). */ if (mark_line_ends) *bpout++ = '$'; /* Output the newline. */ *bpout++ = '\n'; } ch = *bpin++; } while (ch == '\n'); /* Are we at the beginning of a line, and line numbers are requested? */ if (newlines >= 0 && numbers) { next_line_num (); bpout = (uchar *)copystring (bpout, line_num_print); } /* Here CH cannot contain a newline character. */ /* The loops below continue until a newline character is found, which means that the buffer is empty or that a proper newline has been found. */ /* If quoting, i.e. at least one of -v, -e, or -t specified, scan for chars that need conversion. */ if (quote) for (;;) { if (ch >= 32) { if (ch < 127) *bpout++ = ch; else if (ch == 127) *bpout++ = '^', *bpout++ = '?'; else { *bpout++ = 'M', *bpout++ = '-'; if (ch >= 128 + 32) if (ch < 128 + 127) *bpout++ = ch - 128; else *bpout++ = '^', *bpout++ = '?'; else *bpout++ = '^', *bpout++ = ch - 128 + 64; } } else if (ch == '\t' && output_tabs) *bpout++ = '\t'; else if (ch == '\n') { newlines = -1; break; } else *bpout++ = '^', *bpout++ = ch + 64; ch = *bpin++; } else /* Not quoting, neither of -v, -e, or -t specified. */ for (;;) { if (ch == '\t' && !output_tabs) *bpout++ = '^', *bpout++ = ch + 64; else if (ch != '\n') *bpout++ = ch; else { newlines = -1; break; } ch = *bpin++; } } } /* Compute the next line number. */ void next_line_num () { char *endp = line_num_end; do { if ((*endp)++ < '9') return; *endp-- = '0'; } while (endp >= line_num_start); *--line_num_start = '1'; if (line_num_start < line_num_print) line_num_print--; } /* A less stupid strcpy. Returns a pointer to the end of the destination string, instead of to the beginning. Doesn't null-terminate the destination. */ char * copystring (dst, src) char *dst; char *src; { char c; while (c = *src++) *dst++ = c; return dst; } *endp = line_num_end; do { if ((*endp)++ < '9') return; *endp-- = '0'; } while (endp >= line_num_start); *--line_num_start = '1'; if (line_num_start < line_num_print) line_num_print--; } /* A less stupid strcpy. Returns a pointer to the end of the destination string, instead of to the beginning. Doesn't null-terminate the destination. */ char * copystring (dst, src) char *dst; char *src; { char c; while (c = *src+fileutils/chmod.c 644 473 0 17371 4702305041 7345 /* chmod -- change permission modes of files Copyright (C) 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Usage: chmod [-Rcdfv] mode file... mode is [ugoa...][[+-=][rwxXstugo]...][,...] or octal number. Options: -R Recursively change modes of directory contents. -c Verbosely describe only files whose modes actually change. -d Dereference symbolic links (recursively change the modes of directories pointed to by symbolic links). -f Force error messages off. -v Verbosely describe changed modes. David MacKenzie */ #include #include #include "modechange.h" #include "system.h" char *malloc (); char *strcat (); char *strcpy (); char *rindex (); int lstat (); int stat (); char *basename (); char *stp_cpy (); char *xmalloc (); void change_file_mode (); void change_dir_mode (); void describe_change (); void filemodestring (); void invalid_mode (); void memory_out (); void nonfatal_perror (); void usage (); typedef enum { false = 0, true = 1 } boolean; /* The base of the name the program was run with. */ char *program_name; /* If true, chmod directories recursively. */ boolean recurse; /* If true, force silence (no error messages). */ boolean force_silent; /* If true, describe the modes we set. */ boolean verbose; /* If true, describe only modes that change. */ boolean changes_only; /* The exit status. */ int errors; /* A pointer to either lstat or stat, depending on whether dereferencing of symlinks is done. */ int (*xstat) (); /* Parse the ASCII mode given on the command line into a linked list of `struct modechange', and apply that to each file argument. */ int main (argc, argv) int argc; char **argv; { extern int optind; struct modechange *changes; int modeind = 0; /* Index of the mode argument in `argv'. */ int c; program_name = argv[0] = basename (argv[0]); recurse = force_silent = verbose = changes_only = false; errors = 0; xstat = lstat; while ((c = getopt (argc, argv, "RcdfvrwxXstugo")) != EOF) { switch (c) { case 'r': case 'w': case 'x': case 'X': case 's': case 't': case 'u': case 'g': case 'o': if (modeind != 0 && modeind != optind - 1) invalid_mode (); modeind = optind - 1; break; case 'R': recurse = true; break; case 'c': verbose = true; changes_only = true; break; case 'd': xstat = stat; break; case 'f': force_silent = true; break; case 'v': verbose = true; break; default: usage (); } } if (modeind == 0) modeind = optind++; if (optind >= argc) usage (); changes = compile_mode (argv[modeind]); if (changes == MODE_INVALID) invalid_mode (); else if (changes == MODE_MEMORY_EXHAUSTED) memory_out (); for (; optind < argc; ++optind) change_file_mode (argv[optind], changes); exit (errors); } /* Change the mode of `file' according to the list of operations `changes'. */ void change_file_mode (file, changes) char *file; struct modechange *changes; { struct stat file_stats; unsigned short newmode; if ((*xstat) (file, &file_stats) == -1) { nonfatal_perror ("cannot stat", file); return; } newmode = adjust_mode (file_stats.st_mode, changes); if (newmode != (file_stats.st_mode & 07777)) { if (verbose) describe_change (file, newmode, 1); if (chmod (file, (int) newmode) == -1) nonfatal_perror ("cannot change mode of", file); } else if (verbose && changes_only == false) describe_change (file, newmode, 0); if (recurse && (file_stats.st_mode & S_IFMT) == S_IFDIR) change_dir_mode (file, changes, &file_stats); } /* Recursively change the modes of the files in directory `dir' according to the list of operations `changes'. `statp' points to the results of stat on `dir'. */ void change_dir_mode (dir, changes, statp) char *dir; struct modechange *changes; struct stat *statp; { DIR *dirp; struct direct *dp; char *name_space, *file_name; char *path; unsigned dirlength; unsigned pathlength; dirp = opendir (dir); if (dirp == NULL) { nonfatal_perror ("cannot read directory", dir); return; } name_space = (char *) alloca (statp->st_size); file_name = name_space; while ((dp = readdir (dirp)) != NULL) { /* Skip "." and ".." (some NFS filesystems' directories lack them). */ if (dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) file_name = stp_cpy (file_name, dp->d_name) + 1; } *file_name = '\0'; closedir (dirp); /* Now process the files in the directory. */ dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */ pathlength = dirlength; /* Give `path' a dummy value; it will be reallocated before first use. */ path = xmalloc (pathlength); for (file_name = name_space; *file_name; file_name += strlen (file_name) + 1) { if (dirlength + strlen (file_name) + 1 > pathlength) { /* Do not bother copying the old filename with realloc. */ free (path); pathlength *= 2; path = xmalloc (pathlength); strcpy (path, dir); path[dirlength - 1] = '/'; } strcpy (path + dirlength, file_name); change_file_mode (path, changes); } free (path); } /* Tell the user the mode `mode' that file `file' has been set to; if `changed' is zero, `file' had that mode already. */ void describe_change (file, mode, changed) char *file; unsigned short mode; int changed; { struct stat perm_stats; /* For filemodestring. */ char perms[11]; /* "-rwxrwxrwx" ls-style modes. */ perm_stats.st_mode = mode; filemodestring (&perm_stats, perms); perms[10] = '\0'; /* `filemodestring' does not null terminate. */ if (changed) printf ("Mode of %s changed to 0%04o (%s)\n", file, mode & 07777, &perms[1]); else printf ("Mode of %s retained as 0%04o (%s)\n", file, mode & 07777, &perms[1]); } void memory_out () { fprintf (stderr, "%s: virtual memory exhausted\n", program_name); exit (1); } /* Allocate `n' bytes of memory dynamically, with error checking. */ char * xmalloc (n) unsigned n; { char *p; p = malloc (n); if (p == 0) memory_out (); return p; } void nonfatal_perror (message, filename) char *message; char *filename; { if (force_silent == false) { fprintf (stderr, "%s: %s ", program_name, message); perror (filename); } errors = 1; } void invalid_mode () { fprintf (stderr, "%s: invalid mode\n", program_name); exit (1); } /* Copy `source' into `dest' and return dest + strlen (source), i.e., a pointer to the null at the end of `dest'. */ char * stp_cpy (dest, source) char *dest; char *source; { while ((*dest++ = *source++) != 0) /* Do nothing. */ ; return dest - 1; } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-Rcdfv] mode file...\n\ mode is [ugoa...][[+-=][rwxXstugo]...][,...] or octal number\n", program_name); exit (1); } / char * stp_cpy (dest, source) char *dest; char *source; { while ((*dest++ = *source++) != 0) /* Do nothing. */ ; return dest - 1; } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *bfileutils/cmp.c 644 473 0 21605 4702305042 7026 /* cmp -- compare two files. Copyright (C) 1988 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Differences from the Unix cmp: * 6 - 40 - oo times faster. * The file name `-' is always the standard input. If one file name is omitted, the standard input is used as well. * -q for fast compare. Only prints the number of the first differing byte, but not the line number. By tege@nada.kth.se, Torbjorn Granlund. */ #include #include #include #include #include "system.h" #include "getopt.h" extern char *malloc (); extern int errno; extern char *sys_errlist[]; extern int sys_nerr; #define max(h, i) ((h) > (i) ? (h) : (i)) #define min(l, o) ((l) < (o) ? (l) : (o)) int bcmp2 (); void cmp (); int bread (); void printc (); /* Name under which this program was invoked. */ static char *prog_name; /* Filenames of the compared files. */ char *file1; char *file2; /* File descriptors of the files. */ int file1_desc; int file2_desc; /* Read buffers for the files. */ char *buf1; char *buf2; /* Optimal block size for the files. */ int buf_size; int flag = 0; struct option long_options[] = { {"silent", 0, &flag, 's'}, {"quiet", 0, &flag, 's'}, {"quick", 0, &flag, 'q'}, {"verbose", 0, &flag, 'l'}, {NULL, 0, NULL, 0} }; void usage (reason) char *reason; { if (reason != NULL) fprintf (stderr, "%s: %s\n", prog_name, reason); fprintf (stderr, "\ Usage: %s [-lsq] [+verbose] [+silent] [+quiet] [+quick] file1 [file2]\n", prog_name); exit (2); } /* Error handler. ERR is a short description of what operation failed, IN is the argument to the failed command or NULL. RET is the exit status, or 0 if the error wasn't fatal. */ void error (err, in, ret) char *err; char *in; int ret; { fprintf (stderr, "%s: ", prog_name); if (in != NULL) fprintf (stderr, "%s: ", in); fputs (err, stderr); if (errno) { if (errno < sys_nerr) fprintf (stderr, ", %s", sys_errlist[errno]); else fprintf (stderr, ", error code %d", errno); } fputs ("\n", stderr); fflush (stderr); if (ret) exit (ret); errno = 0; } void main (argc, argv) int argc; char *argv[]; { int c; struct stat stat_buf1, stat_buf2; int ind = 0; prog_name = *argv; /* If an argument is omitted, default to the standard input. */ file1 = "-"; file2 = "-"; file1_desc = fileno (stdin); file2_desc = fileno (stdin); /* Parse command line options. */ while ((c = getopt_long (argc, argv, "sql", long_options, &ind)) != EOF) switch (c) { case 0: break; case 's': case 'q': case 'l': flag = c; break; default: usage ((char *) 0); } /* Get out the filename arguments. */ if (optind < argc) file1 = argv[optind++]; if (optind < argc) file2 = argv[optind++]; if (optind < argc) usage ("extra arguments"); /* Open the files. */ if (file1[0] != '-' || file1[1] != 0) { file1_desc = open (file1, O_RDONLY); if (file1_desc < 0) { if (flag == 's') exit (2); else error ("cannot open", file1, 2); } } if (file2[0] != '-' || file2[1] != 0) { file2_desc = open (file2, O_RDONLY); if (file2_desc < 0) { if (flag == 's') exit (2); else error ("cannot open", file2, 2); } } if (file1_desc == file2_desc) usage ("at least one filename should be specified"); if (fstat (file1_desc, &stat_buf1) < 0) error ("cannot stat", file1, 1); if (fstat (file2_desc, &stat_buf2) < 0) error ("cannot stat", file2, 1); /* If both the input descriptors are associated with plain files, we can make it simpler in some cases. */ if ((stat_buf1.st_mode & S_IFMT) == S_IFREG && (stat_buf2.st_mode & S_IFMT) == S_IFREG) { /* Find out if the files are links to the same inode, and therefore identical. */ if (stat_buf1.st_dev == stat_buf2.st_dev && stat_buf1.st_ino == stat_buf2.st_ino) exit (0); /* If only return codee is needed (-s flag), compare the file sizes and conclude that the files differ if they have different sizes. */ if (flag == 's' && stat_buf1.st_size != stat_buf2.st_size) exit (1); } /* Get the optimal block size of the files. */ buf_size = max (ST_BLKSIZE (stat_buf1), ST_BLKSIZE (stat_buf2)); /* Allocate buffers. */ buf1 = malloc (buf_size + sizeof (int)); buf2 = malloc (buf_size + sizeof (int)); if (buf1 == NULL || buf2 == NULL) error ("virtual memory exhausted", (char *) 0, 1); cmp (); } /* Compare the two files already open. */ void cmp () { int line_number = 1; int char_number = 1; int read1, read2; int r; int exit_status = 0; char c1; do { read1 = bread (file1_desc, buf1, buf_size); if (read1 < 0) error ("cannot read", file1, 1); read2 = bread (file2_desc, buf2, buf_size); if (read2 < 0) error ("cannot read", file2, 1); /* Insert sentinels for bcmp2. */ buf1[read1] = ~buf2[read1]; buf2[read2] = ~buf1[read2]; r = bcmp2 (buf1, buf2); char_number += r; c1 = buf1[r]; /* If line number would be written for differing files, count the number of \n in this input block (up till the first byte that differs between the blocks). */ if (!flag) { char *p; buf1[r] = '\n'; /* A sentinel \n to stop the search. */ p = buf1; do { while (*p++ != '\n') ; line_number++; } while (p <= buf1 + r); line_number--; /* Don't count the sentinel newline. */ } /* Did bcmp2 encounter some difference? */ if (r < read1 && r < read2) { if (flag == 's') { exit (1); } else if (!flag) { /* This format is a proposed POSIX standard. */ printf ("%s %s differ: char %d, line %d\n", file1, file2, char_number, line_number); exit (1); } else if (flag == 'q') { printf ("%s %s differ: char %d: ", file1, file2, char_number); printc (stdout, c1); printf (" != "); printc (stdout, buf2[r]); putc ('\n', stdout); exit (1); } else { int x = min (read1, read2); exit_status = 1; while (r < x) { if (buf1[r] != buf2[r]) printf ("%6d %3o %3o\n", r + char_number, (unsigned) buf1[r], (unsigned) buf2[r]); r++; } } } if (read1 != read2) { if (flag == 's') exit (1); else if (!flag) { printf ("eof in \"%s\" at line #%d char #%d\n", (read1 < read2 ? file1 : file2), line_number, char_number); exit (1); } else { printf ("eof in \"%s\" at char #%d\n", ((read1 < read2) ? file1 : file2), char_number); exit (1); } } } while (read1); exit (exit_status); } /* bcmp2 -- compares two blocks of memory until they differ. If the blocks aren't guaranteed different, put a `sentinel' at the end of the blocks. Assumes that p1 and p2 are aligned at int addresses! Returns number of equal bytes + 1. */ int bcmp2 (p1, p2) char *p1, *p2; { int *i1, *i2; char *c1, *c2; /* Find the rough position of the first differ, by reading integers, not bytes. */ for (i1 = (int *) p1, i2 = (int *) p2; *i1++ == *i2++;) ; /* Find out the exact differing position (endianess independant). */ for (c1 = (char *) (i1 - 1), c2 = (char *) (i2 - 1); *c1 == *c2; c1++, c2++) ; return c1 - p1; } /* Read NCHARS bytes from descriptor FD into BUF. Return the number of characters successfully read. */ int bread (fd, buf, nchars) int fd; char *buf; int nchars; { char *bp = buf; int n; for (;;) { n = read (fd, bp, nchars); if (n < 0) return -1; bp += n; if (n == nchars || n == 0) break; nchars -= n; } return bp - buf; } /* Print a character C on the output FILE FS, making unvisible characters visible by quoting like cat(1). */ void printc (fs, c) FILE *fs; unsigned char c; { if (c >= 128) { putc ('M', fs); putc ('-', fs); c -= 128; } if (c < 32) { putc ('^', fs); c += 64; } else if (c == 127) { putc ('^', fs); c = '?'; } putc (c, fs); } bp += n; if (n == nchars || n == 0) break; nchars -= n; } return bp - buf; } /* Print a character C ofileutils/cp-aux.c 644 473 0 6267 4702305043 7434 /* cp-aux.c -- file copying (auxiliary routines) Copyright (C) 1989 Free Software Foundation. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Written by Torbjorn Granlund, Sweden (tege@sics.se). */ #include #include "cp.h" extern char *prog_name; void usage (reason) char *reason; { if (reason != NULL) fprintf (stderr, "%s: %s\n", prog_name, reason); fprintf (stderr, "\ Usage: %s [-dfioprv] [+dereference] [+force] [+interactive]\n\ [+one-file-system] [+preserve] [+recursive] [+verbose]\n\ source dest\n\ \n\ %s [-dfioprv] [+dereference] [+force] [+interactive]\n\ [+one-file-system] [+preserve] [+recursive] [+verbose]\n\ source... existing-dest-dir\n", prog_name, prog_name); exit (2); } /* Error handler. ERR is a short description of what operation failed, CODE is the error code, IN is the argument to the failed command or NULL. RET is the exit status, or 0 if the error wasn't fatal. */ void error (err, code, in, ret) char *err; int code; char *in; int ret; { fputs (prog_name, stderr); fputs (": ", stderr); if (in != NULL) { fputs (in, stderr); fputs (": ", stderr); } fputs (err, stderr); if (code > 0) { if (code < sys_nerr) { fputs (", ", stderr); fputs (sys_errlist[code], stderr); } else fprintf (stderr, ", error code %d", code); } fputs ("\n", stderr); if (ret) exit (ret); else exit_status = 1; } char * xmalloc (size) unsigned size; { char *x = malloc (size); if (x == 0) error ("virtual memory exhausted", 0, (char *) 0, FATAL); return x; } char * xrealloc (ptr, size) char *ptr; unsigned size; { char *x = realloc (ptr, size); if (x == 0) error ("virtual memory exhausted", 0, (char *) 0, FATAL); return x; } char * str_cpy (s1, s2) char *s1; char *s2; { while ((*s1++ = *s2++) != '\0') ; return s1 - 1; } int user_confirm_overwriting (file_name) char *file_name; { int c, t; printf ("Overwrite `%s'? ", file_name); fflush (stdout); c = t = getchar (); while (t != '\n') t = getchar (); return c == 'y' || c == 'Y'; } int member (gid) int gid; { int i; for (i = 0; i < ngroups; i++) { if (gid == groups[i]) return 1; } return 0; } int is_ancestor (sb, ancestors) struct stat *sb; struct dir_list *ancestors; { while (ancestors != 0) { if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev) return 1; ancestors = ancestors->parent; } return 0; } ush (stdout); c = t = getchar (); while (t != '\n') t = getchar (); return c == 'y' || c == 'Y'; } int member (gid) int gid; { int i; for (i = 0; i < ngroups; i++) { if (gid == groups[i]) return 1; } return 0; } int is_ancestor (sb, ancestors) struct stat *sb; struct dir_list *afileutils/cp-hash.c 644 473 0 13632 4702305043 7574 /* cp-hash.c -- file copying (hash search routines, to be replaced!) Copyright (C) 1989 Free Software Foundation. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Written by Torbjorn Granlund, Sweden (tege@sics.se). */ #include #include "cp.h" char *hash_insert (); char *hash_insert2 (); struct htab *htab; char new_file; void remember_created (path) char *path; { struct stat sb; if (stat (path, &sb) < 0) error ("cannot stat new file", errno, path, FATAL); hash_insert (sb.st_ino, sb.st_dev, &new_file); } char * remember_copied (node, ino, dev) char *node; ino_t ino; dev_t dev; { return hash_insert (ino, dev, node); } #if 0 /* Forget about files that have been copied, but remember about created files. Very primitive, only resets the pointers in the htab vector, but doesn't compactify entry_tab. */ void better_forget_copied () { /* Find next etab entry to remove. */ /* Find next htab entry, with an entry to keep, and whose index in etab is larger than the etab entry found. */ } void forget_copied () { int i; struct entry **p; struct entry *e; p = htab->hash; for (i = htab->modulus; i > 0; i--) { e = *p; while (e != NULL) { if (e->node != &new_file) e->ino = 0; e = e->coll_link; } ++p; } } #endif /* hash_init - Allocate space for the hash structures, and set the global variable `htab' to point to it. The initial hash module is specified in MODULUS, and the number of entries are specified in ENTRY_TAB_SIZE. (The hash structure will be rebuilt when ENTRY_TAB_SIZE entries have been inserted, and MODULUS and ENTRY_TAB_SIZE in the global `htab' will be doubled.) */ void hash_init (modulus, entry_tab_size) unsigned modulus; unsigned entry_tab_size; { struct htab *htab_r; htab_r = (struct htab *) xmalloc (sizeof (struct htab) + sizeof (struct entry *) * modulus); htab_r->entry_tab = (struct entry *) xmalloc (sizeof (struct entry) * entry_tab_size); htab_r->modulus = modulus; htab_r->entry_tab_size = entry_tab_size; htab = htab_r; forget_all (); } /* forget_all - Reset the hash structure in the global variable `htab' to contain no entries. */ void forget_all () { int i; struct entry **p; htab->first_free_entry = 0; p = htab->hash; for (i = htab->modulus; i > 0; i--) *p++ = NULL; } /* hash_insert - Insert an item, inode number and device number, in the hash structure in the global variable `htab', if an entry with the same data was not found already. Return zero if the item was inserted and non-zero if it wasn't. */ char * hash_insert (ino, dev, node) ino_t ino; dev_t dev; char *node; { struct htab *htab_r = htab; if (htab_r->first_free_entry >= htab_r->entry_tab_size) { int i; struct entry *ep; unsigned modulus; unsigned entry_tab_size; /* Increase the number of hash entries, and re-hash the data. The method of shrinking and increasing is made to compactify the heap. If twice as much data would be allocated straightforwardly, we would never re-use a byte of memory. */ /* Let htab shrink. Keep only the header, not the pointer vector. */ htab_r = (struct htab *) xrealloc ((char *) htab_r, sizeof (struct htab)); modulus = 2 * htab_r->modulus; entry_tab_size = 2 * htab_r->entry_tab_size; /* Increase the number of possible entries. */ htab_r->entry_tab = (struct entry *) xrealloc ((char *) htab_r->entry_tab, sizeof (struct entry) * entry_tab_size); /* Increase the size of htab again. */ htab_r = (struct htab *) xrealloc ((char *) htab_r, sizeof (struct htab) + sizeof (struct entry *) * modulus); htab_r->modulus = modulus; htab_r->entry_tab_size = entry_tab_size; htab = htab_r; i = htab_r->first_free_entry; /* Make the increased hash table empty. The entries are still available in htab->entry_tab. */ forget_all (); /* Go through the entries and install them in the pointer vector htab->hash. The items are actually inserted in htab->entry_tab at the position where they already are. The htab->coll_link need however be updated. Could be made a little more efficient. */ for (ep = htab_r->entry_tab; i > 0; i--) { hash_insert2 (htab_r, ep->ino, ep->dev, ep->node); ep++; } } return hash_insert2 (htab_r, ino, dev, node); } /* hash_insert2 - insert INO and DEV in the hash structure HTAB, if not already present. Return zero if inserted and non-zero if it already existed. */ char * hash_insert2 (htab, ino, dev, node) struct htab *htab; ino_t ino; dev_t dev; char *node; { struct entry **hp, *ep2, *ep; hp = &htab->hash[ino % htab->modulus]; ep2 = *hp; /* Collision? */ if (ep2 != NULL) { ep = ep2; /* Search for an entry with the same data. */ do { if (ep->ino == ino && ep->dev == dev) return ep->node; /* Found an entry with the same data. */ ep = ep->coll_link; } while (ep != NULL); /* Did not find it. */ } ep = *hp = &htab->entry_tab[htab->first_free_entry++]; ep->ino = ino; ep->dev = dev; ep->node = node; ep->coll_link = ep2; /* ep2 is NULL if not collision. */ return NULL; } /* Collision? */ if (ep2 != NULL) { ep = ep2; /* Search for an entry with the samfileutils/cp.c 644 473 0 42175 4702305044 6660 /* cp.c -- file copying (main routines) Copyright (C) 1989 Free Software Foundation. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Written by Torbjorn Granlund, Sweden (tege@sics.se). */ /* Yet to be done: * Symlink translation. */ #include #include #include "cp.h" #include "getopt.h" extern int geteuid (); extern int getegid (); #define INITIAL_HASH_MODULE 100 #define INITIAL_ENTRY_TAB_SIZE 70 /* A pointer to either lstat or stat, depending on whether dereferencing of symlinks is done. */ int (*xstat) (); /* The invocation name of this program. */ char *prog_name; /* Flags for command line options. */ int flag_dereference = 1; int flag_force = 0; int flag_interactive = 0; int flag_preserve = 0; int flag_recursive = 0; int flag_one_file_system = 0; int flag_verbose = 0; int root; int euid; int egid; int exit_status = 0; int umask_kill; int *groups; int ngroups; /* structure holding the long-named options accepted on the command line */ struct option long_opts[] = { {"dereference", 0, &flag_dereference, 0}, {"force", 0, &flag_force, 1}, {"interactive", 0, &flag_interactive, 1}, {"one-file-system", 0, &flag_one_file_system, 1}, {"preserve", 0, &flag_preserve, 1}, {"recursive", 0, &flag_recursive, 1}, {"verbose", 0, &flag_verbose, 1}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char *argv[]; { int c; int ind = 0; prog_name = argv[0]; /* Find out the current file creation mask, to knock the right bits when using chmod. The creation mask is set to to be liberal, so that created directories can be written, even if it would not have been allowed with the mask this process was started with. */ umask_kill = 0777777 ^ umask (0); while ((c = getopt_long (argc, argv, "dfioprv", long_opts, &ind)) != EOF) { switch (c) { case 0: break; case 'd': flag_dereference = 0; break; case 'f': flag_force = 1; break; case 'i': flag_interactive = 1; break; case 'o': flag_one_file_system = 1; break; case 'p': flag_preserve = 1; break; case 'r': flag_recursive = 1; break; case 'v': flag_verbose = 1; break; default: usage ((char *) 0); } } if (flag_preserve == 1) umask_kill = 0777777; /* The key difference between -d(ereference) and not, is the version of `stat' to call. */ if (flag_dereference) xstat = stat; else xstat = lstat; /* Allocate space for remembering copied and created files. */ hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE); /* Is the current user privileged? Root can copy special nodes. */ euid = geteuid (); root = (euid == 0); if (flag_interactive && !flag_force) { /* Read group information. The group information is needed to determine if an existing target file will be possible to overwrite, to avoid to pose useless questions to the user. */ egid = getegid (); ngroups = 1; groups = (int *) xmalloc (ngroups * sizeof *groups); #ifdef USG *groups = getgid (); #else for (;;) { int ret; ret = getgroups (ngroups, groups); if (ret >= 0) { ngroups = ret; break; } ngroups *= 2; groups = (int *) xrealloc (groups, ngroups * sizeof *groups); } #endif } do_copy (argc, argv); exit (exit_status); } /* Scan the arguments, and copy each by calling `top_copy'. */ void do_copy (argc, argv) int argc; char *argv[]; { char *target; struct stat sb; int new_dst = 0; int x; if (optind >= argc) usage ("missing file arguments"); if (optind >= argc - 1) usage ("missing file argument"); target = argv[argc - 1]; /* Take away any final slash in the target file name. */ x = strlen (target) - 1; if (target[x] == '/') target[x] = '\0'; if (stat (target, &sb) < 0) { if (errno != ENOENT) error ("cannot stat", errno, target, 1); else new_dst = 1; } if (!new_dst && (sb.st_mode & S_IFMT) == S_IFDIR) { /* cp e_file_1...e_file_n e_dir -- copy the files `e_file_1' through `e_file_n' to the existing directory `e_dir'. */ for (;;) { char *arg; char *ap; char *dst_path; arg = argv[optind]; /* Append the last component of `arg', to `target'. */ ap = rindex (arg, '/'); if (ap == 0) /* Why is `rindex' defined so stupidly? */ ap = arg; else ap++; dst_path = xmalloc (strlen (target) + strlen (ap) + 2); str_cpy (str_cpy (str_cpy (dst_path, target), "/"), ap); copy (arg, dst_path, new_dst, 0, (struct dir_list *) 0); forget_all (); ++optind; if (optind == argc - 1) break; } } else if (argc - optind == 2) copy (argv[optind], target, new_dst, 0, (struct dir_list *) 0); else usage ("last argument should be the name of an existing directory"); } /* Copy the file `src_path' to the file `dst_path'. The files may be of any type. If the file `dst_path' cannot exist because its parent directory was just created, `new_dst' should be non-zero. If `dst_path' might already exist, `new_dst' should be zero. */ void copy (src_path, dst_path, new_dst, device, ancestors) char *src_path; char *dst_path; int new_dst; dev_t device; struct dir_list *ancestors; { struct stat src_sb; struct stat dst_sb; int mode; int type; char *earlier_file; if ((*xstat) (src_path, &src_sb) < 0) { error ("cannot stat", errno, src_path, 0); return; } /* Are we crossing a file system boundary? */ if (flag_one_file_system && device != 0 && device != src_sb.st_dev) return; /* (We shouldn't actually insert a node unless nlink > 1, but we need to find created files, not to copy infinitely if a directory is copied into itself.) */ earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev); /* Have we encountered a file just created? */ if (earlier_file == &new_file) return; if (flag_verbose) printf (" %s -> %s\n", src_path, dst_path); /* Did we copy this inode somewhere else (in this command line argument) and therefore this is a second hard link to the inode? */ if (!flag_dereference && src_sb.st_nlink > 1) { if (earlier_file != 0) { unlink (dst_path); if (link (earlier_file, dst_path) < 0) { error ("cannot create link", 0, dst_path, 0); error ("cannot link", error, earlier_file, 0); } return; } } mode = src_sb.st_mode; type = src_sb.st_mode & S_IFMT; if (!new_dst) { if ((*xstat) (dst_path, &dst_sb) < 0) { if (errno != ENOENT) { error ("cannot stat", errno, dst_path, 0); return; } else new_dst = 1; } else { /* The file exists already. */ /* Are the source and destination files actually one and the same? */ if (src_sb.st_ino == dst_sb.st_ino && src_sb.st_dev == dst_sb.st_dev) { fprintf (stderr, "%s: source `%s' and target `%s' are identical (omitted)\n", prog_name, src_path, dst_path); return; } /* Is the file of a type we can ever overwrite? */ if (flag_interactive && !flag_force && (type == S_IFDIR || type == S_IFREG #ifdef S_IFLNK || type == S_IFLNK #endif )) { int perm; /* Avoid (in most cases) to ask the user for permission to overwrite a file that cannot be overwritten. (A directory can be overwritten if it's searchable.) */ if (type == S_IFDIR) perm = S_IEXEC >> 6; else perm = S_IWRITE >> 6; if (root || ((euid == dst_sb.st_uid) ? (dst_sb.st_mode & (perm * 0100)) : ((egid == dst_sb.st_gid || member (dst_sb.st_gid)) ? (dst_sb.st_mode & (perm * 0010)) : (dst_sb.st_mode & (perm * 0001))))) { if (!user_confirm_overwriting (dst_path)) return; } else { error ("cannot overwrite", EACCES, dst_path, 0); return; } } else if (flag_force && (unlink (dst_path) == 0 || errno == ENOENT)) new_dst = 1; } } if (flag_recursive) switch (type) { #ifdef S_IFIFO case S_IFIFO: /* If a fifo already exists, we cannot do a better one. */ if (new_dst) if (mknod (dst_path, mode & umask_kill, 0) < 0) { error ("cannot create fifo inode", errno, dst_path, 0); return; } break; #endif case S_IFCHR: #ifdef S_IFSOCK case S_IFSOCK: #endif case S_IFBLK: if (root) { if (!new_dst) unlink (dst_path); if (mknod (dst_path, mode & umask_kill, src_sb.st_rdev) < 0) { error ("cannot create special inode", errno, dst_path, 0); return; } } else { error ("special inode omitted", 0, src_path, 0); return; } break; case S_IFDIR: { struct dir_list *dir; /* If this directory has been copied before during the recursion, there's a symbolic link to a ancestor directory of the symbolic link. It's impossible to continue to copy this, unless we've got an infinite disk. */ if (is_ancestor (&src_sb, ancestors)) { error ("omitting cyclic symbolic link", 0, src_path, 0); return; } /* Insert the current directory in the list of parents. */ dir = (struct dir_list *) alloca (sizeof (struct dir_list)); dir->parent = ancestors; dir->ino = src_sb.st_ino; dir->dev = src_sb.st_dev; /* Create the directory. If a directory already exists, we cannot create a better one. */ if (new_dst) { /* Create the new directory writable and searchable, so we can create new entries in it. */ if (mkdir (dst_path, 0700) < 0) { error ("cannot create directory", errno, dst_path, 0); return; } else { /* Insert the created directory's inode and device numbers into the search structure, so that we can avoid copying it again. */ remember_created (dst_path); } } /* Now, copy the contents of the directory. */ copy_dir (src_path, dst_path, new_dst, &src_sb, dir); } break; case S_IFREG: if (copy_reg (src_path, dst_path) < 0) return; break; #ifdef S_IFLNK case S_IFLNK: { char *link_val = (char *) alloca (src_sb.st_size + 1); if (readlink (src_path, link_val, src_sb.st_size) < 0) { error ("cannot read symbolic link", errno, src_path, 0); return; } link_val[src_sb.st_size] = '\0'; if (!new_dst) unlink (dst_path); if (symlink (link_val, dst_path) < 0) { error ("cannot create symbolic link", errno, dst_path, 0); return; } } break; #endif default: error ("unknown file type (omitted)", 0, src_path, 0); return; } else { if (type == S_IFDIR) { error ("omitting directory", 0, src_path, 0); return; } else { if (copy_reg (src_path, dst_path) < 0) return; } } /* Change the permissions to the right ones. */ if (flag_preserve || new_dst) chmod (dst_path, mode & umask_kill); /* Adjust the times for the copy, but not if it's a symbolic link, since utimes would change the target for that link, not the link itself. */ if (flag_preserve #ifdef S_IFLNK && type != S_IFLNK #endif ) { /* Adjust the file's times. */ int val = 0; #ifdef USG long tv[2]; tv[0] = src_sb.st_atime; tv[1] = src_sb.st_mtime; val = utime (dst_path, tv); #else struct timeval tv[2]; tv[0].tv_sec = src_sb.st_atime; tv[0].tv_usec = 0; tv[1].tv_sec = src_sb.st_mtime; tv[1].tv_usec = 0; val = utimes (dst_path, tv); #endif if (val < 0) { error ("cannot set access/modification times", errno, dst_path, 0); return; } } } /* Read the contents of the directory `src_path_in', and recursively copy the contents to `dst_path_in'. `new_dst' is non-zero if `dst_path_in' is a directory that was created previously in the recursion. */ void copy_dir (src_path_in, dst_path_in, new_dst, src_sb, ancestors) char *src_path_in; char *dst_path_in; int new_dst; struct stat *src_sb; struct dir_list *ancestors; { DIR *dirp; struct direct *dp; char *name_space; char *file_name; char *src_path; char *dst_path; dirp = opendir (src_path_in); if (dirp == 0) { error ("cannot read directory", errno, src_path_in, 0); return; } /* Read all the names in the directory, before any subdirectory is read. */ name_space = (char *) alloca (src_sb->st_size); file_name = name_space; while ((dp = readdir (dirp)) != 0) { /* Skip "." and ".." (some NFS filesystems' directories lack them). */ if (dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) file_name = 1 + str_cpy (file_name, dp->d_name); } *file_name = '\0'; closedir (dirp); /* Now process the files in the directory. */ file_name = name_space; while (*file_name != '\0') { dst_path = xmalloc (strlen (dst_path_in) + strlen (file_name) + 2); src_path = xmalloc (strlen (src_path_in) + strlen (file_name) + 2); str_cpy (str_cpy (str_cpy (src_path, src_path_in), "/"), file_name); str_cpy (str_cpy (str_cpy (dst_path, dst_path_in), "/"), file_name); copy (src_path, dst_path, new_dst, src_sb->st_dev, ancestors); /* Free the memory for `src_path'. The memory for `dst_path' cannot be deallocated, since it is used to create multiple hard links. (Of course it is possible to optimize memory allocation, as most files have only one link.) */ free (src_path); file_name += strlen (file_name) + 1; } } /* Copy a regular file from `src_path' to `dst_path'. Large blocks of zeroes, as well as holes in the source file, are made into holes in the target file. (Holes are read as zero by the `read' system call.) */ int copy_reg (src_path, dst_path) char *src_path; char *dst_path; { char *buf; int buf_size; int target_desc; int source_desc; int n_read; int n_written; struct stat sb; int return_val = 0; int n_read_total = 0; char *cp; int *ip; source_desc = open (src_path, O_RDONLY); if (source_desc < 0) { error ("cannot open regular file", errno, src_path, 0); return -1; } /* Create the new regular file with small permissions initially, not to create a security hole. */ target_desc = open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (target_desc < 0) { error ("cannot create regular file", errno, dst_path, 0); return_val = -1; goto ret2; } /* Find out the appropriate buffer length. */ if (fstat (target_desc, &sb) < 0) { error ("cannot stat new file", errno, dst_path, 0); return_val = -1; goto ret; } buf_size = ST_BLKSIZE (sb); /* Make a buffer with space for a sentinel at the end. */ buf = (char *) alloca (buf_size + sizeof (int)); for (;;) { n_read = read (source_desc, buf, buf_size); if (n_read < 0) { error ("cannot read", errno, src_path, 0); return_val = -1; goto ret; } if (n_read == 0) break; n_read_total += n_read; ip = (int *) buf; cp = buf; buf[n_read] = 1; /* Sentinel to stop loop. */ /* Find first non-zero byte, or the sentinel. If possible, use integer comparisons for speed, but for short files use char comparisons to prevent erroneous hole creation. */ if (n_read < sizeof (int)) { while (*cp++ == 0) ; } else { while (*ip++ == 0) ; } /* If we found the sentinel, the whole input block was zero, and we can make a hole. */ if ((char *) ip > buf + n_read || cp > buf + n_read) { /* Make a hole. */ if ((int) lseek (target_desc, (off_t) n_read, L_INCR) < 0) { error ("cannot lseek", errno, dst_path, FATAL); return_val = -1; goto ret; } } else { n_written = write (target_desc, buf, n_read); if (n_written < n_read) error ("cannot write", errno, dst_path, FATAL); } } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. */ /* ??? Since USG doesn't have ftruncate, this can't be done. This should be fixed. */ #ifndef USG if (write (target_desc, "", 1) != 1 || ftruncate (target_desc, n_read_total) < 0) error ("cannot write", errno, dst_path, FATAL); #endif ret: if (close (target_desc) < 0) error ("cannot close", errno, dst_path, FATAL); ret2: close (source_desc); return return_val; } at the end. Otherwise the kernel would truncate the file at the end of the last write operation. */ /* ??? Since USG doesn't have ftruncate, this can't be done. This should be fixed. */ #ifndef USG if (write (target_desc, "", 1) != 1 || ftruncate (target_desc, n_read_total) < 0) error ("cannot write", errno, dst_path, FATAL); #endif ret: if (closefileutils/cp.h 644 473 0 5564 4702305044 6646 /* cp.h -- file copying (data definitions) Copyright (C) 1989 Free Software Foundation. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. Written by Torbjorn Granlund, Sweden (tege@sics.se). */ #include #include "system.h" struct dir_list { struct dir_list *parent; ino_t ino; dev_t dev; }; struct entry { ino_t ino; dev_t dev; char *node; /* Path name, or &new_file for new inodes. */ struct entry *coll_link; /* 0 = entry not occupied. */ }; struct htab { unsigned modulus; /* Size of the "hash" pointer vector. */ struct entry *entry_tab; /* Pointer to dynamically growing vector. */ unsigned entry_tab_size; /* Size of current "entry_tab" allocation. */ unsigned first_free_entry; /* Index in "entry_tab". */ struct entry *hash[1]; /* Vector of pointers in "entry_tab". */ }; extern int exit_status; extern struct htab *htab; extern char *xmalloc (); extern char *xrealloc (); extern void forget_copied (); extern void forget_all (); extern void top_copy (); extern int copy_reg (); extern void hash_init (); extern char *remember_copied (); extern void remember_created (); /* For created inodes, a pointer in the search structure to this character identifies that the inode as new. */ extern char new_file; #define FATAL 1 extern void error (); extern void usage (); extern char *str_cpy (); extern int user_confirm_overwriting (); extern int member (); extern void do_copy (); extern void copy (); extern void copy_dir (); extern int *groups; extern int ngroups; extern int is_ancestor (); /* System calls. */ extern int getgroups (); extern int open (); extern int close (); extern int fstat (); extern int stat (); extern int lstat (); extern int read (); extern int write (); extern int symlink (); extern int readlink (); extern int mkdir (); extern int mknod (); extern int umask (); extern int unlink (); extern int link (); extern int chmod (); extern int fchmod (); extern int access (); extern int utimes (); extern int ftruncate (); extern off_t lseek (); /* Library calls. */ extern char *malloc (); extern char *realloc (); extern int free (); extern int errno; extern char *sys_errlist[]; extern int sys_nerr; extern void exit (); extern int fprintf (); extern int fputs (); extern int printf (); ; extern int readlink (); extern int mkdir (); extern int mknod (); extern int umask (); extern int unlink (); extern int link (); extern infileutils/dd.c 644 473 0 40423 4702305045 6640 /* dd -- convert and copy files. Copyright (C) 1985 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written by Paul Rubin. */ #include #include #include #include "system.h" #define equal(p, q) (strcmp ((p),(q)) == 0) #define max(a, b) ((a) > (b) ? (a) : (b)) #define BLOCKSIZE 512 /* Unix dd uses this so we do too */ #define C_ASCII 01 #define C_EBCDIC 02 #define C_IBM 04 #define C_BLOCK 010 #define C_UNBLOCK 020 #define C_LCASE 040 #define C_UCASE 0100 #define C_SWAB 0200 #define C_NOERROR 0400 #define C_SYNC 01000 #define C_HARDWAY 04000 /* Some conversion is needed during i/o. */ char *index (); char *malloc (); long lseek (); char *xmalloc (); void copy (); void fatal (); void parse_conversion (); void pfatal_with_name (); void quit (); void scanargs (); void usage (); struct info { char *i_if; /* input filename */ int i_ifd; /* input file descriptor */ char *i_of; /* output filename */ int i_ofd; /* output file descriptor */ long i_ibs; /* input blocksize */ long i_obs; /* output blocksize */ long i_cbs; /* conversion buffer size */ int i_skip; /* skip this many records on input */ int i_files; /* skip this many files on input (TODO) */ long i_seek; /* seek to this record on output */ int i_count; /* copy only this many records */ int i_convert; /* bit vector of conversions to apply */ }; struct info info = { NULL, 0, NULL, 1, BLOCKSIZE, BLOCKSIZE, BLOCKSIZE, 0, 0, 0, -1, 0 }; int w_partial = 0; /* number of partial blocks written */ int w_full = 0; /* numer of full blocks written */ int r_partial = 0; /* number of partial blocks read */ int r_full = 0; /* number of full blocks read */ int r_truncate = 0; /* records truncated by conv=block */ struct conversion { char *convname; int conversion; }; struct conversion conversions[] = { "ascii", C_ASCII | C_HARDWAY, /* ebcdic to ascii */ "ebcdic", C_EBCDIC | C_HARDWAY, /* ascii to ebcdic */ "ibm", C_IBM | C_HARDWAY, /* slightly different atoe */ "block", C_BLOCK | C_HARDWAY, /* var to fixed len recs */ "unblock", C_UNBLOCK | C_HARDWAY, /* fixed to var */ "lcase", C_LCASE | C_HARDWAY, /* translate upper to lower */ "ucase", C_UCASE | C_HARDWAY, /* translate lower to upper */ "swab", C_SWAB | C_HARDWAY, /* swap bytes of input */ "noerror", C_NOERROR, /* ignore i/o errors */ "sync", C_SYNC, /* pad input recs to ibs */ NULL, 0 }; /* Translation table formed by applying successive transformations. */ char trans_table[256]; char ascii_to_ebcdic[] = { 0, 01, 02, 03, 067, 055, 056, 057, 026, 05, 045, 013, 014, 015, 016, 017, 020, 021, 022, 023, 074, 075, 062, 046, 030, 031, 077, 047, 034, 035, 036, 037, 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, 0247, 0250, 0251, 0300, 0152, 0320, 0241, 07, 040, 041, 042, 043, 044, 025, 06, 027, 050, 051, 052, 053, 054, 011, 012, 033, 060, 061, 032, 063, 064, 065, 066, 010, 070, 071, 072, 073, 04, 024, 076, 0341, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377 }; char ascii_to_ibm[] = { 0, 01, 02, 03, 067, 055, 056, 057, 026, 05, 045, 013, 014, 015, 016, 017, 020, 021, 022, 023, 074, 075, 062, 046, 030, 031, 077, 047, 034, 035, 036, 037, 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, 0247, 0250, 0251, 0300, 0117, 0320, 0241, 07, 040, 041, 042, 043, 044, 025, 06, 027, 050, 051, 052, 053, 054, 011, 012, 033, 060, 061, 032, 063, 064, 065, 066, 010, 070, 071, 072, 073, 04, 024, 076, 0341, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377 }; char ebcdic_to_ascii[] = { 0, 01, 02, 03, 0234, 011, 0206, 0177, 0227, 0215, 0216, 013, 014, 015, 016, 017, 020, 021, 022, 023, 0235, 0205, 010, 0207, 030, 031, 0222, 0217, 034, 035, 036, 037, 0200, 0201, 0202, 0203, 0204, 012, 027, 033, 0210, 0211, 0212, 0213, 0214, 05, 06, 07, 0220, 0221, 026, 0223, 0224, 0225, 0226, 04, 0230, 0231, 0232, 0233, 024, 025, 0236, 032, 040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 0250, 0133, 056, 074, 050, 053, 041, 046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 0260, 0261, 0135, 044, 052, 051, 073, 0136, 055, 057, 0262, 0263, 0264, 0265, 0266, 0267, 0270, 0271, 0174, 054, 045, 0137, 076, 077, 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, 0302, 0140, 072, 043, 0100, 047, 075, 042, 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, 060, 061, 062, 063, 064, 065, 066, 067, 070, 071, 0372, 0373, 0374, 0375, 0376, 0377 }; int main (argc, argv) int argc; char **argv; { int i; /* Initialize translation table to identity translation. */ for (i = 0; i < 256; i++) trans_table[i] = (char) i; /* Decode arguments and fill in `info' structure. */ scanargs (argc, argv); if (info.i_if != NULL) { info.i_ifd = open (info.i_if, 0); if (info.i_ifd < 0) pfatal_with_name (info.i_if, "cannot open", 1); } if (info.i_of != NULL) { info.i_ofd = creat (info.i_of, 0666); if (info.i_ofd < 0) pfatal_with_name (info.i_of, "cannot create", 1); } copy (); quit (0); } void copy () { register int i, oc = 0; int spaces = 0, col = 0; char *ibuf, *obuf; int nread, nwritten; ibuf = xmalloc (info.i_ibs); if (info.i_ibs != info.i_obs || (info.i_convert & C_HARDWAY)) obuf = xmalloc (info.i_obs); else obuf = ibuf; for (i = 0; i < info.i_skip; i++) if (read (info.i_ifd, ibuf, info.i_ibs) < 0) pfatal_with_name (info.i_if, "error reading", 1); if (lseek (info.i_ofd, info.i_seek * info.i_obs, 0) < 0) pfatal_with_name (info.i_of, "seek error on", 1); if (info.i_count >= 0 && r_partial + r_full >= info.i_count) return; while ((info.i_count < 0 || r_partial + r_full < info.i_count) && (nread = read (info.i_ifd, ibuf, info.i_ibs)) != 0) { if (nread < 0) { fprintf (stderr, "dd: error reading "); perror (info.i_if); if (info.i_convert & C_NOERROR) continue; else quit (2); } if (nread < info.i_ibs) { r_partial++; if (info.i_convert & C_SYNC) while (nread < info.i_ibs) ibuf[nread++] = '\0'; } else r_full++; /* If blocksizes are the same and no conversion, just flush. */ if (ibuf == obuf) { nwritten = write (info.i_ofd, obuf, nread); if (nwritten != nread) { fprintf (stderr, "dd: error writing "); perror (info.i_of); w_partial++; if (!(info.i_convert & C_NOERROR)) quit (1); } else if (nread == info.i_ibs) w_full++; else w_partial++; continue; } /* Copy input to output, doing conversion and flushing output as necessary. */ for (i = 0; i < nread; i++) { register int c = trans_table[ibuf[i] & 0377] & 0377; if (info.i_convert & C_BLOCK) { if (spaces-- > 0) { i--; /* Push back char, get it next time. */ c = trans_table[' ']; } else if (c == '\n') { spaces = max (0, info.i_cbs - col); col = 0; continue; } else if (col++ >= info.i_cbs) { r_truncate++; continue; } } else if (info.i_convert & C_UNBLOCK) { if (col++ >= info.i_cbs) { col = spaces = 0; c = trans_table['\n']; i--; /* Get the real `c' next time. */ } else if (c == ' ') spaces++; else if (spaces > 0) { i--; spaces--; c = trans_table[' ']; } } if (info.i_convert & C_SWAB) obuf[oc++ ^ 1] = c; else obuf[oc++] = c; if (oc >= info.i_obs) { nwritten = write (info.i_ofd, obuf, info.i_obs); if (nwritten != info.i_obs) { fprintf (stderr, "dd: error writing "); perror (info.i_of); w_partial++; /* Maybe it would be better to try to write out the rest of the block next time instead of throwing it away. */ if (!(info.i_convert & C_NOERROR)) quit (1); } else w_full++; oc = 0; } } } /* Flush last block. */ if (oc > 0) { /* First, fix earlier screw if swapping bytes and n is odd. */ if ((info.i_convert & C_SWAB) && (oc & 1)) obuf[oc - 1] = obuf[oc]; nwritten = write (info.i_ofd, obuf, oc); if (nwritten != oc) { fprintf (stderr, "dd: error writing "); perror (info.i_of); } if (nwritten > 0) w_partial++; } } void scanargs (argc, argv) int argc; char **argv; { int i, n; for (i = 1; i < argc; i++) { char *name, *val; name = argv[i]; val = index (name, '='); if (val == NULL) usage ("unrecognized option `%s'", name); *val++ = '\0'; if (equal (name, "if")) info.i_if = val; else if (equal (name, "of")) info.i_of = val; else if (equal (name, "conv")) parse_conversion (val); else { n = parse_integer (val); if (equal (name, "ibs")) info.i_ibs = n; else if (equal (name, "obs")) info.i_obs = n; else if (equal (name, "bs")) info.i_obs = info.i_ibs = n; else if (equal (name, "cbs")) info.i_cbs = n; else if (equal (name, "skip")) info.i_skip = n; else if (equal (name, "seek")) info.i_seek = n; else if (equal (name, "count")) info.i_count = n; else usage ("unrecognized option `%s=%s'", name, val); } } } int parse_integer (str) char *str; { int n = 0; char *p = str; while (isdigit (*p)) { n = n * 10 + *p - '0'; p++; } loop: switch (*p++) { case '\0': return n; case 'b': n *= 512; goto loop; case 'k': n *= 1024; goto loop; case 'w': n *= 2; goto loop; case 'x': n *= parse_integer (p); break; default: return 0; } return n; } void parse_conversion (str) char *str; { char *new; int i, j; char *new_trans; do { new = index (str, ','); if (new != NULL) *new++ = '\0'; for (i = 0; conversions[i].convname != NULL; i++) if (equal (conversions[i].convname, str)) { info.i_convert |= conversions[i].conversion; break; } if (conversions[i].convname == NULL) { usage ("%s: invalid conversion", str); exit (1); } #define MX(a) (bit_count (info.i_convert & (a))) if ((MX (C_ASCII | C_EBCDIC | C_IBM) > 1) || (MX (C_BLOCK | C_UNBLOCK) > 1) || (MX (C_LCASE | C_UCASE) > 1) || (MX (C_UNBLOCK | C_SYNC) > 1)) { fatal ("only one conv in {ascii,ebcdic,ibm}, {lcase,ucase}, {block,unblock}, {unblock,sync}"); } #undef MX str = new; } while (new != NULL); /* I don't know if the following restriction is stupid, but it's convenient. */ if ((info.i_convert & C_SWAB) && (info.i_ibs % 2 != 0 || info.i_obs % 2 != 0)) { fatal ("ibs and obs must both be even for swab to work"); } /* Fix up translation table. */ /* Do upper and lower case if necessary. */ if (info.i_convert & C_UCASE) for (j = 'a'; j <= 'z'; j++) trans_table[j] = toupper (j); else if (info.i_convert & C_LCASE) for (j = 'A'; j <= 'Z'; j++) trans_table[j] = tolower (j); /* Now find and apply char set translation. */ if (info.i_convert & C_ASCII) new_trans = ebcdic_to_ascii; else if (info.i_convert & C_EBCDIC) new_trans = ascii_to_ebcdic; else if (info.i_convert & C_IBM) new_trans = ascii_to_ibm; else return; for (i = 0; i < 256; i++) trans_table[i] = new_trans[trans_table[i]]; } /* Return number of 1 bits in `i'. */ int bit_count (i) register int i; { register int n; for (n = 0; i != 0; i = (i >> 1) & ~(1 << 31)) n += i & 1; return n; } /* Print fatal error and `func' message for file `filename', then die with exit status `code'. */ void pfatal_with_name (filename, func, code) char *filename; char *func; int code; { fprintf (stderr, "dd: "); fprintf (stderr, "%s ", func); perror (filename); quit (code); } /* Print statistics and exit. */ void quit (code) int code; { fprintf (stderr, "%d+%d records in\n", r_full, r_partial); fprintf (stderr, "%d+%d records out\n", w_full, w_partial); if (r_truncate > 0) fprintf (stderr, "%d truncated records\n", r_truncate); exit (code); } /* malloc or die. */ char * xmalloc (n) int n; { char *p; p = malloc (n); if (p == NULL) { fprintf (stderr, "dd: virtual memory exhausted\n"); exit (3); } return p; } /* Report a fatal error. STRING is a printf format string and ARG is one arg for it. */ /* VARARGS1 */ void fatal (string, arg0, arg1) char *string, *arg0, *arg1; { fprintf (stderr, "dd: "); fprintf (stderr, string, arg0, arg1); fprintf (stderr, "\n"); exit (1); } /* Like fatal but also prints usage message. */ /* VARARGS1 */ void usage (string, arg0, arg1) char *string, *arg0, *arg1; { fprintf (stderr, "dd: "); fprintf (stderr, string, arg0, arg1); fprintf (stderr, "\n"); fprintf (stderr, "\ Usage: dd [if=file] [of=file]\n\ [ibs=n] [obs=n] [bs=n] [cbs=n] [skip=n] [seek=n] [count=n]\n\ [conv=ascii,ebcdic,ibm,block,unblock,lcase,ucase,swab,noerror,sync]\n\ n is a number of bytes, optionally followed by a modifier:\n\ b:512-byte blocks, k:kilobytes, w:2-byte words, xm:multiply by integer m\n"); exit (1); } usage (string, arg0, arg1) char *string, *arg0, *arg1; { fprintf (stderr, "dd: "); fprintf (stderr, string, arg0, arg1); fprintf (stderr, "\n"); fprintf (stderr, "\ Usage: dd [if=file] [of=file]\n\ [ibs=n] [obs=n] [bsfileutils/dirlib.c 644 473 0 6513 4702305045 7500 /* dirlib.c -- BSD compatible directory functions for System V Copyright (C) 1988, 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* mkdir and rmdir adapted from GNU tar. */ #ifdef NEED_MKDIR /* Make a directory. Compatible with the mkdir system call on 4.2BSD. Written by Robert Rother, Mariah Corporation, August 1985 (sdcsvax!rmr or rmr@uscd). If you want it, it's yours. Severely hacked over by John Gilmore to make a 4.2BSD compatible subroutine. 11Mar86; hoptoad!gnu Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir, subroutine didn't return EEXIST. It does now. */ #include #include #include int mkdir (dpath, dmode) char *dpath; int dmode; { extern int errno; int cpid, status; struct stat statbuf; if (stat (dpath, &statbuf) == 0) { errno = EEXIST; /* stat worked, so it already exists. */ return -1; } /* If stat fails for a reason other than non-existence, return error. */ if (errno != ENOENT) return -1; cpid = fork (); switch (cpid) { case -1: /* Error in fork. */ return -1; /* errno is set already. */ case 0: /* Child process. */ /* Cheap hack to set mode of new directory. Since this child process is going away anyway, we zap its umask. FIXME, this won't suffice to set SUID, SGID, etc. on this directory. */ status = umask (0); /* Get current umask. */ umask (status | (0777 & ~dmode)); /* Set for mkdir. */ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0); _exit (1); /* Can not exec /bin/mkdir. */ default: /* Parent process. */ while (cpid != wait (&status)) /* Wait for kid to finish. */ /* Do nothing. */ ; if (status & 0xFFFF) { errno = EIO; /* /bin/mkdir failed. */ return -1; } return 0; } } /* Remove a directory. Compatible with the rmdir system call on 4.2BSD. */ int rmdir (dpath) char *dpath; { extern int errno; int cpid, status; struct stat statbuf; if (stat (dpath, &statbuf) != 0) return -1; /* stat set errno. */ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { errno = ENOTDIR; return -1; } cpid = fork (); switch (cpid) { case -1: /* Error in fork. */ return -1; /* errno is set already. */ case 0: /* Child process. */ execl ("/bin/rmdir", "rmdir", dpath, (char *) 0); _exit (1); /* Can not exec /bin/rmdir. */ default: /* Parent process. */ while (cpid != wait (&status)) /* Wait for kid to finish. */ /* Do nothing. */ ; if (status & 0xFFFF) { errno = EIO; /* /bin/rmdir failed. */ return -1; } return 0; } } #endif cpid = fork (); switch (cpid) { case -1: /* Error in fork. */ return -1; /* errno is set already. */ case 0: /* Child process. */ execl ("/bin/rmfileutils/du.c 644 473 0 36705 4702305045 6671 /* du -- summarize disk usage Copyright (C) 1988, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Differences from the Unix du: * Doesn't simply ignore the names of regular files given as arguments when -a is given. * New option -l makes du count the size of all files, even if they have appeared already in another hard link. * New option -f makes du not cross file-system boundaries during the recursion. * New option -c to write a grand total of all of the arguments after all arguments have been processed. This can be used to find out the disk usage of a directory, with some files excluded. To do: * Simpler path-handling, with no more copying overhead. Yes, I will do it, but after cp... By tege@sics.se, Torbjorn Granlund. */ #include #include #include #include #include "system.h" #include "getopt.h" /* Initial number of entries in each hash table entry's table of inodes. */ #define INITIAL_HASH_MODULE 100 /* Initial number of entries in the inode hash table. */ #define INITIAL_ENTRY_TAB_SIZE 70 /* Initial size to allocate for `path'. */ #define INITIAL_PATH_SIZE 100 /* Hash structure for inode and device numbers. The separate entry structure makes it easier to rehash "in place". */ struct entry { ino_t ino; dev_t dev; struct entry *coll_link; }; /* Structure for a hash table for inode numbers. */ struct htab { unsigned modulus; /* Size of the `hash' pointer vector. */ struct entry *entry_tab; /* Pointer to dynamically growing vector. */ unsigned entry_tab_size; /* Size of current `entry_tab' allocation. */ unsigned first_free_entry; /* Index in `entry_tab'. */ struct entry *hash[1]; /* Vector of pointers in `entry_tab'. */ }; /* Structure for dynamically resizable strings. */ typedef struct { unsigned alloc; /* Size of allocation for the text. */ unsigned length; /* Length of the text currently. */ char *text; /* Pointer to the text. */ } *string, stringstruct; extern char *malloc (); extern char *realloc (); extern char *xmalloc (); extern char *xrealloc (); extern int errno; extern char *sys_errlist[]; extern int sys_nerr; extern void hash_init (); extern void hash_reset (); extern int hash_insert (); extern int hash_insert2 (); extern void str_init (); extern string str_copyc (); extern string str_concatc (); extern void str_trunc (); /* Name under which this program was invoked. */ char *prog_name; /* Booleans for option flags. */ /* If nonzero, display only a grand total for each argument. */ int opt_summarize_only = 0; /* If nonzero, display counts for all files, not just directories. */ int opt_all = 0; /* If nonzero, count each hard link of files with multiple links. */ int opt_count_all = 0; /* If nonzero, do not cross file-system boundaries. */ int opt_one_file_system = 0; /* If nonzero, print a grand total at the end. */ int opt_combined_arguments = 0; /* Accumulated path for file or directory being processed. */ string path; /* Pointer to hash structure, used by the hash routines. */ struct htab *htab; /* Globally used stat buffer. */ struct stat stat_buf; /* Structure containing long-named options available at command line, for use with getopt_long */ struct option long_options[] = { {"all", 0, &opt_all, 1}, {"total", 0, &opt_combined_arguments, 1}, {"one-file-system", 0, &opt_one_file_system, 1}, {"count-links", 0, &opt_count_all, 1}, {"summarize", 0, &opt_summarize_only, 1}, {NULL, 0, NULL, 0} }; void usage (reason) char *reason; { if (reason != NULL) fprintf (stderr, "%s: %s\n", prog_name, reason); fprintf (stderr, "\ Usage: %s [-acfls] [+all] [+total] [+one-file-system] [+count-links]\n\ [+summarize] [path...]\n", prog_name); exit (2); } /* Error handler. ERR is a short description of what operation failed, IN is the argument to the failed command or NULL. RET is the exit status, or 0 if the error wasn't fatal. */ void error (err, in, ret) char *err; char *in; int ret; { fprintf (stderr, "%s: ", prog_name); if (in != NULL) fprintf (stderr, "%s: ", in); fputs (err, stderr); if (errno) if (errno < sys_nerr) fprintf (stderr, ", %s", sys_errlist[errno]); else fprintf (stderr, ", error code %d", errno); fputs ("\n", stderr); fflush (stderr); if (ret) exit (ret); errno = 0; } void main (argc, argv) int argc; char *argv[]; { int c; int ind; prog_name = argv[0]; while ((c = getopt_long (argc, argv, "acfls", long_options, &ind)) != EOF) { switch (c) { case 0: /* Long option. */ break; case 'a': opt_all = 1; break; case 'c': opt_combined_arguments = 1; break; case 'f': opt_one_file_system = 1; break; case 'l': opt_count_all = 1; break; case 's': opt_summarize_only = 1; break; default: usage ((char *) 0); } } if (opt_all && opt_summarize_only) usage ("cannot both summarize and show all entries"); /* Initialize the hash structure for inode numbers. */ hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE); str_init (&path, INITIAL_PATH_SIZE); if (optind == argc) { str_copyc (path, "."); /* Initialize the hash structure for inode numbers. */ hash_reset (); /* Get the size of the current directory only. */ count_entry (".", 1, 0); } else { char wd[MAXPATHLEN]; char *arg; ino_t right_ino; /* Initial directory's inode. */ dev_t right_dev; /* Initial directory's device. */ int tot_size = 0; /* Grand total size of all args. */ if (getwd (wd) == NULL) error ("cannot get current directory", (char *) 0, 1); /* Remember the inode and device number of the current directory. */ stat (".", &stat_buf); right_ino = stat_buf.st_ino; right_dev = stat_buf.st_dev; do { int s; arg = argv[optind]; /* Delete final slash in the argument, unless the slash is alone. */ s = strlen (arg) - 1; if (s != 0) { if (arg[s] == '/') arg[s] = 0; str_copyc (path, arg); } else if (arg[0] == '/') str_trunc (path, 0);/* Null path for root directory. */ else str_copyc (path, arg); if (!opt_combined_arguments) hash_reset (); tot_size += count_entry (arg, 1, 0); /* Chdir if `count_entry' has changed the working directory. */ stat (".", &stat_buf); if (stat_buf.st_ino != right_ino || stat_buf.st_dev != right_dev) if (chdir (wd) < 0) error ("cannot change directory", ".", 1); optind++; } while (optind < argc); if (opt_combined_arguments) { printf (" %d\ttotal\n", blocks_to_kb (tot_size)); fflush (stdout); } } } /* Return size in blocks (512 bytes) of the file or directory in ENT. TOP is one for external calls, zero for recursive calls. LAST_DEV is the device that the parent dir of ENT is on. */ int count_entry (ent, top, last_dev) char *ent; int top; dev_t last_dev; { int size; if (lstat (ent, &stat_buf) < 0) { error ("cannot stat", path->text, 0); return 0; } if (!opt_count_all && stat_buf.st_nlink > 1 && hash_insert (stat_buf.st_ino, stat_buf.st_dev)) return 0; /* Have counted this already. */ size = ST_NBLOCKS (stat_buf); if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) { DIR *dirp; struct direct *dp; unsigned pathlen; dev_t dir_dev; char *name_space; char *name_p; dir_dev = stat_buf.st_dev; if (opt_one_file_system && !top && last_dev != dir_dev) return 0; /* Don't enter a new file system. */ if (chdir (ent) < 0) { error ("cannot change to directory", path->text, 0); return 0; } dirp = opendir ("."); if (dirp == NULL) { error ("cannot read directory", path->text, 0); chdir (".."); /* Try to return to previous directory. */ return 0; } /* Remember the current path. */ str_concatc (path, "/"); pathlen = path->length; /* Read all the names in the directory, before any subdirectory is read. */ name_space = (char *) alloca (stat_buf.st_size); name_p = name_space; while ((dp = readdir (dirp)) != NULL) { /* Skip "." and ".." (some NFS filesystems' directories lack them). */ if (dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { strcpy (name_p, dp->d_name); name_p += NLENGTH (dp) + 1; } } *name_p = 0; closedir (dirp); /* Now process the files in the directory. */ name_p = name_space; while (*name_p != 0) { str_concatc (path, name_p); size += count_entry (name_p, 0, dir_dev); str_trunc (path, pathlen); name_p += strlen (name_p) + 1; } chdir (".."); if (!opt_summarize_only || top) { printf ("%d\t%s\n", blocks_to_kb (size), path->text); fflush (stdout); } } else if (opt_all || top) { printf ("%d\t%s\n", blocks_to_kb (size), path->text); fflush (stdout); } return size; } int blocks_to_kb (blocks) int blocks; { #if BLKSIZE == 1024 return blocks; #else return (blocks + 1) / 2; #endif } /* Allocate space for the hash structures, and set the global variable `htab' to point to it. The initial hash module is specified in MODULUS, and the number of entries are specified in ENTRY_TAB_SIZE. (The hash structure will be rebuilt when ENTRY_TAB_SIZE entries have been inserted, and MODULUS and ENTRY_TAB_SIZE in the global `htab' will be doubled.) */ void hash_init (modulus, entry_tab_size) unsigned modulus; unsigned entry_tab_size; { struct htab *htab_r; htab_r = (struct htab *) xmalloc (sizeof (struct htab) + sizeof (struct entry *) * modulus); htab_r->entry_tab = (struct entry *) xmalloc (sizeof (struct entry) * entry_tab_size); htab_r->modulus = modulus; htab_r->entry_tab_size = entry_tab_size; htab = htab_r; hash_reset (); } /* Reset the hash structure in the global variable `htab' to contain no entries. */ void hash_reset () { int i; struct entry **p; htab->first_free_entry = 0; p = htab->hash; for (i = htab->modulus; i > 0; i--) *p++ = NULL; } /* Insert an item (inode INO and device DEV) in the hash structure in the global variable `htab', if an entry with the same data was not found already. Return zero if the item was inserted and non-zero if it wasn't. */ int hash_insert (ino, dev) ino_t ino; dev_t dev; { struct htab *htab_r = htab; /* Initially a copy of the global `htab'. */ if (htab_r->first_free_entry >= htab_r->entry_tab_size) { int i; struct entry *ep; unsigned modulus; unsigned entry_tab_size; /* Increase the number of hash entries, and re-hash the data. The method of shrimping and increasing is made to compactify the heap. If twice as much data would be allocated straightforwardly, we would never re-use a byte of memory. */ /* Let `htab' shrimp. Keep only the header, not the pointer vector. */ htab_r = (struct htab *) xrealloc ((char *) htab_r, sizeof (struct htab)); modulus = 2 * htab_r->modulus; entry_tab_size = 2 * htab_r->entry_tab_size; /* Increase the number of possible entries. */ htab_r->entry_tab = (struct entry *) xrealloc ((char *) htab_r->entry_tab, sizeof (struct entry) * entry_tab_size); /* Increase the size of htab again. */ htab_r = (struct htab *) xrealloc ((char *) htab_r, sizeof (struct htab) + sizeof (struct entry *) * modulus); htab_r->modulus = modulus; htab_r->entry_tab_size = entry_tab_size; htab = htab_r; i = htab_r->first_free_entry; /* Make the increased hash table empty. The entries are still available in htab->entry_tab. */ hash_reset (); /* Go through the entries and install them in the pointer vector htab->hash. The items are actually inserted in htab->entry_tab at the position where they already are. The htab->coll_link need however be updated. Could be made a little more efficient. */ for (ep = htab_r->entry_tab; i > 0; i--) { hash_insert2 (htab_r, ep->ino, ep->dev); ep++; } } return hash_insert2 (htab_r, ino, dev); } /* Insert INO and DEV in the hash structure HTAB, if not already present. Return zero if inserted and non-zero if it already existed. */ int hash_insert2 (htab, ino, dev) struct htab *htab; ino_t ino; dev_t dev; { struct entry **hp, *ep2, *ep; hp = &htab->hash[ino % htab->modulus]; ep2 = *hp; /* Collision? */ if (ep2 != NULL) { ep = ep2; /* Search for an entry with the same data. */ do { if (ep->ino == ino && ep->dev == dev) return 1; /* Found an entry with the same data. */ ep = ep->coll_link; } while (ep != NULL); /* Did not find it. */ } ep = *hp = &htab->entry_tab[htab->first_free_entry++]; ep->ino = ino; ep->dev = dev; ep->coll_link = ep2; /* `ep2' is NULL if no collision. */ return 0; } /* Initialize the struct string S1 to contain SIZE characters. */ void str_init (s1, size) string *s1; unsigned size; { string s; s = (string) xmalloc (sizeof (stringstruct)); s->text = xmalloc (size + 1); s->alloc = size; *s1 = s; } static void ensure_space (s, size) string s; unsigned size; { if (s->alloc < size) { s->text = xrealloc (s->text, size + 1); s->alloc = size; } } /* Assign the null-terminated C-string CSTR to S1. */ string str_copyc (s1, cstr) string s1; char *cstr; { unsigned l = strlen (cstr); ensure_space (s1, l); strcpy (s1->text, cstr); s1->length = l; return s1; } string str_concatc (s1, cstr) string s1; char *cstr; { unsigned l1 = s1->length; unsigned l2 = strlen (cstr); unsigned l = l1 + l2; ensure_space (s1, l); strcpy (s1->text + l1, cstr); s1->length = l; return s1; } /* Truncate the string S1 to have length LENGTH. */ void str_trunc (s1, length) string s1; unsigned length; { if (s1->length > length) { s1->text[length] = 0; s1->length = length; } } /* Allocate `n' bytes of memory dynamically, with error checking. */ char * xmalloc (n) unsigned n; { char *p; p = malloc (n); if (p == 0) error ("virtual memory exhausted", (char *) 0, 1); return p; } /* Change the size of an allocated block of memory `p' to `n' bytes, with error checking. If `p' is NULL, run xmalloc. If `n' is 0, run free and return NULL. */ char * xrealloc (p, n) char *p; unsigned n; { if (p == 0) return xmalloc (n); if (n == 0) { free (p); return 0; } p = realloc (p, n); if (p == 0) error ("virtual memory exhausted", (char *) 0, 1); return p; } if (p == 0) error ("virtual memory exhausted", (char *fileutils/filemode.c 644 473 0 11305 4702305045 10032 /* Examine the result of stat and make a string describing file modes. Copyright (C) 1985 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include /* filemodestring - set file attribute data *** WARNING! FILE STRUCTURE DEPENDENT *** Filemodestring converts the data in the st_mode field of file status block `s' to a 10 character attribute string, which it stores in the block that `a' points to. This attribute string is modelled after the string produced by the Berkeley ls. As usual under Unix, the elements of the string are numbered from 0. Their meanings are: 0 File type. 'd' for directory, 'c' for character special, 'b' for block special, 'm' for multiplex, 'l' for symbolic link, 's' for socket, 'p' for fifo, '-' for any other file type 1 'r' if the owner may read, '-' otherwise. 2 'w' if the owner may write, '-' otherwise. 3 'x' if the owner may execute, 's' if the file is set-user-id, '-' otherwise. 'S' if the file is set-user-id, but the execute bit isn't set. (sys v `feature' which helps to catch screw case.) 4 'r' if group members may read, '-' otherwise. 5 'w' if group members may write, '-' otherwise. 6 'x' if group members may execute, 's' if the file is set-group-id, '-' otherwise. 'S' if it is set-group-id but not executable. 7 'r' if any user may read, '-' otherwise. 8 'w' if any user may write, '-' otherwise. 9 'x' if any user may execute, 't' if the file is "sticky" (will be retained in swap space after execution), '-' otherwise. 'T' if the file is sticky but not executable. */ #define VOID void static char ftypelet (); static VOID rwx (), setst (); VOID filemodestring (s,a) struct stat *s; char *a; { a[0] = ftypelet (s); /* Aren't there symbolic names for these byte-fields? */ rwx ((s->st_mode & 0700) << 0, &(a[1])); rwx ((s->st_mode & 0070) << 3, &(a[4])); rwx ((s->st_mode & 0007) << 6, &(a[7])); setst (s->st_mode, a); } /* ftypelet - file type letter *** WARNING! FILE STRUCTURE DEPENDENT *** ftypelet accepts a file status block and returns a character code describing the type of the file. 'd' is returned for directories, 'b' for block special files, 'c' for character special files, 'm' for multiplexor files, 'l' for symbolic link, 's' for socket, 'p' for fifo, '-' for any other file type */ static char ftypelet(s) struct stat *s; { switch (s->st_mode & S_IFMT) { default: return '-'; case S_IFDIR: return 'd'; #ifdef S_IFLNK case S_IFLNK: return 'l'; #endif #ifdef S_IFCHR case S_IFCHR: return 'c'; #endif #ifdef S_IFBLK case S_IFBLK: return 'b'; #endif #ifdef S_IFMPC /* These do not seem to exist */ case S_IFMPC: case S_IFMPB: return 'm'; #endif #ifdef S_IFSOCK case S_IFSOCK: return 's'; #endif #ifdef S_IFIFO #if S_IFIFO != S_IFSOCK case S_IFIFO: return 'p'; #endif #endif #ifdef S_IFNWK /* hp-ux hack */ case S_IFNWK: return 'n'; #endif } } /* rwx - look at read, write, and execute bits and set character flags accordingly *** WARNING! FILE STRUCTURE DEPENDENT *** */ static VOID rwx (bits, chars) unsigned short bits; char chars[]; { chars[0] = (bits & S_IREAD) ? 'r' : '-'; chars[1] = (bits & S_IWRITE) ? 'w' : '-'; chars[2] = (bits & S_IEXEC) ? 'x' : '-'; } /* setst - set s & t flags in a file attributes string */ /* *** WARNING! FILE STRUCTURE DEPENDENT *** */ static VOID setst (bits, chars) unsigned short bits; char chars[]; { #ifdef S_ISUID if (bits & S_ISUID) { if (chars[3] != 'x') /* Screw case: set-uid, but not executable. */ chars[3] = 'S'; else chars[3] = 's'; } #endif #ifdef S_ISGID if (bits & S_ISGID) { if (chars[6] != 'x') /* Screw case: set-gid, but not executable. */ chars[6] = 'S'; else chars[6] = 's'; } #endif #ifdef S_ISVTX if (bits & S_ISVTX) { if (chars[9] != 'x') /* Screw case: sticky, but not executable. */ chars[9] = 'T'; else chars[9] = 't'; } #endif } ID) { if (chars[3] != 'x') /* Screw case: set-uid, but not executable. */ chars[3] = 'S'; else chars[3] = 's'; } #endif #ifdef S_ISGID if (bits & S_ISGID) { if (chars[6] != 'x') /* Screw case: set-gid, but not executable. */ chars[6] = 'S'; else chars[6] = fileutils/getopt.c 644 473 0 36355 4702305046 7565 /* Getopt for GNU. Copyright (C) 1987, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of `argv' so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable _POSIX_OPTION_ORDER disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include /* If compiled with GNU C, use the built-in alloca */ #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not __GNUC__ */ #ifdef sparc #include #else char *alloca (); #endif #endif /* not __GNUC__ */ #ifdef USG #define bcopy(s, d, l) memcpy((d), (s), (l)) #define index strchr #endif char *getenv (); char *index (); char *malloc (); /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = 0; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable _POSIX_OPTION_ORDER is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options. Stop option processing when the first non-option is seen. This is what Unix does. PERMUTE is the default. We permute the contents of `argv' as we scan, so that eventually all the options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code zero. Using `-' as the first character of the list of option characters requests this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Describe the long-named options requested by the application. _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is 1 if the option takes an argument, 2 if it takes an optional argument. */ struct option { char *name; int has_arg; int *flag; int val; }; struct option *_getopt_long_options; /* Name of long-named option actually found. */ char *_getopt_option_name; /* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found. Only valid when a long-named option was found. */ int option_index; /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (argv) char **argv; { int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); char **temp = (char **) alloca (nonopts_size); /* Interchange the two blocks of data in argv. */ bcopy (&argv[first_nonopt], temp, nonopts_size); bcopy (&argv[last_nonopt], &argv[first_nonopt], (optind - last_nonopt) * sizeof (char *)); bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size); /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-', it requests a different method of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER, above. Long-named options begin with `+' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. `getopt' returns 0 when it finds a long-named option. */ int getopt (argc, argv, optstring) int argc; char **argv; char *optstring; { optarg = 0; /* Initialize the internal data when the first call is made. Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ if (optind == 0) { first_nonopt = last_nonopt = optind = 1; nextchar = 0; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') ordering = RETURN_IN_ORDER; else if (getenv ("_POSIX_OPTION_ORDER") != 0) ordering = REQUIRE_ORDER; else ordering = PERMUTE; } if (nextchar == 0 || *nextchar == 0) { if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange (argv); else if (last_nonopt != optind) first_nonopt = optind; /* Now skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == 0) && (_getopt_long_options == 0 || argv[optind][0] != '+' || argv[optind][1] == 0)) optind++; last_nonopt = optind; } /* Special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange (argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == 0) && (_getopt_long_options == 0 || argv[optind][0] != '+' || argv[optind][1] == 0)) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 0; } /* We have found another option-ARGV-element. Start decoding its characters. */ nextchar = argv[optind] + 1; } if (_getopt_long_options != 0 && argv[optind][0] == '+') { struct option *p; char *s = nextchar; int exact = 0; int ambig = 0; struct option *pfound = 0; int indfound; while (*s && *s != '=') s++; /* Test all options for either exact match or abbreviated matches. */ for (p = _getopt_long_options, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, s - nextchar)) { if (s - nextchar == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == 0) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second nonexact match found. */ ambig = 1; } if (ambig && !exact) { fprintf (stderr, "%s: option `+%s' is ambiguous\n", argv[0], nextchar); nextchar += strlen (nextchar); return '?'; } if (pfound != 0) { option_index = indfound; optind++; if (*s) { if (pfound->has_arg > 0) optarg = s + 1; else { fprintf (stderr, "%s: option `+%s' doesn't allow an argument\n", argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg) { if (optind < argc) optarg = argv[optind++]; else if (pfound->has_arg != 2) { fprintf (stderr, "%s: option `+%s' requires an argument\n", argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } _getopt_option_name = pfound->name; nextchar += strlen (nextchar); if (pfound->flag) *(pfound->flag) = pfound->val; return 0; } if (opterr != 0) fprintf (stderr, "%s: unrecognized option `+%s'\n", argv[0], nextchar); nextchar += strlen (nextchar); return '?'; } /* Look at and handle the next option-character. */ { char c = *nextchar++; char *temp = index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == 0) optind++; if (temp == 0 || c == ':') { if (opterr != 0) { if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); } return '?'; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != 0) { optarg = nextchar; optind++; } else optarg = 0; nextchar = 0; } else { /* This is an option that requires an argument. */ if (*nextchar != 0) { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr != 0) fprintf (stderr, "%s: no argument for `-%c' option\n", argv[0], c); c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = 0; } } return c; } } #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { char c; int digit_optind = 0; while (1) { int this_option_optind = optind; if ((c = getopt (argc, argv, "abc:d:0123456789")) == EOF) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } return 0; } #endif /* TEST */ tind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returnfileutils/getopt1.c 644 473 0 5226 4702305046 7617 /* Getopt for GNU. Copyright (C) 1987, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "getopt.h" int getopt_long (argc, argv, options, long_options, opt_index) int argc; char **argv; char *options; struct option *long_options; int *opt_index; { int val; _getopt_long_options = long_options; val = getopt (argc, argv, options); if (val == 0) *opt_index = option_index; return val; } #ifdef TEST #include int main (argc, argv) int argc; char **argv; { char c; int digit_optind = 0; while (1) { int this_option_optind = optind; char *name = '\0'; int option_index = 0; static struct option long_options[] = {{ "add", 1, 0, 0 }, { "append", 0, 0, 0 }, { "delete", 1, 0, 0 }, { "verbose", 0, 0, 0 }, { "create", 0, 0, 0 }, { "file", 1, 0, 0 }, { 0, 0, 0, 0}}; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == EOF) break; switch (c) { case 0: printf ("option %s", (long_options[option_index]).name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } return 0; } #endif /* TEST */ , c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("nofileutils/glob.c 644 473 0 32155 4702305046 7200 /* File-name wildcard pattern matching for GNU. Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* To whomever it may concern: I have never seen the code which most Unix programs use to perform this function. I wrote this from scratch based on specifications for the pattern matching. */ #include #ifdef USGr3 #include #define direct dirent #define D_NAMLEN(d) strlen((d)->d_name) #else /* not USGr3 */ #define D_NAMLEN(d) ((d)->d_namlen) # ifdef USG # ifdef SYSNDIR #include # else #include "ndir.h" /* Get ndir.h from the Emacs distribution. */ # endif /* not SYSNDIR */ # else /* not USG */ #include # endif /* USG */ #endif /* USGr3 */ #ifdef USG #include #include #define bcopy(s, d, n) memcpy ((d), (s), (n)) #define rindex strrchr struct passwd *getpwent(), *getpwuid(), *getpwnam(); extern char *memcpy (); #else /* not USG */ #include extern void bcopy (); #endif /* not USG */ #ifdef __GNUC__ #define alloca(n) __builtin_alloca (n) #else /* Not GCC. */ #ifdef sparc #include #else /* Not sparc. */ extern char *alloca (); #endif /* sparc. */ #endif /* GCC. */ #include extern char *malloc (), *realloc (); extern void free (); #ifndef NULL #define NULL 0 #endif /* Zero if * matches .*. */ int noglob_dot_filenames = 1; /* Nonzero if ~ and ~USER are expanded by glob_filename. */ int glob_tilde = 0; static int glob_match_after_star (); /* Return nonzero if PATTERN has any special globbing chars in it. */ int glob_pattern_p (pattern) char *pattern; { register char *p = pattern; register char c; while ((c = *p++)) { switch (c) { case '?': case '[': case '*': return 1; case '\\': if (*p++ == 0) return 0; default: ; } } return 0; } /* Match the pattern PATTERN against the string TEXT; return 1 if it matches, 0 otherwise. A match means the entire string TEXT is used up in matching. In the pattern string, `*' matches any sequence of characters, `?' matches any character, [SET] matches any character in the specified set, [^SET] matches any character not in the specified set. A set is composed of characters or ranges; a range looks like character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the set of characters allowed in C identifiers. Any other character in the pattern must be matched exactly. To suppress the special syntactic significance of any of `[]*?^-\', and match the character exactly, precede it with a `\'. If DOT_SPECIAL is nonzero, `*' and `?' do not match `.' at the beginning of TEXT. */ int glob_match (pattern, text, dot_special) char *pattern, *text; int dot_special; { register char *p = pattern, *t = text; register char c; while ((c = *p++)) { switch (c) { case '?': if (*t == 0 || (dot_special && t == text && *t == '.')) return 0; else ++t; break; case '\\': if (*p++ != *t++) return 0; break; case '*': if (dot_special && t == text && *t == '.') return 0; return glob_match_after_star (p, t); case '[': { register char c1 = *t++; register int invert = (*p == '^'); if (invert) p++; c = *p++; while (1) { register char cstart = c, cend = c; if (c == '\\') { cstart = *p++; cend = cstart; } if (!c) return (0); c = *p++; if (c == '-') { cend = *p++; if (cend == '\\') cend = *p++; if (!cend) return (0); c = *p++; } if (c1 >= cstart && c1 <= cend) goto match; if (c == ']') break; } if (!invert) return 0; break; match: /* Skip the rest of the [...] construct that already matched. */ while (c != ']') { if (!c || !(c = *p++)) return (0); if (c == '\\') p++; } if (invert) return 0; break; } default: if (c != *t++) return 0; } } if (*t) return 0; return 1; } /* Like glob_match, but match PATTERN against any final segment of TEXT. */ static int glob_match_after_star (pattern, text) char *pattern, *text; { register char *p = pattern, *t = text; register char c, c1; while ((c = *p++) == '?' || c == '*') { if (c == '?' && *t++ == 0) return 0; } if (c == 0) return 1; if (c == '\\') c1 = *p; else c1 = c; for (;;) { if ((c == '[' || *t == c1) && glob_match (p - 1, t, 0)) return 1; if (*t++ == 0) return 0; } } /* Return a vector of names of files in directory DIR whose names match glob pattern PAT. The names are not in any particular order. Wildcards at the beginning of PAT do not match an initial period. The vector is terminated by an element that is a null pointer. To free the space allocated, first free the vector's elements, then free the vector. Return 0 if cannot get enough memory to hold the pointer and the names. Return -1 if cannot access directory DIR. Look in errno for more information. */ char ** glob_vector (pat, dir) char *pat; char *dir; { struct globval { struct globval *next; char *name; }; DIR *d; register struct direct *dp; struct globval *lastlink; register struct globval *nextlink; register char *nextname; int count; int lose; register char **name_vector; register int i; if (!(d = opendir (dir))) return (char **) -1; lastlink = 0; count = 0; lose = 0; /* Scan the directory, finding all names that match. For each name that matches, allocate a struct globval on the stack and store the name in it. Chain those structs together; lastlink is the front of the chain. */ /* Loop reading blocks */ while (1) { dp = readdir (d); if (!dp) break; if (dp->d_ino && glob_match (pat, dp->d_name, noglob_dot_filenames)) { nextlink = (struct globval *) alloca (sizeof (struct globval)); nextlink->next = lastlink; nextname = (char *) malloc (D_NAMLEN(dp) + 1); if (!nextname) { lose = 1; break; } lastlink = nextlink; nextlink->name = nextname; bcopy (dp->d_name, nextname, D_NAMLEN(dp) + 1); count++; } } closedir (d); name_vector = (char **) malloc ((count + 1) * sizeof (char *)); /* Have we run out of memory? */ if (!name_vector || lose) { /* Here free the strings we have got */ while (lastlink) { free (lastlink->name); lastlink = lastlink->next; } return 0; } /* Copy the name pointers from the linked list into the vector */ for (i = 0; i < count; i++) { name_vector[i] = lastlink->name; lastlink = lastlink->next; } name_vector[count] = 0; return name_vector; } /* Return a new array which is the concatenation of each string in ARRAY to DIR. */ static char ** glob_dir_to_array (dir, array) char *dir, **array; { register int i, l; int add_slash = 0; char **result; l = strlen (dir); if (!l) return (array); if (dir[l - 1] != '/') add_slash++; for (i = 0; array[i]; i++); result = (char **)malloc ((1 + i) * sizeof (char *)); if (!result) return (result); for (i = 0; array[i]; i++) { result[i] = (char *)malloc (1 + l + add_slash + strlen (array[i])); if (!result[i]) return (char **)NULL; strcpy (result[i], dir); if (add_slash) strcat (result[i], "/"); strcat (result[i], array[i]); } result[i] = (char *)NULL; /* Free the input array. */ for (i = 0; array[i]; i++) free (array[i]); free (array); return (result); } /* Do globbing on PATHNAME. Return an array of pathnames that match, marking the end of the array with a null-pointer as an element. If no pathnames match, then the array is empty (first element is null). If there isn't enough memory, then return NULL. If a file system error occurs, return -1; `errno' has the error code. Wildcards at the beginning of PAT, or following a slash, do not match an initial period. */ char ** glob_filename (pathname) char *pathname; { char **result; unsigned int result_size; char *directory_name, *filename; unsigned int directory_len; result = (char **) malloc (sizeof (char *)); result_size = 1; if (result == NULL) return NULL; result[0] = NULL; /* Find the filename. */ filename = rindex (pathname, '/'); if (filename == 0) { filename = pathname; directory_name = ""; directory_len = 0; } else { directory_len = filename - pathname; directory_name = (char *) alloca (directory_len + 1); bcopy (pathname, directory_name, directory_len); directory_name[directory_len] = '\0'; ++filename; } if (glob_tilde && *pathname == '~') { if (directory_len == 0) { filename = directory_name; directory_name = pathname; directory_len = strlen (directory_name); } if (directory_len == 1) { extern char *getenv (); static char *home_directory = 0; static unsigned int home_len; if (home_directory == 0) { home_directory = getenv ("HOME"); if (home_directory == NULL) { home_directory = ""; home_len = 0; } else home_len = strlen (home_directory); } directory_name = home_directory; directory_len = home_len; } else { struct passwd *pwent = getpwnam (directory_name + 1); if (pwent == 0) { directory_name = ""; directory_len = 0; } else { directory_name = pwent->pw_dir; directory_len = strlen (directory_name); } } } else if (glob_pattern_p (directory_name)) { /* If directory_name contains globbing characters, then we have to expand the previous levels. Just recurse. */ char **directories; register unsigned int i; if (directory_name[directory_len - 1] == '/') directory_name[directory_len - 1] = '\0'; directories = glob_filename (directory_name); if (directories == NULL) goto memory_error; else if ((int) directories == -1) return (char **) -1; else if (*directories == NULL) { free ((char *) directories); return (char **) -1; } /* We have successfully globbed the preceding directory name. For each name in DIRECTORIES, call glob_vector on it and FILENAME. Concatenate the results together. */ for (i = 0; directories[i] != NULL; ++i) { char **temp_results = glob_vector (filename, directories[i]); if (temp_results == NULL) goto memory_error; else if ((int) temp_results == -1) /* This filename is probably not a directory. Ignore it. */ ; else { char **array = glob_dir_to_array (directories[i], temp_results); register unsigned int l = 0; while (array[l] != NULL) ++l; result = (char **) realloc (result, (result_size + 1) * sizeof (char *)); if (result == NULL) goto memory_error; for (l = 0; array[l] != NULL; ++l) result[result_size++ - 1] = array[l]; result[result_size - 1] = NULL; free ((char *) array); } } /* Free the directories. */ for (i = 0; directories[i]; i++) free (directories[i]); free ((char *) directories); return result; } if (*filename == '\0') { /* If there is only a directory name, return it. */ result = (char **) realloc ((char *) result, 2 * sizeof (char *)); if (result == NULL) return NULL; result[0] = (char *) malloc (directory_len + 1); if (result[0] == NULL) goto memory_error; bcopy (directory_name, result[0], directory_len + 1); result[1] = NULL; return result; } else { /* Otherwise, just return what glob_vector returns appended to the directory name. */ char **temp_results = glob_vector (filename, (directory_len == 0 ? "." : directory_name)); if (temp_results == NULL || (int) temp_results == -1) return temp_results; return glob_dir_to_array (directory_name, temp_results); } memory_error:; if (result != NULL) { register unsigned int i; for (i = 0; result[i] != NULL; ++i) free (result[i]); free ((char *) result); } return NULL; } #ifdef TEST main (argc, argv) int argc; char **argv; { char **value; int i, index = 1; while (index < argc) { value = glob_filename (argv[index]); if ((int) value == 0) printf ("Memory exhausted.\n"); else if ((int) value == -1) perror (argv[index]); else for (i = 0; value[i]; i++) printf ("%s\n", value[i]); index++; } return 0; } #endif /* TEST */ esult[i] != NULL; ++i) free (result[i]); free ((char *) result); } return NULL; } #ifdef TEST main (argc, argv) int argc; char **argv; { char **value; int i, index = 1; while (index < argc) { value = glob_filename (argv[index]); if ((int) value == 0) printf ("Memory exhausted.\n"); else if ((int) value == -1) perror (argv[index]); else fileutils/head.c 644 473 0 17051 4702305047 7155 /* head -- output first part of file(s) Copyright (C) 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Usage: head [-n #] [-lbcqv] [+number #] [+lines] [+blocks] [+chars] [+quiet] [+silent] [+verbose] [file...] head [-#lbcqv] [file...] Options: -n, +number # Print first # items (default 10). -l, +lines Head by lines (the default). -b, +blocks Head by 512-byte blocks. -c, +chars Head by characters. -q, +quiet, +silent Never print filename headers. -v, +verbose Always print filename headers. Reads from standard input if no files are given or when a filename of ``-'' is encountered. By default, filename headers are printed only if more than one file is given. Compile with -DUSG under System V. David MacKenzie */ #include #include #include #define ISDIGIT(c) (isascii (c) && isdigit (c)) #include "getopt.h" #include "system.h" /* Number of lines/chars/blocks to head. */ #define DEFAULT_NUMBER 10 #define BLOCKSIZE 512 /* Size of atomic reads. */ #define BUFSIZE (BLOCKSIZE*8) /* Masks for the operation mode. If neither CHARS nor BLOCKS is set, head operates by lines. */ #define CHARS 1 /* Head in chars. */ #define BLOCKS 2 /* Head in blocks. */ #define HEADERS 4 /* Write filename headers. */ /* When to print the filename banners. */ enum header_mode { multiple_files, always, never }; char *rindex (); char *basename (); void fatal_perror (); void head (); void head_chars (); void head_file (); void head_lines (); void nonfatal_perror (); void usage (); void write_header (); void xwrite (); /* The base of the name this program was run with. */ char *program_name; /* The exit status; 1 if an error occurred, 0 otherwise. */ int errors; struct option long_options[] = { {"number", 1, NULL, 'n'}, {"lines", 0, NULL, 'l'}, {"blocks", 0, NULL, 'b'}, {"chars", 0, NULL, 'c'}, {"quiet", 0, NULL, 'q'}, {"silent", 0, NULL, 'q'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char **argv; { enum header_mode header_mode = multiple_files; int mode = 0; /* Flags. */ int number = -1; /* Number of items to print (-1 if undef.). */ int c; /* Option character. */ int longind; /* Index in `long_options' of option found. */ program_name = argv[0] = basename (argv[0]); errors = 0; if (argc > 1 && argv[1][0] == '-' && ISDIGIT (argv[1][1])) { /* Old option syntax; a dash, one or more digits, and one or more option letters. Move past the number. */ for (number = 0, ++argv[1]; ISDIGIT (*argv[1]); ++argv[1]) number = number * 10 + *argv[1] - '0'; /* Parse any appended option letters with getopt. */ if (*argv[1]) *--argv[1] = '-'; else argv[1] = "-l"; } while ((c = getopt_long (argc, argv, "n:lbcqv", long_options, &longind)) != EOF) { if (c == 0) c = long_options[longind].val; switch (c) { case 'n': number = atou (optarg); if (number == -1) { fprintf (stderr, "%s: invalid number `%s'\n", program_name, optarg); exit (1); } break; case 'l': mode &= ~(CHARS | BLOCKS); break; case 'b': mode |= BLOCKS; mode &= ~CHARS; break; case 'c': mode |= CHARS; mode &= ~BLOCKS; break; case 'q': header_mode = never; break; case 'v': header_mode = always; break; default: usage (); } } if (number == -1) number = DEFAULT_NUMBER; if (mode & BLOCKS) number *= BLOCKSIZE; if (header_mode == always || header_mode == multiple_files && optind < argc - 1) mode |= HEADERS; if (optind == argc) head_file ("-", mode, number); for (; optind < argc; ++optind) head_file (argv[optind], mode, number); exit (errors); } void head_file (filename, mode, number) char *filename; int mode; int number; { int fd; if (!strcmp (filename, "-")) { filename = "standard input"; if (mode & HEADERS) write_header (filename); head (filename, 0, mode, number); } else { fd = open (filename, O_RDONLY); if (fd == -1) nonfatal_perror ("cannot open", filename); else { if (mode & HEADERS) write_header (filename); head (filename, fd, mode, number); close (fd); } } } void write_header (filename) char *filename; { static int first_file = 1; if (first_file) { xwrite (1, "==> ", 4); first_file = 0; } else xwrite (1, "\n==> ", 5); xwrite (1, filename, strlen (filename)); xwrite (1, " <==\n", 5); } void head (filename, fd, mode, number) char *filename; int fd; int mode; int number; { if (mode & (CHARS | BLOCKS)) head_chars (filename, fd, number); else head_lines (filename, fd, number); } void head_chars (filename, fd, chars_to_write) char *filename; int fd; int chars_to_write; { char buffer[BUFSIZE]; int chars_read; while (chars_to_write) { chars_read = read (fd, buffer, BUFSIZE); if (chars_read == -1) nonfatal_perror ("error reading", filename); if (chars_read <= 0) break; if (chars_read > chars_to_write) chars_read = chars_to_write; xwrite (1, buffer, chars_read); chars_to_write -= chars_read; } } void head_lines (filename, fd, lines_to_write) char *filename; int fd; int lines_to_write; { char buffer[BUFSIZE]; int chars_read; int chars_to_write; while (lines_to_write) { chars_read = read (fd, buffer, BUFSIZE); if (chars_read == -1) nonfatal_perror ("error reading", filename); if (chars_read <= 0) break; chars_to_write = 0; while (chars_to_write < chars_read) if (buffer[chars_to_write++] == '\n' && --lines_to_write == 0) break; xwrite (1, buffer, chars_to_write); } } /* Write plus error check. */ void xwrite (fd, buffer, count) int fd; int count; char *buffer; { fd = write (fd, buffer, count); if (fd != count) fatal_perror ("write", "error"); } /* Convert `str', a string of ASCII digits, into an unsigned integer. Return -1 if `str' does not represent a valid unsigned integer. */ int atou (str) char *str; { int value; for (value = 0; ISDIGIT (*str); ++str) value = value * 10 + *str - '0'; return *str ? -1 : value; } void nonfatal_perror (s1, s2) char *s1, *s2; { fprintf (stderr, "%s: %s ", program_name, s1); perror (s2); errors = 1; } void fatal_perror (s1, s2) char *s1, *s2; { nonfatal_perror (s1, s2); exit (1); } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-n #] [-lbcqv] [+number #] [+lines] [+blocks] [+chars]\n\ [+quiet] [+silent] [+verbose] [file...]\n\ \n\ %s [-#lbcqv] [file...]\n", program_name, program_name); exit (1); } void fatal_perror (s1, s2) char *s1, *s2; { nonfatal_perror (s1, s2); exit (1); } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-n #] [-lbcqv] [+number #] [+lines] [+blocks] [+chars]\n\ [+quiet] [+silent] [+verbose] [file...]\n\ \n\ %s [-#lbcqv] [file...]\n", prfileutils/ln.c 644 473 0 14176 4702305047 6672 /* `ln' program to create links among files. Copyright (C) 1986, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ln. Usage is either `ln file1 file2' or `ln file1 file2 ... destdir'. In either case, if the `-s' switch is specified, ln makes symbolic links instead of hard links. In the first form, `file1' is the existing file and `file2' is the name under which the link is to be created. If `file2' is omitted, the link is made in the current directory with the same name as the last component of `file1'. In the second form, the `fileN's are existing files and destdir is a directory in which to create the links. The links all have the same names as the last component of the linked-to files. This shares one problem with the 4.2BSD ln which is nearly impossible to remove in the general case: if you make symbolic links to multiple files where the target is a directory, you can lose. For example: % ln -s foo bar baz subdir % cat subdir/foo cat: subdir/foo: Too many levels of symbolic links % ls -l subdir total 3 -rw-r--r-- 1 mouse 3 Apr 10 17:46 foo -> foo -rw-r--r-- 1 mouse 3 Apr 16 05:08 bar -> bar -rw-r--r-- 1 mouse 3 Apr 19 03:34 baz -> baz Author Mike Parker */ #include #include #include "system.h" #include "getopt.h" char *rindex (); int link (); #ifdef S_IFLNK int symlink (); #endif char *basename (); void usage (); /* A pointer to the function used to make links. This will point to either `link' or `symlink'. */ int (*linkfunc) (); /* If nonzero, make symbolic links; otherwise, make hard links. */ int symbolic_link; /* If nonzero, ask the user before removing existing files. */ int interactive; /* If nonzero, remove existing files unconditionally. */ int force; /* If nonzero, list each file as it is moved. */ int verbose; /* The name by which the program was run, for error messages. */ char *program; static struct option long_options[] = { {"force", 0, &force, 1}, {"interactive", 0, &interactive, 1}, {"symbolic", 0, &symbolic_link, 1}, {"verbose", 0, &verbose, 1}, {NULL, 0, 0, 0} }; int main (argc, argv) int argc; char **argv; { int errors; int c; int ind = 0; program = argv[0] = basename (argv[0]); linkfunc = link; symbolic_link = force = interactive = verbose = 0; errors = 0; while ((c = getopt_long (argc, argv, "fisv", long_options, &ind)) != EOF) { switch (c) { case 0: /* Long-named option. */ break; case 'f': force = 1; break; case 'i': interactive = 1; break; case 's': symbolic_link = 1; break; case 'v': verbose = 1; break; default: usage (); break; } } #ifdef S_IFLNK if (symbolic_link) linkfunc = symlink; #endif if (optind == argc) usage (); else if (optind == argc - 1) errors = do_link (argv[optind], "."); else if (optind == argc - 2) errors = do_link (argv[optind], argv[optind + 1]); else { char *to; to = argv[argc - 1]; if (!isdir (to)) { fprintf (stderr, "%s: target must be a directory for multiple files\n", program); exit (1); } for (; optind < argc - 1; ++optind) errors += do_link (argv[optind], to); } exit (errors != 0); } /* Make a link `new' to existing file `old'. `new' may be a directory. Return 1 if there is an error, otherwise 0. */ int do_link (old, new) char *old; char *new; { struct stat stats; if (isdir (new)) { /* Target is a directory; build the full filename. */ char *new_new; char *old_base; old_base = basename (old); new_new = (char *) alloca (strlen (old_base) + 1 + strlen (new) + 1); sprintf (new_new, "%s/%s", new, old_base); new = new_new; } if (verbose) printf (" %s -> %s\n", old, new); if (force) unlink (new); else if (interactive && lstat (new, &stats) == 0) { if (!confirm (new)) return 0; else unlink (new); } if ((*linkfunc) (old, new) < 0) { fprintf (stderr, "%s: cannot %slink %s to ", #ifdef S_IFLNK program, linkfunc == symlink ? "symbolic " : "", old); #else program, "", old); #endif perror (new); return 1; } return 0; } /* Return 1 if the user gives permission to remove `file', 0 if not. */ int confirm (file) char *file; { int c; int rv; printf ("Remove `%s'? ", file); fflush (stdout); c = getchar (); rv = (c == 'y') || (c == 'Y'); while (c != '\n') c = getchar (); return rv; } /* Return 1 if `file' is a directory, otherwise 0. */ int isdir (file) char *file; { struct stat stats; return stat (file, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR; } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { #ifdef S_IFLNK fprintf (stderr, "\ Usage: %s [-fisv] [+force] [+interactive] [+symbolic] [+verbose] path\n\ %s [-fisv] [+force] [+interactive] [+symbolic] [+verbose] path newpath\n\ %s [-fisv] [+force] [+interactive] [+symbolic] [+verbose] path... dir\n", program, program, program); #else fprintf (stderr, "\ Usage: %s [-fiv] [+force] [+interactive] [+verbose] path\n\ %s [-fiv] [+force] [+interactive] [+verbose] path newpath\n\ %s [-fiv] [+force] [+interactive] [+verbose] path... dir\n", program, program, program); #endif exit (1); } eractive] [+symbolic] [+verbose] path\n\ %s [-fisv] [+force] [+interactive] [+symbolic] [+verbose] path newpath\n\ %s [-fisv] [+force] [+interactive] [+symbolic] [+verbose] path... dir\n", program, program, program); #else fprintf (stderr, "\ Usage: %s [-fiv] [+force] [+interactive] [+verbose] path\n\ %s [-fiv] [+force] [+interactive] [+verbose] path newpatfileutils/ls.c 644 473 0 115715 4702305050 6712 /* `dir', `vdir' and `ls' directory listing programs for GNU. Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* If the macro MULTI_COL is defined, the multi-column format is the default regardless of the type of output device. This is for the `dir' program. If the macro LONG_FORMAT is defined, the long format is the default regardless of the type of output device. This is for the `vdir' program. If neither is defined, the output format depends on whether the output device is a terminal. This is for the `ls' program, which exists for compatibility with Unix. */ /* Written by Richard Stallman and David MacKenzie. */ #include #include #include #include #include #include "getopt.h" #include "system.h" /* Extract the number of blocks from a `struct stat *'. */ #define nblocks(stat) \ (kilobyte_blocks ? (ST_NBLOCKS (stat) + 1) / 2 : ST_NBLOCKS (stat)) char *ctime (); char *getenv (); char *malloc (); char *realloc (); int glob_match (); struct group *getgrgid (); struct passwd *getpwuid (); void filemodestring (); char *basename (); char *copystring (); char *getgroup (); char *getuser (); char *make_link_path (); char *xmalloc (); char *xrealloc (); int compare_atime (); int rev_cmp_atime (); int compare_ctime (); int rev_cmp_ctime (); int compare_mtime (); int rev_cmp_mtime (); int compare_size (); int rev_cmp_size (); int compare_name (); int rev_cmp_name (); int decode_switches (); int file_interesting (); int gobble_file (); int is_not_dot_or_dotdot (); int length_of_file_name_and_frills (); void add_ignore_pattern (); void attach (); void clear_files (); void error (); void extract_dirs_from_files (); void fatal (); void get_link_name (); void indent (); void invalid_arg (); void perror_with_name (); void print_current_files (); void print_dir (); void print_file_name_and_frills (); void print_horizontal (); void print_long_format (); void print_many_per_line (); void print_name_with_quoting (); void print_type_indicator (); void print_with_commas (); void queue_directory (); void sort_files (); void usage (); extern int errno; extern int sys_nerr; extern char *sys_errlist[]; enum filetype { symbolic_link, directory, arg_directory, /* Directory given as command line arg. */ normal /* All others. */ }; struct file { /* The file name. */ char *name; struct stat stat; /* For symbolic link, name of the file linked to, otherwise zero. */ char *linkname; /* For symbolic link and long listing, st_mode of file linked to, otherwise zero. */ unsigned int linkmode; enum filetype filetype; }; /* The table of files in the current directory: `files' points to a vector of `struct file', one per file. `nfiles' is the number of elements space has been allocated for. `files_index' is the number actually in use. */ /* Address of block containing the files that are described. */ struct file *files; /* Length of block that `files' points to, measured in files. */ int nfiles; /* Index of first unused in `files'. */ int files_index; /* Record of one pending directory waiting to be listed. */ struct pending { char *name; /* If the directory is actually the file pointed to by a symbolic link we were told to list, `realname' will contain the name of the symbolic link, otherwise zero. */ char *realname; struct pending *next; }; struct pending *pending_dirs; /* Current time (seconds since 1970). When we are printing a file's time, include the year if it is more than 10 months before this time. */ long current_time; /* The number of digits to use for block sizes. 4, or more if needed for bigger numbers. */ int block_size_size; /* The name the program was run with, stripped of any leading path. */ char *program_name; /* Option flags */ /* long_format for lots of info, one per line. one_per_line for just names, one per line. many_per_line for just names, many per line, sorted vertically. horizontal for just names, many per line, sorted horizontally. with_commas for just names, many per line, separated by commas. -l, -1, -C, -x and -m control this parameter. */ enum format { long_format, /* -l */ one_per_line, /* -1 */ many_per_line, /* -C */ horizontal, /* -x */ with_commas /* -m */ }; enum format format; /* Type of time to print or sort by. Controlled by -c and -u. */ enum time_type { time_mtime, /* default */ time_ctime, /* -c */ time_atime /* -u */ }; enum time_type time_type; /* The file characteristic to sort by. Controlled by -t and -S. */ enum sort_type { sort_none, /* +sort=none */ sort_name, /* default */ sort_time, /* -t */ sort_size /* -S */ }; enum sort_type sort_type; /* Direction of sort. 0 means highest first if numeric, lowest first if alphabetic; these are the defaults. 1 means the opposite order in each case. -r */ int sort_reverse; /* Nonzero means mention the group of each file. -g */ int print_groups; /* Nonzero means print the user and group id's as numbers rather than as names. -n */ int numeric_users; /* Nonzero means mention the size in blocks of each file. -s */ int print_block_size; /* Nonzero means show file sizes in kilobytes instead of blocks. -k This implies print_block_size. */ int kilobyte_blocks; /* none means don't mention the type of files. all means mention the types of all files. not_programs means do so except for executables. Controlled by -F and -p. */ enum indicator_style { none, /* default */ all, /* -F */ not_programs /* -p */ }; enum indicator_style indicator_style; /* Nonzero means mention the inode number of each file. -i */ int print_inode; /* Nonzero means when a symbolic link is found, display info on the file linked to. -L */ int trace_links; /* Nonzero means when a directory is found, display info on its contents. -R */ int trace_dirs; /* Nonzero means when an argument is a directory name, display info on it itself. -d */ int immediate_dirs; /* Nonzero means don't omit files whose names start with `.'. -A */ int all_files; /* Nonzero means don't omit files `.' and `..' This flag implies `all_files'. -a */ int really_all_files; /* A linked list of shell-style globbing patterns. If a non-argument file name matches any of these patterns, it is omitted. Controlled by -I. Multiple -I options accumulate. The -B option adds `*~' and `.*~' to this list. */ struct ignore_pattern { char *pattern; struct ignore_pattern *next; }; struct ignore_pattern *ignore_patterns; /* Nonzero means quote nongraphic chars in file names. -b */ int quote_funny_chars; /* Nonzero means output nongraphic chars in file names as `?'. -q */ int qmark_funny_chars; /* Nonzero means output each file name using C syntax for a string. Always accompanied by `quote_funny_chars'. This mode, together with -x or -C or -m, and without such frills as -F or -s, is guaranteed to make it possible for a program receiving the output to tell exactly what file names are present. -Q */ int quote_as_string; /* Nonzero means we are listing the working directory because no non-option arguments were given. */ int dir_defaulted; /* Nonzero means print each directory name before listing it. */ int print_dir_name; /* The line length to use for breaking lines in many-per-line format. Can be set with -w. */ int line_length; /* If nonzero, the file listing format requires that stat be called on each file. */ int format_needs_stat; int main (argc, argv) int argc; char **argv; { register int i; register struct pending *thispend; dir_defaulted = 1; print_dir_name = 1; pending_dirs = 0; current_time = time ((long *) 0); program_name = argv[0] = basename (argv[0]); i = decode_switches (argc, argv); format_needs_stat = sort_type == sort_time || sort_type == sort_size || format == long_format || trace_links || trace_dirs || indicator_style != none || print_block_size || print_inode; nfiles = 100; files = (struct file *) xmalloc (sizeof (struct file) * nfiles); files_index = 0; clear_files (); if (i < argc) dir_defaulted = 0; for (; i < argc; i++) gobble_file (argv[i], 1, ""); if (dir_defaulted) { if (immediate_dirs) gobble_file (".", 1, ""); else queue_directory (".", 0); } if (files_index) { sort_files (); if (!immediate_dirs) extract_dirs_from_files ("", 0); /* `files_index' might be zero now. */ } if (files_index) { print_current_files (); if (pending_dirs) putchar ('\n'); } else if (pending_dirs && pending_dirs->next == 0) print_dir_name = 0; while (pending_dirs) { thispend = pending_dirs; pending_dirs = pending_dirs->next; print_dir (thispend->name, thispend->realname); free (thispend->name); if (thispend->realname) free (thispend->realname); free (thispend); print_dir_name = 1; } exit (0); } /* If `arg' is an unambiguous match for an element of the null-terminated array `optlist', return the index in `optlist' of the matched element, else -1 if it does not match at all or -2 if it is ambiguous (could match several elements). */ int argmatch (arg, optlist) char *arg; char **optlist; { int i; /* Temporary index in `optlist'. */ int arglen; /* Length of `arg'. */ int matchind = -1; /* Index of first nonexact match. */ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ arglen = strlen (arg); /* Test all elements for either exact match or abbreviated matches. */ for (i = 0; optlist[i]; i++) { if (!strncmp (optlist[i], arg, arglen)) { if (strlen (optlist[i]) == arglen) /* Exact match found. */ return i; else if (matchind == -1) /* First nonexact match found. */ matchind = i; else /* Second nonexact match found. */ ambiguous = 1; } } if (ambiguous) return -2; else return matchind; } struct option long_options[] = { {"all", 0, 0, 'a'}, {"escape", 0, 0, 'b'}, {"directory", 0, 0, 'd'}, {"group", 0, 0, 'g'}, {"inode", 0, 0, 'i'}, {"kilobyte-file-size", 0, 0, 'k'}, {"numeric-uid-gid", 0, 0, 'n'}, {"hide-control-chars", 0, 0, 'q'}, {"reverse", 0, 0, 'r'}, {"size", 0, 0, 's'}, {"width", 1, 0, 'w'}, {"almost-all", 0, 0, 'A'}, {"ignore-backups", 0, 0, 'B'}, {"classify", 0, 0, 'F'}, {"file-type", 0, 0, 'F'}, {"ignore", 1, 0, 'I'}, {"dereference", 0, 0, 'L'}, {"literal", 0, 0, 'N'}, {"quote-name", 0, 0, 'Q'}, {"recursive", 0, 0, 'R'}, {"format", 1, 0, 12}, {"sort", 1, 0, 10}, {"time", 1, 0, 11}, {0, 0, 0, 0} }; char *format_args[] = { "verbose", "long", "commas", "horizontal", "across", "vertical", "single-column", 0 }; enum format formats[] = { long_format, long_format, with_commas, horizontal, horizontal, many_per_line, one_per_line }; char *sort_args[] = { "none", "time", "size", 0 }; enum sort_type sort_types[] = { sort_none, sort_time, sort_size }; char *time_args[] = { "access", "use", "status", 0 }; enum time_type time_types[] = { time_atime, time_atime, time_ctime }; /* Set all the option flags according to the switches specified. Return the index of the first non-option argument. */ int decode_switches (argc, argv) int argc; char **argv; { register char *p; extern char *optarg; extern int optind; int c; int longind; qmark_funny_chars = 0; quote_funny_chars = 0; /* initialize all switches to default settings */ #ifdef MULTI_COL #define PROGNAME "dir" /* This is for the `dir' program. */ format = many_per_line; quote_funny_chars = 1; #else #ifdef LONG_FORMAT #define PROGNAME "vdir" /* This is for the `vdir' program. */ format = long_format; quote_funny_chars = 1; #else #define PROGNAME "ls" /* This is for the `ls' program. */ if (isatty (1)) { format = many_per_line; qmark_funny_chars = 1; } else { format = one_per_line; qmark_funny_chars = 0; } #endif #endif time_type = time_mtime; sort_type = sort_name; sort_reverse = 0; print_groups = 0; numeric_users = 0; print_block_size = 0; kilobyte_blocks = 0; indicator_style = none; print_inode = 0; trace_links = 0; trace_dirs = 0; immediate_dirs = 0; all_files = 0; really_all_files = 0; ignore_patterns = 0; quote_as_string = 0; p = getenv ("COLUMNS"); line_length = p ? atoi (p) : 80; #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl (1, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) line_length = ws.ws_col; } #endif while ((c = getopt_long (argc, argv, "abcdgiklmnpqrstuw:xABCFI:LNQRS1", long_options, &longind)) != EOF) { if (c == 0) c = long_options[longind].val; switch (c) { case 'a': all_files = 1; really_all_files = 1; break; case 'b': quote_funny_chars = 1; qmark_funny_chars = 0; break; case 'c': time_type = time_ctime; break; case 'd': immediate_dirs = 1; break; case 'g': print_groups = 1; break; case 'i': print_inode = 1; break; case 'k': print_block_size = 1; kilobyte_blocks = 1; break; case 'l': format = long_format; break; case 'm': format = with_commas; break; case 'n': numeric_users = 1; break; case 'p': indicator_style = not_programs; break; case 'q': qmark_funny_chars = 1; quote_funny_chars = 0; break; case 'r': sort_reverse = 1; break; case 's': print_block_size = 1; kilobyte_blocks = 0; break; case 't': sort_type = sort_time; break; case 'u': time_type = time_atime; break; case 'w': line_length = atoi (optarg); if (line_length < 1) fatal ("invalid line width: %s", optarg, (char *) 0); break; case 'x': format = horizontal; break; case 'A': all_files = 1; break; case 'B': add_ignore_pattern ("*~"); add_ignore_pattern (".*~"); break; case 'C': format = many_per_line; break; case 'F': indicator_style = all; break; case 'I': add_ignore_pattern (optarg); break; case 'L': trace_links = 1; break; case 'N': quote_funny_chars = 0; qmark_funny_chars = 0; break; case 'Q': quote_as_string = 1; quote_funny_chars = 1; qmark_funny_chars = 0; break; case 'R': trace_dirs = 1; break; case 'S': sort_type = sort_size; break; case '1': format = one_per_line; break; case 10: /* +sort */ longind = argmatch (optarg, sort_args); if (longind >= 0) sort_type = sort_types[longind]; else invalid_arg ("sort type", optarg, longind); break; case 11: /* +time */ longind = argmatch (optarg, time_args); if (longind >= 0) time_type = time_types[longind]; else invalid_arg ("time type", optarg, longind); break; case 12: /* +format */ longind = argmatch (optarg, format_args); if (longind >= 0) format = formats[longind]; else invalid_arg ("format type", optarg, longind); break; default: usage (); } } return optind; } void invalid_arg (kind, value, problem) char *kind; char *value; int problem; { fprintf (stderr, "%s: %s `%s' is ", program_name, kind, value); if (problem == -1) fprintf (stderr, "invalid\n"); else fprintf (stderr, "ambiguous\n"); usage (); } /* Request that the directory named `name' have its contents listed later. If `realname' is nonzero, it will be used instead of `name' when the directory name is printed. This allows symbolic links to directories to be treated as regular directories but still be listed under their real names. */ void queue_directory (name, realname) char *name; char *realname; { struct pending *new; new = (struct pending *) xmalloc (sizeof (struct pending)); new->next = pending_dirs; pending_dirs = new; new->name = copystring (name); if (realname) new->realname = copystring (realname); else new->realname = 0; } /* Read directory `name', and list the files in it. If `realname' is nonzero, print its name instead of `name'; this is used for symbolic links to directories. */ void print_dir (name, realname) char *name; char *realname; { register DIR *reading; register struct direct *next; register int total_blocks = 0; reading = opendir (name); if (!reading) { perror_with_name (name); return; } /* Read the directory entries, and insert the subfiles into the `files' table. */ clear_files (); while (next = readdir (reading)) if (file_interesting (next)) total_blocks += gobble_file (next->d_name, 0, name); closedir (reading); /* Sort the directory contents. */ sort_files (); /* If any member files are subdirectories, perhaps they should have their contents listed rather than being mentioned here as files. */ if (trace_dirs) extract_dirs_from_files (name, 1); if (print_dir_name) { if (realname) printf ("%s:\n", realname); else printf ("%s:\n", name); } if (format == long_format || print_block_size) printf ("total %d\n", total_blocks); if (files_index) print_current_files (); if (pending_dirs) putchar ('\n'); } /* Add `pattern' to the list of patterns for which files that match are not listed. */ void add_ignore_pattern (pattern) char *pattern; { register struct ignore_pattern *ignore; ignore = (struct ignore_pattern *) xmalloc (sizeof (struct ignore_pattern)); ignore->pattern = pattern; /* Add it to the head of the linked list. */ ignore->next = ignore_patterns; ignore_patterns = ignore; } /* Return nonzero if the file in `next' should be listed. */ int file_interesting (next) register struct direct *next; { register struct ignore_pattern *ignore; for (ignore = ignore_patterns; ignore; ignore = ignore->next) if (glob_match (ignore->pattern, next->d_name, 1)) return 0; if (really_all_files || next->d_name[0] != '.' || (all_files && next->d_name[1] != '\0' && (next->d_name[1] != '.' || next->d_name[2] != '\0'))) return 1; return 0; } /* Enter and remove entries in the table `files'. */ /* Empty the table of files. */ void clear_files () { register int i; for (i = 0; i < files_index; i++) { free (files[i].name); if (files[i].linkname) free (files[i].linkname); } files_index = 0; block_size_size = 4; } /* Add a file to the current table of files. Verify that the file exists, and print an error message if it does not. Return the number of blocks that the file occupies. */ int gobble_file (name, explicit_arg, dirname) char *name; int explicit_arg; char *dirname; { register int blocks; register int val; register char *path; if (files_index == nfiles) { nfiles *= 2; files = (struct file *) xrealloc (files, sizeof (struct file) * nfiles); } files[files_index].linkname = 0; files[files_index].linkmode = 0; if (explicit_arg || format_needs_stat) { /* `path' is the absolute pathname of this file. */ if (name[0] == '/' || dirname[0] == 0) path = name; else { path = (char *) alloca (strlen (name) + strlen (dirname) + 2); attach (path, dirname, name); } if (trace_links) { val = stat (path, &files[files_index].stat); if (val < 0) /* Perhaps a symbolically-linked to file doesn't exist; stat the link instead. */ val = lstat (path, &files[files_index].stat); } else val = lstat (path, &files[files_index].stat); if (val < 0) { perror_with_name (path); return 0; } #ifdef S_IFLNK if ((files[files_index].stat.st_mode & S_IFMT) == S_IFLNK) { char *linkpath; struct stat linkstats; get_link_name (path, &files[files_index]); linkpath = make_link_path (path, files[files_index].linkname); /* Stat the file linked to; automatically trace it in non-long listings, get its mode for the filetype indicator in long listings. */ if (linkpath && lstat (linkpath, &linkstats) == 0) { if ((linkstats.st_mode & S_IFMT) == S_IFDIR && explicit_arg && format != long_format) { char *tempname; /* Symbolic links to directories that are mentioned on the command line are automatically traced if not being listed as files. */ if (!immediate_dirs) { tempname = name; name = linkpath; linkpath = files[files_index].linkname; files[files_index].linkname = tempname; } files[files_index].stat = linkstats; } else files[files_index].linkmode = linkstats.st_mode; } if (linkpath) free (linkpath); } #endif switch (files[files_index].stat.st_mode & S_IFMT) { #ifdef S_IFLNK case S_IFLNK: files[files_index].filetype = symbolic_link; break; #endif case S_IFDIR: if (explicit_arg && !immediate_dirs) files[files_index].filetype = arg_directory; else files[files_index].filetype = directory; break; default: files[files_index].filetype = normal; break; } blocks = nblocks (files[files_index].stat); if (blocks >= 10000 && block_size_size < 5) block_size_size = 5; if (blocks >= 100000 && block_size_size < 6) block_size_size = 6; if (blocks >= 1000000 && block_size_size < 7) block_size_size = 7; } else blocks = 0; files[files_index].name = copystring (name); files_index++; return blocks; } #ifdef S_IFLNK /* Put the name of the file that `filename' is a symbolic link to into the `linkname' field of `f'. */ void get_link_name (filename, f) char *filename; struct file *f; { register char *linkbuf; register int bufsiz = f->stat.st_size; linkbuf = (char *) xmalloc (bufsiz + 1); linkbuf[bufsiz] = 0; if (readlink (filename, linkbuf, bufsiz) < 0) { perror_with_name (filename); free (linkbuf); } else f->linkname = linkbuf; } /* If `linkname' is a relative path and `path' contains one or more leading directories, return `linkname' with those directories prepended; otherwise, return a copy of `linkname'. If `linkname' is zero, return zero. */ char * make_link_path (path, linkname) char *path; char *linkname; { char *linkbuf; int bufsiz; if (linkname == 0) return 0; if (*linkname == '/') return copystring (linkname); /* The link is to a relative path. Prepend any leading path in `filename' to the link name. */ linkbuf = rindex (path, '/'); if (linkbuf == 0) return copystring (linkname); bufsiz = linkbuf - path + 1; linkbuf = xmalloc (bufsiz + strlen (linkname) + 1); strncpy (linkbuf, path, bufsiz); strcpy (linkbuf + bufsiz, linkname); return linkbuf; } #endif /* Remove any entries from `files' that are for directories, and queue them to be listed as directories instead. `dirname' is the prefix to prepend to each dirname to make it correct relative to ls's working dir. `recursive' is nonzero if we should not treat `.' and `..' as dirs. This is desirable when processing directories recursively. */ void extract_dirs_from_files (dirname, recursive) char *dirname; int recursive; { register int i, j; register char *path; int dirlen; dirlen = strlen (dirname) + 2; /* Queue the directories last one first, because queueing reverses the order. */ for (i = files_index - 1; i >= 0; i--) if ((files[i].filetype == directory || files[i].filetype == arg_directory) && (!recursive || is_not_dot_or_dotdot (files[i].name))) { if (files[i].name[0] == '/' || dirname[0] == 0) { queue_directory (files[i].name, files[i].linkname); } else { path = (char *) xmalloc (strlen (files[i].name) + dirlen); attach (path, dirname, files[i].name); queue_directory (path, files[i].linkname); free (path); } if (files[i].filetype == arg_directory) free (files[i].name); } /* Now delete the directories from the table, compacting all the remaining entries. */ for (i = 0, j = 0; i < files_index; i++) if (files[i].filetype != arg_directory) files[j++] = files[i]; files_index = j; } /* Return non-zero if `name' doesn't end in `.' or `..' This is so we don't try to recurse on `././././. ...' */ int is_not_dot_or_dotdot (name) char *name; { char *t; t = rindex (name, '/'); if (t) name = t + 1; if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) return 0; return 1; } /* Sort the files now in the table. */ void sort_files () { int (*func) (); switch (sort_type) { case sort_none: return; case sort_time: switch (time_type) { case time_ctime: func = sort_reverse ? rev_cmp_ctime : compare_ctime; break; case time_mtime: func = sort_reverse ? rev_cmp_mtime : compare_mtime; break; case time_atime: func = sort_reverse ? rev_cmp_atime : compare_atime; break; } break; case sort_name: func = sort_reverse ? rev_cmp_name : compare_name; break; case sort_size: func = sort_reverse ? rev_cmp_size : compare_size; break; } qsort (files, files_index, sizeof (struct file), func); } /* Comparison routines for sorting the files. */ int compare_ctime (file1, file2) struct file *file1, *file2; { return file2->stat.st_ctime - file1->stat.st_ctime; } int rev_cmp_ctime (file2, file1) struct file *file1, *file2; { return file2->stat.st_ctime - file1->stat.st_ctime; } int compare_mtime (file1, file2) struct file *file1, *file2; { return file2->stat.st_mtime - file1->stat.st_mtime; } int rev_cmp_mtime (file2, file1) struct file *file1, *file2; { return file2->stat.st_mtime - file1->stat.st_mtime; } int compare_atime (file1, file2) struct file *file1, *file2; { return file2->stat.st_atime - file1->stat.st_atime; } int rev_cmp_atime (file2, file1) struct file *file1, *file2; { return file2->stat.st_atime - file1->stat.st_atime; } int compare_size (file1, file2) struct file *file1, *file2; { return file2->stat.st_size - file1->stat.st_size; } int rev_cmp_size (file2, file1) struct file *file1, *file2; { return file2->stat.st_size - file1->stat.st_size; } int compare_name (file1, file2) struct file *file1, *file2; { return strcmp (file1->name, file2->name); } int rev_cmp_name (file2, file1) struct file *file1, *file2; { return strcmp (file1->name, file2->name); } /* List all the files now in the table. */ void print_current_files () { register int i; switch (format) { case one_per_line: for (i = 0; i < files_index; i++) { print_file_name_and_frills (files + i); putchar ('\n'); } break; case many_per_line: print_many_per_line (); break; case horizontal: print_horizontal (); break; case with_commas: print_with_commas (); break; case long_format: for (i = 0; i < files_index; i++) { print_long_format (files + i); putchar ('\n'); } break; } } void print_long_format (f) struct file *f; { char modebuf[20]; char timebuf[40]; long when; filemodestring (&f->stat, modebuf); modebuf[10] = 0; switch (time_type) { case time_ctime: when = f->stat.st_ctime; break; case time_mtime: when = f->stat.st_mtime; break; case time_atime: when = f->stat.st_atime; break; } strcpy (timebuf, ctime (&when)); if (current_time - when > 300 * 24 * 60 * 60) { /* The file is at least 300 days old. Show its year instead of the time of day. */ strcpy (timebuf + 11, timebuf + 19); } timebuf[16] = 0; if (print_inode) printf ("%5d ", f->stat.st_ino); if (print_block_size) printf ("%*d ", block_size_size, nblocks (f->stat)); printf ("%s%3d ", modebuf, f->stat.st_nlink); if (numeric_users) printf ("%-9d", f->stat.st_uid); else printf ("%-9s", getuser (f->stat.st_uid)); if (print_groups) { if (numeric_users) printf ("%-9d", f->stat.st_gid); else printf ("%-9s", getgroup (f->stat.st_gid)); } if ((f->stat.st_mode & S_IFMT) == S_IFCHR || (f->stat.st_mode & S_IFMT) == S_IFBLK) printf ("%3d, %3d %s ", major (f->stat.st_rdev), minor (f->stat.st_rdev), timebuf + 4); else printf ("%8d %s ", f->stat.st_size, timebuf + 4); print_name_with_quoting (f->name); if (f->filetype == symbolic_link) { if (f->linkname) { fputs (" -> ", stdout); print_name_with_quoting (f->linkname); if (indicator_style != none) print_type_indicator (f->linkmode); } } else if (indicator_style != none) print_type_indicator (f->stat.st_mode); } void print_name_with_quoting (p) register char *p; { register char c; if (quote_as_string) putchar ('"'); while (c = *p++) { if (quote_funny_chars) { switch (c) { case '\\': printf ("\\\\"); break; case '\n': printf ("\\n"); break; case '\b': printf ("\\b"); break; case '\r': printf ("\\r"); break; case '\t': printf ("\\t"); break; case '\f': printf ("\\f"); break; case ' ': printf ("\\ "); break; case '"': printf ("\\\""); break; default: if (c > 040 && c < 0177) putchar (c); else printf ("\\%03o", c); } } else { if (c >= 040 && c < 0177) putchar (c); else if (!qmark_funny_chars) putchar (c); else putchar ('?'); } } if (quote_as_string) putchar ('"'); } /* Print the file name of `f' with appropriate quoting. Also print file size, inode number, and filetype indicator character, as requested by switches. */ void print_file_name_and_frills (f) struct file *f; { if (print_inode) printf ("%5d ", f->stat.st_ino); if (print_block_size) printf ("%*d ", block_size_size, nblocks (f->stat)); print_name_with_quoting (f->name); if (indicator_style != none) print_type_indicator (f->stat.st_mode); } void print_type_indicator (mode) unsigned int mode; { switch (mode & S_IFMT) { case S_IFDIR: putchar ('/'); break; #ifdef S_IFLNK case S_IFLNK: putchar ('@'); break; #endif default: if (indicator_style == all && (mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6))) putchar ('*'); break; } } int length_of_file_name_and_frills (f) struct file *f; { register char *p = f->name; register char c; register int len = 0; if (print_inode) len += 6; if (print_block_size) len += 1 + block_size_size; if (quote_as_string) len += 2; while (c = *p++) { if (quote_funny_chars) { switch (c) { case '\\': case '\n': case '\b': case '\r': case '\t': case '\f': case ' ': len += 2; break; case '"': if (quote_as_string) len += 2; else len += 1; break; default: if (c >= 040 && c < 0177) len += 1; else len += 4; } } else len += 1; } if (indicator_style != none) { if (f->filetype != normal) len += 1; else if (indicator_style == all && (f->stat.st_mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6))) len += 1; } return len; } void print_many_per_line () { int filesno; /* Index into files. */ int row; /* Current row. */ int max_name_length; /* Length of longest file name + frills. */ int name_length; /* Length of each file name + frills. */ int pos; /* Current character column. */ int cols; /* Number of files across. */ int rows; /* Maximum number of files down. */ /* Compute the maximum file name length. */ max_name_length = 0; for (filesno = 0; filesno < files_index; filesno++) { name_length = length_of_file_name_and_frills (files + filesno); if (name_length > max_name_length) max_name_length = name_length; } /* Allow at least two spaces between names. */ max_name_length += 2; /* Calculate the maximum number of columns that will fit. */ cols = line_length / max_name_length; if (cols == 0) cols = 1; /* Calculate the number of rows that will be in each column except possibly for a short column on the right. */ rows = files_index / cols + (files_index % cols != 0); /* Recalculate columns based on rows. */ cols = files_index / rows + (files_index % rows != 0); for (row = 0; row < rows; row++) { filesno = row; pos = 0; /* Print the next row. */ while (1) { print_file_name_and_frills (files + filesno); name_length = length_of_file_name_and_frills (files + filesno); filesno += rows; if (filesno >= files_index) break; indent (pos + name_length, pos + max_name_length); pos += max_name_length; } putchar ('\n'); } } void print_horizontal () { int filesno; int max_name_length; int name_length; int cols; int pos; /* Compute the maximum file name length. */ max_name_length = 0; for (filesno = 0; filesno < files_index; filesno++) { name_length = length_of_file_name_and_frills (files + filesno); if (name_length > max_name_length) max_name_length = name_length; } /* Allow two spaces between names. */ max_name_length += 2; cols = line_length / max_name_length; if (cols == 0) cols = 1; pos = 0; name_length = 0; for (filesno = 0; filesno < files_index; filesno++) { if (filesno != 0) { if (filesno % cols == 0) { putchar ('\n'); pos = 0; } else { indent (pos + name_length, pos + max_name_length); pos += max_name_length; } } print_file_name_and_frills (files + filesno); name_length = length_of_file_name_and_frills (files + filesno); } putchar ('\n'); } void print_with_commas () { int filesno; int pos, old_pos; pos = 0; for (filesno = 0; filesno < files_index; filesno++) { old_pos = pos; pos += length_of_file_name_and_frills (files + filesno); if (filesno + 1 < files_index) pos += 2; /* For the comma and space */ if (old_pos != 0 && pos >= line_length) { putchar ('\n'); pos -= old_pos; } print_file_name_and_frills (files + filesno); if (filesno + 1 < files_index) { putchar (','); putchar (' '); } } putchar ('\n'); } struct userid { int uid; char *name; struct userid *next; }; struct userid *user_alist; /* Translate `uid' to a login name, with cache. */ char * getuser (uid) int uid; { register struct userid *tail; struct passwd *pwent; char usernum_string[20]; for (tail = user_alist; tail; tail = tail->next) if (tail->uid == uid) return tail->name; pwent = getpwuid (uid); tail = (struct userid *) xmalloc (sizeof (struct userid)); tail->uid = uid; tail->next = user_alist; if (pwent == 0) { sprintf (usernum_string, "%d", uid); tail->name = copystring (usernum_string); } else tail->name = copystring (pwent->pw_name); user_alist = tail; return tail->name; } /* We use the same struct as for userids. */ struct userid *group_alist; /* Translate `gid' to a group name, with cache. */ char * getgroup (gid) int gid; { register struct userid *tail; struct group *grent; char groupnum_string[20]; for (tail = group_alist; tail; tail = tail->next) if (tail->uid == gid) return tail->name; grent = getgrgid (gid); tail = (struct userid *) xmalloc (sizeof (struct userid)); tail->uid = gid; tail->next = group_alist; if (grent == 0) { sprintf (groupnum_string, "%d", gid); tail->name = copystring (groupnum_string); } else tail->name = copystring (grent->gr_name); group_alist = tail; return tail->name; } /* Assuming cursor is at position `from', indent up to position `to'. */ void indent (from, to) int from, to; { while (from < to) { if (to / 8 > from / 8) { putchar ('\t'); from += 8 - from % 8; } else { putchar (' '); from++; } } } /* Low level subroutines of general use, not specifically related to the task of listing a directory. */ char * xrealloc (obj, size) char *obj; int size; { char *val = realloc (obj, size); if (!val) fatal ("virtual memory exhausted", (char *) 0, (char *) 0); return val; } char * xmalloc (size) int size; { char *val = malloc (size); if (!val) fatal ("virtual memory exhausted", (char *) 0, (char *) 0); return val; } void fatal (string, arg, arg2) char *string; char *arg, *arg2; { error (string, arg, arg2); exit (1); } void error (string, arg, arg2) char *string; char *arg, *arg2; { fprintf (stderr, "%s: ", program_name); fprintf (stderr, string, arg, arg2); fprintf (stderr, "\n"); } void perror_with_name (name) char *name; { if (errno <= sys_nerr) error ("%s: %s", name, sys_errlist[errno]); else error ("%s: %s", name, "unknown system error"); } /* Return a malloc'd copy of `string'. */ char * copystring (string) char *string; { return strcpy ((char *) xmalloc (strlen (string) + 1), string); } /* Put `dirname/name' into `dest', handling `.' and `/' properly. */ void attach (dest, dirname, name) char *dest, *dirname, *name; { char *dirnamep = dirname; /* Copy dirname if it is not ".". */ if (dirname[0] != '.' || dirname[1] != 0) { while (*dirnamep) *dest++ = *dirnamep++; /* Add '/' if `dirname' doesn't already end with it. */ if (dirnamep > dirname && dirnamep[-1] != '/') *dest++ = '/'; } while (*name) *dest++ = *name++; *dest = 0; } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-abcdgiklmnpqrstuxABCFLNQRS1] [-w cols] [-I pattern]\n\ [+all] [+escape] [+directory] [+group] [+inode]\n\ [+kilobyte-file-size] [+numeric-uid-gid] [+hide-control-chars]\n\ [+reverse] [+size] [+width cols] [+almost-all] [+ignore-backups]\n", program_name); fprintf (stderr, "\ [+classify] [+file-type] [+ignore pattern] [+dereference]\n\ [+literal] [+quote-name] [+recursive]\n\ [+format {long,verbose,commas,across,vertical,single-column}]\n\ [+sort {none,time,size}] [+time {access,use,status}] [path...]\n"); exit (1); } [+escape] [+directory] [+group] [+inode]\n\ fileutils/mkdir.c 644 473 0 12250 4702305050 7350 /* mkdir -- make directories Copyright (C) 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Usage: mkdir [-p] [-m mode] [+path] [+mode mode] dir... Options: -p, +path Ensure that the given path(s) exist: Make any missing parent directories for each argument. Parent dirs default to umask modified by `u+wx'. Do not consider an argument directory that already exists to be an error. -m, +mode mode Set the mode of created directories to `mode', which is symbolic as in chmod and uses the umask as a point of departure. David MacKenzie */ #include #include #include "system.h" #include "getopt.h" #include "modechange.h" char *index (); void error (); void make_path (); void usage (); extern int errno; /* If nonzero, ensure that a path exists. */ int path_mode; /* The name this program was run with. */ char *program_name; /* Exit status. */ int errors; struct option longopts[] = { {"mode", 1, NULL, 'm'}, {"path", 0, &path_mode, 1}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char **argv; { unsigned short newmode; unsigned short parent_mode; struct modechange *change; char *symbolic_mode; int optc; int ind; program_name = argv[0]; errors = 0; path_mode = 0; symbolic_mode = NULL; while ((optc = getopt_long (argc, argv, "pm:", longopts, &ind)) != EOF) { if (optc == 0 && longopts[ind].flag == 0) optc = longopts[ind].val; switch (optc) { case 0: /* Long option. */ break; case 'p': path_mode = 1; break; case 'm': symbolic_mode = optarg; break; default: usage (); } } if (optind == argc) usage (); newmode = 0777 & ~umask (0); parent_mode = newmode | 0300; /* u+wx */ if (symbolic_mode) { change = compile_mode (symbolic_mode); if (change == MODE_INVALID) error (1, 0, "invalid mode"); else if (change == MODE_MEMORY_EXHAUSTED) error (1, 0, "virtual memory exhausted"); newmode = adjust_mode (newmode, change); } for (; optind < argc; ++optind) if (path_mode) make_path (argv[optind], newmode, parent_mode); else if (mkdir (argv[optind], newmode) == -1) error (0, errno, "cannot make directory `%s'", argv[optind]); exit (errors); } /* Make sure directory `path' and all leading directories exist, and give it permission mode `mode'. If any leading directories are created, give them permission mode `parent_mode'. */ void make_path (path, mode, parent_mode) char *path; unsigned short mode; unsigned short parent_mode; { char *slash; struct stat stats; if (stat (path, &stats) == -1) { slash = path; while (slash = index (slash, '/')) { *slash = 0; if (stat (path, &stats) == -1) { if (mkdir (path, parent_mode) == -1) { error (0, errno, "cannot make directory `%s'", path); return; } } else if ((stats.st_mode & S_IFMT) != S_IFDIR) { error (0, 0, "`%s' is not a directory", path); return; } *slash++ = '/'; } if (mkdir (path, mode) == -1) { error (0, errno, "cannot make directory `%s'", path); return; } } else if ((stats.st_mode & S_IFMT) != S_IFDIR) error (0, 0, "`%s' is not a directory", path); else if (chmod (path, mode) == -1) error (0, errno, "cannot change mode of `%s'", path); } #ifdef VPRINTF #include #else #define va_alist args #define va_dcl int args; #endif /* Print the program name and error message `message', which is a printf-style format string with optional args. If `errnum' is nonzero, print its corresponding system error message. Exit with status `status' if it is nonzero; otherwise increment `errors'. */ /* VARARGS */ void error (status, errnum, message, va_alist) int status; int errnum; char *message; va_dcl { extern char *sys_errlist[]; extern int sys_nerr; extern char *program_name; #ifdef VPRINTF va_list args; #endif fprintf (stderr, "%s: ", program_name); #ifdef VPRINTF va_start (args); vfprintf (stderr, message, args); va_end (args); #else _doprnt (message, &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fprintf (stderr, ": %s", sys_errlist[errno]); else fprintf (stderr, ": error code %d", errnum); } putc ('\n', stderr); fflush (stderr); if (status) exit (status); errors = 1; } void usage () { fprintf (stderr, "\ Usage: %s [-p] [-m mode] [+path] [+mode mode] dir...\n", program_name); exit (1); } ; vfprintf (stderr, message, args); va_end (args); #else _doprnt (message, &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fprintf (stderr, ": %s", sys_errlist[errno]); else fprintf (stderr, ": error code %d", errnum); } putc ('\n', stderr); fflush (stderr); if (statufileutils/modechange.c 644 473 0 20672 4702305050 10343 /* modechange.c -- file mode manipulation Copyright (C) 1989, 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written by David MacKenzie */ /* The ASCII mode string is compiled into a linked list of `struct modechange', which can then be applied to each file to be changed. We do this instead of re-parsing the ASCII string for each file because the compiled form requires less computation to use; when changing the mode of many files, this probably results in a performance gain. That is unverified, though -- the real reason we use the compiled form is that it is the first way it occurred to me to do it. */ #include #include #include "modechange.h" char *malloc (); #ifndef NULL #define NULL 0 #endif /* Return newly allocated memory to hold one element of type `type'. */ #define talloc(type) ((type *) malloc (sizeof (type))) #define isodigit(c) ((c) >= '0' && (c) <= '7') /* Return a linked list of file mode change operations created from `mode_string', an ASCII string that contains either an octal number specifying an absolute mode, or symbolic mode change operations with the form: [ugoa...][[+-=][rwxXstugo]...][,...] Return MODE_INVALID if `mode_string' does not contain a valid representation of file mode change operations; return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */ struct modechange * compile_mode (mode_string) register char *mode_string; { struct modechange *head; /* First element of the linked list. */ struct modechange *change; /* An element of the linked list. */ int i; /* General purpose temporary. */ int umask_value; /* The umask value (surprise). */ int use_umask; /* If nonzero, mask off bits set in umask. */ unsigned short affected_bits; /* Which bits in the mode are operated on. */ i = oatoi (mode_string); if (i >= 0) { head = talloc (struct modechange); if (head == NULL) return MODE_MEMORY_EXHAUSTED; head->next = NULL; head->op = '='; head->flags = 0; head->value = i; head->affected = 07777; /* Affect all permissions. */ return head; } head = NULL; --mode_string; umask_value = umask (0); umask (umask_value); /* Restore the old value. */ /* One loop iteration for each "ugoa...=+-rwxXstugo[=+-rwxXstugo...]". */ do { affected_bits = 0; /* Turn on all the bits in `affected_bits' for each group given. */ for (++mode_string;; ++mode_string) switch (*mode_string) { case 'u': affected_bits |= 04700; break; case 'g': affected_bits |= 02070; break; case 'o': affected_bits |= 01007; break; case 'a': affected_bits |= 00777; /* Note: set?id & sticky unaffected. */ break; default: goto no_more_affected; } no_more_affected: /* If none specified, use `a' but do not affect bits set in umask. */ if (affected_bits == 0) { affected_bits = 00777; use_umask = 1; } else use_umask = 0; if (use_umask) /* Turn off, in `affected_bits', the bits set in the umask. */ affected_bits &= ~umask_value; while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-') { /* Add the element to the tail of the list, so the operations are performed in the correct order. */ if (head == NULL) head = change = talloc (struct modechange); else { change->next = talloc (struct modechange); change = change->next; } if (change == NULL) return MODE_MEMORY_EXHAUSTED; change->next = NULL; change->op = *mode_string; /* One of "=+-". */ change->flags = 0; change->affected = affected_bits; change->value = 0; /* Set `value' according to the bits set in `affected_bits'. */ for (++mode_string;; ++mode_string) switch (*mode_string) { case 'r': change->value |= 00444 & affected_bits; break; case 'w': change->value |= 00222 & affected_bits; break; case 'X': change->flags |= MODE_X_IF_ANY_X; /* Fall through. */ case 'x': change->value |= 00111 & affected_bits; break; case 's': /* Set the setuid/gid bits if `u' or `g' is selected. */ change->value |= 06000 & affected_bits; break; case 't': /* Set the "save text image" bit if `o' is selected. */ change->value |= 01000 & affected_bits; break; case 'u': /* Set the affected bits to the value of the `u' bits on the same file. */ if (change->value) goto invalid; change->value = 00700; change->flags |= MODE_COPY_EXISTING; break; case 'g': /* Set the affected bits to the value of the `g' bits on the same file. */ if (change->value) goto invalid; change->value = 00070; change->flags |= MODE_COPY_EXISTING; break; case 'o': /* Set the affected bits to the value of the `o' bits on the same file. */ if (change->value) goto invalid; change->value = 00007; change->flags |= MODE_COPY_EXISTING; break; default: goto no_more_values; } no_more_values:; } } while (*mode_string == ','); if (*mode_string == 0) return head; invalid: free_mode (head); return MODE_INVALID; } /* Return file mode `oldmode', adjusted as indicated by the list of change operations `changes'. If `oldmode' has the S_IFDIR bit set, the type `X' change affects it even if no execute bits were set in `oldmode'. The returned value has the S_IFMT bits cleared. */ unsigned short adjust_mode (oldmode, changes) unsigned short oldmode; register struct modechange *changes; { unsigned short newmode; /* The adjusted mode and one operand. */ unsigned short value; /* The other operand. */ newmode = oldmode & 07777; for (; changes; changes = changes->next) { if (changes->flags & MODE_COPY_EXISTING) { /* Isolate in `value' the bits in `newmode' to copy, given in the mask `changes->value'. */ value = newmode & changes->value; if (changes->value & 00700) /* Copy `u' permissions onto `g' and `o'. */ value |= (value >> 3) | (value >> 6); else if (changes->value & 00070) /* Copy `g' permissions onto `u' and `o'. */ value |= (value << 3) | (value >> 3); else /* Copy `o' permissions onto `u' and `g'. */ value |= (value << 3) | (value << 6); /* In order to change only `u', `g', or `o' permissions, or some combination thereof, clear unselected bits. This can not be done in compile_mode because the value to which the `changes->affected' mask is applied depends on the old mode of each file. */ value &= changes->affected; } else { value = changes->value; /* If `X', do not affect the execute bits if the file is not a directory and no execute bits are already set. */ if ((changes->flags & MODE_X_IF_ANY_X) && (oldmode & S_IFMT) != S_IFDIR && (newmode & 00111) == 0) value &= ~00111; /* Clear the execute bits. */ } switch (changes->op) { case '=': /* Preserve the previous values in `newmode' of bits that are not affected by this change operation. */ newmode = (newmode & ~changes->affected) | value; break; case '+': newmode |= value; break; case '-': newmode &= ~value; break; } } return newmode; } /* Free the memory used by the list of file mode change operations `changes'. */ void free_mode (changes) register struct modechange *changes; { register struct modechange *next; while (changes) { next = changes->next; free (changes); changes = next; } } /* Return a positive integer containing the value of the ASCII octal number `s'. If `s' is not an octal number, return -1. */ int oatoi (s) char *s; { register int i; for (i = 0; isodigit (*s); ++s) i = i * 8 + *s - '0'; if (*s) return -1; return i; } ns `changes'. */ void free_mode (changes) register struct modfileutils/modechange.h 644 473 0 3311 4702305050 10317 /* modechange.h -- definitions for file mode manipulation Copyright (C) 1989, 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Masks for the `flags' field in a `struct modechange'. */ /* Affect the execute bits only if at least one execute bit is set already, or if the file is a directory. */ #define MODE_X_IF_ANY_X 01 /* If set, copy some existing permissions for u, g, or o onto the other two. Which of u, g, or o is copied is determined by which bits are set in the `value' field. */ #define MODE_COPY_EXISTING 02 struct modechange { char op; /* One of "=+-". */ char flags; /* Special operations. */ unsigned short affected; /* Set for u/g/o/s/s/t, if to be affected. */ unsigned short value; /* Bits to add/remove. */ struct modechange *next; /* Link to next change in list. */ }; /* Error return values for compile_mode. */ #define MODE_INVALID (struct modechange *) 0 #define MODE_MEMORY_EXHAUSTED (struct modechange *) 1 struct modechange *compile_mode (); unsigned short adjust_mode (); void free_mode (); int oatoi (); { char op; /* One of "=+-". */ char flags; /* Special operations. */ unsigned short affected; /* Set for u/g/o/s/s/t, if to be affected. */ unsigned short value; /* Bits to add/remove. */ struct modechange *next; /* Link to next change in list. */ }; /* Error return values for compile_mode. */fileutils/mv.c 644 473 0 22643 4702305051 6674 /* mv -- move or rename files Copyright (C) 1986, 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Options: -i (+interactive) Causes mv to require confirmation from the user before performing any move that would destroy an existing file. The question looks like: Move f1 (removing f2)? -f (+force) Overrides the -i option; causes mv to assume a 'y' answer to all questions it would normally ask (and not ask the questions). If -f is not given, the following question is asked when a move would destroy an existing file whose mode prohibits writing: Override mode 0nnn for fn? This test is slightly different from the test 4.2BSD mv makes; 4.2 mv merely checks whether the user can write the file. This actually checks the write bits in the mode. The difference shows when super-user tries to mv onto a file with no write bits set. -v (+verbose) List the name of each file as it is moved, and the name it is moved to. Author Mike Parker */ #include #include #if defined(EACCES) && !defined (EACCESS) #define EACCESS EACCES #endif #include #include "system.h" #include "getopt.h" char *rindex (); void do_move (); void error (); void movefile (); void usage (); extern int errno; /* The name this program was run with. */ char *pgm; /* If nonzero, query the user before overwriting files. */ int interactive; /* If nonzero, override as much protection as possible. */ int force; /* If nonzero, list each file as it is moved. */ int verbose; /* Number of arguments to process. */ int nargs; /* The remaining arguments. */ char **args; /* The exit status. */ int errors; /* Return nonzero if FN is a directory, zero if not. */ int isdir (fn) char *fn; { struct stat stb; return (stat (fn, &stb) >= 0 && (stb.st_mode & S_IFMT) == S_IFDIR); } #ifdef USG int rename (from, to) char *from; char *to; { if (access (from, 0) == 0) { unlink (to); if (link (from, to) == 0) if (unlink (from) == 0) return (0); } return (-1); } #endif struct option long_options[] = { {"force", 0, &force, 1}, {"interactive", 0, &interactive, 1}, {"verbose", 0, &verbose, 1}, {NULL, 0, NULL, 0} }; int main (ac, av) int ac; char **av; { int c; int ind; interactive = force = verbose = 0; errors = 0; pgm = av[0]; while ((c = getopt_long (ac, av, "fiv", long_options, &ind)) != EOF) switch (c) { case 0: break; case 'f': force = 1; break; case 'i': interactive = 1; break; case 'v': verbose = 1; break; default: usage (); } args = &av[optind]; nargs = ac - optind; if (nargs < 2) /* too few arguments */ usage (); if (nargs > 2 && !isdir (args[nargs - 1])) /* >2 args and last not dir */ error (1, 0, "when moving multiple files, last argument must be a directory"); for (; nargs > 1; args++, nargs--)/* move each arg but last onto the last */ movefile (args[0], args[nargs - 1]); exit (errors); } /* Move file FROM onto TO. Handles case when TO is a directory. */ void movefile (from, to) char *from; char *to; { if (isdir (to)) { /* Target is a directory; build full target filename. */ char *cp; char *newto; cp = rindex (from, '/'); if (cp) cp++; else cp = from; newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1); sprintf (newto, "%s/%s", to, cp); do_move (from, newto); } else do_move (from, to); } /* Used for confirmation of the move in various cases. Reads one line and returns true iff that line begins with y or Y. */ int yes () { int c; int rv; fflush (stdout); c = getchar (); rv = (c == 'y') || (c == 'Y'); while (c != '\n') c = getchar (); return rv; } /* Move FROM onto TO. Handles cross-filesystem moves. If TO is a directory, FROM must be also. */ void do_move (from, to) char *from; char *to; { int val; struct stat stb, stbf; /* `to' and `from' stats. */ if (lstat (from, &stbf) != 0) { error (0, errno, "cannot stat `%s', not moved", from); return; } if (verbose) printf (" %s -> %s\n", from, to); if (lstat (to, &stb) == 0) /* Target exists. */ { if (interactive && !force) { printf ("Move `%s', removing `%s'? ", from, to); if (!yes ()) return; } if (stbf.st_dev == stb.st_dev && stbf.st_ino == stb.st_ino) { error (0, 0, "`%s' and `%s' are the same file", from, to); return; } /* check for write access to target */ if (!force) { /* access follows symlinks, which is wrong here. */ if ( #ifdef S_IFLNK (stb.st_mode & S_IFMT) != S_IFLNK && #endif access (to, W_OK) < 0) { if (errno != EACCESS) { /* RO filesystem, text busy etc. */ error (0, errno, "no write access to `%s'", to); return; } else { /* File is nonwritable only due to its access mode. */ printf ("Override mode %04o for `%s'? ", stb.st_mode & 0777, to); if (!yes ()) return; } } else if ((stb.st_mode & ((S_IWRITE >> 6) * 0111)) == 0) { /* Treat file as nonwritable if it lacks write permission bit, even if we are root. */ printf ("Override mode %04o for `%s'? ", stb.st_mode & 0777, to); if (!yes ()) return; } } } val = rename (from, to); if (val < 0) { if (errno == EXDEV) /* rename failed on cross-filesystem link */ { int ifd; int ofd; char buf[1024 * 8]; int len; /* do the following to perform the move: unlink the target open both files copy the permission bits from the source to the target copy the source to the target close both files if no error so far, unlink the source */ if (unlink (to) < 0 && errno != ENOENT) { error (0, errno, "cannot remove `%s'", to); return; } ifd = open (from, O_RDONLY, 0); if (ifd < 0) { error (0, errno, "cannot open `%s'", from); return; } ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0777); if (ofd < 0) { close (ifd); error (0, errno, "cannot create `%s'", to); return; } #ifdef USG chmod (to, stbf.st_mode & 0777); /* again, can't fail */ #else fchmod (ofd, stbf.st_mode & 0777); /* again, can't fail */ #endif while ((len = read (ifd, buf, sizeof (buf))) > 0) { int wrote = 0; char *bp = buf; do { wrote = write (ofd, bp += wrote, len); if (wrote < 0) { error (0, errno, "error writing `%s' while copying from `%s', move not completed", to, from); close (ifd); close (ofd); return; } } while ((len -= wrote) > 0); } if (len < 0) { error (0, errno, "error reading `%s' while copying to `%s', original not removed", from, to); close (ifd); close (ofd); return; } close (ifd); close (ofd); /* Try to copy the old file's modtime and access time. */ { #ifdef USG long tvp[2]; tvp[0] = stbf.st_atime; tvp[1] = stbf.st_mtime; utime (to, tvp); #else struct timeval tvp[2]; tvp[0].tv_sec = stbf.st_atime; tvp[1].tv_sec = stbf.st_mtime; tvp[0].tv_usec = 0; tvp[1].tv_usec = 0; utimes (to, tvp); #endif } /* Delete the old file. */ if (unlink (from) < 0) { error (0, errno, "cannot remove `%s' after successful copy to `%s'", from, to); } } else { error (0, errno, "`%s' not moved to `%s'", from, to); } } } #ifdef VPRINTF #include #else #define va_alist args #define va_dcl int args; #endif /* Print the program name and error message `message', which is a printf-style format string with optional args. If `errnum' is nonzero, print its corresponding system error message. Exit with status `status' if it is nonzero; otherwise increment `errors'. */ /* VARARGS */ void error (status, errnum, message, va_alist) int status; int errnum; char *message; va_dcl { extern char *sys_errlist[]; extern int sys_nerr; extern char *pgm; #ifdef VPRINTF va_list args; #endif fprintf (stderr, "%s: ", pgm); #ifdef VPRINTF va_start (args); vfprintf (stderr, message, args); va_end (args); #else _doprnt (message, &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fprintf (stderr, ": %s", sys_errlist[errno]); else fprintf (stderr, ": error code %d", errnum); } putc ('\n', stderr); fflush (stderr); if (status) exit (status); errors = 1; } void usage () { fprintf (stderr, "\ Usage: %s [-fiv] [+force] [+interactive] [+verbose] file1 file2\n\ %s [-fiv] [+force] [+interactive] [+verbose] file... directory\n", pgm, pgm); exit (1); } , &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fileutils/rm.c 644 473 0 23462 4702305051 6670 /* `rm' file deletion utility for GNU. Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */ #include #include #include #include #include "system.h" #include "getopt.h" char *malloc (); char *rindex (); char *strcpy (); char *strcat (); char *stp_cpy (); char *xmalloc (); void error (); void perror_with_name (); void usage (); extern int errno; /* Path of file now being processed; extended as necessary. */ char *pathname; /* Amount of space currently allocated in `pathname'; made larger when necessary, but never smaller. */ int pnsize; /* Name (not including directories) under this which program was run. */ char *progname; /* If nonzero, display the name of each file removed. */ int verbose; /* If nonzero, ignore unremovable files (print no error messages and always exit with a status of zero) and ask no questions (overrides `interactive'). */ int force_flag; /* If nonzero, recursively remove directories. */ int recursive; /* If nonzero, ask no questions (a subset of `force_flag'; overrides `interactive'). */ int override_mode; /* If nonzero, query the user about whether to remove each file. */ int interactive; struct option long_opts[] = { {"force", 0, &force_flag, 1}, {"interactive", 0, &interactive, 1}, {"override", 0, &override_mode, 1}, {"recursive", 0, &recursive, 1}, {"verbose", 0, &verbose, 1}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char **argv; { int i; int err = 0; int c; int ind; verbose = force_flag = recursive = interactive = override_mode = 0; pnsize = 256; pathname = xmalloc (pnsize); progname = rindex (argv[0], '/'); if (progname) argv[0] = ++progname; /* argv[0] is used in getopt error msgs. */ else progname = argv[0]; while ((c = getopt_long (argc, argv, "fiorv", long_opts, &ind)) != EOF) switch (c) { case 0: break; case 'f': force_flag++; break; case 'i': interactive++; break; case 'o': override_mode++; break; case 'r': recursive++; break; case 'v': verbose++; break; default: usage (); } if (optind == argc) usage (); for (i = optind; i < argc; i++) { if (strlen (argv[i]) > pnsize) { free (pathname); pnsize = 2 * strlen (argv[i]); pathname = xmalloc (pnsize); } strcpy (pathname, argv[i]); err += rm (); } exit (err > 0 && !force_flag); } /* Remove file or directory `pathname' after checking appropriate things. Return 0 if successful, 1 otherwise. */ int rm () { struct stat sbuf; int directory = 0; int err; if (lstat (pathname, &sbuf) < 0) { if (!force_flag) perror_with_name ("cannot stat", pathname); return 1; } if (verbose) printf (" %s\n", pathname); if ((sbuf.st_mode & S_IFMT) == S_IFDIR) { if (!recursive) { if (!force_flag) fprintf (stderr, "%s: %s is a directory\n", progname, pathname); return 1; } directory++; } if (interactive && !force_flag && !override_mode) { printf ("Remove %s`%s'? ", directory ? "directory " : "", pathname); if (!yesno ()) return 1; } if (!interactive && !force_flag && !override_mode && !directory #ifdef S_IFLNK && (sbuf.st_mode & S_IFMT) != S_IFLNK #endif && access (pathname, W_OK) < 0) { printf ("Override protection %o for `%s'? ", sbuf.st_mode & ~S_IFMT, pathname); if (!yesno ()) return 1; } if (!directory) { if (unlink (pathname) != 0) { if (!force_flag) perror_with_name ("cannot remove", pathname); return 1; } return 0; } err = clear_directory (&sbuf); if (err == 0) { err = rmdir (pathname) != 0; if (err != 0 && !force_flag) perror_with_name ("cannot remove directory", pathname); } return err; } /* Query the user for a line from the keyboard; return 1 if yes, 0 otherwise. */ int yesno () { int c, c2; fflush (stdout); c = getchar (); if (c == '\n') return 0; while ((c2 = getchar ()) != '\n' && c2 != EOF) ; return c == 'y' || c == 'Y'; } char * xmalloc (size) int size; { char *p; p = malloc ((unsigned) size); if (p == 0) { error ("virtual memory exhausted"); exit (2); } return p; } void error (string) char *string; { fprintf (stderr, "%s: ", progname); fprintf (stderr, string); fprintf (stderr, "\n"); } void perror_with_name (reason, name) char *reason; char *name; { fprintf (stderr, "%s: %s ", progname, reason); perror (name); } struct pathstack { struct pathstack *next; char *pathp; /* Pointer to character after end. */ ino_t inum; }; /* Chain holding pathnames of directories in progress in recursive rm. The entries actually contain pointers into `pathname'. */ static struct pathstack *pathstack = NULL; /* Read directory `pathname' and remove all of its entries, avoiding use of chdir. On entry, STATP points to the results of stat on `pathname'. Return 0 for success, error count for failure. Upon return, `pathname' will have the same contents as before, but its address might be different; in that case, `pnsize' will be larger, as well. */ int clear_directory (statp) struct stat *statp; { DIR *dirp; struct direct *dp; int err = 0; struct pathstack pathframe; /* New top of stack. */ struct pathstack *pp; /* Temporary. */ char *name_space, *file_name; ino_t *inode_space, *file_inode; errno = 0; dirp = opendir (pathname); if (dirp == NULL) { if (!force_flag) fprintf (stderr, "%s: cannot open directory ", progname); if (errno) perror (pathname); else fprintf (stderr, "%s: Not removed\n", pathname); return 1; } name_space = (char *) alloca (statp->st_size); file_name = name_space; inode_space = (ino_t *) alloca (statp->st_size); file_inode = inode_space; for (dp = readdir (dirp); dp != NULL; dp = readdir (dirp)) { /* Skip "." and ".." (some NFS filesystems' directories lack them). */ if (dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { file_name = stp_cpy (file_name, dp->d_name) + 1; *file_inode++ = dp->d_ino; } } *file_name = 0; closedir (dirp); /* Now process the files in the directory. */ for (file_name = name_space, file_inode = inode_space; *file_name != 0; file_name += strlen (file_name) + 1, file_inode++) { /* Satisfy GNU requirement that filenames can be arbitrarily long. */ if (strlen (file_name) + strlen (pathname) + 2 > pnsize) { char *p; /* Larger copy of `pathname'. */ pnsize *= 4; p = xmalloc (pnsize); strcpy (p, pathname); /* Update the all the pointers in the stack to use the new area. */ for (pp = pathstack; pp != NULL; pp = pp->next) pp->pathp += p - pathname; free (pathname); pathname = p; } /* Add a new frame to the top of the path stack. */ pathframe.next = pathstack; if (pathframe.next == NULL) pathframe.pathp = pathname + strlen (pathname); else pathframe.pathp = pathstack->pathp + strlen (pathstack->pathp); pathframe.inum = *file_inode; pathstack = &pathframe; /* Append '/' and the filename to current pathname, take care of the file (which could result in recursive calls), and take the filename back off. */ *pathstack->pathp = '/'; strcpy (pathstack->pathp + 1, file_name); /* If the i-number has already appeared, there's an error. */ if (check_stack (pathstack->next, pathstack->inum)) return 1; if (rm ()) err++; *pathstack->pathp = '\0'; pathstack = pathstack->next; /* Pop the stack. */ } return err; } /* If STACK does not already have an entry with the same i-number as INUM, return 0. Otherwise, ask the user whether to continue; if yes, return 1, and if no, exit. This assumes that no one tries to remove filesystem mount points; doing so could cause duplication of i-numbers that would not indicate a corrupted file system. */ int check_stack (stack, inum) struct pathstack *stack; ino_t inum; { struct pathstack *p; for (p = stack; p != NULL; p = p->next) { if (p->inum == inum) { fprintf (stderr, "\ %s: WARNING: Circular directory structure.\n\ This almost certainly means you have a corrupted file system.\n\ NOTIFY YOUR SYSTEM MANAGER.\n\ Cycle detected: %s\nis the same as\n\t", progname, pathname); *p->pathp = '\0'; /* Truncate pathname. */ fprintf (stderr, "%s\n", pathname); *p->pathp = '/'; /* Put it back. */ printf ("%s: Continue? ", progname); if (!yesno ()) exit (1); return 1; } } return 0; } /* Copy `source' into `dest' and return dest + strlen (source), i.e., a pointer to the null at the end of `dest'. */ char * stp_cpy (dest, source) char *dest; char *source; { while ((*dest++ = *source++) != 0) /* Do nothing. */ ; return dest - 1; } void usage () { fprintf (stderr, "\ Usage: %s [-fiorv] [+force] [+interactive] [+override] [+recursive]\n\ [+verbose] path...\n", progname); exit (1); } ; if (!yesno ()) exit (1); return 1; } } return 0; } /* Copy `source' into `dest' and return dest + strlen (source), i.e., a pointer to the null at the end of `dest'. */ char * stp_cpfileutils/rmdir.c 644 473 0 7127 4702305051 7347 /* rmdir -- remove directories Copyright (C) 1990 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Usage: rmdir [-p] [+path] dir... Options: -p, +path Remove any parent dirs that are explicitly mentioned in an argument, if they become empty after the argument file is removed. David MacKenzie */ #include #include #include "system.h" #include "getopt.h" char *rindex (); void remove_parents (); void error (); void usage (); extern int errno; /* If nonzero, remove empty parent directories. */ int empty_paths; /* The name this program was run with. */ char *program_name; /* Exit status. */ int errors; struct option longopts[] = { {"path", 0, &empty_paths, 1}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char **argv; { int optc; int ind; program_name = argv[0]; errors = empty_paths = 0; while ((optc = getopt_long (argc, argv, "p", longopts, &ind)) != EOF) { switch (optc) { case 0: /* Long option. */ break; case 'p': empty_paths = 1; break; default: usage (); } } if (optind == argc) usage (); for (; optind < argc; ++optind) { if (rmdir (argv[optind]) != 0) error (0, errno, "cannot remove directory `%s'", argv[optind]); else if (empty_paths) remove_parents (argv[optind]); } exit (errors); } /* Remove any empty parent directories of `path'. Replaces '/' characters in `path' with NULs. */ void remove_parents (path) char *path; { char *slash; do { slash = rindex (path, '/'); if (slash == NULL) break; /* Remove any characters after the slash, skipping any extra slashes in a row. */ while (slash > path && *slash == '/') --slash; slash[1] = 0; } while (rmdir (path) == 0); } #ifdef VPRINTF #include #else #define va_alist args #define va_dcl int args; #endif /* Print the program name and error message `message', which is a printf-style format string with optional args. If `errnum' is nonzero, print its corresponding system error message. Exit with status `status' if it is nonzero; otherwise increment `errors'. */ /* VARARGS */ void error (status, errnum, message, va_alist) int status; int errnum; char *message; va_dcl { extern char *sys_errlist[]; extern int sys_nerr; extern char *program_name; #ifdef VPRINTF va_list args; #endif fprintf (stderr, "%s: ", program_name); #ifdef VPRINTF va_start (args); vfprintf (stderr, message, args); va_end (args); #else _doprnt (message, &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fprintf (stderr, ": %s", sys_errlist[errno]); else fprintf (stderr, ": error code %d", errnum); } putc ('\n', stderr); fflush (stderr); if (status) exit (status); errors = 1; } void usage () { fprintf (stderr, "Usage: %s [-p] [+path] dir...\n", program_name); exit (1); } VPRINTF va_start (args); vfprintf (stderr, message, args); va_end (args); #else _doprnt (message, &args, stderr); #endif if (errnum) { if (errnum > 0 && errnum < sys_nerr) fprintf (stderr, ": %s", sys_errlist[errno]); else fprintf (stderr, ": error code %d", errnum); } putc ('\n', stderr); fflush (stderr); if (status) exit (status); errors = 1; } void usage () { fileutils/tail.c 644 473 0 46256 4702305052 7212 /* tail -- output last part of file(s) Copyright (C) 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Can display any amount of data, unlike the Unix version, which uses a fixed size buffer and therefore can only deliver a limited number of lines. Started by Paul Rubin Finished by David MacKenzie Usage: tail [-n [+]#] [-lbcfqv] [+number [+]#] [+lines] [+blocks] [+chars] [+follow] [+quiet] [+silent] [+verbose] [file...] tail [+/-#lbcfqv] [file...] Options: -n, +number [+]# Number of items to tail (default 10). If the number starts with a `+', begin printing with the #th item from the start of each file, instead of from the end. -l, +lines Tail by lines (the default). -b, +blocks Tail by 512-byte blocks. -c, +chars Tail by characters. -f, +follow Loop forever trying to read more characters at the end of the file, on the assumption that the file is growing. Ignored if reading from a pipe. Cannot be used if more than one file is given. -q, +quiet, +silent Never print filename headers. -v, +verbose Always print filename headers. Reads from standard input if no files are given or when a filename of ``-'' is encountered. By default, filename headers are printed only more than one file is given. Compile with -DUSG under System V. */ #include #include #include #define ISDIGIT(c) (isascii (c) && isdigit (c)) #include "getopt.h" #include "system.h" /* Number of items to tail. */ #define DEFAULT_NUMBER 10 #define BLOCKSIZE 512 /* Size of atomic reads. */ #define BUFSIZE (BLOCKSIZE*8) /* Masks for the operation mode. If neither CHARS nor BLOCKS is set, tail operates by lines. */ #define CHARS 1 /* Tail by characters. */ #define BLOCKS 2 /* Tail by blocks. */ #define FOREVER 4 /* Read from end of file forever. */ #define START 8 /* Count from start of file instead of end. */ #define HEADERS 16 /* Print filename headers. */ /* When to print the filename banners. */ enum header_mode { multiple_files, always, never }; char *index (); char *malloc (); char *rindex (); char *strcat (); char *strcpy (); void free (); char *basename (); char *xmalloc (); int file_lines (); int start_chars (); int start_lines (); void dump_remainder (); void fatal_perror (); void nonfatal_perror (); void pipe_chars (); void pipe_lines (); void tail (); void tail_chars (); void tail_file (); void tail_lines (); void usage (); void write_header (); void xwrite (); /* The base of the name this program was run with. */ char *program_name; /* The exit status; 1 if an error occurred, 0 otherwise. */ int errors; struct option long_options[] = { {"number", 1, NULL, 'n'}, {"lines", 0, NULL, 'l'}, {"blocks", 0, NULL, 'b'}, {"chars", 0, NULL, 'c'}, {"follow", 0, NULL, 'f'}, {"quiet", 0, NULL, 'q'}, {"silent", 0, NULL, 'q'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0} }; int main (argc, argv) int argc; char **argv; { enum header_mode header_mode = multiple_files; int mode = 0; /* Flags. */ /* In START mode, the number of items to skip before printing; otherwise, the number of items at the end of the file to print. Initially, -1 means the value has not been set. */ int number = -1; int c; /* Option character. */ int longind; /* Index in `long_options' of option found. */ program_name = argv[0] = basename (argv[0]); errors = 0; if (argc > 1 && ((argv[1][0] == '-' && ISDIGIT (argv[1][1])) || (argv[1][0] == '+' && (ISDIGIT (argv[1][1]) || argv[1][1] == 0)))) { /* Old option syntax: a dash or plus, one or more digits, and one or more option letters. */ if (argv[1][0] == '+') mode |= START; if (ISDIGIT (argv[1][1])) { for (number = 0, ++argv[1]; ISDIGIT (*argv[1]); ++argv[1]) number = number * 10 + *argv[1] - '0'; /* Parse any appended option letters with getopt. */ if (*argv[1]) *--argv[1] = '-'; else argv[1] = "-l"; } else argv[1] = "-l"; } while ((c = getopt_long (argc, argv, "n:lbcfqv", long_options, &longind)) != EOF) { if (c == 0) c = long_options[longind].val; switch (c) { case 'n': if (*optarg == '+') { mode |= START; ++optarg; } number = atou (optarg); if (number == -1) { fprintf (stderr, "%s: invalid number `%s'\n", program_name, optarg); exit (1); } break; case 'l': mode &= ~(CHARS | BLOCKS); break; case 'b': mode |= BLOCKS; mode &= ~CHARS; break; case 'c': mode |= CHARS; mode &= ~BLOCKS; break; case 'f': mode |= FOREVER; break; case 'q': header_mode = never; break; case 'v': header_mode = always; break; default: usage (); } } if (number == -1) number = DEFAULT_NUMBER; /* To start printing with item `number' from the start of the file, skip `number' - 1 items. `tail -0s' is actually meaningless, but for Unix compatibility it's treated the same as `tail -1s'. */ if (mode & START) { if (number) --number; } if (mode & BLOCKS) number *= BLOCKSIZE; if (optind < argc - 1 && (mode & FOREVER)) { fprintf (stderr, "%s: cannot follow the ends of multiple files\n", program_name); exit (1); } if (header_mode == always || header_mode == multiple_files && optind < argc - 1) mode |= HEADERS; if (optind == argc) tail_file ("-", mode, number); for (; optind < argc; ++optind) tail_file (argv[optind], mode, number); exit (errors); } void tail_file (filename, mode, number) char *filename; int mode; int number; { int fd; if (!strcmp (filename, "-")) { filename = "standard input"; if (mode & HEADERS) write_header (filename); tail (filename, 0, mode, number); } else { fd = open (filename, O_RDONLY); if (fd == -1) nonfatal_perror ("cannot open", filename); else { if (mode & HEADERS) write_header (filename); tail (filename, fd, mode, number); close (fd); } } } void write_header (filename) char *filename; { static int first_file = 1; if (first_file) { xwrite (1, "==> ", 4); first_file = 0; } else xwrite (1, "\n==> ", 5); xwrite (1, filename, strlen (filename)); xwrite (1, " <==\n", 5); } void tail (filename, fd, mode, number) char *filename; int fd; int mode; int number; { if (mode & (CHARS | BLOCKS)) tail_chars (filename, fd, mode, number); else tail_lines (filename, fd, mode, number); } void tail_chars (filename, fd, mode, number) char *filename; int fd; int mode; int number; { int length; if (mode & START) { if (lseek (fd, number, L_SET) < 0) { /* Reading from a pipe. */ mode &= ~FOREVER; if (start_chars (filename, fd, number) == 0) return; } dump_remainder (filename, fd, mode); } else { length = lseek (fd, 0, L_XTND); if (length >= 0) { if (length <= number) /* The file is shorter than we want, or just the right size, so print the whole file. */ lseek (fd, 0, L_SET); else /* The file is longer than we want, so go back. */ lseek (fd, -number, L_XTND); dump_remainder (filename, fd, mode); } else pipe_chars (filename, fd, number); } } void tail_lines (filename, fd, mode, number) char *filename; int fd; int mode; int number; { int length; if (mode & START) { if (lseek (fd, 0, L_SET) < 0) mode &= ~FOREVER; if (start_lines (filename, fd, number) == 0) return; dump_remainder (filename, fd, mode); } else { length = lseek (fd, 0, L_XTND); if (length >= 0) { if (length && file_lines (filename, fd, number, length) == 0) return; dump_remainder (filename, fd, mode); } else pipe_lines (filename, fd, number); } } /* Print the last `number' lines from the end of file `fd'. Go backward through the file, reading `BUFSIZE' bytes at a time (except probably the first), until we hit the start of the file or have read `number' newlines. `pos' starts out as the length of the file (the offset of the last byte of the file + 1). Return 0 on error, 1 if ok. */ int file_lines (filename, fd, number, pos) char *filename; int fd; int number; int pos; { char buffer[BUFSIZE]; int chars_read; int i; /* Index into `buffer' for scanning. */ if (number == 0) return 1; /** Set `chars_read' to the size of the last, probably partial, buffer; 0 < `chars_read' <= `BUFSIZE'. */ chars_read = pos % BUFSIZE; if (chars_read == 0) chars_read = BUFSIZE; /* Make `pos' a multiple of `BUFSIZE' (0 if the file is short), so that all reads will be on block boundaries, which might increase efficiency. */ pos -= chars_read; lseek (fd, pos, L_SET); chars_read = read (fd, buffer, chars_read); if (chars_read == -1) { nonfatal_perror ("error reading", filename); return 0; } /* Count the incomplete line on files that don't end with a newline. */ if (chars_read && buffer[chars_read - 1] != '\n') --number; do { /* Scan backward, counting the newlines in this bufferfull. */ for (i = chars_read - 1; i >= 0; i--) { /* Have we counted the requested number of newlines yet? */ if (buffer[i] == '\n' && number-- == 0) { /* If this newline wasn't the last character in the buffer, print the text after it. */ if (i != chars_read - 1) xwrite (1, &buffer[i + 1], chars_read - (i + 1)); return 1; } } /* Not enough newlines in that bufferfull. */ if (pos == 0) { /* Not enough lines in the file; print the entire file. */ lseek (fd, 0, L_SET); return 1; } pos -= BUFSIZE; lseek (fd, pos, L_SET); } while ((chars_read = read (fd, buffer, BUFSIZE)) > 0); if (chars_read == -1) { nonfatal_perror ("error reading", filename); return 0; } return 1; } /* Print the last `number' lines from the end of pipe `fd'. Buffer the text as a linked list of LBUFFERs, adding them as needed. */ void pipe_lines (filename, fd, number) char *filename; int fd; int number; { struct linebuffer { int nchars, nlines; char buffer[BUFSIZE]; struct linebuffer *next; }; typedef struct linebuffer LBUFFER; LBUFFER *first, *last, *tmp; int i; /* Index into buffers. */ int total_lines = 0; /* Total number of newlines in all buffers. */ first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER)); first->nchars = first->nlines = 0; tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); /* Input is always read into a fresh buffer. */ while ((tmp->nchars = read (fd, tmp->buffer, BUFSIZE)) > 0) { tmp->nlines = 0; tmp->next = NULL; /* Count the number of newlines just read. */ for (i = 0; i < tmp->nchars; i++) if (tmp->buffer[i] == '\n') ++tmp->nlines; total_lines += tmp->nlines; /* If there is enough room in the last buffer read, just append the new one to it. This is because when reading from a pipe, `nchars' can often be very small. */ if (tmp->nchars + last->nchars < BUFSIZE) { bcopy (tmp->buffer, &last->buffer[last->nchars], tmp->nchars); last->nchars += tmp->nchars; last->nlines += tmp->nlines; } else { /* If there's not enough room, link the new buffer onto the end of the list, then either free up the oldest buffer for the next read if that would leave enough lines, or else malloc a new one. Some compaction mechanism is possible but probably not worthwhile. */ last = last->next = tmp; if (total_lines - first->nlines > number) { tmp = first; total_lines -= first->nlines; first = first->next; } else tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); } } if (tmp->nchars == -1) nonfatal_perror ("error reading", filename); free ((char *) tmp); /* This prevents a core dump when the pipe contains no newlines. */ if (number == 0) goto free_lbuffers; /* Count the incomplete line on files that don't end with a newline. */ if (last->buffer[last->nchars - 1] != '\n') { ++last->nlines; ++total_lines; } /* Run through the list, printing lines. First, skip over unneeded buffers. */ for (tmp = first; total_lines - tmp->nlines > number; tmp = tmp->next) total_lines -= tmp->nlines; /* Find the correct beginning, then print the rest of the file. */ if (total_lines > number) { char *cp; /* Skip `total_lines' - `number' newlines. We made sure that `total_lines' - `number' <= `tmp->nlines'. */ cp = tmp->buffer; for (i = total_lines - number; i; --i) while (*cp++ != '\n') /* Do nothing. */ ; i = cp - tmp->buffer; } else i = 0; xwrite (1, &tmp->buffer[i], tmp->nchars - i); for (tmp = tmp->next; tmp; tmp = tmp->next) xwrite (1, tmp->buffer, tmp->nchars); free_lbuffers: while (first) { tmp = first->next; free ((char *) first); first = tmp; } } /* Print the last `number' characters from the end of pipe `fd'. This is a stripped down version of pipe_lines. */ void pipe_chars (filename, fd, number) char *filename; int fd; int number; { struct charbuffer { int nchars; char buffer[BUFSIZE]; struct charbuffer *next; }; typedef struct charbuffer CBUFFER; CBUFFER *first, *last, *tmp; int i; /* Index into buffers. */ int total_chars = 0; /* Total characters in all buffers. */ first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER)); first->nchars = 0; tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); /* Input is always read into a fresh buffer. */ while ((tmp->nchars = read (fd, tmp->buffer, BUFSIZE)) > 0) { tmp->next = NULL; total_chars += tmp->nchars; /* If there is enough room in the last buffer read, just append the new one to it. This is because when reading from a pipe, `nchars' can often be very small. */ if (tmp->nchars + last->nchars < BUFSIZE) { bcopy (tmp->buffer, &last->buffer[last->nchars], tmp->nchars); last->nchars += tmp->nchars; } else { /* If there's not enough room, link the new buffer onto the end of the list, then either free up the oldest buffer for the next read if that would leave enough characters, or else malloc a new one. Some compaction mechanism is possible but probably not worthwhile. */ last = last->next = tmp; if (total_chars - first->nchars > number) { tmp = first; total_chars -= first->nchars; first = first->next; } else { tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); } } } if (tmp->nchars == -1) nonfatal_perror ("error reading", filename); free ((char *) tmp); /* Run through the list, printing characters. First, skip over unneeded buffers. */ for (tmp = first; total_chars - tmp->nchars > number; tmp = tmp->next) total_chars -= tmp->nchars; /* Find the correct beginning, then print the rest of the file. We made sure that `total_chars' - `number' <= `tmp->nchars'. */ if (total_chars > number) i = total_chars - number; else i = 0; xwrite (1, &tmp->buffer[i], tmp->nchars - i); for (tmp = tmp->next; tmp; tmp = tmp->next) xwrite (1, tmp->buffer, tmp->nchars); while (first) { tmp = first->next; free ((char *) first); first = tmp; } } /* Skip `number' characters from the start of pipe `fd', and print any extra characters that were read beyond that. Return 0 on error, 1 if ok. */ int start_chars (filename, fd, number) char *filename; int fd; int number; { char buffer[BUFSIZE]; int chars_read = 0; while (number > 0 && (chars_read = read (fd, buffer, BUFSIZE)) > 0) number -= chars_read; if (chars_read == -1) { nonfatal_perror ("error reading", filename); return 0; } else if (number < 0) xwrite (1, &buffer[chars_read + number], -number); return 1; } /* Skip `number' lines at the start of file or pipe `fd', and print any extra characters that were read beyond that. Return 0 on error, 1 if ok. */ int start_lines (filename, fd, number) char *filename; int fd; int number; { char buffer[BUFSIZE]; int chars_read = 0; int chars_to_skip = 0; while (number && (chars_read = read (fd, buffer, BUFSIZE)) > 0) { chars_to_skip = 0; while (chars_to_skip < chars_read) if (buffer[chars_to_skip++] == '\n' && --number == 0) break; } if (chars_read == -1) { nonfatal_perror ("error reading", filename); return 0; } else if (chars_to_skip < chars_read) xwrite (1, &buffer[chars_to_skip], chars_read - chars_to_skip); return 1; } void dump_remainder (filename, fd, mode) char *filename; int fd; int mode; { char buffer[BUFSIZE]; int chars_read; output: while ((chars_read = read (fd, buffer, BUFSIZE)) > 0) xwrite (1, buffer, chars_read); if (chars_read == -1) fatal_perror ("error reading", filename); if (mode & FOREVER) { sleep (1); goto output; } } /* Write plus error check. */ void xwrite (fd, buffer, count) int fd; int count; char *buffer; { fd = write (fd, buffer, count); if (fd != count) fatal_perror ("write", "error"); } /* Allocate memory dynamically, with error check. */ char * xmalloc (size) int size; { char *p; p = malloc ((unsigned) size); if (p == NULL) { fprintf (stderr, "%s: virtual memory exhausted\n", program_name); exit (1); } return p; } /* Convert `str', a string of ASCII digits, into an unsigned integer. Return -1 if `str' does not represent a valid unsigned integer. */ int atou (str) char *str; { int value; for (value = 0; ISDIGIT (*str); ++str) value = value * 10 + *str - '0'; return *str ? -1 : value; } void nonfatal_perror (s1, s2) char *s1, *s2; { fprintf (stderr, "%s: %s ", program_name, s1); perror (s2); errors = 1; } void fatal_perror (s1, s2) char *s1, *s2; { nonfatal_perror (s1, s2); exit (1); } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-n [+]#] [-lbcfqv] [+number [+]#] [+lines] [+blocks]\n\ [+chars] [+follow] [+quiet] [+silent] [+verbose] [file...]\n\ \n\ %s [+/-#lbcfqv] [file...]\n", program_name, program_name); exit (1); } (s1, s2) char *s1, *s2; { nonfatal_perror (s1, s2); exit (1); } /* Return `name' with any leading path stripped off. */ char * basename (name) char *name; { char *base; base = rindex (name, '/'); return base ? base + 1 : name; } void usage () { fprintf (stderr, "\ Usage: %s [-n [+]#] [-lbcfqv] [+number [+]#] [fileutils/getopt.h 644 473 0 5372 4702305052 7542 /* declarations for getopt Copyright (C) 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Describe the long-named options requested by the application. _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: 0 if the option does not take an argument, 1 if the option requires an argument, 2 if the option takes an optional argument. If the field `flag' is nonzero, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. */ struct option { char *name; int has_arg; int *flag; int val; }; extern struct option *_getopt_long_options; /* Name of long-named option actually found. Only changed when a long-named option is found. */ extern char *_getopt_option_name; /* The index in GETOPT_LONG_OPTIONS of the long-named option found. Only valid when a long-named option has been found by the most recent call to `getopt'. */ extern int option_index; #ifdef __STDC__ int getopt (int, char **, char *); int getopt_long (int, char **, char *, struct option *, int *); #else int getopt (); int getopt_long (); #endif topt_long_options; /* Name of long-named option actually found. Only changed when a long-named option is found. */ extern char *_getopt_option_name; /* The index in GETOPT_LONG_OPTIONS of the long-named option found. Only valid when a long-named optionfileutils/system.h 644 473 0 4401 4702305052 7554 /* system-dependent definitions for fileutils programs. Copyright (C) 1989 Free Software Foundation, Inc. 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Include sys/types.h before this file. */ #include #include #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif #ifdef USG #include #include char *getcwd (); #define index strchr #define rindex strrchr #define bcopy(from, to, len) memcpy ((to), (from), (len)) #define getwd(buf) getcwd ((buf), MAXPATHLEN + 1) #define L_SET 0 #define L_INCR 1 #define L_XTND 2 #define F_OK 0 #define W_OK 2 #define R_OK 4 #include #else char *getwd (); #include #endif #ifdef __GNUC__ #define alloca __builtin_alloca #else #ifdef sparc #include #else char *alloca (); #endif #endif #ifdef USG #define ST_BLKSIZE(statbuf) BLKSIZE #else #define ST_BLKSIZE(statbuf) ((statbuf).st_blksize) #endif #ifdef USG #include #else #include #endif #ifdef USGr3 # include # define direct dirent # define NLENGTH(direct) (strlen((direct)->d_name)) # ifndef DIRSIZ /* Defined in param.h sometimes. */ # define DIRSIZ(dp) DIRBUF # endif #else # define NLENGTH(direct) ((direct)->d_namlen) # ifdef USG # ifdef SYSNDIR # include # else # include # endif # else /* must be BSD */ # include # include # endif #endif /* Extract the number of blocks from a `struct stat *'. */ #ifdef USG #define ST_NBLOCKS(stat) (((stat).st_size + BLKSIZE - 1) / BLKSIZE) #else #define ST_NBLOCKS(stat) ((stat).st_blocks) #endif #ifndef S_IFLNK #define lstat stat #endif s. */ # define DIRSIZ(dp) DIRBUF # endif #else # define NLENGTH(direct) ((direct)->d_namlen) # ifdef USG # ifdef SYSNDIR # include # else # include # endif # else /* must be BSD */ # include # include