summaryrefslogtreecommitdiff
path: root/src/libs/uio
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio')
-rw-r--r--src/libs/uio/COPYING350
-rw-r--r--src/libs/uio/Makeinfo22
-rw-r--r--src/libs/uio/charhashtable.c77
-rw-r--r--src/libs/uio/charhashtable.h39
-rw-r--r--src/libs/uio/debug.c914
-rw-r--r--src/libs/uio/debug.h29
-rw-r--r--src/libs/uio/defaultfs.c41
-rw-r--r--src/libs/uio/defaultfs.h41
-rw-r--r--src/libs/uio/doc/basics178
-rw-r--r--src/libs/uio/doc/conventions30
-rw-r--r--src/libs/uio/doc/todo144
-rw-r--r--src/libs/uio/fileblock.c332
-rw-r--r--src/libs/uio/fileblock.h88
-rw-r--r--src/libs/uio/fstypes.c272
-rw-r--r--src/libs/uio/fstypes.h113
-rw-r--r--src/libs/uio/getint.h141
-rw-r--r--src/libs/uio/gphys.c620
-rw-r--r--src/libs/uio/gphys.h313
-rw-r--r--src/libs/uio/hashtable.c374
-rw-r--r--src/libs/uio/hashtable.h157
-rw-r--r--src/libs/uio/io.c1859
-rw-r--r--src/libs/uio/io.h159
-rw-r--r--src/libs/uio/ioaux.c930
-rw-r--r--src/libs/uio/ioaux.h53
-rw-r--r--src/libs/uio/iointrn.h197
-rw-r--r--src/libs/uio/match.c569
-rw-r--r--src/libs/uio/match.h182
-rw-r--r--src/libs/uio/mem.h61
-rw-r--r--src/libs/uio/memdebug.c293
-rw-r--r--src/libs/uio/memdebug.h97
-rw-r--r--src/libs/uio/mount.c168
-rw-r--r--src/libs/uio/mount.h64
-rw-r--r--src/libs/uio/mounttree.c814
-rw-r--r--src/libs/uio/mounttree.h204
-rw-r--r--src/libs/uio/paths.c602
-rw-r--r--src/libs/uio/paths.h96
-rw-r--r--src/libs/uio/physical.c174
-rw-r--r--src/libs/uio/physical.h92
-rw-r--r--src/libs/uio/stdio/Makeinfo2
-rw-r--r--src/libs/uio/stdio/stdio.c854
-rw-r--r--src/libs/uio/stdio/stdio.h111
-rw-r--r--src/libs/uio/types.h64
-rw-r--r--src/libs/uio/uioport.h173
-rw-r--r--src/libs/uio/uiostream.c603
-rw-r--r--src/libs/uio/uiostream.h97
-rw-r--r--src/libs/uio/uioutils.c228
-rw-r--r--src/libs/uio/uioutils.h92
-rw-r--r--src/libs/uio/utils.c497
-rw-r--r--src/libs/uio/utils.h43
-rw-r--r--src/libs/uio/zip/Makeinfo2
-rw-r--r--src/libs/uio/zip/zip.c1680
-rw-r--r--src/libs/uio/zip/zip.h106
52 files changed, 15441 insertions, 0 deletions
diff --git a/src/libs/uio/COPYING b/src/libs/uio/COPYING
new file mode 100644
index 0000000..2774fef
--- /dev/null
+++ b/src/libs/uio/COPYING
@@ -0,0 +1,350 @@
+Below the text of the GNU General Public License is quoted verbatim.
+It applies to all files in and below this directory that say so.
+Note that most of these files will say only version 2 of the
+GNU General Public License applies, not any later version.
+
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <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/src/libs/uio/Makeinfo b/src/libs/uio/Makeinfo
new file mode 100644
index 0000000..9d127fc
--- /dev/null
+++ b/src/libs/uio/Makeinfo
@@ -0,0 +1,22 @@
+uqm_SUBDIRS="stdio"
+uqm_CFILES="charhashtable.c defaultfs.c fileblock.c fstypes.c gphys.c io.c
+ ioaux.c match.c mount.c mounttree.c paths.c physical.c uiostream.c
+ uioutils.c utils.c"
+uqm_HFILES="charhashtable.h defaultfs.h fileblock.h fstypes.h getint.h
+ gphys.h ioaux.h io.h iointrn.h match.h mem.h mount.h mounttree.h
+ paths.h physical.h types.h uioport.h uiostream.h uioutils.h utils.h"
+
+if [ -n "$uqm_USE_ZIP_IO" ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS zip"
+fi
+
+#if [ -n "$DEBUG" -o -n "$uqm_UIO_DEBUG" ]; then
+ uqm_CFILES="$uqm_CFILES debug.c"
+ uqm_HFILES="$uqm_HFILES debug.h"
+#fi
+
+if [ -n "$MEMDEBUG" ]; then
+ uqm_CFILES="$uqm_CFILES hashtable.c memdebug.c"
+ uqm_HFILES="$uqm_HFILES hashtable.h memdebug.h"
+fi
+
diff --git a/src/libs/uio/charhashtable.c b/src/libs/uio/charhashtable.c
new file mode 100644
index 0000000..df10b2a
--- /dev/null
+++ b/src/libs/uio/charhashtable.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#define HASHTABLE_INTERNAL
+#include "charhashtable.h"
+#include "types.h"
+#include "uioport.h"
+
+static inline uio_uint32 CharHashTable_hash(CharHashTable_HashTable *hashTable,
+ const char *string);
+static inline uio_bool CharHashTable_equal(CharHashTable_HashTable *hashTable,
+ const char *key1, const char *key2);
+static inline char *CharHashTable_copy(CharHashTable_HashTable *hashTable,
+ const char *key);
+static inline void CharHashTable_freeKey(CharHashTable_HashTable *hashTable,
+ char *key);
+
+#include "hashtable.c"
+
+
+static inline uio_uint32
+CharHashTable_hash(CharHashTable_HashTable *hashTable, const char *key) {
+ uio_uint32 hash;
+
+ (void) hashTable;
+ // Rotating hash, variation of something on the web which
+ // wasn't original itself.
+ hash = 0;
+ // Hash was on that web page initialised as the length,
+ // but that isn't known at this time.
+ while (*key != '\0') {
+ hash = (hash << 4) ^ (hash >> 28) ^ *key;
+ key++;
+ }
+ return hash ^ (hash >> 10) ^ (hash >> 20);
+}
+
+static inline uio_bool
+CharHashTable_equal(CharHashTable_HashTable *hashTable,
+ const char *key1, const char *key2) {
+ (void) hashTable;
+ return strcmp(key1, key2) == 0;
+}
+
+static inline char *
+CharHashTable_copy(CharHashTable_HashTable *hashTable,
+ const char *key) {
+ (void) hashTable;
+ return uio_strdup(key);
+}
+
+static inline void
+CharHashTable_freeKey(CharHashTable_HashTable *hashTable,
+ char *key) {
+ (void) hashTable;
+ uio_free(key);
+}
+
+
diff --git a/src/libs/uio/charhashtable.h b/src/libs/uio/charhashtable.h
new file mode 100644
index 0000000..3cd0add
--- /dev/null
+++ b/src/libs/uio/charhashtable.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_CHARHASHTABLE_H_
+#define LIBS_UIO_CHARHASHTABLE_H_
+
+
+#define HASHTABLE_(identifier) CharHashTable ## _ ## identifier
+typedef char HASHTABLE_(Key);
+typedef void HASHTABLE_(Value);
+#define CharHashTable_HASH CharHashTable_hash
+#define CharHashTable_EQUAL CharHashTable_equal
+#define CharHashTable_COPY CharHashTable_copy
+#define CharHashTable_FREEKEY CharHashTable_freeKey
+#define CharHashTable_FREEVALUE(hashTable, value) \
+ ((void) (hashTable), (void) (value))
+
+#include "hashtable.h"
+
+
+#endif /* _CHARHASHTABLE_H */
+
diff --git a/src/libs/uio/debug.c b/src/libs/uio/debug.c
new file mode 100644
index 0000000..66bab47
--- /dev/null
+++ b/src/libs/uio/debug.c
@@ -0,0 +1,914 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#if defined(__unix__) && !defined(_WIN32_WCE)
+# include <sys/wait.h>
+#endif
+
+#include "debug.h"
+#include "uioport.h"
+#include "io.h"
+#include "utils.h"
+#include "types.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#define LINEBUFLEN 1024
+
+typedef struct DebugContext {
+ uio_bool exit;
+ FILE *in;
+ FILE *out;
+ FILE *err;
+ uio_DirHandle *cwd;
+} DebugContext;
+
+typedef struct {
+ const char *name;
+ int (*fun)(DebugContext *, int, char *[]);
+} DebugCommand;
+
+#ifdef STAND_ALONE
+void initRepository(void);
+void unInitRepository(void);
+#endif
+
+static int debugCmdCat(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdCd(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdExec(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdExit(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdLs(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMem(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMount(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMv(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdRm(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdStat(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdWriteTest(DebugContext *debugContext, int argc,
+ char *argv[]);
+static int debugCmdFwriteTest(DebugContext *debugContext, int argc,
+ char *argv[]);
+
+static void makeArgs(char *lineBuf, int *argc, char ***argv);
+static int debugCallCommand(DebugContext *debugContext,
+ int argc, char *argv[]);
+uio_MountHandle *
+debugMountOne(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative);
+
+// In alphabetic order:
+DebugCommand debugCommands[] = {
+ { "cat", debugCmdCat },
+ { "cd", debugCmdCd },
+ { "exec", debugCmdExec },
+ { "exit", debugCmdExit },
+ { "fwritetest", debugCmdFwriteTest },
+ { "ls", debugCmdLs },
+ { "mem", debugCmdMem },
+ { "mkdir", debugCmdMkDir },
+ { "mount", debugCmdMount },
+ { "mv", debugCmdMv },
+ { "pwd", debugCmdPwd },
+ { "rm", debugCmdRm },
+ { "rmdir", debugCmdRmDir },
+ { "stat", debugCmdStat },
+ { "writetest", debugCmdWriteTest },
+};
+
+
+#ifndef STAND_ALONE
+extern uio_Repository *repository;
+#else
+uio_Repository *repository;
+uio_MountHandle *mountHandles[9]; // TODO: remove (this is just a test)
+
+int
+main(int argc, char *argv[]) {
+ initRepository();
+ uio_debugInteractive(stdin, stdout, stderr);
+ unInitRepository();
+ (void) argc;
+ (void) argv;
+ return EXIT_SUCCESS;
+}
+
+uio_MountHandle *
+debugMountOne(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative) {
+ uio_MountHandle *mountHandle;
+
+ mountHandle = uio_mountDir(destRep, mountPoint, fsType, sourceDir,
+ sourcePath, inPath, autoMount, flags, relative);
+ if (mountHandle == NULL) {
+ int savedErrno = errno;
+ fprintf(stderr, "Could not mount '%s' and graft '%s' from that "
+ "into the repository at '%s': %s\n",
+ sourcePath, inPath, mountPoint, strerror(errno));
+ errno = savedErrno;
+ }
+ return mountHandle;
+}
+
+void
+initRepository(void) {
+ static uio_AutoMount autoMountZip = {
+ .pattern = "*.zip",
+ .matchType = match_MATCH_SUFFIX,
+ .fileSystemID = uio_FSTYPE_ZIP,
+ .mountFlags = uio_MOUNT_BELOW | uio_MOUNT_RDONLY
+ };
+ static uio_AutoMount *autoMount[] = {
+ &autoMountZip,
+ NULL
+ };
+
+ uio_init();
+ repository = uio_openRepository(0);
+
+ memset(&mountHandles, '\0', sizeof mountHandles);
+#if 1
+ mountHandles[0] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/home/svdb/cvs/sc2/content", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[1] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/home/svdb/cvs/sc2/src/sc2code/ships",
+ autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[2] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/tmp/vfstest", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+#if 1
+ mountHandles[3] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/tmp/vfstest2", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+
+ // TODO: should work too:
+#if 0
+ mountHandle[4] = debugMountOne(repository, "/zip/", uio_FSTYPE_ZIP, NULL,
+ NULL, "/ziptest/foo.zip", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+ {
+ uio_DirHandle *rootDir;
+ rootDir = uio_openDir(repository, "/", 0);
+ if (rootDir == NULL) {
+ fprintf(stderr, "Could not open '/' dir.\n");
+ } else {
+#if 1
+ mountHandles[4] = debugMountOne(repository, "/example/",
+ uio_FSTYPE_ZIP, rootDir, "/example2.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[5] = debugMountOne(repository, "/example/",
+ uio_FSTYPE_ZIP, rootDir, "/example/example.zip", "/",
+ autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[6] = debugMountOne(repository, "/zip/",
+ uio_FSTYPE_ZIP, rootDir, "/voice.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[7] = debugMountOne(repository, "/foo/",
+ uio_FSTYPE_ZIP, rootDir, "/foo2.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+ uio_closeDir(rootDir);
+ }
+ }
+ mountHandles[8] = debugMountOne(repository, "/tmp/",
+ uio_FSTYPE_STDIO, NULL, NULL, "/tmp/", autoMount,
+ uio_MOUNT_TOP, NULL);
+
+#if 1
+ mountHandles[8] = debugMountOne(repository, "/root/root/",
+ uio_FSTYPE_STDIO, NULL, NULL, "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+
+}
+
+void
+unInitRepository(void) {
+#if 1
+ int i;
+// uio_printMountTree(stderr, repository->mountTree, 0);
+// fprintf(stderr, "\n");
+ for (i = 7; i >= 0; i--) {
+ if (mountHandles[i] != NULL)
+ uio_unmountDir(mountHandles[i]);
+// uio_printMountTree(stderr, repository->mountTree, 0);
+// uio_printMounts(stderr, repository);
+// fprintf(stderr, "\n");
+ }
+#endif
+ uio_closeRepository(repository);
+ uio_unInit();
+}
+#endif /* STAND_ALONE */
+
+void
+uio_debugInteractive(FILE *in, FILE *out, FILE *err) {
+ char lineBuf[LINEBUFLEN];
+ size_t lineLen;
+ int argc;
+ char **argv;
+ DebugContext debugContext;
+ uio_bool interactive;
+
+ memset(&debugContext, '\0', sizeof (DebugContext));
+ debugContext.exit = false;
+ debugContext.in = in;
+ debugContext.out = out;
+ debugContext.err = err;
+ debugContext.cwd = uio_openDir(repository, "/", 0);
+ if (debugContext.cwd == NULL) {
+ fprintf(err, "Fatal: Could not open working dir.\n");
+ abort();
+ }
+
+ interactive = isatty(fileno(in));
+ do {
+ if (interactive)
+ fprintf(out, "> ");
+ if (fgets(lineBuf, LINEBUFLEN, in) == NULL) {
+ if (feof(in)) {
+ // user pressed ^D
+ break;
+ }
+ // error occured
+ clearerr(in);
+ continue;
+ }
+ lineLen = strlen(lineBuf);
+ if (lineBuf[lineLen - 1] != '\n' && lineBuf[lineLen - 1] != '\r') {
+ fprintf(err, "Too long command line.\n");
+ // TODO: read until EOL
+ continue;
+ }
+ makeArgs(lineBuf, &argc, &argv);
+ if (argc == 0) {
+ uio_free(argv);
+ continue;
+ }
+ debugCallCommand(&debugContext, argc, argv);
+ uio_free(argv);
+ } while (!debugContext.exit);
+ if (interactive)
+ fprintf(out, "\n");
+ uio_closeDir(debugContext.cwd);
+}
+
+static void
+makeArgs(char *lineBuf, int *argc, char ***argv) {
+ int numArg;
+ char **args;
+ char *ptr;
+
+ numArg = 0;
+ ptr = lineBuf;
+ while(true) {
+ while (isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ numArg++;
+ while (!isspace((int) *ptr))
+ ptr++;
+ }
+
+ args = uio_malloc((numArg + 1) * sizeof (char *));
+ numArg = 0;
+ ptr = lineBuf;
+ while(true) {
+ while (isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ args[numArg] = ptr;
+ numArg++;
+ while (!isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ *ptr = '\0';
+ ptr++;
+ }
+ args[numArg] = NULL;
+ *argv = args;
+ *argc = numArg;
+}
+
+static int
+debugCallCommand(DebugContext *debugContext, int argc, char *argv[]) {
+ int i;
+ int numDebugCommands;
+
+ i = 0;
+ numDebugCommands = sizeof debugCommands / sizeof debugCommands[0];
+ // could be improved with binary search
+ while(1) {
+ if (i == numDebugCommands) {
+ fprintf(debugContext->err, "Invalid command.\n");
+ return 1;
+ }
+ if (strcmp(argv[0], debugCommands[i].name) == 0)
+ break;
+ i++;
+ }
+ return debugCommands[i].fun(debugContext, argc, argv);
+}
+
+static int
+debugCmdCat(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Handle *handle;
+#define READBUFSIZE 0x10000
+ char readBuf[READBUFSIZE];
+ char *bufPtr;
+ ssize_t numInBuf, numWritten;
+ size_t totalWritten;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ handle = uio_open(debugContext->cwd, argv[1], O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (handle == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ totalWritten = 0;
+ while (1) {
+ numInBuf = uio_read(handle, readBuf, READBUFSIZE);
+ if (numInBuf == -1) {
+ if (errno == EINTR)
+ continue;
+ fprintf(debugContext->err, "Could not read from file: %s\n",
+ strerror(errno));
+ uio_close(handle);
+ return 1;
+ }
+ if (numInBuf == 0)
+ break;
+ bufPtr = readBuf;
+ do {
+ numWritten = write(fileno(debugContext->out), bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(debugContext->err, "Could not read from file: %s\n",
+ strerror(errno));
+ uio_close(handle);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ totalWritten += numWritten;
+ } while (numInBuf > 0);
+ }
+ fprintf(debugContext->out, "[%u bytes]\n", (unsigned int) totalWritten);
+
+ uio_close(handle);
+ return 0;
+}
+
+static int
+debugCmdCd(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_DirHandle *newWd;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+ newWd = uio_openDirRelative(debugContext->cwd, argv[1], 0);
+ if (newWd == NULL) {
+ fprintf(debugContext->err, "Could not access new dir: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ uio_closeDir(debugContext->cwd);
+ debugContext->cwd = newWd;
+ return 0;
+}
+
+static int
+debugCmdExec(DebugContext *debugContext, int argc, char *argv[]) {
+ int i;
+ const char **newArgs;
+ int errCode = 0;
+ uio_StdioAccessHandle **handles;
+ uio_DirHandle *tempDir;
+
+ if (argc < 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ tempDir = uio_openDirRelative(debugContext->cwd, "/tmp", 0);
+ if (tempDir == 0) {
+ fprintf(debugContext->err, "Could not open temp dir: %s.\n",
+ strerror(errno));
+ return 1;
+ }
+
+ newArgs = uio_malloc(argc * sizeof (char *));
+ newArgs[0] = argv[1];
+ handles = uio_malloc(argc * sizeof (uio_StdioAccessHandle *));
+ handles[0] = NULL;
+
+ for (i = 2; i < argc; i++) {
+#if 0
+ if (argv[i][0] == '-') {
+ // Don't try to parse arguments that start with '-'.
+ // They are probably option flags.
+ newArgs[i - 1] = argv[i];
+ }
+#endif
+ handles[i - 1] = uio_getStdioAccess(debugContext->cwd, argv[i],
+ O_RDONLY, tempDir);
+ if (handles[i - 1] == NULL) {
+ if (errno == ENOENT) {
+ // No match; we keep what's typed literally.
+ newArgs[i - 1] = argv[i];
+ continue;
+ }
+
+ // error
+ fprintf(debugContext->err,
+ "Cannot execute: Cannot get stdio access to %s: %s.\n",
+ argv[i], strerror(errno));
+ errCode = 1;
+ argc = i + 1;
+ goto err;
+ }
+
+ newArgs[i - 1] = uio_StdioAccessHandle_getPath(handles[i - 1]);
+ }
+ newArgs[argc - 1] = NULL;
+
+ fprintf(debugContext->err, "Executing: %s", newArgs[0]);
+ for (i = 1; i < argc - 1; i++)
+ fprintf(debugContext->err, " %s", newArgs[i]);
+ fprintf(debugContext->err, "\n");
+
+#if defined(__unix__) && !defined(_WIN32_WCE)
+ {
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ fprintf(debugContext->err, "Error: fork() failed: %s.\n",
+ strerror(errno));
+ break;
+ case 0:
+ // child
+ execvp(newArgs[0], (char * const *) newArgs);
+ fprintf(debugContext->err, "Error: execvp() failed: %s.\n",
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+ break;
+ default: {
+ // parent
+ int status;
+ pid_t retVal;
+
+ while (1) {
+ retVal = waitpid(pid, &status, 0);
+ if (retVal != -1)
+ break;
+ if (errno != EINTR) {
+ fprintf(debugContext->err, "Error: waitpid() "
+ "failed: %s\n", strerror(errno));
+ break;
+ }
+ }
+ if (retVal == -1)
+ break;
+
+ if (WIFEXITED(status)) {
+ fprintf(debugContext->err, "Exit status: %d\n",
+ WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(debugContext->err, "Terminated on signal %d.\n",
+ WTERMSIG(status));
+ } else {
+ fprintf(debugContext->err, "Error: weird exit status.\n");
+ }
+ break;
+ }
+ }
+ }
+#else
+ fprintf(debugContext->err, "Cannot execute: not supported on this "
+ "platform.\n");
+#endif
+
+err:
+ for (i = 1; i < argc - 1; i++) {
+ if (handles[i] != NULL)
+ uio_releaseStdioAccess(handles[i]);
+ }
+ uio_free(handles);
+ uio_free((void *) newArgs);
+ uio_closeDir(tempDir);
+
+ return errCode;
+}
+
+static int
+debugCmdExit(DebugContext *debugContext, int argc, char *argv[]) {
+ debugContext->exit = true;
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdFwriteTest(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Stream *stream;
+ const char testString[] = "0123456789\n";
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ stream = uio_fopen(debugContext->cwd, argv[1], "w+b");
+ if (stream == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ goto err;
+ }
+
+ if (uio_fwrite(testString, strlen(testString), 1, stream) != 1) {
+ fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputs(testString, stream) == EOF) {
+ fprintf(debugContext->err, "uio_fputs() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fseek(stream, 15, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputc('A', stream) != 'A') {
+ fprintf(debugContext->err, "uio_fputc() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fseek(stream, 0, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ {
+ char buf[6];
+ char *ptr;
+ int i;
+ i = 1;
+ while (1) {
+ ptr = uio_fgets(buf, sizeof buf, stream);
+ if (ptr == NULL)
+ break;
+ fprintf(debugContext->out, "%d: [%s]\n", i, ptr);
+ i++;
+ }
+ if (uio_ferror(stream)) {
+ fprintf(debugContext->err, "uio_fgets() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ uio_clearerr(stream);
+ }
+ if (uio_fseek(stream, 4, SEEK_END) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ {
+ char buf[2000];
+ memset(buf, 'Q', sizeof buf);
+ if (uio_fwrite(buf, 100, 20, stream) != 20) {
+ fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ }
+ if (uio_fseek(stream, 5, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputc('B', stream) != 'B') {
+ fprintf(debugContext->err, "uio_fputc() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ uio_fclose(stream);
+ return 0;
+err:
+ uio_fclose(stream);
+ return 1;
+}
+
+static int
+listOneDir(DebugContext *debugContext, const char *arg) {
+ uio_DirList *dirList;
+ int i;
+ const char *pattern;
+ const char *cpath;
+ char *buf = NULL;
+
+ if (arg[0] == '\0') {
+ cpath = arg;
+ pattern = "*";
+ } else {
+ pattern = strrchr(arg, '/');
+ if (pattern == NULL) {
+ // No directory component in 'arg'.
+ cpath = "";
+ pattern = arg;
+ } else if (pattern[1] == '\0') {
+ // 'arg' ends on /
+ cpath = arg;
+ pattern = "*";
+ } else {
+ if (pattern == arg) {
+ cpath = "/";
+ } else {
+ buf = uio_malloc(pattern - arg + 1);
+ memcpy(buf, arg, pattern - arg);
+ buf[pattern - arg] = '\0';
+ cpath = buf;
+ }
+ pattern++;
+ }
+ }
+#ifdef HAVE_GLOB
+ dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
+ match_MATCH_GLOB);
+#else
+ if (pattern[0] == '*' && pattern[1] == '\0') {
+ dirList = uio_getDirList(debugContext->cwd, cpath, "",
+ match_MATCH_PREFIX);
+ } else {
+ dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
+ match_MATCH_LITERAL);
+ }
+#endif
+ if (dirList == NULL) {
+ fprintf(debugContext->out, "Error in uio_getDirList(): %s.\n",
+ strerror(errno));
+ if (buf != NULL)
+ uio_free(buf);
+ return 1;
+ }
+ for (i = 0; i < dirList->numNames; i++)
+ fprintf(debugContext->out, "%s\n", dirList->names[i]);
+ uio_DirList_free(dirList);
+ if (buf != NULL)
+ uio_free(buf);
+ return 0;
+}
+
+static int
+debugCmdLs(DebugContext *debugContext, int argc, char *argv[]) {
+ int argI;
+ int retVal;
+
+ if (argc == 1)
+ return listOneDir(debugContext, unconst(""));
+
+ for (argI = 1; argI < argc; argI++) {
+ retVal = listOneDir(debugContext, argv[argI]);
+ if (retVal != 0)
+ return retVal;
+ }
+
+ return 0;
+}
+
+static int
+debugCmdMem(DebugContext *debugContext, int argc, char *argv[]) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_printPointers(debugContext->out);
+#else
+ fprintf(debugContext->out, "Memory debugging not compiled in.\n");
+#endif
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_mkdir(debugContext->cwd, argv[1], 0777);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not create directory: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdMount(DebugContext *debugContext, int argc, char *argv[]) {
+ if (argc == 1) {
+ uio_printMounts(debugContext->out, repository);
+// uio_printMountTree(debugContext->out, repository->mountTree, 0);
+ }
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdMv(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 3) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_rename(debugContext->cwd, argv[1],
+ debugContext->cwd, argv[2]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not rename: %s\n", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_DirHandle_print(debugContext->cwd, debugContext->out);
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdRm(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_unlink(debugContext->cwd, argv[1]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not remove file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_rmdir(debugContext->cwd, argv[1]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not remove directory: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) {
+ struct stat statBuf;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ if (uio_stat(debugContext->cwd, argv[1], &statBuf) == -1) {
+ // errno is set
+ int savedErrno;
+ savedErrno = errno;
+ fprintf(debugContext->err, "Could not stat file: %s\n",
+ strerror(errno));
+ errno = savedErrno;
+ return 1;
+ }
+
+ fprintf(debugContext->out,
+ "size %ld bytes\n"
+ "uid %d gid %d mode 0%o\n",
+ (unsigned long) statBuf.st_size,
+ (unsigned int) statBuf.st_uid,
+ (unsigned int) statBuf.st_gid,
+ (unsigned int) statBuf.st_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID));
+ // Can't do these next three in one fprintf, as ctime uses a static buffer
+ // that is overwritten with each call.
+ fprintf(debugContext->out,
+ "last access: %s", ctime(&statBuf.st_atime));
+ fprintf(debugContext->out,
+ "last modification: %s", ctime(&statBuf.st_mtime));
+ fprintf(debugContext->out,
+ "last status change: %s", ctime(&statBuf.st_ctime));
+
+ return 0;
+}
+
+static int
+debugCmdWriteTest(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Handle *handle;
+ const char testString[] = "Hello world!\n";
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ handle = uio_open(debugContext->cwd, argv[1],
+ O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0644);
+ if (handle == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ if (uio_write(handle, testString, sizeof testString) == -1) {
+ fprintf(debugContext->err, "Write failed: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ uio_close(handle);
+ return 0;
+}
+
+
diff --git a/src/libs/uio/debug.h b/src/libs/uio/debug.h
new file mode 100644
index 0000000..fd6a3ff
--- /dev/null
+++ b/src/libs/uio/debug.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef UIO_DEBUG_H
+#define UIO_DEBUG_H
+
+void uio_debugInteractive(FILE *in, FILE *out, FILE *err);
+
+
+#endif /* _DEBUG_H */
+
+
diff --git a/src/libs/uio/defaultfs.c b/src/libs/uio/defaultfs.c
new file mode 100644
index 0000000..bf6d247
--- /dev/null
+++ b/src/libs/uio/defaultfs.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "defaultfs.h"
+#include "uioport.h"
+
+extern uio_FileSystemHandler stdio_fileSystemHandler;
+#ifdef HAVE_ZIP
+extern uio_FileSystemHandler zip_fileSystemHandler;
+#endif
+
+const uio_DefaultFileSystemSetup defaultFileSystems[] = {
+ { uio_FSTYPE_STDIO, "stdio", &stdio_fileSystemHandler },
+#ifdef HAVE_ZIP
+ { uio_FSTYPE_ZIP, "zip", &zip_fileSystemHandler },
+#endif
+};
+
+int
+uio_numDefaultFileSystems(void) {
+ return sizeof defaultFileSystems / sizeof defaultFileSystems[0];
+}
+
+
diff --git a/src/libs/uio/defaultfs.h b/src/libs/uio/defaultfs.h
new file mode 100644
index 0000000..711ac0c
--- /dev/null
+++ b/src/libs/uio/defaultfs.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef uio_DEFAULTFS_H
+#define uio_DEFAULTFS_H
+
+typedef struct uio_DefaultFileSystemSetup uio_DefaultFileSystemSetup;
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+
+struct uio_DefaultFileSystemSetup {
+ uio_FileSystemID id;
+ const char *name;
+ uio_FileSystemHandler *handler;
+};
+
+extern const uio_DefaultFileSystemSetup defaultFileSystems[];
+
+int uio_numDefaultFileSystems(void);
+
+#endif /* uio_DEFAULTFS_H */
+
diff --git a/src/libs/uio/doc/basics b/src/libs/uio/doc/basics
new file mode 100644
index 0000000..e04afd8
--- /dev/null
+++ b/src/libs/uio/doc/basics
@@ -0,0 +1,178 @@
+-= Introduction =-
+
+The file io system I present here provides the user with a virtual file
+systems on which normal POSIX-like file operations can be performed.
+
+A virtual file system consists of a number of underlying filesystems merged
+together in one directory structure.
+Underlying file systems can be POSIX filesystems, or zip archives, but
+others can be added easilly.
+Filesystems are grafted into the virtual filesystem either explicitely, by
+mounting, or implicitely, by having files of a specific type (for instance
+those ending on .zip) mounted automatically.
+When a filesystem is mounted to a directory which already exists in an
+earlier mounted file system, files in the later mounted file system hide
+files in an earlier mounted file system. Files present in a filesystem
+mounted earlier that don't exist in a filesystem mounted later will remain
+visible.
+Accessing compressed files inside compressed files is possible, though slow.
+
+
+-= Nomenclature =-
+'repository'
+A collection of files from various sources as a virtual file system.
+
+'physical directory structure'
+An underlying filesystem, such as a POSIX filesystem, or a .zip archive.
+
+'logical directory structure'
+The merger of one or more Physical directory structures.
+
+'mounting'
+Grafting a physical directory structure in a logical directory system.
+
+When mounting several dirs on top of eachother, I refer to later mounted
+dirs as 'higher' dirs, and earlier mounted dirs as 'lower' dirs.
+
+'directory entry'
+A file or subdirectory within a directory.
+
+
+-= API =-
+
+Types:
+uio_Repository - A struct describing a repository
+uio_Handle - A handle for working on files
+uio_DirHandle - A handle for working on directories
+
+
+TODO: functions
+
+
+-= Behaviour relating directory structures =-
+
+The design of the virtual filesystem is guided by the following rules:
+- Combined directories contain the entries of each directory.
+ If some of the source directories contain an entry with the same name,
+ the combined directory will contain the entry of the topmost directory.
+ (this means a directory can hide a file and the other way around)
+- Entries hidden in this way are never accessable.
+- Where possible, actions on directory entries within a combined directory
+ are done as in a normal file system.
+Because of these, some design decisions have been made:
+- New entries are created in the topmost writable filesystem. If the path to
+ the directory where the entry is to be created does not exist, it is
+ created [1].
+- When a file is to be opened for writing, and the file in the combined
+ filesystem exists, but is not writable, and there exists a writable
+ filesystem, mounted higher than the filesystem where the existing file
+ resides, the file is first copied to the topmost writable filesystem.
+
+[1] I could have decided to use a pre-existing writable directory if one
+ is available.
+ With this choice, when no such pre-existing directory exists,
+ it would make sense to complete the path in the writable filesystem
+ in which the part of the path that does exist is the longest.
+ As you can't create a directory inside a specific filesystem,
+ this would complicate and confuse things for the user.
+
+
+In specific, for various actions:
+
+opening a file:
+- if O_RDONLY:
+ - open the file in the highest dir.
+- if O_WRONLY or O_RDWR:
+ - if file already exists:
+ - if O_CREAT and O_EXCL: return EEXIST
+ - if file is writable, use that one
+ - if file is not writable, copy it to the highest writable location
+ higher than the location of that file (don't bother if O_TRUNC) and use
+ the new file. If necessary, create the path leading upto it.
+ If no such location exists, return EACCESS.
+ - if file does not exist:
+ - if not O_CREAT: return ENOENT
+ - if the path to the file does not exists, return ENOENT.
+ - find the highest writable dir and open the file there, creating
+ the path leading upto it if necessary.
+
+removing a file:
+- try removing the specified file from all physical directory structures
+ for a repository.
+- once a file is encountered that can't be removed, return an error for
+ that and don't try the rest.
+
+creating a directory:
+- as for opening a file with O_WRONLY | O_CREAT | O_EXCL
+
+removing a directory:
+- as for removing a file
+ (but a physical directory not being empty is a reason for failure)
+
+
+-= Limitations =-
+
+There's no limit to the length of a path in the logical file system.
+Paths in the underlying physical filesystem can be limited though.
+
+At the moment, the system is not thread safe. Only one thread should access
+a repository at once. Seperate threads working on seperate repositories is
+no problem, as long as the repositories don't overlap.
+
+
+-= Internals =-
+
+Types:
+
+uio_MountTree
+ A node in a data structure describing the mounted directories.
+
+uio_PRoot
+ A struct describing the a physical file system.
+
+uio_PRootExtra
+ Filesystem-dependant extra data for a PRoot.
+
+uio_GPRoot
+ Generic filesystem-dependant data for a PRoot, used as uio_PRootExtra.
+
+uio_GPRootExtra
+ Extra filesystem-dependant data for a PRoot, when using uio_GPRoot for
+ generic filesystem-dependant data.
+
+uio_GPDir
+ Generic structure representing a node in a physical directory structure
+ describing one directory.
+
+uio_GPFile
+ Generic structure describing a file in a physical file system.
+
+
+Helper functions (defined in ioaux.c):
+
+uio_copyFilePhysical
+ Copy a file from one physical directory to another.
+
+uio_getPathPhysicalDirs
+ Get handles to the (existing) physical dirs that are effective in a
+ path 'path' relative from 'dirHandle'
+
+uio_getPhysicalAccess
+ Find PDirHandle and MountInfo structures for reading and writing for a path
+ from a DirHandle.
+
+uio_makePath
+ Create a directory inside a physical directory. All non-existant
+ parent directories will be created as well.
+
+uio_resolvePath
+ Determine the absolute path given a path relative to a given directory.
+
+uio_verifyPath
+ Test whether a path is valid and exists.
+
+uio_walkPhysicalPath
+ Follow a path starting from a specified physical dir for as long as
+ possible.
+
+
diff --git a/src/libs/uio/doc/conventions b/src/libs/uio/doc/conventions
new file mode 100644
index 0000000..f156101
--- /dev/null
+++ b/src/libs/uio/doc/conventions
@@ -0,0 +1,30 @@
+This file describes the various conventions used in the source.
+
+
+-= Naming of constructors and destructors =-
+
+uio_Thing *uio_Thing_new(args)
+ Allocates the structure, and initialises it from the arguments.
+ Arguments are always used as-is; if they are reference-counted,
+ and the caller still needs a reference, it is up to the caller
+ to increment the reference counter.
+void uio_Thing_delete(uio_Thing *thing)
+ Frees the structure and all sub-structures.
+ Decrements reference counters to shared structures.
+uio_Thing *uio_Thing_alloc()
+ Allocates memory for a new structure.
+ Could be omited for standard allocators.
+ Particularly relevant when a non-standard allocation scheme is
+ used (for instance, allocation from a pool of pre-allocated strucures).
+void uio_Thing_free(Thing *thing)
+ Frees the memory allocated for thing (reverse of uio_Thing_alloc).
+ Could be omited for standard deallocators.
+void uio_Thing_ref(Thing *thing)
+ Increments the reference counter to the structure.
+void uio_Thing_unref(Thing *thing)
+ Decrements the reference counter to the structure.
+ Calls uio_Thing_Delete() if the refcounter becomes 0.
+
+These functions are always declared and defined in this order.
+
+
diff --git a/src/libs/uio/doc/todo b/src/libs/uio/doc/todo
new file mode 100644
index 0000000..b9f232c
--- /dev/null
+++ b/src/libs/uio/doc/todo
@@ -0,0 +1,144 @@
+Needed for use in UQM:
+- documentation
+- configuring for GLOB
+- when doing uio_getStdioPhysical(), if write access is required, but
+ not available on the original location, also copy the file to the
+ temporary dir.
+- Call fsync() at appropriate times.
+
+Documentation:
+- use doxygen
+- Warning: getting (part of) the contents of a directory is fairly slow,
+ as dirs need to be merged.
+- It would be (theoretically) possible to add HTTP and FTP support for
+ remote file systems.
+- on adding extra file system types:
+ open, mkdir, rmdir, and unlink should themselves make sure that
+ the physical structure is kept up to date when an entry is
+ removed or added.
+- stream stuff is not thread safe
+- uio_fflush() does not accept NULL as argument to flush all streams,
+ like stdio fflush() does.
+- uio_close() does never fail
+- The physical open function should call uio_Handle_new and store its own
+ data in it.
+ The physical close function should delete its own data. It's called
+ Will cleanup the general Handle.
+ Analogous for mount/unmount with PRoot
+- No need to store MountHandles. They will be automatically freed
+ when the repository is closed.
+- You can use mount stuff from other repositories.
+- ".." works by nullifying one path component, not by following the ".." link
+ in the directory. "/a/../b" will always be functionally equivalent to
+ "/b", even when "/a" is a symlink.
+
+Testing:
+- Test mounting an UNC directory
+
+Bugs:
+- 'openDir(repository, "dir/")' will have the trailing '/'
+ in the dirHandle, which will cause problems.
+- 'openDirRelative(repository, "/")' causes segfaults later on
+- uio_rename() doesn't work on directories
+- uio_getPhysicalAccess() needs to be changed so that it works the same on
+ dirs as on files. stat() can be cleaned up too then.
+- uio_getPhysicalAccess() will not return ENOENT when (only) the last
+ component does not exist, even when O_RDONLY is used.
+ For O_RDRW, O_CREAT should probably be checked too (function description
+ needs to be updated too then).
+- A lot of inlining is not possible because of the order of function
+ definitions.
+- sizeof(size_t) may be less than 4 on some platforms. Check what problems
+ this may cause. At least the size of zip_INPUT_BUFFER_SIZE is an
+ issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime.
+- remove() is probably more compatible than unlink(). remove() is part
+ of the C standard (as well as POSIX), unlink() is just POSIX.
+- seeking in files opened as "text" on Windows goes wrong.
+- Network paths on Windows are not accepted.
+- No CRLF translation (and ^Z recognition) is done for files read from
+ zip files, even though that may be expected on Windows.
+- The order in which "uio_unmountAllDirs() unmounts the directories, may lead
+ it to unmount a dir while there still is a file descriptor for another
+ dir open, causing a warning.
+
+Extra features (not necessary for UQM):
+- Make functions to use for uio_malloc, uio_free and uio_realloc
+ configurable at init.
+- add uio_mmap()
+- add match_MATCH_ALL and match_MATCH_NONE
+- automounting
+ - Unmount automounted filesystems when the originating filesystem
+ is unmounted.
+- when doing stat() on a directory, merge the stat info of the underlying
+ physical dirs.
+- read directory information from zip files.
+- implement ungetc()
+- make fileblocks public
+- setmode() for windows?
+- prevent aliasing of files/dirs, both between two physical file
+ systems (where possible), as within the same filesystem (in particular
+ stdio). On Windows, aliasing can occur easilly when a file or dir
+ is accessed with the long vs the short ("PROGRA~1") name, or when
+ capitalisation is involved.
+- accept non-dir, non-regular-file dir entries in stdio.
+- Add non-merging mounts. Make 'merging' an option for the mount.
+- make uio_rename() work cross-fs, or provide a wrapper which does that.
+ (the system rename() doesn't work cross-fs either, so keeping uio_rename()
+ as it is makes sense, as I'm trying to stay close to the system functions,
+ even though hiding file systems from the user would be nicer)
+- Add a readdir_r(). Right now, for Symbian readdir_r() is defined in
+ uioport.h, but the actual implementation is out of the uio tree.
+
+Optimisations (not necessary for UQM):
+- use mmap for fileBlocks
+- Use a pre-allocated pool of hash table entries for allocHashEntry.
+- optimise certain strings (specifically directory entries) by making
+ a string type which has pointers to a shared char array.
+ Invariant: if two strings are the same, they point to the same
+ character array. Consequence: string comparison is pointer comparison.
+ Making a new string would imply checking the existing strings.
+ Existing strings should be in a hash table.
+- optimise paths
+ Encode all paths internally as (double-linked) chains of path components
+ This way, operations like going up one path component, are cheap.
+ Each path component could either be represented by a pointer to a string,
+ (possibly shared as above), or as a pair of begin and end pointers
+ (or begin pointer and length). In the latter case, there's no need for
+ copying strings when parsing a user-supplied string, but sharing of strings
+ would not be possible.
+ It's probably possible to use both methods next to eachother;
+ shared strings for stored paths, and pointers into a path for user-supplied
+ strings (which would be freed when the call returns).
+ Instead of a linked list, perhaps an array can be used, as paths rarely
+ change. It would be a problem if for some reason, recursively path
+ components are added to a path.
+- (maybe) keep track of already issued handles, so that the ref counter
+ could just be incremented, instead of issuing a new one.
+ (uio_getPDirEntryHandle)
+- Make it possible for people to add their own file system, without giving
+ away the internals. (no wanton including of .h files)
+- Don't cache stdio dir structure
+ (and don't read the dir leading up to the dir that is actually needed)
+- mounting foo to /Foo and foo/bar to /Foo/Bar should result in only one
+ physical structure. Preferably, it shouldn't even lead to two mountInfo
+ structures for /Foo/Bar, so that merging the dirs is not unnecessarilly
+ expensive.
+- add uio_access()
+ (uqm fileExists can be redone then)
+- implement the physical function cleanup() to clean up the physical
+ structure. Int argument that specifies how thoroughly.
+ On it depends whether cache is cleaned, whether stuff is realloc'ed
+ to defragment memory, etc.
+
+Cleanups (not necessary for UQM):
+- rename function names to be more like class names for as far as that's
+ not already done.
+ uio_PRoot_unref etc
+- Add uio_fatal(), uio_error(), uio_warning()
+- Clean up the include structure
+- use stdint.h and stdbool.h types directly, instead of using uio_int16 etc.
+ Remove types.h, and instead, if these types are missing on some platforms,
+ put the fixes in port.h.
+
+
+
diff --git a/src/libs/uio/fileblock.c b/src/libs/uio/fileblock.c
new file mode 100644
index 0000000..bb4f466
--- /dev/null
+++ b/src/libs/uio/fileblock.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define uio_INTERNAL_FILEBLOCK
+
+#include "iointrn.h"
+#include "fileblock.h"
+#include "uioport.h"
+
+#include <errno.h>
+
+static uio_FileBlock *uio_FileBlock_new(uio_Handle *handle, int flags,
+ off_t offset, size_t blockSize, char *buffer, size_t bufSize,
+ off_t bufOffset, size_t bufFill, size_t readAheadBufSize);
+static inline uio_FileBlock *uio_FileBlock_alloc(void);
+static void uio_FileBlock_free(uio_FileBlock *block);
+
+// caller should uio_Handle_ref(handle) (unless it doesn't need it's own
+// reference anymore).
+static uio_FileBlock *
+uio_FileBlock_new(uio_Handle *handle, int flags, off_t offset,
+ size_t blockSize, char *buffer, size_t bufSize, off_t bufOffset,
+ size_t bufFill, size_t readAheadBufSize) {
+ uio_FileBlock *result;
+
+ result = uio_FileBlock_alloc();
+ result->handle = handle;
+ result->flags = flags;
+ result->offset = offset;
+ result->blockSize = blockSize;
+ result->buffer = buffer;
+ result->bufSize = bufSize;
+ result->bufOffset = bufOffset;
+ result->bufFill = bufFill;
+ result->readAheadBufSize = readAheadBufSize;
+ return result;
+}
+
+static inline uio_FileBlock *
+uio_FileBlock_alloc(void) {
+ return uio_malloc(sizeof (uio_FileBlock));
+}
+
+static inline void
+uio_FileBlock_free(uio_FileBlock *block) {
+ uio_free(block);
+}
+
+uio_FileBlock *
+uio_openFileBlock(uio_Handle *handle) {
+ // TODO: if mmap support is available, and it is available natively
+ // for the filesystem (make some sort of sysctl for filesystems
+ // to check this?), use mmap.
+ // mmap the entire file if it's small enough.
+ // N.B. Keep in mind streams of which the size is not known in
+ // advance.
+ struct stat statBuf;
+ if (uio_fstat(handle, &statBuf) == -1) {
+ // errno is set
+ return NULL;
+ }
+ uio_Handle_ref(handle);
+ return uio_FileBlock_new(handle, 0, 0, statBuf.st_size, NULL, 0, 0, 0, 0);
+}
+
+uio_FileBlock *
+uio_openFileBlock2(uio_Handle *handle, off_t offset, size_t size) {
+ // TODO: mmap (see uio_openFileBlock)
+
+ // TODO: check if offset and size are acceptable.
+ // Need to handle streams of which the size is unknown.
+#if 0
+ if (uio_stat(handle, &statBuf) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (statBuf.st_size > offset || (statBuf.st_size - offset > size)) {
+ // NOT: 'if (statBuf.st_size > offset + size)', to protect
+ // against overflow.
+
+ }
+#endif
+ uio_Handle_ref(handle);
+ return uio_FileBlock_new(handle, 0, offset, size, NULL, 0, 0, 0, 0);
+}
+
+static inline ssize_t
+uio_accessFileBlockMmap(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ // TODO
+ errno = ENOSYS;
+ (void) block;
+ (void) offset;
+ (void) length;
+ (void) buffer;
+ return -1;
+}
+
+static inline ssize_t
+uio_accessFileBlockNoMmap(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ ssize_t numRead;
+ off_t start;
+ off_t end;
+ size_t bufSize;
+ char *oldBuffer;
+ //size_t oldBufSize;
+
+ // Don't go beyond the end of the block.
+ if (offset > (off_t) block->blockSize) {
+ *buffer = block->buffer;
+ return 0;
+ }
+ if (length > block->blockSize - offset)
+ length = block->blockSize - offset;
+
+ if (block->buffer != NULL) {
+ // Check whether the requested data is already in the buffer.
+ if (offset >= block->bufOffset &&
+ (offset - block->bufOffset) + length < block->bufFill) {
+ *buffer = block->buffer + (offset - block->bufOffset);
+ return length;
+ }
+ }
+
+ if (length < block->readAheadBufSize &&
+ (block->flags & uio_FB_USAGE_MASK) != 0) {
+ // We can buffer more data.
+ switch (block->flags & uio_FB_USAGE_MASK) {
+ case uio_FB_USAGE_FORWARD:
+ // Read extra data after the requested data.
+ start = offset;
+ end = (block->readAheadBufSize > block->blockSize - offset) ?
+ block->blockSize : offset + block->readAheadBufSize;
+ break;
+ case uio_FB_USAGE_BACKWARD:
+ // Read extra data before the requested data.
+ end = offset + length;
+ start = (end <= (off_t) block->blockSize) ?
+ 0 : end - block->bufSize;
+ break;
+ case uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD: {
+ // Read extra data both before and after the requested data.
+ off_t extraBefore = (block->readAheadBufSize - length) / 2;
+ start = (offset < extraBefore) ? 0 : offset - extraBefore;
+
+ end = (block->readAheadBufSize > block->blockSize - start) ?
+ block->blockSize : start + block->readAheadBufSize;
+ break;
+ }
+ }
+ } else {
+ start = offset;
+ end = offset + length;
+ }
+ bufSize = (length > block->readAheadBufSize) ?
+ length : block->readAheadBufSize;
+
+ // Start contains the start index in the block of the data we're going
+ // to read.
+ // End contains the end index.
+ // bufSize contains the size of the buffer. bufSize >= end - start.
+
+ oldBuffer = block->buffer;
+ //oldBufSize = block->bufSize;
+ if (block->buffer != NULL || block->bufSize != bufSize) {
+ // We don't have a buffer, or we have one, but of the wrong size.
+ block->buffer = uio_malloc(bufSize);
+ block->bufSize = bufSize;
+ }
+
+ if (oldBuffer != NULL) {
+ // TODO: If we have part of the data still in the old buffer, we
+ // can keep that.
+ // memmove(...)
+
+ if (oldBuffer != block->buffer)
+ uio_free(oldBuffer);
+ }
+ block->bufFill = 0;
+ block->bufOffset = start;
+
+ // TODO: lock handle
+ if (uio_lseek(block->handle, block->offset + start, SEEK_SET) ==
+ (off_t) -1) {
+ // errno is set
+ return -1;
+ }
+
+ numRead = uio_read(block->handle, block->buffer, end - start);
+ if (numRead == -1) {
+ // errno is set
+ // TODO: unlock handle
+ return -1;
+ }
+ // TODO: unlock handle
+
+ block->bufFill = numRead;
+ *buffer = block->buffer + (offset - block->bufOffset);
+ if (numRead <= (offset - block->bufOffset))
+ return 0;
+ if ((size_t) numRead >= length)
+ return length;
+ return numRead - offset;
+}
+
+// block remains usable until the next call to uio_accessFileBlock
+// with the same block as argument, or until uio_closeFileBlock with
+// that block as argument.
+// The 'offset' parameter is wrt. the start of the block.
+// Requesting access to data beyond the file block is not an error. The
+// FileBlock is meant to be used as a replacement of seek() and read(), and
+// as with those functions, trying to go beyond the end of a file just
+// goes to the end. The return value is the number of bytes in the buffer.
+ssize_t
+uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ if (block->flags & uio_FB_USE_MMAP) {
+ return uio_accessFileBlockMmap(block, offset, length, buffer);
+ } else {
+ return uio_accessFileBlockNoMmap(block, offset, length, buffer);
+ }
+}
+
+int
+uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer,
+ size_t length) {
+ if (block->flags & uio_FB_USE_MMAP) {
+ // TODO
+ errno = ENOSYS;
+ return -1;
+ } else {
+ ssize_t numCopied = 0;
+ ssize_t readResult;
+
+ // Don't go beyond the end of the block.
+ if (offset > (off_t) block->blockSize)
+ return 0;
+ if (length > block->blockSize - offset)
+ length = block->blockSize - offset;
+
+ // Check whether (part of) the requested data is already in our
+ // own buffer.
+ if (block->buffer != NULL && offset >= block->bufOffset
+ && offset < block->bufOffset + (off_t) block->bufFill) {
+ size_t toCopy = block->bufFill - offset;
+ if (toCopy > length)
+ toCopy = length;
+ memcpy(buffer, block->buffer + (offset - block->bufOffset),
+ toCopy);
+ numCopied += toCopy;
+ length -= toCopy;
+ if (length == 0)
+ return numCopied;
+ buffer += toCopy;
+ offset += toCopy;
+ }
+
+ // TODO: lock handle
+ if (uio_lseek(block->handle, block->offset + offset, SEEK_SET) ==
+ (off_t) -1) {
+ // errno is set
+ return -1;
+ }
+
+ readResult = uio_read(block->handle, buffer, length);
+ // TODO: unlock handle
+ if (readResult == -1) {
+ // errno is set
+ return -1;
+ }
+ numCopied += readResult;
+
+ return numCopied;
+ }
+}
+
+int
+uio_closeFileBlock(uio_FileBlock *block) {
+ if (block->flags & uio_FB_USE_MMAP) {
+#if 0
+ if (block->buffer != NULL)
+ uio_mmunmap(block->buffer);
+#endif
+ } else {
+ if (block->buffer != NULL)
+ uio_free(block->buffer);
+ }
+ uio_Handle_unref(block->handle);
+ uio_FileBlock_free(block);
+ return 0;
+}
+
+// Usage is the or'ed value of zero or more of uio_FB_USAGE_FORWARD,
+// and uio_FB_USAGE_BACKWARD.
+void
+uio_setFileBlockUsageHint(uio_FileBlock *block, int usage,
+ size_t readAheadBufSize) {
+ block->flags = (block->flags & ~uio_FB_USAGE_MASK) |
+ (usage & uio_FB_USAGE_MASK);
+ block->readAheadBufSize = readAheadBufSize;
+}
+
+// Call if you want the memory used by the fileblock to be released, but
+// still want to use the fileblock later. If you don't need the fileblock,
+// call uio_closeFileBlock() instead.
+void
+uio_clearFileBlockBuffers(uio_FileBlock *block) {
+ if (block->buffer != NULL) {
+ uio_free(block->buffer);
+ block->buffer = NULL;
+ }
+}
+
+
diff --git a/src/libs/uio/fileblock.h b/src/libs/uio/fileblock.h
new file mode 100644
index 0000000..97a81e1
--- /dev/null
+++ b/src/libs/uio/fileblock.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_FILEBLOCK_H_
+#define LIBS_UIO_FILEBLOCK_H_
+
+typedef struct uio_FileBlock uio_FileBlock;
+
+#include "io.h"
+#include "uioport.h"
+
+#include <sys/types.h>
+
+#define uio_FB_USAGE_FORWARD 1
+#define uio_FB_USAGE_BACKWARD 2
+#define uio_FB_USAGE_MASK (uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD)
+
+#ifdef uio_INTERNAL_FILEBLOCK
+
+// A fileblock represents a contiguous block of data from a file.
+// It's purpose is to avoid needless copying of data, while enabling
+// buffering.
+
+struct uio_FileBlock {
+ uio_Handle *handle;
+ int flags;
+ // See above for uio_FB_USAGE_FORWARD, uio_FB_USAGE_BACKWARD.
+#define uio_FB_USE_MMAP 4
+ off_t offset;
+ // Offset to the start of the block in the file.
+ size_t blockSize;
+ // Size of the block of data represented by this FileBlock.
+ char *buffer;
+ // either allocated buffer, or buffer to mmap'ed area.
+ size_t bufSize;
+ // Size of the buffer.
+ off_t bufOffset;
+ // Offset of the start of the buffer into the block.
+ size_t bufFill;
+ // Part of 'buffer' which is in use.
+ size_t readAheadBufSize;
+ // Try to read up to this many bytes at a time, even when less
+ // is immediately needed.
+};
+// INV: The FileBlock represents 'fileData[offset..(offset + blockSize - 1)]'
+// where 'fileData' is the contents of the file.
+// INV: If buf != NULL then:
+// bufFill <= bufSize
+// bufFill <= blockSize
+// buffer[0..bufFill - 1] == fileData[
+// (offset + bufOffset)..(offset + bufOffset + bufFill - 1)]
+
+
+#endif /* uio_INTERNAL_FILEBLOCK */
+
+uio_FileBlock *uio_openFileBlock(uio_Handle *handle);
+uio_FileBlock *uio_openFileBlock2(uio_Handle *handle, off_t offset,
+ size_t size);
+ssize_t uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer);
+int uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer,
+ size_t length);
+int uio_closeFileBlock(uio_FileBlock *block);
+#define uio_FB_READAHEAD_BUFSIZE_MAX ((size_t) -1)
+void uio_setFileBlockUsageHint(uio_FileBlock *block, int usage,
+ size_t readAheadBufSize);
+void uio_clearFileBlockBuffers(uio_FileBlock *block);
+
+#endif /* LIBS_UIO_FILEBLOCK_H_ */
+
+
diff --git a/src/libs/uio/fstypes.c b/src/libs/uio/fstypes.c
new file mode 100644
index 0000000..d940e8f
--- /dev/null
+++ b/src/libs/uio/fstypes.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+#include "mem.h"
+#include "defaultfs.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+
+static uio_bool uio_validFileSystemHandler(uio_FileSystemHandler *handler);
+static uio_FileSystemInfo *uio_FileSystemInfo_new(uio_FileSystemID id,
+ uio_FileSystemHandler *handler, char *name);
+static uio_FileSystemInfo **uio_getFileSystemInfoPtr(uio_FileSystemID id);
+
+static inline uio_FileSystemInfo *uio_FileSystemInfo_alloc(void);
+
+static inline void uio_FileSystemInfo_free(
+ uio_FileSystemInfo *fileSystemInfo);
+
+
+uio_FileSystemInfo *uio_fileSystems = NULL;
+ // list sorted by id
+
+
+
+void
+uio_registerDefaultFileSystems(void) {
+ int i;
+ int num;
+ uio_FileSystemID registerResult;
+
+ num = uio_numDefaultFileSystems();
+ for (i = 0; i < num; i++) {
+ registerResult = uio_registerFileSystem(
+ defaultFileSystems[i].id,
+ defaultFileSystems[i].name,
+ defaultFileSystems[i].handler);
+ switch (registerResult) {
+ case 0:
+ fprintf(stderr, "Warning: Default file system '%s' is "
+ "already registered.\n",
+ defaultFileSystems[i].name);
+ break;
+ case -1:
+ fprintf(stderr, "Error: Could not register '%s' file \n"
+ "system: %s\n", defaultFileSystems[i].name,
+ strerror(errno));
+ break;
+ default:
+ assert(registerResult == defaultFileSystems[i].id);
+ break;
+ }
+ }
+}
+
+void
+uio_unRegisterDefaultFileSystems(void) {
+ int i;
+ int num;
+
+ num = uio_numDefaultFileSystems();
+ for (i = 0; i < num; i++) {
+ if (uio_unRegisterFileSystem(defaultFileSystems[i].id) == -1) {
+ fprintf(stderr, "Could not unregister '%s' file system: %s\n",
+ defaultFileSystems[i].name, strerror(errno));
+ }
+ }
+}
+
+// if wantedID = 0, just pick one
+// if wantedID != 0, 0 will be returned if that id wasn't available
+// a copy of 'name' is made
+uio_FileSystemID
+uio_registerFileSystem(uio_FileSystemID wantedID, const char *name,
+ uio_FileSystemHandler *handler) {
+ uio_FileSystemInfo **ptr;
+
+ if (!uio_validFileSystemHandler(handler)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (wantedID == 0) {
+ // Search for the first free id >= uio_FIRST_CUSTOM_ID
+ // it is put in wantedID
+
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next)
+ if ((*ptr)->id >= uio_FS_FIRST_CUSTOM_ID)
+ break;
+
+ wantedID = uio_FS_FIRST_CUSTOM_ID;
+ while (*ptr != NULL) {
+ if ((*ptr)->id != wantedID) {
+ // wantedID is not in use
+ break;
+ }
+ wantedID++;
+ ptr = &(*ptr)->next;
+ }
+ // wantedID contains the new ID
+ } else {
+ // search for the place in the list where to insert the wanted
+ // id, keeping the list sorted
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->id <= wantedID) {
+ if ((*ptr)->id == wantedID)
+ return 0;
+ break;
+ }
+ }
+
+ }
+ // ptr points to the place where the new link can inserted
+
+ if (handler->init != NULL && handler->init() == -1) {
+ // errno is set
+ return -1;
+ }
+
+ {
+ uio_FileSystemInfo *newInfo;
+
+ newInfo = uio_FileSystemInfo_new(wantedID, handler, uio_strdup(name));
+ newInfo->next = *ptr;
+ *ptr = newInfo;
+ return wantedID;
+ }
+}
+
+int
+uio_unRegisterFileSystem(uio_FileSystemID id) {
+ uio_FileSystemInfo **ptr;
+ uio_FileSystemInfo *temp;
+
+ ptr = uio_getFileSystemInfoPtr(id);
+ if (ptr == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((*ptr)->ref > 1) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ if ((*ptr)->handler->unInit != NULL &&
+ ((*ptr)->handler->unInit() == -1)) {
+ // errno is set
+ return -1;
+ }
+
+ temp = *ptr;
+ *ptr = (*ptr)->next;
+
+// uio_FileSystemHandler_unref(temp->handler);
+ uio_free(temp->name);
+ uio_FileSystemInfo_free(temp);
+
+ return 0;
+}
+
+static uio_bool
+uio_validFileSystemHandler(uio_FileSystemHandler *handler) {
+ // Check for the essentials
+ if (handler->mount == NULL ||
+ handler->umount == NULL ||
+ handler->open == NULL ||
+ handler->close == NULL ||
+ handler->read == NULL ||
+ handler->openEntries == NULL ||
+ handler->readEntries == NULL ||
+ handler->closeEntries == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Invalid file system handler.\n");
+#endif
+ return false;
+ }
+ return true;
+}
+
+uio_FileSystemHandler *
+uio_getFileSystemHandler(uio_FileSystemID id) {
+ uio_FileSystemInfo *ptr;
+
+ for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) {
+ if (ptr->id == id)
+ return ptr->handler;
+ }
+ return NULL;
+}
+
+uio_FileSystemInfo *
+uio_getFileSystemInfo(uio_FileSystemID id) {
+ uio_FileSystemInfo *ptr;
+
+ for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) {
+ if (ptr->id == id)
+ return ptr;
+ }
+ return NULL;
+}
+
+static uio_FileSystemInfo **
+uio_getFileSystemInfoPtr(uio_FileSystemID id) {
+ uio_FileSystemInfo **ptr;
+
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->id == id)
+ return ptr;
+ }
+ return NULL;
+}
+
+// sets ref to 1
+static uio_FileSystemInfo *
+uio_FileSystemInfo_new(uio_FileSystemID id, uio_FileSystemHandler *handler,
+ char *name) {
+ uio_FileSystemInfo *result;
+
+ result = uio_FileSystemInfo_alloc();
+ result->id = id;
+ result->handler = handler;
+ result->name = name;
+ result->ref = 1;
+ return result;
+}
+
+// *** Allocators ***
+
+static inline uio_FileSystemInfo *
+uio_FileSystemInfo_alloc(void) {
+ uio_FileSystemInfo *result = uio_malloc(sizeof (uio_FileSystemInfo));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_FileSystemInfo, (void *) result);
+#endif
+ return result;
+}
+
+
+// *** Deallocators ***
+
+static inline void
+uio_FileSystemInfo_free(uio_FileSystemInfo *fileSystemInfo) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_FileSystemInfo, (void *) fileSystemInfo);
+#endif
+ uio_free(fileSystemInfo);
+}
+
+
diff --git a/src/libs/uio/fstypes.h b/src/libs/uio/fstypes.h
new file mode 100644
index 0000000..3ff01a2
--- /dev/null
+++ b/src/libs/uio/fstypes.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_FSTYPES_H_
+#define LIBS_UIO_FSTYPES_H_
+
+typedef int uio_FileSystemID;
+#define uio_FSTYPE_STDIO 1
+#define uio_FSTYPE_ZIP 2
+
+
+#ifdef uio_INTERNAL
+
+#define uio_FS_FIRST_CUSTOM_ID 0x10
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_NativeHandle;
+#endif
+
+// 'forward' declarations
+typedef struct uio_FileSystemHandler uio_FileSystemHandler;
+typedef struct uio_FileSystemInfo uio_FileSystemInfo;
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "physical.h"
+#include "uioport.h"
+
+
+/* Structure describing how to access files _in_ a directory of a certain
+ * type (not files _of_ a certain type.) Except for mount().
+ * in open(), the first arg points to the dir where the file should be
+ * in, and the second arg is the name of that file (no path)
+ */
+struct uio_FileSystemHandler {
+ int (*init) (void);
+ int (*unInit) (void);
+ void (*cleanup) (uio_PRoot *, int);
+ // Called to cleanup memory. The second argument specifies
+ // how thoroughly.
+
+ struct uio_PRoot * (*mount) (uio_Handle *, int);
+ int (*umount) (uio_PRoot *);
+
+ int (*access) (uio_PDirHandle *, const char *, int mode);
+ void (*close) (uio_Handle *);
+ // called when the last reference is closed, not
+ // necessarilly each time when uio_close() is called
+ int (*fstat) (uio_Handle *, struct stat *);
+ int (*stat) (uio_PDirHandle *, const char *,
+ struct stat *);
+ uio_PDirHandle * (*mkdir) (uio_PDirHandle *, const char *, mode_t);
+ uio_Handle * (*open) (uio_PDirHandle *, const char *, int,
+ mode_t);
+ ssize_t (*read) (uio_Handle *, void *, size_t);
+ int (*rename) (uio_PDirHandle *, const char *,
+ uio_PDirHandle *, const char *);
+ int (*rmdir) (uio_PDirHandle *, const char *);
+ off_t (*seek) (uio_Handle *, off_t, int);
+ ssize_t (*write) (uio_Handle *, const void *, size_t);
+ int (*unlink) (uio_PDirHandle *, const char *);
+
+ uio_NativeEntriesContext (*openEntries) (uio_PDirHandle *);
+ int (*readEntries) (uio_NativeEntriesContext *, char *,
+ size_t);
+ void (*closeEntries) (uio_NativeEntriesContext);
+
+ uio_PDirEntryHandle * (*getPDirEntryHandle) (
+ const uio_PDirHandle *, const char *);
+ void (*deletePRootExtra) (uio_PRootExtra pRootExtra);
+ void (*deletePDirHandleExtra) (
+ uio_PDirHandleExtra pDirHandleExtra);
+ void (*deletePFileHandleExtra) (
+ uio_PFileHandleExtra pFileHandleExtra);
+};
+
+struct uio_FileSystemInfo {
+ int ref;
+ uio_FileSystemID id;
+ char *name; // name of the file system
+ uio_FileSystemHandler *handler;
+ struct uio_FileSystemInfo *next;
+};
+
+void uio_registerDefaultFileSystems(void);
+void uio_unRegisterDefaultFileSystems(void);
+uio_FileSystemID uio_registerFileSystem(uio_FileSystemID wantedID,
+ const char *name, uio_FileSystemHandler *handler);
+int uio_unRegisterFileSystem(uio_FileSystemID id);
+uio_FileSystemHandler *uio_getFileSystemHandler(uio_FileSystemID id);
+uio_FileSystemInfo *uio_getFileSystemInfo(uio_FileSystemID id);
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_FSTYPES_H_ */
+
diff --git a/src/libs/uio/getint.h b/src/libs/uio/getint.h
new file mode 100644
index 0000000..ad6e810
--- /dev/null
+++ b/src/libs/uio/getint.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_GETINT_H_
+#define LIBS_UIO_GETINT_H_
+
+/* All these functions return true on success, or false on failure */
+
+#include "types.h"
+#include "uioport.h"
+
+static inline uio_bool
+uio_getU8(uio_Stream *stream, uio_uint8 *result) {
+ int val = uio_getc(stream);
+ if (val == EOF)
+ return false;
+
+ *result = (uio_uint8) val;
+ return true;
+}
+
+static inline uio_bool
+uio_getS8(uio_Stream *stream, uio_sint8 *result) {
+ int val = uio_getc(stream);
+ if (val == EOF)
+ return false;
+
+ *result = (uio_sint8) val;
+ return true;
+}
+
+static inline uio_bool
+uio_getU16LE(uio_Stream *stream, uio_uint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[1] << 8) | buf[0];
+ return true;
+}
+
+static inline uio_bool
+uio_getU16BE(uio_Stream *stream, uio_uint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[0] << 8) | buf[1];
+ return true;
+}
+
+static inline uio_bool
+uio_getS16LE(uio_Stream *stream, uio_sint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint16) ((buf[1] << 8) | buf[0]);
+ return true;
+}
+
+static inline uio_bool
+uio_getS16BE(uio_Stream *stream, uio_sint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint16) ((buf[0] << 8) | buf[1]);
+ return true;
+}
+
+static inline uio_bool
+uio_getU32LE(uio_Stream *stream, uio_uint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ return true;
+}
+
+static inline uio_bool
+uio_getU32BE(uio_Stream *stream, uio_uint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return true;
+}
+
+static inline uio_bool
+uio_getS32LE(uio_Stream *stream, uio_sint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint32) ((buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0]);
+ return true;
+}
+
+static inline uio_bool
+uio_getS32BE(uio_Stream *stream, uio_sint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint32) ((buf[0] << 24) | (buf[1] << 16) |
+ (buf[2] << 8) | buf[3]);
+ return true;
+}
+
+
+#endif /* LIBS_UIO_GETINT_H_ */
+
diff --git a/src/libs/uio/gphys.c b/src/libs/uio/gphys.c
new file mode 100644
index 0000000..f25f557
--- /dev/null
+++ b/src/libs/uio/gphys.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define uio_INTERNAL_PHYSICAL
+#define uio_INTERNAL_GPHYS
+typedef void *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef void *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "gphys.h"
+#include "paths.h"
+#include "uioport.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static void uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir);
+static uio_GPRoot *uio_GPRoot_alloc(void);
+
+static void uio_GPRoot_free(uio_GPRoot *gPRoot);
+
+static inline uio_GPDir *uio_GPDir_alloc(void);
+void uio_GPDir_delete(uio_GPDir *gPDir);
+static inline void uio_GPDir_free(uio_GPDir *gPDir);
+
+static inline uio_GPFile *uio_GPFile_alloc(void);
+void uio_GPFile_delete(uio_GPFile *gPFile);
+static inline void uio_GPFile_free(uio_GPFile *gPFile);
+
+// Call this when you need to edit a file 'dirName' in the GPDir 'gPDir'.
+// a new entry is created when necessary.
+// uio_gPDirCommitSubDir should be called when you're done with it.
+//
+// a copy of dirName is made if needed; the caller remains responsible for
+// freeing the original
+// Allocates a new dir if necessary.
+uio_GPDir *
+uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDir *subDir;
+ uio_GPDirEntry *entry;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, dirName);
+ if (entry != NULL) {
+ if (uio_GPDirEntry_isDir(entry)) {
+ // Return existing subdir.
+ uio_GPDir_ref((uio_GPDir *) entry);
+ return (uio_GPDir *) entry;
+ } else {
+ // There already exists a file with the same name.
+ // This should not happen within one file system.
+ fprintf(stderr, "Directory %s shadows file with the same name "
+ "from the same filesystem.\n", dirName);
+ }
+ }
+
+ // return new subdir
+ subDir = uio_GPDir_new(gPDir->pRoot, NULL, uio_GPDir_DETACHED);
+ // subDir->ref is initialised at 1
+ return subDir;
+}
+
+// call this when you're done with a dir acquired by a call to
+// uio_gPDirPrepareSubDir
+void
+uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName,
+ uio_GPDir *subDir) {
+ if (subDir->flags & uio_GPDir_DETACHED) {
+ // New dir.
+ // reference to the subDir is passed along to the upDir,
+ // so subDir->ref should not be changed.
+ uio_GPDirEntries_add(gPDir->entries, dirName, subDir);
+ subDir->flags &= ~uio_GPDir_DETACHED;
+ if (!(subDir->flags & uio_GPDir_PERSISTENT)) {
+ // Persistent dirs have an extra reference.
+ uio_GPDir_unref(subDir);
+ }
+ } else {
+ uio_GPDir_unref(subDir);
+ }
+}
+
+// a copy of fileName is made if needed; the caller remains responsible for
+// freeing the original
+void
+uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName, uio_GPFile *file) {
+ // A file will never already exist in a dir. There can only be
+ // one entry in a physical dir with the same name.
+ uio_GPDirEntries_add(gPDir->entries, fileName, (uio_GPDirEntry *) file);
+}
+
+// Pre: 'fileName' exists in 'gPDir' and is a dir.
+void
+uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName) {
+ uio_GPDirEntry *entry;
+ uio_GPFile *file;
+ uio_bool retVal;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, fileName);
+ if (entry == NULL) {
+ // This means the file has no associated GPFile.
+ // This can happen when the GPFile structure is only used for caching.
+ return;
+ }
+
+ assert(!uio_GPDirEntry_isDir(entry));
+ file = (uio_GPFile *) entry;
+ uio_GPFile_unref(file);
+ retVal = uio_GPDirEntries_remove(gPDir->entries, fileName);
+ assert(retVal);
+}
+
+// Pre: 'dirName' exists in 'gPDir' and is a dir.
+void
+uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDirEntry *entry;
+ uio_GPDir *subDir;
+ uio_bool retVal;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, dirName);
+ if (entry == NULL) {
+ // This means the directory has no associated GPDir.
+ // This can happen when the GPDir structure is only used for caching.
+ return;
+ }
+
+ assert(uio_GPDirEntry_isDir(entry));
+ subDir = (uio_GPDir *) entry;
+ if (subDir->flags & uio_GPDir_PERSISTENT) {
+ // Persistent dirs have an extra reference.
+ uio_GPDir_unref(subDir);
+ }
+ retVal = uio_GPDirEntries_remove(gPDir->entries, dirName);
+ assert(retVal);
+}
+
+void
+uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag) {
+ if (flag) {
+ gPDir->flags |= uio_GPDir_COMPLETE;
+ } else
+ gPDir->flags &= ~uio_GPDir_COMPLETE;
+}
+
+int
+uio_GPDir_entryCount(const uio_GPDir *gPDir) {
+ return uio_GPDirEntries_count(gPDir->entries);
+}
+
+static void
+uio_GPDir_access(uio_GPDir *gPDir) {
+ if (!(gPDir->flags & uio_GPDir_COMPLETE))
+ uio_GPDir_fill(gPDir);
+}
+
+// The ref counter for the dir entry is not incremented.
+uio_GPDirEntry *
+uio_GPDir_getGPDirEntry(uio_GPDir *gPDir, const char *name) {
+ uio_GPDir_access(gPDir);
+ return uio_GPDirEntries_find(gPDir->entries, name);
+}
+
+// The ref counter for the dir entry is not incremented.
+uio_PDirEntryHandle *
+uio_GPDir_getPDirEntryHandle(const uio_PDirHandle *pDirHandle,
+ const char *name) {
+ uio_GPDirEntry *gPDirEntry;
+
+ gPDirEntry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (gPDirEntry == NULL)
+ return NULL;
+ uio_GPDirEntry_ref(gPDirEntry);
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(pDirHandle->pRoot,
+ (uio_GPDir *) gPDirEntry);
+ } else {
+ return (uio_PDirEntryHandle *) uio_PFileHandle_new(pDirHandle->pRoot,
+ (uio_GPFile *) gPDirEntry);
+ }
+}
+
+/*
+ * Follow a path starting from a specified physical dir as long as possible.
+ * When you can get no further, 'endGPDir' will be filled in with the dir
+ * where you ended up, and 'pathRest' will point into the original path. to
+ * the beginning of the part that was not matched.
+ * It is allowed to have endGPDir point to gPDir and/or restPath
+ * point to path when calling this function.
+ * returns: 0 if the complete path was matched
+ * ENOENT if some component (the next one) didn't exists
+ * ENODIR if a component (the next one) exists but isn't a dir
+ * See also uio_walkPhysicalPath. The difference is that this one works
+ * directly on the GPDirs and is faster because of that.
+ */
+int
+uio_walkGPPath(uio_GPDir *startGPDir, const char *path,
+ size_t pathLen, uio_GPDir **endGPDir, const char **pathRest) {
+ const char *pathEnd;
+ const char *partStart, *partEnd;
+ char *tempBuf;
+ uio_GPDir *gPDir;
+ uio_GPDirEntry *entry;
+ int retVal;
+
+ gPDir = startGPDir;
+ tempBuf = uio_malloc(strlen(path) + 1);
+ // XXX: Use a dynamically allocated array when moving to C99.
+ pathEnd = path + pathLen;
+ getFirstPathComponent(path, pathEnd, &partStart, &partEnd);
+ while (1) {
+ if (partStart == pathEnd) {
+ retVal = 0;
+ break;
+ }
+ memcpy(tempBuf, partStart, partEnd - partStart);
+ tempBuf[partEnd - partStart] = '\0';
+
+ entry = uio_GPDir_getGPDirEntry(gPDir, tempBuf);
+ if (entry == NULL) {
+ retVal = ENOENT;
+ break;
+ }
+ if (!uio_GPDirEntry_isDir(entry)) {
+ retVal = ENOTDIR;
+ break;
+ }
+ gPDir = (uio_GPDir *) entry;
+ getNextPathComponent(pathEnd, &partStart, &partEnd);
+ }
+
+ uio_free(tempBuf);
+ *pathRest = partStart;
+ *endGPDir = gPDir;
+ return retVal;
+}
+
+uio_GPDirEntries_Iterator *
+uio_GPDir_openEntries(uio_PDirHandle *pDirHandle) {
+ uio_GPDir_access(pDirHandle->extra);
+ return uio_GPDirEntries_getIterator(pDirHandle->extra->entries);
+}
+
+// the start of 'buf' will be filled with pointers to strings
+// those strings are stored elsewhere in buf.
+// The function returns the number of strings passed along, or -1 for error.
+// If there are no more entries, the last pointer will be NULL.
+// (this pointer counts towards the return value)
+int
+uio_GPDir_readEntries(uio_GPDirEntries_Iterator **iterator,
+ char *buf, size_t len) {
+ char *end;
+ char **start;
+ int num;
+ const char *name;
+ size_t nameLen;
+
+ // buf will be filled like this:
+ // The start of buf will contain pointers to char *,
+ // the end will contain the actual char[] that those pointers point to.
+ // The start and the end will grow towards eachother.
+ start = (char **) buf;
+ end = buf + len;
+ num = 0;
+ while (!uio_GPDirEntries_iteratorDone(*iterator)) {
+ name = uio_GPDirEntries_iteratorName(*iterator);
+ nameLen = strlen(name) + 1;
+
+ // Does this work with systems that need memory access to be
+ // aligned on a certain number of bytes?
+ if ((size_t) (sizeof (char *) + nameLen) >
+ (size_t) (end - (char *) start)) {
+ // Not enough room to fit the pointer (at the beginning) and
+ // the string (at the end).
+ return num;
+ }
+ end -= nameLen;
+ memcpy(end, name, nameLen);
+ *start = end;
+ start++;
+ num++;
+ *iterator = uio_GPDirEntries_iteratorNext(*iterator);
+ }
+ if (sizeof (char *) > (size_t) (end - (char *) start)) {
+ // not enough room to fit the NULL pointer.
+ // It will have to be reported seperately the next time.
+ return num;
+ }
+ *start = NULL;
+ num++;
+ return num;
+}
+
+void
+uio_GPDir_closeEntries(uio_GPDirEntries_Iterator *iterator) {
+ uio_GPDirEntries_freeIterator(iterator);
+}
+
+void
+uio_GPDir_fill(uio_GPDir *gPDir) {
+ if ((gPDir->flags & uio_GPDir_COMPLETE) &&
+ !(gPDir->flags & uio_GPDir_NOCACHE))
+ return;
+ assert(gPDir->pRoot->extra->ops->fillGPDir != NULL);
+ gPDir->pRoot->extra->ops->fillGPDir(gPDir);
+}
+
+void
+uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot) {
+ if (gPRoot->extra == NULL)
+ return;
+ assert(gPRoot->ops->deleteGPRootExtra != NULL);
+ gPRoot->ops->deleteGPRootExtra(gPRoot->extra);
+}
+
+void
+uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir) {
+ if (gPDir->extra == NULL)
+ return;
+ assert(gPDir->pRoot->extra->ops->deleteGPDirExtra != NULL);
+ gPDir->pRoot->extra->ops->deleteGPDirExtra(gPDir->extra);
+}
+
+void
+uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile) {
+ if (gPFile->extra == NULL)
+ return;
+ assert(gPFile->pRoot->extra->ops->deleteGPFileExtra != NULL);
+ gPFile->pRoot->extra->ops->deleteGPFileExtra(gPFile->extra);
+}
+
+int
+uio_gPDirFlagsFromPRootFlags(int flags) {
+ int newFlags;
+
+ newFlags = 0;
+ if (flags & uio_PRoot_NOCACHE)
+ newFlags |= uio_GPDir_NOCACHE;
+
+ return newFlags;
+}
+
+int
+uio_gPFileFlagsFromPRootFlags(int flags) {
+ int newFlags;
+
+ newFlags = 0;
+ if (flags & uio_PRoot_NOCACHE)
+ newFlags |= uio_GPFile_NOCACHE;
+
+ return newFlags;
+}
+
+// This function is to be called from the physical layer.
+// uio_GPDirHandle is the extra information for an uio_PDirHandle.
+// This is in practice a pointer to the uio_GPDir.
+void
+uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle) {
+ uio_GPDir_unref((uio_GPDir *) gPDirHandle);
+ (void) gPDirHandle;
+}
+
+// This function is to be called from the physical layer.
+// uio_GPFileHandle is the extra information for an uio_PFileHandle.
+// This is in practice a pointer to the uio_GPFile.
+void
+uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle) {
+ uio_GPFile_unref((uio_GPFile *) gPFileHandle);
+ (void) gPFileHandle;
+}
+
+void
+uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry) {
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_GPDir_delete((uio_GPDir *) gPDirEntry);
+ } else {
+ uio_GPFile_delete((uio_GPFile *) gPDirEntry);
+ }
+}
+
+// note: sets ref count to 1
+uio_GPDir *
+uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags) {
+ uio_GPDir *gPDir;
+
+ gPDir = uio_GPDir_alloc();
+ gPDir->ref = 1;
+ gPDir->pRoot = pRoot;
+ gPDir->entries = uio_GPDirEntries_new();
+ gPDir->extra = extra;
+ flags |= uio_gPDirFlagsFromPRootFlags(gPDir->pRoot->flags);
+ if (pRoot->extra->flags & uio_GPRoot_PERSISTENT)
+ flags |= uio_GPDir_PERSISTENT;
+ gPDir->flags = flags | uio_GPDirEntry_TYPE_DIR;
+ return gPDir;
+}
+
+// pre: There are no more references to within the gPDir, and
+// gPDir is the last reference to the gPDir itself.
+void
+uio_GPDir_delete(uio_GPDir *gPDir) {
+#if 0
+ uio_GPDirEntry *entry;
+
+ uio_GPDirEntries_Iterator *iterator;
+
+ iterator = uio_GPDirEntries_getIterator(gPDir->entries);
+ while (!uio_GPDirEntries_iteratorDone(iterator)) {
+ entry = uio_GPDirEntries_iteratorItem(iterator);
+ assert(entry->ref == 0);
+ if (uio_GPDirEntry_isDir(entry)) {
+ uio_GPDir_delete((uio_GPDir *) entry);
+ } else {
+ uio_GPFile_delete((uio_GPFile *) entry);
+ }
+ iterator = uio_GPDirEntries_iteratorNext(iterator);
+ }
+#endif
+
+ assert(gPDir->ref == 0);
+ uio_GPDirEntries_deleteHashTable(gPDir->entries);
+ uio_GPDir_deleteGPDirExtra(gPDir);
+ uio_GPDir_free(gPDir);
+}
+
+static void
+uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir) {
+ uio_GPDirEntry *entry;
+ uio_GPDirEntries_Iterator *iterator;
+
+ iterator = uio_GPDirEntries_getIterator(gPDir->entries);
+ while (!uio_GPDirEntries_iteratorDone(iterator)) {
+ entry = uio_GPDirEntries_iteratorItem(iterator);
+ if (uio_GPDirEntry_isDir(entry)) {
+ uio_GPDir_deepPersistentUnref((uio_GPDir *) entry);
+ } else {
+ uio_GPFile_unref((uio_GPFile *) entry);
+ }
+ iterator = uio_GPDirEntries_iteratorNext(iterator);
+ }
+ uio_GPDirEntries_freeIterator(iterator);
+ if (gPDir->flags & uio_GPDir_PERSISTENT) {
+ uio_GPDir_unref(gPDir);
+ } else {
+ gPDir->flags &= ~uio_GPDir_COMPLETE;
+ }
+}
+
+static inline uio_GPDir *
+uio_GPDir_alloc(void) {
+ uio_GPDir *result = uio_malloc(sizeof (uio_GPDir));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPDir, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_GPDir_free(uio_GPDir *gPDir) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPDir, (void *) gPDir);
+#endif
+ uio_free(gPDir);
+}
+
+// note: sets ref count to 1
+uio_GPFile *
+uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags) {
+ uio_GPFile *gPFile;
+
+ gPFile = uio_GPFile_alloc();
+ gPFile->ref = 1;
+ gPFile->pRoot = pRoot;
+ gPFile->extra = extra;
+ gPFile->flags = flags;
+ return gPFile;
+}
+
+void
+uio_GPFile_delete(uio_GPFile *gPFile) {
+ assert(gPFile->ref == 0);
+ uio_GPFile_deleteGPFileExtra(gPFile);
+ uio_GPFile_free(gPFile);
+}
+
+static inline uio_GPFile *
+uio_GPFile_alloc(void) {
+ uio_GPFile *result = uio_malloc(sizeof (uio_GPFile));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPFile, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_GPFile_free(uio_GPFile *gPFile) {
+ uio_free(gPFile);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPFile, (void *) gPFile);
+#endif
+}
+
+// The ref counter to 'handle' is not incremented.
+uio_PRoot *
+uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags,
+ uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags,
+ uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags) {
+ uio_PRoot *result;
+ uio_GPDir *gPTopDir;
+ uio_GPRoot *gPRoot;
+
+ gPRoot = uio_GPRoot_new(ops, gPRootExtra, gPRootFlags);
+ result = uio_PRoot_new(NULL, handler, handle, gPRoot, pRootFlags);
+
+ gPTopDir = uio_GPDir_new(result, gPDirExtra, gPDirFlags);
+ if (gPRoot->flags & uio_GPRoot_PERSISTENT)
+ uio_GPDir_ref(gPTopDir);
+ result->rootDir = uio_GPDir_makePDirHandle(gPTopDir);
+
+ return result;
+}
+
+// Pre: there are no more references to PRoot or anything inside it.
+int
+uio_GPRoot_umount(uio_PRoot *pRoot) {
+ uio_PDirHandle *topDirHandle;
+ uio_GPDir *topDir;
+
+ topDirHandle = uio_PRoot_getRootDirHandle(pRoot);
+ topDir = topDirHandle->extra;
+ if (pRoot->extra->flags & uio_GPRoot_PERSISTENT)
+ uio_GPDir_deepPersistentUnref(topDir);
+ uio_PDirHandle_unref(topDirHandle);
+ (void) pRoot;
+ return 0;
+}
+
+uio_GPRoot *
+uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra, int flags) {
+ uio_GPRoot *result;
+
+ result = uio_GPRoot_alloc();
+ result->ops = ops;
+ result->extra = extra;
+ result->flags = flags;
+ return result;
+}
+
+void
+uio_GPRoot_delete(uio_GPRoot *gPRoot) {
+ uio_GPRoot_deleteGPRootExtra(gPRoot);
+ uio_GPRoot_free(gPRoot);
+}
+
+static uio_GPRoot *
+uio_GPRoot_alloc(void) {
+ uio_GPRoot *result = uio_malloc(sizeof (uio_GPRoot));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPRoot, (void *) result);
+#endif
+ return result;
+}
+
+static void
+uio_GPRoot_free(uio_GPRoot *gPRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPRoot, (void *) gPRoot);
+#endif
+ uio_free(gPRoot);
+}
+
+// The ref counter to the gPDir is not inremented.
+uio_PDirHandle *
+uio_GPDir_makePDirHandle(uio_GPDir *gPDir) {
+ return uio_PDirHandle_new(gPDir->pRoot, gPDir);
+}
+
+#ifdef DEBUG
+void
+uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry) {
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_GPDir_print(outStream, (uio_GPDir *) gPDirEntry);
+ } else {
+ uio_GPFile_print(outStream, (uio_GPFile *) gPDirEntry);
+ }
+}
+
+void
+uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir) {
+ (void) outStream;
+ (void) gPDir;
+}
+
+void
+uio_GPFile_print(FILE *outStream, uio_GPFile *gPFile) {
+ (void) outStream;
+ (void) gPFile;
+}
+#endif
+
+
diff --git a/src/libs/uio/gphys.h b/src/libs/uio/gphys.h
new file mode 100644
index 0000000..7a9d6be
--- /dev/null
+++ b/src/libs/uio/gphys.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_GPHYS_H_
+#define LIBS_UIO_GPHYS_H_
+
+#include "uioport.h"
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_GPRootExtra;
+typedef void *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+#endif
+
+typedef struct CharHashTable_HashTable uio_GPDirEntries;
+
+#define uio_GPDirEntries_new() \
+ ((uio_GPDirEntries *) CharHashTable_newHashTable(NULL, NULL, NULL, \
+ NULL, NULL, 0, 0.85, 0.9))
+#define uio_GPDirEntries_add(hashTable, name, item) \
+ CharHashTable_add((CharHashTable_HashTable *) hashTable, name, \
+ (void *) item)
+#define uio_GPDirEntries_remove(hashTable, name) \
+ CharHashTable_remove((CharHashTable_HashTable *) hashTable, name)
+#define uio_GPDirEntries_count(hashTable) \
+ CharHashTable_count((CharHashTable_HashTable *) hashTable)
+#define uio_GPDirEntries_find(hashTable, name) \
+ ((uio_GPDirEntry *) CharHashTable_find( \
+ (CharHashTable_HashTable *) hashTable, name))
+#define uio_GPDirEntries_deleteHashTable(hashTable) \
+ CharHashTable_deleteHashTable((CharHashTable_HashTable *) hashTable)
+//#define uio_GPDirEntries_clear(hashTable)
+// CharHashTable_clear((CharHashTable_HashTable *) hashTable)
+#define uio_GPDirEntries_getIterator(hashTable) \
+ ((uio_GPDirEntries_Iterator *) CharHashTable_getIterator( \
+ (const CharHashTable_HashTable *) hashTable))
+#define uio_GPDirEntries_iteratorDone(iterator) \
+ CharHashTable_iteratorDone((const CharHashTable_Iterator *) iterator)
+#define uio_GPDirEntries_iteratorName(iterator) \
+ CharHashTable_iteratorKey((CharHashTable_Iterator *) iterator)
+#define uio_GPDirEntries_iteratorItem(iterator) \
+ ((uio_GPDirEntry *) CharHashTable_iteratorValue( \
+ (CharHashTable_Iterator *) iterator))
+#define uio_GPDirEntries_iteratorNext(iterator) \
+ ((uio_GPDirEntries_Iterator *) CharHashTable_iteratorNext( \
+ (CharHashTable_Iterator *) iterator))
+#define uio_GPDirEntries_freeIterator(iterator) \
+ CharHashTable_freeIterator(iterator)
+
+// 'forward' declarations
+typedef struct uio_GPDirEntry uio_GPDirEntry;
+typedef struct uio_GPDir uio_GPDir;
+typedef struct uio_GPFile uio_GPFile;
+typedef struct uio_GPRoot_Operations uio_GPRoot_Operations;
+typedef struct uio_GPRoot uio_GPRoot;
+
+#include "charhashtable.h"
+typedef CharHashTable_Iterator uio_GPDirEntries_Iterator;
+
+#ifdef uio_INTERNAL_GPHYS
+typedef uio_GPDirEntries_Iterator *uio_NativeEntriesContext;
+#endif
+typedef struct uio_GPRoot *uio_PRootExtra;
+typedef struct uio_GPDir uio_GPDirHandle;
+typedef uio_GPDirHandle *uio_PDirHandleExtra;
+typedef struct uio_GPFile uio_GPFileHandle;
+typedef uio_GPFileHandle *uio_PFileHandleExtra;
+
+
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+#include "iointrn.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+struct uio_GPRoot_Operations {
+ void (*fillGPDir)(uio_GPDir *);
+ void (*deleteGPRootExtra)(uio_GPRootExtra);
+ void (*deleteGPDirExtra)(uio_GPDirExtra);
+ void (*deleteGPFileExtra)(uio_GPFileExtra);
+};
+
+struct uio_GPRoot {
+ int flags;
+#define uio_GPRoot_PERSISTENT 0x4000
+ /* Set if directories in this file system should not be deleted
+ * as long as the file system is mounted. If this flag is not
+ * set, the GPDir structure is only a cache.
+ */
+ uio_GPRoot_Operations *ops;
+ uio_GPRootExtra extra;
+};
+
+#define uio_GPDirEntry_COMMON \
+ int flags; \
+ int ref; \
+ /* Number of times this structure is referenced from the \
+ * outside (so not counting the references from subdirs \
+ * or files when the entry is a directory) \
+ */
+
+#define uio_GPDirEntry_NOCACHE uio_PRoot_NOCACHE
+
+/*
+ * uio_GPDirEntry
+ * super-'class' of uio_GPDir and uio_GPFile
+ */
+struct uio_GPDirEntry {
+ uio_GPDirEntry_COMMON
+ void *extra;
+};
+
+#define uio_GPDirEntry_TYPE_REG 0x0000
+#define uio_GPDirEntry_TYPE_DIR 0x0001
+#define uio_GPDirEntry_TYPEMASK 0x0001
+
+/*
+ * uio_GPDir
+ * Represents a directory in a physical directory structure.
+ * sub-'class' of uio_GPDirEntry
+ */
+struct uio_GPDir {
+ uio_GPDirEntry_COMMON
+# define uio_GPDir_NOCACHE uio_GPDirEntry_NOCACHE
+ /* This directory info will not be cached.
+ * PDIR_COMPLETE is irrelevant in this case */
+# define uio_GPDir_COMPLETE 0x1000
+ /* Set if fillDir should not be called if an entry does not
+ * exist in a directory. Usually set if the entire dir has been
+ * completely read in.
+ */
+# define uio_GPDir_DETACHED 0x2000
+ /* Set if this dir is not linked to from elsewhere in the physical
+ * structure */
+# define uio_GPDir_PERSISTENT 0x4000
+ /* Set if this dir should not be deleted as long as the file
+ * system is mounted. If this flag is not set, the GPDir
+ * structure is only a cache.
+ */
+ uio_GPDirExtra extra;
+ /* extra internal data for some filesystem types */
+ uio_PRoot *pRoot;
+ uio_GPDirEntries *entries;
+};
+
+
+/*
+ * uio_GPFile
+ * Represents a file in a physical directory structure.
+ * sub-'class' of uio_GPDirEntry
+ */
+struct uio_GPFile {
+ uio_GPDirEntry_COMMON
+# define uio_GPFile_NOCACHE uio_GPDirEntry_NOCACHE
+ /* Info on this file will not be cached. */
+ uio_GPFileExtra extra;
+ /* extra internal data for some filesystem types */
+ uio_PRoot *pRoot;
+};
+
+
+static inline uio_bool
+uio_GPDirEntry_isReg(uio_GPDirEntry *gPDirEntry) {
+ return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) ==
+ uio_GPDirEntry_TYPE_REG;
+}
+
+static inline uio_bool
+uio_GPDirEntry_isDir(uio_GPDirEntry *gPDirEntry) {
+ return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) ==
+ uio_GPDirEntry_TYPE_DIR;
+}
+
+
+#ifdef DEBUG
+void uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry);
+void uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir);
+void uio_GPFile_print(FILE *outStream, uio_GPFile *pFile);
+#endif
+
+uio_NativeEntriesContext uio_GPDir_openEntries(uio_PDirHandle *pDirHandle);
+int uio_GPDir_readEntries(uio_NativeEntriesContext *iterator,
+ char *buf, size_t len);
+void uio_GPDir_closeEntries(uio_NativeEntriesContext iterator);
+int uio_GPDir_entryCount(const uio_GPDir *gPDir);
+
+int uio_gPDirFlagsFromPRootFlags(int flags);
+int uio_gPFileFlagsFromPRootFlags(int flags);
+uio_PRoot *uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags,
+ uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags,
+ uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags);
+int uio_GPRoot_umount(uio_PRoot *pRoot);
+
+uio_GPDir *uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName);
+void uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName,
+ uio_GPDir *subDir);
+void uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName,
+ uio_GPFile *file);
+void uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName);
+void uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName);
+void uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag);
+uio_GPDirEntry *uio_GPDir_getGPDirEntry(uio_GPDir *gPDir,
+ const char *name);
+uio_PDirEntryHandle *uio_GPDir_getPDirEntryHandle(
+ const uio_PDirHandle *pDirHandle, const char *name);
+int uio_walkGPPath(uio_GPDir *startGPDir, const char *path,
+ size_t pathLen, uio_GPDir **endGPDir, const char **pathRest);
+uio_PDirHandle *uio_GPDir_makePDirHandle(uio_GPDir *gPDir);
+
+void uio_GPDir_fill(uio_GPDir *gPDir);
+void uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot);
+void uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir);
+void uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile);
+
+void uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle);
+void uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle);
+void uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry);
+uio_GPRoot *uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra,
+ int flags);
+void uio_GPRoot_delete(uio_GPRoot *gPRoot);
+uio_GPDir *uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags);
+void uio_GPDir_delete(uio_GPDir *gPDir);
+uio_GPFile *uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags);
+void uio_GPFile_delete(uio_GPFile *gPFile);
+
+
+static inline void
+uio_GPDirEntry_ref(uio_GPDirEntry *gPDirEntry) {
+#ifdef uio_MEM_DEBUG
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_MemDebug_debugRef(uio_GPDir, (void *) gPDirEntry);
+ } else {
+ uio_MemDebug_debugRef(uio_GPFile, (void *) gPDirEntry);
+ }
+#endif
+ gPDirEntry->ref++;
+}
+
+static inline void
+uio_GPDirEntry_unref(uio_GPDirEntry *gPDirEntry) {
+ assert(gPDirEntry->ref > 0);
+#ifdef uio_MEM_DEBUG
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDirEntry);
+ } else {
+ uio_MemDebug_debugUnref(uio_GPFile, (void *) gPDirEntry);
+ }
+#endif
+ gPDirEntry->ref--;
+ if (gPDirEntry->ref == 0)
+ uio_GPDirEntry_delete(gPDirEntry);
+}
+
+static inline void
+uio_GPDir_ref(uio_GPDir *gPDir) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_GPDir, (void *) gPDir);
+#endif
+ gPDir->ref++;
+}
+
+static inline void
+uio_GPDir_unref(uio_GPDir *gPDir) {
+ assert(gPDir->ref > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDir);
+#endif
+ gPDir->ref--;
+ if (gPDir->ref == 0)
+ uio_GPDir_delete(gPDir);
+}
+
+static inline void
+uio_GPFile_ref(uio_GPFile *gPFile) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_GPFile, (void *) gPFile);
+#endif
+ gPFile->ref++;
+}
+
+static inline void
+uio_GPFile_unref(uio_GPFile *gPFile) {
+ assert(gPFile->ref > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_GPFile, (void *) gPFile);
+#endif
+ gPFile->ref--;
+ if (gPFile->ref == 0)
+ uio_GPFile_delete(gPFile);
+}
+
+
+#endif /* LIBS_UIO_GPHYS_H_ */
+
diff --git a/src/libs/uio/hashtable.c b/src/libs/uio/hashtable.c
new file mode 100644
index 0000000..1f376e1
--- /dev/null
+++ b/src/libs/uio/hashtable.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef HASHTABLE_INTERNAL
+ // If HASHTABLE_INTERNAL is already defined, this file is included
+ // as a template. In this case hashtable.h has already been included.
+# define HASHTABLE_INTERNAL
+# include "hashtable.h"
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#include "mem.h"
+#include "uioport.h"
+
+static void HASHTABLE_(setup)(HASHTABLE_(HashTable) *HashTable,
+ uio_uint32 size);
+static void HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable);
+static inline uio_uint32 nextPower2(uio_uint32 x);
+
+static inline HASHTABLE_(HashTable) *HASHTABLE_(allocHashTable)(void);
+static inline HASHTABLE_(HashEntry) *HASHTABLE_(newHashEntry)(uio_uint32 hash,
+ HASHTABLE_(Key) *key, HASHTABLE_(Value) *value,
+ HASHTABLE_(HashEntry) *next);
+static inline HASHTABLE_(HashEntry) *HASHTABLE_(allocHashEntry)(void);
+static inline void HASHTABLE_(freeHashEntry)(
+ HASHTABLE_(HashEntry) *entry);
+
+// Create a new HashTable.
+HASHTABLE_(HashTable) *
+HASHTABLE_(newHashTable)(
+ HASHTABLE_(HashFunction) hashFunction,
+ HASHTABLE_(EqualFunction) equalFunction,
+ HASHTABLE_(CopyFunction) copyFunction,
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction,
+ HASHTABLE_(FreeValueFunction) freeValueFunction,
+ uio_uint32 initialSize,
+ double minFillQuotient,
+ double maxFillQuotient) {
+ HASHTABLE_(HashTable) *hashTable;
+
+ assert(maxFillQuotient >= minFillQuotient);
+
+ hashTable = HASHTABLE_(allocHashTable)();
+ hashTable->hashFunction = hashFunction;
+ hashTable->equalFunction = equalFunction;
+ hashTable->copyFunction = copyFunction;
+ hashTable->freeKeyFunction = freeKeyFunction;
+ hashTable->freeValueFunction = freeValueFunction;
+
+ hashTable->minFillQuotient = minFillQuotient;
+ hashTable->maxFillQuotient = maxFillQuotient;
+ HASHTABLE_(setup)(hashTable, initialSize);
+
+ return hashTable;
+}
+
+// Add an entry to the HashTable.
+uio_bool
+HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) *entry;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = hashTable->entries[hash & hashTable->hashMask];
+ while (entry != NULL) {
+ if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) {
+ // key is already present
+ return false;
+ }
+ entry = entry->next;
+ }
+
+#ifdef HashTable_PROFILE
+ if (hashTable->entries[hash & hashTable->hashMask] != NULL)
+ hashTable->numCollisions++;
+#endif
+ hashTable->entries[hash & hashTable->hashMask] =
+ HASHTABLE_(newHashEntry)(hash,
+ HASHTABLE_(COPY)(hashTable, key), value,
+ hashTable->entries[hash & hashTable->hashMask]);
+
+ hashTable->numEntries++;
+ if (hashTable->numEntries > hashTable->maxSize)
+ HASHTABLE_(resize)(hashTable);
+
+ return true;
+}
+
+// Remove an entry with a specified Key from the HashTable.
+uio_bool
+HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) **entry, *next;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = &hashTable->entries[hash & hashTable->hashMask];
+ while (1) {
+ if (*entry == NULL)
+ return false;
+ if (HASHTABLE_(EQUAL)(hashTable, key, (*entry)->key)) {
+ // found the key
+ break;
+ }
+ entry = &(*entry)->next;
+ }
+ next = (*entry)->next;
+ HASHTABLE_(FREEKEY)(hashTable, (*entry)->key);
+ HASHTABLE_(FREEVALUE)(hashTable, (*entry)->value);
+ HASHTABLE_(freeHashEntry)(*entry);
+ *entry = next;
+
+ hashTable->numEntries--;
+ if (hashTable->numEntries < hashTable->minSize)
+ HASHTABLE_(resize)(hashTable);
+
+ return true;
+}
+
+// Find the Value stored for some Key.
+HASHTABLE_(Value) *
+HASHTABLE_(find)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) *entry;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = hashTable->entries[hash & hashTable->hashMask];
+ while (entry != NULL) {
+ if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) {
+ // found the key
+ return entry->value;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+// Returns the number of entries in the HashTable.
+uio_uint32
+HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable) {
+ return hashTable->numEntries;
+}
+
+// Auxiliary function to (re)initialise the buckets in the HashTable.
+static void
+HASHTABLE_(setup)(HASHTABLE_(HashTable) *hashTable, uio_uint32 initialSize) {
+ if (initialSize < 4)
+ initialSize = 4;
+ hashTable->size = nextPower2(ceil(((double) initialSize) /
+ hashTable->maxFillQuotient));
+ hashTable->hashMask = hashTable->size - 1;
+ hashTable->minSize = ceil(((double) (hashTable->size >> 1))
+ * hashTable->minFillQuotient);
+ hashTable->maxSize = floor(((double) hashTable->size)
+ * hashTable->maxFillQuotient);
+ hashTable->entries = uio_calloc(hashTable->size,
+ sizeof (HASHTABLE_(HashEntry) *));
+ hashTable->numEntries = 0;
+#ifdef HashTable_PROFILE
+ hashTable->numCollisions = 0;
+#endif
+}
+
+// Resize the buckets in the HashTable.
+static void
+HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable) {
+ HASHTABLE_(HashEntry) **oldEntries;
+ HASHTABLE_(HashEntry) *entry, *next;
+ HASHTABLE_(HashEntry) **newLocation;
+ uio_uint32 oldNumEntries;
+ uio_uint32 i;
+
+ oldNumEntries = hashTable->numEntries;
+ oldEntries = hashTable->entries;
+
+ HASHTABLE_(setup)(hashTable, hashTable->numEntries);
+ hashTable->numEntries = oldNumEntries;
+
+ i = 0;
+ while (oldNumEntries > 0) {
+ entry = oldEntries[i];
+ while (entry != NULL) {
+ next = entry->next;
+ newLocation = &hashTable->entries[entry->hash &
+ hashTable->hashMask];
+#ifdef HashTable_PROFILE
+ if (*newLocation != NULL)
+ hashTable->numCollisions++;
+#endif
+ entry->next = *newLocation;
+ *newLocation = entry;
+ oldNumEntries--;
+ entry = next;
+ }
+ i++;
+ }
+
+ uio_free(oldEntries);
+}
+
+// Adapted from "Hackers Delight"
+// Returns the smallest power of two greater or equal to x.
+static inline uio_uint32
+nextPower2(uio_uint32 x) {
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
+}
+
+// Get an iterator to iterate through all the entries in the HashTable.
+// NB: Iterator should be considered invalid if the HashTable is changed.
+// TODO: change this (make it thread-safe)
+// this can be done by only marking items as deleted when
+// there are outstanding iterators.
+HASHTABLE_(Iterator) *
+HASHTABLE_(getIterator)(const HASHTABLE_(HashTable) *hashTable) {
+ HASHTABLE_(Iterator) *iterator;
+ uio_uint32 i;
+
+ iterator = uio_malloc(sizeof (HASHTABLE_(Iterator)));
+ iterator->hashTable = hashTable;
+
+ // Look for the first used bucket.
+ for (i = 0; i < iterator->hashTable->size; i++) {
+ if (iterator->hashTable->entries[i] != NULL) {
+ // Found a used bucket.
+ iterator->bucketNr = i;
+ iterator->entry = iterator->hashTable->entries[i];
+ return iterator;
+ }
+ }
+
+ // No entries were found.
+ iterator->bucketNr = i;
+ iterator->entry = NULL;
+ return iterator;
+}
+
+// Returns true if and only if there are no more entries in the hash table
+// for the Iterator to find.
+int
+HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator) {
+ return iterator->bucketNr >= iterator->hashTable->size;
+}
+
+// Get the Key of the entry pointed to by an Iterator.
+HASHTABLE_(Key) *
+HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator) {
+ return iterator->entry->key;
+}
+
+// Get the Value of the entry pointed to by an Iterator.
+HASHTABLE_(Value) *
+HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator) {
+ return iterator->entry->value;
+}
+
+// Move the Iterator to the next entry in the HashTable.
+// Should not be called if the iterator is already past the last entry.
+HASHTABLE_(Iterator) *
+HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator) {
+ uio_uint32 i;
+
+ // If there's another entry in this bucket, use that.
+ iterator->entry = iterator->entry->next;
+ if (iterator->entry != NULL)
+ return iterator;
+
+ // Look for the next used bucket.
+ for (i = iterator->bucketNr + 1; i < iterator->hashTable->size; i++) {
+ if (iterator->hashTable->entries[i] != NULL) {
+ // Found another used bucket.
+ iterator->bucketNr = i;
+ iterator->entry = iterator->hashTable->entries[i];
+ return iterator;
+ }
+ }
+
+ // No more entries were found.
+ iterator->bucketNr = i;
+ iterator->entry = NULL;
+ return iterator;
+}
+
+// Free the Iterator.
+void
+HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator) {
+ uio_free(iterator);
+}
+
+// Auxiliary function to allocate a HashTable.
+static inline HASHTABLE_(HashTable) *
+HASHTABLE_(allocHashTable)(void) {
+ return uio_malloc(sizeof (HASHTABLE_(HashTable)));
+}
+
+// Auxiliary function to create a HashEntry.
+static inline HASHTABLE_(HashEntry) *
+HASHTABLE_(newHashEntry)(uio_uint32 hash, HASHTABLE_(Key) *key,
+ HASHTABLE_(Value) *value, HASHTABLE_(HashEntry) *next) {
+ HASHTABLE_(HashEntry) *result;
+
+ result = HASHTABLE_(allocHashEntry)();
+ result->hash = hash;
+ result->key = key;
+ result->value = value;
+ result->next = next;
+ return result;
+}
+
+// Allocate a new HashEntry.
+static inline HASHTABLE_(HashEntry) *
+HASHTABLE_(allocHashEntry)(void) {
+ return uio_malloc(sizeof (HASHTABLE_(HashEntry)));
+}
+
+// Delete the HashTable.
+void
+HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable) {
+ uio_uint32 i;
+ HASHTABLE_(HashEntry) *entry, *next;
+ HASHTABLE_(HashEntry) **bucketPtr;
+
+ i = hashTable->numEntries;
+ bucketPtr = hashTable->entries;
+ while (i > 0) {
+ entry = *bucketPtr;
+ while (entry != NULL) {
+ next = entry->next;
+ HASHTABLE_(FREEKEY)(hashTable, entry->key);
+ HASHTABLE_(FREEVALUE)(hashTable, entry->value);
+ HASHTABLE_(freeHashEntry)(entry);
+ entry = next;
+ i--;
+ }
+ bucketPtr++;
+ }
+ uio_free(hashTable->entries);
+ uio_free(hashTable);
+}
+
+// Auxiliary function to deallocate a HashEntry.
+static inline void
+HASHTABLE_(freeHashEntry)(HASHTABLE_(HashEntry) *entry) {
+ uio_free(entry);
+}
+
diff --git a/src/libs/uio/hashtable.h b/src/libs/uio/hashtable.h
new file mode 100644
index 0000000..eb4437f
--- /dev/null
+++ b/src/libs/uio/hashtable.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The 'already included' check must be done slightly more complicated
+// than usually. This file may be included directly only once,
+// but it may be included my derivative HashTable definitions that use
+// this file as a template more than once.
+#if !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC)
+#if defined(HASHTABLE_)
+# define HASHTABLE_GENERIC
+#endif
+
+#include "types.h"
+#include "uioport.h"
+
+// Define to enable profiling.
+#define HashTable_PROFILE
+
+// You can use inline hash functions for extra speed, by using this file as
+// a template.
+// To do this, make a new .h and .c file. In the .h file, define the macros
+// (and typedefs) from the HASHTABLE_ block below.
+// In the .c file, #define HASHTABLE_INTERNAL, #include the .h file
+// and hashtable.c (in this order), and add the necessary functions.
+// If you do not need to free the Value, you can define HashTable_FREEVALUE
+// as a no-op.
+#ifndef HASHTABLE_
+# define HASHTABLE_(identifier) HashTable ## _ ## identifier
+ typedef void HashTable_Key;
+ typedef void HashTable_Value;
+# define HashTable_HASH(hashTable, hashValue) \
+ (hashTable)->hashFunction(hashValue)
+# define HashTable_EQUAL(hashTable, hashKey1, hashKey2) \
+ (hashTable)->equalFunction(hashKey1, hashKey2)
+# define HashTable_COPY(hashTable, hashKey) \
+ (hashTable)->copyFunction(hashKey)
+# define HashTable_FREEKEY(hashTable, hashKey) \
+ (hashTable)->freeKeyFunction(hashKey)
+# define HashTable_FREEVALUE(hashTable, hashValue) \
+ (hashTable)->freeValueFunction(hashValue)
+#endif
+
+
+
+typedef uio_uint32 (*HASHTABLE_(HashFunction))(const HASHTABLE_(Key) *);
+typedef uio_bool (*HASHTABLE_(EqualFunction))(const HASHTABLE_(Key) *,
+ const HASHTABLE_(Key) *);
+typedef HASHTABLE_(Value) *(*HASHTABLE_(CopyFunction))(
+ const HASHTABLE_(Key) *);
+typedef void (*HASHTABLE_(FreeKeyFunction))(HASHTABLE_(Key) *);
+typedef void (*HASHTABLE_(FreeValueFunction))(HASHTABLE_(Value) *);
+
+typedef struct HASHTABLE_(HashTable) HASHTABLE_(HashTable);
+typedef struct HASHTABLE_(HashEntry) HASHTABLE_(HashEntry);
+typedef struct HASHTABLE_(Iterator) HASHTABLE_(Iterator);
+
+struct HASHTABLE_(HashTable) {
+ HASHTABLE_(HashFunction) hashFunction;
+ // Function creating a uio_uint32 hash of a key.
+ HASHTABLE_(EqualFunction) equalFunction;
+ // Function used to compare two keys.
+ HASHTABLE_(CopyFunction) copyFunction;
+ // Function used to copy a key.
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction;
+ // Function used to free a key.
+ HASHTABLE_(FreeValueFunction) freeValueFunction;
+ // Function used to free a value. Called when an entry is
+ // removed using the remove function, or for entries
+ // still in the HashTable when the HashTable is deleted.
+
+ double minFillQuotient;
+ // How much of half of the hashtable needs to be filled
+ // before resizing to size/2.
+ double maxFillQuotient;
+ // How much of the hashTable needs to be filled before
+ // resizing to size*2.
+ uio_uint32 minSize;
+ // Resize to size/2 when below this size.
+ uio_uint32 maxSize;
+ // Resize to size*2 when above this size.
+ uio_uint32 size;
+ // The number of buckets in the hash table.
+ uio_uint32 hashMask;
+ // Mask to take on a the calculated hash value, to make it
+ // fit into the table.
+
+ HASHTABLE_(HashEntry) **entries;
+ // The actual entries
+
+ uio_uint32 numEntries;
+#ifdef HashTable_PROFILE
+ uio_uint32 numCollisions;
+#endif
+};
+
+struct HASHTABLE_(HashEntry) {
+ uio_uint32 hash;
+ HASHTABLE_(Key) *key;
+ HASHTABLE_(Value) *value;
+ HASHTABLE_(HashEntry) *next;
+};
+
+struct HASHTABLE_(Iterator) {
+ const HASHTABLE_(HashTable) *hashTable;
+ uio_uint32 bucketNr;
+ HASHTABLE_(HashEntry) *entry;
+};
+
+HASHTABLE_(HashTable) *HASHTABLE_(newHashTable)(
+ HASHTABLE_(HashFunction) hashFunction,
+ HASHTABLE_(EqualFunction) equalFunction,
+ HASHTABLE_(CopyFunction) copyFunction,
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction,
+ HASHTABLE_(FreeValueFunction) freeValueFunction,
+ uio_uint32 initialSize,
+ double minFillQuotient, double maxFillQuotient);
+uio_bool HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value);
+uio_bool HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key);
+HASHTABLE_(Value) *HASHTABLE_(find)(
+ HASHTABLE_(HashTable) *hashTable, const HASHTABLE_(Key) *key);
+uio_uint32 HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable);
+void HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable);
+HASHTABLE_(Iterator) *HASHTABLE_(getIterator)(
+ const HASHTABLE_(HashTable) *hashTable);
+int HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Key) *HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Value) *HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Iterator) *HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator);
+void HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator);
+
+#ifndef HASHTABLE_INTERNAL
+# undef HASHTABLE_
+#endif
+
+#endif /* !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC) */
+
+
+
diff --git a/src/libs/uio/io.c b/src/libs/uio/io.c
new file mode 100644
index 0000000..247d1e2
--- /dev/null
+++ b/src/libs/uio/io.c
@@ -0,0 +1,1859 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "iointrn.h"
+#include "ioaux.h"
+#include "mount.h"
+#include "fstypes.h"
+#include "mounttree.h"
+#include "physical.h"
+#include "paths.h"
+#include "mem.h"
+#include "uioutils.h"
+#include "uioport.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#if 0
+static int uio_accessDir(uio_DirHandle *dirHandle, const char *path,
+ int mode);
+#endif
+static int uio_statDir(uio_DirHandle *dirHandle, const char *path,
+ struct stat *statBuf);
+static int uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf);
+
+static void uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[],
+ int numPDirHandles);
+
+static inline uio_PDirHandle *uio_PDirHandle_alloc(void);
+static inline void uio_PDirHandle_free(uio_PDirHandle *pDirHandle);
+static inline uio_PFileHandle *uio_PFileHandle_alloc(void);
+static inline void uio_PFileHandle_free(uio_PFileHandle *pFileHandle);
+
+static uio_DirHandle *uio_DirHandle_new(uio_Repository *repository, char *path,
+ char *rootEnd);
+static inline uio_DirHandle *uio_DirHandle_alloc(void);
+static inline void uio_DirHandle_free(uio_DirHandle *dirHandle);
+
+static inline uio_Handle *uio_Handle_alloc(void);
+static inline void uio_Handle_free(uio_Handle *handle);
+
+static uio_MountHandle *uio_MountHandle_new(uio_Repository *repository,
+ uio_MountInfo *mountInfo);
+static inline void uio_MountHandle_delete(uio_MountHandle *mountHandle);
+static inline uio_MountHandle *uio_MountHandle_alloc(void);
+static inline void uio_MountHandle_free(uio_MountHandle *mountHandle);
+
+
+
+void
+uio_init(void) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_init();
+#endif
+ uio_registerDefaultFileSystems();
+}
+
+void
+uio_unInit(void) {
+ uio_unRegisterDefaultFileSystems();
+#ifdef uio_MEM_DEBUG
+# ifdef DEBUG
+ uio_MemDebug_printPointers(stderr);
+ fflush(stderr);
+# endif
+ uio_MemDebug_unInit();
+#endif
+}
+
+uio_Repository *
+uio_openRepository(int flags) {
+ return uio_Repository_new(flags);
+}
+
+void
+uio_closeRepository(uio_Repository *repository) {
+ uio_unmountAllDirs(repository);
+ uio_Repository_unref(repository);
+}
+
+/*
+ * Function name: uio_mountDir
+ * Description: Grafts a directory from inside a physical fileSystem
+ * into the locical filesystem, at a specified directory.
+ * Arguments: destRep - the repository where the newly mounted dir
+ * is to be grafted.
+ * mountPoint - the path to the directory where the dir
+ * is to be grafted.
+ * fsType - the file system type of physical fileSystem
+ * pointed to by sourcePath.
+ * sourceDir - the directory to which 'sourcePath' is to
+ * be taken relative.
+ * sourcePath - a path relative to sourceDir, which contains
+ * the file/directory to be mounted.
+ * If sourceDir and sourcePath are NULL, the file
+ * system of the operating system will be used.
+ * inPath - the location relative to the root of the newly
+ * mounted fileSystem, pointing to the directory
+ * that is to be grafted.
+ * Note: If fsType is uio_FSTYPE_STDIO, inPath is
+ * relative to the root of the filesystem, NOT to
+ * the current working dir.
+ * autoMount - array of automount options in function
+ * in this mountPoint.
+ * flags - one of uio_MOUNT_TOP, uio_MOUNT_BOTTOM,
+ * uio_MOUNT_BELOW, uio_MOUNT_ABOVE, specifying
+ * the precedence of this mount, OR'ed with
+ * one or more of the following flags:
+ * uio_MOUNT_RDONLY (no writing is allowed)
+ * relative - If 'flags' includes uio_MOUNT_BELOW or
+ * uio_MOUNT_ABOVE, this is the mount handle
+ * where the new mount is relative to.
+ * Otherwise, it should be NULL.
+ * Returns: a handle suitable for uio_unmountDir()
+ * NULL if an error occured. In this case 'errno' is set.
+ */
+uio_MountHandle *
+uio_mountDir(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative) {
+ uio_PRoot *pRoot;
+ uio_Handle *handle;
+ uio_FileSystemHandler *handler;
+ uio_MountInfo *relativeInfo;
+
+ switch (flags & uio_MOUNT_LOCATION_MASK) {
+ case uio_MOUNT_TOP:
+ case uio_MOUNT_BOTTOM:
+ if (relative != NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = NULL;
+ break;
+ case uio_MOUNT_BELOW:
+ case uio_MOUNT_ABOVE:
+ if (relative == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = relative->mountInfo;
+ break;
+ default:
+ abort();
+ }
+
+ if (mountPoint[0] == '/')
+ mountPoint++;
+ if (!validPathName(mountPoint, strlen(mountPoint))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // TODO: check if the filesystem is already mounted, and if so, reuse it.
+ // A RO filedescriptor will need to be replaced though if the
+ // filesystem needs to be remounted RW now.
+ if (sourceDir == NULL) {
+ if (sourcePath != NULL) {
+ // bad: sourceDir is NULL, but sourcePath isn't
+ errno = EINVAL;
+ return NULL;
+ }
+ handle = NULL;
+ } else {
+ if (sourcePath == NULL) {
+ // bad: sourcePath is NULL, but sourceDir isn't
+ errno = EINVAL;
+ return NULL;
+ }
+ handle = uio_open(sourceDir, sourcePath,
+ ((flags & uio_MOUNT_RDONLY) == uio_MOUNT_RDONLY ?
+ O_RDONLY : O_RDWR)
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (handle == NULL) {
+ // errno is set
+ return NULL;
+ }
+ }
+
+ handler = uio_getFileSystemHandler(fsType);
+ if (handler == NULL) {
+ if (handle)
+ uio_close(handle);
+ errno = ENODEV;
+ return NULL;
+ }
+
+ assert(handler->mount != NULL);
+ pRoot = (handler->mount)(handle, flags);
+ if (pRoot == NULL) {
+ int savedErrno;
+
+ savedErrno = errno;
+ if (handle)
+ uio_close(handle);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ if (handle) {
+ // Close this reference to handle.
+ // The physical layer may store the link in pRoot, in which it
+ // will be cleaned up from uio_unmount().
+ uio_close(handle);
+ }
+
+ // The new file system is ready, now we need to find the specified
+ // dir inside it and put it in its place in the mountTree.
+ {
+ uio_PDirHandle *endDirHandle;
+ const char *endInPath;
+ char *dirName;
+ uio_MountInfo *mountInfo;
+ uio_MountTree *mountTree;
+ uio_PDirHandle *pRootHandle;
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ char *unixPath;
+
+ unixPath = dosToUnixPath(inPath);
+ inPath = unixPath;
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+
+ if (inPath[0] == '/')
+ inPath++;
+ pRootHandle = uio_PRoot_getRootDirHandle(pRoot);
+ uio_walkPhysicalPath(pRootHandle, inPath, strlen(inPath),
+ &endDirHandle, &endInPath);
+ if (*endInPath != '\0') {
+ // Path inside the filesystem to mount does not exist.
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ uio_free(unixPath);
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ uio_PDirHandle_unref(endDirHandle);
+ uio_PRoot_unrefMount(pRoot);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ dirName = uio_malloc(endInPath - inPath + 1);
+ memcpy(dirName, inPath, endInPath - inPath);
+ dirName[endInPath - inPath] = '\0';
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ // InPath is a copy with the paths fixed.
+ uio_free(unixPath);
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ mountInfo = uio_MountInfo_new(fsType, NULL, endDirHandle, dirName,
+ autoMount, NULL, flags);
+ uio_repositoryAddMount(destRep, mountInfo,
+ flags & uio_MOUNT_LOCATION_MASK, relativeInfo);
+ mountTree = uio_mountTreeAddMountInfo(destRep, destRep->mountTree,
+ mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK,
+ relativeInfo);
+ // mountTree is the node in destRep->mountTree where mountInfo
+ // leads to.
+ mountInfo->mountTree = mountTree;
+ mountInfo->mountHandle = uio_MountHandle_new(destRep, mountInfo);
+ return mountInfo->mountHandle;
+ }
+}
+
+// Mount a repository directory into same repository at a different location
+// From fossil.
+uio_MountHandle *
+uio_transplantDir(const char *mountPoint, uio_DirHandle *sourceDir, int flags,
+ uio_MountHandle *relative) {
+ uio_MountInfo *relativeInfo;
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+ uio_MountTreeItem **treeItems;
+ int i;
+ uio_MountHandle *handle = NULL;
+
+ if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) {
+ // Only read-only transplants supported atm
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ switch (flags & uio_MOUNT_LOCATION_MASK) {
+ case uio_MOUNT_TOP:
+ case uio_MOUNT_BOTTOM:
+ if (relative != NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = NULL;
+ break;
+ case uio_MOUNT_BELOW:
+ case uio_MOUNT_ABOVE:
+ if (relative == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = relative->mountInfo;
+ break;
+ default:
+ abort();
+ }
+
+ if (mountPoint[0] == '/')
+ mountPoint++;
+ if (!validPathName(mountPoint, strlen(mountPoint))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (uio_getPathPhysicalDirs(sourceDir, "", 0,
+ &pDirHandles, &numPDirHandles, &treeItems) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ // TODO: We only transplant the first read-only physical dir that we find
+ // Maybe transplant all of them? We would then have several
+ // uio_MountHandles to return.
+ for (i = 0; i < numPDirHandles; ++i) {
+ uio_PDirHandle *pDirHandle = pDirHandles[i];
+ uio_MountInfo *oldMountInfo = treeItems[i]->mountInfo;
+ uio_Repository *rep = oldMountInfo->mountHandle->repository;
+ uio_MountInfo *mountInfo;
+ uio_MountTree *mountTree;
+
+ // Only interested in read-only dirs in this incarnation
+ if (!uio_mountInfoIsReadOnly(oldMountInfo))
+ continue;
+
+ mountInfo = uio_MountInfo_new(oldMountInfo->fsID, NULL, pDirHandle,
+ uio_strdup(""), oldMountInfo->autoMount, NULL, flags);
+ // New mount references the same handles
+ uio_PDirHandle_ref(pDirHandle);
+ uio_PRoot_refMount(pDirHandle->pRoot);
+
+ uio_repositoryAddMount(rep, mountInfo,
+ flags & uio_MOUNT_LOCATION_MASK, relativeInfo);
+ mountTree = uio_mountTreeAddMountInfo(rep, rep->mountTree,
+ mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK,
+ relativeInfo);
+ // mountTree is the node in rep->mountTree where mountInfo leads to
+ mountInfo->mountTree = mountTree;
+ mountInfo->mountHandle = uio_MountHandle_new(rep, mountInfo);
+ handle = mountInfo->mountHandle;
+ break;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(treeItems);
+
+ if (handle == NULL)
+ errno = ENOENT;
+
+ return handle;
+}
+
+int
+uio_unmountDir(uio_MountHandle *mountHandle) {
+ uio_PRoot *pRoot;
+
+ pRoot = mountHandle->mountInfo->pDirHandle->pRoot;
+
+ // check if it's in use
+#ifdef DEBUG
+ if (pRoot->mountRef == 1 && pRoot->handleRef > 0) {
+ fprintf(stderr, "Warning: File system to be unmounted still "
+ "has file descriptors open. The file system will not "
+ "be deallocated until these are all closed.\n");
+ }
+#endif
+
+ // TODO: lock (and furtheron unlock) repository
+
+ // remove from mount tree
+ uio_mountTreeRemoveMountInfo(mountHandle->repository,
+ mountHandle->mountInfo->mountTree,
+ mountHandle->mountInfo);
+
+ // remove from mount list.
+ uio_repositoryRemoveMount(mountHandle->repository,
+ mountHandle->mountInfo);
+
+ uio_MountInfo_delete(mountHandle->mountInfo);
+
+ uio_MountHandle_delete(mountHandle);
+ uio_PRoot_unrefMount(pRoot);
+ return 0;
+}
+
+int
+uio_unmountAllDirs(uio_Repository *repository) {
+ int i;
+
+ i = repository->numMounts;
+ while (i--)
+ uio_unmountDir(repository->mounts[i]->mountHandle);
+ return 0;
+}
+
+uio_FileSystemID
+uio_getMountFileSystemType(uio_MountHandle *mountHandle) {
+ return mountHandle->mountInfo->fsID;
+}
+
+int
+uio_close(uio_Handle *handle) {
+ uio_Handle_unref(handle);
+ return 0;
+}
+
+int
+uio_rename(uio_DirHandle *oldDir, const char *oldPath,
+ uio_DirHandle *newDir, const char *newPath) {
+ uio_PDirHandle *oldPReadDir, *newPReadDir, *newPWriteDir;
+ uio_MountInfo *oldReadMountInfo, *newReadMountInfo, *newWriteMountInfo;
+ char *oldName, *newName;
+ int retVal;
+
+ if (uio_getPhysicalAccess(oldDir, oldPath, O_RDONLY, 0,
+ &oldReadMountInfo, &oldPReadDir, NULL,
+ NULL, NULL, NULL, &oldName) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (uio_getPhysicalAccess(newDir, newPath, O_WRONLY | O_CREAT | O_EXCL,
+ uio_GPA_NOWRITE, &newReadMountInfo, &newPReadDir, NULL,
+ &newWriteMountInfo, &newPWriteDir, NULL, &newName) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_free(oldName);
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (oldReadMountInfo != newWriteMountInfo) {
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = EXDEV;
+ return -1;
+ }
+
+ if (uio_mountInfoIsReadOnly(oldReadMountInfo)) {
+ // XXX: Doesn't uio_getPhysicalAccess already handle this?
+ // It doesn't return EROFS though; perhaps it should.
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = EROFS;
+ return -1;
+ }
+
+ if (oldReadMountInfo->pDirHandle->pRoot->handler->rename == NULL) {
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = ENOSYS;
+ return -1;
+ }
+ retVal = (oldReadMountInfo->pDirHandle->pRoot->handler->rename)(
+ oldPReadDir, oldName, newPWriteDir, newName);
+ if (retVal == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ return 0;
+}
+
+int
+uio_access(uio_DirHandle *dir, const char *path, int mode) {
+ (void) dir;
+ (void) path;
+ (void) mode;
+ errno = ENOSYS; // Not implemented.
+ return -1;
+
+#if 0
+ uio_PDirHandle *pReadDir;
+ uio_MountInfo *readMountInfo;
+ char *name;
+ int result;
+
+ if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0,
+ &readMountInfo, &pReadDir, NULL,
+ NULL, NULL, NULL, &name) == -1) {
+ // XXX: I copied this part from uio_stat(). Is this what I need?
+ if (uio_accessDir(dir, path, statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+ }
+
+ if (pReadDir->pRoot->handler->access == NULL) {
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ result = (pReadDir->pRoot->handler->access)(pReadDir, name, mode);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ return result;
+#endif
+}
+
+#if 0
+// auxiliary function to uio_access
+static int
+uio_accessDir(uio_DirHandle *dirHandle, const char *path, int mode) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+
+ if (mode & R_OK)
+ {
+ // Read permission is always granted. Nothing to check here.
+ }
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (mode & F_OK)
+ {
+ // We need to check whether each of the directories is complete
+
+ // WORK
+ }
+
+ if (mode & W_OK) {
+ // If there is any directory where writing is allowed, then
+ // we can write.
+
+ // WORK
+ errno = ENOENT;
+ return -1;
+
+#if 0
+ if (uio_statOneDir(pDirHandles[0], statBuf) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+ // TODO: atm, fstat'ing a dir will show the info for the topmost
+ // dir. Maybe it would make sense of merging the bits. (How?)
+
+#if 0
+ for (i = 1; i < numPDirHandles; i++) {
+ struct stat statOne;
+ uio_PDirHandle *pDirHandle;
+
+ if (statOneDir(pDirHandles[i], &statOne) == -1) {
+ // errno is set
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Merge dirs:
+
+
+ }
+#endif
+#endif
+ }
+
+ if (mode & X_OK) {
+ // XXX: Not implemented.
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ return 0;
+}
+#endif
+
+int
+uio_fstat(uio_Handle *handle, struct stat *statBuf) {
+ if (handle->root->handler->fstat == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->fstat)(handle, statBuf);
+}
+
+int
+uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf) {
+ uio_PDirHandle *pReadDir;
+ uio_MountInfo *readMountInfo;
+ char *name;
+ int result;
+
+ if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0,
+ &readMountInfo, &pReadDir, NULL,
+ NULL, NULL, NULL, &name) == -1) {
+ if (uio_statDir(dir, path, statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+ }
+
+ if (pReadDir->pRoot->handler->stat == NULL) {
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ result = (pReadDir->pRoot->handler->stat)(pReadDir, name, statBuf);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ return result;
+}
+
+// auxiliary function to uio_stat
+static int
+uio_statDir(uio_DirHandle *dirHandle, const char *path,
+ struct stat *statBuf) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (uio_statOneDir(pDirHandles[0], statBuf) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+ // TODO: atm, fstat'ing a dir will show the info for the topmost
+ // dir. Maybe it would make sense of merging the bits. (How?)
+
+#if 0
+ for (i = 1; i < numPDirHandles; i++) {
+ struct stat statOne;
+ uio_PDirHandle *pDirHandle;
+
+ if (statOneDir(pDirHandles[i], &statOne) == -1) {
+ // errno is set
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Merge dirs:
+
+
+ }
+#endif
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ return 0;
+}
+
+static int
+uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf) {
+ if (pDirHandle->pRoot->handler->stat == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (pDirHandle->pRoot->handler->stat)(pDirHandle, ".", statBuf);
+ // sets errno on error
+}
+
+int
+uio_mkdir(uio_DirHandle *dir, const char *path, mode_t mode) {
+ uio_PDirHandle *pReadDir, *pWriteDir;
+ uio_MountInfo *readMountInfo, *writeMountInfo;
+ char *name;
+ uio_PDirHandle *newDirHandle;
+
+ if (uio_getPhysicalAccess(dir, path, O_WRONLY | O_CREAT | O_EXCL, 0,
+ &readMountInfo, &pReadDir, NULL,
+ &writeMountInfo, &pWriteDir, NULL, &name) == -1) {
+ // errno is set
+ if (errno == EISDIR)
+ errno = EEXIST;
+ return -1;
+ }
+ uio_PDirHandle_unref(pReadDir);
+
+ if (pWriteDir->pRoot->handler->mkdir == NULL) {
+ uio_free(name);
+ uio_PDirHandle_unref(pWriteDir);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ newDirHandle = (pWriteDir->pRoot->handler->mkdir)(pWriteDir, name, mode);
+ if (newDirHandle == NULL) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(pWriteDir);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pWriteDir);
+ uio_PDirHandle_unref(newDirHandle);
+ uio_free(name);
+ return 0;
+}
+
+uio_Handle *
+uio_open(uio_DirHandle *dir, const char *path, int flags, mode_t mode) {
+ uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle;
+ uio_MountInfo *readMountInfo, *writeMountInfo;
+ char *name;
+ uio_Handle *handle;
+
+ if (uio_getPhysicalAccess(dir, path, flags, 0,
+ &readMountInfo, &readPDirHandle, NULL,
+ &writeMountInfo, &writePDirHandle, NULL, &name) == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // WritePDirHandle is not filled in.
+ pDirHandle = readPDirHandle;
+ } else if (readPDirHandle == writePDirHandle) {
+ // In general, the dirs can be the same even when the handles are
+ // not the same. But here it works, because uio_getPhysicalAccess
+ // guarantees it.
+ uio_PDirHandle_unref(writePDirHandle);
+ pDirHandle = readPDirHandle;
+ } else {
+ // need to write
+ uio_PDirEntryHandle *entry;
+
+ entry = uio_getPDirEntryHandle(readPDirHandle, name);
+ if (entry != NULL) {
+ // file already exists
+ uio_PDirEntryHandle_unref(entry);
+ if ((flags & O_CREAT) == O_CREAT &&
+ (flags & O_EXCL) == O_EXCL) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = EEXIST;
+ return NULL;
+ }
+ if ((flags & O_TRUNC) == O_TRUNC) {
+ // No use copying the file to the writable dir.
+ // As it doesn't exists there, O_TRUNC needs to be turned off
+ // though.
+ flags &= ~O_TRUNC;
+ } else {
+ // file needs to be copied
+ if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle,
+ name) == -1) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+ } else {
+ // file does not exist
+ if (((flags & O_ACCMODE) == O_RDONLY) ||
+ (flags & O_CREAT) != O_CREAT) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+ uio_PDirHandle_unref(readPDirHandle);
+ pDirHandle = writePDirHandle;
+ }
+
+ handle = (pDirHandle->pRoot->handler->open)(pDirHandle, name, flags, mode);
+ // Also adds a new entry to the physical dir if appropriate.
+ if (handle == NULL) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(pDirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ uio_free(name);
+ uio_PDirHandle_unref(pDirHandle);
+ return handle;
+}
+
+uio_DirHandle *
+uio_openDir(uio_Repository *repository, const char *path, int flags) {
+ uio_DirHandle *dirHandle;
+ const char * const rootStr = "";
+
+ dirHandle = uio_DirHandle_new(repository,
+ unconst(rootStr), unconst(rootStr));
+ // dirHandle->path will be replaced before uio_openDir()
+ // exits()
+ if (uio_verifyPath(dirHandle, path, &dirHandle->path) == -1) {
+ int savedErrno = errno;
+ uio_DirHandle_free(dirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+ // dirHandle->path is no longer equal to 'path' at this point.
+ // TODO: increase ref in repository?
+ dirHandle->rootEnd = dirHandle->path;
+ if (flags & uio_OD_ROOT)
+ dirHandle->rootEnd += strlen(dirHandle->path);
+ return dirHandle;
+}
+
+uio_DirHandle *
+uio_openDirRelative(uio_DirHandle *base, const char *path, int flags) {
+ uio_DirHandle *dirHandle;
+ char *newPath;
+
+ if (uio_verifyPath(base, path, &newPath) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (flags & uio_OD_ROOT) {
+ dirHandle = uio_DirHandle_new(base->repository,
+ newPath, newPath + strlen(newPath));
+ // TODO: increase ref in base->repository?
+ } else {
+ // use the root of the base dir
+ dirHandle = uio_DirHandle_new(base->repository,
+ newPath, newPath + (base->rootEnd - base->path));
+ }
+ return dirHandle;
+}
+
+int
+uio_closeDir(uio_DirHandle *dirHandle) {
+ uio_DirHandle_unref(dirHandle);
+ return 0;
+}
+
+ssize_t
+uio_read(uio_Handle *handle, void *buf, size_t count) {
+ return (handle->root->handler->read)(handle, buf, count);
+}
+
+int
+uio_rmdir(uio_DirHandle *dirHandle, const char *path) {
+ int numPDirHandles;
+ uio_PDirHandle *pDirHandle, **pDirHandles;
+ const char *pathEnd, *name;
+ uio_PDirEntryHandle *entry;
+ uio_MountTreeItem **items;
+ int i;
+ int numDeleted;
+
+ pathEnd = strrchr(path, '/');
+ if (pathEnd == NULL) {
+ pathEnd = path;
+ name = path;
+ } else
+ name = pathEnd + 1;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path,
+ &pDirHandles, &numPDirHandles, &items) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ entry = NULL;
+ // Should be set before a possible goto.
+
+ if (name[0] == '\0') {
+ // path was of the form "foo/bar/" or "/foo/bar/"
+ // These are intentionally not accepted.
+ // I see this as a path and not as a directory identifier.
+ errno = ENOENT;
+ goto err;
+ }
+
+ numDeleted = 0;
+ for (i = 0; i < numPDirHandles; i++) {
+ pDirHandle = pDirHandles[i];
+ entry = uio_getPDirEntryHandle(pDirHandle, name);
+
+ if (entry == NULL)
+ continue;
+
+ if (!uio_PDirEntryHandle_isDir(entry)) {
+ errno = ENOTDIR;
+ goto err;
+ }
+
+ if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) {
+ errno = EROFS;
+ goto err;
+ }
+
+ if (pDirHandle->pRoot->handler->rmdir == NULL) {
+ errno = ENOSYS;
+ goto err;
+ }
+
+ if ((pDirHandle->pRoot->handler->rmdir)(pDirHandle, name) == -1) {
+ // errno is set
+ goto err;
+ }
+ numDeleted++;
+ uio_PDirEntryHandle_unref(entry);
+ }
+ entry = NULL;
+
+ if (numDeleted == 0) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ if (entry != NULL)
+ uio_PDirEntryHandle_unref(entry);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static void
+uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[], int numPDirHandles) {
+ while (numPDirHandles--)
+ uio_PDirHandle_unref(pDirHandles[numPDirHandles]);
+ uio_free(pDirHandles);
+}
+
+int
+uio_lseek(uio_Handle *handle, off_t offset, int whence) {
+ if (handle->root->handler->seek == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->seek)(handle, offset, whence);
+}
+
+ssize_t
+uio_write(uio_Handle *handle, const void *buf, size_t count) {
+ if (handle->root->handler->write == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->write)(handle, buf, count);
+}
+
+int
+uio_unlink(uio_DirHandle *dirHandle, const char *path) {
+ int numPDirHandles;
+ uio_PDirHandle *pDirHandle, **pDirHandles;
+ const char *pathEnd, *name;
+ uio_PDirEntryHandle *entry;
+ uio_MountTreeItem **items;
+ int i;
+ int numDeleted;
+
+ pathEnd = strrchr(path, '/');
+ if (pathEnd == NULL) {
+ pathEnd = path;
+ name = path;
+ } else
+ name = pathEnd + 1;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path,
+ &pDirHandles, &numPDirHandles, &items) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ entry = NULL;
+ // Should be set before a possible goto.
+
+ if (name[0] == '\0') {
+ // path was of the form "foo/bar/" or "/foo/bar/"
+ errno = ENOENT;
+ goto err;
+ }
+
+ numDeleted = 0;
+ for (i = 0; i < numPDirHandles; i++) {
+ pDirHandle = pDirHandles[i];
+ entry = uio_getPDirEntryHandle(pDirHandle, name);
+
+ if (entry == NULL)
+ continue;
+
+ if (uio_PDirEntryHandle_isDir(entry)) {
+ errno = EISDIR;
+ goto err;
+ }
+
+ if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) {
+ errno = EROFS;
+ goto err;
+ }
+
+ if (pDirHandle->pRoot->handler->unlink == NULL) {
+ errno = ENOSYS;
+ goto err;
+ }
+
+ if ((pDirHandle->pRoot->handler->unlink)(pDirHandle, name) == -1) {
+ // errno is set
+ goto err;
+ }
+ numDeleted++;
+ uio_PDirEntryHandle_unref(entry);
+ }
+ entry = NULL;
+
+ if (numDeleted == 0) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ if (entry != NULL)
+ uio_PDirEntryHandle_unref(entry);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+// inPath and *outPath may point to the same location
+int
+uio_getFileLocation(uio_DirHandle *dir, const char *inPath,
+ int flags, uio_MountHandle **mountHandle, char **outPath) {
+ uio_PDirHandle *readPDirHandle, *writePDirHandle;
+ uio_MountInfo *readMountInfo, *writeMountInfo, *mountInfo;
+ char *name;
+ char *readPRootPath, *writePRootPath, *pRootPath;
+
+ if (uio_getPhysicalAccess(dir, inPath, flags, 0,
+ &readMountInfo, &readPDirHandle, &readPRootPath,
+ &writeMountInfo, &writePDirHandle, &writePRootPath,
+ &name) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // TODO: This code is partly the same as the code in uio_open().
+ // probably some code could be put in a seperate function.
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // WritePDirHandle is not filled in.
+ uio_PDirHandle_unref(readPDirHandle);
+ pRootPath = readPRootPath;
+ mountInfo = readMountInfo;
+ } else if (readPDirHandle == writePDirHandle) {
+ // In general, the dirs can be the same even when the handles are
+ // not the same. But here it works, because uio_getPhysicalAccess
+ // guarantees it.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ pRootPath = readPRootPath;
+ mountInfo = readMountInfo;
+ uio_free(writePRootPath);
+ } else {
+ // need to write
+ uio_PDirEntryHandle *entry;
+
+ entry = uio_getPDirEntryHandle(readPDirHandle, name);
+ if (entry != NULL) {
+ // file already exists
+ uio_PDirEntryHandle_unref(entry);
+ if ((flags & O_CREAT) == O_CREAT &&
+ (flags & O_EXCL) == O_EXCL) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = EEXIST;
+ return -1;
+ }
+ if ((flags & O_TRUNC) == O_TRUNC) {
+ // No use copying the file to the writable dir.
+ // As it doesn't exists there, O_TRUNC needs to be turned off
+ // though.
+ flags &= ~O_TRUNC;
+ } else {
+ // file needs to be copied
+ if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle,
+ name) == -1) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = savedErrno;
+ return -1;
+ }
+ }
+ } else {
+ // file does not exist
+ if (((flags & O_ACCMODE) == O_RDONLY) ||
+ (flags & O_CREAT) != O_CREAT) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ pRootPath = writePRootPath;
+ mountInfo = writeMountInfo;
+ uio_free(readPRootPath);
+ }
+
+ uio_free(name);
+
+ *mountHandle = mountInfo->mountHandle;
+ *outPath = pRootPath;
+ return 0;
+}
+
+
+// *** begin dirList stuff *** //
+
+#define uio_DIR_BUFFER_SIZE 2048
+ // Large values will give significantly better speed for large
+ // directories. What is stored in the buffer is file names
+ // plus one pointer per file name, so with an average size of 16
+ // characters per file (including \0), a buffer of size 2048 will
+ // store approximately 100 files.
+ // It should be at least big enough to store one entry (NAME_MAX is
+ // 255 on POSIX systems).
+ // TODO: add a compile-time check for this
+
+typedef struct uio_DirBufferLink {
+ char *buffer;
+ int numEntries;
+ struct uio_DirBufferLink *next;
+} uio_DirBufferLink;
+
+static int strPtrCmp(const char * const *ptr1, const char * const *ptr2);
+static void uio_DirBufferLink_free(uio_DirBufferLink *sdbl);
+static void uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink);
+static uio_DirList *uio_getDirListMulti(uio_PDirHandle **pDirHandles,
+ int numPDirHandles, const char *pattern, match_MatchType matchType);
+static uio_DirList *uio_makeDirList(const char **newNames,
+ const char * const *names, int numNames);
+static uio_DirList *uio_DirList_new(const char **names, int numNames,
+ char *buffer);
+static void uio_collectDirEntries(uio_PDirHandle *pDirHandle,
+ uio_DirBufferLink **linkPtr, int *numEntries);
+static inline uio_DirList *uio_DirList_alloc(void);
+static void uio_filterNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames,
+ match_MatchContext *matchContext);
+
+static uio_EntriesContext *uio_openEntriesPhysical(uio_PDirHandle *dirHandle);
+static int uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf,
+ size_t len);
+static void uio_closeEntriesPhysical(uio_EntriesContext *iterator);
+static uio_EntriesContext *uio_EntriesContext_new(uio_PRoot *pRoot,
+ uio_NativeEntriesContext *native);
+static inline uio_EntriesContext *uio_EntriesContext_alloc(void);
+static inline void uio_EntriesContext_delete(uio_EntriesContext *entriesContext);
+static inline void uio_EntriesContext_free(uio_EntriesContext
+ *entriesContext);
+
+// The caller may modify the elements of the .names field of the result, but
+// .names itself, and the rest of the elements of dirList should be left
+// alone, so that they will be freed by uio_DirList_free().
+uio_DirList *
+uio_getDirList(uio_DirHandle *dirHandle, const char *path, const char *pattern,
+ match_MatchType matchType) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+ uio_DirList *result;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ if (numPDirHandles == 0) {
+ assert(pDirHandles == NULL);
+ // nothing to free
+ return uio_DirList_new(NULL, 0, NULL);
+ }
+
+ result = uio_getDirListMulti(pDirHandles, numPDirHandles, pattern,
+ matchType);
+
+ {
+ int savedErrno;
+ savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ }
+ return result;
+}
+
+// Names and newNames may point to the same location.
+// numNewNames may point to &numNames.
+static void
+uio_filterDoubleNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames) {
+ const char * const *endNames;
+ const char *prevName;
+ int newNum;
+
+ if (numNames == 0) {
+ *numNewNames = 0;
+ return;
+ }
+
+ endNames = names + numNames;
+ prevName = *names;
+ *newNames = *names;
+ newNames++;
+ names++;
+ newNum = 1;
+ while (names < endNames) {
+ if (strcmp(prevName, *names) != 0) {
+ *newNames = *names;
+ newNum++;
+ prevName = *names;
+ newNames++;
+ }
+ names++;
+ }
+ *numNewNames = newNum;
+}
+
+static uio_DirList *
+uio_getDirListMulti(uio_PDirHandle **pDirHandles,
+ int numPDirHandles, const char *pattern, match_MatchType matchType) {
+ int pDirI; // physical dir iterator
+ uio_DirBufferLink **links; // array of bufferLinks for each physical dir
+ uio_DirBufferLink *linkPtr;
+ int *numNames; // number of entries in each physical dir
+ int totalNumNames;
+ const char **bigNameBuffer; // buffer where all names will end up together
+ const char **destPtr;
+ uio_DirList *result;
+ match_Result matchResult;
+ match_MatchContext *matchContext;
+
+ matchResult = match_prepareContext(pattern, &matchContext, matchType);
+ if (matchResult != match_OK) {
+#ifdef DEBUG
+ fprintf(stderr, "Error compiling match function: %s.\n",
+ match_errorString(matchContext, matchResult));
+#endif
+ match_freeContext(matchContext);
+ errno = EIO;
+ // I actually want to signal an internal error.
+ // EIO comes closes
+ return NULL;
+ }
+
+ // first get the directory listings for all seperate relevant dirs.
+ totalNumNames = 0;
+ links = uio_malloc(numPDirHandles * sizeof (uio_DirBufferLink *));
+ numNames = uio_malloc(numPDirHandles * sizeof (int));
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++) {
+ uio_collectDirEntries(pDirHandles[pDirI], &links[pDirI],
+ &numNames[pDirI]);
+ totalNumNames += numNames[pDirI];
+ }
+
+ bigNameBuffer = uio_malloc(totalNumNames * sizeof (uio_DirBufferLink *));
+
+ // Fill the bigNameBuffer with all the names from all the DirBufferLinks
+ // of all the physical dirs.
+ destPtr = bigNameBuffer;
+ totalNumNames = 0;
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++) {
+ for (linkPtr = links[pDirI]; linkPtr != NULL;
+ linkPtr = linkPtr->next) {
+ int numNewNames;
+ uio_filterNames((const char * const *) linkPtr->buffer,
+ linkPtr->numEntries, destPtr,
+ &numNewNames, matchContext);
+ totalNumNames += numNewNames;
+ destPtr += numNewNames;
+ }
+ }
+
+ match_freeContext(matchContext);
+
+ // Sort the bigNameBuffer
+ // Necessary for removing doubles.
+ // Not really necessary if the big list was the result of only one
+ // physical dir, but let's output a sorted list anyhow.
+ qsort((void *) bigNameBuffer, totalNumNames, sizeof (char *),
+ (int (*)(const void *, const void *)) strPtrCmp);
+
+ // remove doubles
+ // (unnecessary if the big list was the result of only one physical dir)
+ if (numPDirHandles > 1) {
+ uio_filterDoubleNames(bigNameBuffer, totalNumNames,
+ bigNameBuffer, &totalNumNames);
+ }
+
+ // resize the bigNameBuffer
+ bigNameBuffer = uio_realloc((void *) bigNameBuffer,
+ totalNumNames * sizeof (char *));
+
+ // put the lot in a DirList, copying the strings themselves
+ result = uio_makeDirList(bigNameBuffer, bigNameBuffer,
+ totalNumNames);
+
+ // free the old junk
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++)
+ uio_DirBufferChain_free(links[pDirI]);
+ uio_free(links);
+ uio_free(numNames);
+
+ return result;
+}
+
+// 'buffer' and 'names' may be the same dir
+// 'names' contains an array of 'numNames' pointers.
+// 'newNames', if non-NULL, will be used as the array of new pointers
+// (to a copy of the strings) in the DirList.
+static uio_DirList *
+uio_makeDirList(const char **newNames, const char * const *names,
+ int numNames) {
+ int i;
+ size_t len, totLen;
+ char *bufPtr;
+ uio_DirList *result;
+
+ if (newNames == NULL)
+ newNames = uio_malloc(numNames * sizeof (char *));
+
+ totLen = 0;
+ for (i = 0; i < numNames; i++)
+ totLen += strlen(names[i]);
+ totLen += numNames;
+ // for the \0's
+
+ result = uio_DirList_new(newNames, numNames, uio_malloc(totLen));
+
+ bufPtr = result->buffer;
+ for (i = 0; i < numNames; i++) {
+ len = strlen(names[i]) + 1;
+ memcpy(bufPtr, names[i], len);
+ newNames[i] = bufPtr;
+ bufPtr += len;
+ }
+ return result;
+}
+
+static void
+uio_collectDirEntries(uio_PDirHandle *pDirHandle, uio_DirBufferLink **linkPtr,
+ int *numEntries) {
+ uio_EntriesContext *entriesContext;
+ uio_DirBufferLink **linkEndPtr; // where to attach the next link
+ int numRead;
+ int totalEntries;
+ char *buffer;
+
+ entriesContext = uio_openEntriesPhysical(pDirHandle);
+ if (entriesContext == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: uio_openEntriesPhysical() failed: %s\n",
+ strerror(errno));
+#endif
+ *linkPtr = NULL;
+ *numEntries = 0;
+ return;
+ }
+ linkEndPtr = linkPtr;
+ totalEntries = 0;
+ while (1) {
+ *linkEndPtr = uio_malloc(sizeof (uio_DirBufferLink));
+ buffer = uio_malloc(uio_DIR_BUFFER_SIZE);
+ (*linkEndPtr)->buffer = buffer;
+ numRead = uio_readEntriesPhysical(entriesContext, buffer,
+ uio_DIR_BUFFER_SIZE);
+ if (numRead == 0) {
+ fprintf(stderr, "Warning: uio_DIR_BUFFER_SIZE is too small to "
+ "hold a certain large entry on its own!\n");
+ uio_DirBufferLink_free(*linkEndPtr);
+ break;
+ }
+ totalEntries += numRead;
+ (*linkEndPtr)->numEntries = numRead;
+ if (((char **) buffer)[numRead - 1] == NULL) {
+ // The entry being NULL means this is the last buffer
+ // Decrement the amount of queries to get the real number.
+ (*linkEndPtr)->numEntries--;
+ totalEntries--;
+ linkEndPtr = &(*linkEndPtr)->next;
+ break;
+ }
+ linkEndPtr = &(*linkEndPtr)->next;
+ }
+ *linkEndPtr = NULL;
+ uio_closeEntriesPhysical(entriesContext);
+ *numEntries = totalEntries;
+}
+
+static void
+uio_filterNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames,
+ match_MatchContext *matchContext) {
+ int newNum;
+ const char * const *namesEnd;
+ match_Result matchResult;
+
+ newNum = 0;
+ namesEnd = names + numNames;
+ while (names < namesEnd) {
+ matchResult = match_matchPattern(matchContext, *names);
+ if (matchResult == match_MATCH) {
+ *newNames = *names;
+ newNames++;
+ newNum++;
+ } else if (matchResult != match_NOMATCH) {
+ fprintf(stderr, "Error trying to match pattern: %s.\n",
+ match_errorString(matchContext, matchResult));
+ }
+ names++;
+ }
+ *numNewNames = newNum;
+}
+
+static int
+strPtrCmp(const char * const *ptr1, const char * const *ptr2) {
+ return strcmp(*ptr1, *ptr2);
+}
+
+static uio_EntriesContext *
+uio_openEntriesPhysical(uio_PDirHandle *dirHandle) {
+ uio_NativeEntriesContext *native;
+ uio_PRoot *pRoot;
+
+ pRoot = dirHandle->pRoot;
+
+ assert(pRoot->handler->openEntries != NULL);
+ native = pRoot->handler->openEntries(dirHandle);
+ if (native == NULL)
+ return NULL;
+ uio_PRoot_refHandle(pRoot);
+ return uio_EntriesContext_new(pRoot, native);
+}
+
+static int
+uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf, size_t len) {
+ assert(iterator->pRoot->handler->readEntries != NULL);
+ return iterator->pRoot->handler->readEntries(&iterator->native, buf, len);
+}
+
+static void
+uio_closeEntriesPhysical(uio_EntriesContext *iterator) {
+ assert(iterator->pRoot->handler->closeEntries != NULL);
+ iterator->pRoot->handler->closeEntries(iterator->native);
+ uio_PRoot_unrefHandle(iterator->pRoot);
+ uio_EntriesContext_delete(iterator);
+}
+
+static uio_EntriesContext *
+uio_EntriesContext_new(uio_PRoot *pRoot, uio_NativeEntriesContext *native) {
+ uio_EntriesContext *result;
+ result = uio_EntriesContext_alloc();
+ result->pRoot = pRoot;
+ result->native = native;
+ return result;
+}
+
+static inline uio_EntriesContext *
+uio_EntriesContext_alloc(void) {
+ return uio_malloc(sizeof (uio_EntriesContext));
+}
+
+static inline void
+uio_EntriesContext_delete(uio_EntriesContext *entriesContext) {
+ uio_EntriesContext_free(entriesContext);
+}
+
+static inline void
+uio_EntriesContext_free(uio_EntriesContext *entriesContext) {
+ uio_free(entriesContext);
+}
+
+static void
+uio_DirBufferLink_free(uio_DirBufferLink *dirBufferLink) {
+ uio_free(dirBufferLink->buffer);
+ uio_free(dirBufferLink);
+}
+
+static void
+uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink) {
+ uio_DirBufferLink *next;
+
+ while (dirBufferLink != NULL) {
+ next = dirBufferLink->next;
+ uio_DirBufferLink_free(dirBufferLink);
+ dirBufferLink = next;
+ }
+}
+
+static uio_DirList *
+uio_DirList_new(const char **names, int numNames, char *buffer) {
+ uio_DirList *result;
+
+ result = uio_DirList_alloc();
+ result->names = names;
+ result->numNames = numNames;
+ result->buffer = buffer;
+ return result;
+}
+
+static uio_DirList *
+uio_DirList_alloc(void) {
+ return uio_malloc(sizeof (uio_DirList));
+}
+
+void
+uio_DirList_free(uio_DirList *dirList) {
+ if (dirList->buffer)
+ uio_free(dirList->buffer);
+ if (dirList->names)
+ uio_free((void *) dirList->names);
+ uio_free(dirList);
+}
+
+// *** end DirList stuff *** //
+
+
+// *** PDirEntryHandle stuff *** //
+
+uio_PDirEntryHandle *
+uio_getPDirEntryHandle(const uio_PDirHandle *dirHandle,
+ const char *name) {
+ assert(dirHandle->pRoot->handler != NULL);
+ return dirHandle->pRoot->handler->getPDirEntryHandle(dirHandle, name);
+}
+
+void
+uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle) {
+ if (pDirHandle->extra == NULL)
+ return;
+ assert(pDirHandle->pRoot->handler->deletePDirHandleExtra != NULL);
+ pDirHandle->pRoot->handler->deletePDirHandleExtra(pDirHandle->extra);
+}
+
+void
+uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle) {
+ if (pFileHandle->extra == NULL)
+ return;
+ assert(pFileHandle->pRoot->handler->deletePFileHandleExtra != NULL);
+ pFileHandle->pRoot->handler->deletePFileHandleExtra(pFileHandle->extra);
+}
+
+uio_PDirHandle *
+uio_PDirHandle_new(uio_PRoot *pRoot, uio_PDirHandleExtra extra) {
+ uio_PDirHandle *result;
+
+ result = uio_PDirHandle_alloc();
+ result->flags = uio_PDirEntryHandle_TYPE_DIR;
+ result->ref = 1;
+ result->pRoot = pRoot;
+ result->extra = extra;
+ return result;
+}
+
+void
+uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle) {
+ if (uio_PDirEntryHandle_isDir(pDirEntryHandle)) {
+ uio_PDirHandle_delete((uio_PDirHandle *) pDirEntryHandle);
+ } else {
+ uio_PFileHandle_delete((uio_PFileHandle *) pDirEntryHandle);
+ }
+}
+
+void
+uio_PDirHandle_delete(uio_PDirHandle *pDirHandle) {
+ uio_PDirHandle_deletePDirHandleExtra(pDirHandle);
+ uio_PDirHandle_free(pDirHandle);
+}
+
+static inline uio_PDirHandle *
+uio_PDirHandle_alloc(void) {
+ uio_PDirHandle *result = uio_malloc(sizeof (uio_PDirHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PDirHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PDirHandle_free(uio_PDirHandle *pDirHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PDirHandle, (void *) pDirHandle);
+#endif
+ uio_free(pDirHandle);
+}
+
+uio_PFileHandle *
+uio_PFileHandle_new(uio_PRoot *pRoot, uio_PFileHandleExtra extra) {
+ uio_PFileHandle *result;
+
+ result = uio_PFileHandle_alloc();
+ result->flags = uio_PDirEntryHandle_TYPE_REG;
+ result->ref = 1;
+ result->pRoot = pRoot;
+ result->extra = extra;
+ return result;
+}
+
+void
+uio_PFileHandle_delete(uio_PFileHandle *pFileHandle) {
+ uio_PFileHandle_deletePFileHandleExtra(pFileHandle);
+ uio_PFileHandle_free(pFileHandle);
+}
+
+static inline uio_PFileHandle *
+uio_PFileHandle_alloc(void) {
+ uio_PFileHandle *result = uio_malloc(sizeof (uio_PFileHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PFileHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PFileHandle_free(uio_PFileHandle *pFileHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PFileHandle, (void *) pFileHandle);
+#endif
+ uio_free(pFileHandle);
+}
+
+// *** PDirEntryHandle stuff *** //
+
+
+// ref count set to 1
+uio_Handle *
+uio_Handle_new(uio_PRoot *root, uio_NativeHandle native, int openFlags) {
+ uio_Handle *handle;
+
+ handle = uio_Handle_alloc();
+ handle->ref = 1;
+ uio_PRoot_refHandle(root);
+ handle->root = root;
+ handle->native = native;
+ handle->openFlags = openFlags;
+ return handle;
+}
+
+void
+uio_Handle_delete(uio_Handle *handle) {
+ (handle->root->handler->close)(handle);
+ uio_PRoot_unrefHandle(handle->root);
+ uio_Handle_free(handle);
+}
+
+static inline uio_Handle *
+uio_Handle_alloc(void) {
+ uio_Handle *result = uio_malloc(sizeof (uio_Handle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Handle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_Handle_free(uio_Handle *handle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Handle, (void *) handle);
+#endif
+ uio_free(handle);
+}
+
+// ref count set to 1
+static uio_DirHandle *
+uio_DirHandle_new(uio_Repository *repository, char *path, char *rootEnd) {
+ uio_DirHandle *result;
+
+ result = uio_DirHandle_alloc();
+ result->ref = 1;
+ result->repository = repository;
+ result->path = path;
+ result->rootEnd = rootEnd;
+ return result;
+}
+
+void
+uio_DirHandle_delete(uio_DirHandle *dirHandle) {
+ uio_free(dirHandle->path);
+ uio_DirHandle_free(dirHandle);
+}
+
+static inline uio_DirHandle *
+uio_DirHandle_alloc(void) {
+ uio_DirHandle *result = uio_malloc(sizeof (uio_DirHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_DirHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_DirHandle_free(uio_DirHandle *dirHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_DirHandle, (void *) dirHandle);
+#endif
+ uio_free(dirHandle);
+}
+
+void
+uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out) {
+ fprintf(out, "[");
+ fwrite(dirHandle->path, dirHandle->path - dirHandle->rootEnd, 1, out);
+ fprintf(out, "]%s", dirHandle->rootEnd);
+}
+
+static uio_MountHandle *
+uio_MountHandle_new(uio_Repository *repository, uio_MountInfo *mountInfo) {
+ uio_MountHandle *result;
+
+ result = uio_MountHandle_alloc();
+ result->repository = repository;
+ result->mountInfo = mountInfo;
+ return result;
+}
+
+static inline void
+uio_MountHandle_delete(uio_MountHandle *mountHandle) {
+ uio_MountHandle_free(mountHandle);
+}
+
+static inline uio_MountHandle *
+uio_MountHandle_alloc(void) {
+ uio_MountHandle *result = uio_malloc(sizeof (uio_MountHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountHandle_free(uio_MountHandle *mountHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountHandle, (void *) mountHandle);
+#endif
+ uio_free(mountHandle);
+}
+
+
+
diff --git a/src/libs/uio/io.h b/src/libs/uio/io.h
new file mode 100644
index 0000000..813a2c3
--- /dev/null
+++ b/src/libs/uio/io.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IO_H_
+#define LIBS_UIO_IO_H_
+
+#include <assert.h>
+#include <sys/stat.h>
+
+typedef struct uio_Handle uio_Handle;
+typedef struct uio_DirHandle uio_DirHandle;
+typedef struct uio_DirList uio_DirList;
+typedef struct uio_MountHandle uio_MountHandle;
+
+typedef enum {
+ uio_MOUNT_BOTTOM = (0 << 2),
+ uio_MOUNT_TOP = (1 << 2),
+ uio_MOUNT_BELOW = (2 << 2),
+ uio_MOUNT_ABOVE = (3 << 2)
+} uio_MountLocation;
+
+#include "match.h"
+#include "fstypes.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "uiostream.h"
+#include "getint.h"
+#include "debug.h"
+
+struct uio_AutoMount {
+ const char *pattern;
+ match_MatchType matchType;
+ uio_FileSystemID fileSystemID;
+ int mountFlags;
+// uio_AutoMount **autoMount;
+ // automount rules to apply to file systems automounted
+ // because of this automount rule.
+};
+
+#ifndef uio_INTERNAL
+struct uio_DirList {
+ const char **names;
+ int numNames;
+ // The rest of the fields are not visible from the outside
+};
+#endif
+
+
+// Initialise the resource system
+void uio_init(void);
+
+// Uninitialise the resource system
+void uio_unInit(void);
+
+// Open a repository.
+uio_Repository *uio_openRepository(int flags);
+
+// Close a repository opened by uio_openRepository().
+void uio_closeRepository(uio_Repository *repository);
+
+// Mount a directory into a repository
+uio_MountHandle *uio_mountDir(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative);
+
+// Mount a repository directory into same repository at a different
+// location.
+// From fossil.
+uio_MountHandle *uio_transplantDir(const char *mountPoint,
+ uio_DirHandle *sourceDir, int flags, uio_MountHandle *relative);
+
+// Unmount a previously mounted dir.
+int uio_unmountDir(uio_MountHandle *mountHandle);
+
+// Unmount all previously mounted dirs.
+int uio_unmountAllDirs(uio_Repository *repository);
+
+// Get the filesystem identifier for a mounted directory.
+uio_FileSystemID uio_getMountFileSystemType(uio_MountHandle *mountHandle);
+
+// Open a file
+uio_Handle *uio_open(uio_DirHandle *dir, const char *file, int flags,
+ mode_t mode);
+
+// Close a file descriptor for a file opened by uio_open
+int uio_close(uio_Handle *handle);
+
+// Rename or move a file or directory.
+int uio_rename(uio_DirHandle *oldDir, const char *oldPath,
+ uio_DirHandle *newDir, const char *newPath);
+
+// Test permissions on a file or directory.
+int uio_access(uio_DirHandle *dir, const char *path, int mode);
+
+// Fstat a file descriptor
+int uio_fstat(uio_Handle *handle, struct stat *statBuf);
+
+int uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf);
+
+int uio_mkdir(uio_DirHandle *dir, const char *name, mode_t mode);
+
+ssize_t uio_read(uio_Handle *handle, void *buf, size_t count);
+
+int uio_rmdir(uio_DirHandle *dirHandle, const char *path);
+
+int uio_lseek(uio_Handle *handle, off_t offset, int whence);
+
+ssize_t uio_write(uio_Handle *handle, const void *buf, size_t count);
+
+int uio_unlink(uio_DirHandle *dirHandle, const char *path);
+
+int uio_getFileLocation(uio_DirHandle *dir, const char *inPath,
+ int flags, uio_MountHandle **mountHandle, char **outPath);
+
+// Get a directory handle.
+uio_DirHandle *uio_openDir(uio_Repository *repository, const char *path,
+ int flags);
+#define uio_OD_ROOT 1
+
+// Get a directory handle using a path relative to another handle.
+uio_DirHandle *uio_openDirRelative(uio_DirHandle *base, const char *path,
+ int flags);
+
+// Release a directory handle
+int uio_closeDir(uio_DirHandle *dirHandle);
+
+uio_DirList *uio_getDirList(uio_DirHandle *dirHandle, const char *path,
+ const char *pattern, match_MatchType matchType);
+void uio_DirList_free(uio_DirList *dirList);
+
+// For debugging purposes
+void uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out);
+
+
+#ifdef DEBUG
+# define uio_DEBUG
+#endif
+
+#endif /* LIBS_UIO_IO_H_ */
+
diff --git a/src/libs/uio/ioaux.c b/src/libs/uio/ioaux.c
new file mode 100644
index 0000000..3dc0880
--- /dev/null
+++ b/src/libs/uio/ioaux.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// auxiliary functions for use by io.c
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include "ioaux.h"
+#include "iointrn.h"
+#include "uioport.h"
+#include "paths.h"
+
+static int copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf);
+
+/**
+ * Follow a path starting from a specified physical dir for as long as
+ * possible.
+ *
+ * @param[in] startPDirHandle The physical dir to start from.
+ * @param[in] path The path to follow, relative to
+ * 'startPDirHandle'.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] endPDirHandle The physical dir where you end up after
+ * following 'path' for as long as possible. Unmodified if an error
+ * occurs.
+ * @param[out] pathRest '*pathRest' will point into 'path' to the
+ * start the part that was not matched. Unmodified if an error occurs.
+ *
+ * @retval 0 if the complete path was matched
+ * @retval ENOENT if some component (the next one in '*pathRest') didn't
+ * exist.
+ * @retval ENODIR if a component (the next one in '*pathRest') did exist,
+ * but wasn't a dir.
+ *
+ * @note It is allowed to have 'endPDirHandle' point to pDirHandle, but
+ * care should be taken to keep a reference to the original so its
+ * reference counter can be decremented.
+ * @note It is allowed to have 'pathRest' point to 'path'.
+ */
+int
+uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle **endPDirHandle,
+ const char **pathRest) {
+ const char *pathEnd;
+ const char *partStart, *partEnd;
+ char *tempBuf;
+ uio_PDirHandle *pDirHandle;
+ uio_PDirEntryHandle *entry;
+ int retVal;
+
+ uio_PDirHandle_ref(startPDirHandle);
+ pDirHandle = startPDirHandle;
+ tempBuf = uio_malloc(strlen(path) + 1);
+ // XXX: Use a dynamically allocated array when moving to C99.
+ pathEnd = path + pathLen;
+ getFirstPathComponent(path, pathEnd, &partStart, &partEnd);
+ for (;;) {
+ if (partStart == pathEnd) {
+ retVal = 0;
+ break;
+ }
+ memcpy(tempBuf, partStart, partEnd - partStart);
+ tempBuf[partEnd - partStart] = '\0';
+
+ entry = uio_getPDirEntryHandle(pDirHandle, tempBuf);
+ if (entry == NULL) {
+ retVal = ENOENT;
+ break;
+ }
+ if (!uio_PDirEntryHandle_isDir(entry)) {
+ uio_PDirEntryHandle_unref(entry);
+ retVal = ENOTDIR;
+ break;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = (uio_PDirHandle *) entry;
+ getNextPathComponent(pathEnd, &partStart, &partEnd);
+ }
+
+ uio_free(tempBuf);
+ *pathRest = partStart;
+ *endPDirHandle = pDirHandle;
+ return retVal;
+}
+
+/**
+ * Create a directory inside a physical directory. All non-existant
+ * parent directories will be created as well.
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is relative
+ * @param[in] path The path to the directory to create, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[in] mode The access mode for the newly created directories.
+ *
+ * @returns the new (physical) directory, or NULL if an error occurs, in
+ * which case #errno will be set.
+ */
+uio_PDirHandle *
+uio_makePath(uio_PDirHandle *pDirHandle, const char *path, size_t pathLen,
+ mode_t mode) {
+ const char *rest, *start, *end;
+ uio_PDirHandle *(*mkdirFun)(uio_PDirHandle *, const char *, mode_t);
+ char *buf;
+ const char *pathEnd;
+ uio_PDirHandle *newPDirHandle;
+
+ mkdirFun = pDirHandle->pRoot->handler->mkdir;
+ if (mkdirFun == NULL) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ pathEnd = path + pathLen;
+
+ buf = uio_malloc(pathLen + 1);
+ // worst case length
+ // XXX: Use a dynamically allocated array when moving to C99.
+
+ uio_walkPhysicalPath(pDirHandle, path, pathLen, &pDirHandle, &rest);
+ // The reference to the original pDirHandle is still kept
+ // by the calling function; uio_PDirHandle_unref should not
+ // be called for it.
+ // Rest now points into 'path' to the part from where no physical
+ // dir exists.
+
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+
+ newPDirHandle = mkdirFun(pDirHandle, buf, mode);
+ if (newPDirHandle == NULL) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pDirHandle);
+ errno = savedErrno;
+ uio_free(buf);
+ return NULL;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = newPDirHandle;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ uio_free(buf);
+ return pDirHandle;
+}
+
+
+/**
+ * Copy a file from one physical directory to another.
+ * The copy will have the same file permissions as the original.
+ *
+ * @param[in] fromDir The physical directory where the file to copy is
+ * located.
+ * @param[in] fromName The name of the file to copy.
+ * @param[in] toDir The physical directory where to put the copy.
+ * @param[in] toName The name to use for the copy.
+ *
+ * @note It is up to the caller to make any relevant permissions checks.
+ *
+ * @note This function will fail if a file with the name in 'toName' already
+ * exists, leaving the original file intact. If an error occurs during
+ * copying, an attempt is made to remove the file that was to be the
+ * copy.
+ */
+int
+uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName,
+ uio_PDirHandle *toDir, const char *toName) {
+ uio_FileSystemHandler *fromHandler, *toHandler;
+ uio_Handle *fromHandle;
+ uio_Handle *toHandle;
+#define BUFSIZE 0x10000
+ struct stat statBuf;
+ char *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ fromHandler = fromDir->pRoot->handler;
+ toHandler = toDir->pRoot->handler;
+ if (toHandler->write == NULL || fromHandler->fstat == NULL ||
+ toHandler->unlink == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ fromHandle = (fromHandler->open)(fromDir, fromName, O_RDONLY, 0);
+ if (fromHandle == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ if ((fromHandler->fstat)(fromHandle, &statBuf) == -1)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ toHandle = (toHandler->open)(toDir, toName, O_WRONLY | O_CREAT | O_EXCL,
+ statBuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (toHandle == NULL)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ buf = uio_malloc(BUFSIZE);
+ // not allocated on the stack, as this function may be called
+ // from a thread with little stack space.
+ while (1) {
+ numInBuf = (fromHandler->read)(fromHandle, buf, BUFSIZE);
+ if (numInBuf == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do {
+ numWritten = (toHandler->write)(toHandle, bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ uio_free(buf);
+ (toHandler->close)(toHandle);
+ (fromHandler->close)(fromHandle);
+ return 0;
+}
+
+/*
+ * Closes fromHandle if it's not -1.
+ * Closes fromHandle if it's not -1.
+ * Removes 'toName' from 'toDir' if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1, setting errno to 'error'.
+ */
+static int
+copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf) {
+#ifdef DEBUG
+ fprintf(stderr, "Error while copying: %s\n", strerror(error));
+#endif
+
+ if (fromHandle != NULL)
+ (fromHandler->close)(fromHandle);
+
+ if (toHandle != NULL)
+ (toHandler->close)(toHandle);
+
+ if (toName != NULL)
+ (toHandler->unlink)(toDir, toName);
+
+ if (buf != NULL)
+ uio_free(buf);
+
+ errno = error;
+ return -1;
+}
+
+/*
+ * Description: find PDirHandle and MountInfo structures for reading and
+ * writing for a path from a DirHandle.
+ * This can be used for opening a file and creating directories.
+ * Arguments: dirHandle - the directory to which the path is relative.
+ * path - the path to the component that is to be accessed.
+ * The last component does not have to exist.
+ * flags - used to specify what kind of access is requested
+ * Either O_RDONLY, O_RDWR, O_WRONLY. They may be
+ * OR'd with other values accepted by open(). These
+ * are ignored.
+ * XXX: this is no longer true.
+ * TODO: update this doc, and check uio_open() and
+ * perhaps others (uio_mkdir()) for unnecessary
+ * checks on O_CREAT and O_EXCL.
+ * extraFlags - either 0 or uio_GPA_NOWRITE
+ * When 0, the path will be created if it doesn't
+ * exist in the writing location, but does exist
+ * in the reading location. With uio_GPA_NOWRITE, it
+ * won't be created, and -1 will be returned and errno
+ * will be set to ENOENT.
+ * mountInfoReadPtr - pointer to location where the pointer
+ * to the MountInfo structure for the reading location
+ * should be stored.
+ * readPDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for reading should be stored.
+ * readPRootPath - pointer to the location where the pointer
+ * to the physical path to the reading location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * mountInfoWritePtr - pointer to location where the pointer
+ * to the MountInfo structure for the writing location
+ * should be stored.
+ * writePDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for writing should be stored.
+ * NULL if O_RDONLY was specified.
+ * If this is the same dir as the one refered
+ * to by readPDirHandlePtr, the handles will be the
+ * same too.
+ * writePRootPath - pointer to the location where the pointer
+ * to the physical path to the writing location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * restPtr - pointer to the location where a newly created
+ * string with as contents the last component of 'path'
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * Returns: 0 - success
+ * -1 - failure (errno set)
+ * NB: This is the function that would most benefit from
+ * the introduction of LDirs.
+ * This is also the most messy function. It could
+ * use some more comments, or a cleanup (if possible).
+ */
+int
+uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path,
+ int flags, int extraFlags,
+ uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr,
+ char **readPRootPathPtr,
+ uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr,
+ char **writePRootPathPtr,
+ char **restPtr) {
+ char *fullPath; // path from dirHandle with 'path' added
+ const char *pRootPath; // path from the pRoot of a physical tree
+ const char *readPRootPath, *writePRootPath;
+ const char *rest, *readRest;
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ uio_MountTreeItem *readItem, *writeItem;
+ uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle;
+ int retVal;
+ uio_bool entryExists;
+ // Set if the entry pointed to by path exists (including
+ // the last component of the path)
+
+ // 'path' is relative to dirHandle.
+ // Fill 'fullPath' with the absolute path.
+ if (uio_resolvePath(dirHandle, path, strlen(path), &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // Walk the tree of mount points along 'fullPath'.
+ // 'tree' will be the part of the tree where we end up when we can go
+ // no further. tree->pLocs are all the mounts relevant there.
+ // 'rest' will point within 'fullPath' to what is left, after we can
+ // walk the tree of mountpoints no further.
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ readItem = NULL;
+ readPDirHandle = NULL;
+ readPRootPath = NULL;
+ writeItem = NULL;
+ writePDirHandle = NULL;
+ writePRootPath = NULL;
+ readRest = NULL;
+ // Satisfy compiler.
+ entryExists = false;
+ // try all the MountInfo structures in effect for this MountTree
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ // rest points inside fullPath
+ if (retVal == 0) {
+ // Even the last component appeared to be a dir.
+ // As the last component did exist, we don't go on.
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EISDIR;
+ return -1;
+ }
+ // check if this MountTreeItem is suitable for writing
+ if (writeItem == NULL && !uio_mountInfoIsReadOnly(item->mountInfo))
+ writeItem = item;
+ if (strchr(rest, '/') == NULL) {
+ // There's only one dir component that was not matched.
+ uio_PDirEntryHandle *entry;
+
+ // This MountInfo will do for reading, if the file from the last
+ // component is present in this dir.
+ entry = uio_getPDirEntryHandle(pDirHandle, rest);
+ if (entry != NULL) {
+ // 'rest' exists, and it is not a dir, as otherwise
+ // uio_getPDirEntryHandle wouldn't have stopped where it did.
+ uio_PDirEntryHandle_unref(entry);
+ readItem = item;
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ entryExists = true;
+ break;
+ } else {
+ // 'rest' doesn't exist
+ // We're only interested in this dir if we want to write too.
+ if (((flags & O_ACCMODE) != O_RDONLY) && readItem == NULL) {
+ // Keep the first one.
+ readItem = item;
+ assert(readPDirHandle == NULL);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ continue;
+ }
+ }
+ } else {
+ // There is more than one dir component that was not matched.
+ // If the first component of the non-matched part is a file,
+ // stop here and don't check lower dirs.
+ if (retVal == ENOTDIR) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ } // for
+ // readItem is set to the first readItem for which the path completely
+ // exists (including the final component). If there's no such readItem,
+ // it is set to the first path for which the path exists, but without
+ // the final component (entryExists is false in this case). If there's
+ // no such path either, it's set to NULL.
+
+ if (readItem == NULL) {
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // write access is not needed
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ // Don't touch mountInfoWritePtr and writePDirHandlePtr.
+ // they'd be NULL.
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ } else {
+ if (entryExists) {
+ if ((flags & O_CREAT) && (flags & O_EXCL)) {
+ // An entry should be created, but it already exists and
+ // it may not be overwritten.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = EEXIST;
+ return -1;
+ }
+ } else {
+ // Though the path to the entry existed (readPDirHandle is
+ // set to it), the entry itself doesn't, so we can't use it
+ // unless we intend to create it.
+ if (flags & O_CREAT) {
+ // The entry does not exist, but we can create it.
+ // Handled below.
+ } else {
+ // O_CREAT was not specified, so we cannot create
+ // this entry.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ }
+ }
+ if (writeItem == readItem) {
+ // The read directory is usable as write directory too.
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ // writeItem == readItem
+ uio_PDirHandle_ref(readPDirHandle);
+ *writePDirHandlePtr = readPDirHandle;
+ // No copy&paste error, the read PDirHandle is the write
+ // pDirHandle too.
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ }
+ if (writeItem == NULL) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EPERM;
+ // readItem is not NULL, so ENOENT would not be correct here.
+ return -1;
+ }
+
+ // Left is the case where the write location is different from the
+ // read location.
+
+ pRootPath = uio_mountTreeItemRestPath(writeItem, tree->lastComp,
+ fullPath);
+
+ rest = strrchr(pRootPath, '/');
+ // rest points inside fullPath
+ if (rest == NULL) {
+ rest = pRootPath;
+ uio_PDirHandle_ref(writeItem->mountInfo->pDirHandle);
+ writePDirHandle = writeItem->mountInfo->pDirHandle;
+ } else {
+ // There exists no path for a write dir, so it will have to be created.
+ // writeMountInfo indicates the physical tree where it should end up.
+
+ if (extraFlags & uio_GPA_NOWRITE) {
+ // The caller has specified that the path should not be created.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ writePDirHandle = uio_makePath(writeItem->mountInfo->pDirHandle,
+ pRootPath, rest - pRootPath, 0777);
+ if (writePDirHandle == NULL) {
+ int savedErrno;
+ if (errno == ENOSYS) {
+ // mkdir not supported. We want to report that we failed
+ // because of an error in the underlying layer.
+ // EIO sounds like the best choice.
+ errno = EIO;
+ }
+ savedErrno = errno;
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = savedErrno;
+ return -1;
+ }
+ rest++; // skip the '/'
+ }
+
+ if (!entryExists) {
+ // The path to the read dir exists, but the entry itself doesn't.
+ // After we created the write dir, the same thing holds for
+ // the write dir. As it occurs in an earlier MountItem, we'll use
+ // the writeItem (and writePDirHandle) for reading too.
+ readItem = writeItem;
+ uio_PDirHandle_ref(writePDirHandle);
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = writePDirHandle;
+ }
+
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ *writePDirHandlePtr = writePDirHandle;
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(rest);
+ uio_free(fullPath);
+ return 0;
+}
+
+
+/**
+ * Get handles to the (existing) physical dirs that are effective in a
+ * path 'path' relative from 'dirHandle'
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is
+ * relative.
+ * @param[in] path The path to get the physical dirs for, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] resPDirHandles *resPDirHandles is set to the handles to the
+ * (existing) physical dirs that are effective in 'path' (relative to
+ * pDirHandle), or NULL if there are none.
+ * @param[out] resNumPDirHandles The number of PDirHandles found.
+ * @param[out] resItems If 'resItems' != NULL, *resItems is set to the
+ * MountTreeItems belonging to $pDirHandles, or NULL if none were found.
+ *
+ * @retval 0 if everything went ok.
+ * @retval -1 if an error occurred; #errno is set.
+ */
+int
+uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle ***resPDirHandles,
+ int *resNumPDirHandles, uio_MountTreeItem ***resItems) {
+ uio_PDirHandle **pDirHandles;
+ char *fullPath; // path from dirHandle with 'path' added
+ uio_MountTree *tree;
+ const char *rest;
+ uio_MountTreeItem *item, **items;
+ const char *pRootPath; // path from the pRoot of a physical tree
+ int numPDirHandles;
+ int pDirI; // PDirHandle iterator
+
+ // Determine the absolute path from 'path', which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, pathLen, &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ // fill pDirHandles with all the PDirHandles for the path
+ numPDirHandles = uio_mountTreeCountPLocs(tree);
+ pDirHandles = uio_malloc(numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL) {
+ items = uio_malloc(numPDirHandles * sizeof (uio_MountTreeItem *));
+ } else {
+ items = NULL; // satisfy compiler
+ }
+ pDirI = 0;
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ switch (uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest)) {
+ case 0:
+ // complete path was matched
+ pDirHandles[pDirI] = pDirHandle;
+ if (resItems != NULL)
+ items[pDirI] = item;
+ pDirI++;
+ continue;
+ case ENOENT:
+ // some component couldn't be matched
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ case ENOTDIR:
+ // next component was not a dir
+ // Don't look further at other mount Items.
+ uio_PDirHandle_unref(pDirHandle);
+ break;
+ default:
+ assert(false);
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ }
+ break;
+ }
+ numPDirHandles = pDirI;
+
+ uio_free(fullPath);
+
+ *resPDirHandles = uio_realloc(pDirHandles,
+ numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL)
+ *resItems = uio_realloc(items,
+ numPDirHandles * sizeof (uio_MountTreeItem *));
+ *resNumPDirHandles = numPDirHandles;
+
+ return 0;
+}
+
+// returns 0 if the path is valid and exists
+// returns -1 if the path is not valid or does not exist.
+// in this case errno will be set to:
+// ENOENT if some component didn't exist
+// ENOTDIR is some component exists, but is not a dir
+// something else (like EPATHTOOLONG) for internal errors
+// On success, 'resolvedPath' will be set to the absolute path as returned by
+// uio_resolvePath.
+int
+uio_verifyPath(uio_DirHandle *dirHandle, const char *path,
+ char **resolvedPath) {
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ const char *rest;
+ int retVal;
+
+ // TODO: "////", "/somedir//", and "//somedir" are accepted as valid
+
+ // Determine the absolute path from 'path' which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, strlen(path), resolvedPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, *resolvedPath,
+ &tree, &rest);
+
+ if (rest[0] == '\0') {
+ // Complete match. Even if there are no pLocs in effect here
+ // (which can only happen in case the mount Tree is empty and
+ // we're viewing '/').
+ return 0;
+ }
+
+ // Try all the MountInfo structures in effect for this MountTree.
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ const char *pRootPath;
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp,
+ *resolvedPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ uio_PDirHandle_unref(pDirHandle);
+ switch (retVal) {
+ case 0:
+ // Complete match. We're done.
+ return 0;
+ case ENOTDIR:
+ // A component is matched, but not as a dir. Failed.
+ uio_free(*resolvedPath);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ // No match; try the next pLoc.
+ continue;
+ default:
+ // Unknown error. Let's bail out just to be safe.
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Unknown error from "
+ "uio_walkPhysicalPath: %s\n", strerror(retVal));
+#endif
+ uio_free(*resolvedPath);
+ errno = retVal;
+ return -1;
+ }
+ }
+
+ // No match, exit with ENOENT.
+ uio_free(*resolvedPath);
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * Determine the absolute path given a path relative to a given directory.
+ *
+ * @param[in] dirHandle The directory to which 'path' is relative.
+ * @param[in] path The path, relative to 'dirHandle', to make
+ * absolute.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] destPath Filled with a newly allocated string containing
+ * the sought absolute path. It will not contain a '/' as the first
+ * or last character. It should be freed with uio_free().
+ * Unmodified if an error occurs.
+ *
+ * @returns the length of '*destPath', or -1 if an error occurs, in which
+ * case #errno will be set.
+ */
+ssize_t
+uio_resolvePath(uio_DirHandle *dirHandle, const char *path, size_t pathLen,
+ char **destPath) {
+ size_t len;
+ const char *pathEnd, *start, *end;
+ int numUp; // number of ".." dirs still need to be matched.
+ char *buffer;
+ char *endBufPtr;
+ uio_bool absolute;
+
+ absolute = path[0] == '/';
+ pathEnd = path + pathLen;
+
+ // Pass 1: count the amount of space needed
+ len = 0;
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+
+ // The part from 'dirHandle->path' to 'dirHandle->rootEnd' is
+ // always copied (for a valid path). The rest we'll have to count.
+ // (Note the 'rootEnd' in the initialiser of the for loop.)
+ len += (dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+ }
+ if (numUp > 0) {
+ // too many ".."
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (len == 0) {
+ *destPath = uio_malloc(1);
+ (*destPath)[0] = '\0';
+ return 0;
+ }
+
+ // len--; // we don't want a '/' at the start
+ // len++; // for the terminating '\0'
+ buffer = uio_malloc(len);
+
+ // Pass 2: fill the buffer
+ endBufPtr = buffer + len - 1;
+ *endBufPtr = '\0';
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ *destPath = buffer;
+ return len;
+ }
+ }
+ // copy the part from dirHandle->path to dirHandle->rootEnd
+ endBufPtr -= (dirHandle->rootEnd - dirHandle->path);
+ memcpy(endBufPtr, dirHandle->path, dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ // copy (some of) the components from dirHandle->rootEnd on.
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ break;
+ }
+ }
+ }
+
+ *destPath = buffer;
+ return len;
+}
+
+
diff --git a/src/libs/uio/ioaux.h b/src/libs/uio/ioaux.h
new file mode 100644
index 0000000..41c7e39
--- /dev/null
+++ b/src/libs/uio/ioaux.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IOAUX_H_
+#define LIBS_UIO_IOAUX_H_
+
+#include "iointrn.h"
+#include "physical.h"
+#include "uioport.h"
+
+int uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle **endPDirHandle,
+ const char **pathRest);
+uio_PDirHandle *uio_makePath(uio_PDirHandle *pDirHandle, const char *path,
+ size_t pathLen, mode_t mode);
+int uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName,
+ uio_PDirHandle *toDir, const char *toName);
+int uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path,
+ int flags, int extraFlags,
+ uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr,
+ char **readPRootPathPtr,
+ uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr,
+ char **writePRootPathPtr,
+ char **restPtr);
+#define uio_GPA_NOWRITE 1
+int uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle ***resPDirHandles,
+ int *resNumPDirHandles, uio_MountTreeItem ***resItems);
+int uio_verifyPath(uio_DirHandle *dirHandle, const char *path,
+ char **resolvedPath);
+ssize_t uio_resolvePath(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, char **destPath);
+
+
+#endif /* LIBS_UIO_IOAUX_H_ */
+
diff --git a/src/libs/uio/iointrn.h b/src/libs/uio/iointrn.h
new file mode 100644
index 0000000..522ce18
--- /dev/null
+++ b/src/libs/uio/iointrn.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IOINTRN_H_
+#define LIBS_UIO_IOINTRN_H_
+
+#define uio_INTERNAL
+
+typedef struct uio_PDirEntryHandle uio_PDirEntryHandle;
+typedef struct uio_PDirHandle uio_PDirHandle;
+typedef struct uio_PFileHandle uio_PFileHandle;
+typedef struct uio_EntriesContext uio_EntriesContext;
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_PDirHandleExtra;
+typedef void *uio_PFileHandleExtra;
+#endif
+
+#include "io.h"
+#include "uioport.h"
+#include "physical.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "match.h"
+#include "mem.h"
+
+struct uio_Handle {
+ int ref;
+ struct uio_PRoot *root;
+ uio_NativeHandle native;
+ int openFlags;
+ // need to know whether the handle is a RO or RW handle.
+};
+
+struct uio_DirHandle {
+ int ref;
+ struct uio_Repository *repository;
+ char *path;
+ // does not contain any '.' or '..'; does not start or end
+ // with a /
+ char *rootEnd;
+ // points to the end of the part of path that is considered
+ // the root dir. (you can't use '..' to get above the root dir)
+};
+
+struct uio_MountHandle {
+ struct uio_Repository *repository;
+ struct uio_MountInfo *mountInfo;
+};
+
+struct uio_DirList {
+ const char **names;
+ int numNames;
+ char *buffer;
+};
+
+
+#define uio_PHandle_COMMON \
+ int flags; \
+ int ref; \
+ uio_PRoot *pRoot;
+
+#define uio_PDirEntryHandle_TYPE_REG 0x0000
+#define uio_PDirEntryHandle_TYPE_DIR 0x0001
+#define uio_PDirEntryHandle_TYPEMASK 0x0001
+
+struct uio_PDirEntryHandle {
+ uio_PHandle_COMMON
+};
+
+struct uio_PDirHandle {
+ uio_PHandle_COMMON
+ uio_PDirHandleExtra extra;
+};
+
+struct uio_PFileHandle {
+ uio_PHandle_COMMON
+ uio_PFileHandleExtra extra;
+};
+
+struct uio_EntriesContext {
+ uio_PRoot *pRoot;
+ uio_NativeEntriesContext native;
+};
+
+
+uio_Handle *uio_Handle_new(uio_PRoot *root, uio_NativeHandle native,
+ int openFlags);
+void uio_Handle_delete(uio_Handle *handle);
+void uio_DirHandle_delete(uio_DirHandle *dirHandle);
+
+uio_PDirEntryHandle *uio_getPDirEntryHandle(
+ const uio_PDirHandle *dirHandle, const char *name);
+void uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle);
+void uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle);
+
+uio_PDirHandle *uio_PDirHandle_new(uio_PRoot *pRoot,
+ uio_PDirHandleExtra extra);
+uio_PFileHandle *uio_PFileHandle_new(uio_PRoot *pRoot,
+ uio_PFileHandleExtra extra);
+void uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle);
+void uio_PDirHandle_delete(uio_PDirHandle *pDirHandle);
+void uio_PFileHandle_delete(uio_PFileHandle *pFileHandle);
+
+
+static inline void
+uio_Handle_ref(uio_Handle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_Handle_unref(uio_Handle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_Handle_delete(handle);
+}
+
+static inline void
+uio_DirHandle_ref(uio_DirHandle *dirHandle) {
+ dirHandle->ref++;
+}
+
+static inline void
+uio_DirHandle_unref(uio_DirHandle *dirHandle) {
+ assert(dirHandle->ref > 0);
+ dirHandle->ref--;
+ if (dirHandle->ref == 0)
+ uio_DirHandle_delete(dirHandle);
+}
+
+static inline uio_bool
+uio_PDirEntryHandle_isDir(const uio_PDirEntryHandle *handle) {
+ return (handle->flags & uio_PDirEntryHandle_TYPEMASK) ==
+ uio_PDirEntryHandle_TYPE_DIR;
+}
+
+static inline void
+uio_PDirEntryHandle_ref(uio_PDirEntryHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PDirEntryHandle_unref(uio_PDirEntryHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PDirEntryHandle_delete(handle);
+}
+
+static inline void
+uio_PDirHandle_ref(uio_PDirHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PDirHandle_unref(uio_PDirHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PDirHandle_delete(handle);
+}
+
+static inline void
+uio_PFileHandle_ref(uio_PFileHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PFileHandle_unref(uio_PFileHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PFileHandle_delete(handle);
+}
+
+
+#endif /* LIBS_UIO_IOINTRN_H_ */
+
+
diff --git a/src/libs/uio/match.c b/src/libs/uio/match.c
new file mode 100644
index 0000000..68e6897
--- /dev/null
+++ b/src/libs/uio/match.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <string.h>
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+
+#define match_INTERNAL
+
+#include "match.h"
+#include "mem.h"
+#include "uioport.h"
+
+#ifdef HAVE_GLOB
+# include <fnmatch.h>
+#endif
+
+
+static inline match_MatchContext *match_allocMatchContext(void);
+static inline void match_freeMatchContext(match_MatchContext *context);
+
+static inline match_LiteralContext *match_newLiteralContext(char *pattern);
+static inline match_LiteralContext *match_allocLiteralContext(void);
+static inline void match_freeLiteralContext(match_LiteralContext *context);
+static inline match_PrefixContext *match_newPrefixContext(char *pattern);
+static inline match_PrefixContext *match_allocPrefixContext(void);
+static inline void match_freePrefixContext(match_PrefixContext *context);
+static inline match_SuffixContext *match_newSuffixContext(
+ char *pattern, size_t len);
+static inline match_SuffixContext *match_allocSuffixContext(void);
+static inline void match_freeSuffixContext(match_SuffixContext *context);
+static inline match_SubStringContext *match_newSubStringContext(
+ char *pattern);
+static inline match_SubStringContext *match_allocSubStringContext(void);
+static inline void match_freeSubStringContext(match_SubStringContext *context);
+#ifdef HAVE_GLOB
+static inline match_GlobContext *match_newGlobContext(char *pattern);
+static inline match_GlobContext *match_allocGlobContext(void);
+static inline void match_freeGlobContext(match_GlobContext *context);
+#endif /* HAVE_GLOB */
+#ifdef HAVE_REGEX
+static inline match_RegexContext *match_newRegexContext(void);
+static inline match_RegexContext *match_allocRegexContext(void);
+static inline void match_freeRegexContext(match_RegexContext *context);
+#endif /* HAVE_REGEX */
+
+
+// *** General part ***
+
+static inline match_MatchContext *
+match_allocMatchContext(void) {
+ return uio_malloc(sizeof (match_MatchContext));
+}
+
+static inline void
+match_freeMatchContext(match_MatchContext *context) {
+ uio_free(context);
+}
+
+// NB: Even if this function fails, *contextPtr contains a context
+// which needs to be freed.
+match_Result
+match_prepareContext(const char *pattern, match_MatchContext **contextPtr,
+ match_MatchType type) {
+ match_Result result;
+
+ *contextPtr = match_allocMatchContext();
+ (*contextPtr)->type = type;
+ switch (type) {
+ case match_MATCH_LITERAL:
+ result = match_prepareLiteral(pattern,
+ &(*contextPtr)->u.literal);
+ break;
+ case match_MATCH_PREFIX:
+ result = match_preparePrefix(pattern, &(*contextPtr)->u.prefix);
+ break;
+ case match_MATCH_SUFFIX:
+ result = match_prepareSuffix(pattern, &(*contextPtr)->u.suffix);
+ break;
+ case match_MATCH_SUBSTRING:
+ result = match_prepareSubString(pattern,
+ &(*contextPtr)->u.subString);
+ break;
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ result = match_prepareGlob(pattern, &(*contextPtr)->u.glob);
+ break;
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ result = match_prepareRegex(pattern, &(*contextPtr)->u.regex);
+ break;
+#endif
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "match_prepareContext called with unsupported "
+ "type %d matching.\n", type);
+#endif
+ return match_ENOSYS;
+ }
+ return result;
+}
+
+match_Result
+match_matchPattern(match_MatchContext *context, const char *string) {
+ switch (context->type) {
+ case match_MATCH_LITERAL:
+ return match_matchLiteral(context->u.literal, string);
+ case match_MATCH_PREFIX:
+ return match_matchPrefix(context->u.prefix, string);
+ case match_MATCH_SUFFIX:
+ return match_matchSuffix(context->u.suffix, string);
+ case match_MATCH_SUBSTRING:
+ return match_matchSubString(context->u.subString, string);
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ return match_matchGlob(context->u.glob, string);
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ return match_matchRegex(context->u.regex, string);
+#endif
+ default:
+ abort();
+ }
+}
+
+// context may be NULL
+const char *
+match_errorString(match_MatchContext *context, match_Result result) {
+ switch (result) {
+ case match_OK:
+ case match_MATCH:
+// case match_NOMATCH: // same value as match_OK
+ return "Success";
+ case match_ENOSYS:
+ return "Not implemented";
+ case match_ENOTINIT:
+ return "Uninitialised use";
+ case match_ECUSTOM:
+ // Depends on match type. Printed below.
+ break;
+ default:
+ return "Unknown error";
+ }
+
+ if (context == NULL)
+ return "Unknown match-type specific error.";
+ // We can't be any more specific if no 'context' is supplied.
+
+ switch (context->type) {
+#if 0
+ case match_MATCH_LITERAL:
+ return match_errorStringLiteral(context->u.literal, result);
+ case match_MATCH_PREFIX:
+ return match_errorStringPrefix(context->u.prefix, result);
+ case match_MATCH_SUFFIX:
+ return match_errorStringSuffix(context->u.suffix, result);
+ case match_MATCH_SUBSTRING:
+ return match_errorStringSubString(context->u.subString, result);
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ return match_errorStringGlob(context->u.glob, result);
+#endif
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ return match_errorStringRegex(context->u.regex, result);
+#endif
+ default:
+ abort();
+ }
+}
+
+void
+match_freeContext(match_MatchContext *context) {
+ switch (context->type) {
+ case match_MATCH_LITERAL:
+ match_freeLiteral(context->u.literal);
+ break;
+ case match_MATCH_PREFIX:
+ match_freePrefix(context->u.prefix);
+ break;
+ case match_MATCH_SUFFIX:
+ match_freeSuffix(context->u.suffix);
+ break;
+ case match_MATCH_SUBSTRING:
+ match_freeSubString(context->u.subString);
+ break;
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ match_freeGlob(context->u.glob);
+ break;
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ match_freeRegex(context->u.regex);
+ break;
+#endif
+ default:
+ abort();
+ }
+ match_freeMatchContext(context);
+}
+
+match_Result
+match_matchPatternOnce(const char *pattern, match_MatchType type,
+ const char *string) {
+ match_MatchContext *context;
+ match_Result result;
+
+ result = match_prepareContext(pattern, &context, type);
+ if (result != match_OK)
+ goto out;
+
+ result = match_matchPattern(context, string);
+
+out:
+ match_freeContext(context);
+ return result;
+}
+
+
+// *** Literal part ***
+
+match_Result
+match_prepareLiteral(const char *pattern,
+ match_LiteralContext **contextPtr) {
+ *contextPtr = match_newLiteralContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchLiteral(match_LiteralContext *context, const char *string) {
+ return (strcmp(context->pattern, string) == 0) ?
+ match_MATCH : match_NOMATCH;
+}
+
+void
+match_freeLiteral(match_LiteralContext *context) {
+ uio_free(context->pattern);
+ match_freeLiteralContext(context);
+}
+
+static inline match_LiteralContext *
+match_newLiteralContext(char *pattern) {
+ match_LiteralContext *result;
+
+ result = match_allocLiteralContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_LiteralContext *
+match_allocLiteralContext(void) {
+ return uio_malloc(sizeof (match_LiteralContext));
+}
+
+static inline void
+match_freeLiteralContext(match_LiteralContext *context) {
+ uio_free(context);
+}
+
+
+// *** Prefix part ***
+
+match_Result
+match_preparePrefix(const char *pattern,
+ match_PrefixContext **contextPtr) {
+ *contextPtr = match_newPrefixContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchPrefix(match_PrefixContext *context, const char *string) {
+ char *patPtr;
+
+ patPtr = context->pattern;
+ while (1) {
+ if (*patPtr == '\0') {
+ // prefix has completely matched
+ return match_MATCH;
+ }
+ if (*string == '\0') {
+ // no more string left, and still prefix to match
+ return match_NOMATCH;
+ }
+ if (*patPtr != *string)
+ return match_NOMATCH;
+ patPtr++;
+ string++;
+ }
+}
+
+void
+match_freePrefix(match_PrefixContext *context) {
+ uio_free(context->pattern);
+ match_freePrefixContext(context);
+}
+
+static inline match_PrefixContext *
+match_newPrefixContext(char *pattern) {
+ match_PrefixContext *result;
+
+ result = match_allocPrefixContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_PrefixContext *
+match_allocPrefixContext(void) {
+ return uio_malloc(sizeof (match_PrefixContext));
+}
+
+static inline void
+match_freePrefixContext(match_PrefixContext *context) {
+ uio_free(context);
+}
+
+
+// *** Suffix part ***
+
+match_Result
+match_prepareSuffix(const char *pattern,
+ match_SuffixContext **contextPtr) {
+ *contextPtr = match_newSuffixContext(
+ uio_strdup(pattern), strlen(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchSuffix(match_SuffixContext *context, const char *string) {
+ size_t stringLen;
+
+ stringLen = strlen(string);
+ if (stringLen < context->len) {
+ // Supplied suffix is larger than string
+ return match_NOMATCH;
+ }
+
+ return memcmp(string + stringLen - context->len, context->pattern,
+ context->len) == 0 ? match_MATCH : match_NOMATCH;
+}
+
+void
+match_freeSuffix(match_SuffixContext *context) {
+ uio_free(context->pattern);
+ match_freeSuffixContext(context);
+}
+
+static inline match_SuffixContext *
+match_newSuffixContext(char *pattern, size_t len) {
+ match_SuffixContext *result;
+
+ result = match_allocSuffixContext();
+ result->pattern = pattern;
+ result->len = len;
+ return result;
+}
+
+static inline match_SuffixContext *
+match_allocSuffixContext(void) {
+ return uio_malloc(sizeof (match_SuffixContext));
+}
+
+static inline void
+match_freeSuffixContext(match_SuffixContext *context) {
+ uio_free(context);
+}
+
+
+// *** SubString part ***
+
+match_Result
+match_prepareSubString(const char *pattern,
+ match_SubStringContext **contextPtr) {
+ *contextPtr = match_newSubStringContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchSubString(match_SubStringContext *context, const char *string) {
+ return strstr(string, context->pattern) != NULL;
+}
+
+void
+match_freeSubString(match_SubStringContext *context) {
+ uio_free(context->pattern);
+ match_freeSubStringContext(context);
+}
+
+static inline match_SubStringContext *
+match_newSubStringContext(char *pattern) {
+ match_SubStringContext *result;
+
+ result = match_allocSubStringContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_SubStringContext *
+match_allocSubStringContext(void) {
+ return uio_malloc(sizeof (match_SubStringContext));
+}
+
+static inline void
+match_freeSubStringContext(match_SubStringContext *context) {
+ uio_free(context);
+}
+
+
+// *** Glob part ***
+
+#ifdef HAVE_GLOB
+match_Result
+match_prepareGlob(const char *pattern, match_GlobContext **contextPtr) {
+ *contextPtr = match_newGlobContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchGlob(match_GlobContext *context, const char *string) {
+ int retval;
+
+ retval = fnmatch(context->pattern, string, 0);
+ switch (retval) {
+ case 0:
+ return match_MATCH;
+ case FNM_NOMATCH:
+ return match_NOMATCH;
+#if 0
+ case FNM_NOSYS:
+ return match_ENOSYS;
+#endif
+ default:
+ return match_EUNKNOWN;
+ }
+}
+
+void
+match_freeGlob(match_GlobContext *context) {
+ uio_free(context->pattern);
+ match_freeGlobContext(context);
+}
+
+static inline match_GlobContext *
+match_newGlobContext(char *pattern) {
+ match_GlobContext *result;
+
+ result = match_allocGlobContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_GlobContext *
+match_allocGlobContext(void) {
+ return uio_malloc(sizeof (match_GlobContext));
+}
+
+static inline void
+match_freeGlobContext(match_GlobContext *context) {
+ uio_free(context);
+}
+#endif /* HAVE_GLOB */
+
+
+// *** Regex part ***
+
+#ifdef HAVE_REGEX
+match_Result
+match_prepareRegex(const char *pattern, match_RegexContext **contextPtr) {
+ *contextPtr = match_newRegexContext();
+ (*contextPtr)->errorCode = regcomp(&(*contextPtr)->native, pattern,
+ REG_EXTENDED | REG_NOSUB);
+ if ((*contextPtr)->errorCode == 0) {
+ (*contextPtr)->flags = match_REGEX_INITIALISED;
+ return match_OK;
+ }
+ return match_ECUSTOM;
+}
+
+match_Result
+match_matchRegex(match_RegexContext *context, const char *string) {
+ int retval;
+
+ if ((context->flags & match_REGEX_INITIALISED) !=
+ match_REGEX_INITIALISED) {
+ return match_ENOTINIT;
+ }
+ if (context->errorString) {
+ uio_free(context->errorString);
+ context->errorString = NULL;
+ }
+ retval = regexec(&context->native, string, 0, NULL, 0);
+ switch (retval) {
+ case 0:
+ return match_MATCH;
+ case REG_NOMATCH:
+ return match_NOMATCH;
+ default:
+ context->errorCode = retval;
+ return match_ECUSTOM;
+ }
+}
+
+const char *
+match_errorStringRegex(match_RegexContext *context, int errorCode) {
+ size_t errorStringLength;
+
+ if (context->errorString != NULL)
+ uio_free(context->errorString);
+
+ errorStringLength = regerror(context->errorCode, &context->native,
+ NULL, 0);
+ context->errorString = uio_malloc(errorStringLength);
+ regerror(context->errorCode, &context->native,
+ context->errorString, errorStringLength);
+
+ (void) errorCode;
+ return context->errorString;
+}
+
+void
+match_freeRegex(match_RegexContext *context) {
+ regfree(&context->native);
+ if (context->errorString)
+ uio_free(context->errorString);
+ match_freeRegexContext(context);
+}
+
+static inline match_RegexContext *
+match_newRegexContext(void) {
+ match_RegexContext *result;
+ result = match_allocRegexContext();
+ result->errorString = NULL;
+ result->errorCode = 0;
+ result->flags = 0;
+ return result;
+}
+
+static inline match_RegexContext *
+match_allocRegexContext(void) {
+ return uio_malloc(sizeof (match_RegexContext));
+}
+
+static inline void
+match_freeRegexContext(match_RegexContext *context) {
+ uio_free(context);
+}
+#endif /* HAVE_REGEX */
+
diff --git a/src/libs/uio/match.h b/src/libs/uio/match.h
new file mode 100644
index 0000000..0557e7d
--- /dev/null
+++ b/src/libs/uio/match.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MATCH_H_
+#define LIBS_UIO_MATCH_H_
+
+typedef struct match_MatchContext match_MatchContext;
+
+#include <sys/types.h>
+
+// TODO: make this into a configurable option
+//#define HAVE_GLOB
+#define HAVE_REGEX
+
+
+typedef enum {
+ match_MATCH_LITERAL = 0,
+ match_MATCH_PREFIX,
+ match_MATCH_SUFFIX,
+ match_MATCH_SUBSTRING,
+#ifdef HAVE_GLOB
+ match_MATCH_GLOB,
+#endif
+#ifdef HAVE_REGEX
+ match_MATCH_REGEX,
+#endif
+} match_MatchType;
+
+typedef int match_Result;
+#define match_NOMATCH 0
+#define match_MATCH 1
+#define match_OK 0
+#define match_EUNKNOWN -1
+#define match_ENOSYS -2
+#define match_ECUSTOM -3
+#define match_ENOTINIT -4
+
+typedef struct match_LiteralContext match_LiteralContext;
+typedef struct match_PrefixContext match_PrefixContext;
+typedef struct match_SuffixContext match_SuffixContext;
+typedef struct match_SubStringContext match_SubStringContext;
+#ifdef HAVE_GLOB
+typedef struct match_GlobContext match_GlobContext;
+#endif
+#ifdef HAVE_REGEX
+typedef struct match_RegexContext match_RegexContext;
+#endif
+
+match_Result match_prepareContext(const char *pattern,
+ match_MatchContext **contextPtr, match_MatchType type);
+match_Result match_matchPattern(match_MatchContext *context,
+ const char *string);
+const char *match_errorString(match_MatchContext *context,
+ match_Result result);
+void match_freeContext(match_MatchContext *context);
+match_Result match_matchPatternOnce(const char *pattern, match_MatchType type,
+ const char *string);
+
+
+/* *** Internal definitions follow *** */
+#ifdef match_INTERNAL
+
+#include <sys/types.h>
+#ifdef HAVE_REGEX
+# include <regex.h>
+#endif
+
+#include "uioport.h"
+
+struct match_MatchContext {
+ match_MatchType type;
+ union {
+ match_LiteralContext *literal;
+ match_PrefixContext *prefix;
+ match_SuffixContext *suffix;
+ match_SubStringContext *subString;
+#ifdef HAVE_GLOB
+ match_GlobContext *glob;
+#endif
+#ifdef HAVE_REGEX
+ match_RegexContext *regex;
+#endif
+ } u;
+};
+
+struct match_LiteralContext {
+ char *pattern;
+};
+
+struct match_PrefixContext {
+ char *pattern;
+};
+
+struct match_SuffixContext {
+ char *pattern;
+ size_t len;
+ // for speed
+};
+
+struct match_SubStringContext {
+ char *pattern;
+};
+
+#ifdef HAVE_GLOB
+struct match_GlobContext {
+ char *pattern;
+};
+#endif
+
+#ifdef HAVE_REGEX
+struct match_RegexContext {
+ regex_t native;
+ char *errorString;
+ int errorCode;
+ int flags;
+#define match_REGEX_INITIALISED 1
+};
+#endif
+
+match_Result match_prepareLiteral(const char *pattern,
+ match_LiteralContext **contextPtr);
+match_Result match_matchLiteral(match_LiteralContext *context,
+ const char *string);
+void match_freeLiteral(match_LiteralContext *context);
+
+match_Result match_preparePrefix(const char *pattern,
+ match_PrefixContext **contextPtr);
+match_Result match_matchPrefix(match_PrefixContext *context,
+ const char *string);
+void match_freePrefix(match_PrefixContext *context);
+
+match_Result match_prepareSuffix(const char *pattern,
+ match_SuffixContext **contextPtr);
+match_Result match_matchSuffix(match_SuffixContext *context,
+ const char *string);
+void match_freeSuffix(match_SuffixContext *context);
+
+match_Result match_prepareSubString(const char *pattern,
+ match_SubStringContext **contextPtr);
+match_Result match_matchSubString(match_SubStringContext *context,
+ const char *string);
+void match_freeSubString(match_SubStringContext *context);
+
+#ifdef HAVE_GLOB
+match_Result match_prepareGlob(const char *pattern,
+ match_GlobContext **contextPtr);
+match_Result match_matchGlob(match_GlobContext *context,
+ const char *string);
+void match_freeGlob(match_GlobContext *context);
+#endif /* HAVE_GLOB */
+
+#ifdef HAVE_REGEX
+match_Result match_prepareRegex(const char *pattern,
+ match_RegexContext **contextPtr);
+match_Result match_matchRegex(match_RegexContext *context,
+ const char *string);
+const char *match_errorStringRegex(match_RegexContext *context,
+ int errorCode);
+void match_freeRegex(match_RegexContext *context);
+#endif
+
+#endif /* match_INTERNAL */
+
+#endif /* LIBS_UIO_MATCH_H_ */
+
diff --git a/src/libs/uio/mem.h b/src/libs/uio/mem.h
new file mode 100644
index 0000000..915837e
--- /dev/null
+++ b/src/libs/uio/mem.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MEM_H_
+#define LIBS_UIO_MEM_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include "uioport.h"
+
+#define uio_malloc malloc
+#define uio_realloc realloc
+#define uio_free free
+#define uio_calloc calloc
+
+#ifdef uio_MEM_DEBUG
+// When uio_strdup is defined to the libc strdup, there's no opportunity
+// to intercept the alloc. Hence this function here.
+static inline char *
+uio_strdup(const char *s) {
+ char *result;
+ size_t size;
+
+ size = strlen(s) + 1;
+ result = uio_malloc(size);
+ memcpy(result, s, size);
+ return result;
+}
+#else
+# define uio_strdup strdup
+#endif
+
+// Allocates new memory, copies 'len' characters from 'src', and adds a '\0'.
+static inline char *
+uio_memdup0(const char *src, size_t len) {
+ char *dst = uio_malloc(len + 1);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ return dst;
+}
+
+#endif
+
+
diff --git a/src/libs/uio/memdebug.c b/src/libs/uio/memdebug.c
new file mode 100644
index 0000000..b6bd1b6
--- /dev/null
+++ b/src/libs/uio/memdebug.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "memdebug.h"
+#include "hashtable.h"
+#include "mem.h"
+#include "uioutils.h"
+#include "types.h"
+#include "uioport.h"
+
+// I'm intentionally not including the .h files. It would only make things
+// messy, because the second arguments would be other pointers, which
+// would require typecasts of the functions in uio_MemDebug_logTypeInfo.
+extern void uio_DirHandle_print(FILE *out, const void *);
+extern void uio_printMountInfo(FILE *out, const void *);
+extern void uio_printMountTreeItem(FILE *out, const void *);
+extern void uio_printPathComp(FILE *out, const void *);
+
+// Must be in the same order as in uio_MemDebug_LogTypes
+// Change the third field to have debug info printed for specific actions.
+// See memdebug.h file the possible values.
+const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[] = {
+ { "uio_DirHandle", uio_DirHandle_print, 0 },
+ { "uio_FileSystemInfo", NULL, 0 },
+ { "uio_GPDir", NULL, 0 },
+ { "uio_GPFile", NULL, 0 },
+ { "uio_GPRoot", NULL, 0 },
+ { "uio_Handle", NULL, 0 },
+ { "uio_MountHandle", NULL, 0 },
+ { "uio_MountInfo", uio_printMountInfo, 0 },
+ { "uio_MountTree", NULL, 0 },
+ { "uio_MountTreeItem", uio_printMountTreeItem, 0 },
+ { "uio_PathComp", uio_printPathComp, 0 },
+ { "uio_PFileHandle", NULL, 0 },
+ { "uio_PDirHandle", NULL, 0 },
+ { "uio_PRoot", NULL, 0 },
+ { "uio_Repository", NULL, 0 },
+ { "uio_Stream", NULL, 0 },
+ { "stdio_GPDirData", NULL, 0 },
+#ifdef HAVE_ZIP
+ { "zip_GPFileData", NULL, 0 },
+ { "zip_GPDirData", NULL, 0 },
+#endif
+};
+
+HashTable_HashTable **uio_MemDebug_logs;
+
+
+static uio_uint32 uio_MemDebug_pointerHash(const void *ptr);
+static uio_bool uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2);
+static void *uio_MemDebug_pointerCopy(const void *ptr);
+static void uio_MemDebug_pointerFree(void *ptr);
+
+static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_new(int ref);
+static inline void uio_MemDebug_PointerInfo_delete(
+ uio_MemDebug_PointerInfo *pointerInfo);
+static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_alloc(void);
+static inline void uio_MemDebug_PointerInfo_free(
+ uio_MemDebug_PointerInfo *pointerInfo);
+
+void
+uio_MemDebug_init(void) {
+ int i;
+
+ assert((int) uio_MemDebug_numLogTypes ==
+ (sizeof uio_MemDebug_logTypeInfo /
+ sizeof uio_MemDebug_logTypeInfo[0]));
+ uio_MemDebug_logs = uio_malloc(uio_MemDebug_numLogTypes *
+ sizeof (HashTable_HashTable *));
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++) {
+ uio_MemDebug_logs[i] = HashTable_newHashTable(
+ uio_MemDebug_pointerHash,
+ uio_MemDebug_pointerCompare,
+ uio_MemDebug_pointerCopy,
+ uio_MemDebug_pointerFree,
+ 4, 0.85, 0.90);
+ }
+}
+
+void
+uio_MemDebug_unInit(void) {
+ int i;
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++)
+ HashTable_deleteHashTable(uio_MemDebug_logs[i]);
+
+ uio_free(uio_MemDebug_logs);
+}
+
+static uio_uint32
+uio_MemDebug_pointerHash(const void *ptr) {
+ uio_uintptr ptrInt;
+
+ ptrInt = (uio_uintptr) ptr;
+ return (uio_uint32) (ptrInt ^ (ptrInt >> 10) ^ (ptrInt >> 20));
+}
+
+static uio_bool
+uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2) {
+ return ptr1 == ptr2;
+}
+
+static void *
+uio_MemDebug_pointerCopy(const void *ptr) {
+ return unconst(ptr);
+}
+
+static void
+uio_MemDebug_pointerFree(void *ptr) {
+ (void) ptr;
+}
+
+void
+uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Allocated pointer is (%s *) NULL.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_ALLOC) {
+ fprintf(stderr, "Alloc ");
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+ pointerInfo = uio_MemDebug_PointerInfo_new(1);
+ HashTable_add(uio_MemDebug_logs[type], ptr, (void *) pointerInfo);
+}
+
+void
+uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to free (%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_FREE) {
+ fprintf(stderr, "Free ");
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to free unallocated pointer "
+ "(%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+#if 0
+ if (pointerInfo->ref != 0) {
+ fprintf(stderr, "Fatal: Attempt to free pointer with references "
+ "left (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+#endif
+ uio_MemDebug_PointerInfo_free(pointerInfo);
+ HashTable_remove(uio_MemDebug_logs[type], ptr);
+}
+
+void
+uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to increment reference to a "
+ "(%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to increment reference to "
+ "unallocated pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ pointerInfo->pointerRef++;
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_REF) {
+ fprintf(stderr, "Ref++ to %d, ", pointerInfo->pointerRef);
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference to a "
+ "(%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference to "
+ "unallocated pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ if (pointerInfo->pointerRef == 0) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference below 0 for "
+ "pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ pointerInfo->pointerRef--;
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_UNREF) {
+ fprintf(stderr, "Ref-- to %d, ", pointerInfo->pointerRef);
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type, void *ptr) {
+ fprintf(out, "(%s *) %p", uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ if (uio_MemDebug_logTypeInfo[(int) type].printFunction != NULL) {
+ fprintf(out, ": ");
+ uio_MemDebug_logTypeInfo[(int) type].printFunction(out, ptr);
+ }
+}
+
+void
+uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type) {
+ HashTable_Iterator *iterator;
+
+ for (iterator = HashTable_getIterator(uio_MemDebug_logs[type]);
+ !HashTable_iteratorDone(iterator);
+ iterator = HashTable_iteratorNext(iterator)) {
+ uio_MemDebug_printPointer(out, type, HashTable_iteratorKey(iterator));
+ fprintf(out, "\n");
+ }
+ HashTable_freeIterator(iterator);
+}
+
+void
+uio_MemDebug_printPointers(FILE *out) {
+ int i;
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++)
+ uio_MemDebug_printPointersType(out, i);
+}
+
+static inline uio_MemDebug_PointerInfo *
+uio_MemDebug_PointerInfo_new(int ref) {
+ uio_MemDebug_PointerInfo *result;
+ result = uio_MemDebug_PointerInfo_alloc();
+ result->pointerRef = ref;
+ return result;
+}
+
+static inline void
+uio_MemDebug_PointerInfo_delete(uio_MemDebug_PointerInfo *pointerInfo) {
+ uio_MemDebug_PointerInfo_free(pointerInfo);
+}
+
+static inline uio_MemDebug_PointerInfo *
+uio_MemDebug_PointerInfo_alloc(void) {
+ return uio_malloc(sizeof (uio_MemDebug_PointerInfo));
+}
+
+static inline void
+uio_MemDebug_PointerInfo_free(uio_MemDebug_PointerInfo *pointerInfo) {
+ uio_free(pointerInfo);
+}
+
diff --git a/src/libs/uio/memdebug.h b/src/libs/uio/memdebug.h
new file mode 100644
index 0000000..e04a8d2
--- /dev/null
+++ b/src/libs/uio/memdebug.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MEMDEBUG_H_
+#define LIBS_UIO_MEMDEBUG_H_
+
+#include <stdio.h>
+#include "uioport.h"
+
+// Important: if you add an item here, add it to uio_MemDebug_LogTypeInfo
+// too. The order should be the same.
+typedef enum {
+ uio_MemDebug_LogType_uio_DirHandle,
+ uio_MemDebug_LogType_uio_FileSystemInfo,
+ uio_MemDebug_LogType_uio_GPDir,
+ uio_MemDebug_LogType_uio_GPFile,
+ uio_MemDebug_LogType_uio_GPRoot,
+ uio_MemDebug_LogType_uio_Handle,
+ uio_MemDebug_LogType_uio_MountHandle,
+ uio_MemDebug_LogType_uio_MountInfo,
+ uio_MemDebug_LogType_uio_MountTree,
+ uio_MemDebug_LogType_uio_MountTreeItem,
+ uio_MemDebug_LogType_uio_PathComp,
+ uio_MemDebug_LogType_uio_PFileHandle,
+ uio_MemDebug_LogType_uio_PDirHandle,
+ uio_MemDebug_LogType_uio_PRoot,
+ uio_MemDebug_LogType_uio_Repository,
+ uio_MemDebug_LogType_uio_Stream,
+ uio_MemDebug_LogType_stdio_GPDirData,
+ uio_MemDebug_LogType_zip_GPFileData,
+ uio_MemDebug_LogType_zip_GPDirData,
+
+ uio_MemDebug_numLogTypes, /* This needs to be the last entry */
+} uio_MemDebug_LogType;
+
+typedef void (uio_MemDebug_PrintFunction)(FILE *out, const void *arg);
+
+typedef struct uio_MemDebug_LogTypeInfo {
+ const char *name;
+ uio_MemDebug_PrintFunction *printFunction;
+ int flags;
+#define uio_MemDebug_PRINT_ALLOC 0x1
+#define uio_MemDebug_PRINT_FREE 0x2
+#define uio_MemDebug_PRINT_REF 0x4
+#define uio_MemDebug_PRINT_UNREF 0x8
+#define uio_MemDebug_PRINT_ALL \
+ (uio_MemDebug_PRINT_ALLOC | uio_MemDebug_PRINT_FREE | \
+ uio_MemDebug_PRINT_REF | uio_MemDebug_PRINT_UNREF)
+} uio_MemDebug_LogTypeInfo;
+
+extern const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[];
+
+typedef struct uio_MemDebug_PointerInfo {
+ int pointerRef;
+ // Ref counter for the associated pointer, not the pointerInfo
+ // itself.
+} uio_MemDebug_PointerInfo;
+
+void uio_MemDebug_init(void);
+void uio_MemDebug_unInit(void);
+void uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type,
+ void *ptr);
+void uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type);
+void uio_MemDebug_printPointers(FILE *out);
+
+#define uio_MemDebug_debugAlloc(type, pointer) \
+ uio_MemDebug_logAllocation(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugFree(type, pointer) \
+ uio_MemDebug_logDeallocation(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugRef(type, pointer) \
+ uio_MemDebug_logRef(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugUnref(type, pointer) \
+ uio_MemDebug_logUnref(uio_MemDebug_LogType_ ## type, pointer)
+
+#endif /* LIBS_UIO_MEMDEBUG_H_ */
+
diff --git a/src/libs/uio/mount.c b/src/libs/uio/mount.c
new file mode 100644
index 0000000..f095d3f
--- /dev/null
+++ b/src/libs/uio/mount.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static void uio_Repository_delete(uio_Repository *repository);
+static uio_Repository *uio_Repository_alloc(void);
+static void uio_Repository_free(uio_Repository *repository);
+
+
+void
+uio_repositoryAddMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo, uio_MountLocation location,
+ uio_MountInfo *relative) {
+ lockRepository(repository, uio_LOCK_WRITE);
+ switch (location) {
+ case uio_MOUNT_TOP: {
+ uio_MountInfo **newMounts;
+
+ newMounts = uio_malloc(
+ (repository->numMounts + 2) * sizeof (uio_MountInfo *));
+ newMounts[0] = mountInfo;
+ memcpy(&newMounts[1], repository->mounts,
+ (repository->numMounts + 1) * sizeof (uio_MountInfo *));
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ case uio_MOUNT_BOTTOM:
+ repository->mounts = uio_realloc(repository->mounts,
+ (repository->numMounts + 2) * sizeof (uio_MountInfo *));
+ repository->mounts[repository->numMounts] = mountInfo;
+ repository->numMounts++;
+ break;
+ case uio_MOUNT_ABOVE: {
+ int i;
+ uio_MountInfo **newMounts;
+
+ i = 0;
+ while (repository->mounts[i] != relative)
+ i++;
+ newMounts = (uio_MountInfo **) insertArrayPointer(
+ (const void **) repository->mounts,
+ repository->numMounts + 1, i, (void *) mountInfo);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ case uio_MOUNT_BELOW: {
+ int i;
+ uio_MountInfo **newMounts;
+
+ i = 0;
+ while (repository->mounts[i] != relative)
+ i++;
+ i++;
+ newMounts = (uio_MountInfo **) insertArrayPointer(
+ (const void **) repository->mounts,
+ repository->numMounts + 1, i, (void *) mountInfo);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ default:
+ assert(false);
+ }
+ unlockRepository(repository);
+}
+
+void
+uio_repositoryRemoveMount(uio_Repository *repository, uio_MountInfo *mountInfo) {
+ int i;
+ uio_MountInfo **newMounts;
+
+ lockRepository(repository, uio_LOCK_WRITE);
+
+ i = 0;
+ while (repository->mounts[i] != mountInfo)
+ i++;
+ newMounts = (uio_MountInfo **) excludeArrayPointer(
+ (const void **) repository->mounts, repository->numMounts + 1,
+ i, 1);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts--;
+ unlockRepository(repository);
+}
+
+// sets ref to 1
+uio_Repository *
+uio_Repository_new(int flags) {
+ uio_Repository *result;
+
+ result = uio_Repository_alloc();
+ result->ref = 1;
+ result->flags = flags;
+ result->numMounts = 0;
+ result->mounts = uio_malloc(1 * sizeof (uio_MountInfo *));
+ result->mounts[0] = NULL;
+ result->mountTree = uio_makeRootMountTree();
+ return result;
+}
+
+void
+uio_Repository_unref(uio_Repository *repository) {
+ assert(repository->ref > 0);
+ repository->ref--;
+ if (repository->ref == 0)
+ uio_Repository_delete(repository);
+}
+
+static uio_Repository *
+uio_Repository_alloc(void) {
+ uio_Repository *result = uio_malloc(sizeof (uio_Repository));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Repository, (void *) result);
+#endif
+ return result;
+}
+
+static void
+uio_Repository_delete(uio_Repository *repository) {
+ assert(repository->numMounts == 0);
+ uio_free(repository->mounts);
+ uio_MountTree_delete(repository->mountTree);
+ uio_Repository_free(repository);
+}
+
+static void
+uio_Repository_free(uio_Repository *repository) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Repository, (void *) repository);
+#endif
+ uio_free(repository);
+}
+
+
diff --git a/src/libs/uio/mount.h b/src/libs/uio/mount.h
new file mode 100644
index 0000000..237f54b
--- /dev/null
+++ b/src/libs/uio/mount.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MOUNT_H_
+#define LIBS_UIO_MOUNT_H_
+
+
+typedef struct uio_Repository uio_Repository;
+typedef struct uio_AutoMount uio_AutoMount;
+#define uio_MOUNT_RDONLY (1 << 1)
+
+/* *** Internal definitions follow */
+#ifdef uio_INTERNAL
+
+#define uio_MOUNT_LOCATION_MASK (3 << 2)
+
+#include "uioport.h"
+#include "fstypes.h"
+#include "mounttree.h"
+#include "match.h"
+
+struct uio_Repository {
+ int ref; /* reference counter */
+ int flags;
+ int numMounts;
+ struct uio_MountInfo **mounts;
+ // first in the list is considered the top
+ // last entry is NULL
+ struct uio_MountTree *mountTree;
+};
+
+#define lockRepository(repository, prot)
+#define unlockRepository(repository)
+
+uio_Repository *uio_Repository_new(int flags);
+void uio_Repository_unref(uio_Repository *repository);
+void uio_repositoryAddMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo, uio_MountLocation location,
+ uio_MountInfo *relative);
+void uio_repositoryRemoveMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo);
+
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_MOUNT_H_ */
+
diff --git a/src/libs/uio/mounttree.c b/src/libs/uio/mounttree.c
new file mode 100644
index 0000000..eb5bdec
--- /dev/null
+++ b/src/libs/uio/mounttree.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+#include <assert.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "mounttree.h"
+#include "paths.h"
+#include "types.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static uio_MountTree *uio_mountTreeAddMountInfoRecTree(
+ uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, const char *start, const char *end,
+ uio_PathComp *upComp, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static inline uio_MountTree *uio_mountTreeAddMountInfoRecTreeSub(
+ uio_Repository *repository, uio_MountTree **tree,
+ uio_MountInfo *mountInfo, const char *start,
+ const char *end, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static void uio_mountTreeAddMountInfoLocAll(uio_Repository *repository,
+ uio_MountTree *tree, uio_MountInfo *mountInfo, int depth,
+ uio_MountLocation location, const uio_MountInfo *relative);
+static uio_MountTreeItem *uio_copyMountTreeItems(uio_MountTreeItem *item,
+ int extraDepth);
+static void uio_addMountTreeItem(uio_Repository *repository,
+ uio_MountTreeItem **pLocs, uio_MountTreeItem *item,
+ uio_MountLocation location, const uio_MountInfo *relative);
+static uio_MountTree *uio_mountTreeAddNewSubTree(uio_Repository *repository,
+ uio_MountTree *tree, const char *path, uio_MountInfo *mountInfo,
+ uio_PathComp *upComp, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static void uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub);
+static uio_MountTree * uio_splitMountTree(uio_MountTree **tree, uio_PathComp
+ *lastComp, int depth);
+static void uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo);
+static void uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo);
+
+static inline uio_MountTree * uio_MountTree_new(uio_MountTree *subTrees,
+ uio_MountTreeItem *pLocs, uio_MountTree *upTree, uio_PathComp
+ *comps, uio_PathComp *lastComp, uio_MountTree *next);
+static inline uio_MountTreeItem *uio_MountTree_newItem(
+ uio_MountInfo *mountInfo, int depth, uio_MountTreeItem *next);
+
+static inline void uio_MountTreeItem_delete(uio_MountTreeItem *item);
+
+static inline uio_MountTree *uio_MountTree_alloc(void);
+static inline uio_MountTreeItem *uio_MountTreeItem_alloc(void);
+static inline uio_MountInfo *uio_MountInfo_alloc(void);
+
+static inline void uio_MountTree_free(uio_MountTree *mountTree);
+static inline void uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem);
+static inline void uio_MountInfo_free(uio_MountInfo *mountInfo);
+
+
+// make the root mount Tree
+uio_MountTree *
+uio_makeRootMountTree(void) {
+ return uio_MountTree_new(NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+// Add a MountInfo structure to a MountTree in the place pointed
+// to by 'path'.
+// returns the MountTree for the location at the end of the path
+uio_MountTree *
+uio_mountTreeAddMountInfo(uio_Repository *repository, uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo, const char *path, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ const char *start, *end;
+
+ getFirstPath0Component(path, &start, &end);
+ return uio_mountTreeAddMountInfoRecTree(repository, mountTree, mountInfo,
+ start, end, NULL, location, relative);
+}
+
+// recursive helper for uio_mountTreeAddMountInfo
+// returns the MountTree for the location at the end of the path
+static uio_MountTree *
+uio_mountTreeAddMountInfoRecTree(uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, const char *start, const char *end,
+ uio_PathComp *upComp,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ uio_MountTree **sub;
+
+ if (*start == '\0') {
+ // End of the path. Put the MountInfo here and on all subtrees below
+ // this level.
+ uio_mountTreeAddMountInfoLocAll(repository, tree, mountInfo, 0,
+ location, relative);
+ return tree;
+ }
+
+ // Check if sub trees match the path.
+ for (sub = &tree->subTrees; *sub != NULL; sub = &(*sub)->next) {
+ uio_MountTree *resTree;
+
+ resTree = uio_mountTreeAddMountInfoRecTreeSub(repository, sub,
+ mountInfo, start, end, location, relative);
+ if (resTree != NULL) {
+ // handled
+ return resTree;
+ }
+ }
+ // No subtree found matching (part of) 'path'.
+
+ // Need to add a new tree sub
+ return uio_mountTreeAddNewSubTree(repository, tree, start, mountInfo,
+ upComp, location, relative);
+}
+
+// recursive helper for uio_mountTreeAddMountInfo
+// Pre: *start != '\0'
+// returns the MountTree for the location at the end of the path, if
+// that falls within this tree. If not, returns NULL.
+static inline uio_MountTree *
+uio_mountTreeAddMountInfoRecTreeSub(uio_Repository *repository,
+ uio_MountTree **tree, uio_MountInfo *mountInfo,
+ const char *start, const char *end, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ uio_PathComp *comp, *lastComp;
+ int depth;
+
+ comp = (*tree)->comps;
+ if (strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // first component does not match; this is not the correct subTree
+ return NULL;
+ }
+
+ depth = 1;
+ // try to match all components of the directory path to this subTree.
+ while (1) {
+ getNextPath0Component(&start, &end);
+ lastComp = comp;
+ comp = comp->next;
+
+ if (comp == NULL)
+ break;
+
+ if (*start == '\0') {
+ // end of the path reached
+ // We need to split up the components and insert a new
+ // MountTree here.
+ uio_MountTree *newTree;
+ newTree = uio_splitMountTree(tree, lastComp, depth);
+
+ // Add mountInfo to each of the MountTrees below newTree.
+ uio_mountTreeAddMountInfoLocAll(repository, newTree, mountInfo, 0,
+ location, relative);
+
+ return newTree;
+ }
+ if (strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // Some, but not all components matched; we need to split
+ // up the components and add a new subTree here for the
+ // (non-matching) rest of the path.
+ uio_MountTree *newTree;
+
+ newTree = uio_splitMountTree(tree, lastComp, depth);
+
+ // A new Tree is added at the split-point.
+ return uio_mountTreeAddNewSubTree(repository, newTree, start,
+ mountInfo, lastComp, location, relative);
+ }
+ getNextPath0Component(&start, &end);
+ depth++;
+ }
+
+ // All components matched. We can recurse to the next subdir.
+ return uio_mountTreeAddMountInfoRecTree(repository, *tree, mountInfo,
+ start, end, lastComp, location, relative);
+}
+
+// Add a MountInfo struct 'mountInfo' to the pLocs fields of all subTrees
+// starting with 'tree'.
+// 'depth' is the distance to the MountTree where the MountInfo is located.
+static void
+uio_mountTreeAddMountInfoLocAll(uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, int depth, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ uio_MountTreeItem *newPLoc;
+ uio_MountTree *subTree;
+ int compCount;
+
+ // Add a new PLoc to this mountTree
+ newPLoc = uio_MountTree_newItem(mountInfo, depth, NULL);
+ uio_addMountTreeItem(repository, &tree->pLocs, newPLoc, location, relative);
+
+ // Recurse for subtrees
+ for (subTree = tree->subTrees; subTree != NULL;
+ subTree = subTree->next) {
+ compCount = uio_countPathComps(subTree->comps);
+ uio_mountTreeAddMountInfoLocAll(
+ repository, subTree, mountInfo, depth + compCount,
+ location, relative);
+ }
+}
+
+// pre: repository->mounts is already updated
+// pre: if location is uio_MOUNT_BELOW or uio_MOUNT_ABOVE, 'relative'
+// exists in repository->mounts
+static void
+uio_addMountTreeItem(uio_Repository *repository, uio_MountTreeItem **pLocs,
+ uio_MountTreeItem *item,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ switch (location) {
+ case uio_MOUNT_TOP:
+ item->next = *pLocs;
+ *pLocs = item;
+ break;
+ case uio_MOUNT_BOTTOM:
+ while (*pLocs != NULL)
+ pLocs = &(*pLocs)->next;
+ item->next = NULL;
+ *pLocs = item;
+ break;
+ case uio_MOUNT_ABOVE: {
+ uio_MountInfo **mountInfo;
+ mountInfo = repository->mounts;
+ while (*mountInfo != relative) {
+ assert(*mountInfo != NULL);
+ if ((*pLocs)->mountInfo == *mountInfo)
+ pLocs = &(*pLocs)->next;
+ mountInfo++;
+ }
+ item->next = *pLocs;
+ *pLocs = item;
+ break;
+ }
+ case uio_MOUNT_BELOW: {
+ uio_MountInfo **mountInfo;
+ mountInfo = repository->mounts;
+ while (*mountInfo != relative) {
+ assert(*mountInfo != NULL);
+ if ((*pLocs)->mountInfo == *mountInfo)
+ pLocs = &(*pLocs)->next;
+ mountInfo++;
+ }
+ item->next = (*pLocs)->next;
+ (*pLocs)->next = item;
+ break;
+ }
+ default:
+ assert(false);
+ }
+}
+
+// Copy a chain of MountTreeItems, but increase the depth by 'extraDepth'.
+static uio_MountTreeItem *
+uio_copyMountTreeItems(uio_MountTreeItem *item, int extraDepth) {
+ uio_MountTreeItem *result, **resPtr;
+ uio_MountTreeItem *newItem;
+
+ resPtr = &result;
+ while (item != NULL) {
+ newItem = uio_MountTree_newItem(
+ item->mountInfo, item->depth + extraDepth, NULL);
+ *resPtr = newItem;
+ resPtr = &newItem->next;
+ item = item->next;
+ }
+ *resPtr = NULL;
+ return result;
+}
+
+// add a new sub tree under a tree 'tree'.
+// 'path' is the part leading up to the new tree and
+// 'mountInfo' is the MountInfo structure to at there.
+// 'upComp' points to the last path component that lead to 'tree'.
+static uio_MountTree *
+uio_mountTreeAddNewSubTree(uio_Repository *repository, uio_MountTree *tree,
+ const char *path, uio_MountInfo *mountInfo, uio_PathComp *upComp,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ uio_MountTreeItem *item, *items;
+ uio_MountTree *newTree;
+ uio_PathComp *compList, *lastComp;
+ int compCount;
+
+ compList = uio_makePathComps(path, upComp);
+ compCount = uio_countPathComps(compList);
+ lastComp = uio_lastPathComp(compList);
+ item = uio_MountTree_newItem(mountInfo, 0, NULL);
+ item->next = NULL;
+ items = uio_copyMountTreeItems(tree->pLocs, compCount);
+ uio_addMountTreeItem(repository, &items, item, location,
+ relative);
+ newTree = uio_MountTree_new(
+ NULL /* subTrees */,
+ items /* pLocs */,
+ tree /* upTree */,
+ compList /* comps */,
+ lastComp /* lastComp */,
+ NULL /* next */);
+ uio_mountTreeAddSub(tree, newTree);
+ return newTree;
+}
+
+// add a sub structure to the end of the 'subTrees' list of a tree.
+static void
+uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub) {
+ uio_MountTree **subPtr;
+
+ for (subPtr = &tree->subTrees; *subPtr != NULL;
+ subPtr = &(*subPtr)->next) {
+ // Nothing to do here.
+ }
+ *subPtr = sub;
+}
+
+// Add a new MountTree structure in between two MountTrees.
+// Tree points to the pointer for the tree in front of which the new
+// tree needs to be placed (at depth 'depth').
+// 'lastComp' is the last pathComp of the part before the splitting point
+// It returns the new MountTree.
+static uio_MountTree *
+uio_splitMountTree(uio_MountTree **tree, uio_PathComp *lastComp, int depth) {
+ uio_MountTree *newTree;
+ uio_MountTreeItem *items;
+
+ items = uio_copyMountTreeItems((*tree)->upTree->pLocs, depth);
+ newTree = uio_MountTree_new(
+ *tree /* subTrees */,
+ items /* pLocs */,
+ (*tree)->upTree /* upTree */,
+ (*tree)->comps /* comps */,
+ lastComp /* lastComp */,
+ NULL /* next */);
+ (*tree)->upTree = newTree;
+ (*tree)->comps = lastComp->next;
+ lastComp->next = NULL;
+ *tree = newTree;
+ return newTree;
+}
+
+void
+uio_mountTreeRemoveMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo) {
+ uio_MountTree **subTreePtr;
+ uio_MountTree *upTree;
+
+ // If the tree has no sub-trees and it has the same items as the
+ // upTree, with 'mountInfo' added, then the tree is a dead end
+ // and can be removed entirely.
+ // First we handle the other case.
+ // Note that if the upTree has exactly one item less than the tree
+ // itself, these items must be the same, plus mountInfo for the
+ // tree itself, as each tree has at least the items of its upTree.
+ if (mountTree->upTree == NULL || mountTree->subTrees != NULL ||
+ uio_mountTreeCountPLocs(mountTree) !=
+ uio_mountTreeCountPLocs(mountTree->upTree) + 1) {
+ // We can't remove the tree itself.
+ // We need to remove the mountInfo from the tree, and all subTrees.
+ // Then we're done.
+ uio_mountTreeRemoveMountInfoRec(mountTree, mountInfo);
+ return;
+ }
+
+ // mountTree itself can be removed.
+ // First remove the tree from the list of subtrees of the upTree.
+ subTreePtr = &mountTree->upTree->subTrees;
+ while (1) {
+ assert(*subTreePtr != NULL);
+ if (*subTreePtr == mountTree)
+ break;
+ subTreePtr = &(*subTreePtr)->next;
+ }
+ *subTreePtr = mountTree->next;
+
+ // Save the upTree for later.
+ upTree = mountTree->upTree;
+
+ // Remove the tree itself.
+ uio_MountTree_delete(mountTree);
+
+ // The upTree itself could have become unnecessary now.
+ // This is the case when upTree now only has one subTree, and upTree
+ // and the subTree have the same items.
+ // Again, same item count implies same items.
+ if (upTree->subTrees == NULL || upTree->subTrees->next != NULL ||
+ uio_mountTreeCountPLocs(upTree) !=
+ uio_mountTreeCountPLocs(upTree->subTrees)) {
+ // upTree is still necessary. We're done.
+ return;
+ }
+
+ // Merge upTree and upTree->subTrees.
+ // It would be easiest to keep upTree, and throw upTree->subTrees away,
+ // but that's not possible as external links point to upTree->subTrees.
+ // First merge the path components:
+ assert(upTree->subTrees->lastComp != NULL);
+ upTree->subTrees->lastComp->next = upTree->subTrees->comps;
+ upTree->subTrees->lastComp = upTree->lastComp;
+ upTree->subTrees->comps = upTree->comps;
+ // Now let the pointer that pointed to upTree, point to upTree->subTrees.
+ // Change upTree->next accordingly.
+ if (upTree->upTree == NULL) {
+ assert(repository->mountTree == upTree);
+ repository->mountTree = upTree->subTrees;
+ // upTree->subTrees->next is already NULL
+ } else {
+ uio_MountTree *next;
+ subTreePtr = &upTree->upTree->subTrees;
+ while (1) {
+ assert(*subTreePtr != NULL);
+ if (*subTreePtr == upTree)
+ break;
+ subTreePtr = &(*subTreePtr)->next;
+ }
+ next = (*subTreePtr)->next;
+ *subTreePtr = upTree->subTrees;
+ upTree->subTrees->next = next;
+ }
+
+ // Now delete the tree itself
+ upTree->subTrees = NULL;
+ upTree->comps = NULL;
+ uio_MountTree_delete(upTree);
+}
+
+// pre: mountInfo exists in mountTree->pLocs (and hence in pLocs for
+// every sub-tree)
+static void
+uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo) {
+ uio_MountTree *subTree;
+ uio_MountTreeItem **itemPtr, *item;
+
+ // recurse for all subTrees
+ for (subTree = mountTree->subTrees; subTree != NULL;
+ subTree = subTree->next)
+ uio_mountTreeRemoveMountInfoRec(subTree, mountInfo);
+
+ // Find the mount info in this tree.
+ itemPtr = &mountTree->pLocs;
+ while (1) {
+ assert(*itemPtr != NULL);
+ // We know an item with the specified mountInfo
+ // must be here somewhere.
+ if ((*itemPtr)->mountInfo == mountInfo) {
+ // Found it.
+ break;
+ }
+ itemPtr = &(*itemPtr)->next;
+ }
+
+ item = *itemPtr;
+ *itemPtr = item->next;
+ uio_MountTreeItem_delete(item);
+}
+
+// Count the number of pLocs in a tree that leads to.
+int
+uio_mountTreeCountPLocs(const uio_MountTree *tree) {
+ int count;
+ uio_MountTreeItem *item;
+
+ count = 0;
+ for (item = tree->pLocs; item != NULL; item = item->next)
+ count++;
+ return count;
+}
+
+// resTree may point to top
+// pPath may point to path
+void
+uio_findMountTree(uio_MountTree *top, const char *path,
+ uio_MountTree **resTree, const char **pPath) {
+ const char *start, *end, *pathFromTree;
+ uio_MountTree *tree, *sub;
+ uio_PathComp *comp;
+
+ getFirstPath0Component(path, &start, &end);
+ tree = top;
+ while(1) {
+ if (*start == '\0') {
+ *resTree = tree;
+ *pPath = start;
+ return;
+ }
+
+ pathFromTree = start;
+ sub = tree->subTrees;
+ while(1) {
+ if (sub == NULL) {
+ // No matching sub Dirs found. So we report back the current
+ // dir.
+ *resTree = tree;
+ *pPath = pathFromTree;
+ return;
+ }
+ comp = sub->comps;
+ if (strncmp(comp->name, start, end - start) == 0 &&
+ comp->name[end - start] == '\0')
+ break;
+ sub = sub->next;
+ }
+ // Found a Sub dir which matches at least partially.
+
+ while (1) {
+ getNextPath0Component(&start, &end);
+ comp = comp->next;
+ if (comp == NULL)
+ break;
+ if (*start == '\0' ||
+ strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // either the path ends here, or the path in the tree does.
+ // either way, the last Tree is the one we want.
+ *resTree = tree;
+ *pPath = pathFromTree;
+ return;
+ }
+ }
+ // all components matched until the next MountTree
+ tree = sub;
+ }
+}
+
+// finds the path to the MountInfo associated with a mountTreeItem
+// given a path to the 'item' itself.
+// 'item' is the mountTreeItem
+// 'endComp' is the last PathComp leading to 'item'
+// 'start' is the start of the path to the item
+char *
+uio_mountTreeItemRestPath(const uio_MountTreeItem *item,
+ uio_PathComp *endComp, const char *path) {
+ int i;
+ const char *pathPtr;
+
+ i = item->depth;
+ while (i--)
+ endComp = endComp->up;
+
+ pathPtr = path;
+ if (endComp != NULL) {
+ while (1) {
+ pathPtr += endComp->nameLen;
+ endComp = endComp->up;
+ if (endComp == NULL)
+ break;
+ pathPtr++;
+ // for a '/'
+ }
+ }
+ if (*path == '/') {
+ // / at the beginning of the path
+ pathPtr++;
+ }
+ if (*pathPtr == '/') {
+ // / at the end of the path
+ pathPtr++;
+ }
+// return (char *) pathPtr;
+ // gives warning
+// return *((char **)((void *) &pathPtr));
+ // not portable
+ return (char *) unconst((const void *) pathPtr);
+}
+
+void
+uio_printMountTree(FILE *outStream, const uio_MountTree *tree, int indent) {
+ uio_MountTree *sub;
+ uio_PathComp *comp;
+
+ fprintf(outStream, "(");
+ uio_printMountTreeItems(outStream, tree->pLocs);
+ fprintf(outStream, ")\n");
+ for (sub = tree->subTrees; sub != NULL; sub = sub->next) {
+ int newIndent;
+
+ newIndent = indent;
+ fprintf(outStream, "%*s", indent, "");
+ for (comp = sub->comps; comp != NULL; comp = comp->next) {
+ fprintf(outStream, "/%s", comp->name);
+ newIndent += 1 + comp->nameLen;
+ }
+ fprintf(outStream, " ");
+ newIndent += 1;
+ uio_printMountTree(outStream, sub, newIndent);
+ }
+}
+
+void
+uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item) {
+ uio_printMountInfo(outStream, item->mountInfo);
+ fprintf(outStream, ":%d", item->depth);
+}
+
+void
+uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item) {
+ if (!item)
+ return;
+ while(1) {
+ uio_printMountTreeItem(outStream, item);
+ item = item->next;
+ if (item == NULL)
+ break;
+ fprintf(outStream, ", ");
+ }
+}
+
+void
+uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree) {
+ if (tree->upTree == NULL) {
+ fprintf(outStream, "/");
+ } else
+ uio_printPathToComp(outStream, tree->lastComp);
+}
+
+void
+uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo) {
+ uio_FileSystemInfo *fsInfo;
+
+ fsInfo = uio_getFileSystemInfo(mountInfo->fsID);
+ fprintf(outStream, "%s:/%s", fsInfo->name, mountInfo->dirName);
+}
+
+static void
+uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo) {
+ uio_FileSystemInfo *fsInfo;
+
+ fsInfo = uio_getFileSystemInfo(mountInfo->fsID);
+ fprintf(outStream, "???:%s on ", mountInfo->dirName);
+ uio_printPathToMountTree(outStream, mountInfo->mountTree);
+ fprintf(outStream, " type %s (", fsInfo->name);
+ if (mountInfo->flags & uio_MOUNT_RDONLY) {
+ fprintf(outStream, "ro");
+ } else
+ fprintf(outStream, "rw");
+ fprintf(outStream, ")\n");
+}
+
+void
+uio_printMounts(FILE *outStream, const uio_Repository *repository) {
+ int i;
+
+ for (i = 0; i < repository->numMounts; i++) {
+ uio_printMount(outStream, repository->mounts[i]);
+ }
+}
+
+
+// *** uio_MountTree*** //
+
+static inline uio_MountTree *
+uio_MountTree_new(uio_MountTree *subTrees, uio_MountTreeItem *pLocs,
+ uio_MountTree *upTree, uio_PathComp *comps, uio_PathComp *lastComp,
+ uio_MountTree *next) {
+ uio_MountTree *result;
+
+ result = uio_MountTree_alloc();
+ result->subTrees = subTrees;
+ result->pLocs = pLocs;
+ result->upTree = upTree;
+ result->comps = comps;
+ result->lastComp = lastComp;
+ result->next = next;
+ return result;
+}
+
+void
+uio_MountTree_delete(uio_MountTree *tree) {
+ uio_MountTree *subTree, *nextTree;
+ uio_MountTreeItem *item, *nextItem;
+
+ subTree = tree->subTrees;
+ while (subTree != NULL) {
+ nextTree = subTree->next;
+ uio_MountTree_delete(subTree);
+ subTree = nextTree;
+ }
+
+ item = tree->pLocs;
+ while (item != NULL) {
+ nextItem = item->next;
+ uio_MountTreeItem_delete(item);
+ item = nextItem;
+ }
+
+ if (tree->comps != NULL)
+ uio_PathComp_delete(tree->comps);
+
+ uio_MountTree_free(tree);
+}
+
+static inline uio_MountTree *
+uio_MountTree_alloc(void) {
+ uio_MountTree *result = uio_malloc(sizeof (uio_MountTree));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountTree, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountTree_free(uio_MountTree *mountTree) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountTree, (void *) mountTree);
+#endif
+ uio_free(mountTree);
+}
+
+
+// *** uio_MountTreeItem *** //
+
+static inline uio_MountTreeItem *
+uio_MountTree_newItem(uio_MountInfo *mountInfo, int depth,
+ uio_MountTreeItem *next) {
+ uio_MountTreeItem *result;
+
+ result = uio_MountTreeItem_alloc();
+ result->mountInfo = mountInfo;
+ result->depth = depth;
+ result->next = next;
+ return result;
+}
+
+static inline void
+uio_MountTreeItem_delete(uio_MountTreeItem *item) {
+ uio_MountTreeItem_free(item);
+}
+
+static inline uio_MountTreeItem *
+uio_MountTreeItem_alloc(void) {
+ uio_MountTreeItem *result = uio_malloc(sizeof (uio_MountTreeItem));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountTreeItem, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountTreeItem, (void *) mountTreeItem);
+#endif
+ uio_free(mountTreeItem);
+}
+
+
+// *** uio_MountInfo *** //
+
+uio_MountInfo *
+uio_MountInfo_new(uio_FileSystemID fsID, uio_MountTree *mountTree,
+ uio_PDirHandle *pDirHandle, char *dirName, uio_AutoMount **autoMount,
+ uio_MountHandle *mountHandle, int flags) {
+ uio_MountInfo *result;
+
+ result = uio_MountInfo_alloc();
+ result->fsID = fsID;
+ result->mountTree = mountTree;
+ result->pDirHandle = pDirHandle;
+ result->dirName = dirName;
+ result->autoMount = autoMount;
+ result->mountHandle = mountHandle;
+ result->flags = flags;
+ return result;
+}
+
+void
+uio_MountInfo_delete(uio_MountInfo *mountInfo) {
+ uio_free(mountInfo->dirName);
+ uio_PDirHandle_unref(mountInfo->pDirHandle);
+ uio_MountInfo_free(mountInfo);
+}
+
+static inline uio_MountInfo *
+uio_MountInfo_alloc(void) {
+ uio_MountInfo *result = uio_malloc(sizeof (uio_MountInfo));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountInfo, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountInfo_free(uio_MountInfo *mountInfo) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountInfo, (void *) mountInfo);
+#endif
+ uio_free(mountInfo);
+}
+
+
diff --git a/src/libs/uio/mounttree.h b/src/libs/uio/mounttree.h
new file mode 100644
index 0000000..b0041bc
--- /dev/null
+++ b/src/libs/uio/mounttree.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MOUNTTREE_H_
+#define LIBS_UIO_MOUNTTREE_H_
+
+#include <stdio.h>
+#include "mount.h"
+
+void uio_printMounts(FILE *outStream, const uio_Repository *repository);
+
+/* *** Internal definitions follow *** */
+#ifdef uio_INTERNAL
+
+#include <sys/types.h>
+
+typedef struct uio_MountTreeItem uio_MountTreeItem;
+typedef struct uio_MountTree uio_MountTree;
+typedef struct uio_MountInfo uio_MountInfo;
+
+#include "physical.h"
+#include "types.h"
+#include "uioport.h"
+#include "paths.h"
+
+
+/*
+ * A MountTree describes the relation between dirs (PRoot structures)
+ * mounted in a directory structure.
+ * A MountTree structure represents a node (a dir) in this directory
+ * structure.
+ * It describes what MountInfo structures apply in that dir and below (if
+ * not overrided in subnodes) (the 'pLocs' field).
+ * These path components are also linked together 'up'-wards (towards the
+ * root of the tree) by the 'up' field.
+ */
+
+struct uio_MountTreeItem {
+ struct uio_MountInfo *mountInfo;
+ int depth;
+ // 'mountInfo->pDirHandle' and 'depth' together point to a
+ // location in a physical tree. An uio_pDirHandle alone can't be
+ // used as the directory might not exist.
+ // So pDirHandle points to the top dir that was mounted, and
+ // 'depth' indicates how many directory names of the path to
+ // this point in the Mount Tree need to be followed.
+ // Example:
+ // This MountTreeItem is somewhere in a tree /foo/bar/bla
+ // and depth = 1. Then this MountTreeItem points to
+ // /bla in the specified root.
+ struct uio_MountTreeItem *next;
+ // The next MountTreeItem in a MountTree
+};
+
+struct uio_MountTree {
+ struct uio_MountTree *subTrees;
+ // Trees for subdirs in this MountTree
+ struct uio_MountTreeItem *pLocs;
+ // The physical locations that have effect in this MountTree.
+ struct uio_MountTree *upTree;
+ // the MountTree that pointed to this MountTree
+ struct uio_PathComp *comps;
+ // the names of the path components that lead to the tree.
+ // Not necessary every PathComp is connected to a MountTree.
+ // If you have /foo and /foo/bar/zut mounted, then
+ // there are MountTrees for /, /foo and /foo/bar/zut,
+ // but there are PathComps for 'foo', 'bar' and 'zut'.
+ struct uio_PathComp *lastComp;
+ // The last PathComp of comps that pointed to this MountTree.
+ // This can be used to trace the path back to the top.
+ struct uio_MountTree *next;
+ // If this tree is a subTree of a tree, 'next' points to the
+ // next subTree of that tree.
+};
+
+/*
+ * A MountInfo structure describes how a physical structure was mounted.
+ * A physical structure can be used by several MountInfo structures.
+ */
+struct uio_MountInfo {
+ int flags;
+ /* Mount flags */
+# define uio_MOUNTINFO_RDONLY uio_MOUNT_RDONLY
+ uio_FileSystemID fsID;
+ char *dirName;
+ /* The path inside the mounted fs leading to pDirHandle */
+ uio_PDirHandle *pDirHandle;
+ /* The pDirHandle belonging to this mount */
+ uio_MountTree *mountTree;
+ /* The MountTree node for the mountpoint */
+ uio_AutoMount **autoMount;
+ uio_MountHandle *mountHandle;
+};
+
+
+/*
+ * Say we've got mounted (in order):
+ * Bla -> /
+ * Bar -> /foo/bar
+ * Foo -> /foo
+ * Zut -> /zut/123
+ * Fop -> /zut/123
+ *
+ * This will build a tree that looks like this:
+ * (the strings between brackets are the mounted filesystems that have effect
+ * in a dir, in order)
+ *
+ * / (Bar)
+ * foo (Foo, Bla)
+ * bar (Foo, Bar, Bla)
+ * zut/123 (Fop, Zut, Bla)
+ *
+ * The MountTree will look like:
+ * / = {
+ * sub = {
+ * /foo,
+ * /zut/123
+ * },
+ * pLocs = {
+ * BlaDir:/ (0)
+ * }
+ * }
+ * /foo = {
+ * sub = {
+ * /foo/bar
+ * },
+ * pLocs = {
+ * BlaDir:/foo (1),
+ * FooDir:/ (0)
+ * }
+ * }
+ * /foo/bar = {
+ * sub = { },
+ * pLocs = {
+ * BlaDir:/foo/bar (2),
+ * BarDir:/ (0),
+ * FooDir:/bar (1)
+ * }
+ * }
+ * /zut/123 = {
+ * sub = { },
+ * pLocs = {
+ * FooDir:/ (0)
+ * ZutDir:/ (0)
+ * BlaDir:/zut/123 (2)
+ * }
+ * }
+ *
+ * 'BlaDir:/zut/123 (2)' means the pDirHandle is 'Bla', and the dir into
+ * that directory is '/zut/123', but as this is always a postfix of the
+ * path where we are, the number of dirs (the '(2)') is enough to store
+ * (apart from the pDirHandle).
+ */
+
+uio_MountTree *uio_makeRootMountTree(void);
+void uio_MountTree_delete(uio_MountTree *tree);
+uio_MountTree *uio_mountTreeAddMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo, const char *path,
+ uio_MountLocation location, const uio_MountInfo *relative);
+void uio_mountTreeRemoveMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo);
+void uio_findMountTree(uio_MountTree *top, const char *path,
+ uio_MountTree **resTree, const char **pPath);
+char *uio_mountTreeItemRestPath(const uio_MountTreeItem *item,
+ uio_PathComp *endComp, const char *path);
+int uio_mountTreeCountPLocs(const uio_MountTree *tree);
+uio_MountInfo *uio_MountInfo_new(uio_FileSystemID fsID,
+ uio_MountTree *mountTree, uio_PDirHandle *pDirHandle,
+ char *dirName, uio_AutoMount **autoMount,
+ uio_MountHandle *mountHandle, int flags);
+void uio_MountInfo_delete(uio_MountInfo *mountInfo);
+void uio_printMountTree(FILE *outStream, const uio_MountTree *tree,
+ int indent);
+void uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item);
+void uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item);
+void uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree);
+void uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo);
+
+static inline uio_bool
+uio_mountInfoIsReadOnly(uio_MountInfo *mountInfo) {
+ return (mountInfo->flags & uio_MOUNTINFO_RDONLY) != 0;
+}
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_MOUNTTREE_H_ */
+
diff --git a/src/libs/uio/paths.c b/src/libs/uio/paths.c
new file mode 100644
index 0000000..f8411cd
--- /dev/null
+++ b/src/libs/uio/paths.c
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "paths.h"
+#include "uioport.h"
+#include "mem.h"
+
+static inline uio_PathComp *uio_PathComp_alloc(void);
+static inline void uio_PathComp_free(uio_PathComp *pathComp);
+
+// gets the first dir component of a path
+// sets '*start' to the start of the first component
+// sets '*end' to the end of the first component
+// if *start >= dirEnd, then the end has been reached.
+void
+getFirstPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp) {
+ assert(dir <= dirEnd);
+ *startComp = dir;
+ if (*startComp == dirEnd) {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = memchr(*startComp, '/', dirEnd - *startComp);
+ if (*endComp == NULL)
+ *endComp = dirEnd;
+}
+
+// gets the first dir component of a path
+// sets '*start' to the start of the first component
+// sets '*end' to the end of the first component
+// if **start == '\0', then the end has been reached.
+void
+getFirstPath0Component(const char *dir,
+ const char **startComp, const char **endComp) {
+ *startComp = dir;
+ if (**startComp == '\0') {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = strchr(*startComp, '/');
+ if (*endComp == NULL)
+ *endComp = *startComp + strlen(*startComp);
+}
+
+// gets the next component of a path
+// '*start' should be set to the start of the last component
+// '*end' should be set to the end of the last component
+// '*start' will be set to the start of the next component
+// '*end' will be set to the end of the next component
+// if *start >= dirEnd, then the end has been reached.
+void
+getNextPathComponent(const char *dirEnd,
+ const char **startComp, const char **endComp) {
+ assert(*endComp <= dirEnd);
+ if (*endComp == dirEnd) {
+ *startComp = *endComp;
+ return;
+ }
+ assert(**endComp == '/');
+ *startComp = *endComp + 1;
+ *endComp = memchr(*startComp, '/', dirEnd - *startComp);
+ if (*endComp == NULL)
+ *endComp = dirEnd;
+}
+
+// gets the next component of a path
+// '*start' should be set to the start of the last component
+// '*end' should be set to the end of the last component
+// '*start' will be set to the start of the next component
+// '*end' will be set to the end of the next component
+// if **start == '\0', then the end has been reached.
+void
+getNextPath0Component(const char **startComp, const char **endComp) {
+ if (**endComp == '\0') {
+ *startComp = *endComp;
+ return;
+ }
+ assert(**endComp == '/');
+ *startComp = *endComp + 1;
+ *endComp = strchr(*startComp, '/');
+ if (*endComp == NULL)
+ *endComp = *startComp + strlen(*startComp);
+}
+
+// if *end == dir, the beginning has been reached
+void
+getLastPathComponent(const char *dir, const char *endDir,
+ const char **startComp, const char **endComp) {
+ *endComp = endDir;
+// if (*(*endComp - 1) == '/')
+// (*endComp)--;
+ *startComp = *endComp;
+ // TODO: use memrchr where available
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// if *end == dir, the beginning has been reached
+// pre: dir is \0-terminated
+void
+getLastPath0Component(const char *dir,
+ const char **startComp, const char **endComp) {
+ *endComp = dir + strlen(dir);
+// if (*(*endComp - 1) == '/')
+// (*endComp)--;
+ *startComp = *endComp;
+ // TODO: use memrchr where available
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// if *end == dir, the beginning has been reached
+void
+getPreviousPathComponent(const char *dir,
+ const char **startComp, const char **endComp) {
+ if (*startComp == dir) {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = *startComp - 1;
+ *startComp = *endComp;
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// Combine two parts of a paths into a new path.
+// The new path starts with a '/' only when the first part does.
+// The first path may (but doesn't have to) end on a '/', or may be empty.
+// Pre: the second path doesn't start with a '/'
+char *
+joinPaths(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ if (first[0] == '\0')
+ return uio_strdup(second);
+
+ firstLen = strlen(first);
+ if (first[firstLen - 1] == '/')
+ firstLen--;
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 2);
+ resPtr = result;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// Combine two parts of a paths into a new path.
+// The new path will always start with a '/'.
+// The first path may (but doesn't have to) end on a '/', or may be empty.
+// Pre: the second path doesn't start with a '/'
+char *
+joinPathsAbsolute(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ if (first[0] == '\0') {
+ secondLen = strlen(second);
+ result = uio_malloc(secondLen + 2);
+ result[0] = '/';
+ memcpy(&result[1], second, secondLen);
+ result[secondLen + 1] = '\0';
+ return result;
+ }
+
+ firstLen = strlen(first);
+ if (first[firstLen - 1] == '/')
+ firstLen--;
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 3);
+ resPtr = result;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// Returns 'false' if
+// - one of the path components is empty, or
+// - one of the path components is ".", or
+// - one of the path components is ".."
+// and 'true' otherwise.
+uio_bool
+validPathName(const char *path, size_t len) {
+ const char *pathEnd;
+ const char *start, *end;
+
+ pathEnd = path + len;
+ getFirstPathComponent(path, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.'))
+ return false;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ return true;
+}
+
+// returns 0 if the path is not a valid UNC path.
+// Does not skip trailing slashes.
+size_t
+uio_skipUNCServerShare(const char *inPath) {
+ const char *path = inPath;
+
+ // Skip the initial two backslashes.
+ if (path[0] != '\\' || path[1] != '\\')
+ return (size_t) 0;
+ path += 2;
+
+ // Skip the server part.
+ while (*path != '\\' && *path != '/') {
+ if (*path == '\0')
+ return (size_t) 0;
+ path++;
+ }
+
+ // Skip the seperator.
+ path++;
+
+ // Skip the share part.
+ while (*path != '\0' && *path != '\\' && *path != '/')
+ path++;
+
+ return (size_t) (path - inPath);
+}
+
+/**
+ * Find the server and share part of a Windows "Universal Naming Convention"
+ * path (a path of the form '\\server\share\path\file').
+ * The path must contain at least a server and share path to be valid.
+ * The initial two slashes must be backslashes, the other slashes may each
+ * be either a forward slash or a backslash.
+ *
+ * @param[in] inPath The path to parse.
+ * @param[out] outPath Will contain a newly allocated string (to be
+ * freed using uio_free(), containing the server and share part
+ * of inPath, separated by a backslash, or NULL if 'inPath' was
+ * not a valid UNC path.
+ * @param[out] outLen If not NULL on entry, it will contain the string
+ * length of '*outPath', or 0 if 'inPath' was not a valid UNC path.
+ *
+ * @returns The number of characters to add to 'inPath' to get to the first
+ * path component past the server and share part, or 0 if 'inPath'
+ * was not a valid UNC path.
+ */
+size_t
+uio_getUNCServerShare(const char *inPath, char **outPath, size_t *outLen) {
+ const char *ptr;
+ const char *server;
+ const char *serverEnd;
+ const char *share;
+ const char *shareEnd;
+ char *name;
+ char *nameEnd;
+ size_t nameLen;
+
+ ptr = inPath;
+
+ if (ptr[0] != '\\' || ptr[1] != '\\')
+ goto noMatch;
+
+ // Parse the server part.
+ server = ptr + 2;
+ serverEnd = server;
+ for (;;) {
+ if (*serverEnd == '\0')
+ goto noMatch;
+ if (isPathDelimiter(*serverEnd))
+ break;
+ serverEnd++;
+ }
+ if (serverEnd == server)
+ goto noMatch;
+
+ // Parse the share part.
+ share = serverEnd + 1;
+ shareEnd = share;
+ while (*shareEnd != '\0') {
+ if (isPathDelimiter(*shareEnd))
+ break;
+ serverEnd++;
+ }
+
+ // Skip any trailing path delimiters.
+ ptr = shareEnd;
+ while (isPathDelimiter(*ptr))
+ ptr++;
+
+ // Allocate a new string and fill it.
+ nameLen = (serverEnd - server) + (shareEnd - share) + 3;
+ name = uio_malloc(nameLen + 1);
+ nameEnd = name;
+ *(nameEnd++) = '\\';
+ *(nameEnd++) = '\\';
+ memcpy(nameEnd, server, serverEnd - server);
+ *(nameEnd++) = '\\';
+ memcpy(nameEnd, share, shareEnd - share);
+ *nameEnd = '\0';
+
+ *outPath = name;
+ if (outLen != NULL)
+ *outLen = nameLen;
+ return (size_t) (ptr - inPath);
+
+noMatch:
+ *outPath = NULL;
+ if (outLen != NULL)
+ *outLen = 0;
+ return (size_t) 0;
+}
+
+// Decomposes a path into its components.
+// If isAbsolute is not NULL, *isAbsolute will be set to true
+// iff the path is absolute.
+// As POSIX considers multiple consecutive slashes to be equivalent to
+// a single slash, so will uio (but not in the "\\MACHINE\share" part
+// of a Windows UNC path).
+int
+decomposePath(const char *path, uio_PathComp **pathComp,
+ uio_bool *isAbsolute) {
+ uio_PathComp *result;
+ uio_PathComp *last;
+ uio_PathComp **endResult = &result;
+ uio_bool absolute = false;
+ char *name;
+#ifdef HAVE_UNC_PATHS
+ size_t nameLen;
+#endif /* HAVE_UNC_PATHS */
+
+ if (path[0] == '\0') {
+ errno = ENOENT;
+ return -1;
+ }
+
+ last = NULL;
+#ifdef HAVE_UNC_PATHS
+ path += uio_getUNCServerShare(path, &name, &nameLen);
+ if (name != NULL) {
+ // UNC path
+ *endResult = uio_PathComp_new(name, nameLen, last);
+ last = *endResult;
+ endResult = &last->next;
+
+ absolute = true;
+ } else
+#endif /* HAVE_UNC_PATHS */
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(path[0]) && path[1] == ':') {
+ // DOS/Windows drive letter.
+ if (path[2] != '\0' && !isPathDelimiter(path[2])) {
+ errno = ENOENT;
+ return -1;
+ }
+ name = uio_memdup0(path, 2);
+ *endResult = uio_PathComp_new(name, 2, last);
+ last = *endResult;
+ endResult = &last->next;
+ absolute = true;
+ } else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ if (isPathDelimiter(*path)) {
+ absolute = true;
+ do {
+ path++;
+ } while (isPathDelimiter(*path));
+ }
+ }
+
+ while (*path != '\0') {
+ const char *start = path;
+ while (*path != '\0' && !isPathDelimiter(*path))
+ path++;
+
+ name = uio_memdup0(path, path - start);
+ *endResult = uio_PathComp_new(name, path - start, last);
+ last = *endResult;
+ endResult = &last->next;
+
+ while (isPathDelimiter(*path))
+ path++;
+ }
+
+ *endResult = NULL;
+ *pathComp = result;
+ if (isAbsolute != NULL)
+ *isAbsolute = absolute;
+ return 0;
+}
+
+// Pre: pathComp forms a valid path for the platform.
+void
+composePath(const uio_PathComp *pathComp, uio_bool absolute,
+ char **path, size_t *pathLen) {
+ size_t len;
+ const uio_PathComp *ptr;
+ char *result;
+ char *pathPtr;
+
+ assert(pathComp != NULL);
+
+ // First determine how much space is required.
+ len = 0;
+ if (absolute)
+ len++;
+ ptr = pathComp;
+ while (ptr != NULL) {
+ len += ptr->nameLen;
+ ptr = ptr->next;
+ }
+
+ // Allocate the required space.
+ result = (char *) uio_malloc(len + 1);
+
+ // Fill the path.
+ pathPtr = result;
+ ptr = pathComp;
+ if (absolute) {
+#ifdef HAVE_UNC_PATHS
+ if (ptr->name[0] == '\\') {
+ // UNC path
+ assert(ptr->name[1] == '\\');
+ // Nothing to do.
+ } else
+#endif /* HAVE_UNC_PATHS */
+#ifdef HAVE_DRIVE_LETTERS
+ if (ptr->nameLen == 2 && ptr->name[1] == ':'
+ && isDriveLetter(ptr->name[0])) {
+ // Nothing to do.
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ *(pathPtr++) = '/';
+ }
+ }
+
+ for (;;) {
+ memcpy(pathPtr, ptr->name, ptr->nameLen);
+ pathPtr += ptr->nameLen;
+
+ ptr = ptr->next;
+ if (ptr == NULL)
+ break;
+
+ *(pathPtr++) = '/';
+ }
+
+ *path = result;
+ *pathLen = len;
+}
+
+
+// *** uio_PathComp *** //
+
+static inline uio_PathComp *
+uio_PathComp_alloc(void) {
+ uio_PathComp *result = uio_malloc(sizeof (uio_PathComp));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PathComp, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PathComp_free(uio_PathComp *pathComp) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PathComp, (void *) pathComp);
+#endif
+ uio_free(pathComp);
+}
+
+// 'name' should be a null terminated string. It is stored in the PathComp,
+// no copy is made.
+// 'namelen' should be the length of 'name'
+uio_PathComp *
+uio_PathComp_new(char *name, size_t nameLen, uio_PathComp *upComp) {
+ uio_PathComp *result;
+
+ result = uio_PathComp_alloc();
+ result->name = name;
+ result->nameLen = nameLen;
+ result->up = upComp;
+ return result;
+}
+
+void
+uio_PathComp_delete(uio_PathComp *pathComp) {
+ uio_PathComp *next;
+
+ while (pathComp != NULL) {
+ next = pathComp->next;
+ uio_free(pathComp->name);
+ uio_PathComp_free(pathComp);
+ pathComp = next;
+ }
+}
+
+// Count the number of path components that 'comp' leads to.
+int
+uio_countPathComps(const uio_PathComp *comp) {
+ int count;
+
+ count = 0;
+ for (; comp != NULL; comp = comp->next)
+ count++;
+ return count;
+}
+
+uio_PathComp *
+uio_lastPathComp(uio_PathComp *comp) {
+ if (comp == NULL)
+ return NULL;
+
+ while (comp->next != NULL)
+ comp = comp->next;
+ return comp;
+}
+
+// make a list of uio_PathComps from a path string
+uio_PathComp *
+uio_makePathComps(const char *path, uio_PathComp *upComp) {
+ const char *start, *end;
+ char *str;
+ uio_PathComp *result;
+ uio_PathComp **compPtr; // Where to put the next PathComp
+
+ compPtr = &result;
+ getFirstPath0Component(path, &start, &end);
+ while (*start != '\0') {
+ str = uio_malloc(end - start + 1);
+ memcpy(str, start, end - start);
+ str[end - start] = '\0';
+
+ *compPtr = uio_PathComp_new(str, end - start, upComp);
+ upComp = *compPtr;
+ compPtr = &(*compPtr)->next;
+ getNextPath0Component(&start, &end);
+ }
+ *compPtr = NULL;
+ return result;
+}
+
+void
+uio_printPathComp(FILE *outStream, const uio_PathComp *comp) {
+ fprintf(outStream, "%s", comp->name);
+}
+
+void
+uio_printPathToComp(FILE *outStream, const uio_PathComp *comp) {
+ if (comp == NULL)
+ return;
+ uio_printPathToComp(outStream, comp->up);
+ fprintf(outStream, "/");
+ uio_printPathComp(outStream, comp);
+}
+
+
diff --git a/src/libs/uio/paths.h b/src/libs/uio/paths.h
new file mode 100644
index 0000000..bb5090d
--- /dev/null
+++ b/src/libs/uio/paths.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_PATHS_H_
+#define LIBS_UIO_PATHS_H_
+
+typedef struct uio_PathComp uio_PathComp;
+
+#include "types.h"
+#include "uioport.h"
+
+#include <stdio.h>
+
+struct uio_PathComp {
+ char *name;
+ // The name of this path component, 0-terminated
+ size_t nameLen;
+ // The length of the 'name' field, for fast lookups.
+ struct uio_PathComp *next;
+ // Next component in the path.
+ struct uio_PathComp *up;
+ // Previous component in the path.
+};
+
+void getFirstPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getFirstPath0Component(const char *dir, const char **startComp,
+ const char **endComp);
+void getNextPathComponent(const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getNextPath0Component(const char **startComp, const char **endComp);
+void getLastPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getLastPath0Component(const char *dir, const char **startComp,
+ const char **endComp);
+void getPreviousPathComponent(const char *dir, const char **startComp,
+ const char **endComp);
+#define getPreviousPath0Component getPreviousPathComponent
+char *joinPaths(const char *first, const char *second);
+char *joinPathsAbsolute(const char *first, const char *second);
+
+uio_bool validPathName(const char *path, size_t len);
+size_t uio_skipUNCServerShare(const char *inPath);
+size_t uio_getUNCServerShare(const char *inPath, char **outPath,
+ size_t *outLen);
+
+#ifdef HAVE_DRIVE_LETTERS
+static inline int
+isDriveLetter(int c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+#endif /* HAVE_DRIVE_LETTERS */
+
+static inline int
+isPathDelimiter(int c)
+{
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ return c == '/' || c == '\\';
+#else
+ return c == '/';
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+}
+
+int decomposePath(const char *path, uio_PathComp **pathComp,
+ uio_bool *isAbsolute);
+void composePath(const uio_PathComp *pathComp, uio_bool absolute,
+ char **path, size_t *pathLen);
+uio_PathComp *uio_PathComp_new(char *name, size_t nameLen,
+ uio_PathComp *upComp);
+void uio_PathComp_delete(uio_PathComp *pathComp);
+int uio_countPathComps(const uio_PathComp *comp);
+uio_PathComp *uio_lastPathComp(uio_PathComp *comp);
+uio_PathComp *uio_makePathComps(const char *path, uio_PathComp *upComp);
+void uio_printPathComp(FILE *outStream, const uio_PathComp *comp);
+void uio_printPathToComp(FILE *outStream, const uio_PathComp *comp);
+
+#endif /* LIBS_UIO_PATHS_H_ */
+
diff --git a/src/libs/uio/physical.c b/src/libs/uio/physical.c
new file mode 100644
index 0000000..18f96d1
--- /dev/null
+++ b/src/libs/uio/physical.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "physical.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+#include "uioport.h"
+
+static inline uio_PRoot *uio_PRoot_alloc(void);
+static inline void uio_PRoot_free(uio_PRoot *pRoot);
+
+// NB: ref counter is not incremented
+uio_PDirHandle *
+uio_PRoot_getRootDirHandle(uio_PRoot *pRoot) {
+ return pRoot->rootDir;
+}
+
+void
+uio_PRoot_deletePRootExtra(uio_PRoot *pRoot) {
+ if (pRoot->extra == NULL)
+ return;
+ assert(pRoot->handler->deletePRootExtra != NULL);
+ pRoot->handler->deletePRootExtra(pRoot->extra);
+}
+
+// note: sets refMount count to 1
+// set handlerRef count to 0
+uio_PRoot *
+uio_PRoot_new(uio_PDirHandle *topDirHandle,
+ uio_FileSystemHandler *handler, uio_Handle *handle,
+ uio_PRootExtra extra, int flags) {
+ uio_PRoot *pRoot;
+
+ pRoot = uio_PRoot_alloc();
+ pRoot->mountRef = 1;
+ pRoot->handleRef = 0;
+ pRoot->rootDir = topDirHandle;
+ pRoot->handler = handler;
+ pRoot->handle = handle;
+ pRoot->extra = extra;
+ pRoot->flags = flags;
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ pRoot->numCloseHandlers = 0;
+ pRoot->closeHandlers = NULL;
+#endif
+
+ return pRoot;
+}
+
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+// Closehandlers code disabled.
+// It was only meant for internal use, but I don't need it any more.
+// Keeping it around for a while until I'm confident I won't need it in the
+// future.
+
+void
+uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *), void *arg) {
+ pRoot->numCloseHandlers++;
+ pRoot->closeHandlers = uio_realloc(pRoot->closeHandlers,
+ pRoot->numCloseHandlers * sizeof (uio_PRoot_CloseHandler));
+ pRoot->closeHandlers[pRoot->numCloseHandlers - 1].fun = fun;
+ pRoot->closeHandlers[pRoot->numCloseHandlers - 1].arg = arg;
+}
+
+void
+uio_PRoot_callCloseHandlers(uio_PRoot *pRoot) {
+ int i;
+
+ i = pRoot->numCloseHandlers;
+ while (i--) {
+ uio_PRoot_CloseHandler *closeHandler;
+
+ closeHandler = &pRoot->closeHandlers[i];
+ (closeHandler->fun)(closeHandler->arg);
+ }
+}
+
+void
+uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot) {
+ pRoot->numCloseHandlers = 0;
+ if (pRoot->closeHandlers != NULL)
+ uio_free(pRoot->closeHandlers);
+ pRoot->closeHandlers = NULL;
+}
+#endif
+
+static inline void
+uio_PRoot_delete(uio_PRoot *pRoot) {
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ uio_PRoot_callCloseHandlers(pRoot);
+ uio_PRoot_removeCloseHandlers(pRoot);
+#endif
+ assert(pRoot->handler->umount != NULL);
+ pRoot->handler->umount(pRoot);
+ if (pRoot->handle)
+ uio_Handle_unref(pRoot->handle);
+ uio_PRoot_deletePRootExtra(pRoot);
+ uio_PRoot_free(pRoot);
+}
+
+static inline uio_PRoot *
+uio_PRoot_alloc(void) {
+ uio_PRoot *result = uio_malloc(sizeof (uio_PRoot));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PRoot, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PRoot_free(uio_PRoot *pRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PRoot, (void *) pRoot);
+#endif
+ uio_free(pRoot);
+}
+
+void
+uio_PRoot_refHandle(uio_PRoot *pRoot) {
+ pRoot->handleRef++;
+}
+
+void
+uio_PRoot_unrefHandle(uio_PRoot *pRoot) {
+ assert(pRoot->handleRef > 0);
+ pRoot->handleRef--;
+ if (pRoot->handleRef == 0 && pRoot->mountRef == 0)
+ uio_PRoot_delete(pRoot);
+}
+
+void
+uio_PRoot_refMount(uio_PRoot *pRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_PRoot, (void *) pRoot);
+#endif
+ pRoot->mountRef++;
+}
+
+void
+uio_PRoot_unrefMount(uio_PRoot *pRoot) {
+ assert(pRoot->mountRef > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_PRoot, (void *) pRoot);
+#endif
+ pRoot->mountRef--;
+ if (pRoot->mountRef == 0 && pRoot->handleRef == 0)
+ uio_PRoot_delete(pRoot);
+}
+
+
diff --git a/src/libs/uio/physical.h b/src/libs/uio/physical.h
new file mode 100644
index 0000000..71bc8e6
--- /dev/null
+++ b/src/libs/uio/physical.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_PHYSICAL_H_
+#define LIBS_UIO_PHYSICAL_H_
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_PRootExtra;
+typedef void *uio_NativeEntriesContext;
+#endif
+
+// 'forward' declarations
+typedef struct uio_PRoot uio_PRoot;
+typedef struct uio_PRoot_CloseHandler uio_PRoot_CloseHandler;
+
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+
+
+/*
+ * Represents the root of a physical directory structure.
+ */
+struct uio_PRoot {
+ int mountRef;
+ /* Number of times this structure is referenced from
+ * mount trees. */
+ int handleRef;
+ /* Number of file or directory handles that point inside the
+ * physical directory strucure of this pRoot.
+ */
+ struct uio_PDirHandle *rootDir;
+ struct uio_FileSystemHandler *handler;
+ /* How to handle files in this PRoot tree */
+ int flags;
+# define uio_PRoot_NOCACHE 0x0002
+ struct uio_Handle *handle;
+ /* The handle through which this PRoot is opened,
+ * this is NULL for the top PRoot */
+ // TODO: move this to extra?
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ int numCloseHandlers;
+ uio_PRoot_CloseHandler *closeHandlers;
+#endif
+ uio_PRootExtra extra;
+ /* extra internal data for some filesystem types */
+};
+
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+struct uio_PRoot_CloseHandler {
+ void (*fun)(void *);
+ void *arg;
+};
+#endif
+
+void uio_PRoot_deletePRootExtra(uio_PRoot *pRoot);
+uio_PRoot *uio_PRoot_new(uio_PDirHandle *topDirHandle,
+ uio_FileSystemHandler *handler, uio_Handle *handle,
+ uio_PRootExtra extra, int flags);
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+void uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *),
+ void *arg);
+void uio_PRoot_callCloseHandlers(uio_PRoot *pRoot);
+void uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot);
+#endif
+uio_PDirHandle *uio_PRoot_getRootDirHandle(uio_PRoot *pRoot);
+void uio_PRoot_refHandle(uio_PRoot *pRoot);
+void uio_PRoot_unrefHandle(uio_PRoot *pRoot);
+void uio_PRoot_refMount(uio_PRoot *pRoot);
+void uio_PRoot_unrefMount(uio_PRoot *pRoot);
+
+#endif /* LIBS_UIO_PHYSICAL_H_ */
+
+
diff --git a/src/libs/uio/stdio/Makeinfo b/src/libs/uio/stdio/Makeinfo
new file mode 100644
index 0000000..2d99c66
--- /dev/null
+++ b/src/libs/uio/stdio/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="stdio.c"
+uqm_HFILES="stdio.h"
diff --git a/src/libs/uio/stdio/stdio.c b/src/libs/uio/stdio/stdio.c
new file mode 100644
index 0000000..a4421d1
--- /dev/null
+++ b/src/libs/uio/stdio/stdio.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The GPDir structures and functions are used for caching only.
+
+#ifdef __svr4__
+# define _POSIX_PTHREAD_SEMANTICS
+ // For the POSIX variant of readdir_r()
+#endif
+
+#include "./stdio.h"
+
+#ifdef WIN32
+# include <io.h>
+#else
+# include <sys/stat.h>
+# include <unistd.h>
+# include <dirent.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "../uioport.h"
+#include "../paths.h"
+#include "../mem.h"
+#include "../physical.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+static inline uio_GPFile *stdio_addFile(uio_GPDir *gPDir,
+ const char *fileName);
+static inline uio_GPDir *stdio_addDir(uio_GPDir *gPDir, const char *dirName);
+static char *stdio_getPath(uio_GPDir *gPDir);
+static stdio_GPDirData *stdio_GPDirData_new(char *name, char *cachedPath,
+ uio_GPDir *upDir);
+static void stdio_GPDirData_delete(stdio_GPDirData *gPDirData);
+static inline stdio_GPDirData *stdio_GPDirData_alloc(void);
+static inline void stdio_GPDirData_free(stdio_GPDirData *gPDirData);
+static inline stdio_EntriesIterator *stdio_EntriesIterator_alloc(void);
+static inline void stdio_EntriesIterator_free(
+ stdio_EntriesIterator *iterator);
+
+uio_FileSystemHandler stdio_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ stdio_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ stdio_access,
+ /* .close = */ stdio_close,
+ /* .fstat = */ stdio_fstat,
+ /* .stat = */ stdio_stat,
+ /* .mkdir = */ stdio_mkdir,
+ /* .open = */ stdio_open,
+ /* .read = */ stdio_read,
+ /* .rename = */ stdio_rename,
+ /* .rmdir = */ stdio_rmdir,
+ /* .seek = */ stdio_seek,
+ /* .write = */ stdio_write,
+ /* .unlink = */ stdio_unlink,
+
+ /* .openEntries = */ stdio_openEntries,
+ /* .readEntries = */ stdio_readEntries,
+ /* .closeEntries = */ stdio_closeEntries,
+
+ /* .getPDirEntryHandle = */ stdio_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations stdio_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ stdio_GPDirData_delete,
+ /* .deleteGPFileExtra = */ NULL,
+};
+
+
+void
+stdio_close(uio_Handle *handle) {
+ int fd;
+ int result;
+
+ fd = handle->native->fd;
+ uio_free(handle->native);
+
+ while (1) {
+ result = close(fd);
+ if (result == 0)
+ break;
+ if (errno != EINTR) {
+ fprintf(stderr, "Warning: Error while closing socket: %s\n",
+ strerror(errno));
+ break;
+ }
+ }
+}
+
+int
+stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = access(path, mode);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+int
+stdio_fstat(uio_Handle *handle, struct stat *statBuf) {
+ return fstat(handle->native->fd, statBuf);
+}
+
+int
+stdio_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = stat(path, statBuf);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+uio_PDirHandle *
+stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name, mode_t mode) {
+ char *path;
+ uio_GPDir *newGPDir;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (MKDIR(path, mode) == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ uio_free(path);
+
+ newGPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(newGPDir);
+ return uio_PDirHandle_new(pDirHandle->pRoot, newGPDir);
+}
+
+/*
+ * Function name: stdio_open
+ * Description: open a file from a normal stdio environment
+ * Arguments: gPDir - the dir where to open the file
+ * file - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, for use in functions accessing the opened file.
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode) {
+ stdio_Handle *handle;
+ char *path;
+ int fd;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), file);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ fd = open(path, flags, mode);
+ if (fd == -1) {
+ int save_errno;
+
+ save_errno = errno;
+ uio_free(path);
+ errno = save_errno;
+ return NULL;
+ }
+ uio_free(path);
+
+#if 0
+ if (flags & O_CREAT) {
+ if (uio_GPDir_getGPDirEntry(pDirHandle->extra, file) == NULL)
+ stdio_addFile(pDirHandle->extra, file);
+ }
+#endif
+
+ handle = uio_malloc(sizeof (stdio_Handle));
+ handle->fd = fd;
+
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+stdio_read(uio_Handle *handle, void *buf, size_t count) {
+ return read(handle->native->fd, buf, count);
+}
+
+int
+stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName,
+ uio_PDirHandle *newPDirHandle, const char *newName) {
+ char *newPath, *oldPath;
+ int result;
+
+ oldPath = joinPaths(stdio_getPath(oldPDirHandle->extra), oldName);
+ if (oldPath == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ newPath = joinPaths(stdio_getPath(newPDirHandle->extra), newName);
+ if (newPath == NULL) {
+ // errno is set
+ uio_free(oldPath);
+ return -1;
+ }
+
+ result = rename(oldPath, newPath);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(oldPath);
+ uio_free(newPath);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(oldPath);
+ uio_free(newPath);
+
+ {
+ // update the GPDir structure
+ uio_GPDirEntry *entry;
+
+ // TODO: add locking
+ entry = uio_GPDir_getGPDirEntry(oldPDirHandle->extra, oldName);
+ if (entry != NULL) {
+ uio_GPDirEntries_remove(oldPDirHandle->extra->entries, oldName);
+ uio_GPDirEntries_add(newPDirHandle->extra->entries, newName,
+ entry);
+ }
+ }
+
+ return result;
+}
+
+int
+stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = rmdir(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeSubDir(pDirHandle->extra, name);
+
+ return result;
+}
+
+off_t
+stdio_seek(uio_Handle *handle, off_t offset, int whence) {
+ return lseek(handle->native->fd, offset, whence);
+}
+
+ssize_t
+stdio_write(uio_Handle *handle, const void *buf, size_t count) {
+ return write(handle->native->fd, buf, count);
+}
+
+int
+stdio_unlink(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = unlink(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeFile(pDirHandle->extra, name);
+
+ return result;
+}
+
+uio_PDirEntryHandle *
+stdio_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, const char *name) {
+ uio_PDirEntryHandle *result;
+ const char *pathUpTo;
+ char *path;
+ struct stat statBuf;
+#ifdef HAVE_DRIVE_LETTERS
+ char driveName[3];
+#endif /* HAVE_DRIVE_LETTERS */
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Top dir. Contains only drive letters and UNC \\server\share
+ // parts.
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(name[0]) && name[1] == ':' && name[2] == '\0') {
+ driveName[0] = tolower(name[0]);
+ driveName[1] = ':';
+ driveName[2] = '\0';
+ name = driveName;
+ } else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ {
+ size_t uncLen;
+
+ uncLen = uio_skipUNCServerShare(name);
+ if (name[uncLen] != '\0') {
+ // 'name' contains neither a drive letter, nor the
+ // first part of a UNC path.
+ return NULL;
+ }
+ }
+#else /* !defined(HAVE_UNC_PATHS) */
+ {
+ // Make sure that there is an 'else' case if HAVE_DRIVE_LETTERS
+ // is defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ result = uio_GPDir_getPDirEntryHandle(pDirHandle, name);
+ if (result != NULL)
+ return result;
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Need to create a 'directory' for the drive letter or UNC
+ // "\\server\share" part.
+ // It's no problem if we happen to create a dir for a non-existing
+ // drive. It should just produce an empty dir.
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ pathUpTo = stdio_getPath(pDirHandle->extra);
+ if (pathUpTo == NULL) {
+ // errno is set
+ return NULL;
+ }
+ path = joinPaths(pathUpTo, name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (stat(path, &statBuf) == -1) {
+#ifdef __SYMBIAN32__
+ // XXX: HACK: If we don't have access to a directory, we can still
+ // have access to the underlying entries. We don't actually know
+ // whether the entry is a directory, but I know of no way to find
+ // out. We just pretend that it is; worst case, a file which we can't
+ // access shows up as a directory which we can't access.
+ if (errno == EACCES) {
+ statBuf.st_mode = S_IFDIR;
+ // Fake a directory; the other fields of the stat
+ // structure are unused.
+ } else
+#endif
+ {
+ // errno is set.
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+ uio_free(path);
+
+ if (S_ISREG(statBuf.st_mode)) {
+ uio_GPFile *gPFile;
+
+ gPFile = stdio_addFile(pDirHandle->extra, name);
+ uio_GPFile_ref(gPFile);
+ return (uio_PDirEntryHandle *) uio_PFileHandle_new(
+ pDirHandle->pRoot, gPFile);
+ } else if (S_ISDIR(statBuf.st_mode)) {
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Attempt to access '%s' from '%s', "
+ "which is not a regular file, nor a directory.\n", name,
+ pathUpTo);
+#endif
+ return NULL;
+ }
+}
+
+uio_PRoot *
+stdio_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ stdio_GPDirData *extra;
+
+ assert (handle == NULL);
+ extra = stdio_GPDirData_new(
+ uio_strdup("") /* name */,
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Full paths start with a drive letter or \\server\share
+ uio_strdup("") /* cached path */,
+#else
+ uio_strdup("/") /* cached path */,
+#endif /* HAVE_DRIVE_LETTERS */
+ NULL /* parent dir */);
+
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_STDIO), flags,
+ &stdio_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, extra, 0);
+
+ uio_GPDir_setComplete(result->rootDir->extra, true);
+
+ return result;
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ char path[PATH_MAX];
+ char *pathEnd;
+ size_t dirPathLen;
+ stdio_EntriesIterator *iterator;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirPathLen = strlen(dirPath);
+ if (dirPathLen > PATH_MAX - 3) {
+ // dirPath ++ '/' ++ '*' ++ '\0'
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ memcpy(path, dirPath, dirPathLen);
+ pathEnd = path + dirPathLen;
+ pathEnd[0] = '/';
+ pathEnd[1] = '*';
+ pathEnd[2] = '\0';
+ iterator = stdio_EntriesIterator_new(0);
+ iterator->dirHandle = _findfirst(path, &iterator->findData);
+ if (iterator->dirHandle == 1) {
+ if (errno != ENOENT) {
+ stdio_EntriesIterator_delete(iterator);
+ return NULL;
+ }
+ iterator->status = 1;
+ } else
+ iterator->status = 0;
+ return iterator;
+}
+#endif
+
+#ifndef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ DIR *dirHandle;
+ stdio_EntriesIterator *result;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirHandle = opendir(dirPath);
+ if (dirHandle == NULL) {
+ // errno is set;
+ return NULL;
+ }
+
+ result = stdio_EntriesIterator_new(dirHandle);
+ result->status = readdir_r(dirHandle, result->direntBuffer,
+ &result->entry);
+#ifndef WIN32
+# ifdef DEBUG
+ if (result->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(result->status));
+ }
+# endif
+#endif
+ return result;
+}
+#endif
+
+// the start of 'buf' will be filled with pointers to strings
+// those strings are stored elsewhere in buf.
+// The function returns the number of strings passed along, or -1 for error.
+// If there are no more entries, the last pointer will be NULL.
+// (this pointer counts towards the return value)
+int
+stdio_readEntries(stdio_EntriesIterator **iteratorPtr,
+ char *buf, size_t len) {
+ char *end;
+ char **start;
+ int num;
+ const char *name;
+ size_t nameLen;
+ stdio_EntriesIterator *iterator;
+
+ iterator = *iteratorPtr;
+
+ // buf will be filled like this:
+ // The start of buf will contain pointers to char *,
+ // the end will contain the actual char[] that those pointers point to.
+ // The start and the end will grow towards eachother.
+ start = (char **) buf;
+ end = buf + len;
+ num = 0;
+#ifdef WIN32
+ for (; iterator->status == 0;
+ iterator->status = _findnext(iterator->dirHandle,
+ &iterator->findData))
+#else
+ for (; iterator->status == 0 && iterator->entry != NULL;
+ iterator->status = readdir_r(iterator->dirHandle,
+ iterator->direntBuffer, &iterator->entry))
+#endif
+ {
+#ifdef WIN32
+ name = iterator->findData.name;
+#else
+ name = iterator->entry->d_name;
+#endif
+ if (name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0'))) {
+ // skip directories "." and ".."
+ continue;
+ }
+ nameLen = strlen(name) + 1;
+
+ // Does this work with systems that need memory access to be
+ // aligned on a certain number of bytes?
+ if ((size_t) (sizeof (char *) + nameLen) >
+ (size_t) (end - (char *) start)) {
+ // Not enough room to fit the pointer (at the beginning) and
+ // the string (at the end).
+ return num;
+ }
+ end -= nameLen;
+ memcpy(end, name, nameLen);
+ *start = end;
+ start++;
+ num++;
+ }
+#ifndef WIN32
+# ifdef DEBUG
+ if (iterator->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(iterator->status));
+ }
+# endif
+#endif
+ if (sizeof (char *) > (size_t) (end - (char *) start)) {
+ // not enough room to fit the NULL pointer.
+ // It will have to be reported seperately the next time.
+ return num;
+ }
+ *start = NULL;
+ num++;
+ return num;
+}
+
+void
+stdio_closeEntries(stdio_EntriesIterator *iterator) {
+#ifdef WIN32
+ _findclose(iterator->dirHandle);
+#else
+ closedir(iterator->dirHandle);
+#endif
+ stdio_EntriesIterator_delete(iterator);
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(long dirHandle) {
+ stdio_EntriesIterator *result;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+ return result;
+}
+#else
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(DIR *dirHandle) {
+ stdio_EntriesIterator *result;
+ size_t bufferSize;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+
+ // Linux's and FreeBSD's struct dirent are defined with a
+ // maximum d_name field (NAME_MAX).
+ // However, POSIX doesn't require this, and in fact
+ // at least QNX defines struct dirent with an empty d_name field.
+ // Solaris defineds it with a d_name field of length 1.
+ // This should take care of it:
+ bufferSize = sizeof (struct dirent)
+ - sizeof (((struct dirent *) 0)->d_name) + (NAME_MAX + 1);
+ // Take the length of the dirent structure as it is defined,
+ // subtract the length of the d_name field, and add the length
+ // of the maximum length d_name field (NAME_MAX plus 1 for
+ // the '\0').
+ // XXX: Could this give problems with weird alignments?
+ result->direntBuffer = uio_malloc(bufferSize);
+ return result;
+}
+#endif
+
+void
+stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator) {
+#ifndef WIN32
+ uio_free(iterator->direntBuffer);
+#endif
+ stdio_EntriesIterator_free(iterator);
+}
+
+static inline stdio_EntriesIterator *
+stdio_EntriesIterator_alloc(void) {
+ return uio_malloc(sizeof (stdio_EntriesIterator));
+}
+
+static inline void
+stdio_EntriesIterator_free(stdio_EntriesIterator *iterator) {
+ uio_free(iterator);
+}
+
+static inline uio_GPFile *
+stdio_addFile(uio_GPDir *gPDir, const char *fileName) {
+ uio_GPFile *file;
+
+ file = uio_GPFile_new(gPDir->pRoot, NULL,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, fileName, file);
+ return file;
+}
+
+// called by fillGPDir when a subdir is found
+static inline uio_GPDir *
+stdio_addDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDir *subDir;
+
+ subDir = uio_GPDir_prepareSubDir(gPDir, dirName);
+ if (subDir->extra == NULL) {
+ // It's a new dir, we'll need to add our own data.
+ uio_GPDir_ref(gPDir);
+ subDir->extra = stdio_GPDirData_new(uio_strdup(dirName),
+ NULL, gPDir);
+ uio_GPDir_setComplete(subDir, true);
+ // fillPDir should not be called.
+ }
+ uio_GPDir_commitSubDir(gPDir, dirName, subDir);
+ return subDir;
+}
+
+// returns a pointer to gPDir->extra->cachedPath
+// pointer should not be stored, the memory it points to can be freed
+// lateron. TODO: not threadsafe.
+static char *
+stdio_getPath(uio_GPDir *gPDir) {
+ if (gPDir->extra->cachedPath == NULL) {
+ char *upPath;
+ size_t upPathLen, nameLen;
+
+ if (gPDir->extra->upDir == NULL) {
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Drive letter or UNC \\server\share still needs to follow.
+ gPDir->extra->cachedPath = uio_malloc(1);
+ gPDir->extra->cachedPath[0] = '\0';
+#else
+ gPDir->extra->cachedPath = uio_malloc(2);
+ gPDir->extra->cachedPath[0] = '/';
+ gPDir->extra->cachedPath[1] = '\0';
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ return gPDir->extra->cachedPath;
+ }
+
+ upPath = stdio_getPath(gPDir->extra->upDir);
+ if (upPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (upPath[0] == '\0') {
+ // The up dir is the root dir. Directly below the root dir are
+ // only dirs for drive letters and UNC \\share\server parts.
+ // No '/' needs to be attached.
+ gPDir->extra->cachedPath = uio_strdup(gPDir->extra->name);
+ return gPDir->extra->cachedPath;
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ upPathLen = strlen(upPath);
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS)
+ if (upPath[upPathLen - 1] == '/') {
+ // should only happen for "/"
+ upPathLen--;
+ }
+#endif /* !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS) */
+ nameLen = strlen(gPDir->extra->name);
+ if (upPathLen + nameLen + 1 >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ gPDir->extra->cachedPath = uio_malloc(upPathLen + nameLen + 2);
+ memcpy(gPDir->extra->cachedPath, upPath, upPathLen);
+ gPDir->extra->cachedPath[upPathLen] = '/';
+ memcpy(gPDir->extra->cachedPath + upPathLen + 1,
+ gPDir->extra->name, nameLen);
+ gPDir->extra->cachedPath[upPathLen + nameLen + 1] = '\0';
+ }
+ return gPDir->extra->cachedPath;
+}
+
+static stdio_GPDirData *
+stdio_GPDirData_new(char *name, char *cachedPath, uio_GPDir *upDir) {
+ stdio_GPDirData *result;
+
+ result = stdio_GPDirData_alloc();
+ result->name = name;
+ result->cachedPath = cachedPath;
+ result->upDir = upDir;
+ return result;
+}
+
+static void
+stdio_GPDirData_delete(stdio_GPDirData *gPDirData) {
+ if (gPDirData->upDir != NULL)
+ uio_GPDir_unref(gPDirData->upDir);
+ stdio_GPDirData_free(gPDirData);
+}
+
+static inline stdio_GPDirData *
+stdio_GPDirData_alloc(void) {
+ stdio_GPDirData *result;
+
+ result = uio_malloc(sizeof (stdio_GPDirData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(stdio_GPDirData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+stdio_GPDirData_free(stdio_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(stdio_GPDirData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData->name);
+ if (gPDirData->cachedPath != NULL)
+ uio_free(gPDirData->cachedPath);
+ uio_free(gPDirData);
+}
+
+
diff --git a/src/libs/uio/stdio/stdio.h b/src/libs/uio/stdio/stdio.h
new file mode 100644
index 0000000..914a1d7
--- /dev/null
+++ b/src/libs/uio/stdio/stdio.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+typedef struct stdio_Handle *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef struct stdio_GPDirData *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+typedef struct stdio_EntriesIterator stdio_EntriesIterator;
+typedef stdio_EntriesIterator *uio_NativeEntriesContext;
+
+
+#define uio_INTERNAL_PHYSICAL
+
+#include "../gphys.h"
+#include "../iointrn.h"
+#include "../uioport.h"
+#include "../fstypes.h"
+#include "../physical.h"
+
+#include <sys/stat.h>
+#ifndef WIN32
+# include <dirent.h>
+#endif
+
+
+typedef struct stdio_GPDirData {
+ // The reason that names are stored is that in the system filesystem
+ // you need names to refer to files and directories.
+ // (you could keep a file descriptor to each one, but that would
+ // mean a lot of open file descriptors, and for some it won't even
+ // be enough).
+ // This is not needed for all filesystems; therefor this info is not
+ // in uio_GPDir itself.
+ // The reasons for including upDir here are similar.
+ char *name;
+ char *cachedPath;
+ uio_GPDir *upDir;
+} stdio_GPDirData;
+
+typedef struct stdio_Handle {
+ int fd;
+} stdio_Handle;
+
+#ifdef WIN32
+struct stdio_EntriesIterator {
+ long dirHandle;
+ struct _finddata_t findData;
+ int status;
+};
+#endif
+
+#ifndef WIN32
+struct stdio_EntriesIterator {
+ DIR *dirHandle;
+ struct dirent *entry;
+ struct dirent *direntBuffer;
+ int status;
+};
+#endif
+
+
+uio_PRoot *stdio_mount(uio_Handle *handle, int flags);
+int stdio_umount(uio_PRoot *);
+uio_PDirHandle *stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name,
+ mode_t mode);
+uio_Handle *stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode);
+void stdio_close(uio_Handle *handle);
+int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int stdio_fstat(uio_Handle *handle, struct stat *statBuf);
+int stdio_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf);
+ssize_t stdio_read(uio_Handle *handle, void *buf, size_t count);
+int stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName,
+ uio_PDirHandle *newPDirHandle, const char *newName);
+int stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name);
+off_t stdio_seek(uio_Handle *handle, off_t offset, int whence);
+ssize_t stdio_write(uio_Handle *handle, const void *buf, size_t count);
+int stdio_unlink(uio_PDirHandle *pDirHandle, const char *name);
+
+stdio_EntriesIterator *stdio_openEntries(uio_PDirHandle *pDirHandle);
+int stdio_readEntries(stdio_EntriesIterator **iterator,
+ char *buf, size_t len);
+void stdio_closeEntries(stdio_EntriesIterator *iterator);
+#ifdef WIN32
+stdio_EntriesIterator *stdio_EntriesIterator_new(long dirHandle);
+#else
+stdio_EntriesIterator *stdio_EntriesIterator_new(DIR *dirHandle);
+#endif
+void stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator);
+uio_PDirEntryHandle *stdio_getPDirEntryHandle(
+ const uio_PDirHandle *pDirHandle, const char *name);
+
diff --git a/src/libs/uio/types.h b/src/libs/uio/types.h
new file mode 100644
index 0000000..b92f7a4
--- /dev/null
+++ b/src/libs/uio/types.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _uio_TYPES_H
+#define _uio_TYPES_H
+
+#include "config.h"
+
+// ISO C99 compatible boolean types. The ISO C99 standard defines:
+// - An object declared as type _Bool, large enough to store the values 0
+// and 1, the rank of which is less than the rank of all other standard
+// integer types.
+// - A macro "bool", which expands to "_Bool".
+// - A macro "true", which expands to the integer constant 1, suitable for
+// use in #if preprocessing directives.
+// - A macro "false", which expands to the integer constant 0, suitable for
+// use in #if preprocessing directives.
+// - A macro "__bool_true_false_are_defined", which expands to the integer
+// constant 1, suitable for use in #if preprocessing directives.
+#ifndef __bool_true_false_are_defined
+#undef bool
+#undef false
+#undef true
+#ifndef HAVE__BOOL
+typedef unsigned char _Bool;
+#endif /* HAVE_BOOL */
+#define bool _Bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined
+#endif /* __bool_true_false_are_defined */
+
+typedef bool uio_bool;
+
+typedef unsigned char uio_uint8;
+typedef signed char uio_sint8;
+typedef unsigned short uio_uint16;
+typedef signed short uio_sint16;
+typedef unsigned int uio_uint32;
+typedef signed int uio_sint32;
+
+typedef unsigned long uio_uintptr;
+ // Needs to be adapted for 64 bits systems
+
+#endif /* _uio_TYPES_H */
+
+
diff --git a/src/libs/uio/uioport.h b/src/libs/uio/uioport.h
new file mode 100644
index 0000000..69d7e8e
--- /dev/null
+++ b/src/libs/uio/uioport.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOPORT_H_
+#define LIBS_UIO_UIOPORT_H_
+
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+
+// Compilation related
+#ifndef inline
+# ifdef _MSC_VER
+# define inline __inline
+# else
+# define inline __inline__
+# endif
+#endif
+
+// Paths
+#ifdef WIN32
+# include <stdlib.h>
+# define PATH_MAX _MAX_PATH
+# define NAME_MAX _MAX_FNAME
+ // _MAX_DIR and FILENAME_MAX could also be candidates.
+ // If anyone can tell me which one matches NAME_MAX, please
+ // let me know.
+#elif defined(_WIN32_WCE)
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+ /* PATH_MAX is per POSIX defined in <limits.h>, but:
+ * "A definition of one of the values from Table 2.6 shall bea
+ * omitted from <limits.h> on specific implementations where the
+ * corresponding value is equal to or greater than the
+ * stated minimum, but where the value can vary depending
+ * on the file to which it is applied. The actual value supported
+ * for a specific pathname shall be provided by the pathconf()
+ * function."
+ * _POSIX_NAME_MAX will provide a minimum (14).
+ * This is relevant (at least) for Solaris.
+ */
+# ifndef NAME_MAX
+# define NAME_MAX _POSIX_NAME_MAX
+# endif
+#endif
+
+// Variations in path handling
+#if defined(WIN32) || defined(__SYMBIAN32__)
+ // HAVE_DRIVE_LETTERS is defined to signify that DOS/Windows style drive
+ // letters are to be recognised on this platform.
+# define HAVE_DRIVE_LETTERS
+ // BACKSLASH_IS_PATH_SEPARATOR is defined to signify that the backslash
+ // character is to be recognised as a path separator on this platform.
+ // This does not affect the acceptance of forward slashes as path
+ // separators.
+# define BACKSLASH_IS_PATH_SEPARATOR
+#endif
+#if defined(WIN32)
+ // HAVE_UNC_PATHS is defined to signify that Universal Naming Convention
+ // style paths are to be recognised on this platform.
+# define HAVE_UNC_PATHS
+#endif
+
+// User ids
+#ifdef WIN32
+typedef short uid_t;
+typedef short gid_t;
+#endif
+
+// Some types
+#ifdef _MSC_VER
+typedef int ssize_t;
+typedef unsigned short mode_t;
+#endif
+
+// Directories
+#include <sys/stat.h>
+#ifdef WIN32
+# ifdef _MSC_VER
+# define MKDIR(name, mode) ((void) mode, _mkdir(name))
+# else
+# define MKDIR(name, mode) ((void) mode, mkdir(name))
+# endif
+#else
+# define MKDIR mkdir
+#endif
+#ifdef _MSC_VER
+# include <direct.h>
+# define chdir _chdir
+# define getcwd _getcwd
+# define chdir _chdir
+# define getcwd _getcwd
+# define access _access
+# define F_OK 0
+# define W_OK 2
+# define R_OK 4
+# define open _open
+# define read _read
+# define rmdir _rmdir
+# define lseek _lseek
+# define lstat _lstat
+# define fstat _fstat
+# define S_IRUSR S_IREAD
+# define S_IWUSR S_IWRITE
+# define S_IXUSR S_IEXEC
+# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
+# define S_IRGRP 0
+# define S_IWGRP 0
+# define S_IXGRP 0
+# define S_IROTH 0
+# define S_IWOTH 0
+# define S_IXOTH 0
+# define S_IRWXG 0
+# define S_IRWXO 0
+# define S_ISUID 0
+# define S_ISGID 0
+# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+# define S_IFMT _S_IFMT
+# define S_IFREG _S_IFREG
+# define S_IFCHR _S_IFCHR
+# define S_IFDIR _S_IFDIR
+# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
+# define S_ISREG(mode) (((mode) & _S_IFMT) == _S_IFREG)
+# define write _write
+# define stat _stat
+# define unlink _unlink
+#elif defined (__MINGW32__)
+# define S_IRGRP 0
+# define S_IWGRP 0
+# define S_IXGRP 0
+# define S_IROTH 0
+# define S_IWOTH 0
+# define S_IXOTH 0
+# define S_IRWXG 0
+# define S_IRWXO 0
+# define S_ISUID 0
+# define S_ISGID 0
+# define S_IFMT _S_IFMT
+# define S_IFREG _S_IFREG
+# define S_IFCHR _S_IFCHR
+# define S_IFDIR _S_IFDIR
+#endif
+#ifdef __SYMBIAN32__
+ // TODO: Symbian doesn't have readdir_r(). If uio is to be usable
+ // outside of uqm (which defines its own backup readdir_r()), an
+ // implementation of that function needs to be added to uio.
+# include <dirent.h>
+ int readdir_r (DIR *dirp, struct dirent *entry, struct dirent **result);
+#endif
+
+#endif /* LIBS_UIO_UIOPORT_H_ */
+
diff --git a/src/libs/uio/uiostream.c b/src/libs/uio/uiostream.c
new file mode 100644
index 0000000..eb101a5
--- /dev/null
+++ b/src/libs/uio/uiostream.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "uioport.h"
+#include "iointrn.h"
+#include "uiostream.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "uioutils.h"
+#include "utils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#define uio_Stream_BLOCK_SIZE 1024
+
+static inline uio_Stream *uio_Stream_new(uio_Handle *handle, int openFlags);
+static inline void uio_Stream_delete(uio_Stream *stream);
+static inline uio_Stream *uio_Stream_alloc(void);
+static inline void uio_Stream_free(uio_Stream *stream);
+#ifdef NDEBUG
+# define uio_assertReadSanity(stream)
+# define uio_assertWriteSanity(stream)
+#else
+static void uio_assertReadSanity(uio_Stream *stream);
+static void uio_assertWriteSanity(uio_Stream *stream);
+#endif
+static int uio_Stream_fillReadBuffer(uio_Stream *stream);
+static int uio_Stream_flushWriteBuffer(uio_Stream *stream);
+static void uio_Stream_discardReadBuffer(uio_Stream *stream);
+
+
+uio_Stream *
+uio_fopen(uio_DirHandle *dir, const char *path, const char *mode) {
+ int openFlags;
+ uio_Handle *handle;
+ uio_Stream *stream;
+ int i;
+
+ switch (*mode) {
+ case 'r':
+ openFlags = O_RDONLY;
+ break;
+ case 'w':
+ openFlags = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case 'a':
+ openFlags = O_WRONLY| O_CREAT | O_APPEND;
+ default:
+ errno = EINVAL;
+ fprintf(stderr, "Invalid mode string in call to uio_fopen().\n");
+ return NULL;
+ }
+ mode++;
+
+ // C'89 says 'b' may either be the second or the third character.
+ // If someone specifies both 'b' and 't', he/she is out of luck.
+ i = 2;
+ while (i-- && (*mode != '\0')) {
+ switch (*mode) {
+ case 'b':
+#ifdef WIN32
+ openFlags |= O_BINARY;
+#endif
+ break;
+ case 't':
+#ifdef WIN32
+ openFlags |= O_TEXT;
+#endif
+ break;
+ case '+':
+ openFlags = (openFlags & ~O_ACCMODE) | O_RDWR;
+ break;
+ default:
+ i = 0;
+ // leave the while loop
+ break;
+ }
+ mode++;
+ }
+
+ // Any characters in the mode string that might follow are ignored.
+
+ handle = uio_open(dir, path, openFlags, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (handle == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ stream = uio_Stream_new(handle, openFlags);
+ return stream;
+}
+
+int
+uio_fclose(uio_Stream *stream) {
+ if (stream->operation == uio_StreamOperation_write)
+ uio_Stream_flushWriteBuffer(stream);
+ uio_close(stream->handle);
+ uio_Stream_delete(stream);
+ return 0;
+}
+
+// "The file position indicator for the stream (if defined) is advanced by
+// the number of characters successfully read. If an error occurs, the
+// resulting value of the file position indicator for the stream is
+// indeterminate. If a partial element is read, its value is
+// indeterminate." (from POSIX for fread()).
+size_t
+uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
+ size_t bytesToRead;
+ size_t bytesRead;
+
+ bytesToRead = size * nmemb;
+ bytesRead = 0;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ if (stream->dataEnd > stream->dataStart) {
+ // First use what's in the buffer.
+ size_t numRead;
+
+ numRead = minu(stream->dataEnd - stream->dataStart, bytesToRead);
+ memcpy(buf, stream->dataStart, numRead);
+ buf = (void *) ((char *) buf + numRead);
+ stream->dataStart += numRead;
+ bytesToRead -= numRead;
+ bytesRead += numRead;
+ }
+ if (bytesToRead == 0) {
+ // Done already
+ return nmemb;
+ }
+
+ {
+ // Read the rest directly into the caller's buffer.
+ ssize_t numRead;
+ numRead = uio_read(stream->handle, buf, bytesToRead);
+ if (numRead == -1) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ goto out;
+ }
+ bytesRead += numRead;
+ if ((size_t) numRead < bytesToRead) {
+ // End of file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ goto out;
+ }
+ }
+
+out:
+ if (bytesToRead == 0)
+ return nmemb;
+ return bytesRead / size;
+}
+
+char *
+uio_fgets(char *s, int size, uio_Stream *stream) {
+ int orgSize;
+ char *buf;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ size--;
+ orgSize = size;
+ buf = s;
+ while (size > 0) {
+ size_t maxRead;
+ const char *newLinePos;
+
+ // Fill buffer if empty.
+ if (stream->dataStart == stream->dataEnd) {
+ if (uio_Stream_fillReadBuffer(stream) == -1) {
+ // errno is set
+ stream->status = uio_Stream_STATUS_ERROR;
+ return NULL;
+ }
+ if (stream->dataStart == stream->dataEnd) {
+ // End-of-file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ if (size == orgSize) {
+ // Nothing was read.
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ // Search in buffer
+ maxRead = minu(stream->dataEnd - stream->dataStart, size);
+ newLinePos = memchr(stream->dataStart, '\n', maxRead);
+ if (newLinePos != NULL) {
+ // Newline found.
+ maxRead = newLinePos + 1 - stream->dataStart;
+ memcpy(buf, stream->dataStart, maxRead);
+ stream->dataStart += maxRead;
+ buf[maxRead] = '\0';
+ return buf;
+ }
+ // No newline present.
+ memcpy(buf, stream->dataStart, maxRead);
+ stream->dataStart += maxRead;
+ buf += maxRead;
+ size -= maxRead;
+ }
+
+ *buf = '\0';
+ return s;
+}
+
+int
+uio_fgetc(uio_Stream *stream) {
+ int result;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ if (stream->dataStart == stream->dataEnd) {
+ // Buffer is empty
+ if (uio_Stream_fillReadBuffer(stream) == -1) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ return (int) EOF;
+ }
+ if (stream->dataStart == stream->dataEnd) {
+ // End-of-file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ return (int) EOF;
+ }
+ }
+
+ result = (int) *((unsigned char *) stream->dataStart);
+ stream->dataStart++;
+ return result;
+}
+
+// Only one character pushback is guaranteed, just like with stdio ungetc().
+int
+uio_ungetc(int c, uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
+ assert(c >= 0 && c <= 255);
+
+ return (int) EOF;
+ // not implemented
+// return c;
+}
+
+// NB. POSIX allows errno to be set for vsprintf(), but does not require it:
+// "The value of errno may be set to nonzero by a library function call
+// whether or not there is an error, provided the use of errno is not
+// documented in the description of the function in this International
+// Standard." The latter is the case for vsprintf().
+int
+uio_vfprintf(uio_Stream *stream, const char *format, va_list args) {
+ // This could be done faster, but going through snprintf() is easiest,
+ // and is fast enough for now.
+ char *buf;
+ int putResult;
+ int savedErrno;
+
+ buf = uio_vasprintf(format, args);
+ if (buf == NULL) {
+ // errno may or may not be set
+ return -1;
+ }
+
+ putResult = uio_fputs(buf, stream);
+ savedErrno = errno;
+
+ uio_free(buf);
+
+ errno = savedErrno;
+ return putResult;
+}
+
+int
+uio_fprintf(uio_Stream *stream, const char *format, ...) {
+ va_list args;
+ int result;
+
+ va_start(args, format);
+ result = uio_vfprintf(stream, format, args);
+ va_end(args);
+
+ return result;
+}
+
+int
+uio_fputc(int c, uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
+ assert(c >= 0 && c <= 255);
+
+ uio_assertWriteSanity(stream);
+ stream->operation = uio_StreamOperation_write;
+
+ if (stream->dataEnd == stream->bufEnd) {
+ // The buffer is full. Flush it out.
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ // Error status (for ferror()) is set.
+ return EOF;
+ }
+ }
+
+ *(unsigned char *) stream->dataEnd = (unsigned char) c;
+ stream->dataEnd++;
+ return c;
+}
+
+int
+uio_fputs(const char *s, uio_Stream *stream) {
+ int result;
+
+ result = uio_fwrite(s, strlen(s), 1, stream);
+ if (result != 1)
+ return EOF;
+ return 0;
+}
+
+int
+uio_fseek(uio_Stream *stream, long offset, int whence) {
+ int newPos;
+
+ if (stream->operation == uio_StreamOperation_read) {
+ uio_Stream_discardReadBuffer(stream);
+ } else if (stream->operation == uio_StreamOperation_write) {
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+ assert(stream->dataStart == stream->buf);
+ assert(stream->dataEnd == stream->buf);
+ stream->operation = uio_StreamOperation_none;
+
+ newPos = uio_lseek(stream->handle, offset, whence);
+ if (newPos == -1) {
+ // errno is set
+ return -1;
+ }
+ stream->status = uio_Stream_STATUS_OK;
+ // Clear error or end-of-file flag.
+
+ return 0;
+}
+
+long
+uio_ftell(uio_Stream *stream) {
+ off_t newPos;
+
+ newPos = uio_lseek(stream->handle, 0, SEEK_CUR);
+ if (newPos == (off_t) -1) {
+ // errno is set
+ return (long) -1;
+ }
+
+ if (stream->operation == uio_StreamOperation_write) {
+ newPos += stream->dataEnd - stream->dataStart;
+ } else if (stream->operation == uio_StreamOperation_read) {
+ newPos -= stream->dataEnd - stream->dataStart;
+ }
+
+ return (long) newPos;
+}
+
+// If less that nmemb elements could be written, or an error occurs, the
+// file pointer is undefined. clearerr() followed by fseek() need to be
+// called before attempting to read or write again.
+// I don't have the C standard myself, but I suspect this is the official
+// behaviour for fread() and fwrite().
+size_t
+uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
+ ssize_t bytesToWrite;
+ ssize_t bytesWritten;
+
+ uio_assertWriteSanity(stream);
+ stream->operation = uio_StreamOperation_write;
+
+ // NB. If a file is opened in append mode, the file position indicator
+ // is moved to the end of the file before writing.
+ // We leave that up to the physical layer.
+
+ bytesToWrite = size * nmemb;
+ if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
+ // There's enough space in the write buffer to store everything.
+ memcpy(stream->dataEnd, buf, bytesToWrite);
+ stream->dataEnd += bytesToWrite;
+ return nmemb;
+ }
+
+ // Not enough space in the write buffer to write everything.
+ // Flush what's left in the write buffer first.
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ // Error status (for ferror()) is set.
+ return 0;
+ }
+
+ if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
+ // The now empty write buffer is large enough to store everything.
+ memcpy(stream->dataEnd, buf, bytesToWrite);
+ stream->dataEnd += bytesToWrite;
+ return nmemb;
+ }
+
+ // There is more data to write than fits in the (empty) write buffer.
+ // The data is written directly, in its entirety, without going
+ // through the write buffer.
+ bytesWritten = uio_write(stream->handle, buf, bytesToWrite);
+ if (bytesWritten != bytesToWrite) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ if (bytesWritten == -1)
+ return 0;
+ }
+
+ if (bytesWritten == bytesToWrite)
+ return nmemb;
+ return (size_t) bytesWritten / size;
+}
+
+// NB: stdio fflush() accepts NULL to flush all streams. uio_flush() does
+// not.
+int
+uio_fflush(uio_Stream *stream) {
+ assert(stream != NULL);
+
+ if (stream->operation == uio_StreamOperation_write) {
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ return (int) EOF;
+ }
+ stream->operation = uio_StreamOperation_none;
+ }
+
+ return 0;
+}
+
+int
+uio_feof(uio_Stream *stream) {
+ return stream->status == uio_Stream_STATUS_EOF;
+}
+
+int
+uio_ferror(uio_Stream *stream) {
+ return stream->status == uio_Stream_STATUS_ERROR;
+}
+
+void
+uio_clearerr(uio_Stream *stream) {
+ stream->status = uio_Stream_STATUS_OK;
+}
+
+// Counterpart of fileno()
+uio_Handle *
+uio_streamHandle(uio_Stream *stream) {
+ return stream->handle;
+}
+
+#ifndef NDEBUG
+static void
+uio_assertReadSanity(uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
+
+ if (stream->operation == uio_StreamOperation_write) {
+ // "[...] output shall not be directly followed by input without an
+ // intervening call to the fflush function or to a file positioning
+ // function (fseek, fsetpos, or rewind), and input shall not be
+ // directly followed by output without an intervening call to a file
+ // positioning function, unless the input operation encounters
+ // end-of-file." (POSIX, C)
+ fprintf(stderr, "Error: Reading on a file directly after writing, "
+ "without an intervening call to fflush() or a file "
+ "positioning function.\n");
+ abort();
+ }
+}
+#endif
+
+#ifndef NDEBUG
+static void
+uio_assertWriteSanity(uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
+
+ if (stream->operation == uio_StreamOperation_read) {
+ // "[...] output shall not be directly followed by input without an
+ // intervening call to the fflush function or to a file positioning
+ // function (fseek, fsetpos, or rewind), and input shall not be
+ // directly followed by output without an intervening call to a file
+ // positioning function, unless the input operation encounters
+ // end-of-file." (POSIX, C)
+ fprintf(stderr, "Error: Writing on a file directly after reading, "
+ "without an intervening call to a file positioning "
+ "function.\n");
+ abort();
+ }
+ assert(stream->dataStart == stream->buf);
+}
+#endif
+
+static int
+uio_Stream_flushWriteBuffer(uio_Stream *stream) {
+ ssize_t bytesWritten;
+
+ assert(stream->operation == uio_StreamOperation_write);
+
+ bytesWritten = uio_write(stream->handle, stream->dataStart,
+ stream->dataEnd - stream->dataStart);
+ if (bytesWritten != stream->dataEnd - stream->dataStart) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ return -1;
+ }
+ assert(stream->dataStart == stream->buf);
+ stream->dataEnd = stream->buf;
+
+ return 0;
+}
+
+static void
+uio_Stream_discardReadBuffer(uio_Stream *stream) {
+ assert(stream->operation == uio_StreamOperation_read);
+ stream->dataStart = stream->buf;
+ stream->dataEnd = stream->buf;
+ // TODO: when implementing pushback: throw away pushback buffer.
+}
+
+static int
+uio_Stream_fillReadBuffer(uio_Stream *stream) {
+ ssize_t numRead;
+
+ assert(stream->operation == uio_StreamOperation_read);
+
+ numRead = uio_read(stream->handle, stream->buf,
+ uio_Stream_BLOCK_SIZE);
+ if (numRead == -1)
+ return -1;
+ stream->dataStart = stream->buf;
+ stream->dataEnd = stream->buf + numRead;
+ return 0;
+}
+
+static inline uio_Stream *
+uio_Stream_new(uio_Handle *handle, int openFlags) {
+ uio_Stream *result;
+
+ result = uio_Stream_alloc();
+ result->handle = handle;
+ result->openFlags = openFlags;
+ result->status = uio_Stream_STATUS_OK;
+ result->operation = uio_StreamOperation_none;
+ result->buf = uio_malloc(uio_Stream_BLOCK_SIZE);
+ result->dataStart = result->buf;
+ result->dataEnd = result->buf;
+ result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE;
+ return result;
+}
+
+static inline uio_Stream *
+uio_Stream_alloc(void) {
+ uio_Stream *result = uio_malloc(sizeof (uio_Stream));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Stream, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_Stream_delete(uio_Stream *stream) {
+ uio_free(stream->buf);
+ uio_Stream_free(stream);
+}
+
+static inline void
+uio_Stream_free(uio_Stream *stream) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Stream, (void *) stream);
+#endif
+ uio_free(stream);
+}
+
diff --git a/src/libs/uio/uiostream.h b/src/libs/uio/uiostream.h
new file mode 100644
index 0000000..f65487e
--- /dev/null
+++ b/src/libs/uio/uiostream.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOSTREAM_H_
+#define LIBS_UIO_UIOSTREAM_H_
+
+
+typedef struct uio_Stream uio_Stream;
+
+#include "io.h"
+
+#include <stdarg.h>
+
+
+uio_Stream *uio_fopen(uio_DirHandle *dir, const char *path, const char *mode);
+int uio_fclose(uio_Stream *stream);
+size_t uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream);
+char *uio_fgets(char *buf, int size, uio_Stream *stream);
+int uio_fgetc(uio_Stream *stream);
+#define uio_getc uio_fgetc
+int uio_ungetc(int c, uio_Stream *stream);
+int uio_vfprintf(uio_Stream *stream, const char *format, va_list args);
+int uio_fprintf(uio_Stream *stream, const char *format, ...);
+int uio_fputc(int c, uio_Stream *stream);
+#define uio_putc uio_fputc
+int uio_fputs(const char *s, uio_Stream *stream);
+int uio_fseek(uio_Stream *stream, long offset, int whence);
+long uio_ftell(uio_Stream *stream);
+size_t uio_fwrite(const void *buf, size_t size, size_t nmemb,
+ uio_Stream *stream);
+int uio_fflush(uio_Stream *stream);
+int uio_feof(uio_Stream *stream);
+int uio_ferror(uio_Stream *stream);
+void uio_clearerr(uio_Stream *stream);
+uio_Handle *uio_streamHandle(uio_Stream *stream);
+
+
+/* *** Internal definitions follow *** */
+#ifdef uio_INTERNAL
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include "iointrn.h"
+
+typedef enum {
+ uio_StreamOperation_none,
+ uio_StreamOperation_read,
+ uio_StreamOperation_write
+} uio_StreamOperation;
+
+struct uio_Stream {
+ char *buf;
+ // Start of the buffer.
+ char *dataStart;
+ // Start of the part of the buffer that is in use.
+ char *dataEnd;
+ // Start of the unused part of the buffer.
+ char *bufEnd;
+ // End of the buffer.
+ // INV: buf <= dataStart <= dataEnd <= bufEnd
+ // INV: if 'operation == uio_StreamOperation_write' then buf == dataStart
+
+ uio_Handle *handle;
+ int status;
+#define uio_Stream_STATUS_OK 0
+#define uio_Stream_STATUS_EOF 1
+#define uio_Stream_STATUS_ERROR 2
+ uio_StreamOperation operation;
+ // What was the last action (reading or writing). This
+ // determines whether the buffer is a read or write buffer.
+ int openFlags;
+ // Flags used for opening the file.
+};
+
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_UIOSTREAM_H_ */
+
+
diff --git a/src/libs/uio/uioutils.c b/src/libs/uio/uioutils.c
new file mode 100644
index 0000000..fbe3cd4
--- /dev/null
+++ b/src/libs/uio/uioutils.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "uioutils.h"
+#include "mem.h"
+#include "paths.h"
+#include "uioport.h"
+
+/**
+ * Concatenate two strings into a newly allocated buffer.
+ *
+ * @param[in] first The first (left) string, '\0' terminated.
+ * @param[in] second The second (right) string, '\0' terminated.
+ *
+ * @returns A newly allocated string consisting of the concatenation of
+ * 'first' and 'second', to be freed using uio_free().
+ */
+char *
+strcata(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ firstLen = strlen(first);
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 1);
+ resPtr = result;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// returns a copy of a generic array 'array' with 'element' inserted in
+// position 'insertPos'
+void *
+insertArray(const void *array, size_t oldNumElements, int insertPos,
+ const void *element, size_t elementSize) {
+ void *newArray, *newArrayPtr;
+ const void *arrayPtr;
+ size_t preInsertSize;
+
+ newArray = uio_malloc((oldNumElements + 1) * elementSize);
+ preInsertSize = insertPos * elementSize;
+ memcpy(newArray, array, preInsertSize);
+ newArrayPtr = (char *) newArray + preInsertSize;
+ arrayPtr = (const char *) array + preInsertSize;
+ memcpy(newArrayPtr, element, elementSize);
+ newArrayPtr = (char *) newArrayPtr + elementSize;
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - insertPos) * elementSize);
+ return newArray;
+}
+
+// returns a copy of a pointer array 'array' with 'element' inserted in
+// position 'insertPos'
+void **
+insertArrayPointer(const void **array, size_t oldNumElements, int insertPos,
+ const void *element) {
+ void **newArray, **newArrayPtr;
+ const void **arrayPtr;
+ size_t preInsertSize;
+
+ newArray = uio_malloc((oldNumElements + 1) * sizeof (void *));
+ preInsertSize = insertPos * sizeof (void *);
+ memcpy(newArray, array, preInsertSize);
+ newArrayPtr = newArray + insertPos;
+ arrayPtr = array + insertPos;
+ *newArrayPtr = unconst(element);
+ newArrayPtr++;
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - insertPos) * sizeof (void *));
+ return newArray;
+}
+
+// returns a copy of a generic array 'array' with 'numExclude' elements,
+// starting from startpos, removed.
+void *
+excludeArray(const void *array, size_t oldNumElements, int startPos,
+ int numExclude, size_t elementSize) {
+ void *newArray, *newArrayPtr;
+ const void *arrayPtr;
+ size_t preExcludeSize;
+
+ newArray = uio_malloc((oldNumElements - numExclude) * elementSize);
+ preExcludeSize = startPos * elementSize;
+ memcpy(newArray, array, preExcludeSize);
+ newArrayPtr = (char *) newArray + preExcludeSize;
+ arrayPtr = (const char *) array +
+ (startPos + numExclude) * sizeof (elementSize);
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - startPos - numExclude) * elementSize);
+ return newArray;
+}
+
+// returns a copy of a pointer array 'array' with 'numExclude' elements,
+// starting from startpos, removed.
+void **
+excludeArrayPointer(const void **array, size_t oldNumElements, int startPos,
+ int numExclude) {
+ void **newArray;
+
+ newArray = uio_malloc((oldNumElements - numExclude) * sizeof (void *));
+ memcpy(newArray, array, startPos * sizeof (void *));
+ memcpy(&newArray[startPos], &array[startPos + numExclude],
+ (oldNumElements - startPos - numExclude) * sizeof (void *));
+ return newArray;
+}
+
+// If the given DOS date/time is invalid, the result is unspecified,
+// but the function won't crash.
+time_t
+dosToUnixTime(uio_uint16 date, uio_uint16 tm) {
+ // DOS date has the following format:
+ // bits 0-4 specify the number of the day in the month (1-31).
+ // bits 5-8 specify the number of the month in the year (1-12).
+ // bits 9-15 specify the year number since 1980 (0-127)
+ // DOS time has the fillowing format:
+ // bits 0-4 specify the number of seconds/2 in the minute (0-29)
+ // (only accurate on 2 seconds)
+ // bits 5-10 specify the number of minutes in the hour (0-59)
+ // bits 11-15 specify the number of hours since midnight (0-23)
+
+ int year, month, day;
+ int hours, minutes, seconds;
+ long result;
+
+ static const int daysUntilMonth[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+ 334, 334, 334, 334 };
+ // The last 4 entries are there so that there's no
+ // invalid memory access if the date is invalid.
+
+ year = date >> 9;
+ month = ((date >> 5) - 1) & 0x0f; // Number in [0..15]
+ day = (date - 1) & 0x1f; // Number in [0..31]
+ hours = tm >> 11;
+ minutes = (tm >> 5) & 0x3f;
+ seconds = (tm & 0x1f) * 2; // Even number in [0..62]
+
+ result = year * 365 + daysUntilMonth[month] + day;
+ // Count the (non-leap) days in all those years
+
+ // Add a leapday for each 4th year
+ if (year % 4 == 0 && month <= 2) {
+ // The given date is a leap-year but the leapday hasn't occured yet.
+ result += year / 4;
+ } else {
+ result += 1 + year / 4;
+ }
+ // result now is the number of days between 1980-01-01 and the given day.
+
+ // Add the days between 1970-01-01 and 1980-01-01
+ // (2 leapdays in this period)
+ result += 365 * 10 + 2;
+
+ result = (result * 24) + hours; // days to hours
+ result = (result * 60) + minutes; // hours to minutes
+ result = (result * 60) + seconds; // minutes to seconds
+
+ return (time_t) result;
+}
+
+char *
+dosToUnixPath(const char *path) {
+ const char *srcPtr;
+ char *result, *dstPtr;
+ size_t skip;
+
+ result = uio_malloc(strlen(path) + 1);
+ srcPtr = path;
+ dstPtr = result;
+
+ // A UNC path will look like this: "\\server\share/..."; the first two
+ // characters will be backslashes, and the separator between the server
+ // and the share too. The rest will be slashes.
+ // The goal is that at every forward slash, the path should be
+ // stat()'able.
+ skip = uio_skipUNCServerShare(srcPtr);
+ if (skip != 0) {
+ char *slash;
+ memcpy(dstPtr, srcPtr, skip);
+
+ slash = memchr(srcPtr + 2, '/', skip - 2);
+ if (slash != NULL)
+ *slash = '\\';
+
+ srcPtr += skip;
+ dstPtr += skip;
+ }
+
+ while (*srcPtr != '\0') {
+ if (*srcPtr == '\\') {
+ *dstPtr = '/';
+ } else
+ *dstPtr = *srcPtr;
+ srcPtr++;
+ dstPtr++;
+ }
+ *dstPtr = '\0';
+ return result;
+}
+
+
diff --git a/src/libs/uio/uioutils.h b/src/libs/uio/uioutils.h
new file mode 100644
index 0000000..6e04843
--- /dev/null
+++ b/src/libs/uio/uioutils.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOUTILS_H_
+#define LIBS_UIO_UIOUTILS_H_
+
+#include <time.h>
+
+#include "types.h"
+#include "uioport.h"
+
+char *strcata(const char *first, const char *second);
+void *insertArray(const void *array, size_t oldNumElements, int insertPos,
+ const void *element, size_t elementSize);
+void **insertArrayPointer(const void **array, size_t oldNumElements,
+ int insertPos, const void *element);
+void *excludeArray(const void *array, size_t oldNumElements, int startPos,
+ int numExclude, size_t elementSize);
+void **excludeArrayPointer(const void **array, size_t oldNumElements,
+ int startPos, int numExclude);
+time_t dosToUnixTime(uio_uint16 date, uio_uint16 tm);
+char *dosToUnixPath(const char *path);
+
+/* Sometimes you just have to remove a 'const'.
+ * (for instance, when implementing a function like strchr)
+ */
+static inline void *
+unconst(const void *arg) {
+ union {
+ void *c;
+ const void *cc;
+ } u;
+ u.cc = arg;
+ return u.c;
+}
+
+// byte1 is the lowest byte, byte4 the highest
+static inline uio_uint32
+makeUInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) {
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+}
+
+static inline uio_uint16
+makeUInt16(uio_uint8 byte1, uio_uint8 byte2) {
+ return byte1 | (byte2 << 8);
+}
+
+static inline uio_sint32
+makeSInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) {
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+}
+
+static inline uio_sint16
+makeSInt16(uio_uint8 byte1, uio_uint8 byte2) {
+ return byte1 | (byte2 << 8);
+}
+
+static inline uio_bool
+isBitSet(uio_uint32 bitField, int bit) {
+ return ((bitField >> bit) & 1) == 1;
+}
+
+static inline int
+mins(int i1, int i2) {
+ return i1 <= i2 ? i1 : i2;
+}
+
+static inline unsigned int
+minu(unsigned int i1, unsigned int i2) {
+ return i1 <= i2 ? i1 : i2;
+}
+
+
+#endif /* LIBS_UIO_UIOUTILS_H_ */
+
diff --git a/src/libs/uio/utils.c b/src/libs/uio/utils.c
new file mode 100644
index 0000000..1f705bb
--- /dev/null
+++ b/src/libs/uio/utils.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+#ifdef _MSC_VER
+# include <stdarg.h>
+#endif /* _MSC_VER */
+
+#include "iointrn.h"
+#include "ioaux.h"
+#include "utils.h"
+
+static int uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf);
+
+struct uio_StdioAccessHandle {
+ uio_DirHandle *tempRoot;
+ char *tempDirName;
+ uio_DirHandle *tempDir;
+ char *fileName;
+ char *stdioPath;
+};
+
+static inline uio_StdioAccessHandle *uio_StdioAccessHandle_new(
+ uio_DirHandle *tempRoot, char *tempDirName,
+ uio_DirHandle *tempDir, char *fileName,
+ char *stdioPath);
+static inline void uio_StdioAccessHandle_delete(
+ uio_StdioAccessHandle *handle);
+static inline uio_StdioAccessHandle *uio_StdioAccessHandle_alloc(void);
+static inline void uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle);
+
+/*
+ * Copy a file with path srcName to a file with name newName.
+ * If the destination already exists, the operation fails.
+ * Links are followed.
+ * Special files (fifos, char devices, block devices, etc) will be
+ * read as long as there is data available and the destination will be
+ * a regular file with that data.
+ * The new file will have the same permissions as the old.
+ * If an error occurs during copying, an attempt will be made to
+ * remove the copy.
+ */
+int
+uio_copyFile(uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName) {
+ uio_Handle *src, *dst;
+ struct stat sb;
+#define BUFSIZE 65536
+ uio_uint8 *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ src = uio_open(srcDir, srcName, O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (src == NULL)
+ return -1;
+
+ if (uio_fstat(src, &sb) == -1)
+ return uio_copyError(src, NULL, NULL, NULL, NULL);
+
+ dst = uio_open(dstDir, newName, O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (dst == NULL)
+ return uio_copyError(src, NULL, NULL, NULL, NULL);
+
+ buf = uio_malloc(BUFSIZE);
+ // This was originally a statically allocated buffer,
+ // but as this function might be run from a thread with
+ // a small Stack, this is better.
+ while (1) {
+ numInBuf = uio_read(src, buf, BUFSIZE);
+ if (numInBuf == -1) {
+ if (errno == EINTR)
+ continue;
+ return uio_copyError(src, dst, dstDir, newName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do
+ {
+ numWritten = uio_write(dst, bufPtr, numInBuf);
+ if (numWritten == -1) {
+ if (errno == EINTR)
+ continue;
+ return uio_copyError(src, dst, dstDir, newName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ uio_free(buf);
+ uio_close(src);
+ uio_close(dst);
+ errno = 0;
+ return 0;
+}
+
+/*
+ * Closes srcHandle if it's not -1.
+ * Closes dstHandle if it's not -1.
+ * Removes unlinkpath from the unlinkHandle dir if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1.
+ * errno is what was before the call.
+ */
+static int
+uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf) {
+ int savedErrno;
+
+ savedErrno = errno;
+
+#ifdef DEBUG
+ fprintf(stderr, "Error while copying: %s\n", strerror(errno));
+#endif
+
+ if (srcHandle != NULL)
+ uio_close(srcHandle);
+
+ if (dstHandle != NULL)
+ uio_close(dstHandle);
+
+ if (unlinkPath != NULL)
+ uio_unlink(unlinkHandle, unlinkPath);
+
+ if (buf != NULL)
+ uio_free(buf);
+
+ errno = savedErrno;
+ return -1;
+}
+
+#define NUM_TEMP_RETRIES 16
+ // Retry this many times to create a temporary dir, before giving
+ // up. If undefined, keep trying indefinately.
+
+uio_StdioAccessHandle *
+uio_getStdioAccess(uio_DirHandle *dir, const char *path, int flags,
+ uio_DirHandle *tempDir) {
+ int res;
+ uio_MountHandle *mountHandle;
+ const char *name;
+ char *newPath;
+ char *tempDirName;
+ uio_DirHandle *newDir;
+ uio_FileSystemID fsID;
+
+ res = uio_getFileLocation(dir, path, flags, &mountHandle, &newPath);
+ if (res == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ fsID = uio_getMountFileSystemType(mountHandle);
+ if (fsID == uio_FSTYPE_STDIO) {
+ // Current location is usable.
+ return uio_StdioAccessHandle_new(NULL, NULL, NULL, NULL, newPath);
+ }
+ uio_free(newPath);
+
+ {
+ uio_uint32 dirNum;
+ int i;
+
+ // Current location is not usable. Create a directory with a
+ // generated name, as a temporary location to store a copy of
+ // the file.
+ dirNum = (uio_uint32) time(NULL);
+ tempDirName = uio_malloc(sizeof "01234567");
+ for (i = 0; ; i++) {
+#ifdef NUM_TEMP_RETRIES
+ if (i >= NUM_TEMP_RETRIES) {
+ // Using ENOSPC to report that we couldn't create a
+ // temporary dir, getting EEXIST.
+ uio_free(tempDirName);
+ errno = ENOSPC;
+ return NULL;
+ }
+#endif
+
+ sprintf(tempDirName, "%08lx", (unsigned long) dirNum + i);
+
+ res = uio_mkdir(tempDir, tempDirName, 0700);
+ if (res == -1) {
+ int savedErrno;
+ if (errno == EEXIST)
+ continue;
+ savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not create temporary dir: %s\n",
+ strerror(errno));
+#endif
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+ break;
+ }
+
+ newDir = uio_openDirRelative(tempDir, tempDirName, 0);
+ if (newDir == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not open temporary dir: %s\n",
+ strerror(errno));
+#endif
+ res = uio_rmdir(tempDir, tempDirName);
+#ifdef DEBUG
+ if (res == -1)
+ fprintf(stderr, "Warning: Could not remove temporary dir: "
+ "%s.\n", strerror(errno));
+#endif
+ uio_free(tempDirName);
+ errno = EIO;
+ return NULL;
+ }
+
+ // Get the last component of path. This should be the file to
+ // access.
+ name = strrchr(path, '/');
+ if (name == NULL)
+ name = path;
+
+ // Copy the file
+ res = uio_copyFile(dir, path, newDir, name);
+ if (res == -1) {
+ int savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not copy file to temporary dir: "
+ "%s\n", strerror(errno));
+#endif
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+
+ res = uio_getFileLocation(newDir, name, flags, &mountHandle, &newPath);
+ if (res == -1) {
+ int savedErrno = errno;
+ fprintf(stderr, "Error: uio_getStdioAccess: Could not get location "
+ "of temporary dir: %s.\n", strerror(errno));
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ fsID = uio_getMountFileSystemType(mountHandle);
+ if (fsID != uio_FSTYPE_STDIO) {
+ // Temp dir isn't on a stdio fs either.
+ fprintf(stderr, "Error: uio_getStdioAccess: Temporary file location "
+ "isn't on a stdio filesystem.\n");
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ uio_free(newPath);
+// errno = EXDEV;
+ errno = EINVAL;
+ return NULL;
+ }
+
+ uio_DirHandle_ref(tempDir);
+ return uio_StdioAccessHandle_new(tempDir, tempDirName, newDir,
+ uio_strdup(name), newPath);
+}
+
+void
+uio_releaseStdioAccess(uio_StdioAccessHandle *handle) {
+ if (handle->tempDir != NULL) {
+ if (uio_unlink(handle->tempDir, handle->fileName) == -1) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not remove temporary file: "
+ "%s\n", strerror(errno));
+#endif
+ }
+
+ // Need to free this handle in advance. There should be no handles
+ // to a dir left when removing it.
+ uio_DirHandle_unref(handle->tempDir);
+ handle->tempDir = NULL;
+
+ if (uio_rmdir(handle->tempRoot, handle->tempDirName) == -1) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not remove temporary directory: "
+ "%s\n", strerror(errno));
+#endif
+ }
+ }
+
+ uio_StdioAccessHandle_delete(handle);
+}
+
+const char *
+uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle) {
+ return (const char *) handle->stdioPath;
+}
+
+// references to tempRoot and tempDir are not increased.
+// no copies of arguments are made.
+// By calling this function control of the values is transfered to
+// the handle.
+static inline uio_StdioAccessHandle *
+uio_StdioAccessHandle_new(
+ uio_DirHandle *tempRoot, char *tempDirName,
+ uio_DirHandle *tempDir, char *fileName, char *stdioPath) {
+ uio_StdioAccessHandle *result;
+
+ result = uio_StdioAccessHandle_alloc();
+ result->tempRoot = tempRoot;
+ result->tempDirName = tempDirName;
+ result->tempDir = tempDir;
+ result->fileName = fileName;
+ result->stdioPath = stdioPath;
+
+ return result;
+}
+
+static inline void
+uio_StdioAccessHandle_delete(uio_StdioAccessHandle *handle) {
+ if (handle->tempDir != NULL)
+ uio_DirHandle_unref(handle->tempDir);
+ if (handle->fileName != NULL)
+ uio_free(handle->fileName);
+ if (handle->tempRoot != NULL)
+ uio_DirHandle_unref(handle->tempRoot);
+ if (handle->tempDirName != NULL)
+ uio_free(handle->tempDirName);
+ uio_free(handle->stdioPath);
+ uio_StdioAccessHandle_free(handle);
+}
+
+static inline uio_StdioAccessHandle *
+uio_StdioAccessHandle_alloc(void) {
+ return uio_malloc(sizeof (uio_StdioAccessHandle));
+}
+
+static inline void
+uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle) {
+ uio_free(handle);
+}
+
+#ifdef _MSC_VER
+# include <stdarg.h>
+#if 0 /* Unneeded for now */
+// MSVC does not have snprintf(). It does have a _snprintf(), but it does
+// not \0-terminate a truncated string as the C standard prescribes.
+static inline int
+snprintf(char *str, size_t size, const char *format, ...)
+{
+ int result;
+ va_list args;
+
+ va_start (args, format);
+ result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ va_end (args);
+
+ return result;
+}
+#endif
+
+// MSVC does not have vsnprintf(). It does have a _vsnprintf(), but it does
+// not \0-terminate a truncated string as the C standard prescribes.
+static inline int
+vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ int result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ return result;
+}
+#endif /* _MSC_VER */
+
+// The result should be freed using uio_free().
+// NB. POSIX allows errno to be set for vsprintf(), but does not require it:
+// "The value of errno may be set to nonzero by a library function call
+// whether or not there is an error, provided the use of errno is not
+// documented in the description of the function in this International
+// Standard." The latter is the case for vsprintf().
+char *
+uio_vasprintf(const char *format, va_list args) {
+ // TODO: If there is a system vasprintf, use that.
+ // XXX: That would mean that the allocation would always go through
+ // malloc() or so, instead of uio_malloc(), which may not be
+ // desirable.
+
+ char *buf;
+ size_t bufSize = 128;
+ // Start with enough for one screen line, and a power of 2,
+ // which might give faster result with allocations.
+
+ buf = uio_malloc(bufSize);
+ if (buf == NULL) {
+ // errno is set.
+ return NULL;
+ }
+
+ for (;;) {
+ int printResult = vsnprintf(buf, bufSize, format, args);
+ if (printResult < 0) {
+ // This means the buffer was not large enough, but vsnprintf()
+ // does not give us any clue on how large it should be.
+ // Note that this does not happen with a C'99 compliant
+ // vsnprintf(), but it will happen on MS Windows, and on
+ // glibc before version 2.1.
+ bufSize *= 2;
+ } else if ((unsigned int) printResult >= bufSize) {
+ // The buffer was too small, but printResult contains the size
+ // that the buffer needs to be (excluding the '\0' character).
+ bufSize = printResult + 1;
+ } else {
+ // Success.
+ if ((unsigned int) printResult + 1 != bufSize) {
+ // Shorten the resulting buffer to the size that was
+ // actually needed.
+ char *newBuf = uio_realloc(buf, printResult + 1);
+ if (newBuf == NULL) {
+ // We could have returned the (overly large) original
+ // buffer, but the unused memory might not be
+ // acceptable, and the program would be likely to run
+ // into problems sooner or later anyhow.
+ int savedErrno = errno;
+ uio_free(buf);
+ errno = savedErrno;
+ return NULL;
+ }
+ return newBuf;
+ }
+
+ return buf;
+ }
+
+ {
+ char *newBuf = uio_realloc(buf, bufSize);
+ if (newBuf == NULL)
+ {
+ int savedErrno = errno;
+ uio_free(buf);
+ errno = savedErrno;
+ return NULL;
+ }
+ buf = newBuf;
+ }
+ }
+}
+
+// As uio_vasprintf(), but with an argument list.
+char *
+uio_asprintf(const char *format, ...) {
+ // TODO: If there is a system asprintf, use that.
+ // XXX: That would mean that the allocation would always go through
+ // malloc() or so, instead of uio_malloc(), which may not be
+ // desirable.
+
+ va_list args;
+ char *result;
+ int savedErrno;
+
+ va_start(args, format);
+ result = uio_vasprintf(format, args);
+ savedErrno = errno;
+ va_end(args);
+
+ errno = savedErrno;
+ return result;
+}
+
+
diff --git a/src/libs/uio/utils.h b/src/libs/uio/utils.h
new file mode 100644
index 0000000..313bf67
--- /dev/null
+++ b/src/libs/uio/utils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UTILS_H_
+#define LIBS_UIO_UTILS_H_
+
+#include <stdarg.h>
+
+#ifdef uio_INTERNAL
+typedef struct uio_StdioAccessHandle uio_StdioAccessHandle;
+#else
+typedef void uio_StdioAccessHandle;
+#endif
+
+int uio_copyFile(uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName);
+uio_StdioAccessHandle *uio_getStdioAccess(uio_DirHandle *dir,
+ const char *path, int flags, uio_DirHandle *tempDir);
+const char *uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle);
+void uio_releaseStdioAccess(uio_StdioAccessHandle *handle);
+
+char *uio_vasprintf(const char *format, va_list args);
+char *uio_asprintf(const char *format, ...);
+
+#endif /* LIBS_UIO_UTILS_H_ */
+
diff --git a/src/libs/uio/zip/Makeinfo b/src/libs/uio/zip/Makeinfo
new file mode 100644
index 0000000..64ae8d5
--- /dev/null
+++ b/src/libs/uio/zip/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="zip.c"
+uqm_HFILES="zip.h"
diff --git a/src/libs/uio/zip/zip.c b/src/libs/uio/zip/zip.c
new file mode 100644
index 0000000..6da462b
--- /dev/null
+++ b/src/libs/uio/zip/zip.c
@@ -0,0 +1,1680 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * This file makes use of zlib (http://www.gzip.org/zlib/)
+ *
+ * References:
+ * The .zip format description from PKWare:
+ * http://www.pkware.com/products/enterprise/white_papers/appnote.html
+ * The .zip format description from InfoZip:
+ * ftp://ftp.info-zip.org/pub/infozip/doc/appnote-011203-iz.zip
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "zip.h"
+#include "../physical.h"
+#include "../uioport.h"
+#include "../paths.h"
+#include "../uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+
+#define DIR_STRUCTURE_READ_BUFSIZE 0x10000
+
+static int zip_badFile(zip_GPFileData *gPFileData, char *fileName);
+static int zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle);
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+#endif
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static off_t zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock);
+static int zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+static int zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos);
+int zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData);
+#endif
+static int zip_fillDirStructureProcessExtraFields(
+ uio_FileBlock *fileBlock, off_t extraFieldLength,
+ zip_GPFileData *gPFileData, const char *path, off_t pos,
+ uio_bool central);
+static inline int zip_foundFile(uio_GPDir *gPDir, const char *path,
+ zip_GPFileData *gPFileData);
+static inline int zip_foundDir(uio_GPDir *gPDir, const char *dirName,
+ zip_GPDirData *gPDirData);
+static int zip_initZipStream(z_stream *zipStream);
+static int zip_unInitZipStream(z_stream *zipStream);
+static int zip_reInitZipStream(z_stream *zipStream);
+
+static voidpf zip_alloc(voidpf opaque, uInt items, uInt size);
+static void zip_free(voidpf opaque, voidpf address);
+
+static inline zip_GPFileData * zip_GPFileData_new(void);
+static inline void zip_GPFileData_delete(zip_GPFileData *gPFileData);
+static inline zip_GPFileData *zip_GPFileData_alloc(void);
+static inline void zip_GPFileData_free(zip_GPFileData *gPFileData);
+static inline void zip_GPDirData_delete(zip_GPDirData *gPDirData);
+static inline void zip_GPDirData_free(zip_GPDirData *gPDirData);
+
+static ssize_t zip_readStored(uio_Handle *handle, void *buf, size_t count);
+static ssize_t zip_readDeflated(uio_Handle *handle, void *buf, size_t count);
+static off_t zip_seekStored(uio_Handle *handle, off_t offset);
+static off_t zip_seekDeflated(uio_Handle *handle, off_t offset);
+
+uio_FileSystemHandler zip_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ zip_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ zip_access,
+ /* .close = */ zip_close,
+ /* .fstat = */ zip_fstat,
+ /* .stat = */ zip_stat,
+ /* .mkdir = */ NULL,
+ /* .open = */ zip_open,
+ /* .read = */ zip_read,
+ /* .rename = */ NULL,
+ /* .rmdir = */ NULL,
+ /* .seek = */ zip_seek,
+ /* .write = */ NULL,
+ /* .unlink = */ NULL,
+
+ /* .openEntries = */ uio_GPDir_openEntries,
+ /* .readEntries = */ uio_GPDir_readEntries,
+ /* .closeEntries = */ uio_GPDir_closeEntries,
+
+ /* .getPDirEntryHandle = */ uio_GPDir_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations zip_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ zip_GPDirData_delete,
+ /* .deleteGPFileExtra = */ zip_GPFileData_delete,
+};
+
+
+#define NUM_COMPRESSION_METHODS 11
+/*
+ * [0] = stored uncompressed
+ * [1] = Shrunk
+ * [2] = Reduced with compression factor 1
+ * [3] = Reduced with compression factor 2
+ * [4] = Reduced with compression factor 3
+ * [5] = Reduced with compression factor 4
+ * [6] = Imploded
+ * [7] = Reserved for Tokenizing
+ * [8] = Deflated
+ * [9] = Deflate64
+ * [10] = "PKWARE Data Compression Library Imploding"
+ */
+static const uio_bool
+ zip_compressionMethodSupported[NUM_COMPRESSION_METHODS] = {
+ true, false, false, false, false, false, false, false,
+ true, false, false };
+
+typedef ssize_t (*zip_readFunctionType)(uio_Handle *handle,
+ void *buf, size_t count);
+zip_readFunctionType zip_readMethods[NUM_COMPRESSION_METHODS] = {
+ zip_readStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_readDeflated, NULL, NULL
+};
+typedef off_t (*zip_seekFunctionType)(uio_Handle *handle, off_t offset);
+zip_seekFunctionType zip_seekMethods[NUM_COMPRESSION_METHODS] = {
+ zip_seekStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_seekDeflated, NULL, NULL
+};
+
+
+typedef enum {
+ zip_OSType_FAT,
+ zip_OSType_Amiga,
+ zip_OSType_OpenVMS,
+ zip_OSType_UNIX,
+ zip_OSType_VMCMS,
+ zip_OSType_AtariST,
+ zip_OSType_HPFS,
+ zip_OSType_HFS,
+ zip_OSType_ZSystem,
+ zip_OSType_CPM,
+ zip_OSType_TOPS20,
+ zip_OSType_NTFS,
+ zip_OSType_QDOS,
+ zip_OSType_Acorn,
+ zip_OSType_VFAT,
+ zip_OSType_MVS,
+ zip_OSType_BeOS,
+ zip_OSType_Tandem,
+
+ zip_numOSTypes
+} zip_OSType;
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static mode_t zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes);
+#endif
+
+#define zip_INPUT_BUFFER_SIZE 0x10000
+ // TODO: make this configurable a la sysctl?
+#define zip_SEEK_BUFFER_SIZE zip_INPUT_BUFFER_SIZE
+
+
+void
+zip_close(uio_Handle *handle) {
+ zip_Handle *zip_handle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_close - handle=%p\n", (void *) handle);
+#endif
+ zip_handle = handle->native;
+ uio_GPFile_unref(zip_handle->file);
+ zip_unInitZipStream(&zip_handle->zipStream);
+ uio_closeFileBlock(zip_handle->fileBlock);
+ uio_free(zip_handle);
+}
+
+static void
+zip_fillStat(struct stat *statBuf, const zip_GPFileData *gPFileData) {
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_size = gPFileData->uncompressedSize;
+ statBuf->st_uid = gPFileData->uid;
+ statBuf->st_gid = gPFileData->gid;
+ statBuf->st_mode = gPFileData->mode;
+ statBuf->st_atime = gPFileData->atime;
+ statBuf->st_mtime = gPFileData->mtime;
+ statBuf->st_ctime = gPFileData->ctime;
+}
+
+int
+zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ errno = ENOSYS; // Not implemented.
+ (void) pDirHandle;
+ (void) name;
+ (void) mode;
+ return -1;
+
+#if 0
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (mode & R_OK)
+ {
+ // Read permission is always granted. Nothing to check here.
+ }
+
+ if (mode & W_OK) {
+ errno = EACCES;
+ return -1;
+ }
+
+ if (mode & X_OK) {
+ if (uio_GPDirEntry_isDir(entry)) {
+ // Search permission on directories is always granted.
+ } else {
+ // WORK
+#error
+ }
+ }
+
+ if (mode & F_OK) {
+ // WORK
+#error
+ }
+
+ return 0;
+#endif
+}
+
+int
+zip_fstat(uio_Handle *handle, struct stat *statBuf) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (handle->native->file->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(handle->root->handle,
+ handle->native->file->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+ zip_fillStat(statBuf, handle->native->file->extra);
+ return 0;
+}
+
+int
+zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) {
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (uio_GPDirEntry_isDir(entry) && entry->extra == NULL) {
+ // No information about this directory was stored.
+ // We'll have to make something up.
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IWGRP | S_IXOTH |
+ S_IROTH | S_IWOTH | S_IXOTH;
+ statBuf->st_uid = 0;
+ statBuf->st_gid = 0;
+ return 0;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+#ifndef zip_INCOMPLETE_STAT
+ if (((zip_GPFileData *) entry->extra)->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ (zip_GPFileData *) entry->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* !defined(zip_INCOMPLETE_STAT) */
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+ zip_fillStat(statBuf, (zip_GPFileData *) entry->extra);
+ return 0;
+}
+
+/*
+ * Function name: zip_open
+ * Description: open a file in zip file
+ * Arguments: pDirHandle - handle to the dir where to open the file
+ * name - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, as from stdio open()
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+zip_open(uio_PDirHandle *pDirHandle, const char *name, int flags,
+ mode_t mode) {
+ zip_Handle *handle;
+ uio_GPFile *gPFile;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_open - pDirHandle=%p name=%s flags=%d mode=0%o\n",
+ (void *) pDirHandle, name, flags, mode);
+#endif
+
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ gPFile = (uio_GPFile *) uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (gPFile == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (gPFile->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ gPFile->extra) == -1) {
+ // errno is set
+ return NULL;
+ }
+ }
+#endif
+
+ handle = uio_malloc(sizeof (zip_Handle));
+ uio_GPFile_ref(gPFile);
+ handle->file = gPFile;
+ handle->fileBlock = uio_openFileBlock2(pDirHandle->pRoot->handle,
+ gPFile->extra->fileOffset, gPFile->extra->compressedSize);
+ if (handle->fileBlock == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (zip_initZipStream(&handle->zipStream) == -1) {
+ uio_GPFile_unref(gPFile);
+ uio_closeFileBlock(handle->fileBlock);
+ return NULL;
+ }
+ handle->compressedOffset = 0;
+ handle->uncompressedOffset = 0;
+
+ (void) mode;
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+zip_read(uio_Handle *handle, void *buf, size_t count) {
+ ssize_t result;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_read - handle=%p buf=%p count=%d: ", (void *) handle,
+ (void *) buf, count);
+#endif
+ result = zip_readMethods[handle->native->file->extra->compressionMethod]
+ (handle, buf, count);
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "%d\n", result);
+#endif
+ return result;
+}
+
+static ssize_t
+zip_readStored(uio_Handle *handle, void *buf, size_t count) {
+ int numBytes;
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ numBytes = uio_copyFileBlock(zipHandle->fileBlock,
+ zipHandle->uncompressedOffset, buf, count);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+ zipHandle->uncompressedOffset += numBytes;
+ zipHandle->compressedOffset += numBytes;
+ return numBytes;
+}
+
+static ssize_t
+zip_readDeflated(uio_Handle *handle, void *buf, size_t count) {
+ zip_Handle *zipHandle;
+ int inflateResult;
+
+ zipHandle = handle->native;
+
+ if (count > ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out)
+ count = ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out;
+
+ zipHandle->zipStream.next_out = (Bytef *) buf;
+ zipHandle->zipStream.avail_out = count;
+ while (zipHandle->zipStream.avail_out > 0) {
+ if (zipHandle->zipStream.avail_in == 0) {
+ ssize_t numBytes;
+ numBytes = uio_accessFileBlock(zipHandle->fileBlock,
+ zipHandle->compressedOffset, zip_INPUT_BUFFER_SIZE,
+ (char **) &zipHandle->zipStream.next_in);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+#if 0
+ if (numBytes == 0) {
+ if (zipHandle->uncompressedOffset !=
+ zipHandle->file->extra->uncompressedSize) {
+ // premature eof
+ errno = EIO;
+ return -1;
+ }
+ break;
+ }
+#endif
+ zipHandle->zipStream.avail_in = numBytes;
+ zipHandle->compressedOffset += numBytes;
+ }
+ inflateResult = inflate(&zipHandle->zipStream, Z_SYNC_FLUSH);
+ zipHandle->uncompressedOffset = zipHandle->zipStream.total_out;
+ if (inflateResult == Z_STREAM_END) {
+ // Everything is decompressed
+ break;
+ }
+ if (inflateResult != Z_OK) {
+ switch (inflateResult) {
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ case Z_NEED_DICT:
+ fprintf(stderr, "Error: Decompressing requires "
+ "preset dictionary.\n");
+ break;
+ case Z_DATA_ERROR:
+ fprintf(stderr, "Error: Compressed file is corrupted.\n");
+ break;
+ case Z_STREAM_ERROR:
+ // This means zipHandle->zipStream is bad, which is
+ // most likely an error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available "
+ "while decompressing.\n");
+ break;
+ case Z_BUF_ERROR:
+ // No progress possible. Probably caused by premature
+ // end of input file.
+ fprintf(stderr, "Error: When decompressing: premature "
+ "end of input file.\n");
+ errno = EIO;
+ return -1;
+#if 0
+ // If this happens, either the input buffer is empty
+ // or the output buffer is full. This should not happen.
+ fprintf(stderr, "Fatal: internal error using zlib: "
+ " no progress is possible in decompression.\n");
+ abort();
+ break;
+#endif
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflate().\n");
+ abort();
+ }
+ if (zipHandle->zipStream.msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n",
+ zipHandle->zipStream.msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ }
+ return count - zipHandle->zipStream.avail_out;
+}
+
+off_t
+zip_seek(uio_Handle *handle, off_t offset, int whence) {
+ zip_Handle *zipHandle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_seek - handle=%p offset=%d whence=%s\n",
+ (void *) handle, (int) offset,
+ whence == SEEK_SET ? "SEEK_SET" :
+ whence == SEEK_CUR ? "SEEK_CUR" :
+ whence == SEEK_END ? "SEEK_END" : "INVALID");
+#endif
+ zipHandle = handle->native;
+
+ assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
+ switch(whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += zipHandle->uncompressedOffset;
+ break;
+ case SEEK_END:
+ offset += zipHandle->file->extra->uncompressedSize;
+ break;
+ }
+ if (offset < 0) {
+ offset = 0;
+ } else if (offset > zipHandle->file->extra->uncompressedSize) {
+ offset = zipHandle->file->extra->uncompressedSize;
+ }
+ return zip_seekMethods[handle->native->file->extra->compressionMethod]
+ (handle, offset);
+}
+
+static off_t
+zip_seekStored(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ if (offset > zipHandle->file->extra->uncompressedSize)
+ offset = zipHandle->file->extra->uncompressedSize;
+
+ zipHandle->compressedOffset = offset;
+ zipHandle->uncompressedOffset = offset;
+ return offset;
+}
+
+static off_t
+zip_seekDeflated(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+
+ if (offset < zipHandle->uncompressedOffset) {
+ // The new offset is earlier than the current offset. We need to
+ // seek from the beginning.
+ if (zip_reInitZipStream(&zipHandle->zipStream) == -1) {
+ // Need to abort. Handle would get in an inconsistent state.
+ // Should not fail anyhow.
+ fprintf(stderr, "Fatal: Could not reinitialise zip stream: "
+ "%s.\n", strerror(errno));
+ abort();
+ }
+ zipHandle->compressedOffset = 0;
+ zipHandle->uncompressedOffset = 0;
+ }
+
+ if (offset == zipHandle->uncompressedOffset)
+ return offset;
+
+ // Seek from the current position.
+ {
+ char *buffer;
+ ssize_t numRead;
+ size_t toRead;
+
+ buffer = uio_malloc(zip_SEEK_BUFFER_SIZE);
+ toRead = offset - zipHandle->uncompressedOffset;
+ while (toRead > 0) {
+ numRead = zip_read(handle, buffer,
+ toRead < zip_SEEK_BUFFER_SIZE ?
+ toRead : zip_SEEK_BUFFER_SIZE);
+ if (numRead == -1) {
+ fprintf(stderr, "Warning: Could not read zipped file: %s\n",
+ strerror(errno));
+ break;
+ // The current location is returned.
+ }
+ toRead -= numRead;
+ }
+ uio_free(buffer);
+ }
+ return zipHandle->uncompressedOffset;
+}
+
+uio_PRoot *
+zip_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ uio_PDirHandle *rootDirHandle;
+
+ if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ uio_Handle_ref(handle);
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_ZIP), flags,
+ &zip_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, NULL, uio_GPDir_COMPLETE);
+
+ rootDirHandle = uio_PRoot_getRootDirHandle(result);
+ if (zip_fillDirStructure(rootDirHandle->extra, handle) == -1) {
+ int savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: failed to read the zip directory "
+ "structure - %s.\n", strerror(errno));
+#endif
+ uio_GPRoot_umount(result);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ return zip_fillDirStructureCentral(top, handle);
+#endif
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+ return zip_fillDirStructureLocal(top, handle);
+#endif
+}
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static int
+zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+ uio_uint16 numEntries;
+ // TODO: use numEntries to initialise the hash table
+ // to a smart size
+ off_t eocdr;
+ off_t startCentralDir;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ goto err;
+ }
+
+ // first find the 'End of Central Directory Record'
+ eocdr = zip_findEndOfCentralDirectoryRecord(handle, fileBlock);
+ if (eocdr == -1) {
+ // errno is set
+ goto err;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, eocdr, 22, &buf);
+ if (numBytes == -1) {
+ // errno is set
+ goto err;
+ }
+ if (numBytes != 22) {
+ errno = EIO;
+ goto err;
+ }
+
+ numEntries = makeUInt16(buf[10], buf[11]);
+ if (numEntries == 0xffff) {
+ fprintf(stderr, "Error: Zip64 .zip files are not supported.\n");
+ errno = ENOSYS;
+ goto err;
+ }
+
+ startCentralDir = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+
+ // Enable read-ahead buffering, for speed.
+ uio_setFileBlockUsageHint(fileBlock, uio_FB_USAGE_FORWARD,
+ DIR_STRUCTURE_READ_BUFSIZE);
+
+ pos = startCentralDir;
+ while (numEntries--) {
+ if (zip_fillDirStructureCentralProcessEntry(top, fileBlock, &pos)
+ == -1) {
+ // errno is set
+ goto err;
+ }
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ if (fileBlock != NULL)
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint32 signature;
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ uio_uint16 fileCommentLength;
+ char *fileName;
+ zip_OSType creatorOS;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 46, &buf);
+ if (numBytes != 46)
+ return zip_badFile(NULL, NULL);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x02014b50) {
+ fprintf(stderr, "Error: Premature end of central directory.\n");
+ errno = EIO;
+ return -1;
+ }
+
+ gPFileData = zip_GPFileData_new();
+ creatorOS = (zip_OSType) buf[5];
+ gPFileData->compressionFlags = makeUInt16(buf[8], buf[9]);
+ gPFileData->compressionMethod = makeUInt16(buf[10], buf[11]);
+ lastModTime = makeUInt16(buf[12], buf[13]);
+ lastModDate = makeUInt16(buf[14], buf[15]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ crc = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[20], buf[21], buf[22], buf[23]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[24], buf[25], buf[26], buf[27]);
+ fileNameLength = makeUInt16(buf[28], buf[29]);
+ extraFieldLength = makeUInt16(buf[30], buf[31]);
+ fileCommentLength = makeUInt16(buf[32], buf[33]);
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = zip_makeFileMode(creatorOS,
+ makeUInt32(buf[38], buf[39], buf[40], buf[41]));
+
+ gPFileData->headerOffset =
+ (off_t) makeSInt32(buf[42], buf[43], buf[44], buf[45]);
+ gPFileData->fileOffset = (off_t) -1;
+
+ *pos += 46;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ fileCommentLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+ *pos += fileNameLength;
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff ||
+ gPFileData->headerOffset < 0) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, true)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+
+ *pos += extraFieldLength;
+
+ // If ctime or atime is 0, they will be filled in when the local
+ // file header is read.
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+
+static off_t
+zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock) {
+ off_t fileSize;
+ off_t endPos, startPos;
+ char *buf, *bufPtr;
+ ssize_t bufLen;
+
+ struct stat statBuf;
+ if (uio_fstat(handle, &statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ fileSize = statBuf.st_size;
+ startPos = fileSize - 0xffff - 22; // max comment and record size
+ if (startPos < 0)
+ startPos = 0;
+ endPos = fileSize - 22; // last position to be checked
+ bufLen = uio_accessFileBlock(fileBlock, startPos, endPos - startPos + 4,
+ &buf);
+ if (bufLen == -1) {
+ int savedErrno = errno;
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = savedErrno;
+ return -1;
+ }
+ if (bufLen != endPos - startPos + 4) {
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ bufPtr = buf + (endPos - startPos);
+ while (1) {
+ if (bufPtr < buf) {
+ fprintf(stderr, "Error: Zip file corrupt; could not find "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ if (bufPtr[0] == 0x50 && bufPtr[1] == 0x4b && bufPtr[2] == 0x05 &&
+ bufPtr[3] == 0x06)
+ break;
+ bufPtr--;
+ }
+ return startPos + (bufPtr - buf);
+}
+
+static mode_t
+zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes) {
+ switch (creatorOS) {
+ case zip_OSType_FAT:
+ case zip_OSType_NTFS:
+ case zip_OSType_VFAT: {
+ // Only the least signigicant byte is relevant.
+ mode_t mode;
+
+ if (modeBytes == 0) {
+ // File came from standard input
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH;
+ }
+ if (modeBytes & 0x10) {
+ // Directory
+ mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH;
+ } else {
+ // Regular file
+ mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (modeBytes & 0x01) {
+ // readonly
+ return mode;
+ } else {
+ // Write allowed
+ return mode | S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ }
+ case zip_OSType_UNIX:
+ return (mode_t) (modeBytes >> 16);
+ default:
+ fprintf(stderr, "Warning: file created by unknown OS.\n");
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ }
+}
+
+// If the data is read from the central directory, certain data
+// will need to be updated from the local directory when it is needed.
+// This function does that.
+// returns 0 for success, -1 for error (errno is set)
+// NB: Only the fields that may offer new information are checked.
+// the fields that were already read from the central directory
+// aren't verified.
+static int
+zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos) {
+ ssize_t numBytes;
+ char *buf;
+ uio_uint32 signature;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 30, &buf);
+ if (numBytes != 30) {
+ errno = EIO;
+ return -1;
+ }
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ errno = EIO;
+ return -1;
+ }
+ fileNameLength = makeUInt16(buf[26], buf[27]);
+ extraFieldLength = makeUInt16(buf[28], buf[29]);
+ pos += 30 + fileNameLength;
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, "<unknown>", pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1:
+ // File is not acceptable (but according to the central header
+ // it was)
+ fprintf(stderr, "Warning: according to the central directory "
+ "of a zip file, some file inside is acceptable, "
+ "but according to the local header it isn't.\n");
+ errno = EIO;
+ return -1;
+ case -1:
+ errno = EIO;
+ return -1;
+ }
+ pos += extraFieldLength;
+ gPFileData->fileOffset = pos;
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int
+zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+
+ pos = uio_lseek(handle, 0, SEEK_SET);
+ if (pos == -1) {
+ int savedErrno = errno;
+ errno = savedErrno;
+ return -1;
+ }
+ if (pos != 0) {
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+
+ // We read all the files from the beginning of the zip file to the end.
+ // (the directory record at the end of the file is ignored)
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ pos = 0;
+ while (1) {
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes == -1)
+ goto err;
+ if (numBytes != 4)
+ break;
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ // End of file data reached.
+ break;
+ }
+ pos += 4;
+ if (zip_fillDirStructureLocalProcessEntry(top, fileBlock, &pos) == -1)
+ goto err;
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ char *fileName;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 26, &buf);
+ if (numBytes != 26)
+ return zip_badFile(NULL, NULL);
+
+ gPFileData = zip_GPFileData_new();
+ gPFileData->compressionFlags = makeUInt16(buf[2], buf[3]);
+ gPFileData->compressionMethod = makeUInt16(buf[4], buf[5]);
+ lastModTime = makeUInt16(buf[6], buf[7]);
+ lastModDate = makeUInt16(buf[8], buf[9]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!isBitSet(gPFileData->compressionFlags, 3)) {
+ // If bit 3 is not set, this info will be in the data descriptor
+ // behind the file data.
+ crc = makeUInt32(buf[10], buf[11], buf[12], buf[13]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[14], buf[15], buf[16], buf[17]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[18], buf[19], buf[20], buf[21]);
+ }
+ fileNameLength = makeUInt16(buf[22], buf[23]);
+ extraFieldLength = makeUInt16(buf[24], buf[25]);
+ *pos += 26;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // There's a data descriptor present behind the file data.
+ nextEntryOffset += 16;
+ }
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ *pos += fileNameLength;
+ if (buf[fileNameLength - 1] == '/') {
+ gPFileData->mode |= S_IFDIR;
+ fileNameLength--;
+ } else
+ gPFileData->mode |= S_IFREG;
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+ *pos += extraFieldLength;
+
+ gPFileData->fileOffset = *pos;
+
+ *pos += gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // Now comes a data descriptor.
+ // The PKWare version (which was never used) misses the signature.
+ // The InfoZip version is used below.
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 16, &buf);
+ if (numBytes != 16)
+ return zip_badFile(gPFileData, fileName);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x08074b50)
+ return zip_badFile(gPFileData, fileName);
+ crc = makeUInt32(buf[4], buf[5], buf[6], buf[7]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[8], buf[9], buf[10], buf[11]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[12], buf[13], buf[14], buf[15]);
+ }
+
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_LOCAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+int
+zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData) {
+ uio_FileBlock *fileBlock;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+ if (zip_updatePFileDataFromLocalFileHeader(gPFileData,
+ fileBlock, gPFileData->headerOffset) == -1) {
+ int savedErrno = errno;
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+ uio_closeFileBlock(fileBlock);
+ return 0;
+}
+#endif
+
+// If the zip file is bad, -1 is returned (no errno set!)
+// If the file in the zip file should be skipped, 1 is returned.
+// If the file in the zip file is ok, 0 is returned.
+static int
+zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock,
+ off_t extraFieldLength, zip_GPFileData *gPFileData,
+ const char *fileName, off_t pos, uio_bool central) {
+ off_t posEnd;
+ uio_uint16 headerID;
+ ssize_t dataSize;
+ ssize_t numBytes;
+ char *buf;
+
+ posEnd = pos + extraFieldLength;
+ while (pos < posEnd) {
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes != 4)
+ return -1;
+ headerID = makeUInt16(buf[0], buf[1]);
+ dataSize = (ssize_t) makeUInt16(buf[2], buf[3]);
+ pos += 4;
+ numBytes = uio_accessFileBlock(fileBlock, pos, dataSize, &buf);
+ if (numBytes != dataSize)
+ return -1;
+ switch(headerID) {
+ case 0x000d: // 'Unix0'
+ // fallthrough
+ case 0x5855: // 'Unix1'
+ gPFileData->atime = (time_t) makeUInt32(
+ buf[0], buf[1], buf[2], buf[3]);
+ gPFileData->mtime = (time_t) makeUInt32(
+ buf[4], buf[5], buf[6], buf[7]);
+ if (central)
+ break;
+ if (dataSize > 8) {
+ gPFileData->uid = (uid_t) makeUInt16(buf[8], buf[9]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[10], buf[11]);
+ }
+ // Unix0 has an extra (ignored) field at the end.
+ break;
+ case 0x5455: { // 'time'
+ uio_uint8 flags;
+ const char *bufPtr;
+ flags = buf[0];
+ bufPtr = buf + 1;
+ if (isBitSet(flags, 0)) {
+ // modification time is present
+ gPFileData->mtime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // The flags field, even in the central header, specifies what
+ // is present in the local header.
+ // The central header only contains a field for the mtime
+ // when it is present in the local header too, and
+ // never contains fields for other times.
+ if (central)
+ break;
+ if (isBitSet(flags, 1)) {
+ // modification time is present
+ gPFileData->atime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // Creation time and possible other times are skipped.
+ break;
+ }
+ case 0x7855: // 'Unix2'
+ if (central)
+ break;
+ gPFileData->uid = (uid_t) makeUInt16(buf[0], buf[1]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[2], buf[3]);
+ break;
+ case 0x756e: { // 'Unix3'
+ mode_t mode;
+ mode = (mode_t) makeUInt16(buf[4], buf[5]);
+ if (!S_ISREG(mode) && !S_ISDIR(mode)) {
+ fprintf(stderr, "Warning: Skipping '%s'; not a regular "
+ "file, nor a directory.\n", fileName);
+ return 1;
+ }
+ gPFileData->uid = (uid_t) makeUInt16(buf[10], buf[11]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[12], buf[13]);
+ break;
+ }
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "Debug: Extra field 0x%04x unsupported, "
+ "used for file '%s' - ignored.\n", headerID,
+ fileName);
+#endif
+ break;
+ } // switch
+ pos += dataSize;
+ } // while
+ if (pos != posEnd)
+ return -1;
+ return 0;
+}
+
+static int
+zip_badFile(zip_GPFileData *gPFileData, char *fileName) {
+ fprintf(stderr, "Error: Bad file format for .zip file.\n");
+ if (gPFileData != NULL)
+ zip_GPFileData_delete(gPFileData);
+ if (fileName != NULL)
+ uio_free(fileName);
+ errno = EINVAL; // Is this the best choice?
+ return -1;
+}
+
+static inline int
+zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) {
+ uio_GPFile *file;
+ size_t pathLen;
+ const char *rest;
+ const char *pathEnd;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ if (path[pathLen - 1] == '/') {
+ fprintf(stderr, "Warning: '%s' is not a valid file name - skipped.\n",
+ path);
+ errno = EISDIR;
+ return -1;
+ }
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The entire path was matched. The last part was not supposed
+ // to be a dir.
+ fprintf(stderr, "Warning: '%s' already exists as a dir - "
+ "skipped.\n", path);
+ errno = EISDIR;
+ return -1;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component to '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (1) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: file '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ if (end == pathEnd) {
+ // This is the last component; it should be the name of the dir.
+ rest = start;
+ break;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+
+ uio_free(buf);
+
+ file = uio_GPFile_new(gPDir->pRoot, (uio_GPFileExtra) gPFileData,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, rest, file);
+ return 0;
+}
+
+static inline int
+zip_foundDir(uio_GPDir *gPDir, const char *path, zip_GPDirData *gPDirData) {
+ size_t pathLen;
+ const char *pathEnd;
+ const char *rest;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The dir already exists. Only need to add gPDirData
+ if (gPDir->extra != NULL) {
+ fprintf(stderr, "Warning: '%s' is present more than once "
+ "in the zip file. The last entry will be used.\n",
+ path);
+ zip_GPDirData_delete(gPDir->extra);
+ }
+ gPDir->extra = gPDirData;
+ return 0;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component of '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: directory '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ gPDir->extra = gPDirData;
+
+ uio_free(buf);
+ return 0;
+}
+
+static int
+zip_initZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ zipStream->zalloc = zip_alloc;
+ zipStream->zfree = zip_free;
+ zipStream->opaque = NULL;
+ retVal = inflateInit2(zipStream, -MAX_WBITS);
+ // Negative window size means that no zlib header is present.
+ // This feature is undocumented in zlib, but it's used
+ // in the minizip program from the zlib contrib dir.
+ // The absolute value is used as real Window size.
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available for "
+ " decompression.\n");
+ break;
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_unInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ retVal = inflateEnd(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateEnd().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_reInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ retVal = inflateReset(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+
+// Used internally by zlib for allocating memory.
+static voidpf
+zip_alloc(voidpf opaque, uInt items, uInt size) {
+ (void) opaque;
+ return (voidpf) uio_calloc((size_t) items, (size_t) size);
+}
+
+// Used internally by zlib for freeing memory.
+static void
+zip_free(voidpf opaque, voidpf address) {
+ (void) opaque;
+ uio_free((void *) address);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_new(void) {
+ return zip_GPFileData_alloc();
+}
+
+static inline void
+zip_GPFileData_delete(zip_GPFileData *gPFileData) {
+ zip_GPFileData_free(gPFileData);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_alloc(void) {
+ zip_GPFileData *result = uio_malloc(sizeof (zip_GPFileData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(zip_GPFileData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+zip_GPFileData_free(zip_GPFileData *gPFileData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPFileData);
+#endif
+ uio_free(gPFileData);
+}
+
+static inline void
+zip_GPDirData_delete(zip_GPDirData *gPDirData) {
+ zip_GPDirData_free(gPDirData);
+}
+
+static inline void
+zip_GPDirData_free(zip_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData);
+}
+
+
+
diff --git a/src/libs/uio/zip/zip.h b/src/libs/uio/zip/zip.h
new file mode 100644
index 0000000..4b2762f
--- /dev/null
+++ b/src/libs/uio/zip/zip.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+typedef struct zip_Handle *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef struct zip_GPFileData *uio_GPFileExtra;
+typedef struct zip_GPFileData *uio_GPDirExtra;
+typedef struct uio_GPDirEntries_Iterator *uio_NativeEntriesContext;
+
+#define uio_INTERNAL_PHYSICAL
+
+#include "../gphys.h"
+#include "../iointrn.h"
+#include "../uioport.h"
+#include "../physical.h"
+#include "../types.h"
+#include "../fileblock.h"
+
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// zip_USE_HEADERS determinines what header for files within a .zip file
+// is used when building the directory structure.
+// Set to 'zip_USE_CENTRAL_HEADERS' to use the central directory header,
+// set to 'zip_USE_LOCAL_HEADERS' to use the local file header.
+// Central is highly adviced: it uses much less seeking, and hence is much
+// faster.
+#define zip_USE_HEADERS zip_USE_CENTRAL_HEADERS
+#define zip_USE_CENTRAL_HEADERS 1
+#define zip_USE_LOCAL_HEADERS 2
+
+#define zip_INCOMPLETE_STAT
+ // Ignored unless zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS.
+ // If defined, extra meta-data for files in the .zip archive
+ // isn't retrieved from the local file header when zip_stat()
+ // is called. The uid, gid, file mode, and file times may be
+ // inaccurate. The advantage is that a possibly costly seek and
+ // read can be avoided.
+
+typedef struct zip_GPFileData {
+ off_t compressedSize;
+ off_t uncompressedSize;
+ uio_uint16 compressionFlags;
+ uio_uint16 compressionMethod;
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ off_t headerOffset; // start of the local header for this file
+#endif
+ off_t fileOffset; // start of the compressed data in the .zip file
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ time_t atime; // access time
+ time_t mtime; // modification time
+ time_t ctime; // change time
+} zip_GPFileData;
+
+typedef zip_GPFileData zip_GPDirData;
+// TODO: some of the fields from zip_GPFileData are not needed for
+// directories. A few bytes could be saved here by making a seperate
+// structure.
+
+typedef struct zip_Handle {
+ uio_GPFile *file;
+ z_stream zipStream;
+ uio_FileBlock *fileBlock;
+ off_t uncompressedOffset;
+ // seek location in the uncompressed stream
+ off_t compressedOffset;
+ // seek location in the compressed stream, from the start
+ // of the compressed file
+} zip_Handle;
+
+
+uio_PRoot *zip_mount(uio_Handle *handle, int flags);
+int zip_umount(struct uio_PRoot *);
+uio_Handle *zip_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode);
+void zip_close(uio_Handle *handle);
+int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int zip_fstat(uio_Handle *handle, struct stat *statBuf);
+int zip_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf);
+ssize_t zip_read(uio_Handle *handle, void *buf, size_t count);
+off_t zip_seek(uio_Handle *handle, off_t offset, int whence);
+
+
+
+