]> git.tuebingen.mpg.de Git - adu.git/commitdiff
Merge commit 'meins/master'
authorAndre Noll <maan@systemlinux.org>
Mon, 10 Nov 2008 09:23:15 +0000 (10:23 +0100)
committerAndre Noll <maan@systemlinux.org>
Mon, 10 Nov 2008 09:23:15 +0000 (10:23 +0100)
19 files changed:
COPYING [new file with mode: 0644]
README
adu.c
adu.ggo
adu.h
create.c
error.h
fd.c
format.c
format.h
gcc-compat.h
interactive.c
select.c
select.ggo
select.h
sha1.c [deleted file]
string.c
user.c
user.h

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..3912109
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
index 5648ac5de9c9c12b0568243d6cea782a53ae45c3..7e19d779fb0b576b989291b7422fdf24028d2f3d 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
 adu creates a database containing disk usage statistics of a given
 directory. It allows to query that database to quickly retrieve usage
-patterns of subdirectories and/or files owned by a given user.
+patterns of subdirectories containing files owned by a given user.
 
 Four different output modes are available: global list, global summary,
 user list, user summary. The format of the output may be customized
diff --git a/adu.c b/adu.c
index 657adb244dd138281f68d10c48487dcd4d567e86..dda9fd24ccd33fa1875d0a8f2e8a33d02ba2d723 100644 (file)
--- a/adu.c
+++ b/adu.c
@@ -1,4 +1,10 @@
-/** \file adu.c The main functions used by all modes of operation. */
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file adu.c \brief The main functions used by all modes of operation. */
 #include "adu.h"
 #include <dirent.h> /* readdir() */
 #include <pwd.h>
@@ -23,8 +29,6 @@ struct gengetopt_args_info conf;
 /** Options passed to --select-options. */
 struct select_args_info select_conf;
 
-/** The number of different uids found so far. */
-uint32_t num_uids = 0;
 
 /**
  * The table containing the directory names and statistics.
@@ -108,6 +112,7 @@ static void close_dir_table(void)
 
        if (!dir_table)
                return;
+       NOTICE_LOG("closing dir table\n");
        ret = osl(osl_close_table(dir_table, OSL_MARK_CLEAN));
        if (ret < 0)
                ERROR_LOG("failed to close dir table: %s\n", adu_strerror(-ret));
@@ -119,7 +124,6 @@ void close_all_tables(void)
 {
        close_dir_table();
        close_user_tables();
-       free_hash_table();
 }
 
 static void signal_handler(int s)
@@ -149,15 +153,20 @@ static int init_signals(void)
 
 int open_dir_table(int create)
 {
+
+       if (dir_table)
+               return 1;
        dir_table_desc.dir = adu_strdup(conf.database_dir_arg);
 
        if (create) {
+               NOTICE_LOG("creating dir table\n");
                int ret = osl(osl_create_table(&dir_table_desc));
                if (ret < 0) {
                        free((char *)dir_table_desc.dir);
                        return ret;
                }
        }
+       INFO_LOG("opening dir table\n");
        return osl(osl_open_table(&dir_table_desc, &dir_table));
 }
 
@@ -185,7 +194,6 @@ static int check_args(void)
 static int print_complete_help_and_die(void)
 {
        const char **line;
-       select_cmdline_parser_init(&select_conf);
 
        printf("%s-%s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
        printf("%s\n\n", gengetopt_args_info_purpose);
@@ -208,6 +216,8 @@ static int print_complete_help_and_die(void)
 
        printf("Interactive commands:\n");
        print_interactive_help();
+       cmdline_parser_free(&conf);
+       select_cmdline_parser_free(&select_conf);
        exit(EXIT_FAILURE);
 }
 
@@ -215,16 +225,19 @@ int main(int argc, char **argv)
 {
        int ret;
        struct cmdline_parser_params params = {
-               .override = 0,
+               .override = 1,
                .initialize = 1,
                .check_required = 0,
                .check_ambiguity = 0,
                .print_errors = 0
        };
+       select_cmdline_parser_init(&select_conf);
+       cmdline_parser_init(&conf);
        /* ignore errors and print complete help if --help was given */
        cmdline_parser_ext(argc, argv, &conf, &params);
        if (conf.help_given || conf.detailed_help_given)
                print_complete_help_and_die();
+       cmdline_parser_free(&conf);
        params.check_required = 1;
        params.check_ambiguity = 1;
        params.print_errors = 1;
@@ -248,9 +261,12 @@ int main(int argc, char **argv)
        if (ret < 0)
                goto out;
 out:
+       close_all_tables();
        if (ret < 0) {
                ERROR_LOG("%s\n", adu_strerror(-ret));
                return -EXIT_FAILURE;
        }
+       cmdline_parser_free(&conf);
+       select_cmdline_parser_free(&select_conf);
        return EXIT_SUCCESS;
 }
diff --git a/adu.ggo b/adu.ggo
index 8c23e3611f657ced1fca94635e2d82ff4faba490..2ecdea4b6b3116b6e6e2f32742ae8966778910e3 100644 (file)
--- a/adu.ggo
+++ b/adu.ggo
@@ -41,26 +41,13 @@ option "loglevel" l
 #~~~~~~~~~~~~~~~~~~
 "Set loglevel (0-6)"
 int typestr="level"
-default="3"
+default="4"
 optional
 details="
        Log messages are always written to stderr while normal output
        goes to stdout. Lower values mean more verbose logging.
 "
 
-option "paths" p
-#~~~~~~~~~~~~~~~
-"files to take into account"
-string typestr="pattern"
-optional
-details="
-       Shell wildcard pattern that must match a file in order to be
-       included in the database in --create mode or in the output
-       for --select mode. Only the part of the filename below the
-       base directory is matched against the pattern. The default
-       is to take all files into account. See fnmatch(3) for details.
-"
-
 ###############
 section "Modes"
 ###############
@@ -80,10 +67,10 @@ groupoption "create" C
 "Create a new database"
 group="mode"
 details="
-       Traverse the given directory and track disk user on a per-user
-       basis. Results are stored in N + 1 osl tables where N is
-       the number of uids that own at least one regular file in
-       that directory.
+       Traverse the given directory and track disk usage on a
+       per-user basis. Results are stored in N + 1 osl tables where
+       N is the number of uids that own at least one regular file
+       in that directory.
 "
 
 groupoption "interactive" I
@@ -91,7 +78,7 @@ groupoption "interactive" I
 "activate interactive mode"
 group="mode"
 details="
-       In this mode, adu reads commands from stdin.  This makes it
+       In this mode, adu reads commands from stdin. This makes it
        possible to run different select queries without opening the
        underlying osl database for each query (which is expensive).
 
@@ -105,8 +92,9 @@ groupoption "select" S
 group="mode"
 details="
        This option prints statistics about matching subdirectories
-       to stdout. The output can be customized by specifying select
-       options, see below.
+       to stdout, to an output file or pipes the output to a given
+       command, depending on the --output option. The output format
+       can be customized by specifying select options, see below.
 "
 
 ##############################
@@ -120,11 +108,10 @@ string typestr="path"
 dependon="create"
 optional
 details="
-       The base directory to be traversed recursively. Must be
-       given if --create mode was selected. A warning message is
-       printed for each subdirectory that could not be read because
-       of insufficient permission. These directories will be ignored
-       when computing statistics.
+       The base directory to be traversed recursively. A warning
+       message is printed for each subdirectory that could not be
+       read because of insufficient permissions. These directories
+       will be ignored when computing statistics.
 "
 
 option "one-file-system" x
@@ -163,7 +150,7 @@ optional
 dependon="select"
 details="
        This option takes a string whose content is another set of
-       options as described below.  Select options may be specified
+       options as described below. Select options may be specified
        either directly in select mode, in which case you have use
        quotes to prevent the select options from being interpreted
        as adu options, or via the \"set\" command in interactive mode.
diff --git a/adu.h b/adu.h
index 509c0a2248ea392decce4e2891a6c89606d34cc3..74083ec3f3eb6e849a2b2294af99aefb99f384b0 100644 (file)
--- a/adu.h
+++ b/adu.h
@@ -134,7 +134,6 @@ enum dir_table_columns {
        NUM_DT_COLUMNS
 };
 
-extern uint32_t num_uids;
 extern struct osl_table *dir_table;
 
 /** The adu command line options. */
index d08da1c026909de78ccd4386d22d9545de139c80..3a221ca58947eab378543717714593270a6ceb24 100644 (file)
--- a/create.c
+++ b/create.c
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file create.c The create mode of adu. */
+/** \file create.c \brief The create mode of adu. */
 
 #include <dirent.h> /* readdir() */
 #include "format.h"
@@ -58,9 +58,7 @@ static int update_user_row(struct osl_table *t, uint64_t dir_num,
                objects[UT_BYTES].size = sizeof(*add);
                objects[UT_FILES].data = &num_files;
                objects[UT_FILES].size = sizeof(num_files);
-               INFO_LOG("######################### ret: %d\n", ret);
                ret = osl(osl_add_row(t, objects));
-               INFO_LOG("######################### ret: %d\n", ret);
                return ret;
        } else { /* add size and increment file count */
                uint64_t num;
@@ -133,7 +131,7 @@ static int scan_dir(char *dirname, uint64_t *parent_dir_num)
                dir_size += size;
                dir_files++;
                uid = s.st_uid;
-               ret = search_uid(uid, NULL, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
+               ret = create_user_table(uid, &ui);
                if (ret < 0)
                        goto out;
                ui->bytes += size;
@@ -174,6 +172,5 @@ int com_create(void)
                goto out;
        ret = write_uid_file();
 out:
-       close_all_tables();
        return ret;
 }
diff --git a/error.h b/error.h
index 00fbbc867dd4d488c4886283c391950d4fcb7f34..9874003903b202fceecb53ee318cc3e1982419df 100644 (file)
--- a/error.h
+++ b/error.h
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 /**
  * This bit indicates whether a number is considered a system error code.
  * If yes, the system errno is just the result of clearing this bit from
@@ -32,6 +38,8 @@
        _ERROR(UNIT, "no unit allowed here") \
        _ERROR(BAD_UNIT, "invalid unit specifier") \
        _ERROR(BAD_ATOM, "invalid atom") \
+       _ERROR(BAD_OUTPUT_ARG, "invalid name for output") \
+       _ERROR(REGEX, "regular expression error")
 
 
 /**
diff --git a/fd.c b/fd.c
index 5c125faaf90e9cdd5e7d725a933c30eddc6d7e32..e1d243e1e8f34006326bdfab72f24455ad801eff 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file fd.c Helper functions for file descriptor handling. */
+/** \file fd.c \brief Helper functions for file descriptor handling. */
 
 #include <sys/types.h>
 #include <dirent.h>
index 37bb087ba02fff7f472d14b0739e715626263c26..f1d1247074da8c97c41368aa3663b037dbbfc4bd 100644 (file)
--- a/format.c
+++ b/format.c
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file format.c Functions for pretty-printing numbers and strings. */
+/** \file format.c \brief Functions for pretty-printing numbers and strings. */
 
 #include <dirent.h> /* readdir() */
 #include "adu.h"
@@ -457,5 +457,7 @@ char *format_items(struct format_info *info, union atom_value *values)
                buf = adu_strcat(buf, val);
                free(val);
        }
+       if (!buf)
+               buf = adu_strdup("");
        return buf;
 }
index 4446f49c7c7908477dfd6fb546291e0102eadaaf..9761ea2548e9362db9fc37cad284dfa3a328c5f7 100644 (file)
--- a/format.h
+++ b/format.h
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 /** \file format.h Exported symbols from \p format.c. */
 
 /** The possible types of format string directives (aka atoms). */
index 3d5fb28aa45d9ab3d4350a06ad924281ff37bd47..df80001c941142ecb609a68d0a8fc8b66615f7b9 100644 (file)
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 # define inline                inline __attribute__ ((always_inline))
 # define __noreturn    __attribute__ ((noreturn))
 # define __malloc      __attribute__ ((malloc))
index 9460877e344e8f8452b0f0d20f1d6ceef83dd39e..4c0392784604d4af06701dcdee355197d7ba868a 100644 (file)
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 #include <ctype.h> /* isspace() */
 
 #include "adu.h"
@@ -30,7 +36,8 @@ static struct format_info *fi;
        INTERACTIVE_COMMAND(set, "change the current configuration") \
        INTERACTIVE_COMMAND(reset, "reset configuration to defaults") \
        INTERACTIVE_COMMAND(help, "show list of commands and one-line descriptions") \
-       INTERACTIVE_COMMAND(run, "start the query according to the current configuration")
+       INTERACTIVE_COMMAND(run, "start the query according to the current configuration") \
+       INTERACTIVE_COMMAND(source, "read and execute interactive commands from a file")
 
 
 #define INTERACTIVE_COMMAND(name, desc) \
@@ -82,16 +89,18 @@ void print_interactive_help(void)
 
 static int icom_reset(__a_unused char *line)
 {
+       NOTICE_LOG("resetting configuration to default\n");
        free_format_info(fi);
        fi = NULL;
        free(admissible_uids);
        admissible_uids = NULL;
        select_cmdline_parser_init(&select_conf);
-       return 1;
+       return parse_select_options(NULL, NULL, &admissible_uids, &fi);
 }
 
 static int icom_set(char *line)
 {
+       int ret;
        struct select_cmdline_parser_params params = {
                .override = 1,
                .initialize = 0,
@@ -108,7 +117,10 @@ static int icom_set(char *line)
        fi = NULL;
        free(admissible_uids);
        admissible_uids = NULL;
-       return parse_select_options(line, &params, &admissible_uids, &fi);
+       ret = parse_select_options(line, &params, &admissible_uids, &fi);
+       if (ret >= 0)
+               return ret;
+       return icom_reset(NULL);
 }
 
 static int exec_interactive_command(char *line)
@@ -142,7 +154,7 @@ static int exec_interactive_command(char *line)
        else {
                *args = '\0';
                args++;
-               /* let p point to the next non-whitespace char */
+               /* let args point to the next non-whitespace char */
                args += strspn(args, delim);
                if (!*args)
                        args = NULL;
@@ -159,6 +171,25 @@ static int exec_interactive_command(char *line)
        return ret;
 }
 
+static int icom_source(char *args)
+{
+       char line[255];
+       FILE *src = fopen(args, "r");
+       int ret;
+
+       if (!src)
+               return -ERRNO_TO_ERROR(errno);
+       while (fgets(line, sizeof(line), src)) {
+               ret = exec_interactive_command(line);
+               if (ret < 0)
+                       goto out;
+       }
+       ret = 1;
+out:
+       fclose(src);
+       return ret;
+}
+
 int com_interactive(void)
 {
        char line[255];
@@ -166,6 +197,11 @@ int com_interactive(void)
 
        select_cmdline_parser_init(&select_conf);
        ret = parse_select_options(NULL, NULL, &admissible_uids, &fi);
+       if (ret< 0)
+               return ret;
+       ret = read_uid_file();
+       if (ret < 0)
+               return ret;
        while (read_input_line(line, sizeof(line)) >= 0) {
                ret = exec_interactive_command(line);
                if (ret < 0)
index e66b2dd41a28127dcada1a8b3cbe587ea5d464a6..209bf144a9e99e1bc2a27e67139459a9b2e6f302 100644 (file)
--- a/select.c
+++ b/select.c
@@ -4,9 +4,12 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file select.c The select mode of adu. */
+/** \file select.c \brief The select mode of adu. */
 
 #include <dirent.h> /* readdir() */
+#include <sys/types.h>
+#include <regex.h>
+
 #include "format.h"
 #include "adu.h"
 #include "gcc-compat.h"
@@ -67,6 +70,21 @@ struct atom user_list_atoms[] = {
 enum user_list_atoms {USER_LIST_ATOMS};
 #undef ATOM
 
+/* user list header */
+#define USER_LIST_HEADER_ATOMS \
+       ATOM(pw_name, STRING) \
+       ATOM(uid, ID)
+
+#define ATOM(x, y) { .name = #x, .type = AT_ ## y},
+struct atom user_list_header_atoms[] = {
+       USER_LIST_HEADER_ATOMS
+       {.name = NULL}
+};
+#undef ATOM
+#define ATOM(x, y) ulha_ ## x,
+enum user_list_header_atoms {USER_LIST_HEADER_ATOMS};
+#undef ATOM
+
 /* user summary */
 #define USER_SUMMARY_ATOMS \
        ATOM(pw_name, STRING) \
@@ -90,6 +108,8 @@ struct global_list_info {
        int ret;
        int osl_errno;
        struct format_info *fi;
+       regex_t *preg;
+       int inverse_matching;
 };
 
 struct global_summary_info {
@@ -99,6 +119,8 @@ struct global_summary_info {
        uint64_t num_files;
        /** Global bytes count. */
        uint64_t num_bytes;
+       regex_t *preg;
+       int inverse_matching;
        int ret;
        int osl_errno;
 };
@@ -107,16 +129,29 @@ struct user_list_info {
        uint32_t count;
        struct user_info *ui;
        struct format_info *fi;
+       regex_t *preg;
+       int inverse_matching;
        int ret;
        int osl_errno;
 };
 
+struct user_list_format_info {
+       struct format_info *fi;
+       struct format_info *header_fi;
+};
+
 struct user_summary_info {
        struct user_info *ui;
        int ret;
        int osl_errno;
+       regex_t *preg;
+       int inverse_matching;
 };
 
+struct user_summary_line_info {
+       struct format_info *fi;
+       uint32_t count;
+};
 
 static FILE *output_file;
 
@@ -238,6 +273,61 @@ static int get_num_user_bytes(struct osl_row *row, struct user_info *ui,
        return 1;
 }
 
+static void free_regex(regex_t *preg)
+{
+       if (!preg)
+               return;
+       regfree(preg);
+       free(preg);
+}
+
+static int compile_regex(regex_t **preg, int *invert)
+{
+       int ret;
+       size_t size;
+       char *buf, *p = select_conf.pattern_arg;
+
+       if (!select_conf.pattern_given || !p[0]) {
+               *preg = NULL;
+               return 0;
+       }
+       if (p[0] == '!') {
+               if (!p[1]) {
+                       *preg = NULL;
+                       return -E_REGEX;
+               }
+               *invert = 1;
+               p++;
+       } else
+               *invert = 0;
+       *preg = adu_malloc(sizeof(regex_t));
+       ret = regcomp(*preg, p, 0);
+       if (!ret)
+               return 1;
+       size = regerror(ret, *preg, NULL, 0);
+       buf = adu_malloc(size);
+       regerror(ret, *preg, buf, size);
+       ERROR_LOG("%s\n", buf);
+       free(buf);
+       free_regex(*preg);
+       *preg = NULL;
+       return -E_REGEX;
+}
+
+static int dir_is_admissible(char *dirname, regex_t *preg, int inverse_matching)
+{
+       int ret;
+
+       if (!preg)
+               return 1;
+       ret = regexec(preg, dirname, 0, NULL, 0);
+       if (ret == REG_NOMATCH && !inverse_matching)
+               return 0;
+       if (ret != REG_NOMATCH && inverse_matching)
+               return 0;
+       return 1;
+}
+
 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
 {
        if (ret >= 0)
@@ -269,6 +359,17 @@ static int global_summary_loop_function(struct osl_row *row, void *data)
        int ret;
        uint64_t num;
 
+       if (gsi->preg) {
+               char *dirname;
+               ret = get_dir_name_of_row(row, &dirname);
+               if (ret < 0)
+                       goto err;
+               ret = dir_is_admissible(dirname, gsi->preg, gsi->inverse_matching);
+               free(dirname);
+               if (!ret)
+                       return 1;
+       }
+
        ret = get_num_files_of_row(row, &num);
        if (ret < 0)
                goto err;
@@ -291,6 +392,8 @@ static int print_global_summary(struct format_info *fi)
        int ret;
        char *buf;
        struct global_summary_info gsi = {.num_dirs = 0};
+       char *header = select_conf.header_given? select_conf.header_arg :
+               "Global summary\n";
 
        union atom_value values[] = {
                [gsa_dirs] = {.num_value = 0ULL},
@@ -298,18 +401,21 @@ static int print_global_summary(struct format_info *fi)
                [gsa_size] = {.num_value =  0ULL}
        };
 
+       ret = compile_regex(&gsi.preg, &gsi.inverse_matching);
+       if (ret < 0)
+               return ret;
        ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
                global_summary_loop_function, &gsi.ret, &gsi.osl_errno);
+       free_regex(gsi.preg);
        if (ret < 0)
                return ret;
        values[gsa_dirs].num_value = (long long unsigned)gsi.num_dirs;
        values[gsa_files].num_value = (long long unsigned)gsi.num_files;
        values[gsa_size].num_value = (long long unsigned)gsi.num_bytes;
-       if (!select_conf.no_headers_given) {
-               ret = output("Global summary\n");
-               if (ret < 0)
-                       return ret;
-       }
+
+       ret = output("%s", header);
+       if (ret < 0)
+               return ret;
        buf = format_items(fi, values);
        ret = output("%s", buf);
        free(buf);
@@ -322,6 +428,16 @@ static int user_summary_loop_function(struct osl_row *row, void *data)
        uint64_t num;
        int ret;
 
+       if (usi->preg) {
+               char *dirname;
+               ret = get_dir_name_of_row(row, &dirname);
+               if (ret < 0)
+                       goto err;
+               ret = dir_is_admissible(dirname, usi->preg, usi->inverse_matching);
+               free(dirname);
+               if (!ret)
+                       return 1;
+       }
        ret = get_num_user_files(row, usi->ui, &num);
        if (ret < 0)
                goto err;
@@ -341,14 +457,19 @@ err:
 static int compute_user_summary(struct user_info *ui, __a_unused void *data)
 {
        struct user_summary_info usi = {.ui = ui};
+       int ret = compile_regex(&usi.preg, &usi.inverse_matching);
 
-       return adu_loop_reverse(ui->table, UT_BYTES, &usi, user_summary_loop_function,
+       if (ret < 0)
+               return ret;
+       ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_summary_loop_function,
                &usi.ret, &usi.osl_errno);
+       free_regex(usi.preg);
+       return ret;
 }
 
 static int print_user_summary_line(struct user_info *ui, void *data)
 {
-       struct format_info *fi = data;
+       struct user_summary_line_info *usli = data;
        union atom_value values[] = {
                [usa_pw_name] = {.string_value = ui->pw_name?
                        ui->pw_name : "?"},
@@ -358,18 +479,22 @@ static int print_user_summary_line(struct user_info *ui, void *data)
                [usa_size] = {.num_value =  (long long unsigned)ui->bytes}
        };
        char *buf;
-       int ret;
+       int ret = -E_LOOP_COMPLETE;
 
-       buf = format_items(fi, values);
+       if (!usli->count)
+               return ret;
+
+       buf = format_items(usli->fi, values);
        ret = output("%s", buf);
        free(buf);
+       usli->count--;
        return ret;
 }
 
-static int name_comp(const void *a, const void *b)
+static int name_comp(struct user_info *a, struct user_info *b)
 {
-       char *x = ((struct user_info *)a)->pw_name;
-       char *y = ((struct user_info *)b)->pw_name;
+       char *x = a->pw_name;
+       char *y = b->pw_name;
 
        if (!x)
                return 1;
@@ -378,56 +503,65 @@ static int name_comp(const void *a, const void *b)
        return strcmp(x, y);
 }
 
-static int uid_comp(const void *a, const void *b)
+static int uid_comp(struct user_info *a, struct user_info *b)
 {
-       return -NUM_COMPARE(((struct user_info *)a)->uid,
-               ((struct user_info *)b)->uid);
+       return -NUM_COMPARE(a->uid, b->uid);
 }
 
-static int dir_count_comp(const void *a, const void *b)
+static int dir_count_comp(struct user_info *a, struct user_info *b)
 {
-       return NUM_COMPARE(((struct user_info *)a)->dirs,
-               ((struct user_info *)b)->dirs);
+       return NUM_COMPARE(a->dirs, b->dirs);
 }
 
-static int file_count_comp(const void *a, const void *b)
+static int file_count_comp(struct user_info *a, struct user_info *b)
 {
-       return NUM_COMPARE(((struct user_info *)a)->files,
-               ((struct user_info *)b)->files);
+       return NUM_COMPARE(a->files, b->files);
 }
 
-static int size_comp(const void *a, const void *b)
+static int size_comp(struct user_info *a, struct user_info *b)
 {
-       return NUM_COMPARE(((struct user_info *)a)->bytes,
-               ((struct user_info *)b)->bytes);
+       return NUM_COMPARE(a->bytes, b->bytes);
 }
 
 static int print_user_summary(struct format_info *fi)
 {
-       /*
-        * The comparators for sorting the user summary.
-        *
-        * This is an array of pointers to functions taking two constant void *
-        * pointers and returning an int.
-        */
-       static int (*summary_comparators[])(const void *, const void *) = {
-               [user_summary_sort_arg_name] = name_comp,
-               [user_summary_sort_arg_uid] = uid_comp,
-               [user_summary_sort_arg_dir_count] = dir_count_comp,
-               [user_summary_sort_arg_file_count] = file_count_comp,
-               [user_summary_sort_arg_size] = size_comp,
+       int ret;
+       int (*comp)(struct user_info *a, struct user_info *b);
+       struct user_summary_line_info usli = {
+               .fi = fi,
+               .count = select_conf.limit_arg
        };
+       char *header = select_conf.header_given? select_conf.header_arg :
+               "User summary\n";
 
-       if (!select_conf.no_headers_given) {
-               int ret = output("User summary\n");
-               if (ret < 0)
-                       return ret;
-       }
-       int ret = for_each_admissible_user(compute_user_summary, fi);
+       ret = output("%s", header);
+       if (ret < 0)
+               return ret;
+       ret = for_each_admissible_user(compute_user_summary, NULL);
        if (ret < 0)
                return ret;
-       sort_hash_table(summary_comparators[select_conf.user_summary_sort_arg]);
-       return for_each_admissible_user(print_user_summary_line, fi);
+       switch (select_conf.user_summary_sort_arg) {
+       case user_summary_sort_arg_name:
+               comp = name_comp;
+               break;
+       case user_summary_sort_arg_uid:
+               comp = uid_comp;
+               break;
+       case user_summary_sort_arg_dir_count:
+               comp = dir_count_comp;
+               break;
+       case user_summary_sort_arg_file_count:
+               comp = file_count_comp;
+               break;
+       case user_summary_sort_arg_size:
+               comp = size_comp;
+               break;
+       }
+       sort_hash_table(comp);
+       ret = for_each_admissible_user(print_user_summary_line, &usli);
+       if (ret == -E_LOOP_COMPLETE)
+               ret = 1;
+       return ret;
 }
 
 static int user_list_loop_function(struct osl_row *row, void *data)
@@ -443,13 +577,22 @@ static int user_list_loop_function(struct osl_row *row, void *data)
        };
        uint64_t num;
        int ret;
-       char *dirname, *buf;
+       char *dirname = NULL, *buf;
 
        check_signals();
        ret = -E_LOOP_COMPLETE;
        if (!uli->count)
                goto err;
 
+       ret = get_dir_name_of_user_row(row, uli->ui, &dirname);
+       if (ret < 0)
+               goto err;
+       if (!dir_is_admissible(dirname, uli->preg, uli->inverse_matching)) {
+               free(dirname);
+               return 1;
+       }
+       values[ula_dirname].string_value = dirname;
+
        ret = get_num_user_files(row, uli->ui, &num);
        if (ret < 0)
                goto err;
@@ -460,13 +603,9 @@ static int user_list_loop_function(struct osl_row *row, void *data)
                goto err;
        values[ula_size].num_value = num;
 
-       ret = get_dir_name_of_user_row(row, uli->ui, &dirname);
-       if (ret < 0)
-               goto err;
-       values[ula_dirname].string_value = dirname;
-
        buf = format_items(uli->fi, values);
        free(dirname);
+       dirname = NULL;
        ret = output("%s", buf);
        free(buf);
        if (ret < 0)
@@ -474,6 +613,7 @@ static int user_list_loop_function(struct osl_row *row, void *data)
        uli->count--;
        return ret;
 err:
+       free(dirname);
        uli->ret = ret;
        uli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
        return ret;
@@ -481,26 +621,51 @@ err:
 
 static int print_user_list(struct user_info *ui, void *data)
 {
-       struct format_info *fi = data;
+       struct user_list_format_info *ulfi = data;
        int ret;
-       enum user_table_columns sort_column = UT_BYTES;
+       enum user_table_columns sort_column;
        struct user_list_info uli = {
                .ui = ui,
-               .fi = fi,
+               .fi = ulfi->fi,
                .count = select_conf.limit_arg
        };
+       union atom_value header_values[] = {
+               [ulha_uid] = {.num_value = (long long unsigned)ui->uid},
+               [ulha_pw_name] = {.string_value = ui->pw_name?
+                       ui->pw_name : "?"}
+       };
+       char *buf = format_items(ulfi->header_fi, header_values);
 
+       ret = output("%s", buf);
+       free(buf);
+       if (ret < 0)
+               return ret;
        if (select_conf.list_sort_arg == list_sort_arg_file_count)
                sort_column = UT_FILES;
+       else
+               sort_column = UT_BYTES;
 
-       if (!select_conf.no_headers_given) {
-               ret = output("%s (uid %u)\n",
-                       ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid);
-               if (ret < 0)
-                       return ret;
-       }
-       return adu_loop_reverse(ui->table, sort_column, &uli, user_list_loop_function,
-               &uli.ret, &uli.osl_errno);
+       ret = compile_regex(&uli.preg, &uli.inverse_matching);
+       if (ret < 0)
+               return ret;
+       ret = adu_loop_reverse(ui->table, sort_column, &uli,
+               user_list_loop_function, &uli.ret, &uli.osl_errno);
+       free_regex(uli.preg);
+       return ret;
+}
+
+static int print_user_lists(struct format_info *fi)
+{
+       struct user_list_format_info ulfi = {.fi = fi};
+       char *header_fmt = select_conf.header_given?
+               select_conf.header_arg : "uid %(uid)(%(pw_name)):\n";
+       int ret = parse_format_string(header_fmt,
+               user_list_header_atoms, &ulfi.header_fi);
+       if (ret < 0)
+               return ret;
+       ret = for_each_admissible_user(print_user_list, &ulfi);
+       free_format_info(ulfi.header_fi);
+       return ret;
 }
 
 static int global_list_loop_function(struct osl_row *row, void *data)
@@ -512,7 +677,7 @@ static int global_list_loop_function(struct osl_row *row, void *data)
                [gla_dirname] = {.string_value = NULL}
        };
        uint64_t num_files, num_bytes;
-       char *dirname, *buf;
+       char *dirname = NULL, *buf;
        int ret;
 
        check_signals();
@@ -520,6 +685,15 @@ static int global_list_loop_function(struct osl_row *row, void *data)
        if (!gli->count)
                goto err;
 
+       ret = get_dir_name_of_row(row, &dirname);
+       if (ret < 0)
+               goto err;
+       if (!dir_is_admissible(dirname, gli->preg, gli->inverse_matching)) {
+               free(dirname);
+               return 1;
+       }
+       values[gla_dirname].string_value = dirname;
+
        ret = get_num_files_of_row(row, &num_files);
        if (ret < 0)
                goto err;
@@ -530,13 +704,9 @@ static int global_list_loop_function(struct osl_row *row, void *data)
                goto err;
        values[gla_size].num_value = (long long unsigned)num_bytes;
 
-       ret = get_dir_name_of_row(row, &dirname);
-       if (ret < 0)
-               goto err;
-       values[gla_dirname].string_value = dirname;
-
        buf = format_items(gli->fi, values);
        free(dirname);
+       dirname = NULL;
        ret = output("%s", buf);
        free(buf);
        if (ret < 0)
@@ -545,6 +715,7 @@ static int global_list_loop_function(struct osl_row *row, void *data)
                gli->count--;
        return ret;
 err:
+       free(dirname);
        gli->ret = ret;
        gli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
        return -1;
@@ -553,63 +724,156 @@ err:
 static int print_global_list(struct format_info *fi)
 {
        int ret;
-       enum dir_table_columns sort_column = DT_BYTES;
+       enum dir_table_columns sort_column;
        struct global_list_info gli = {
                .fi = fi,
                .count = select_conf.limit_arg
        };
+       char *header = select_conf.header_given?
+               select_conf.header_arg : "Global list\n";
 
-       if (!select_conf.no_headers_given) {
-               ret = output("Global list\n");
-               if (ret < 0)
-                       return ret;
-       }
+       ret = output("%s", header);
+       if (ret < 0)
+               return ret;
        if (select_conf.list_sort_arg == list_sort_arg_file_count)
                sort_column = DT_FILES;
-       return adu_loop_reverse(dir_table, sort_column, &gli,
+       else
+               sort_column = DT_BYTES;
+       ret = compile_regex(&gli.preg, &gli.inverse_matching);
+       if (ret < 0)
+               return ret;
+       ret = adu_loop_reverse(dir_table, sort_column, &gli,
                global_list_loop_function, &gli.ret, &gli.osl_errno);
+       free_regex(gli.preg);
+       return ret;
 }
 
 static int print_statistics(struct format_info *fi)
 {
        switch (select_conf.select_mode_arg) {
-               case select_mode_arg_global_list:
-                       return print_global_list(fi);
-               case select_mode_arg_global_summary:
-                       return print_global_summary(fi);
-               case select_mode_arg_user_list:
-                       return for_each_admissible_user(print_user_list, fi);
-               case select_mode_arg_user_summary:
-                       return print_user_summary(fi);
+       case select_mode_arg_global_list:
+               return print_global_list(fi);
+       case select_mode_arg_global_summary:
+               return print_global_summary(fi);
+       case select_mode_arg_user_list:
+               return print_user_lists(fi);
+       case select_mode_arg_user_summary:
+               return print_user_summary(fi);
        };
        ERROR_LOG("bad select mode\n");
-       return -ERRNO_TO_ERROR(-EINVAL);
+       return -ERRNO_TO_ERROR(EINVAL);
 }
 
-int run_select_query(struct uid_range *admissible_uids, struct format_info *fi)
+static int open_pipe(char *path)
 {
-       int ret;
+       int p[2], ret, argc;
+       char **argv;
 
-       if (select_conf.output_given && strcmp(select_conf.output_arg, "-")) {
-               output_file = fopen(select_conf.output_arg, "w");
+       ret = pipe(p);
+       if (ret < 0)
+               return ERRNO_TO_ERROR(errno);
+       ret = fork();
+       if (ret < 0)
+               return ERRNO_TO_ERROR(errno);
+       if (ret) { /* parent */
+               DEBUG_LOG("created process %d\n", ret);
+               close(p[0]);
+               output_file = fdopen(p[1], "w");
                if (!output_file)
-                       return -ERRNO_TO_ERROR(errno);
-       } else
-               output_file = stdout;
+                       return ERRNO_TO_ERROR(errno);
+               return 1;
+       }
+       close(p[1]);
+       if (p[0] != STDIN_FILENO)
+               dup2(p[0], STDIN_FILENO);
+       DEBUG_LOG("executing %s\n", path);
+       argc = split_args(path, &argv, " \t");
+       execvp(argv[0], argv);
+       ERROR_LOG("error executing %s: %s\n", path,
+               adu_strerror(ERRNO_TO_ERROR(errno)));
+       _exit(EXIT_FAILURE);
+}
 
+static int open_output_stream(void)
+{
+       char *p;
+       int ret, flags = O_WRONLY | O_CREAT;
+
+       if (!select_conf.output_given)
+               goto use_stdout;
+       p = select_conf.output_arg;
+       switch (p[0]) {
+       case '\0': /* empty string */
+               goto bad_output_arg;
+       case '-':
+               if (!p[1]) /* "-" means stdout */
+                       goto use_stdout;
+               /* string starting with a dash */
+               flags |= O_EXCL;
+               goto open_file;
+       case '>':
+               if (!p[1]) /* ">" is invalid */
+                       goto bad_output_arg;
+               if (p[1] != '>') {
+                       p++;
+                       flags |= O_TRUNC;
+                       goto open_file;
+               }
+               /* string starting with ">>" */
+               if (!p[2]) /* ">>" is invalid */
+                       goto bad_output_arg;
+               flags |= O_APPEND;
+               p += 2;
+               goto open_file;
+       case '|':
+               if (!p[1]) /* "|" is invalid */
+                       goto bad_output_arg;
+               p++;
+               return open_pipe(p);
+       default: /* args starts with no magic character */
+               flags |= O_EXCL;
+               goto open_file;
+       }
+use_stdout:
+       output_file = stdout;
+       return 1;
+bad_output_arg:
+       output_file = NULL;
+       return -E_BAD_OUTPUT_ARG;
+open_file:
+       /*
+        * glibc's 'x' mode to fopen is not portable, so use open() and
+        * fdopen().
+        */
+       ret = open(p, flags, 0644);
+       if (ret < 0)
+               return -ERRNO_TO_ERROR(errno);
+       output_file = fdopen(ret, "w");
+       if (!output_file)
+               return -ERRNO_TO_ERROR(errno);
+       return 1;
+}
+
+int run_select_query(struct uid_range *admissible_uids, struct format_info *fi)
+{
+       int ret = open_output_stream();
+
+       if (ret < 0)
+               goto out;
        ret = open_dir_table(0);
        if (ret < 0)
                goto out;
        check_signals();
-       ret = read_uid_file(admissible_uids);
+       ret = open_admissible_user_tables(admissible_uids);
        if (ret < 0)
                goto out;
        check_signals();
        ret = print_statistics(fi);
 out:
-       close_all_tables();
-       if (output_file != stdout)
+       if (output_file && output_file != stdout) {
                fclose(output_file);
+               output_file = NULL;
+       }
        return ret;
 }
 
@@ -647,7 +911,7 @@ static int setup_format_string(char *fmt, struct format_info **fi)
                break;
        default:
                ERROR_LOG("bad select mode\n");
-               return -ERRNO_TO_ERROR(-EINVAL);
+               return -ERRNO_TO_ERROR(EINVAL);
        };
        INFO_LOG("format string: %s\n", fmt);
        return parse_format_string(fmt, atoms, fi);
@@ -712,12 +976,16 @@ int com_select(void)
                .print_errors = 1
        };
 
-       select_cmdline_parser_init(&select_conf);
        ret = parse_select_options(conf.select_options_arg, &params,
                &admissible_uids, &fi);
-       if (ret <= 0) /* do not run query if help was given */
-               return ret;
-       ret = run_select_query(admissible_uids, fi);
-       free_format_info(fi);
+       if (ret > 0) {
+               ret = read_uid_file();
+               if (ret < 0)
+                       goto out;
+               ret = run_select_query(admissible_uids, fi);
+               free_format_info(fi);
+       }
+out:
+       select_cmdline_parser_free(&select_conf);
        return ret;
 }
index d02fcd39112745d7d9f86f5be48113343ae54b60..c4e1ec0319841322a189e643b04c578c862398c5 100644 (file)
@@ -35,37 +35,70 @@ details="
        (the default), all users are taken into account.
 "
 
-option "limit" L
+option "limit" l
 #~~~~~~~~~~~~~~~
 "Limit output"
-int  typestr="num"
+int typestr="num"
 default="-1"
 optional
 details="
        Only print num lines of output. If negative (the default),
-       print all lines.
+       print all lines. This option is honored in all select modes
+       except global_summary (which outputs only one single line).
 "
 
-option "no-headers" -
-#~~~~~~~~~~~~~~~~~~~~
-"suppress descriptions for listings/summaries"
-flag off
+option "pattern" p
+#~~~~~~~~~~~~~~~~~
+"only consider matching directories"
+string typestr="regex"
+optional
+details="
+       Regular expression that must match the directory name for
+       the directory to be considered for the output of the query.
+       See regex(7) for details.
+
+       Depending on whether --print-base-dir is given, the absolute
+       directory name or only the part of the directory name below
+       the base directory is matched against \"regex\".
+
+       If this option is not given (the default) all directories
+       are taken into account.
+
+       If \"regex\" starts with '!', directories are matched against
+       the remaining part of \"regex\" and the sense of matching is
+       reversed.
+"
+
+option "header" H
+#~~~~~~~~~~~~~~~~
+"use a customized header for listings/summaries"
+string typestr="string"
+optional
 details="
-       This is mostly useful to feed the output of adu to scripts.
+       This option can be used to print any string instead of the
+       default header line (which depends on the selected mode).
+
+       In user_list mode the header is a format string which allows
+       to include the uid and the user name in the header. See the
+       --format option for more details.
+
+       It is possible to set this to the empty string to suppress
+       the header completely. This is mostly useful to feed the
+       output to scripts.
 "
 
 option "select-mode" m
 #~~~~~~~~~~~~~~~~~~~~~
 "How to print the results of the query"
 enum typestr="<key>"
-values="global_list","global_summary","user_list","user_summary"
-default="global_list"
+values="user_summary","user_list","global_summary","global_list"
+default="user_summary"
 optional
 details="
-       global_list: List of directories, regardless of owner.
-       global_summary: Only print totals.
+       user_summary: Print totals for each admissible uid.
        user_list: Print a list for each admissible uid.
-       user_summary Print totals for each admissible uid.
+       global_summary: Only print totals.
+       global_list: List of directories, regardless of owner.
 "
 
 option "list-sort" s
@@ -141,12 +174,12 @@ optional
 details="
 
        A string that specifies how the output of the select query is
-       going to be formated.  Depending on the chosen select-mode,
+       going to be formated. Depending on the chosen select-mode,
        several conversion specifiers are available and a different
        default value for this option applies.
 
        adu knows four different types of directives: string, id,
-       count and size.  These are explained in more detail below.
+       count and size. These are explained in more detail below.
 
        The general syntax for string and id directives is %(name:a:w)
        where \"name\" is the name of the directive, \"a\" specifies
@@ -156,7 +189,7 @@ details="
        The alignment specifier is a single character: Either \"l\",
        \"r\", or \"c\" may be given to specify left, right and
        centered alignment respectively. The with specifier is a
-       positive integer.  Both \"a\" and \"w\" are optional.
+       positive integer. Both \"a\" and \"w\" are optional.
 
        A string directive supported by adu is \"dirname\" which is
        substituted by the name of the directory. It is available
@@ -216,19 +249,20 @@ details="
        together with their types, and for which modes each of
        them may be used.
 
-               pw_name (string): user name. Available for user_list
-               and user_summary
+               pw_name (string): user name. Available for user_list,
+               user_summary and for the header in user_list mode.
 
-               uid (id): user id. Available for user_list and
-               user_summary.
+               uid (id): user id. Available for user_list,
+               user_summary and for the header in user_list mode.
 
-               files (count): number of files. Available everywhere.
+               files (count): number of files. Available for all
+               modes.
 
                dirname (string): name of the directory. Available
                for user_list and global_list.
 
                size (size): total size/ directory size. Available
-               everywhere.
+               for all modes.
 
                dirs (count): number of directories. Available
                for user_summary and global_summary.
index c55e97c81bd9e246df2538a0bc39bf51e562cadb..7891ff8220e0631e858dd4912f1a5cb8bc8d70fa 100644 (file)
--- a/select.h
+++ b/select.h
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 int parse_select_options(char *string, struct select_cmdline_parser_params *params,
                struct uid_range **admissible_uids, struct format_info **fi);
 int run_select_query(struct uid_range *admissible_uids, struct format_info *fi);
diff --git a/sha1.c b/sha1.c
deleted file mode 100644 (file)
index 1c3d00f..0000000
--- a/sha1.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file sha1.c Secure Hash Algorithm, provided by openssl. */
-
-#include "adu.h"
-#include <openssl/sha.h>
-
-/**
- * Compute the sha1 hash.
- *
- * \param data Pointer to the data to compute the hash value from.
- * \param len The length of \a data in bytes.
- * \param sha1 Result pointer.
- *
- * \a sha1 must point to an area at least 20 bytes large.
- *
- * \sa sha(3), openssl(1).
- * */
-void sha1_hash(const char *data, unsigned long len, unsigned char *sha1)
-{
-       SHA_CTX c;
-       SHA1_Init(&c);
-       SHA1_Update(&c, data, len);
-       SHA1_Final(sha1, &c);
-}
index aec62ae9918f298578ec4881d7e9eebd63a1197f..d42a31b5cb84df53ea8c6d3efed7f5d8f5c4c4af 100644 (file)
--- a/string.c
+++ b/string.c
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file string.c Memory allocation and string handling functions. */
+/** \file string.c \brief Memory allocation and string handling functions. */
 
 #include "adu.h"
 #include "string.h"
diff --git a/user.c b/user.c
index 1f7e04a26f32cae30636c21b04a0092a14f2c3de..8959c847b0b2069c76965850fc2acc07a8cfa097 100644 (file)
--- a/user.c
+++ b/user.c
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file user.c uid User and user ID handling. */
+/** \file user.c \brief User and user ID handling. */
 
 #include "adu.h"
 #include <dirent.h> /* readdir() */
@@ -29,6 +29,7 @@ struct uid_range {
        uint32_t high;
 };
 
+/** Iterate over all uid ranges. */
 #define FOR_EACH_UID_RANGE(ur, urs) for (ur = urs; ur->low <= ur->high; ur++)
 
 /** Flags for the user hash table. */
@@ -38,6 +39,7 @@ enum uid_info_flags {
        /** Whether this uid should be taken into account. */
        UI_FL_ADMISSIBLE = 2,
 };
+
 /*
  * Contains info for each user that owns at least one regular file.
  *
@@ -50,6 +52,12 @@ static struct user_info *uid_hash_table;
 /** This is always a power of two. It is set in create_hash_table(). */
 static uint32_t uid_hash_table_size;
 
+/* Array of indices to the entries of \a uid_hash_table. */
+static int *uid_hash_table_sort_idx;
+
+/** The number of used slots in the hash table. */
+static uint32_t num_uids;
+
 /*
  * The columns of the per-user tables.
  *
@@ -138,6 +146,15 @@ out:
        return ret;
 }
 
+/**
+ * Convert the --uid argument to an array of uid ranges.
+ *
+ * \param orig_arg The argument to the --uid option.
+ * \param ur Result pointer.
+ *
+ * Returns Negative on errors. On success, the number of uid ranges
+ * is returned.
+ */
 int parse_uid_arg(const char *orig_arg, struct uid_range **ur)
 {
        char *arg, **argv;
@@ -186,6 +203,22 @@ out:
        return ret;
 }
 
+/**
+ * Add each given user to the array of admissible users.
+ *
+ * \param users Array of user names to add.
+ * \param num_users Length of \a users.
+ * \param admissible_uids The users which are already admissible.
+ * \param num_uid_ranges The number of intervals of \a admissible_uids.
+ *
+ * For each given user, the function checks whether that user is already
+ * admissible, i.e. its uid is contained in one of the ranges given by \a
+ * admissible_uids. If it is, the function ignores that user. Otherwise, a new
+ * length-one range consisting of that uid only is appended to \a
+ * admissible_uids.
+ *
+ * \return Negative on errors, the new number of uid ranges on success.
+ */
 int append_users(char **users, int num_users,
                struct uid_range **admissible_uids, int num_uid_ranges)
 {
@@ -243,8 +276,7 @@ static int open_user_table(struct user_info *ui, int create)
        if (pw && pw->pw_name)
                ui->pw_name = adu_strdup(pw->pw_name);
 
-       INFO_LOG(".............................uid #%u: %u\n",
-               (unsigned)num_uids, (unsigned)ui->uid);
+       INFO_LOG("opening table for uid %u\n", (unsigned)ui->uid);
        if (create) {
                ret = osl(osl_create_table(ui->desc));
                if (ret < 0)
@@ -268,16 +300,33 @@ err:
        return ret;
 }
 
+/** Iterate over each user in the uid hash table. */
+#define FOR_EACH_USER(ui) for (ui = uid_hash_table; ui < \
+       uid_hash_table + uid_hash_table_size; ui++)
+
+
+/**
+ * Execute the given function for each admissible user.
+ *
+ * \param func The function to execute.
+ * \param data Arbitrary pointer.
+ *
+ * This function calls \a func for each admissible user in the uid hash table.
+ * The \a data pointer is passed as the second argument to \a func.
+ *
+ * \return As soon as \a func returns a negative value, the loop is terminated
+ * and that negative value is returned. Otherwise, the function returns 1.
+ */
 int for_each_admissible_user(int (*func)(struct user_info *, void *),
                void *data)
 {
-       struct user_info *ui = uid_hash_table;
-
-       if (!ui)
-               return -ERRNO_TO_ERROR(EFAULT);
+       int i;
 
-       for (; ui < uid_hash_table + uid_hash_table_size; ui++) {
+       assert(uid_hash_table);
+       for (i = 0; i < uid_hash_table_size; i++) {
                int ret;
+               struct user_info *ui = uid_hash_table +
+                       uid_hash_table_sort_idx[i];
 
                if (!ui_used(ui) || !ui_admissible(ui))
                        continue;
@@ -293,46 +342,52 @@ int for_each_admissible_user(int (*func)(struct user_info *, void *),
 
 void create_hash_table(unsigned bits)
 {
+       int i;
+
        uid_hash_table_size = 1 << bits;
        uid_hash_table = adu_calloc(uid_hash_table_size *
                sizeof(struct user_info));
+       uid_hash_table_sort_idx = adu_malloc(uid_hash_table_size * sizeof(int));
+       for (i = 0; i < uid_hash_table_size; i++)
+               uid_hash_table_sort_idx[i] = i;
 }
 
-void free_hash_table(void)
-{
-       free(uid_hash_table);
-       uid_hash_table = NULL;
-}
-
-static int close_user_table(struct user_info *ui, __a_unused void *data)
+void close_user_tables(void)
 {
-       int ret;
+       struct user_info *ui;
 
-       ret = osl(osl_close_table(ui->table, OSL_MARK_CLEAN));
-       if (ret < 0)
-               ERROR_LOG("failed to close user table %u: %s\n",
-                       (unsigned) ui->uid, adu_strerror(-ret));
-       free((char *)ui->desc->name);
-       ui->desc->name = NULL;
-       free((char *)ui->desc->dir);
-       ui->desc->dir = NULL;
-       free(ui->pw_name);
-       ui->pw_name = NULL;
-       free(ui->desc);
-       ui->desc = NULL;
-       ui->table = NULL;
-       ui->flags = 0;
-       return 1;
-}
+       FOR_EACH_USER(ui) {
+               int ret;
 
-void close_user_tables(void)
-{
-       for_each_admissible_user(close_user_table, NULL);
+               if (!ui_used(ui))
+                       continue;
+               if (!ui->table)
+                       continue;
+               INFO_LOG("closing user table for uid %u\n", (unsigned)ui->uid);
+               ret = osl(osl_close_table(ui->table, OSL_MARK_CLEAN));
+               if (ret < 0)
+                       ERROR_LOG("failed to close user table %u: %s\n",
+                               (unsigned)ui->uid, adu_strerror(-ret));
+               free((char *)ui->desc->name);
+               ui->desc->name = NULL;
+               free((char *)ui->desc->dir);
+               ui->desc->dir = NULL;
+               free(ui->pw_name);
+               ui->pw_name = NULL;
+               free(ui->desc);
+               ui->desc = NULL;
+               ui->table = NULL;
+               ui->flags = 0;
+       }
+       free(uid_hash_table);
+       uid_hash_table = NULL;
+       free(uid_hash_table_sort_idx);
+       uid_hash_table_sort_idx = NULL;
 }
 
 /*
  * We use a hash table of size s=2^uid_hash_bits to map the uids into the
- * interval [0..s]. Hash collisions are treated by open addressing, i.e.
+ * interval [0..s-1]. Hash collisions are treated by open addressing, i.e.
  * unused slots in the table are used to store different uids that hash to the
  * same slot.
  *
@@ -344,7 +399,7 @@ void close_user_tables(void)
  * An odd number is sufficient to make sure each entry of the hash table gets
  * probed for probe_num between 0 and s-1 because s is a power of two, hence
  * the second hash value has never a common divisor with the hash table size.
- * IOW: h is invertible in the ring [0..s].
+ * IOW: h is invertible in the ring [0..s-1].
  */
 static uint32_t double_hash(uint32_t uid, uint32_t probe_num)
 {
@@ -352,38 +407,32 @@ static uint32_t double_hash(uint32_t uid, uint32_t probe_num)
                % uid_hash_table_size;
 }
 
-int search_uid(uint32_t uid, struct uid_range *urs,
-               enum search_uid_flags flags, struct user_info **ui_ptr)
+static struct user_info *lookup_uid(uint32_t uid)
 {
        uint32_t p;
 
        for (p = 0; p < uid_hash_table_size; p++) {
                struct user_info *ui = uid_hash_table + double_hash(uid, p);
-
-               if (!ui_used(ui)) {
-                       int ret;
-                       if (!flags)
-                               return -E_BAD_UID;
-                       ui->uid = uid;
-                       ui->flags |= UI_FL_SLOT_USED;
-                       if (!uid_is_admissible(uid, urs))
-                               return 0;
-                       ui->flags |= UI_FL_ADMISSIBLE;
-                       ret = open_user_table(ui, flags & CREATE_USER_TABLE);
-                       if (ret < 0)
-                               return ret;
-
-                       if (ui_ptr)
-                               *ui_ptr = ui;
-                       return 1;
-               }
-               if (ui->uid != uid)
-                       continue;
-               if (ui_ptr)
-                       *ui_ptr = ui;
-               return 0;
+               if (!ui_used(ui))
+                       return ui;
+               if (ui->uid == uid)
+                       return ui;
        }
-       return flags? -E_HASH_TABLE_OVERFLOW : -E_BAD_UID;
+       return NULL;
+}
+
+int create_user_table(uint32_t uid, struct user_info **ui_ptr)
+{
+       struct user_info *ui = lookup_uid(uid);
+
+       if (!ui)
+               return -E_HASH_TABLE_OVERFLOW;
+       *ui_ptr = ui;
+       if (ui_used(ui))
+               return 1;
+       ui->uid = uid;
+       ui->flags |= UI_FL_SLOT_USED;
+       return open_user_table(ui, 1);
 }
 
 static char *get_uid_list_name(void)
@@ -391,13 +440,49 @@ static char *get_uid_list_name(void)
        return make_message("%s/uid_list", conf.database_dir_arg);
 }
 
-void sort_hash_table(int (*comp)(const void *, const void *))
+static int (*hash_table_comparator)(struct user_info *a, struct user_info *b);
+
+static int comp_wrapper(const void *a, const void *b)
+{
+       struct user_info *x = uid_hash_table + *(unsigned *)a;
+       struct user_info *y = uid_hash_table + *(unsigned *)b;
+       return hash_table_comparator(x, y);
+}
+
+void sort_hash_table(int (*comp)(struct user_info *, struct user_info *))
+{
+       hash_table_comparator = comp;
+       qsort(uid_hash_table_sort_idx, uid_hash_table_size,
+               sizeof(*uid_hash_table_sort_idx), comp_wrapper);
+}
+
+int open_admissible_user_tables(struct uid_range *admissible_uids)
 {
-       qsort(uid_hash_table, uid_hash_table_size, sizeof(struct user_info),
-               comp);
+       struct user_info *ui;
+
+       assert(uid_hash_table);
+       DEBUG_LOG("size: %d\n", uid_hash_table_size);
+       FOR_EACH_USER(ui) {
+               int ret;
+
+               if (!ui_used(ui))
+                       continue;
+               if (!uid_is_admissible(ui->uid, admissible_uids)) {
+                       DEBUG_LOG("uid %u is not admissible\n", ui->uid);
+                       ui->flags &= ~UI_FL_ADMISSIBLE;
+                       continue;
+               }
+               ui->flags |= UI_FL_ADMISSIBLE;
+               if (ui->table)
+                       continue;
+               ret = open_user_table(ui, 0);
+               if (ret < 0)
+                       return ret;
+       }
+       return 1;
 }
 
-int read_uid_file(struct uid_range *admissible_uids)
+int read_uid_file(void)
 {
        size_t size;
        uint32_t n;
@@ -406,7 +491,7 @@ int read_uid_file(struct uid_range *admissible_uids)
        unsigned bits;
 
        if (ret < 0) {
-               INFO_LOG("failed to map %s\n", filename);
+               ERROR_LOG("failed to map %s\n", filename);
                free(filename);
                return ret;
        }
@@ -423,40 +508,41 @@ int read_uid_file(struct uid_range *admissible_uids)
        create_hash_table(bits);
        for (n = 0; n < num_uids; n++) {
                uint32_t uid = read_u32(map + n * sizeof(uid));
-               ret = search_uid(uid, admissible_uids, OPEN_USER_TABLE, NULL);
-               if (ret < 0)
+               struct user_info *ui = lookup_uid(uid);
+               assert(ui);
+               if (ui_used(ui)) { /* impossible */
+                       ERROR_LOG("duplicate user id!?\n");
+                       ret = -EFAULT;
                        goto out;
+               }
+               ui->uid = uid;
+               ui->flags |= UI_FL_SLOT_USED;
        }
+       ret = 1;
 out:
        adu_munmap(map, size);
        return ret;
 }
 
-static int write_uid(struct user_info *ui, void *data)
-{
-       char **p = data;
-
-       write_u32(*p, ui->uid);
-       *p += sizeof(uint32_t);
-       return 1;
-}
-
 int write_uid_file(void)
 {
        char *buf, *p, *filename;
        size_t size = num_uids * sizeof(uint32_t);
        int ret;
+       struct user_info *ui;
 
        if (!num_uids)
                return 0;
        buf = p = adu_malloc(size);
-       ret = for_each_admissible_user(write_uid, &p);
-       if (ret < 0)
-               goto out;
+       FOR_EACH_USER(ui) {
+               if (!ui_used(ui))
+                       continue;
+               write_u32(p, ui->uid);
+               p += sizeof(uint32_t);
+       }
        filename = get_uid_list_name();
        ret = adu_write_file(filename, buf, size);
        free(filename);
-out:
        free(buf);
        return ret;
 }
diff --git a/user.h b/user.h
index 0eba5bbdc610facd7ae85227cd09a17bc0ed0bc7..c561a3fe8335a90d1cc8261ba4fafa63543273a3 100644 (file)
--- a/user.h
+++ b/user.h
@@ -1,3 +1,9 @@
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
 /** The columns of the id table. */
 enum user_table_columns {
        /** The numer of the directory. */
@@ -33,19 +39,12 @@ struct user_info {
 /** An opaque struct that contains info about which users are admissible. */
 struct uid_range;
 
-enum search_uid_flags {
-       OPEN_USER_TABLE = 1,
-       CREATE_USER_TABLE = 2,
-};
-int search_uid(uint32_t uid, struct uid_range *urs,
-               enum search_uid_flags flags, struct user_info **ui_ptr);
-
-int read_uid_file(struct uid_range *admissible_uids);
+int create_user_table(uint32_t uid, struct user_info **ui_ptr);
+int read_uid_file(void);
 int write_uid_file(void);
 
 void create_hash_table(unsigned bits);
-void sort_hash_table(int (*comp)(const void *, const void *));
-void free_hash_table(void);
+void sort_hash_table(int (*comp)(struct user_info *, struct user_info *));
 
 int for_each_admissible_user(int (*func)(struct user_info *, void *),
                void *data);
@@ -53,3 +52,4 @@ int parse_uid_arg(const char *orig_arg, struct uid_range **ur);
 int append_users(char **users, int num_users,
                struct uid_range **admissible_uids, int num_uid_ranges);
 void close_user_tables(void);
+int open_admissible_user_tables(struct uid_range *admissible_uids);