summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad Bishop <bradleyb@us.ibm.com>2014-06-30 22:10:16 -0500
committerPatrick Williams <iawillia@us.ibm.com>2014-07-02 22:49:29 -0500
commitbf4630076762d9c20c16c80c1c791f352b046dd1 (patch)
treeefc67bad010a28fd1abf5aeeefda2a969514fd97
downloadffs-bf4630076762d9c20c16c80c1c791f352b046dd1.tar.gz
ffs-bf4630076762d9c20c16c80c1c791f352b046dd1.zip
Port FFS tools over from Building Block repository.
-rw-r--r--LICENSE339
-rw-r--r--Makefile15
-rw-r--r--NOTICE3
-rw-r--r--clib/.gitignore21
-rw-r--r--clib/Doxyfile1721
-rw-r--r--clib/Makefile10
-rw-r--r--clib/Rules.cunit.mk21
-rw-r--r--clib/Rules.mk50
-rw-r--r--clib/Rules.test.mk69
-rw-r--r--clib/align.h49
-rw-r--r--clib/array.h439
-rw-r--r--clib/array_iter.h216
-rw-r--r--clib/assert.h42
-rw-r--r--clib/attribute.h105
-rw-r--r--clib/bb_trace.h419
-rw-r--r--clib/bitset.h359
-rw-r--r--clib/builtin.h136
-rw-r--r--clib/checksum.h43
-rw-r--r--clib/compare.h43
-rw-r--r--clib/crc32.h54
-rw-r--r--clib/cunit/clib.c49
-rw-r--r--clib/cunit/ecc.c91
-rw-r--r--clib/cunit/ecc.h19
-rw-r--r--clib/cunit/map.c301
-rw-r--r--clib/cunit/map.h19
-rw-r--r--clib/cunit/slab.c63
-rw-r--r--clib/cunit/slab.h19
-rw-r--r--clib/cunit/splay.c384
-rw-r--r--clib/cunit/splay.h19
-rw-r--r--clib/cunit/tree.c398
-rw-r--r--clib/cunit/tree.h19
-rw-r--r--clib/cunit/vector.c169
-rw-r--r--clib/cunit/vector.h19
-rw-r--r--clib/db.h442
-rw-r--r--clib/dispatch.h93
-rw-r--r--clib/ecc.h217
-rw-r--r--clib/err.h290
-rw-r--r--clib/exception.h299
-rw-r--r--clib/hash.h248
-rw-r--r--clib/heap.h70
-rw-r--r--clib/ident.h53
-rw-r--r--clib/libclib.h42
-rw-r--r--clib/list.h537
-rw-r--r--clib/list_iter.h227
-rw-r--r--clib/map.h238
-rw-r--r--clib/map_iter.h157
-rw-r--r--clib/max.h38
-rw-r--r--clib/memory_leak_detection.h387
-rw-r--r--clib/min.h38
-rw-r--r--clib/misc.h73
-rw-r--r--clib/mq.h144
-rw-r--r--clib/nargs.h51
-rw-r--r--clib/queue.h57
-rw-r--r--clib/raii.h46
-rw-r--r--clib/signal.h72
-rw-r--r--clib/slab.h222
-rw-r--r--clib/src/array.c490
-rw-r--r--clib/src/array_iter.c180
-rw-r--r--clib/src/checksum.c43
-rw-r--r--clib/src/crc32.c150
-rw-r--r--clib/src/crc32_main.c76
-rw-r--r--clib/src/db.c400
-rw-r--r--clib/src/dispatch.c155
-rw-r--r--clib/src/ecc.c456
-rw-r--r--clib/src/err.c208
-rw-r--r--clib/src/exception.c145
-rw-r--r--clib/src/heap.c152
-rw-r--r--clib/src/list.c49
-rw-r--r--clib/src/list_iter.c139
-rw-r--r--clib/src/memory_leak_detection.c1277
-rw-r--r--clib/src/misc.c234
-rw-r--r--clib/src/mq.c208
-rw-r--r--clib/src/signal.c135
-rw-r--r--clib/src/slab.c388
-rw-r--r--clib/src/table.c679
-rw-r--r--clib/src/table_iter.c138
-rw-r--r--clib/src/timer.c133
-rw-r--r--clib/src/trace_indent.c185
-rw-r--r--clib/src/tree.c630
-rw-r--r--clib/src/tree_iter.c137
-rw-r--r--clib/src/value.c143
-rw-r--r--clib/src/vector.c662
-rw-r--r--clib/src/vector_iter.c177
-rw-r--r--clib/src/watch.c135
-rw-r--r--clib/stack.h99
-rw-r--r--clib/table.h272
-rw-r--r--clib/table_iter.h172
-rw-r--r--clib/test/array.c70
-rw-r--r--clib/test/checksum.c117
-rw-r--r--clib/test/dispatch.c63
-rw-r--r--clib/test/ecc.c220
-rw-r--r--clib/test/err.c73
-rw-r--r--clib/test/exc_throw.c47
-rw-r--r--clib/test/exception.c73
-rw-r--r--clib/test/heap.c38
-rw-r--r--clib/test/list.c71
-rw-r--r--clib/test/map.c76
-rw-r--r--clib/test/mq.c74
-rw-r--r--clib/test/slab.c51
-rw-r--r--clib/test/splay.c96
-rw-r--r--clib/test/table.c102
-rw-r--r--clib/test/tree.c84
-rw-r--r--clib/test/vector.c83
-rw-r--r--clib/test/watch.c44
-rw-r--r--clib/timer.h77
-rw-r--r--clib/trace_indent.h130
-rw-r--r--clib/tree.h455
-rw-r--r--clib/tree_iter.h188
-rw-r--r--clib/type.h82
-rw-r--r--clib/value.h558
-rw-r--r--clib/vector.h432
-rw-r--r--clib/vector_iter.h181
-rw-r--r--clib/version.h32
-rw-r--r--clib/watch.h87
-rw-r--r--clib/x86/Makefile5
-rw-r--r--clib/x86/cunit/Makefile7
-rw-r--r--clib/x86/test/Makefile7
-rw-r--r--ecc/.gitignore1
-rw-r--r--ecc/Makefile10
-rw-r--r--ecc/Rules.mk20
-rw-r--r--ecc/src/main.c625
-rw-r--r--ecc/src/main.h78
-rw-r--r--ecc/x86/Makefile6
-rw-r--r--fcp/.gitignore3
-rw-r--r--fcp/Makefile10
-rw-r--r--fcp/Rules.mk30
-rwxr-xr-xfcp/fcp.sh313
-rw-r--r--fcp/ppc/Makefile9
-rw-r--r--fcp/src/cmd_copy.c582
-rw-r--r--fcp/src/cmd_erase.c203
-rw-r--r--fcp/src/cmd_list.c166
-rw-r--r--fcp/src/cmd_probe.c179
-rw-r--r--fcp/src/cmd_read.c187
-rw-r--r--fcp/src/cmd_trunc.c163
-rw-r--r--fcp/src/cmd_user.c201
-rw-r--r--fcp/src/cmd_write.c202
-rw-r--r--fcp/src/main.c709
-rw-r--r--fcp/src/main.h120
-rw-r--r--fcp/src/misc.c937
-rw-r--r--fcp/src/misc.h72
-rw-r--r--fcp/x86/Makefile7
-rw-r--r--ffs/.gitignore3
-rw-r--r--ffs/Makefile10
-rw-r--r--ffs/Rules.mk30
-rw-r--r--ffs/Rules.test.mk25
-rw-r--r--ffs/ffs.h148
-rw-r--r--ffs/libffs.h174
-rw-r--r--ffs/libffs2.h332
-rw-r--r--ffs/src/ffs-fsp.h59
-rw-r--r--ffs/src/libffs.c1520
-rw-r--r--ffs/src/libffs2.c489
-rw-r--r--ffs/test/Makefile8
-rwxr-xr-xffs/test/ffs_tool_test.sh244
-rwxr-xr-xffs/test/libffs_test.sh248
-rw-r--r--ffs/test/test_libffs.c654
-rw-r--r--ffs/test/test_libffs.h45
-rw-r--r--ffs/x86/Makefile7
-rw-r--r--fpart/.gitignore1
-rw-r--r--fpart/Makefile10
-rw-r--r--fpart/Rules.mk30
-rw-r--r--fpart/Rules.test.mk25
-rwxr-xr-xfpart/fpart.sh398
-rw-r--r--fpart/ppc/Makefile9
-rw-r--r--fpart/src/cmd_add.c102
-rw-r--r--fpart/src/cmd_compare.c231
-rw-r--r--fpart/src/cmd_copy.c282
-rw-r--r--fpart/src/cmd_create.c113
-rw-r--r--fpart/src/cmd_delete.c78
-rw-r--r--fpart/src/cmd_erase.c206
-rw-r--r--fpart/src/cmd_hexdump.c153
-rw-r--r--fpart/src/cmd_list.c168
-rw-r--r--fpart/src/cmd_read.c166
-rw-r--r--fpart/src/cmd_trunc.c134
-rw-r--r--fpart/src/cmd_user.c156
-rw-r--r--fpart/src/cmd_write.c229
-rw-r--r--fpart/src/command.c296
-rw-r--r--fpart/src/main.c597
-rw-r--r--fpart/src/main.h125
-rw-r--r--fpart/x86/Makefile7
179 files changed, 34179 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2869524
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+SUBDIRS = clib fcp ecc ffs fpart
+
+.PHONY: subdirs $(SUBDIRS)
+subdirs: $(SUBDIRS)
+
+fpart:: ffs clib
+fcp:: ffs clib
+ecc:: clib
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all install: $(SUBDIRS)
+
+clean distclean: $(SUBDIRS)
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..5982501
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,3 @@
+All files contained within are licensed under the terms of the GNU
+General Public License v2.0. A copy may be obtained at:
+ http://www.gnu.org/licenses/gpl-2.0
diff --git a/clib/.gitignore b/clib/.gitignore
new file mode 100644
index 0000000..1a7d377
--- /dev/null
+++ b/clib/.gitignore
@@ -0,0 +1,21 @@
+*.gch
+*.a
+*.o
+*.so*
+*/test/array
+*/test/dispatch
+*/test/exception
+*/test/list
+*/test/map
+*/test/mq
+*/test/slab
+*/test/splay
+*/test/table
+*/test/tree
+*/test/vector
+*/test/watch
+*/test/checksum
+*/test/ecc
+*/test/err
+*/cunit/clib
+*/crc32
diff --git a/clib/Doxyfile b/clib/Doxyfile
new file mode 100644
index 0000000..f355ead
--- /dev/null
+++ b/clib/Doxyfile
@@ -0,0 +1,1721 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME =
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is adviced to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# If the HTML_TIMESTAMP tag is set to YES then the generated HTML
+# documentation will contain the timesstamp.
+
+HTML_TIMESTAMP = NO
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/clib/Makefile b/clib/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/clib/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/clib/Rules.cunit.mk b/clib/Rules.cunit.mk
new file mode 100644
index 0000000..961412a
--- /dev/null
+++ b/clib/Rules.cunit.mk
@@ -0,0 +1,21 @@
+CFLAGS += -m32 -std=gnu99 -D__USE_GNU -I$(DEPTH)/apps
+LDFLAGS = -rdynamic -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR)
+
+TARGETS=clib
+
+OBJS=slab.o splay.o map.o vector.o ecc.o tree.o clib.o
+
+vpath %.h ../../src
+vpath %.c ../../cunit
+
+all: $(TARGETS)
+
+clib: $(OBJS) $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a
+ $(CC) $(LDFLAGS) -o $@ $^ -lcunit -lrt
+
+clean:
+ $(RM) $(OBJS) $(TARGETS)
+
+install:
+ $(INSTALL) -d $(CLIB_INSTALL)
+ $(INSTALL) $(TARGETS) $(CLIB_INSTALL)
diff --git a/clib/Rules.mk b/clib/Rules.mk
new file mode 100644
index 0000000..308d1d5
--- /dev/null
+++ b/clib/Rules.mk
@@ -0,0 +1,50 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -fshort-enums -D_FILE_OFFSET_BITS=64
+CFLAGS += -iquote$(DEPTH)
+
+LDFLAGS=-L. -m32
+
+# array.o array_iter.o dispatch.o watch.o signal.o heap.o
+
+OBJS = err.o crc32.o misc.o ecc.o \
+ exception.o slab.o \
+ list.o list_iter.o \
+ tree.o tree_iter.o \
+ vector.o vector_iter.o \
+ table.o table_iter.o \
+ value.o mq.o \
+ memory_leak_detection.o \
+ trace_indent.o checksum.o
+
+
+TARGETS=libclib.so libclib.a crc32
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+libclib.so: $(OBJS)
+ $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $^ -lpthread \
+ -lrt
+
+libclib.a: $(OBJS)
+ $(AR) -r $@ $^
+
+db.o: db.c db.h
+ $(CC) $(CFLAGS) -DSQLITE3 -c $^
+
+crc32: crc32.c crc32_main.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean distclean:
+ $(RM) *.so *.o *.gch *.CKP *.lst $(TARGETS)
+ $(RM) html latex
+
+install: $(TARGETS) ../*.h
+ $(INSTALL) -d $(CLIB_INSTALL)/include/clib $(CLIB_INSTALL)/{bin,lib}
+ $(INSTALL) ../crc32.h $(CLIB_INSTALL)/include
+ $(INSTALL) ../*.h $(CLIB_INSTALL)/include/clib
+ $(INSTALL) libclib.so libclib.a $(CLIB_INSTALL)/lib
+ $(INSTALL) crc32 $(CLIB_INSTALL)/bin
+ $(MAKE) -C test $@
+ $(MAKE) -C cunit $@
diff --git a/clib/Rules.test.mk b/clib/Rules.test.mk
new file mode 100644
index 0000000..35ab9a4
--- /dev/null
+++ b/clib/Rules.test.mk
@@ -0,0 +1,69 @@
+CFLAGS += -m32 -std=gnu99 -D__USE_GNU -I$(DEPTH)/apps
+LDFLAGS = -rdynamic -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR)
+
+TARGETS=exception \
+ slab \
+ list \
+ ecc \
+ tree \
+ splay \
+ map \
+ table \
+ mq \
+ vector \
+ checksum \
+ err
+
+vpath %.h ../../src
+vpath %.c ../../test
+
+all: $(TARGETS)
+
+exception: exception.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+slab: slab.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+list: list.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+tree: tree.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+splay: splay.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+map: map.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+mq: mq.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+table: table.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+watch: watch.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+dispatch: dispatch.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+vector: vector.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+checksum: checksum.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+err: err.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ecc: ecc.o ../libclib.so
+ $(CC) $(LDFLAGS) -o $@ $^
+
+clean:
+ $(RM) $(TARGETS:=.o) $(TARGETS)
+
+install:
+ $(INSTALL) -d $(CLIB_INSTALL)
+ $(INSTALL) $(TARGETS) $(CLIB_INSTALL)
diff --git a/clib/align.h b/clib/align.h
new file mode 100644
index 0000000..b2a8a6c
--- /dev/null
+++ b/clib/align.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file align.h
+ * @brief Alignment helpers
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __ALIGN_H__
+#define __ALIGN_H__
+
+/*!
+ * @def alignof(t)
+ * @hideinitializer
+ * @brief Returns the alignment of an object or minimum alignment required by a type
+ * @param t [in] Object or type name
+ */
+#define alignof(t) __alignof__(t)
+
+#ifndef offsetof
+
+/*!
+ * @def offsetof(t,m)
+ * @hideinitializer
+ * @brief Returns the offset of a member within a structure
+ * @param t [in] Structure type name
+ * @param m [in] Member name within a structure
+ */
+#define offsetof(t,m) __builtin_offsetof(t, m)
+#endif
+
+#endif /* __ALIGN_H__ */
diff --git a/clib/array.h b/clib/array.h
new file mode 100644
index 0000000..8fce5ee
--- /dev/null
+++ b/clib/array.h
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file array.h
+ * @brief Array container
+ * @details An array class whose elements are partitioned into data `pages'
+ * that are allocated upon first-touch, possibly leading to a pages
+ * that are sparsely populated.
+ * @details For example,
+ * @code
+ * #include <clib/array.h>
+ * #include <clib/array_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * array_t a;
+ * array_init(&a, 4, 1024);
+ *
+ * array_dump(&a, stdout);
+ *
+ * array_put(&a, 52, (uint32_t[]){52});
+ * array_put(&a, 53, (uint32_t[]){53});
+ * array_put(&a, 167, (uint32_t[]){167});
+ * array_put(&a, 223, (uint32_t[]){223});
+ * array_put(&a, 78, (uint32_t[]){78});
+ * array_put(&a, 205, (uint32_t[]){205});
+ * array_put(&a, 183, (uint32_t[]){183});
+ * array_put(&a, 150, (uint32_t[]){150});
+ * array_put(&a, 90, (uint32_t[]){90});
+ * array_put(&a, 66, (uint32_t[]){66});
+ * array_put(&a, 91, (uint32_t[]){91});
+ * array_put(&a, 45, (uint32_t[]){45});
+ * array_put(&a, 211, (uint32_t[]){211});
+ *
+ * uint32_t arr[] = {985,986,987,988,990,991,992,993,994};
+ * array_put(&a, 985, arr, 9);
+ *
+ * array_iter_t it;
+ * array_iter_init(&it, &a, AI_FLAG_FWD);
+ *
+ * uint32_t * j;
+ * array_for_each(&it, j) {
+ * printf("arr[%d]\n", *j);
+ * }
+ *
+ * array_dump(&a, stdout);
+ * array_delete(&a);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __ARRAY_H__
+#define __ARRAY_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "builtin.h"
+#include "mqueue.h"
+#include "tree.h"
+#include "nargs.h"
+
+/* ======================================================================= */
+
+typedef struct array array_t; //!< Alias for the @em array class
+
+/*!
+ * @brief Array container class
+ */
+struct array {
+ uint32_t magic; //!< Array magic number
+
+ uint32_t page_size; //!< Array data page size (in bytes)
+
+ uint16_t elem_size; //!< Array element size (in bytes)
+ uint16_t elem_num; //!< Array element count (per page)
+
+ size_t size; //!< Number of initialized elements
+ size_t pages; //!< Number of data pages allocated (currently)
+
+ size_t low; //!< @private
+ size_t high; //!< @private
+
+ size_t map_size; //!< @private
+ tree_t tree; //!< @private
+};
+
+/* ======================================================================= */
+
+/*!
+ * @brief Constructs an @em array container object
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param elem_size [in] array element size, in bytes
+ * @param elem_num [in] array element number, per data page
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void array_init(array_t * self, size_t elem_size, size_t elem_num)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Destructs an @em array container object
+ * @details Deallocate all backing storage associated with this \em array object
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 524, &count);
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return None
+ */
+extern void array_delete(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Returns a reference to the element at position @em idx in the @em array
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 524, &count);
+ * printf("ar[524] = %d\n", *(int *)array_at(&ar, 524));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param elem_num [in] array element index
+ * @return Reference to array element at @em idx on SUCCESS
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if array element at @em idx is uninitialized
+ */
+extern const void *array_at(array_t * self, size_t elem_num)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn void array_get(array_t * self, size_t elem_off, const void * ptr, size_t elem_num=1)
+ * @brief Copy content from the @em array
+ * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em array to destination pointer @em ptr
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 524, &count);
+ * array_get(&ar, 524, &count);
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param elem_off [in] array element index
+ * @param ptr [out] Destination storage pointer
+ * @param elem_num [in] Desgination element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define array_put(...) STRCAT(array_put, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void array_get3(array_t * self, size_t elem_off, void *ptr)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern void array_get4(array_t * self, size_t elem_off, void *ptr,
+ size_t elem_num)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn void array_put(array_t * self, size_t elem_off, const void * ptr, size_t elem_num=1)
+ * @brief Assign new content to the @em array
+ * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em array starting at position @em elem_off
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 524, &count);
+ * array_get(&ar, 524, &count);
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param elem_off [in] array element index
+ * @param ptr [in] Source storage pointer
+ * @param elem_num [in] Source element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define array_put(...) STRCAT(array_put, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void array_put3(array_t * self, size_t elem_off, const void *ptr)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern void array_put4(array_t * self, size_t elem_off, const void *ptr,
+ size_t elem_num)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn bool array_status(array_t * self, size_t elem_num, bool state)
+ * @brief Set or return the initialized status of the @em array element at position @em idx
+ * @note If the third parameter is omitted, the element's current status is returned
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 524, &count);
+ * printf("%d %d\n", array_status(&ar, 524), array_status(&ar, 600));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param elem_num [in] array element index
+ * @param state [in] Element state (optional)
+ * @return None
+ * @throws UNEXPECTED if @em idx is out of bounds
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define array_status(...) STRCAT(array_status, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern bool array_status2(array_t * self, size_t elem_num)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern bool array_status3(array_t * self, size_t elem_num, bool state)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn size_t array_size(array_t * self, size_t size = 1)
+ * @brief Return or set the size of the @em array
+ * @details Return or set the number of allocated elements in the @em array
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_size(&ar, 2040);
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @param size [in] New array size
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+#define array_size(...) STRCAT(array_size, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern size_t array_size1(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void array_size2(array_t * self, size_t size)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return pages of the @em array container
+ * @details Return the number of pages in the @em array container
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_size(&ar, 2040);
+ * printf("pages = %d\n", array_pages(&ar));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return The number of pages that conform the array's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t array_pages(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return capacity of the @em array
+ * @details Return the number of allocated and unallocated elements in the @em array container
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_size(&ar, 2040);
+ * printf("capacity = %d\n", array_capacity(&ar));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return The number of total elements that conform the array's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t array_capacity(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return @em array index lower bound
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 7, &count);
+ * array_put(&ar, 7000, &count);
+ * printf("low = %d\n", array_low(&ar));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t array_low(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return @em array index upper bound
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 7, &count);
+ * array_put(&ar, 7000, &count);
+ * printf("high = %d\n", array_high(&ar));
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t array_high(array_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Send (write) an @em array object to a message queue
+ * @details For example,
+ * @code
+ * ...
+ * array_init(&ar, 4, 1024);
+ * array_put(&ar, 7, &count);
+ * array_put(&ar, 7000, &count);
+ * ...
+ * mqueue mq;
+ * mqueue_init(&mq, "my_server");
+ * mqueue_create(&mq, gettid());
+ * ...
+ * array_send(&ar, &mq);
+ * array_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void array_send(array_t * self, mqueue_t * mq)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief receive (read) an @em array object from a message queue
+ * @details For example,
+ * @code
+ * ...
+ * array ar;
+ * ...
+ * mqueue mq;
+ * mqueue_open(&mq, path);
+ * ...
+ * array_receive(&ar, &mq);
+ * array_dump(&ar);
+ * ...
+ * @endcode
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void array_receive(array_t * self, mqueue_t * mq)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print the contents of an @em array to stdout
+ * @memberof array
+ * @param self [in] array object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void array_dump(array_t * self, FILE * out)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#endif /* __ARRAY_H__ */
diff --git a/clib/array_iter.h b/clib/array_iter.h
new file mode 100644
index 0000000..efb6e0c
--- /dev/null
+++ b/clib/array_iter.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file array_iter.h
+ * @brief Sparse Array Iterator
+ * @details An array class whose elements are partitioned into data `pages'
+ * that are allocated upon first-touch.
+ * @code
+ * ...
+ * array_t arr;
+ * array_init(&arr, sizeof(uint32_t), 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &arr, VI_FLAG_FWD);
+ *
+ * uint32_t * j;
+ * array_for_each(&it, j) {
+ * printf("arr[%d] = %d\n", it.idx, *j);
+ * }
+ * ...
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __ARRAY_ITER_H__
+#define __ARRAY_ITER_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "builtin.h"
+#include "array.h"
+
+/* ======================================================================= */
+
+typedef struct array_iter array_iter_t; //!< Alias for the @em array_iter class
+
+/*!
+ * @brief Array iterator
+ * @details Array iterator class
+*/
+struct array_iter {
+ array_t *array; //!< Reference to the target array object
+ int32_t idx; //!< Current position of the iterator
+ uint32_t flags; //!< Iterator configuration flags
+};
+
+/* ======================================================================= */
+
+#define AI_FLAG_NONE 0x00000000 //!< No flag mask
+#define AI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask
+#define AI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask
+#define AI_FLAG_MASK 0x00000003 //!< All flags mask
+
+/*!
+ * @brief Initializes an @em array_iter iterator object
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &ar, AI_FLAG_FWD);
+ * ...
+ * @endcode
+ * @memberof array_iter
+ * @param self [in] array_iter object @em self pointer
+ * @param array [in] array container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em array pointer is NULL
+ */
+extern void array_iter_init(array_iter_t * self, array_t * array,
+ uint32_t flags)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets an @em array iterator object
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &ar, AI_FLAG_FWD);
+ * array_iter_clear(&it);
+ * ...
+ * @endcode
+ * @memberof array_iter
+ * @param self [in] array_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void array_iter_clear(array_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to @em array element bytes at the current iterator position
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &ar, AI_FLAG_FWD);
+ * ...
+ * data_t * d = (data_t *)array_iter_elem(&it);
+ * ...
+ * @endcode
+ * @memberof array_iter
+ * @param self [in] array_iter object @em self pointer
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern const void *array_iter_elem(array_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn const void * array_iter_inc(array_iter_t * self, size_t count = 1)
+ * @brief Increment the position of an @em array iterator
+ * @details If the second (2nd) parameter is omitted, the iterator is incremented by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &ar, AI_FLAG_FWD);
+ * array_iter_inc(&it);
+ * ...
+ * @endcode
+ * @memberof array_iter
+ * @param self [in] array_iter object @em self pointer
+ * @param count [in] Number of positions to increment (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define array_iter_inc(...) STRCAT(array_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const void *array_iter_inc1(array_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const void *array_iter_inc2(array_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn const void * array_iter_dec(array_iter_t * self, size_t count = 1)
+ * @brief decrement the position of an @em array iterator
+ * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * array_t ar;
+ * array_init(&ar, 4, 1024);
+ * ...
+ * array_iter_t it;
+ * array_iter_init(&it, &ar, AI_FLAG_FWD);
+ * array_iter_dec(&it, 3);
+ * ...
+ * @endcode
+ * @memberof array_iter
+ * @param self [in] array_iter object @em self pointer
+ * @param count [in] Number of positions to decrement (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define array_iter_dec(...) STRCAT(array_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const void *array_iter_dec1(array_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const void *array_iter_dec2(array_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @def array_for_each(it, i)
+ * @hideinitializer
+ * @brief Array for-each algorithm
+ * @param it [in] Tree iterator object
+ * @param i [in] Tree element variable
+ */
+#define array_for_each(it, i) \
+ for (i = (typeof(i))array_iter_elem(it); \
+ i != NULL; \
+ i = (typeof(i))array_iter_inc(it, 1))
+
+/* ======================================================================= */
+
+#endif /* __ARRAY_ITER_H__ */
diff --git a/clib/assert.h b/clib/assert.h
new file mode 100644
index 0000000..8740eda
--- /dev/null
+++ b/clib/assert.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file assert.h
+ * @brief Assertion helpers
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifdef assert
+#undef assert
+#endif
+
+/*!
+ * @def assert(e)
+ * @hideinitializer
+ * @brief Check for program assertion failures
+ * @param e [in] assertion expression
+ * @throws ASSERTION iff expression @em e evaluates @em false
+ */
+
+#ifdef NDEBUG
+#define assert(e) ((void)0)
+#else
+#include "exception.h"
+#define assert(e) ((void)((e) ? (void)0 : throw_bytes(ASSERTION, __STRING((e)), strlen(__STRING((e))))))
+#endif
diff --git a/clib/attribute.h b/clib/attribute.h
new file mode 100644
index 0000000..2213096
--- /dev/null
+++ b/clib/attribute.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: attribute.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: GCC attribute helpers
+ * Note:
+ * Date: 08/29/10
+ */
+/*! @file attribute.h
+ * @brief Attribute helpers
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __ATTRIBUTE_H__
+#define __ATTRIBUTE_H__
+
+/*!
+ * @def __constructor
+ * @hideinitializer
+ * @brief Constructor function attribute
+ */
+#define __constructor __attribute__ ((constructor))
+
+/*!
+ * @def __destructor
+ * @hideinitializer
+ * @brief Desstructor function attribute
+ */
+#define __destructor __attribute__ ((destructor))
+
+/*!
+ * @def __deprecated
+ * @hideinitializer
+ * @brief Deprecated function attribute
+ */
+#define __deprecated __attribute__ ((deprecated))
+
+/*!
+ * @def __inline
+ * @hideinitializer
+ * @brief Inline function attribute
+ */
+#define __inline __attribute__ ((always_inline))
+
+/*!
+ * @def __used
+ * @hideinitializer
+ * @brief Used identifier attribute
+ */
+#define __used __attribute__ ((__used__))
+
+/*!
+ * @def __used
+ * @hideinitializer
+ * @brief Used identifier attribute
+ */
+#define __unused__ __attribute__ ((__unused__))
+
+/*!
+ * @def __const
+ * @hideinitializer
+ * @brief Const identifier attribute
+ */
+#define __const __attribute__ ((__const__))
+
+/*!
+ * @def __must_check
+ * @hideinitializer
+ * @brief Warning about "unused" identifier attribute
+ */
+#define __must_check __attribute__ ((warn_unused_result))
+
+/*!
+ * @def __packed
+ * @hideinitializer
+ * @brief Packed structure identifier attribute
+ */
+#define __packed __attribute__ ((packed))
+
+/*!
+ * @def __cleanup
+ * @hideinitializer
+ * @brief Clenaup variable attribute
+ */
+#define __cleanup(f) __attribute__ ((cleanup(f)))
+
+#endif /* __ATTRIBUTE_H__ */
diff --git a/clib/bb_trace.h b/clib/bb_trace.h
new file mode 100644
index 0000000..a3557c4
--- /dev/null
+++ b/clib/bb_trace.h
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2011
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: B. Rafanello
+ *
+ * Module: bb_trace.h
+ *
+ * Implements a simple trace system.
+ *
+ * Version 0.0.0.1
+ */
+
+#ifndef BB_TRACE_H
+
+#define BB_TRACE_H
+
+#include <stdio.h>
+#include "trace_indent.h"
+
+#if (ENABLE_API_TRACE || ENABLE_FUNCTION_TRACE || ENABLE_FUNCTION_PARAMETER_TRACE || ENABLE_API_TRACE || ENABLE_API_PARAMETER_TRACE || ENABLE_EXTRA_CHECKS || ENABLE_USER_TRACE )
+
+#define ENABLE_BB_TRACE_BASE
+
+#endif
+
+#ifdef ENABLE_ALL_BB_TRACE
+
+#define ENABLE_API_TRACE
+#define ENABLE_FUNCTION_TRACE
+#define ENABLE_API_PARAMETER_TRACE
+#define ENABLE_FUNCTION_PARAMETER_TRACE
+#define ENABLE_EXTRA_CHECKS
+#define ENABLE_USER_TRACE
+#define ENABLE_BB_TRACE_BASE
+
+#endif
+
+#ifdef ENABLE_BB_TRACE_BASE
+
+ /* The trace level is a bitmask. The following bits, if 1, activate the corresponding trace function:
+ *
+ * 0 : API Entry & Exit
+ * 1 : Local function entry & exit
+ * 2 : API parameters will be listed on entry and return values listed on exit
+ * 3 : Local function parameters and return values will be listed
+ * 4 : Low bit, extra checks level number.
+ * 5 : High bit, extra checks level number.
+ * 6 : Low bit, user defined trace level number.
+ * 7 : High bit, user defined trace level number.
+ */
+
+#define API_ENTRY_EXIT_TRACE 0x01
+#define FUNCTION_ENTRY_EXIT_TRACE 0x02
+#define API_PARAMETER_TRACE 0x04
+#define FUNCTION_PARAMETER_TRACE 0x08
+#define EXTRA_CHECKS_LEVEL_1 0x10
+#define EXTRA_CHECKS_LEVEL_2 0x20
+#define EXTRA_CHECKS_LEVEL_3 0x30
+#define USER_TRACE_LEVEL_1 0x40
+#define USER_TRACE_LEVEL_2 0x80
+#define USER_TRACE_LEVEL_3 0xC0
+
+extern unsigned char BB_TRACE_LEVEL;
+extern unsigned char BB_PREVIOUS_TRACE_LEVEL;
+
+#define TRACE_INDENT() if (BB_TRACE_LEVEL != 0) \
+ Indent_Trace_Output();
+
+#define TRACE_OUTDENT() if ( BB_TRACE_LEVEL != 0 ) \
+ Outdent_Trace_Output();
+
+#define TRACE_STOP() if ( BB_TRACE_LEVEL != 0 ) \
+ { \
+ BB_PREVIOUS_TRACE_LEVEL = BB_TRACE_LEVEL; \
+ BB_TRACE_LEVEL = 0; \
+ }
+
+#define TRACE_START() if ( BB_TRACE_LEVEL == 0 ) \
+ { \
+ BB_TRACE_LEVEL = BB_PREVIOUS_TRACE_LEVEL; \
+ }
+
+#define SET_TRACE_LEVEL( Level ) BB_TRACE_LEVEL = Level;
+
+#else
+
+#define API_ENTRY_EXIT_TRACE
+#define FUNCTION_ENTRY_EXIT_TRACE
+#define API_PARAMETER_TRACE
+#define FUNCTION_PARAMETER_TRACE
+#define EXTRA_CHECKS_LEVEL_1
+#define EXTRA_CHECKS_LEVEL_2
+#define EXTRA_CHECKS_LEVEL_3
+#define USER_TRACE_LEVEL_1
+#define USER_TRACE_LEVEL_2
+#define USER_TRACE_LEVEL_3
+
+#define TRACE_INDENT()
+
+#define TRACE_OUTDENT()
+
+#define TRACE_STOP()
+
+#define TRACE_START()
+
+#define SET_TRACE_LEVEL( Level )
+
+#endif
+
+#ifdef ENABLE_FUNCTION_TRACE
+
+#define FUNCTION_ENTRY( ) if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Entering %s.\n", __func__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n\n"); \
+ Indent_Trace_Output(); \
+ }
+
+#define FUNCTION_EXIT( ) if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Outdent_Trace_Output(); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Leaving %s.\n", __func__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n\n"); \
+ } \
+ return;
+
+#define FUNCTION_RETURN( Format, Value ) \
+ if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Outdent_Trace_Output(); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Leaving %s with return value " Format "\n", __func__ , Value); \
+ Do_Indent(); \
+ fprintf( stderr, "-------------------- \n\n"); \
+ } \
+ return Value;
+
+#else
+
+#define FUNCTION_ENTRY( )
+
+#define FUNCTION_EXIT( ) return;
+
+#define FUNCTION_RETURN( Format, Value ) return Value;
+
+#endif
+
+#ifdef ENABLE_FUNCTION_PARAMETER_TRACE
+
+#define PRINT_FUNCTION_PARAMETER( ... ) if ( BB_TRACE_LEVEL & FUNCTION_PARAMETER_TRACE ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#else
+
+#define PRINT_FUNCTION_PARAMETER( ... )
+
+#endif
+
+#ifdef ENABLE_API_TRACE
+
+#define API_FUNCTION_ENTRY( ) if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Entering %s API.\n", __func__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n\n"); \
+ Indent_Trace_Output(); \
+ }
+
+#define API_FUNCTION_EXIT( ) if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Outdent_Trace_Output(); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Leaving %s API.\n", __func__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n\n"); \
+ } \
+ return;
+
+#define API_FUNCTION_RETURN( Format, Value ) \
+ if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \
+ { \
+ fprintf( stderr, " \n\n"); \
+ Outdent_Trace_Output(); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Leaving %s API with return value " Format "\n", __func__, Value); \
+ Do_Indent(); \
+ fprintf( stderr, "==================== \n\n"); \
+ } \
+ return Value;
+
+#else
+
+#define API_FUNCTION_ENTRY( )
+
+#define API_FUNCTION_EXIT( ) return;
+
+#define API_FUNCTION_RETURN( Format, Value ) return Value;
+
+#endif
+
+#ifdef ENABLE_API_PARAMETER_TRACE
+
+#define PRINT_API_PARAMETER( ... ) if ( BB_TRACE_LEVEL & API_PARAMETER_TRACE ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#else
+
+#define PRINT_API_PARAMETER( ... )
+
+#endif
+
+#ifdef ENABLE_EXTRA_CHECKS
+
+#define LEVEL1_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_1 ) \
+ { \
+ if ( __VA_ARGS__ ) \
+ { \
+ fprintf( stderr, "\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \
+ __FILE__, __LINE__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ } \
+ \
+ }
+
+#define LEVEL1_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_1 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define LEVEL2_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_2 ) \
+ { \
+ if ( __VA_ARGS__ ) \
+ { \
+ fprintf( stderr, "\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \
+ __FILE__, __LINE__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ } \
+ \
+ }
+
+#define LEVEL2_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_2 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define LEVEL3_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_3 ) \
+ { \
+ if ( __VA_ARGS__ ) \
+ { \
+ fprintf( stderr, "\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \
+ __FILE__, __LINE__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ } \
+ \
+ }
+
+#define LEVEL3_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_3 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define EXTRA_CHECK( Trace_Level, ... ) if ( BB_TRACE_LEVEL & Trace_Level ) \
+ { \
+ if ( __VA_ARGS__ ) \
+ { \
+ fprintf( stderr, "\n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \
+ __FILE__, __LINE__ ); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ Do_Indent(); \
+ fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \
+ } \
+ \
+ }
+
+#else
+
+#define LEVEL1_EXTRA_CHECK( ... )
+
+#define LEVEL2_EXTRA_CHECK( ... )
+
+#define LEVEL3_EXTRA_CHECK( ... )
+
+#define LEVEL1_EC_PRINT_LINE( ... )
+
+#define LEVEL2_EC_PRINT_LINE( ... )
+
+#define LEVEL3_EC_PRINT_LINE( ... )
+
+#define EXTRA_CHECK( Trace_Level, ... )
+
+#endif
+
+#ifdef ENABLE_USER_TRACE
+
+#define USER1_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_1 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define USER2_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_2 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define USER3_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_3 ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#define TRACE_PRINT_LINE( Trace_Level, ... ) if ( BB_TRACE_LEVEL & Trace_Level ) \
+ { \
+ Do_Indent(); \
+ fprintf( stderr, __VA_ARGS__ ); \
+ fprintf( stderr, "\n"); \
+ }
+
+#else
+
+#define USER1_PRINT_LINE( ... )
+
+#define USER2_PRINT_LINE( ... )
+
+#define USER3_PRINT_LINE( ... )
+
+#define TRACE_PRINT_LINE( Trace_Level, ... )
+
+#endif
+
+#endif
diff --git a/clib/bitset.h b/clib/bitset.h
new file mode 100644
index 0000000..d1947aa
--- /dev/null
+++ b/clib/bitset.h
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file bitset.h
+ * @brief bitset container
+ * @details bitsets are a special container that are design to store
+ * bits (elements with only 2 possible values: 0 or 1)
+ * @details For example,
+ * @code
+ * #include <clib/bitset.h>
+ * #include <clib/bitset_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * bitset_t a;
+ * bitset_init(&a, 4, 1024);
+ *
+ * bitset_size(&a, 10);
+ *
+ * int i;
+ * for (i=0; i<10; i++)
+ * bitset_put(&a, i, &i);
+ * bitset_put(&a, 223, (uint32_t[]){223});
+ *
+ * bitset_iter_t it;
+ * bitset_iter_init(&it, &a, VI_FLAG_FWD);
+ *
+ * uint32_t * j;
+ * bitset_for_each(&it, j) {
+ * printf("vec[%d]\n", *j);
+ * }
+ *
+ * bitset_dump(&a, stdout);
+ * bitset_delete(&a);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __bitset_H__
+#define __bitset_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "builtin.h"
+
+#include "nargs.h"
+#include "mqueue.h"
+#include "tree.h"
+
+/* ======================================================================= */
+
+typedef struct bitset bitset_t; //!< Alias for the @em bitset class
+
+/*!
+ * @brief bitset container
+ * @details bitset container
+ */
+struct bitset { //! The bitset class
+ uint32_t magic; //!< bitset magic number
+
+ uint32_t page_size; //!< bitset data page size (in bytes)
+
+ uint16_t elem_size; //!< bitset element size (in bytes)
+ uint16_t elem_num; //!< bitset element count (per page)
+
+ size_t size; //!< Number of initialized elements
+ size_t pages; //!< Number of data pages allocated (currently)
+
+ tree_t tree; //!< @private
+};
+
+/* ======================================================================= */
+
+/*!
+ * @brief Constructs an @em bitset container object
+ * @details For example,
+ * @code
+ * ...
+ * bitset_t ar;
+ * bitset_init(&ar, 4, 1024);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @param elem_size [in] bitset element size, in bytes
+ * @param elem_num [in] bitset element number, per data page
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define bitset_init(...) STRCAT(bitset_init, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void bitset_init2(bitset_t * self, size_t elem_size)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void bitset_init3(bitset_t * self, size_t elem_size, size_t page_size)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Destructs an @em bitset container object
+ * @details Deallocate all backing storage associated with this \em bitset object
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_put(&ar, 524, &count);
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return None
+ */
+extern void bitset_delete(bitset_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Returns a reference to the element at position @em idx in the @em bitset
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_put(&ar, 524, &count);
+ * printf("ar[524] = %d\n", *(int *)bitset_at(&ar, 524));
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @param elem_num [in] bitset element index
+ * @return Reference to bitset element at @em idx on SUCCESS
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if bitset element at @em idx is uninitialized
+ */
+extern const void *bitset_at(bitset_t * self, size_t elem_num)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn void bitset_get(bitset_t * self, size_t elem_off, const void * ptr, size_t elem_num=1)
+ * @brief Copy content from the @em bitset
+ * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em bitset to destination pointer @em ptr
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_put(&ar, 524, &count);
+ * bitset_get(&ar, 524, &count);
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @param elem_off [in] bitset element index
+ * @param ptr [out] Destination storage pointer
+ * @param elem_num [in] Desgination element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define bitset_get(...) STRCAT(bitset_get, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void bitset_get3(bitset_t * self, size_t elem_off, void *ptr)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern void bitset_get4(bitset_t * self, size_t elem_off, void *ptr,
+ size_t elem_num)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn void bitset_put(bitset_t * self, size_t elem_off, const void * ptr, size_t elem_num=1)
+ * @brief Assign new content to the @em bitset
+ * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em bitset starting at position @em elem_off
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_put(&ar, 524, &count);
+ * bitset_get(&ar, 524, &count);
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @param elem_off [in] bitset element index
+ * @param ptr [in] Source storage pointer
+ * @param elem_num [in] Source element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define bitset_put(...) STRCAT(bitset_put, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void bitset_put3(bitset_t * self, size_t elem_off, const void *ptr)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern void bitset_put4(bitset_t * self, size_t elem_off, const void *ptr,
+ size_t elem_num)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn size_t bitset_size(bitset_t * self, size_t size = 1)
+ * @brief Return or set the size of the @em bitset
+ * @details Return or set the number of allocated elements in the @em bitset
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_size(&ar, 2040);
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @param size [in] New bitset size
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define bitset_size(...) STRCAT(bitset_size, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern size_t bitset_size1(bitset_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void bitset_size2(bitset_t * self, size_t size)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Return pages of the @em bitset container
+ * @details Return the number of pages in the @em bitset container
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_size(&ar, 2040);
+ * printf("pages = %d\n", bitset_pages(&ar));
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return The number of pages that conform the bitset's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t bitset_pages(bitset_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return capacity of the @em bitset
+ * @details Return the number of allocated and unallocated elements in the @em bitset container
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_size(&ar, 2040);
+ * printf("capacity = %d\n", bitset_capacity(&ar));
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return The number of total elements that conform the bitset's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t bitset_capacity(bitset_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Send (write) an @em bitset object to a message queue
+ * @details For example,
+ * @code
+ * ...
+ * bitset_init(&ar, 4, 1024);
+ * bitset_put(&ar, 7, &count);
+ * bitset_put(&ar, 7000, &count);
+ * ...
+ * mqueue mq;
+ * mqueue_init(&mq, "my_server");
+ * mqueue_create(&mq, gettid());
+ * ...
+ * bitset_send(&ar, &mq);
+ * bitset_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void bitset_send(bitset_t * self, mqueue_t * mq)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief receive (read) an @em bitset object from a message queue
+ * @details For example,
+ * @code
+ * ...
+ * bitset ar;
+ * ...
+ * mqueue mq;
+ * mqueue_open(&mq, path);
+ * ...
+ * bitset_receive(&ar, &mq);
+ * bitset_dump(&ar);
+ * ...
+ * @endcode
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void bitset_receive(bitset_t * self, mqueue_t * mq)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print the contents of an @em bitset to stdout
+ * @memberof bitset
+ * @param self [in] bitset object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void bitset_dump(bitset_t * self, FILE * out)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#endif /* __bitset_H__ */
diff --git a/clib/builtin.h b/clib/builtin.h
new file mode 100644
index 0000000..5827fb9
--- /dev/null
+++ b/clib/builtin.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file builtin.h
+ * @brief Builtin Function Macros
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __BUILTIN_H__
+#define __BUILTIN_H__
+
+/*!
+ * @def likely(x)
+ * @hideinitializer
+ * @brief Indicate an expression is likely to occur
+ * @param x [in] Expression
+ */
+#define likely(x) __builtin_expect(!!(x), 1)
+
+/*!
+ * @def unlikely(x)
+ * @hideinitializer
+ * @brief Indicate an expression is unlikely to occur
+ * @param x [in] Expression
+ */
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+/*!
+ * @def parity(x)
+ * @hideinitializer
+ * @brief Return parity of the input
+ * @param x [in] Object
+ */
+#define parity(x) __builtin_parity((x))
+#define ffs(x) __builtin_ffs((x))
+
+/*!
+ * @def popcount(x)
+ * @hideinitializer
+ * @brief Return number of 0b'1' bits of an int
+ * @param x [in] Object
+ */
+#define popcount(x) __builtin_popcount((x))
+
+/*!
+ * @def ctz(x)
+ * @hideinitializer
+ * @brief Count trailing zeros of an int
+ * @param x [in] @em int value
+ */
+#define ctz(x) __builtin_ctz((x))
+
+/*!
+ * @def ctzl(x)
+ * @hideinitializer
+ * @brief Count trailing zeros of a long
+ * @param x [in] @em long value
+ */
+#define ctzl(x) __builtin_ctzl((x))
+
+/*!
+ * @def ctzll(x)
+ * @hideinitializer
+ * @brief Count trailing zeros of a long long
+ * @param x [in] @em long @em long value
+ */
+#define ctzll(x) __builtin_ctzll((x))
+
+/*!
+ * @def clz(x)
+ * @hideinitializer
+ * @brief Count leading zeros of an int
+ * @param x [in] @em int value
+ */
+#define clz(x) __builtin_clzl((x))
+
+/*!
+ * @def clzl(x)
+ * @hideinitializer
+ * @brief Count leading zeros of a long
+ * @param x [in] @em long value
+ */
+#define clzl(x) __builtin_clzl((x))
+
+/*!
+ * @def clzll(x)
+ * @hideinitializer
+ * @brief Count leading zeros of a long long
+ * @param x [in] @em long long value
+ */
+#define clzll(x) __builtin_clzll((x))
+
+/*!
+ * @def caller(x)
+ * @hideinitializer
+ * @brief Return callers return address
+ * @param x [in] Function name
+ */
+#define caller(x) __builtin_return_address((x))
+
+#define choose_expr(x,y,z) __builtin_choose_expr((x),(y),(z))
+
+/*!
+ * @def const_expr(x)
+ * @hideinitializer
+ * @brief Return callers return address
+ * @param x [in] Function name
+ */
+#define const_expr(x) __builtin_constant_p((x))
+
+/*!
+ * @def compatible_type(x)
+ * @hideinitializer
+ * @brief Return @em true if typeof(x) and typeof(y) are compatible
+ * @param x [in] Type name
+ * @param y [in] Type name
+ */
+#define compatible_type(x,y) __builtin_types_compatible_p((x),(y))
+
+#endif /* __BUILTIN_H__ */
diff --git a/clib/checksum.h b/clib/checksum.h
new file mode 100644
index 0000000..528b77e
--- /dev/null
+++ b/clib/checksum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file checksum.h
+ * @brief Simple XOR checksum
+ * @author Mike Kobler <mkobler@us.ibm.com>
+ * @date 2007-2012
+ */
+
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <stdint.h>
+
+/*!
+ * @brief Copy bytes from the source reference to the destination reference
+ * while computing a 32-bit checksum
+ * @param __dst [in] Destination reference
+ * @param __src [in] Source reference (must be 4 byte aligned)
+ * @param __n [in] Number of bytes to copy / compute
+ * @return 32-bit Checksum value
+ */
+extern uint32_t memcpy_checksum(void *__restrict __dst,
+ const void *__restrict __src, size_t __n)
+/*! @cond */
+__THROW __nonnull((2)) /*! @endcond */ ;
+
+#endif /* __CHECKSUM_H__ */
diff --git a/clib/compare.h b/clib/compare.h
new file mode 100644
index 0000000..0461b51
--- /dev/null
+++ b/clib/compare.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file compare.h
+ * @brief Generic compare function
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __COMPARE_H__
+#define __COMPARE_H__
+
+/*!
+ * @brief Pointer to a generic compare function
+ */
+typedef int (*compare_f) (const void *, const void *);
+
+/*!
+ * @brief Default compare function
+ */
+static inline int default_compare(const void *v1, const void *v2)
+{
+ const int i1 = (const int)v1, i2 = (const int)v2;
+ return i1 - i2;
+}
+
+#endif /* __COMPARE_H__ */
diff --git a/clib/crc32.h b/clib/crc32.h
new file mode 100644
index 0000000..ce77a31
--- /dev/null
+++ b/clib/crc32.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file crc32.h
+ * @brief 32-bit CRC
+ * @details Gary Brown's CRC32 algorithm for polynomial:
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ * @author Gary Brown
+ * @date 2010-2011
+ */
+
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*!
+ * @brief Compute a 32-bit CRC value for an octet
+ * @param c [in] Input data (octet)
+ * @param crc [in] Input 32-bit CRC value
+ * @return 32-bit CRC value
+ */
+extern uint32_t clib_crc32(unsigned char c, uint32_t crc);
+
+/*!
+ * @brief Copy bytes from the source reference to the destination reference
+ * while computing a 32-bit CRC
+ * @param __dst [in] Destination reference
+ * @param __src [in] Source reference
+ * @param __n [in] Number of bytes to copy / compute
+ * @return 32-bit CRC value
+ */
+extern uint32_t memcpy32(void *__restrict __dst, const void *__restrict __src,
+ size_t __n)
+/*! @cond */
+__THROW __nonnull((2)) /*! @endcond */ ;
+
+#endif /* __CRC32_H__ */
diff --git a/clib/cunit/clib.c b/clib/cunit/clib.c
new file mode 100644
index 0000000..e9f1053
--- /dev/null
+++ b/clib/cunit/clib.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <CUnit/Basic.h>
+
+#include "vector.h"
+#include "slab.h"
+#include "tree.h"
+#include "splay.h"
+#include "map.h"
+#include "ecc.h"
+
+int main(void) {
+ /* initialize the CUnit test registry */
+ if (CUE_SUCCESS != CU_initialize_registry())
+ return CU_get_error();
+
+ vector_test();
+ slab_test();
+ tree_test();
+ splay_test();
+ map_test();
+ ecc_test();
+
+ /* Run all tests using the CUnit Basic interface */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_cleanup_registry();
+
+ return CU_get_error();
+}
diff --git a/clib/cunit/ecc.c b/clib/cunit/ecc.c
new file mode 100644
index 0000000..3ae6303
--- /dev/null
+++ b/clib/cunit/ecc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <clib/exception.h>
+#include <clib/assert.h>
+#include <clib/type.h>
+
+#include <clib/ecc.h>
+#include <clib/misc.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 10000
+#define SEED 41
+
+static int init_ecc(void) {
+ return 0;
+}
+
+static int clean_ecc(void) {
+ return 0;
+}
+
+static void ecc_1(void) {
+ int size[] = {7, 8, 16, 1024+16, 4096+32, 8*1024*4+64, 0};
+
+ for (int * s = size; *s != 0; s++) {
+ unsigned char in[*s];
+ unsigned char out[*s + (*s / 8)];
+
+ ssize_t in_sz = sizeof in;
+ ssize_t out_sz = sizeof out;
+
+ memset(in, 0, in_sz);
+ memset(out, 0, out_sz);
+
+ FILE * f = fopen("/dev/urandom", "r");
+ CU_ASSERT_FATAL(f != NULL);
+ CU_ASSERT_FATAL(fread(in, in_sz, 1, f) == 1);
+ fclose(f);
+
+ ssize_t rc = sfc_ecc_inject(out, out_sz, in, in_sz);
+ if ((*s % 8) != 0) {
+ CU_ASSERT(rc == -1);
+ } else {
+ CU_ASSERT(rc == in_sz + (in_sz / 8));
+ }
+
+ unsigned char cmp[*s];
+ ssize_t cmp_sz = sizeof cmp;
+ memset(cmp, 0, cmp_sz);
+
+ rc = sfc_ecc_remove(cmp, cmp_sz, out, out_sz);
+ if ((out_sz % 9) != 0) {
+ CU_ASSERT(rc == -1);
+ } else {
+ CU_ASSERT(rc == in_sz)
+ CU_ASSERT_FATAL(memcmp(in, cmp, in_sz) == 0);
+#ifdef DEBUG
+ dump_memory(stdout, 0, in, in_sz);
+ dump_memory(stdout, 0, cmp, cmp_sz);
+#endif
+ }
+ }
+}
+
+void ecc_test(void) {
+ CU_pSuite suite = CU_add_suite("ecc", init_ecc, clean_ecc);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "test of --> ecc_1", ecc_1) == NULL) return;
+}
diff --git a/clib/cunit/ecc.h b/clib/cunit/ecc.h
new file mode 100644
index 0000000..0eec8da
--- /dev/null
+++ b/clib/cunit/ecc.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void ecc_test(void);
diff --git a/clib/cunit/map.c b/clib/cunit/map.c
new file mode 100644
index 0000000..3761b1a
--- /dev/null
+++ b/clib/cunit/map.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/libclib.h>
+
+#include <clib/slab.h>
+#include <clib/map.h>
+#include <clib/map_iter.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 20000
+#define SEED 22
+
+slab_t slab;
+
+typedef struct {
+ map_node_t node;
+ int i;
+ float f;
+} data_t;
+
+static int init_map(void) {
+ slab_init(&slab, "my_slab", sizeof(data_t), 4096);
+ return 0;
+}
+
+static int clean_map(void) {
+ slab_delete(&slab);
+ return 0;
+}
+
+static void __insert(map_t * t, int i) {
+ data_t * d = (data_t *)slab_alloc(&slab);
+
+ i %= INT32_MAX;
+
+ d->i = i;
+ d->f = (float)i;
+ map_node_init(&d->node, (const void *)(intptr_t)(d->i));
+
+ if (map_insert(t, &d->node) < 0) {
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+}
+
+static data_t * __remove(map_t * t, int i) {
+
+ i %= INT32_MAX;
+
+ map_node_t * n = map_find(t, (const void *)i);
+ if (n == NULL) map_dump(t, stdout);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ map_remove(t, n);
+
+ data_t * d = container_of(n, data_t, node);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ if (0 <= i)
+ CU_ASSERT((int)d->node.key == i);
+
+ return d;
+}
+
+static int compare(const void * v1, const void * v2) {
+ const int i1 = (const int)v1, i2 = (const int)v2;
+ return i1 - i2;
+}
+
+static void map_1(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_min(&t) == NULL);
+ CU_ASSERT(map_max(&t) == NULL);
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+}
+
+static void map_2(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)map_min(&t)->key);
+ CU_ASSERT(COUNT == (int)map_max(&t)->key);
+ __remove(&t, (int)map_min(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)map_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)map_max(&t)->key);
+ __remove(&t, (int)map_max(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+}
+
+static void map_3(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)map_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)map_max(&t)->key);
+ __remove(&t, (int)map_max(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)map_min(&t)->key);
+ CU_ASSERT(COUNT == (int)map_max(&t)->key);
+ __remove(&t, (int)map_min(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+}
+
+static void map_4(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, (int)random());
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)map_min(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, (int)random());
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)map_max(&t)->key);
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, (int)random());
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)random());
+ CU_ASSERT(map_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+}
+
+static void map_5(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, (int)random());
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ data_t * d;
+ int key = 0;
+
+ map_iter_t it;
+ map_iter_init(&it, &t, MI_FLAG_FWD);
+ map_for_each(&it, d, node) {
+ CU_ASSERT(key < (int)d->node.key);
+ key = (int)d->node.key;
+ }
+
+ key = INT32_MAX;
+ map_iter_init(&it, &t, MI_FLAG_BWD);
+ map_for_each(&it, d, node) {
+ CU_ASSERT((int)d->node.key < key);
+ key = (int)d->node.key;
+ }
+}
+
+static void map_6(void) {
+ map_t t;
+ map_init(&t, compare);
+
+ CU_ASSERT(map_empty(&t) == true);
+ CU_ASSERT(map_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, (int)random());
+
+ CU_ASSERT(map_empty(&t) == false);
+ CU_ASSERT(map_size(&t) == COUNT);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++) {
+ map_node_t * node = map_find(&t, (const void *)random());
+ CU_ASSERT_PTR_NOT_NULL_FATAL(node);
+ }
+}
+
+void map_test(void) {
+ CU_pSuite suite = CU_add_suite("map", init_map, clean_map);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "test of --> map_1", map_1) == NULL) return;
+ if (CU_add_test(suite, "test of --> map_2", map_2) == NULL) return;
+ if (CU_add_test(suite, "test of --> map_3", map_3) == NULL) return;
+ if (CU_add_test(suite, "test of --> map_4", map_4) == NULL) return;
+ if (CU_add_test(suite, "test of --> map_5", map_5) == NULL) return;
+ if (CU_add_test(suite, "test of --> map_6", map_6) == NULL) return;
+}
diff --git a/clib/cunit/map.h b/clib/cunit/map.h
new file mode 100644
index 0000000..9996b54
--- /dev/null
+++ b/clib/cunit/map.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void map_test(void);
diff --git a/clib/cunit/slab.c b/clib/cunit/slab.c
new file mode 100644
index 0000000..c9585d3
--- /dev/null
+++ b/clib/cunit/slab.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <clib/exception.h>
+#include <clib/assert.h>
+#include <clib/type.h>
+
+#include <clib/slab.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 10000
+#define SEED 41
+
+static int init_slab(void) {
+ return 0;
+}
+
+static int clean_slab(void) {
+ return 0;
+}
+
+static void slab_1(void) {
+ slab_t s[SLAB_ALLOC_MAX+1] = {INIT_SLAB,};
+
+ for (int i=SLAB_ALLOC_MIN; i<=SLAB_ALLOC_MAX; i+=3) {
+ slab_init(&s[i], "my_slab", i);
+//slab_dump(&s[i], stdout);
+
+ for (int j=0; j<COUNT; j++) {
+ void * ptr = slab_alloc(&s[i]);
+ memset(ptr, j, i);
+ }
+
+ slab_delete(&s[i]);
+ }
+}
+
+void slab_test(void) {
+ CU_pSuite suite = CU_add_suite("slab", init_slab, clean_slab);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "test of --> slab_1", slab_1) == NULL) return;
+}
diff --git a/clib/cunit/slab.h b/clib/cunit/slab.h
new file mode 100644
index 0000000..32e2aa6
--- /dev/null
+++ b/clib/cunit/slab.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void slab_test(void);
diff --git a/clib/cunit/splay.c b/clib/cunit/splay.c
new file mode 100644
index 0000000..302310e
--- /dev/null
+++ b/clib/cunit/splay.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/libclib.h>
+
+#include <clib/slab.h>
+#include <clib/tree.h>
+#include <clib/tree_iter.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 30000
+#define SEED 22
+
+slab_t slab;
+
+typedef struct {
+ tree_node_t node;
+ int i;
+ float f;
+} data_t;
+
+static int init_splay(void) {
+ slab_init(&slab, "my_slab", sizeof(data_t), 4096);
+ return 0;
+}
+
+static int clean_splay(void) {
+ slab_delete(&slab);
+ return 0;
+}
+
+static void __insert(tree_t * t, int i) {
+ data_t * d = (data_t *)slab_alloc(&slab);
+
+ d->i = i;
+ d->f = (float)i;
+
+ tree_node_init(&d->node, (const void *)(d->i));
+
+ if (splay_insert(t, &d->node) < 0) {
+ tree_dump(t, stdout);
+
+ fprintf(stdout, "key: %d root->key: %d\n",
+ i, (int)tree_root(t)->key);
+
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+}
+
+static data_t * __remove(tree_t * t, int i) {
+ tree_node_t * n = tree_find(t, (const void *)i);
+ if (n == NULL) tree_dump(t, stdout);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ splay_remove(t, n);
+ CU_ASSERT_PTR_NULL(tree_node_parent(n));
+ CU_ASSERT_PTR_NULL(tree_node_left(n));
+ CU_ASSERT_PTR_NULL(tree_node_right(n));
+ CU_ASSERT_PTR_NOT_NULL(tree_node_key(n));
+
+ data_t * d = container_of(n, data_t, node);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ if (0 <= i)
+ CU_ASSERT(d->i == i);
+
+ return d;
+}
+
+static int compare(const void * v1, const void * v2) {
+ const int i1 = (const int)v1, i2 = (const int)v2;
+ return i1 - i2;
+}
+
+static void splay_1(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_min(&t) == NULL);
+ CU_ASSERT(tree_max(&t) == NULL);
+ CU_ASSERT(t.compare != NULL);
+
+ CU_ASSERT(tree_root(&t) == NULL);
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void splay_2(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void splay_3(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void splay_4(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_root(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, random());
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void splay_5(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+ data_t * d;
+
+ tree_iter_t it;
+
+ int key = 0;
+ tree_iter_init(&it, &t, TI_FLAG_FWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(key < d->i);
+ key = d->i;
+ }
+
+ key = INT32_MAX;
+ tree_iter_init(&it, &t, TI_FLAG_BWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(d->i < key);
+ key = d->i;
+ }
+}
+
+static void splay_6(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ data_t * d;
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ tree_iter_t it;
+ tree_iter_init(&it, &t, TI_FLAG_FWD);
+
+ int key = 0;
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(key < d->i);
+ key = d->i;
+ }
+
+ __remove(&t, (int)tree_min(&t)->key);
+
+ if (0 < tree_size(&t)) {
+ CU_ASSERT(tree_min(&t) != NULL);
+ } else if (tree_size(&t) <= 0) {
+ CU_ASSERT(tree_min(&t) == NULL);
+ }
+
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+}
+
+static void splay_7(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ data_t * d;
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ tree_iter_t it;
+
+ int key = INT32_MAX;
+ tree_iter_init(&it, &t, TI_FLAG_BWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(d->i < key);
+ key = d->i;
+ }
+
+ __remove(&t, (int)tree_max(&t)->key);
+
+ if (0 < tree_size(&t)) {
+ CU_ASSERT(tree_max(&t) != NULL);
+ } else if ( tree_size(&t) <= 0) {
+ CU_ASSERT(tree_max(&t) == NULL);
+ }
+
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+}
+
+void splay_test(void) {
+ CU_pSuite suite = CU_add_suite("splay", init_splay, clean_splay);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "test of --> splay_1", splay_1) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_2", splay_2) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_3", splay_3) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_4", splay_4) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_5", splay_5) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_6", splay_6) == NULL) return;
+ if (CU_add_test(suite, "test of --> splay_7", splay_7) == NULL) return;
+}
diff --git a/clib/cunit/splay.h b/clib/cunit/splay.h
new file mode 100644
index 0000000..2cc256d
--- /dev/null
+++ b/clib/cunit/splay.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void splay_test(void);
diff --git a/clib/cunit/tree.c b/clib/cunit/tree.c
new file mode 100644
index 0000000..2f67ab7
--- /dev/null
+++ b/clib/cunit/tree.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/libclib.h>
+
+#include <clib/slab.h>
+#include <clib/tree.h>
+#include <clib/tree_iter.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 30000
+#define SEED 22
+
+slab_t slab;
+
+typedef struct {
+ tree_node_t node;
+ int i;
+ float f;
+} data_t;
+
+static int init_tree(void) {
+ slab_init(&slab, "my_slab", sizeof(data_t), 4096);
+ return 0;
+}
+
+static int clean_tree(void) {
+ slab_delete(&slab);
+ return 0;
+}
+
+static void __insert(tree_t * t, int i) {
+ data_t * d = (data_t *)slab_alloc(&slab);
+
+ d->i = i;
+ d->f = (float)i;
+
+ tree_node_init(&d->node, (const void *)(d->i));
+
+ if (tree_insert(t, &d->node) < 0) {
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+}
+
+static data_t * __remove(tree_t * t, int i) {
+ tree_node_t * n = tree_find(t, (const void *)i);
+ if (n == NULL) tree_dump(t, stdout);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ tree_remove(t, n);
+ CU_ASSERT_PTR_NULL(tree_node_parent(n));
+ CU_ASSERT_PTR_NULL(tree_node_left(n));
+ CU_ASSERT_PTR_NULL(tree_node_right(n));
+ CU_ASSERT_PTR_NOT_NULL(tree_node_key(n));
+
+ data_t * d = container_of(n, data_t, node);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(n);
+
+ if (0 <= i)
+ CU_ASSERT(d->i == i);
+
+ return d;
+}
+
+static int compare(const void * v1, const void * v2) {
+ const int i1 = (const int)v1, i2 = (const int)v2;
+ return i1 - i2;
+}
+
+static void tree_1(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_min(&t) == NULL);
+ CU_ASSERT(tree_max(&t) == NULL);
+ CU_ASSERT(t.compare != NULL);
+
+ CU_ASSERT(tree_root(&t) == NULL);
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void tree_2(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, i);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void tree_3(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(1 == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, COUNT - i + 1);
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_root(&t) != NULL);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ CU_ASSERT(i == (int)tree_min(&t)->key);
+ CU_ASSERT(COUNT == (int)tree_max(&t)->key);
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void tree_4(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_min(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_max(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, (int)tree_root(&t)->key);
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++) {
+ __remove(&t, random());
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+}
+
+static void tree_5(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ data_t * d;
+
+ tree_iter_t it;
+
+ int key = 0;
+ tree_iter_init(&it, &t, TI_FLAG_FWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(key < d->i);
+ key = d->i;
+ }
+
+ key = INT32_MAX;
+ tree_iter_init(&it, &t, TI_FLAG_BWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(d->i < key);
+ key = d->i;
+ }
+}
+
+static void tree_6(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ data_t * d;
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ tree_iter_t it;
+ tree_iter_init(&it, &t, TI_FLAG_FWD);
+
+ int key = 0;
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(key < d->i);
+ key = d->i;
+ }
+
+ __remove(&t, (int)tree_min(&t)->key);
+
+ if (0 < tree_size(&t)) {
+ CU_ASSERT(tree_min(&t) != NULL);
+ } else if (tree_size(&t) <= 0) {
+ CU_ASSERT(tree_min(&t) == NULL);
+ }
+
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+}
+
+static void tree_7(void) {
+ tree_t t;
+ tree_init(&t, compare);
+
+ data_t * d;
+
+ CU_ASSERT(tree_empty(&t) == true);
+ CU_ASSERT(tree_size(&t) == 0);
+
+ srandom(SEED);
+ for (int i=1; i<=COUNT; i++)
+ __insert(&t, random());
+
+ CU_ASSERT(tree_empty(&t) == false);
+ CU_ASSERT(tree_size(&t) == COUNT);
+
+ for (int i=1; i<=COUNT; i++) {
+ tree_iter_t it;
+
+ int key = INT32_MAX;
+ tree_iter_init(&it, &t, TI_FLAG_BWD);
+ tree_for_each(&it, d, node) {
+ CU_ASSERT(d->i < key);
+ key = d->i;
+ }
+
+ __remove(&t, (int)tree_max(&t)->key);
+
+ if (0 < tree_size(&t)) {
+ CU_ASSERT(tree_max(&t) != NULL);
+ } else if ( tree_size(&t) <= 0) {
+ CU_ASSERT(tree_max(&t) == NULL);
+ }
+
+ CU_ASSERT(tree_size(&t) + i == COUNT);
+ }
+}
+
+
+void tree_test(void) {
+ CU_pSuite suite = CU_add_suite("tree", init_tree, clean_tree);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "test of --> tree_1", tree_1) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_2", tree_2) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_3", tree_3) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_4", tree_4) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_5", tree_5) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_6", tree_6) == NULL)
+ goto error;
+ if (CU_add_test(suite, "test of --> tree_7", tree_7) == NULL)
+ goto error;
+
+ if (false) {
+ err_t * err;
+error:
+ err = err_get();
+ fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+
+}
diff --git a/clib/cunit/tree.h b/clib/cunit/tree.h
new file mode 100644
index 0000000..264670c
--- /dev/null
+++ b/clib/cunit/tree.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void tree_test(void);
diff --git a/clib/cunit/vector.c b/clib/cunit/vector.c
new file mode 100644
index 0000000..7ead584
--- /dev/null
+++ b/clib/cunit/vector.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/libclib.h>
+
+#include <clib/crc32.h>
+#include <clib/vector.h>
+
+#include <CUnit/Basic.h>
+
+#define COUNT 10000
+#define SEED 41
+
+static int init_vector(void) {
+ return 0;
+}
+
+static int clean_vector(void) {
+ return 0;
+}
+
+static void vector_1(void) {
+ vector_t v;
+
+ CU_ASSERT(vector_init(&v, "my_vector", 0) == -1)
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+
+ for (size_t i=VECTOR_ELEM_MIN; i<=VECTOR_ELEM_MAX; i++) {
+ CU_ASSERT(vector_init(&v, "my_vector", i) == 0);
+
+ CU_ASSERT(vector_size(&v) == 0);
+ CU_ASSERT(vector_pages(&v) == 0);
+ CU_ASSERT(vector_capacity(&v) == 0);
+ CU_ASSERT(vector_elem_size(&v) == i);
+ CU_ASSERT(vector_elem_size(&v) * v.hdr.elem_num <= v.hdr.page_size);
+
+ CU_ASSERT(vector_delete(&v) == 0);
+ }
+}
+
+uint32_t crc2;
+
+static void vector_2(void) {
+ vector_t v;
+
+ CU_ASSERT(vector_init(&v, "my_vector.bin", 1) == 0);
+ CU_ASSERT(vector_size(&v) == 0);
+ CU_ASSERT(vector_pages(&v) == 0);
+ CU_ASSERT(vector_capacity(&v) == 0);
+ CU_ASSERT(vector_elem_size(&v) == 1);
+
+ CU_ASSERT(vector_size(&v, COUNT) == COUNT);
+ CU_ASSERT(COUNT <= vector_capacity(&v));
+ CU_ASSERT(COUNT <= vector_size(&v));
+ CU_ASSERT(3 <= vector_pages(&v));
+
+ for (int i=0; i<COUNT; i++) {
+ unsigned char c = (unsigned char)i;
+ crc2 = clib_crc32(c, crc2);
+ CU_ASSERT(vector_put(&v, i, &c) == 0);
+ }
+ CU_ASSERT_FATAL(crc2 != 0);
+
+ FILE * f = fopen(v.hdr.name, "w");
+ CU_ASSERT_FATAL(f != NULL);
+ CU_ASSERT(COUNT < vector_save(&v, f));
+ fclose(f), f = NULL;
+
+ CU_ASSERT(vector_delete(&v) == 0);
+}
+
+uint32_t crc3;
+
+static void vector_3(void) {
+ vector_t v;
+ vector_init(&v, __func__, 1);
+
+ CU_ASSERT(vector_size(&v) == 0);
+ CU_ASSERT(vector_pages(&v) == 0);
+ CU_ASSERT(vector_capacity(&v) == 0);
+ CU_ASSERT(vector_elem_size(&v) == 1);
+
+ FILE * f = fopen("my_vector.bin", "r");
+ CU_ASSERT_FATAL(f != NULL);
+ CU_ASSERT(COUNT < vector_load(&v, f));
+ fclose(f), f = NULL;
+
+ crc3 = 0;
+
+ for (int i=0; i<COUNT; i++) {
+ unsigned char c;
+ vector_get(&v, i, &c);
+ crc3 = clib_crc32(c, crc3);
+ }
+ CU_ASSERT_FATAL(crc3 != 0);
+
+ CU_ASSERT_FATAL(crc2 == crc3);
+
+ vector_delete(&v);
+}
+
+static void vector_4(void) {
+ vector_t v;
+
+ if (vector_init(&v, NULL, 0) < 0) {
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+
+ if (vector_init(&v, "my_vector", 0) < 0) {
+ err_t * err = err_get();
+ fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n",
+ err_file(err), err_line(err), err_size(err),
+ (const char *)err_data(err));
+ }
+
+ for (size_t i=VECTOR_ELEM_MIN; i<=VECTOR_ELEM_MAX; i+=3) {
+ vector_init(&v, "my_vector", i);
+
+ CU_ASSERT(vector_size(&v) == 0);
+ CU_ASSERT(vector_pages(&v) == 0);
+ CU_ASSERT(vector_capacity(&v) == 0);
+ CU_ASSERT(vector_elem_size(&v) == i);
+ CU_ASSERT(vector_elem_size(&v) * v.hdr.elem_num <= v.hdr.page_size);
+
+ vector_size(&v, COUNT);
+
+ CU_ASSERT(vector_size(&v) == COUNT);
+ CU_ASSERT(vector_size(&v) <= vector_capacity(&v));
+ CU_ASSERT(0 < vector_pages(&v));
+
+ vector_delete(&v);
+ }
+}
+
+void vector_test(void) {
+ CU_pSuite suite = CU_add_suite("vector", init_vector, clean_vector);
+ if (NULL == suite)
+ return;
+
+ if (CU_add_test(suite, "vector_1: vector_init()", vector_1) == NULL) return;
+ if (CU_add_test(suite, "vector_2: vector_save()", vector_2) == NULL) return;
+ if (CU_add_test(suite, "vector_3: vector_load()", vector_3) == NULL) return;
+ if (CU_add_test(suite, "vector_4: vector_size()", vector_4) == NULL) return;
+}
diff --git a/clib/cunit/vector.h b/clib/cunit/vector.h
new file mode 100644
index 0000000..c201b1a
--- /dev/null
+++ b/clib/cunit/vector.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+void vector_test(void);
diff --git a/clib/db.h b/clib/db.h
new file mode 100644
index 0000000..1aca879
--- /dev/null
+++ b/clib/db.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file db.h
+ * @brief Database client
+ * @details Embedded database API wrapper
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __DB_H__
+#define __DB_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sqlite3.h>
+
+#include "exception.h"
+
+/* ======================================================================= */
+
+#define DB_ROW SQLITE_ROW //!< Statement contains another row @hideinitializer
+#define DB_DONE SQLITE_DONE //!< Statement contains no more rows @hideinitializer
+#define DB_OK SQLITE_OK //!< Database command ok @hideinitializer
+
+#define DB 4 //!< Database exception class
+#define SQL 5 //!< Statement exception class
+
+typedef struct db db_t; //!< Alias for the @em db class
+typedef struct statement statement_t; //!< Alias for the @em statement class
+
+/*!
+ * @brief Database access class
+ */
+struct db {
+#ifdef SQLITE3
+ const char *path; //!< @private
+ sqlite3 *db; //!< @private
+#endif
+};
+
+typedef enum transaction_type transaction_type_t; //!< Alias for the @em transaction_type enum
+
+/*!
+ * @brief Transaction types
+ */
+enum transaction_type {
+ tt_ERROR = -1, //!< Invalid / unknown transaction
+ tt_DEFERRED, //!< Defferred locking transaction
+ tt_IMMEDIATE, //!< Immediate locking transaction
+ tt_EXCLUSIVE, //!< Exclusive locking transaction
+};
+
+#ifdef SQLITE3
+typedef sqlite3_context db_context_t; //!< User-defined database context
+typedef sqlite3_value db_value_t; //!< User-defined value
+#else
+#error MUST define db_context_t and db_value_t
+#endif
+
+/*!
+ * @brief User-defined function
+ */
+typedef void (*db_f) (db_context_t *, int, db_value_t **);
+
+/*!
+ * @brief Statement class
+ */
+struct statement {
+ db_t *db; //!< Database object
+#ifdef SQLITE3
+ sqlite3_stmt *stmt; //!< @private
+#endif
+};
+
+/* ======================================================================= */
+
+#if 0
+extern db_t *db_new(const char *) __nonnull((1));
+#endif
+
+/*!
+ * @brief Constructs a @em db object
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @param path [in] db file name
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_init(db_t * self, const char *path)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Destructs a @em db object
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_delete(db_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Execute a SQL txt statement
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @param fmt [in] printf-like format string
+ * @param ... [in] printf-like arguments
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_execute(db_t * self, const char *fmt, ...)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Open a @em db object
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @param flags [in] Open flagsprintf-like format string
+ * @return 0 on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_open(db_t * self, int flags)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Close a @em db object
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @return 0 on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_close(db_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Register a user-defined function on a @em db object
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @param name [in] db function name
+ * @param argc [in] Function argument count
+ * @param func [in] User-defined function
+ * @param data [in] User-defined data passed to @em finc
+ * @return 0 on siccess, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_register_function(db_t * self, const char *name, int argc,
+ db_f func, void *data)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Start a database transaction
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @param type [in] Transaction type
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_begin(db_t * self, transaction_type_t type)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Commit a database transaction
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_commit(db_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Rollback a database transaction
+ * @memberof db
+ * @param self [in] db object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int db_rollback(db_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#if 0
+extern statement_t *statement_new(db_t *) __nonnull((1));
+#endif
+
+/*!
+ * @brief Constructs a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param db [in] Pointer to owning database object
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_init(statement_t * self, db_t * db)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Destructs a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_delete(statement_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Prepare a @em statement object for execution
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @return DB_ROW when a row is available, DB_DONE when no more rows left
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_prepare(statement_t * self, const char *sql)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Execute the next step of @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_step(statement_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Reset a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_reset(statement_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Finalize a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_finalize(statement_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Binds an integer to a bound variable of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Bound varialble position number
+ * @param val [in] Integer value
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_bind_int(statement_t * self, int pos, int val)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Binds a long long integer to a bound variable of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Bound varialble position number
+ * @param val [in] Long Long Integer value
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_bind_int64(statement_t * self, int pos, int64_t val)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Binds a text string to a bound variable of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Bound varialble position number
+ * @param val [in] Text string value
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_bind_text(statement_t * self, int pos, const char *val)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Binds a blob to a bound variable of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Bound varialble position number
+ * @param val [in] Blob value
+ * @param len [in] Lengh of blob value (in bytes)
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_bind_blob(statement_t * self, int pos, const void *val,
+ int len)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Return an integer from the result set of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Column position number
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_column_int(statement_t * self, int pos)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return an long long integer from the result set of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Column position number
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int64_t statement_column_int64(statement_t * self, int pos)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the length of a column from the result set of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Column position number
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int statement_column_bytes(statement_t * self, int pos)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a text string from the result set of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Column position number
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern const unsigned char *statement_column_text(statement_t * self, int pos)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a blob from the result set of a @em statement object
+ * @memberof statement
+ * @param self [in] statement object @em self pointer
+ * @param pos [in] Column position number
+ * @return DB_OK on success, non-0 otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern const void *statement_column_blob(statement_t * self, int pos)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+#ifdef SQLITE3
+#define DBERR(d) ({ \
+ err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \
+ memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \
+ _e->size = snprintf(_e->data, ERR_DATA_SIZE, "%s (code=%d)", \
+ sqlite3_errmsg(d), sqlite3_errcode(d)); \
+ _e->magic = ERR_MAGIC, _e->type = DB, _e->code = sqlite3_errcode(d); \
+ _e->file = __FILE__, _e->line = __LINE__; \
+ err_put(_e); \
+ })
+
+#define SQLERR(d,s) ({ \
+ err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \
+ memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \
+ _e->size = snprintf(_e->data, ERR_DATA_SIZE, "'%s' => %s (code=%d)", \
+ sqlite3_sql(s), sqlite3_errmsg(d), \
+ sqlite3_errcode(d)); \
+ _e->magic = ERR_MAGIC, _e->type = SQL, _e->code = sqlite3_errcode(d); \
+ _e->file = __FILE__, _e->line = __LINE__; \
+ err_put(_e); \
+ })
+#if 0
+#define throw_db(x) ({ \
+ char __e[strlen(sqlite3_errmsg((x))) + 32]; \
+ __exc_throw(DB,((void*)__e), \
+ sprintf(__e, "(%d) %s", \
+ sqlite3_errcode((x)), \
+ sqlite3_errmsg((x))), \
+ __FILE__, __LINE__); \
+ })
+
+#define throw_statement(x,s) ({ \
+ char __e[strlen(sqlite3_errmsg((x))) + \
+ strlen(sqlite3_sql((s))) + 32]; \
+ __exc_throw(STATEMENT,((void*)__e), \
+ sprintf(__e, "(%d) %s\n%s", \
+ sqlite3_errcode((x)), \
+ sqlite3_errmsg((x)), \
+ sqlite3_sql((s))), \
+ __FILE__, __LINE__); \
+ })
+#endif
+#else
+#error FIX ME
+#endif
+
+/* ======================================================================= */
+
+#endif /* __DB_H__ */
diff --git a/clib/dispatch.h b/clib/dispatch.h
new file mode 100644
index 0000000..9cb82ef
--- /dev/null
+++ b/clib/dispatch.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: dispatch.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: File descriptor activity callback dispatcher
+ * Note:
+ * Date: 10/03/10
+ */
+
+#ifndef __DISPATCH_H__
+#define __DISPATCH_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <sys/epoll.h>
+
+#include "attribute.h"
+#include "builtin.h"
+#include "assert.h"
+
+#include "tree.h"
+#include "array.h"
+
+#define DISPATCH_READ EPOLLIN
+#define DISPATCH_WRITE EPOLLOUT
+#define DISPATCH_READ_HUP EPOLLRDHUP
+#define DISPATCH_READ_PRIORITY EPOLLPRI
+#define DISPATCH_ERROR EPOLLERR
+#define DISPATCH_WRITE_HUP EPOLLRDHUP
+#define DISPATCH_EDGE_TRIGGER EPOLLET
+#define DISPATCH_ONE_SHOT EPOLLONESHOT
+
+/* ======================================================================= */
+
+typedef struct dispatch dispatch_t;
+typedef struct dispatch_callback dispatch_callback_t;
+typedef struct dispatch_event dispatch_event_t;
+
+typedef int (*dispatch_f) (dispatch_event_t *, void *);
+
+struct dispatch {
+ int fd;
+ array_t events;
+};
+
+struct dispatch_callback {
+ int fd;
+ void *data;
+ dispatch_f func;
+};
+
+struct dispatch_event {
+ int fd;
+ uint32_t events;
+};
+
+/* ======================================================================= */
+
+extern void dispatch_init(dispatch_t *) __nonnull((1));
+
+extern void dispatch_delete(dispatch_t *) __nonnull((1));
+
+extern int dispatch_fileno(dispatch_t *) __nonnull((1));
+
+extern int dispatch_add(dispatch_t *, int, uint32_t, dispatch_callback_t *)
+__nonnull((1, 4));
+extern void dispatch_remove(dispatch_t *, int) __nonnull((1));
+
+#define dispatch_wait(...) STRCAT(dispatch_wait, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void dispatch_wait1(dispatch_t *) __nonnull((1));
+extern void dispatch_wait2(dispatch_t *, int) __nonnull((1));
+
+/* ======================================================================= */
+
+#endif /* __DISPATCH_H__ */
diff --git a/clib/ecc.h b/clib/ecc.h
new file mode 100644
index 0000000..c6f67f7
--- /dev/null
+++ b/clib/ecc.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file ecc.h
+ * @brief FSP-2 & P8 ECC functions
+ * @details These functions are used to insert and remove FSP-2 & P8 ECC
+ * bytes. 8-bits of ECC is inserted every 8 bytes of data such as:
+ * XXxxXXxxXXxxXXxxYY (where XXxx is 4 nibbles of data and YY is
+ * 2 nibbles of ECC)
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2011
+ */
+
+#ifndef __ECC_H__
+#define __ECC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+/** Status for the ECC removal function. */
+enum ecc_status
+ {
+ CLEAN=0, //< No ECC Error was detected.
+ CORRECTED=1, //< ECC error detected and corrected.
+ UNCORRECTABLE=2 //< ECC error detected and uncorrectable.
+ };
+typedef enum ecc_status ecc_status_t;
+
+enum ecc_bitfields
+ {
+ GD = 0xff, //< Good, ECC matches.
+ UE = 0xfe, //< Uncorrectable.
+ E0 = 71, //< Error in ECC bit 0
+ E1 = 70, //< Error in ECC bit 1
+ E2 = 69, //< Error in ECC bit 2
+ E3 = 68, //< Error in ECC bit 3
+ E4 = 67, //< Error in ECC bit 4
+ E5 = 66, //< Error in ECC bit 5
+ E6 = 65, //< Error in ECC bit 6
+ E7 = 64 //< Error in ECC bit 7
+ };
+
+/*!
+ * @brief Compute the 8-bit ECC (SFC) value given an array of 8
+ * unsigned char data values
+ * @param data [in] Input data
+ * @return 8-bit SFC ECC value
+ */
+ extern uint8_t sfc_ecc(uint8_t __data[8]);
+
+/*!
+ * @brief Copy bytes from the source buffer to the destination buffer while
+ * computing and injecting an 8-bit SFC ECC value for every 8-bytes
+ * of source buffer read
+ * @param __dst [in] Destination buffer
+ * @param __dst_sz [in] Destination buffer size (in bytes) which must be large
+ * enough to store both the data and the ECC bytes
+ * @param __src [in] Source buffer
+ * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple
+ * of 8 bytes
+ * @return -1 if an error occurs, number of bytes copied (including ECC bytes)
+ * otherwise.
+ * EINVAL if __src_sz is 0 or not a multiple of 8 bytes
+ * ENOBUFS if __dst_sz is not large enough to store the ECC bytes
+ *
+ */
+ extern ssize_t sfc_ecc_inject(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src,
+ size_t __src_sz)
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Copy bytes from the source buffer to the destination buffer while
+ * computing and removing an 8-bit SFC ECC value for every 9-bytes
+ * of source buffer read
+ * @param __dst [in] Destination buffer
+ * @param __dst_sz [in] Destination buffer size (in bytes) which must be large
+ * enough to store the data (after ECC removal)
+ * @param __src [in] Source buffer
+ * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple
+ * 9 bytes
+ * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes)
+ * otherwise. if an ECC mismatch error occurs the function returns
+ * immediately and the return code indicates the number of bytes processed
+ * prior to the ECC error.
+ * EINVAL if __src_sz is 0 or not a multiple of 9 bytes
+ * ENOBUFS if __dst_sz is not large enough to store the ECC bytes
+ */
+ extern ssize_t sfc_ecc_remove(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src,
+ size_t __src_sz)
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Hexdump the contents of a memory buffer to an output stream.
+ * This is a buck-standard hexdump except it issolates the ECC value
+ * in a separate column for easy debug.
+ * @param __out [in] Output stream
+ * @param __addr [in] Starting put to display (in bytes)
+ * @param __buf [in] Data buffer
+ * @param __buf_sz [in] Data buffer size (in bytes)
+ * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes)
+ * otherwise. if an ECC mismatch error occurs the function highlights the
+ * the corrupted data with red ANSI.
+ */
+ extern void sfc_ecc_dump(FILE * __out, uint32_t __addr,
+ void *__restrict __buf, size_t __buf_sz)
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Copy bytes from the source buffer to the destination buffer while
+ * computing and injecting an 8-bit P8 ECC value for every 8-bytes
+ * of source buffer read
+ * @param __dst [in] Destination buffer
+ * @param __dst_sz [in] Destination buffer size (in bytes) which must be large
+ * enough to store both the data and the ECC bytes
+ * @param __src [in] Source buffer
+ * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple
+ * of 8 bytes
+ * @return -1 if an error occurs, number of bytes copied (including ECC bytes)
+ * otherwise.
+ * EINVAL if __src_sz is 0 or not a multiple of 8 bytes
+ * ENOBUFS if __dst_sz is not large enough to store the ECC bytes
+ *
+ */
+ extern ssize_t p8_ecc_inject(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src,
+ size_t __src_sz)
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Copy bytes from the source buffer to the destination buffer while
+ * computing and removing an 8-bit P8 ECC value for every 9-bytes
+ * of source buffer read
+ * @param __dst [in] Destination buffer
+ * @param __dst_sz [in] Destination buffer size (in bytes) which must be large
+ * enough to store the data (after ECC removal)
+ * @param __src [in] Source buffer
+ * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple
+ * 9 bytes
+ * @return 0 - CLEAN for success
+ * 1 - CORRECTED error - _src [in] buffer changed
+ * 2 - UNCORRECTABLE error.
+ */
+ extern ecc_status_t p8_ecc_remove(void *__restrict __dst, size_t __dst_sz,
+ void *__restrict __src,
+ size_t __src_sz)
+
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Copy bytes from the source buffer to the destination buffer while
+ * computing and removing an 8-bit P8 ECC value for every 9-bytes
+ * of source buffer read
+ * @param __dst [in] Destination buffer
+ * @param __dst_sz [in] Destination buffer size (in bytes) which must be large
+ * enough to store the data (after ECC removal)
+ * @param __src [in] Source buffer
+ * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple
+ * 9 bytes
+ * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes)
+ * otherwise. if an ECC mismatch error occurs the function returns
+ * immediately and the return code indicates zero number of bytes
+ * processed
+ * EINVAL if __src_sz is 0 or not a multiple of 9 bytes
+ * ENOBUFS if __dst_sz is not large enough to store the ECC bytes
+ */
+ extern ssize_t p8_ecc_remove_size(void *__restrict __dst, size_t __dst_sz,
+ void *__restrict __src, size_t __src_sz)
+
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+/*!
+ * @brief Hexdump the contents of a memory buffer to an output stream.
+ * This is a buck-standard hexdump except it issolates the P8 ECC
+ * value in a separate column for easy debug.
+ * @param __out [in] Output stream
+ * @param __addr [in] Starting put to display (in bytes)
+ * @param __buf [in] Data buffer
+ * @param __buf_sz [in] Data buffer size (in bytes)
+ * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes)
+ * otherwise. if an ECC mismatch error occurs the function highlights the
+ * the corrupted data with red ANSI.
+ */
+ extern void p8_ecc_dump(FILE * __out, uint32_t __addr,
+ void *__restrict __buf, size_t __buf_sz)
+/*! @cond */
+ __nonnull((1, 3)) /*! @endcond */ ;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ECC_H__ */
diff --git a/clib/err.h b/clib/err.h
new file mode 100644
index 0000000..5b326d7
--- /dev/null
+++ b/clib/err.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file err.h
+ * @brief CLib error container
+ * @details Error is a special container that contains a library error
+ * @details For example,
+ * @code
+ * #include <clib/error.h>
+ * #include <clib/vector.h>
+ * #include <clib/vector_iter.h>
+ *
+ * FIX ME
+ *
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2013
+ */
+
+#ifndef __ERR_H__
+#define __ERR_H__
+
+#include <sys/cdefs.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "nargs.h"
+#include "list.h"
+
+/* ======================================================================= */
+
+#define ERR_NONE 0
+#define ERR_ERRNO 1
+#define ERR_UNEXPECTED 2
+#define ERR_VERSION 3
+#define ERR_LAST 4
+
+#define ERR_MAGIC 0x45525220 // "ERR "
+#define ERR_DATA_SIZE 1000
+
+#define INIT_ERR {ERR_MAGIC, 0, 0, NULL, 0, {0,}, 0}
+
+#define ERROR(t,c,f, ...) ({ \
+ err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \
+ memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \
+ _e->size = snprintf(_e->data, ERR_DATA_SIZE, (f), ##__VA_ARGS__); \
+ _e->magic = ERR_MAGIC, _e->type = (t), _e->code = (c); \
+ _e->file = __FILE__, _e->line = __LINE__; \
+ err_put(_e); \
+ })
+
+#define ERRNO(e) \
+ ERROR(ERR_ERRNO, (e), "%s => %s (errno=%d)", __func__, strerror(e), e)
+
+#define UNEXPECTED(f, ...) \
+ ERROR(ERR_UNEXPECTED, -1, (f), ##__VA_ARGS__)
+
+#define VERSION(v,f, ...) \
+ ERROR(ERR_VERSION, v, (f), ##__VA_ARGS__)
+
+/*!
+ * @brief error container
+ */
+struct err { //!< The error class
+ unsigned long magic; //!< Error magic number
+ list_node_t node; //!@ private
+
+ int type, code; //!< Error type & code
+ const char *file; //!< Error source file name
+ int line; //!< Error source line number
+
+ int size; //!< Extended error data size
+ char data[]; //!< Extended error data
+};
+typedef struct err err_t; //!< Alias for the @em error class
+
+/* ======================================================================= */
+
+extern void err_delete(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the last library error
+ * @details For example,
+ * @code
+ * ...
+ * err_clear(err_get());
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @return None
+ */
+extern err_t *err_get(void);
+
+/*!
+ * @brief Set the last library error
+ * @details For example,
+ * @code
+ * ...
+ * err_out(&e);
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @return None
+ */
+extern void err_put(err_t *);
+
+/*!
+ * @fn int err_type(err_t *self, [ int type ])
+ * @brief Set or get the type from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_type(&e, MY_ERROR_TYPE);
+ * if (err_type(&e) == MY_ERROR_TYPE)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @param type [in] error type
+ * @return current error type
+ */
+/*! @cond */
+#define err_type(...) STRCAT(err_type, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int err_type1(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void err_type2(err_t *, int)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn int err_code(err_t *self, [ int code ])
+ * @brief Set or get the code from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_code(&e, errno);
+ * if (err_code(&e) == EINVAL)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @param code [in] error code
+ * @return current error code
+ */
+/*! @cond */
+#define err_code(...) STRCAT(err_code, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int err_code1(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void err_code2(err_t *, int)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn int err_file(err_t *self, [ const char * file ])
+ * @brief Set or get the file name from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_file(&e, "example.txt");
+ * if (strcmp(err_file(&e), "example.txt") == 0)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @param file [in] error file name
+ * @return current file name
+ */
+/*! @cond */
+#define err_file(...) STRCAT(err_file, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const char *err_file1(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void err_file2(err_t *, const char *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn int err_line(err_t *self, [ int line ])
+ * @brief Set or get the line number from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_line(&e, errno);
+ * if (err_line(&e) == EINVAL)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @param line [in] error line
+ * @return current error line
+ */
+/*! @cond */
+#define err_line(...) STRCAT(err_line, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int err_line1(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void err_line2(err_t *, int)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn int err_data(err_t *self, [ const void *data ])
+ * @brief Set or get the extended data from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_data(&e, extended_data);
+ * memcpy(err_log, err_data(&e), err_size(&e));
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @param data [in] error extended data
+ * @return current error extended data
+ */
+/*! @cond */
+#define err_data(...) STRCAT(err_data, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const void *err_data1(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern void err_data2(err_t *, int, const void *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Get the data size from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * err_size(&e, data_size);
+ * if (err_size(&e) <= 0)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @return current error data size
+ */
+extern int err_size(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Get the type name from an @em error
+ * @details For example,
+ * @code
+ * ...
+ * if (strcmp(err_name(&e), "errno") == 0)
+ * ...
+ * @endcode
+ * @memberof error
+ * @param self [in] error object @em self pointer
+ * @return current error data size
+ */
+/*! @cond */
+extern const char *err_type_name(err_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#endif /* __ERR_H__ */
diff --git a/clib/exception.h b/clib/exception.h
new file mode 100644
index 0000000..26eaec2
--- /dev/null
+++ b/clib/exception.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file exception.h
+ * @brief Exceptions for C
+ * @details This file implements setjump / longjump based exceptions
+ * @note Using these macros will create an exception context in each thread
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2008-2011
+ */
+
+#ifndef __EXCEPTION_H__
+#define __EXCEPTION_H__
+
+#include <setjmp.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define EX_PAGE_SIZE 4096 //!< Max. size of an exception payload
+#define EXC_MAGIC 0x45584350 //!< Magic number, i.e. "EXCP"
+
+#define ASSERTION 1 //!< Assertion exception class
+//#define UNEXPECTED 2 //!< Unexpected result exception class
+//#define ERRNO 3 //!< @em errno error exception class
+#define EXC_LAST 4
+
+typedef struct exception_frame exception_frame_t; //!< Alias for the @em exception_frame class
+
+/*!
+ * @brief Exception class
+ */
+typedef struct {
+ int ex; //!< Exception class
+
+ const char *file; //!< Source file exception was thrown
+ int line; //!< Source line exception was thrown
+
+ void *data; //!< User-defined data associated with the exception
+ int size; //!< Size (in bytes) of the user-defined data
+} exception_t;
+
+/*!
+ * @brief Exception frame class
+ */
+struct exception_frame {
+ unsigned long magic; //!< @private
+ exception_frame_t *prev; //!< @private
+ jmp_buf jmp; //!< @private
+ exception_t exc; //!< @private
+};
+
+/*!
+ * @def try
+ * @brief Simulate a try {...} catch {...} else {...} end_try block
+ * @hideinitializer
+ * @details For example,
+ * @code
+ * ...
+ * #define ERROR 4
+ * ...
+ * exception_t * ex;
+ * ...
+ * try {
+ * ...
+ * } catch (ERROR, ex) {
+ * fprintf(stderr, "caught ERROR exception at: %s(%d)\n",
+ * ex->file, ex->line);
+ * struct ErrorStruct * err = (struct ErrorStruct *)ex->data
+ * ...format and log err...
+ * } end_try
+ * @endcode
+ */
+#define try \
+do { \
+ __exc_init(); \
+ exception_frame_t __frame; \
+ memset(&__frame, 0, sizeof(__frame)); \
+ __frame.magic = EXC_MAGIC, __frame.prev = __exc_get_frame(); \
+ __exc_set_frame(&__frame); \
+ volatile int __flag = setjmp(__frame.jmp); \
+ if (__flag == 0) {
+
+/*!
+ * @def catch
+ * @brief Simulate a try {...} catch {...} else {...} end_try block
+ * @hideinitializer
+ * @details For example,
+ * @code
+ * ...
+ * exception_t ex;
+ * ...
+ * try {
+ * ....
+ * } catch (TYPE1, ex) {
+ * ...
+ * } catch (TYPE2, ex) {
+ * ...
+ * } catch (TYPE3, ex) {
+ * ...
+ * } else (ex) {
+ * ...
+ * } end_try
+ * @endcode
+ */
+#define catch(x, e) \
+ } else if (__flag == (x)) { \
+ exception_frame_t * __tmp = __exc_get_frame(); \
+ (e) = __tmp->exc, (e).ex = (__flag); \
+
+/*!
+ * @def else
+ * @brief Simulate a try {...} catch {...} else {...} end_try block
+ * @hideinitializer
+ * @details For example,
+ * @code
+ * ...
+ * exception_t ex;
+ * ...
+ * try {
+ * ....
+ * } catch (TYPE1, ex) {
+ * ...
+ * } catch (TYPE2, ex) {
+ * ...
+ * } catch (TYPE3, ex) {
+ * ...
+ * } else (ex) {
+ * ...
+ * } end_try
+ * @endcode
+ */
+#define else(e) catch(__flag, (e))
+
+/*!
+ * @def end_try
+ * @brief Simulate a try {...} catch {...} else {...} end_try block
+ * @hideinitializer
+ */
+#define end_try \
+ } else { \
+ __exc_set_frame(__frame.prev); \
+ throw_bytes(__flag, __frame.exc.data, __frame.exc.size); \
+ } \
+ __exc_set_frame(__frame.prev); \
+} while (0);
+
+/*!
+ * @def throw(x, f, ...)
+ * @brief Throw a C exception with printf-like formatting
+ * @hideinitializer
+ * @param x [in] Exception class
+ * @param d [in] Pointer to user-defined data
+ * @param s [in] Size of user-defined data (in bytes)
+ * @details For example,
+ * @code
+ * ...
+ * #define ERRNO_STRING
+ * ...
+ * #define throw_errno_string(x) \
+ * throw(ERRNO_STRING, "errno=%d : %s", (x),
+ * (void*)strerror((x)), __FILE__, __LINE__)
+ * ...
+ * if (rc < 0)
+ * throw_errno_string(errno);
+ * ...
+ * exception_t ex;
+ * ...
+ * catch (ERRNO_STRING ex) {
+ * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n",
+ * (char *)ex.data, ex.file, ex.line);
+ * exit(1);
+ * }
+ * @endcode
+ */
+#define throw(x, f, ...) ({ \
+ char __d[EX_PAGE_SIZE]; \
+ __exc_throw((x), __d, snprintf(__d, sizeof __d, (f), ##__VA_ARGS__),\
+ __FILE__, __LINE__); \
+})
+
+/*!
+ * @def rethrow(x, f, ...)
+ * @brief Rethrow a C exception from within a try ... catch ... end_try block
+ * @hideinitializer
+ * @param e [in] Exception object
+ * @details For example,
+ * @code
+ * ...
+ * exception_t ex;
+ * ...
+ * catch (ERRNO_STRING ex) {
+ * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n",
+ * (char *)ex.data, ex.file, ex.line);
+ * rethrow(ex);
+ * }
+ * @endcode
+ */
+#define rethrow(e) ({ \
+ __exc_rethrow((e).ex, (e).data, (e).size, (e).file, (e).line); \
+})
+
+/*!
+ * @def throw_bytes(x, d, s)
+ * @brief Throw a C exception with user-defined data
+ * @hideinitializer
+ * @param x [in] Exception class
+ * @param d [in] Pointer to user-defined data
+ * @param s [in] Size of user-defined data (in bytes)
+ * @details For example,
+ * @code
+ * ...
+ * #define ERRNO_STRING
+ * ...
+ * #define throw_errno_string(x) \
+ * throw_bytes(ERRNO_STRING, (void*)strerror((x)),
+ * strlen(strerror((x)), __FILE__, __LINE__)
+ * ...
+ * if (rc < 0)
+ * throw_errno_string(errno);
+ * ...
+ * exception_t ex;
+ * ...
+ * catch (ERRNO_STRING ex) {
+ * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n",
+ * (char *)ex.data, ex.file, ex.line);
+ * exit(1);
+ * }
+ * @endcode
+ */
+#define throw_bytes(x, d, s) \
+ __exc_throw((x), (d), (s), __FILE__, __LINE__)
+
+/*!
+ * @def throw_unexpected(x)
+ * @brief Throw a message (NULL terminated C string), due to an @em
+ * unexpected error
+ * @hideinitializer
+ * @param x [in] Unexpected error message
+ * @details For example,
+ * @code
+ * ...
+ * rc = foo_function();
+ * if (rc < 0)
+ * throw_unexpected("Invalid return code from foo_function()");
+ * ...
+ * @endcode
+ */
+#define throw_unexpected(x) ({ \
+ __exc_throw(UNEXPECTED,((void*)x), strlen((x)), \
+ __FILE__, __LINE__); \
+ })
+
+/*!
+ * @def throw_errno(x)
+ * @brief Throw an errno number, due to an invalid function result.
+ * @hideinitializer
+ * @param x [in] Unexpected errno number
+ * @details For example,
+ * @code
+ * ...
+ * rc = open(...);
+ * if (rc == -1)
+ * throw_errno(errno);
+ * ...
+ * @endcode
+ */
+#define throw_errno(x) ({ \
+ __exc_throw(ERRNO,(void*)(x), 0, \
+ __FILE__, __LINE__); \
+ })
+
+/*! @cond */
+extern void __exc_init(void);
+extern exception_frame_t *__exc_get_frame(void);
+extern void __exc_set_frame(exception_frame_t *);
+extern int __exc_throw(int, void *, int, const char *, int);
+extern int __exc_rethrow(int, void *, int, const char *, int);
+extern void __exc_backtrace(const char *, ...);
+extern const char *__exc_name(int exc);
+/*! @endcond */
+
+#endif /* __EXCEPTION_H__ */
diff --git a/clib/hash.h b/clib/hash.h
new file mode 100644
index 0000000..2f1cb9d
--- /dev/null
+++ b/clib/hash.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: hash.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Various int32 hash functions
+ * Note:
+ * Date: 10/03/10
+ */
+
+#ifndef __HASH_H__
+#define __HASH_H__
+
+#include <stdint.h>
+
+/* ======================================================================= */
+
+static inline int32_t int32_hash1(int32_t);
+static inline int32_t int32_hash2(int32_t);
+static inline int32_t int32_hash3(int32_t);
+
+typedef uint32_t(*hash_t) (char *, uint32_t);
+
+static inline uint32_t rs_hash(char *, uint32_t);
+static inline uint32_t js_hash(char *, uint32_t);
+static inline uint32_t pjw_hash(char *, uint32_t);
+static inline uint32_t elf_hash(char *, uint32_t);
+static inline uint32_t bkdr_hash(char *, uint32_t);
+static inline uint32_t sdbm_hash(char *, uint32_t);
+static inline uint32_t djb_hash(char *, uint32_t);
+static inline uint32_t dek_hash(char *, uint32_t);
+static inline uint32_t bp_hash(char *, uint32_t);
+static inline uint32_t fnv_hash(char *, uint32_t);
+static inline uint32_t ap_hash(char *, uint32_t);
+
+/* ======================================================================= */
+
+static inline int32_t int32_hash1(int32_t key)
+{
+ key = ~key + (key << 15);
+ key = key ^ (key >> 12);
+ key = key + (key << 2);
+ key = key ^ (key >> 4);
+ key = key * 2057;
+ key = key ^ (key >> 16);
+
+ return key;
+}
+
+static inline int32_t int32_hash2(int32_t key)
+{
+ key = (key + 0x7ed55d16) + (key << 12);
+ key = (key ^ 0xc761c23c) ^ (key >> 19);
+ key = (key + 0x165667b1) + (key << 5);
+ key = (key + 0xd3a2646c) ^ (key << 9);
+ key = (key + 0xfd7046c5) + (key << 3);
+ key = (key ^ 0xb55a4f09) ^ (key >> 16);
+
+ return key;
+}
+
+static inline int32_t int32_hash3(int32_t key)
+{
+ int32_t c2 = 0x27d4eb2d;
+
+ key = (key ^ 61) ^ (key >> 16);
+ key = key + (key << 3);
+ key = key ^ (key >> 4);
+ key = key * c2;
+ key = key ^ (key >> 15);
+
+ return key;
+}
+
+/* ======================================================================= */
+
+static inline uint32_t rs_hash(char *str, uint32_t len)
+{
+ uint32_t b = 378551;
+ uint32_t a = 63689;
+ uint32_t hash = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = hash * a + (*str);
+ a = a * b;
+ }
+
+ return hash;
+}
+
+static inline uint32_t js_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 1315423911;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash ^= ((hash << 5) + (*str) + (hash >> 2));
+ }
+
+ return hash;
+}
+
+static inline uint32_t pjw_hash(char *str, uint32_t len)
+{
+ const uint32_t BitsInUnsignedInt = (uint32_t) (sizeof(uint32_t) * 8);
+ const uint32_t ThreeQuarters = (uint32_t) ((BitsInUnsignedInt * 3) / 4);
+ const uint32_t OneEighth = (uint32_t) (BitsInUnsignedInt / 8);
+ const uint32_t HighBits =
+ (uint32_t) (0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);
+ uint32_t hash = 0;
+ uint32_t test = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = (hash << OneEighth) + (*str);
+
+ if ((test = hash & HighBits) != 0) {
+ hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
+ }
+ }
+
+ return hash;
+}
+
+static inline uint32_t elf_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 0;
+ uint32_t x = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = (hash << 4) + (*str);
+ if ((x = hash & 0xF0000000L) != 0) {
+ hash ^= (x >> 24);
+ }
+ hash &= ~x;
+ }
+
+ return hash;
+}
+
+static inline uint32_t bkdr_hash(char *str, uint32_t len)
+{
+ uint32_t seed = 131; /* 31 131 1313 13131 131313 etc.. */
+ uint32_t hash = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = (hash * seed) + (*str);
+ }
+
+ return hash;
+}
+
+static inline uint32_t sdbm_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = (*str) + (hash << 6) + (hash << 16) - hash;
+ }
+
+ return hash;
+}
+
+static inline uint32_t djb_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 5381;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = ((hash << 5) + hash) + (*str);
+ }
+
+ return hash;
+}
+
+static inline uint32_t dek_hash(char *str, uint32_t len)
+{
+ uint32_t hash = len;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);
+ }
+ return hash;
+}
+
+static inline uint32_t bp_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash = hash << 7 ^ (*str);
+ }
+
+ return hash;
+}
+
+static inline uint32_t fnv_hash(char *str, uint32_t len)
+{
+ const uint32_t fnv_prime = 0x811C9DC5;
+ uint32_t hash = 0;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash *= fnv_prime;
+ hash ^= (*str);
+ }
+
+ return hash;
+}
+
+static inline uint32_t ap_hash(char *str, uint32_t len)
+{
+ uint32_t hash = 0xAAAAAAAA;
+ uint32_t i = 0;
+
+ for (i = 0; i < len; str++, i++) {
+ hash ^= ((i & 1) == 0) ? ((hash << 7) ^ (*str) * (hash >> 3)) :
+ (~((hash << 11) + ((*str) ^ (hash >> 5))));
+ }
+
+ return hash;
+}
+
+/* ======================================================================= */
+
+#endif /* __HASH_H__ */
diff --git a/clib/heap.h b/clib/heap.h
new file mode 100644
index 0000000..3f82acb
--- /dev/null
+++ b/clib/heap.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: heap.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Arena based (slab) memory allocator
+ * Note:
+ * Date: 07/28/10
+ */
+
+#ifndef __HEAP_H__
+#define __HEAP_H__
+
+#include <stdlib.h>
+
+#include "slab.h"
+
+/* ======================================================================= */
+
+typedef struct heap heap_t;
+
+struct heap {
+ size_t page_size;
+ size_t alloc_size;
+
+ size_t slab_size;
+ slab_t *slab[];
+};
+
+/* ======================================================================= */
+
+#define heap_init(a,c) (__heap_init((h),(a),(c),__FILE__,__LINE__))
+extern void __heap_init(heap_t * self, size_t alloc_size, size_t cache_size,
+ const char *file, int line);
+
+#define heap_new(a,c) (__heap_new((a),(c),__FILE__,__LINE__))
+extern heap_t *__heap_new(size_t alloc_size, size_t cache_size,
+ const char *file, int line);
+#define heap_delete(h) (__heap_delete((h),__FILE__,__LINE__))
+extern void __heap_delete(heap_t * self, const char *file,
+ int line) __nonnull((1));
+
+#define heap_alloc(h,s) (__heap_alloc((h),(s),__FILE__,__LINE__))
+extern void *__heap_alloc(heap_t * self, size_t size, const char *file,
+ int line) __nonnull((1));
+#define heap_free(h,p) (__heap_free((h),(p),__FILE__,__LINE__))
+extern void __heap_free(heap_t * self, void *ptr, const char *file,
+ int line) __nonnull((1, 2));
+
+extern void heap_dump(heap_t * self, FILE * out) __nonnull((1));
+
+/* ======================================================================= */
+
+#endif /* __HEAP_H__ */
diff --git a/clib/ident.h b/clib/ident.h
new file mode 100644
index 0000000..a4b5394
--- /dev/null
+++ b/clib/ident.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file ident.h
+ * @brief Identification object
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2008-2011
+ */
+
+#ifndef __IDENT_H__
+#define __IDENT_H__
+
+#include <stdint.h>
+
+#define IDENT_MAGIC_0 0
+#define IDENT_MAGIC_1 1
+#define IDENT_MAGIC_2 2
+#define IDENT_MAGIC_3 3
+#define IDENT_MAJOR 4
+#define IDENT_MINOR 5
+#define IDENT_PATCH 6
+#define IDENT_FLAGS 7
+#define IDENT_SIZE 8
+
+#define INIT_IDENT {0,}
+
+#define MAGIC_CHECK(i, m) ({ \
+ bool rc = (((i)[IDENT_MAGIC_0] != (m)[IDENT_MAGIC_0]) || \
+ ((i)[IDENT_MAGIC_1] != (m)[IDENT_MAGIC_1]) || \
+ ((i)[IDENT_MAGIC_2] != (m)[IDENT_MAGIC_2]) || \
+ ((i)[IDENT_MAGIC_3] != (m)[IDENT_MAGIC_3])); \
+ rc; \
+ })
+
+typedef uint8_t ident_t[IDENT_SIZE];
+
+#endif /* __IDENT_H__ */
diff --git a/clib/libclib.h b/clib/libclib.h
new file mode 100644
index 0000000..6f6bedc
--- /dev/null
+++ b/clib/libclib.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+#ifndef __LIBCLIB_H__
+#define __LIBCLIB_H__
+
+#define CLIB_MAJOR 0x01
+#define CLIB_MINOR 0x00
+#define CLIB_PATCH 0x00
+
+#include <features.h>
+
+#include "attribute.h"
+#include "version.h"
+#include "builtin.h"
+#include "assert.h"
+#include "ident.h"
+#include "nargs.h"
+#include "hash.h"
+#include "misc.h"
+#include "type.h"
+#include "min.h"
+#include "max.h"
+#include "err.h"
+#include "raii.h"
+
+#endif /* __LIBCLIB_H__ */
diff --git a/clib/list.h b/clib/list.h
new file mode 100644
index 0000000..db8ef59
--- /dev/null
+++ b/clib/list.h
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file list.h
+ * @brief List container
+ * @details
+ * A List is a data structure (container) used for collecting a sequence of elements.
+ * List allow for efficient insertion, removal and retreival of elements.
+ *
+ * @details For example,
+ * @code
+ * #include <clib/list.h>
+ * #include <clib/list_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * typedef struct {
+ * list_node_t node;
+ * int i;
+ * float f;
+ * } data_t;
+ *
+ * slab_t s;
+ * slab_init(&s, sizeof(data_t), 0);
+ *
+ * list_t a;
+ * list_init(&a);
+ *
+ * int i;
+ * for (i=0; i<10; i++) {
+ * data_t * d = (data_t *)slab_alloc(&s);
+ *
+ * d->i = i;
+ * d->f = (float)i;
+ *
+ * list_add_tail(&l, &d->node);
+ * }
+ *
+ * data_t * d;
+ * list_for_each(&l, d, node) {
+ * printf("i: %d f: %f\n", d->i, d->f);
+ * }
+ *
+ * list_dump(&l, stdout);
+ * slab_delete(&s);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @date 2010-2011
+ */
+
+#ifndef __LIST_H__
+#define __LIST_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "builtin.h"
+#include "assert.h"
+#include "type.h"
+
+/* ==================================================================== */
+
+#define INIT_LIST_NODE {NULL,NULL}
+#define INIT_LIST {INIT_LIST_NODE}
+
+typedef struct list_node list_node_t; //!< Alias for the @em list_node class
+typedef struct list list_t; //!< Alias for the @em list class
+
+/*!
+ * @brief list node
+ * @details Primitive types cannot be stored in the @em list container, instead the user must
+ * embed a @em list_node object within the stored object.
+ */
+struct list_node {
+ list_node_t *prev; //!< Reference the previous list_node
+ list_node_t *next; //!< Reference the next list_node
+};
+
+/*!
+ * @brief list container
+ */
+struct list {
+ list_node_t node;
+};
+
+/* ==================================================================== */
+
+/*!
+ * @brief Return a pointer to the node's next node
+ * @details For example,
+ * @code
+ * ...
+ * list_node_t * node = list_head(&l);
+ * list_node_t * next = list_node_next(node);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list node object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_node_next(list_node_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to the node's previous node
+ * @details For example,
+ * @code
+ * ...
+ * list_node_t * node = list_head(&l);
+ * list_node_t * next = list_node_prev(node);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list node object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_node_prev(list_node_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Constructs an @em list container object
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline void list_init(list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Inserts a node at the head of the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ *
+ * data_t * d = (data_t *) MALLOC(sizeof(*d));
+ * list_add_head(&l, &d->node);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @param node [in] list_node object to insert
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline void list_add_head(list_t * self, list_node_t * node)
+/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Inserts a node at the tail of the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ *
+ * data_t * d = (data_t *) MALLOC(sizeof(*d));
+ * list_add_tail(&l, &d->node);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @param node [in] list_node object to insert
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline void list_add_tail(list_t * self, list_node_t * node)
+/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Remove a node at the head of the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * data_t * d = (data_t *)list_remove_head(&l);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_remove_head(list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Remove a node at the tail of the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * data_t * d = (data_t *)list_remove_tail(&l);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_remove_tail(list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Remove the list_node referenced by @em node from the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * data_t * d = (data_t *) MALLOC(sizeof(*d));
+ * list_add_tail(&l, &d->node);
+ * list_remove_node(&l, &d->node);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_remove_node(list_t * self, list_node_t * node)
+/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Test whether a @em list container is empty
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * if (list_empty(&l)) {
+ * ...
+ * }
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return true if empty, false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline bool list_empty(const list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the head list_node from the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_node_t * node = list_head(&l);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_head(list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the tail list_node from the @em list container
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_node_t * node = list_tail(&l);
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline list_node_t *list_tail(list_t * self)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print and dump the contents of the @em list to output stream @em out
+ * @details For example,
+ * @code
+ * ...
+ * typedef struct {
+ * ...
+ * list_node_t node;
+ * ...
+ * } data_t;
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_dump(&l, stdout) {
+ * ...
+ * }
+ * ...
+ * @endcode
+ * @memberof list
+ * @param self [in] list object @em self pointer
+ * @param out [in] output stream
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void list_dump(list_t * self, FILE *)
+/*! @cond */ __nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @def list_entry(node_pointer, containing_type, member)
+ * @hideinitializer
+ * @brief Returns a pointer to the containing structure of a node
+ * @param n [in] Pointer to a list_node object
+ * @param T [in] Type of the containing structure
+ * @param m [in] Name of the list_node member
+ */
+#define list_entry(n, T, m) container_of(n, T, m)
+
+#if 0
+/*!
+ * @def list_head(list_pointer, containing_type, member)
+ * @hideinitializer
+ * @brief Returns a pointer to the head element in the @em list
+ * @param n [in] Pointer to a list_node object
+ * @param T [in] Type of the containing structure
+ * @param m [in] Name of the list_node member
+ */
+#define list_head(l, T, m) ({ \
+ list_node_t * __h = (list_empty(l) ? NULL : list_entry((l)->node.next, T, m)) \
+ __h; \
+ })
+
+/*!
+ * @def list_tail(list_pointer, containing_type, member)
+ * @hideinitializer
+ * @brief Returns a pointer to the tail element in the @em list
+ * @param n [in] Pointer to a list_node object
+ * @param T [in] Type of the containing structure
+ * @param m [in] Name of the list_node member
+ */
+#define list_tail(h, T, m) ({ \
+ list_node_t * __t = (list_empty(l) ? NULL : list_entry((l)->node.prev, T, m)) \
+ __t; \
+ })
+#endif
+
+/* ==================================================================== */
+
+/*! @cond */
+static inline list_node_t *list_node_next(list_node_t * self)
+{
+ assert(self != NULL);
+ return self->next;
+}
+
+static inline list_node_t *list_node_prev(list_node_t * self)
+{
+ assert(self != NULL);
+ return self->prev;
+}
+
+static inline void list_init(list_t * self)
+{
+ assert(self != NULL);
+ self->node.prev = self->node.next = &self->node;
+}
+
+static inline void list_add_head(list_t * self, list_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ node->next = self->node.next;
+ node->prev = &self->node;
+ self->node.next->prev = node;
+ self->node.next = node;
+}
+
+static inline void list_add_tail(list_t * self, list_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ node->next = &self->node;
+ node->prev = self->node.prev;
+ self->node.prev->next = node;
+ self->node.prev = node;
+}
+
+static inline list_node_t *list_remove_head(list_t * self)
+{
+ assert(self != NULL);
+
+ list_node_t *node = self->node.next;
+
+ if (node != NULL) {
+ self->node.next = self->node.next->next;
+ self->node.next->prev = node->prev;
+ node->next = node->prev = NULL;
+ }
+
+ return node;
+}
+
+static inline list_node_t *list_remove_tail(list_t * self)
+{
+ assert(self != NULL);
+
+ list_node_t *node = self->node.prev;
+
+ if (node != NULL) {
+ self->node.prev = self->node.prev->prev;
+ self->node.prev->next = node->next;
+ node->prev = node->next = NULL;
+ }
+
+ return node;
+}
+
+static inline list_node_t *list_remove_node(list_t * self, list_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ node->next->prev = node->prev;
+ node->prev->next = node->next;
+ node->next = node->prev = NULL;
+
+ return node;
+}
+
+static inline bool list_empty(const list_t * self)
+{
+ return self->node.next == &self->node;
+}
+
+static inline list_node_t *list_head(list_t * self)
+{
+ assert(self != NULL);
+ return self->node.next;
+}
+
+static inline list_node_t *list_tail(list_t * self)
+{
+ assert(self != NULL);
+ return self->node.prev;
+}
+
+/*! @endcond */
+
+/* ==================================================================== */
+
+#endif /* __LIST_H__ */
diff --git a/clib/list_iter.h b/clib/list_iter.h
new file mode 100644
index 0000000..d9cfcec
--- /dev/null
+++ b/clib/list_iter.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file list_iter.h
+ * @brief List iterator
+ * @details For example,
+ * @code
+ * ...
+ * data_t * d;
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &t, LI_FLAG_FWD);
+ * ...
+ * list_for_each(&it, d, node) {
+ * printf("forward i[%d] f[%f]\n", d->i, d->f);
+ * }
+ * ...
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __LIST_ITER_H__
+#define __LIST_ITER_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "builtin.h"
+#include "list.h"
+#include "nargs.h"
+
+/* ======================================================================= */
+
+typedef struct list_iter list_iter_t; //!< Alias for the @em list_iter class
+
+/*!
+ * @brief list iterator
+ * @details list iterator class
+*/
+struct list_iter {
+ list_t *list; //!< Reference to the target list object
+ list_node_t *node; //!< Current position of the iterator
+ uint32_t flags; //!< Iterator configuration flags
+};
+
+/* ======================================================================= */
+
+#define LI_FLAG_NONE 0x00000000 //!< No flag mask
+#define LI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask
+#define LI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask
+#define LI_FLAG_MASK 0x00000003 //!< All flags mask
+
+/*!
+ * @brief Initializes an @em list_iter iterator object
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * @endcode
+ * @memberof list_iter
+ * @param self [in] list_iter object @em self pointer
+ * @param list [in] list container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em list pointer is NULL
+ */
+extern void list_iter_init(list_iter_t * self, list_t * list, uint32_t flags)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets a @em list iterator object
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &l, l_FLAG_FWD);
+ * list_iter_clear(&it);
+ * ...
+ * @endcode
+ * @memberof list_iter
+ * @param self [in] list_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void list_iter_clear(list_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to @em list element bytes at the current iterator position
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l, 4, 1024);
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * data_t * d = (data_t *)list_iter_elem(&it);
+ * ...
+ * @endcode
+ * @memberof list_iter
+ * @param self [in] list_iter object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern list_node_t *list_iter_elem(list_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn list_node_t * list_iter_inc(list_iter_t * self, size_t count = 1)
+ * @brief Increment the position of an @em list iterator
+ * @details If the second (2nd) parameter is omitted, the iterator is incremented by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * list_iter_inc(&it);
+ * ...
+ * @endcode
+ * @memberof list_iter
+ * @param self [in] list_iter object @em self pointer
+ * @param count [in] Number of positions to increment (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define list_iter_inc(...) STRCAT(list_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern list_node_t *list_iter_inc1(list_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern list_node_t *list_iter_inc2(list_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn list_node_td * list_iter_dec(list_iter_t * self, size_t count = 1)
+ * @brief decrement the position of an @em list iterator
+ * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * list_t l;
+ * list_init(&l);
+ * ...
+ * list_iter_t it;
+ * list_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * list_iter_dec(&it, 3);
+ * ...
+ * @endcode
+ * @memberof list_iter
+ * @param self [in] list_iter object @em self pointer
+ * @param count [in] Number of positions to decrement (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define list_iter_dec(...) STRCAT(list_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern list_node_t *list_iter_dec1(list_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern list_node_t *list_iter_dec2(list_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @def list_for_each(it, i, m)
+ * @hideinitializer
+ * @brief List for-each algorithm
+ * @param it [in] Tree iterator object
+ * @param i [in] Tree element variable
+ * @param m [in] Member name
+ */
+#if 0
+#define list_for_each(l, i, m) \
+ for (i = container_of_var(l->node.next, i, m); \
+ &i->m != &(l)->node; \
+ i = container_of_var(i->m.next, i, m))
+#define list_for_each_safe(l, i, n, m) \
+ for (i = container_of_var((l)->node.next, i, m), \
+ n = container_of_var(i->m.next, i, m); \
+ &i->m != &(l)->node; \
+ i = n, n = container_of_var(i->m.next, i, m))
+#endif
+
+#define list_for_each(it, i, m) \
+ for (i = container_of_var(list_iter_elem(it), i, m); \
+ list_iter_inc(it) != NULL; \
+ i = container_of_var(list_iter_elem(it), i, m))
+
+#endif /* __LIST_ITER_H__ */
diff --git a/clib/map.h b/clib/map.h
new file mode 100644
index 0000000..80911be
--- /dev/null
+++ b/clib/map.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: map.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Map container
+ * Note:
+ * Date: 10/22/10
+ */
+
+#ifndef __MAP_H__
+#define __MAP_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "builtin.h"
+#include "compare.h"
+#include "type.h"
+
+#include "tree.h"
+
+/* ==================================================================== */
+
+typedef tree_t map_t; //!< Alias for the @em map class
+typedef tree_node_t map_node_t; //!< Alias for the @em map_node class
+
+/*!
+ * @brief Constructs a @em map_node object
+ * @memberof map_node
+ * @param self [in] map_node object @em self pointer
+ * @param key [in] pointer to key bytes
+ * @return Reference to an initialized map_node object on SUCCESS
+ */
+static inline map_node_t *map_node_init(map_node_t *, const void *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @fn void map_init(map_t * self, compare_f cmp = default_compare)
+ * @brief Constructs a @em map object
+ * @memberof map
+ * @param self [in] map_node object @em self pointer
+ * @param cmp [in] Reference to the @em map_node compare function
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define map_init(...) STRCAT(map_init, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline int map_init1(map_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+static inline int map_init2(map_t *, compare_f)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Insert a new @em map_node into the @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @param node [in] Reference to the @em map_node to insert
+ * @return @em true if the @em map_node was inserted, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em map_node.key points to a duplicate key
+ */
+static inline int map_insert(map_t *, map_node_t *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Removes a @em map_node from the @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @param node [in] Reference to the @em map_node to remove
+ * @return @em true if the @em map_node was removed, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline int map_remove(map_t *, map_node_t *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Find a @em map_node within the @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @param key [in] Reference to the @em key to find
+ * @return Reference to a @em map_node on SUCCESS, false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline map_node_t *map_find(map_t *, const void *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Hexdump the contents of a @em map to @em out output stream
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @param out [in] Reference to the @em out output stream
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline void map_dump(map_t *, FILE *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Return whether a @em map container is empty
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @return @em true if @em map is empty, false otherwise
+ */
+static inline bool map_empty(map_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the node count of a @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @return @em 0 if @em map is empty, @em non-0 otherwise
+ */
+static inline size_t map_size(map_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the minimum @em map_node of a @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @return Reference to a @em map_node if @em self is non-NULL, NULL otherwise
+ */
+static inline map_node_t *map_min(map_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the maximum @em map_node of a @em map container
+ * @memberof map
+ * @param self [in] map object @em self pointer
+ * @return Reference to a @em map_node if @em self is non-NULL, NULL otherwise
+ */
+static inline map_node_t *map_max(map_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ==================================================================== */
+
+static inline map_node_t *map_node_init(map_node_t * self, const void *key)
+{
+ return (map_node_t *) tree_node_init((tree_node_t *) self, key);
+}
+
+/* ==================================================================== */
+
+static inline int map_init1(map_t * self)
+{
+ assert(self != NULL);
+ return tree_init((tree_t *) self, default_compare);
+}
+
+static inline int map_init2(map_t * self, compare_f cmp)
+{
+ assert(self != NULL);
+ assert(cmp != NULL);
+ return tree_init((tree_t *) self, cmp);
+}
+
+static inline int map_insert(map_t * self, map_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+ return splay_insert((tree_t *) self, (tree_node_t *) node);
+}
+
+static inline int map_remove(map_t * self, map_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+ return splay_remove((tree_t *) self, (tree_node_t *) node);
+}
+
+static inline map_node_t *map_find(map_t * self, const void *key)
+{
+ assert(self != NULL);
+ assert(key != NULL);
+ return (map_node_t *) tree_find((tree_t *) self, key);
+}
+
+static inline bool map_empty(map_t * self)
+{
+ assert(self != NULL);
+ return tree_empty((tree_t *) self);
+}
+
+static inline void map_dump(map_t * self, FILE * out)
+{
+ assert(self != NULL);
+ tree_dump((tree_t *) self, out ? out : stdout);
+}
+
+static inline size_t map_size(map_t * self)
+{
+ assert(self != NULL);
+ return tree_size((tree_t *) self);
+}
+
+static inline map_node_t *map_min(map_t * self)
+{
+ assert(self != NULL);
+ return tree_min((tree_t *) self);
+}
+
+static inline map_node_t *map_max(map_t * self)
+{
+ assert(self != NULL);
+ return tree_max((tree_t *) self);
+}
+
+/* ==================================================================== */
+
+#endif /* __MAP_H__ */
diff --git a/clib/map_iter.h b/clib/map_iter.h
new file mode 100644
index 0000000..5954e63
--- /dev/null
+++ b/clib/map_iter.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: map_iter.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Iterator for the Map container
+ * Note:
+ * Date: 10/22/10
+ */
+
+#ifndef __MAP_ITER_H__
+#define __MAP_ITER_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "builtin.h"
+
+#include "map.h"
+#include "tree_iter.h"
+
+/* ==================================================================== */
+
+typedef tree_iter_t map_iter_t;
+
+/* ==================================================================== */
+
+#define MI_FLAG_NONE TI_FLAG_NONE
+#define MI_FLAG_FWD TI_FLAG_FWD
+#define MI_FLAG_BWD TI_FLAG_BWD
+
+/*!
+ * @brief Initializes an @em map_iter iterator object
+ * @memberof map_iter
+ * @param self [in] map_iter object @em self pointer
+ * @param map [in] map container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em map pointer is NULL
+ */
+static inline int map_iter_init(map_iter_t *, map_t *, uint32_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets an @em map iterator object
+ * @memberof map_iter
+ * @param self [in] map_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline int map_iter_clear(map_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to a @em map_node element at the current iterator
+ * position
+ * @memberof map_iter
+ * @param self [in] map_iter object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline map_node_t *map_iter_elem(map_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Increment the position of an @em map iterator
+ * @memberof map_iter
+ * @param self [in] map_iter object @em self pointer
+ * @param count [in] Number of positions to increment
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline map_node_t *map_iter_inc(map_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Decrement the position of an @em map iterator
+ * @memberof map_iter
+ * @param self [in] map_iter object @em self pointer
+ * @param count [in] Number of positions to decrement
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+static inline map_node_t *map_iter_dec(map_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @def map_for_each(it, i, m)
+ * @hideinitializer
+ * @brief Tree for-each algorithm
+ * @param it [in] Tree iterator object
+ * @param i [in] Tree element variable
+ * @param m [in] Member name
+ */
+#define map_for_each(it, i, m) \
+ for (map_iter_clear(it), i = container_of_var(map_iter_elem(it), i, m); \
+ map_iter_elem(it) != NULL; \
+ i = container_of_var(map_iter_inc(it), i, m))
+
+/* ==================================================================== */
+
+static inline int map_iter_init(map_iter_t * self, map_t * map, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(map != NULL);
+ return tree_iter_init((tree_iter_t *) self, (tree_t *) map, flags);
+}
+
+int map_iter_clear(map_iter_t * self)
+{
+ assert(self != NULL);
+ return tree_iter_clear((tree_iter_t *) self);
+}
+
+static inline map_node_t *map_iter_elem(map_iter_t * self)
+{
+ assert(self != NULL);
+ return (map_node_t *) tree_iter_elem((tree_iter_t *) self);
+}
+
+static inline map_node_t *map_iter_inc(map_iter_t * self)
+{
+ assert(self != NULL);
+ return (map_node_t *) tree_iter_inc((tree_iter_t *) self);
+}
+
+static inline map_node_t *map_iter_dec(map_iter_t * self)
+{
+ assert(self != NULL);
+ return (map_node_t *) tree_iter_dec((tree_iter_t *) self);
+}
+
+/* ==================================================================== */
+
+#endif /* __MAP_ITER_H__ */
diff --git a/clib/max.h b/clib/max.h
new file mode 100644
index 0000000..56957a5
--- /dev/null
+++ b/clib/max.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: max.h
+ * Author:
+ * Descr: typesafe max function
+ * Note:
+ * Date: 10/22/10
+ */
+
+#ifndef __MAX_H__
+#define __MAX_H__
+
+#define max(x,y) \
+({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void)((void*)&_x == (void*)&_y); \
+ _x < _y ? _y : _x; \
+})
+
+#endif /* __MAX_H__ */
diff --git a/clib/memory_leak_detection.h b/clib/memory_leak_detection.h
new file mode 100644
index 0000000..5c1afb1
--- /dev/null
+++ b/clib/memory_leak_detection.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2011
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module: memory_leak_detection.h
+ *
+ *
+ * Functions:
+ *
+ * Description: This module defines macros which should be used in place
+ * of malloc and free. These macros default to malloc and
+ * free, but, when FIND_MEMORY_LEAK is defined, they
+ * default to functions which wrapper malloc and free to
+ * track memory allocations and find memory leaks. Several
+ * other useful functions are defined as well.
+ *
+ * Author: B. Rafanello
+ *
+ * Version 0.0.0.1
+ */
+
+#ifndef MEMORY_LEAK_DETECTION_H
+
+#define MEMORY_LEAK_DETECTION_H 1
+
+#if ( FIND_MEMORY_LEAK > 0 )
+
+#warning Memory Leak Detection enabled!
+
+/* ================================================================= */
+/* ================================================================= */
+/* */
+/* The following functions are not meant to be called directly! Use */
+/* macros defined after the functions instead of the functions */
+/* themselves. */
+/* ================================================================= */
+/* ================================================================= */
+
+/*********************************************************************/
+/* */
+/* Function Name: MEMORY_func */
+/* */
+/* Descriptive Name: This function acts as a replacement for */
+/* malloc. It allocates memory (using malloc) */
+/* and enters that memory into a tracking */
+/* structure so that memory leaks, if any, may */
+/* be found. */
+/* */
+/* Input: size_t sz - The number of bytes to be allocated. */
+/* unsigned int Alignment - 0 for non-aligned (normal) */
+/* malloc, > 0 to return an */
+/* address aligned on a specific */
+/* memory boundary. If > 0, then */
+/* Alignment must be a power of 2 */
+/* and a multiple of sizeof(void *)*/
+/* void ** Memory_Location - The address of a variable which*/
+/* will hold the address of the */
+/* allocated by this function. */
+/* const char * mod_name - The name of the module from which*/
+/* this function was called. */
+/* const char * func - The name of the function from which */
+/* this function was called. */
+/* const int line - The line number of the code containing */
+/* the call to this function. */
+/* */
+/* Output: If Success : The function return value will be 0. */
+/* *Memory_Location will be set to the address*/
+/* of the first byte of the user portion of */
+/* any memory that was allocated. */
+/* */
+/* If Failure : The function return value will be EINVAL or*/
+/* ENOMEM. Errors may be reported on stderr. */
+/* *Memory_Location will be set to NULL. */
+/* */
+/* Error Handling: This function will abort if an error is */
+/* is detected. Errors could be lack of memory, or*/
+/* corruption of the internal structures used to */
+/* track allocated blocks of memory. */
+/* */
+/* Side Effects: Memory may be allocated, errors may be reported */
+/* on stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+int MEMORY_func(size_t sz,
+ unsigned int Alignment,
+ void **Memory_Location,
+ const char *mod_name, const char *func, const int line);
+
+/*********************************************************************/
+/* */
+/* Function Name: MALLOC_func */
+/* */
+/* Descriptive Name: This function acts as a replacement for */
+/* malloc. It allocates memory (using malloc) */
+/* and enters that memory into a tracking */
+/* structure so that memory leaks, if any, may */
+/* be found. */
+/* */
+/* Input: size_t sz - The number of bytes to be allocated. */
+/* const char * mod_name - The name of the module from which*/
+/* this function was called. */
+/* const char * func - The name of the function from which */
+/* this function was called. */
+/* const int line - The line number of the code containing */
+/* the call to this function. */
+/* */
+/* Output: If Success : The function return value will be non-NULL.*/
+/* */
+/* If Failure : The function return value will be NULL. */
+/* Errors may be reported on stderr. */
+/* */
+/* Error Handling: This function will abort if an error is */
+/* is detected. Errors could be lack of memory, or*/
+/* corruption of the internal structures used to */
+/* track allocated blocks of memory. */
+/* */
+/* Side Effects: Memory may be allocated, errors may be reported */
+/* on stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void *MALLOC_func(size_t sz, const char *mod_name, const char *func,
+ const int line);
+
+/*********************************************************************/
+/* */
+/* Function Name: FREE_func */
+/* */
+/* Descriptive Name: This function frees a block of memory being */
+/* tracked by this module and removes the block */
+/* from its tracking structures. */
+/* */
+/* Input: const void * p - The address of the block of memory to */
+/* be freed. */
+/* const char * mod_name - The name of the module requesting*/
+/* the block of memory be freed. */
+/* const char * func - The name of the function requesting */
+/* the block of memory be freed. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : None. */
+/* */
+/* If Failure : Errors may be reported to stderr. */
+/* */
+/* Error Handling: This function causes the internal structures */
+/* of this module to be checked as part of the */
+/* process of freeing the address p. This may */
+/* cause errors to be reported on stderr. If any */
+/* errors are found, then the address p may not be */
+/* freed. */
+/* */
+/* Side Effects: The block of memory associated with the address p */
+/* will be freed and available for reallocation. */
+/* Also, the memory tracking structures in this */
+/* module will undergo a series of checks. */
+/* */
+/* Notes: This function was not intended to be called directly but */
+/* rather through the macro FREE. */
+/* */
+/*********************************************************************/
+void FREE_func(const void *p, const char *mod_name, const char *func,
+ const int line);
+
+/*********************************************************************/
+/* */
+/* Function Name: Print_Leak_List */
+/* */
+/* Descriptive Name: This function walks the list of allocated */
+/* memory blocks and prints information about */
+/* each one. If this is done at program exit, */
+/* the resulting list of memory blocks most */
+/* likely represents leaked memory. */
+/* */
+/* Input: None. */
+/* */
+/* Output: If Success : If there are any memory blocks being */
+/* tracked by this module, information about */
+/* block still being tracked will be sent to */
+/* stderr. */
+/* */
+/* If Failure : Error messages may be sent to stderr. */
+/* */
+/* Error Handling: If errors are detected, then error messages are */
+/* output on stderr. */
+/* */
+/* Side Effects: The internal structures of this module are checked*/
+/* for errors with any errors being reported on */
+/* stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Print_Leak_List(void);
+
+/*********************************************************************/
+/* */
+/* Function Name: Test_Address_Allocation */
+/* */
+/* Descriptive Name: This function tests the specified address to */
+/* to see if it lies within an allocated block */
+/* tracked by this module. */
+/* */
+/* Input: void * p - The address to be tested. */
+/* */
+/* Output: If Success : If the address p was found, then 0 will be */
+/* returned if the address is the start of */
+/* a block of allocated memory. If the */
+/* address p was found within an allocated */
+/* block of memory, then 1 is returned. */
+/* */
+/* If Failure : If the address p was NOT found, then 2 is */
+/* returned. If there was an error in the */
+/* memory tracking system then 3 will be */
+/* returned. */
+/* */
+/* Error Handling: This function relies on the error handling */
+/* built into the Check_Leak_List function and */
+/* has no error handling of its own. */
+/* */
+/* Side Effects: If the list of memory allocations contains errors */
+/* then those errors will be detected and reported */
+/* on stderr. */
+/* */
+/* Notes: If NULL is passed in as the address to test, then the */
+/* integrity of the internal tracking structures will be */
+/* checked, in which case a return value of 0 signifies */
+/* that the internal tracking structures have passed the */
+/* checks and a return value of 3 indicates that errors */
+/* were found. */
+/* */
+/*********************************************************************/
+unsigned int Test_Address_Allocation(void *p);
+
+/*********************************************************************/
+/* */
+/* Function Name: Duplicate_String */
+/* */
+/* Descriptive Name: This function duplicates a string. The memory*/
+/* allocated for the duplicate is allocated */
+/* using the MALLOC_func routine in this module */
+/* and is thus tracked by this module. */
+/* */
+/* Input: const char * Source - The string to be copied. */
+/* const char * mod_name - The name of the module containing*/
+/* the function which called this */
+/* function. */
+/* const char * func - The name of the function calling */
+/* this function. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : The function return value will be non-NULL */
+/* and will point to a duplicate of the */
+/* string given in Source. */
+/* */
+/* If Failure : The function return value will be NULL. */
+/* */
+/* Error Handling: Any errors detected by this function result in */
+/* a function return value of NULL. */
+/* */
+/* Side Effects: The memory tracking features of this module are */
+/* employed to allocate memory for the duplicate */
+/* string produced by this funciton. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+char *Duplicate_String(const char *Source,
+ const char *mod_name, const char *func, const int line);
+
+/*********************************************************************/
+/* */
+/* Function Name: Realloc_func */
+/* */
+/* Descriptive Name: This function performs the same function as */
+/* the realloc function in the ANSI C library. */
+/* */
+/* Input: const void * p - The address of the block of memory to */
+/* be reallocated. */
+/* size_t size - The size of the memory block to return. */
+/* const char * mod_name - The name of the module requesting*/
+/* the block of memory be freed. */
+/* const char * func - The name of the function requesting */
+/* the block of memory be freed. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : The function return value will be a pointer*/
+/* to the new block of memory. */
+/* */
+/* If Failure : NULL will be returned and errno will be set*/
+/* to a non-null error code. */
+/* */
+/* Error Handling: This function causes the internal structures */
+/* of this module to be checked. This may */
+/* cause errors to be reported on stderr. If any */
+/* errors are found, then the address p may not be */
+/* freed. */
+/* */
+/* Side Effects: A new block of memory of size bytes will be */
+/* allocated, the contents of the current block will */
+/* be copied to the new block (at least as much as */
+/* will fit, and the current block will be freed. */
+/* This will cause internal structures in this module*/
+/* to be modified accordingly. */
+/* */
+/* Notes: This function was not intended to be called directly but */
+/* rather through the macro REALLOC. */
+/* */
+/* If p is NULL, then this will cause this function to */
+/* behave like malloc. */
+/* */
+/* If size is 0, then this will cause this function to */
+/* behave like free. */
+/* */
+/*********************************************************************/
+void *Realloc_func(void *p,
+ size_t size,
+ const char *mod_name, const char *func, const int line);
+
+/* ================================================================= */
+/* ================================================================= */
+/* */
+/* Macros to use for accessing the functions in this module. */
+/* */
+/* ================================================================= */
+/* ================================================================= */
+
+#ifdef MALLOC
+#undef MALLOC
+#endif
+#ifdef FREE
+#undef FREE
+#endif
+
+/* This macro should be used in place of posix_memalign. */
+#define POSIX_MEMALIGN(a, b, c ) MEMORY_func( c, b, a, __FILE__, __func__, __LINE__ )
+
+/* This macro should be used in place of the malloc function. */
+#define MALLOC( a ) MALLOC_func( a, __FILE__, __func__, __LINE__ )
+
+/* This macro should be used in place of the free function. */
+#define FREE( a ) FREE_func( a, __FILE__, __func__, __LINE__ )
+
+/* This macro should be used to print a list of allocated memory blocks. */
+#define PRINT_ALLOCATION_LIST Print_Leak_List()
+
+/* This macro should be used to see if an address lies within a block of
+ memory being tracked by this module. */
+#define ADDRESS_CHECK( p ) Test_Address_Allocation( p )
+
+/* This macro should be used in place of the strdup function. */
+#define STRDUP( p ) Duplicate_String( p, __FILE__, __func__, __LINE__ )
+
+/* This macro should be used in place of the realloc function. */
+#define REALLOC( p, s ) Realloc_func( p, s, __FILE__, __func__, __LINE__ )
+
+#else
+
+#define POSIX_MEMALIGN(a, b, c ) posix_memalign( a, b, c )
+#define MALLOC(s) malloc(s)
+#define FREE(p) free(p)
+#define PRINT_ALLOCATION_LIST
+#define ADDRESS_CHECK( p ) 0
+#define STRDUP( p ) strdup( p )
+#define REALLOC( p, s ) realloc( p, s )
+
+#endif
+
+#endif
diff --git a/clib/min.h b/clib/min.h
new file mode 100644
index 0000000..69633bc
--- /dev/null
+++ b/clib/min.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: min.h
+ * Author:
+ * Descr: typesafe min function
+ * Note:
+ * Date: 10/22/10
+ */
+
+#ifndef __MIN_H__
+#define __MIN_H__
+
+#define min(x,y) \
+({ \
+ typeof(x) _x = (x); \
+ typeof(y) _y = (y); \
+ (void)((void*)&_x == (void*)&_y); \
+ _x < _y ? _x : _y; \
+})
+
+#endif /* __MIN_H__ */
diff --git a/clib/misc.h b/clib/misc.h
new file mode 100644
index 0000000..ad8f261
--- /dev/null
+++ b/clib/misc.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: misc.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com?
+ * Descr: Misc. library helper functions
+ * Note:
+ * Date: 10/22/10
+ */
+
+#ifndef __MISC_H__
+#define __MISC_H__
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <sys/syscall.h>
+
+#include "attribute.h"
+
+#include "align.h"
+#include "min.h"
+#include "max.h"
+
+#define INT32_BIT (CHAR_BIT*sizeof(int32_t))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#ifndef gettid
+#define gettid() ({ \
+ pid_t __tid = (pid_t)syscall(SYS_gettid); \
+ __tid; \
+ })
+#endif
+
+#define is_pow2(x) \
+({ \
+ typeof(x) _x = (x); \
+ (((_x) != 0) && !((_x) & ((_x) - 1))); \
+})
+
+#define __align(x,y) \
+({ \
+ assert(is_pow2((y))); \
+ typeof((y)) _y = (y) - 1; \
+ typeof((x)) _x = ((x)+_y) & ~_y; \
+ _x; \
+})
+
+extern void prefetch(void *addr, size_t len, ...) __nonnull((1));
+extern void print_binary(FILE *, void *, size_t);
+extern void dump_memory(FILE *, uint32_t, const void *__restrict,
+ size_t) __nonnull((1, 3));
+extern unsigned long align(unsigned long size, unsigned long offset);
+extern int __round_pow2(int size);
+
+#endif /* __MISC_H__ */
diff --git a/clib/mq.h b/clib/mq.h
new file mode 100644
index 0000000..95cca4d
--- /dev/null
+++ b/clib/mq.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file mqueue.h
+ * @brief Message queues
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __MQUEUE_H__
+#define __MQUEUE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <mqueue.h>
+
+#include "attribute.h"
+
+/* ==================================================================== */
+
+typedef struct mqueue mqueue_t; //!< Alias for the @em mqueue class
+typedef struct mq_attr mqueue_attr_t;
+
+#define INIT_MQUEUE {NULL,-1,-1}
+
+/*!
+ * @brief POSIX message queues
+ */
+struct mqueue {
+ char *service; //!< Message queue name
+
+ mqd_t in; //!< Inbound message queue
+ mqd_t out; //!< Outbound message queue
+};
+
+/* ==================================================================== */
+
+/*!
+ * @brief Constructs a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param service [in] mqueue service name
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int mqueue_init(mqueue_t *, const char *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Destructs a @em mqueue object
+ * @details Closes all message queues associated with this \em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @return None
+ */
+extern int mqueue_delete(mqueue_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Create the server-side of a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param tid [in] thread id
+ * @return None
+ */
+extern int mqueue_create(mqueue_t *, pid_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Create the client-side of a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param path [in] FIX ME
+ * @return None
+ */
+extern int mqueue_open(mqueue_t *, char *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Close a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param path [in] FIX ME
+ * @return None
+ */
+extern int mqueue_close(mqueue_t *, char *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Get message queue operating attributes of a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @return Initialized mqueue_attr object
+ */
+extern mqueue_attr_t mqueue_getattr(mqueue_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Write (send) a message to a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param ptr [in] Reference to data bytes to send
+ * @param ptr [in] Length (in bytes) of the data bytes
+ * @return non-0 on success, 0 othereise
+ */
+extern int mqueue_send(mqueue_t *, void *, size_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Read (receive) a message from a @em mqueue object
+ * @memberof mqueue
+ * @param self [in] mqueue object @em self pointer
+ * @param ptr [out] Reference to data bytes to send
+ * @param ptr [in] Length (in bytes) of the buffer referenced by @em ptr
+ * @return non-0 on success, 0 othereise
+ */
+extern int mqueue_receive(mqueue_t *, void *, size_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+#endif /* __MQUEUE_H__ */
diff --git a/clib/nargs.h b/clib/nargs.h
new file mode 100644
index 0000000..3a8666d
--- /dev/null
+++ b/clib/nargs.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file nargs.h
+ * @brief Macro utilities
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __NARGS_H__
+#define __NARGS_H__
+
+/*!
+ * @def STRCAT(x,y)
+ * @hideinitializer
+ * @brief C string concatination of @em x and @em y
+ * @param x [in] C-style string
+ * @param y [in] C-style string
+ */
+/*! @cond */
+#define STRCAT(x,y) __C__(x, y)
+#define __C__(x,y) x ## y
+/*! @endcond */
+
+/*!
+ * @def NARGS(...)
+ * @hideinitializer
+ * @brief Return the number of pre-process macro parameters
+ */
+/*! @cond */
+#define __NARGS__(junk, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _, ...) _
+#define NARGS(...) __NARGS__(junk, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+/*! @endcond */
+
+#endif /* __NARGS_H__ */
diff --git a/clib/queue.h b/clib/queue.h
new file mode 100644
index 0000000..8e3a5c6
--- /dev/null
+++ b/clib/queue.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: queue.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Queue container
+ * Note: This is an adaptor of the list container
+ * Date: 10/07/10
+ */
+
+#ifndef __QUEUE_H__
+#define __QUEUE_H__
+
+#include "list.h"
+#include "type.h"
+
+typedef list queue;
+typedef list_node queue_node;
+
+#define queue_init(q) list_init((list *)(q))
+#define queue_push(q,n) list_add_tail((list *)(q),(n))
+#define queue_pop(q) list_remove_head((list *)(q))
+#define stack_empty(q) list_empty((list *)(q))
+#define queue_dump(q,s) list_dump((list *)(q),(s))
+
+#define queue_entry(n, t, m) list_entry((n),(t),(m))
+#define queue_top(q) list_head((list *)(q))
+#define queue_bottom(q) list_tail((list *)(q))
+
+#define queue_for_each(q, i, m) \
+ for (i = container_of_var(q->node.next, i, m); \
+ &i->m != &(q)->node; \
+ i = container_of_var(i->m.next, i, m))
+
+#define queue_for_each_safe(q, i, n, m) \
+ for (i = container_of_var((q)->node.next, i, m), \
+ n = container_of_var(i->m.next, i, m); \
+ &i->m != &(q)->node; \
+ i = n, n = container_of_var(i->m.next, i, m))
+
+#endif /* __QUEUE_H__ */
diff --git a/clib/raii.h b/clib/raii.h
new file mode 100644
index 0000000..8fdb1ce
--- /dev/null
+++ b/clib/raii.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: raii.h
+ * Author:
+ * Descr: RAII macro
+ * Note:
+ * Date: 04/03/13
+ */
+
+#ifndef __RAII_H__
+#define __RAII_H__
+
+#include "attribute.h"
+
+#define CLEANUP(type,name,func) \
+ void __cleanup_ ## name (type * __v) { \
+ if (__v != NULL) \
+ func(__v); \
+ } \
+ type name __cleanup(__cleanup_##name)
+
+#define RAII(type,name,ctor,dtor) \
+ void __cleanup_##name (type * __v) { \
+ if (__v != NULL && *__v != NULL) \
+ dtor(*__v); \
+ } \
+ type name __cleanup(__cleanup_##name) = (ctor)
+
+#endif /* __RAII_H__ */
diff --git a/clib/signal.h b/clib/signal.h
new file mode 100644
index 0000000..7c7be18
--- /dev/null
+++ b/clib/signal.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: signal.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: File descriptor activity callback signaler
+ * Note:
+ * Date: 10/03/10
+ */
+
+#ifndef __SIGNAL_H__
+#define __SIGNAL_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+
+#include "attribute.h"
+#include "builtin.h"
+#include "assert.h"
+
+#include "tree.h"
+#include "array.h"
+
+/* ======================================================================= */
+
+typedef struct signal signal_t;
+typedef struct signalfd_siginfo signal_event_t;
+
+struct signal {
+ int fd;
+
+ sigset_t mask;
+ int flags;
+};
+
+/* ======================================================================= */
+
+extern void signal_init(signal_t * self) __nonnull((1));
+extern void signal_delete(signal_t * self) __nonnull((1));
+
+extern int signal_fileno(signal_t * self) __nonnull((1));
+
+extern int signal_setmask(signal_t * self, const sigset_t mask) __nonnull((1));
+
+#if 0
+#define signalfd_wait(...) STRCAT(signalfd_wait, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern void signal_wait1(signal_t *) __nonnull((1));
+extern void signal_wait2(_sisignal_tgnal *, int) __nonnull((1));
+#endif
+
+/* ======================================================================= */
+
+#endif /* __SIGNAL_H__ */
diff --git a/clib/slab.h b/clib/slab.h
new file mode 100644
index 0000000..7b52bfd
--- /dev/null
+++ b/clib/slab.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file slab.h
+ * @brief Slab allocator
+ * @details A slab allocator is a bit-mapped allocator for which all allocations are the same size
+ * @details For example,
+ * @code
+ * #include <clib/slab.h>
+ *
+ * #define SIZE 20000
+ *
+ * int main(const int argc, const char * argv[]) {
+ * slab_t s = INIT_SLAB;
+ * slab_init(&s, "my_slab", 16, 4096);
+ *
+ * void * ptr[SIZE] = {NULL,};
+ *
+ * int i;
+ * for (i=0; i<SIZE; i++) {
+ * ptr[i] = slab_alloc(&s);
+ * }
+ *
+ * for (i=0; i<SIZE; i++) {
+ * slab_free(&s, ptr[i]);
+ * }
+ *
+ * slab_delete(&s);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __SLAB_H__
+#define __SLAB_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "ident.h"
+#include "nargs.h"
+#include "tree.h"
+
+/* ======================================================================= */
+
+#define SLAB_MAGIC "SLAB"
+
+#define SLAB_NAME_SIZE 40 //!< Maximum vector name size (in bytes)
+
+#define SLAB_FLAG_LSB 0x01 //!< Little-endian header data
+#define SLAB_FLAG_MSB 0x02 //!< Big-endian header data
+
+#define SLAB_ALLOC_MIN 4
+#define SLAB_ALLOC_MAX 8192
+
+#define INIT_SLAB_HEADER {INIT_IDENT,{0,},0,0,0,0,0,0}
+#define INIT_SLAB {INIT_SLAB_HEADER,INIT_TREE}
+
+/*!
+ * @brief Slab allocator header
+ */
+struct slab_header {
+ ident_t id; //!< identification
+ char name[SLAB_NAME_SIZE]; //!< slab name
+
+ size_t page_size; //!< page size
+ size_t align_size; //!< page alignment size
+ size_t page_count; //!< page count
+
+ size_t alloc_size; //!< allocation size
+ size_t data_size; //!< data size
+ size_t bitmap_size; //!< bitmap size
+};
+typedef struct slab_header slab_header_t;
+
+/*!
+ * @brief Slab allocator
+ */
+struct slab { //!< The slab class
+ slab_header_t hdr;
+
+ tree_t tree; //!< @private
+};
+typedef struct slab slab_t; //!< Alias for the @em slab class
+
+/* ======================================================================= */
+
+/*!
+ * @brief Constructs a @em slab allocator
+ * @memberof slab
+ * @param self [in] self object
+ * @name name [in] name strnig ('\0' terminated)
+ * @param alloc_size [in] allocation size (in bytes)
+ * @param page_size [in] page size (in bytes)
+ * @return None
+ */
+/*! @cond */
+#define slab_init(...) STRCAT(slab_init, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int slab_init3(slab_t * self, const char *name, size_t alloc_size)
+__nonnull((1, 2));
+extern int slab_init4(slab_t * self, const char *name, size_t alloc_size,
+ size_t page_size) __nonnull((1, 2));
+extern int slab_init5(slab_t * self, const char *name, size_t alloc_size,
+ size_t page_size, size_t align_size) __nonnull((1, 2));
+/*! @endcond */
+
+/*!
+ * @brief Deallocate the memory associated with a @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @note Upon success, the cache tree is deallocated from the heap
+ * @return None
+ */
+extern int slab_delete(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Allocate first available slot in the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ */
+extern void *slab_alloc(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Deallocate a slot in the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @param ptr [in] pointer to a slab allocation
+ * @note Upon success, a slab slot is deallocated from the slab object
+ * @return None
+ */
+extern int slab_free(slab_t * self, void *ptr)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the allocation (i.e. slot) size of the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-0 on success, 0 otherwise
+ */
+extern size_t slab_alloc_size(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the page (i.e. slot) size of the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-0 on success, 0 otherwise
+ */
+extern size_t slab_page_size(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the data size of the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-0 on success, 0 otherwise
+ */
+extern size_t slab_data_size(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the map (i.e. slot) size of the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-0 on success, 0 otherwise
+ */
+extern size_t slab_bitmap_size(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the alignment size of the @em slab allocator
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return non-0 on success, 0 otherwise
+ */
+extern size_t slab_align_size(slab_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print the contents of a @em slab to stdout
+ * @memberof slab
+ * @param self [in] slab object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void slab_dump(slab_t * self, FILE * out)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#endif /* __SLAB_H__ */
diff --git a/clib/src/array.c b/clib/src/array.c
new file mode 100644
index 0000000..55e261c
--- /dev/null
+++ b/clib/src/array.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: array.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Sparse Array
+ * Note:
+ * Date: 08/29/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "assert.h"
+#include "misc.h"
+#include "hash.h"
+#include "array.h"
+#include "slab.h"
+#include "mqueue.h"
+#include "tree.h"
+#include "tree_iter.h"
+#include "bb_trace.h"
+#include "memory_leak_detection.h"
+
+/*! @cond */
+#define ARRAY_ELEM_SIZE_MIN 1
+#define ARRAY_ELEM_NUM_MIN 64
+
+#define ARRAY_MAGIC 0x48444152
+#define ARRAY_NODE_MAGIC 0x4E444152
+/*! @endcond */
+
+/* ======================================================================= */
+
+/*! @cond */
+typedef struct array_node array_node_t;
+
+struct array_node {
+ uint32_t magic;
+ uint32_t address;
+
+ tree_node_t node;
+};
+
+const char *__array_msg[] = {
+ "array: unexpected NULL pointer",
+ "array: unexpected cache and/or alloc size",
+ "array: out-of-memory",
+ "array: uninitialized array element",
+ "array: unexpected magic number",
+ "array: unexpected NULL message channel",
+};
+
+#define ARRAY_NULL (__array_msg[0])
+#define ARRAY_SIZE (__array_msg[1])
+#define ARRAY_OOM (__array_msg[2])
+#define ARRAY_UNINIT_ELEM (__array_msg[3])
+#define ARRAY_MAGIC_CHECK (__array_msg[4])
+#define ARRAY_MQ_NULL (__array_msg[5])
+/*! @endcond */
+
+/* ======================================================================= */
+
+#define __index_to_page(i,s) \
+({ \
+ typeof(i) _p = ((i) / (s)); \
+ (i) >= 0 ? _p :_p - 1; \
+})
+
+#define __index_to_page_hashed(i,s) \
+({ \
+ typeof(i) _h = int32_hash1(__index_to_page((i),(s))); \
+ _h; \
+})
+
+#define __page_to_low(p,s) \
+({ \
+ typeof(p) _l = (p) * (s); \
+ (p) >= 0 ? _l : _l - 1; \
+})
+
+#define __page_to_high(p,s) \
+({ \
+ typeof(p) _h = (p) * (s) + (s) - (typeof(p))1; \
+ _h; \
+})
+
+/* ======================================================================= */
+
+static array_node_t *__array_grow(array_t * self, size_t idx)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ array_node_t *node = NULL;
+ switch (POSIX_MEMALIGN
+ ((void **)&node, self->page_size, self->page_size)) {
+ case EINVAL:
+ throw_unexpected(ARRAY_SIZE);
+ case ENOMEM:
+ throw_unexpected(ARRAY_OOM);
+ }
+ assert(node != NULL);
+
+ node->magic = ARRAY_NODE_MAGIC;
+ node->address = (uint32_t) node;
+
+ const void *key =
+ (const void *)__index_to_page_hashed(idx, self->elem_num);
+ tree_node_init(&node->node, key);
+
+ splay_insert(&self->tree, &node->node);
+ self->pages++;
+
+ size_t page = __index_to_page(idx, self->elem_num);
+
+ self->low = min(self->low, __page_to_low(page, self->elem_num));
+ self->high = max(self->high, __page_to_high(page, self->elem_num));
+
+ return node;
+}
+
+static int __array_compare(const int i1, const int i2)
+{
+ return i1 - i2;
+}
+
+void array_init(array_t * self, size_t elem_size, size_t elem_num)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ self->magic = ARRAY_MAGIC;
+ self->high = 0;
+ self->low = UINT32_MAX;
+ self->size = 0;
+ self->pages = 0;
+
+ self->elem_size = max(elem_size, ARRAY_ELEM_SIZE_MIN);
+ self->elem_num = max(elem_num, ARRAY_ELEM_NUM_MIN);
+
+ self->page_size = __round_pow2(elem_size * elem_num);
+
+ self->map_size = align(elem_num / CHAR_BIT, sizeof(uint32_t));
+
+ self->elem_num =
+ (self->page_size - sizeof(array_node_t) -
+ self->map_size) / self->elem_size;
+
+ tree_init(&self->tree, (compare_f) __array_compare);
+}
+
+static void __array_delete(array_t * self)
+{
+ if (self != NULL) {
+ while (self->tree.root != NULL) {
+ array_node_t *node;
+ node =
+ container_of(tree_root(&self->tree), array_node_t,
+ node);
+ splay_remove(&self->tree, &node->node);
+ FREE(node);
+ }
+ tree_init(&self->tree, (compare_f) __array_compare);
+ }
+}
+
+void array_delete(array_t * self)
+{
+ __array_delete(self);
+}
+
+static void *__array_find_page(array_t * self, size_t idx)
+{
+ const void *hash =
+ (const void *)__index_to_page_hashed(idx, self->elem_num);
+ tree_node_t *node = tree_find(&self->tree, hash);
+
+ if (node == NULL)
+ return (void *)__array_grow(self, idx);
+ else
+ return (void *)container_of(node, array_node_t, node);
+
+ return NULL;
+}
+
+static uint8_t *__array_find_map(array_t * self, size_t idx)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ const void *hash =
+ (const void *)__index_to_page_hashed(idx, self->elem_num);
+ uint8_t *map = (uint8_t *) tree_find(&self->tree, hash);
+ if (map != NULL)
+ map += sizeof(tree_node_t);
+
+ return map;
+}
+
+const void *array_at(array_t * self, size_t idx)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ uint32_t offset = idx % self->elem_num;
+
+ uint8_t *map = __array_find_page(self, idx);
+ map += sizeof(array_node_t);
+
+ uint8_t byte = offset / CHAR_BIT;
+ uint8_t mask = 0x80 >> (offset % CHAR_BIT);
+
+ if ((map[byte] & mask) == 0)
+ throw_unexpected(ARRAY_UNINIT_ELEM);
+
+ return map + self->map_size + (self->elem_size * offset);
+}
+
+void array_get3(array_t * self, size_t elem_off, void *ptr)
+{
+ return array_get4(self, elem_off, ptr, 1);
+}
+
+void array_get4(array_t * self, size_t elem_off, void *ptr, size_t elem_num)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ while (0 < elem_num) {
+ memcpy(ptr, array_at(self, elem_off), self->elem_size);
+
+ elem_off++;
+ elem_num--;
+
+ ptr += self->elem_size;
+ }
+}
+
+void __array_put(array_t * self, size_t elem_off, const void *ptr)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ uint32_t offset = elem_off % self->elem_num;
+
+ uint8_t *map = __array_find_page(self, elem_off);
+ map += sizeof(array_node_t);
+
+ uint8_t byte = offset / CHAR_BIT;
+ uint8_t mask = 0x80 >> (offset % CHAR_BIT);
+
+ if ((map[byte] & mask) == 0)
+ self->size++;
+
+ map[byte] |= mask;
+
+ memcpy(map + self->map_size + (self->elem_size * offset),
+ ptr, self->elem_size);
+}
+
+void array_put3(array_t * self, size_t elem_off, const void *ptr)
+{
+ return array_put4(self, elem_off, ptr, 1);
+}
+
+void array_put4(array_t * self, size_t elem_off, const void *ptr,
+ size_t elem_num)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+
+ while (0 < elem_num) {
+ __array_put(self, elem_off, ptr);
+
+ elem_off++;
+ elem_num--;
+
+ ptr += self->elem_size;
+ }
+}
+
+bool array_status2(array_t * self, size_t idx)
+{
+ uint8_t *map = __array_find_map(self, idx);
+
+ uint32_t offset = idx % self->elem_num;
+ uint8_t byte = offset / CHAR_BIT;
+ uint8_t mask = 0x80 >> (offset % CHAR_BIT);
+
+ return ! !(map[byte] & mask);
+}
+
+bool array_status3(array_t * self, size_t idx, bool status)
+{
+ uint8_t *map = __array_find_map(self, idx);
+
+ if (map == NULL) {
+ map = (uint8_t *) __array_grow(self, idx);
+ map += sizeof(array_node_t);
+ }
+
+ uint32_t offset = idx % self->elem_num;
+ uint8_t byte = offset / CHAR_BIT;
+ uint8_t mask = 0x80 >> (offset % CHAR_BIT);
+
+ bool ret = ! !(map[byte] & mask);
+
+ map[byte] &= ~mask;
+ if (status)
+ map[byte] |= mask;
+
+ return ret;
+}
+
+void array_resize(array_t * self, size_t size)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ while (0 < size) {
+ (void)__array_grow(self, self->high + 1);
+ size /= self->elem_num;
+ }
+}
+
+size_t array_size(array_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ return self->size;
+}
+
+size_t array_pages(array_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ return self->pages;
+}
+
+size_t array_capacity(array_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ return self->high - self->low + 1;
+}
+
+size_t array_low(array_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ return self->low;
+}
+
+size_t array_high(array_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ return self->high;
+}
+
+void array_send(array_t * self, mqueue_t * mq)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ if (unlikely(mq == NULL))
+ throw_unexpected(ARRAY_MQ_NULL);
+
+ mqueue_send(mq, (char *)self, sizeof(*self));
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ array_node_t *node;
+ tree_for_each(&it, node, node) {
+ if (node->magic != ARRAY_NODE_MAGIC)
+ throw_unexpected(ARRAY_MAGIC_CHECK);
+
+ mqueue_send(mq, (char *)node, self->page_size);
+ }
+}
+
+void array_receive(array_t * self, mqueue_t * mq)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_NULL);
+ if (unlikely(mq == NULL))
+ throw_unexpected(ARRAY_MQ_NULL);
+
+ __array_delete(self);
+
+ mqueue_attr_t attr = mqueue_getattr(mq);
+
+ array_node_t *node = NULL;
+ switch (POSIX_MEMALIGN
+ ((void **)&node, attr.mq_msgsize, attr.mq_msgsize)) {
+ case EINVAL:
+ throw_unexpected(ARRAY_SIZE);
+ case ENOMEM:
+ throw_unexpected(ARRAY_OOM);
+ }
+ assert(node != NULL);
+
+ ssize_t len = mqueue_receive(mq, (void *)node, attr.mq_msgsize);
+ assert(0 < len);
+
+ memcpy(self, (void *)node, sizeof(*self));
+ tree_init(&self->tree, (compare_f) __array_compare);
+
+ for (int i = 0; i < array_pages(self); i++) {
+ if (node == NULL) {
+ switch (POSIX_MEMALIGN
+ ((void **)&node, attr.mq_msgsize,
+ attr.mq_msgsize)) {
+ case EINVAL:
+ throw_unexpected(ARRAY_SIZE);
+ case ENOMEM:
+ throw_unexpected(ARRAY_OOM);
+ }
+ }
+
+ len = mqueue_receive(mq, (void *)node, attr.mq_msgsize);
+ assert(0 < len);
+
+ node->address = (uint32_t) node;
+ tree_node_init(&node->node, node->node.key);
+ splay_insert(&self->tree, &node->node);
+
+ node = NULL;
+ }
+}
+
+void array_dump(array_t * self, FILE * out)
+{
+ if (self != NULL) {
+ fprintf(out,
+ "array: [ page_size: %5u pages: %5u map_size: %5u capacity: %10u ]\n",
+ self->page_size, self->pages, self->map_size,
+ array_capacity(self));
+ fprintf(out,
+ " [ elem_size: %5u elem_num: %5u size: %10u range: (%u....%u) ]\n",
+ self->elem_size, self->elem_num, array_size(self),
+ array_low(self), array_high(self));
+
+ dump_memory(out, (unsigned long)self, self, sizeof(*self));
+
+ tree_iter_t it;
+ for (tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+ tree_iter_elem(&it); tree_iter_inc(&it)) {
+ array_node_t *node;
+ node =
+ container_of(tree_iter_elem(&it), array_node_t,
+ node);
+
+ fprintf(out, "magic[%x] address[%x]\n",
+ node->magic, node->address);
+ fprintf(out,
+ "n[%p] left[%p] right[%p] parent[%p] key[%p] \n",
+ &node->node, node->node.left, node->node.right,
+ node->node.parent, node->node.key);
+
+ dump_memory(out, (unsigned long)node, node,
+ self->page_size);
+ }
+ }
+}
+
+/* ======================================================================= */
diff --git a/clib/src/array_iter.c b/clib/src/array_iter.c
new file mode 100644
index 0000000..921bf97
--- /dev/null
+++ b/clib/src/array_iter.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: array_iter.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: dynamic array
+ * Note:
+ * Date: 10/22/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "assert.h"
+#include "misc.h"
+#include "hash.h"
+#include "nargs.h"
+#include "array_iter.h"
+
+/* ======================================================================= */
+
+const char *__array_iter_msg[] = {
+ "array_iter: unexpected NULL pointer",
+ "array_iter: index out-of-range",
+};
+
+#define ARRAY_ITER_NULL (__array_iter_msg[0])
+#define ARRAY_ITER_RANGE (__array_iter_msg[1])
+
+/* ======================================================================= */
+
+static inline const void *__array_iter_bwd(array_iter_t * self)
+{
+ size_t low = array_low(self->array);
+ const void *ret = NULL;
+
+ if (low < self->idx)
+ self->idx--;
+
+ while (low < self->idx) {
+ if (array_status(self->array, self->idx))
+ break;
+ self->idx--;
+ }
+
+ if (low < self->idx)
+ ret = array_at(self->array, self->idx);
+
+ return ret;
+}
+
+static inline const void *__array_iter_fwd(array_iter_t * self)
+{
+ size_t high = array_high(self->array);
+ const void *ret = NULL;
+
+ if (self->idx < high)
+ self->idx++;
+
+ while (self->idx < high) {
+ if (array_status(self->array, self->idx))
+ break;
+ self->idx++;
+ }
+
+ if (self->idx < high)
+ ret = array_at(self->array, self->idx);
+
+ return ret;
+}
+
+void array_iter_init(array_iter_t * self, array_t * array, uint32_t flags)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+ if (unlikely(array == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+
+ self->flags = flags;
+ self->array = array;
+
+ if (self->flags & AI_FLAG_BWD) {
+ self->idx = array_high(self->array);
+ __array_iter_bwd(self);
+ } else {
+ self->idx = array_low(self->array);
+ __array_iter_fwd(self);
+ }
+}
+
+void array_iter_clear(array_iter_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+
+ if (self->flags & AI_FLAG_BWD)
+ self->idx = array_high(self->array);
+ else
+ self->idx = array_low(self->array);
+
+ self->array = NULL;
+}
+
+const void *array_iter_elem(array_iter_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+ if (self->idx < array_low(self->array))
+ throw_unexpected(ARRAY_ITER_RANGE);
+ if (array_high(self->array) <= self->idx)
+ throw_unexpected(ARRAY_ITER_RANGE);
+
+ return array_status(self->array, self->idx) ?
+ array_at(self->array, self->idx) : NULL;
+}
+
+const void *array_iter_inc1(array_iter_t * self)
+{
+ return array_iter_inc2(self, 1);
+}
+
+const void *array_iter_inc2(array_iter_t * self, size_t inc)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+
+ const void *ret = NULL;
+
+ if (self->flags & AI_FLAG_BWD)
+ ret = __array_iter_bwd(self);
+ else
+ ret = __array_iter_fwd(self);
+
+ return ret;
+}
+
+const void *array_iter_dec1(array_iter_t * self)
+{
+ return array_iter_dec2(self, 1);
+}
+
+const void *array_iter_dec2(array_iter_t * self, size_t dec)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(ARRAY_ITER_NULL);
+
+ const void *ret = NULL;
+
+ if (self->flags & AI_FLAG_BWD)
+ ret = __array_iter_fwd(self);
+ else
+ ret = __array_iter_bwd(self);
+
+ return ret;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/checksum.c b/clib/src/checksum.c
new file mode 100644
index 0000000..fea96b4
--- /dev/null
+++ b/clib/src/checksum.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "assert.h"
+#include "checksum.h"
+
+uint32_t memcpy_checksum(void *__restrict __dst, const void *__restrict __src,
+ size_t __n)
+{
+ uint8_t sum[4] = { 0, };
+
+ /* assert(((uintptr_t)__src & 3) == 0); */
+
+ size_t i;
+
+ if (__dst == NULL)
+ for (i = 0; i < __n; i++)
+ sum[i & 3] ^= ((uint8_t *) __src)[i];
+ else
+ for (i = 0; i < __n; i++)
+ sum[i & 3] ^= ((uint8_t *) __src)[i],
+ ((uint8_t *) __dst)[i] = ((uint8_t *) __src)[i];
+
+ return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
+}
diff --git a/clib/src/crc32.c b/clib/src/crc32.c
new file mode 100644
index 0000000..d0cf657
--- /dev/null
+++ b/clib/src/crc32.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/* Crc - 32 BIT ANSI X3.66 CRC checksum files */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "crc32.h"
+
+/**********************************************************************\
+|* Demonstration program to compute the 32-bit CRC used as the frame *|
+|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *|
+|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *|
+|* protocol). The 32-bit FCS was added via the Federal Register, *|
+|* 1 June 1982, p.23798. I presume but don't know for certain that *|
+|* this polynomial is or will be included in CCITT V.41, which *|
+|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *|
+|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *|
+|* errors by a factor of 10^-5 over 16-bit FCS. *|
+\**********************************************************************/
+
+/* Copyright (C) 1986 Gary S. Brown. You may use this program, or
+ code or tables extracted from it, as desired without restriction.*/
+
+/* First, the polynomial itself and its table of feedback terms. The */
+/* polynomial is */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in */
+/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+/* the MSB being 1. */
+
+/* Note that the usual hardware shift register implementation, which */
+/* is what we're using (we're merely optimizing it by doing eight-bit */
+/* chunks at a time) shifts bits into the lowest-order term. In our */
+/* implementation, that means shifting towards the right. Why do we */
+/* do it this way? Because the calculated CRC must be transmitted in */
+/* order from highest-order term to lowest-order term. UARTs transmit */
+/* characters in order from LSB to MSB. By storing the CRC this way, */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part. Reception works similarly. */
+
+/* The feedback terms table consists of 256, 32-bit entries. Notes: */
+/* */
+/* 1. The table can be generated at runtime if desired; code to do so */
+/* is shown later. It might not be obvious, but the feedback */
+/* terms simply represent the results of eight shift/xor opera- */
+/* tions for all combinations of data and CRC register values. */
+/* */
+/* 2. The CRC accumulation logic is the same for all CRC polynomials, */
+/* be they sixteen or thirty-two bits wide. You simply choose the */
+/* appropriate table. Alternatively, because the table can be */
+/* generated at runtime, you can start by generating the table for */
+/* the polynomial in question and use exactly the same "updcrc", */
+/* if your application needn't simultaneously handle two CRC */
+/* polynomials. (Note, however, that XMODEM is strange.) */
+/* */
+/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */
+/* of course, 32-bit entries work OK if the high 16 bits are zero. */
+/* */
+/* 4. The values must be right-shifted by eight bits by the "updcrc" */
+/* logic; the shift must be unsigned (bring in zeroes). On some */
+/* hardware you could probably optimize the shift in assembler by */
+/* using byte-swap instructions. */
+
+static uint32_t __crc32_table[] = { /* CRC polynomial 0xedb88320 */
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+inline uint32_t clib_crc32(unsigned char octet, uint32_t crc)
+{
+ return __crc32_table[(crc ^ (uint8_t) octet) & 0xff] ^ (crc >> 8);
+}
+
+uint32_t memcpy32(void *__restrict __dst, const void *__restrict __src,
+ size_t __n)
+{
+ register uint32_t __crc = 0xFFFFFFFF;
+
+ if (__dst == NULL)
+ for (; __n; --__n, ++__src) {
+ __crc = clib_crc32(*(uint8_t *) __src, __crc);
+ } else
+ for (; __n; --__n, ++__src, ++__dst) {
+ __crc = clib_crc32(*(uint8_t *) __src, __crc);
+ *(uint8_t *) __dst = *(uint8_t *) __src;
+ }
+
+ return ~__crc;
+}
diff --git a/clib/src/crc32_main.c b/clib/src/crc32_main.c
new file mode 100644
index 0000000..3bdf072
--- /dev/null
+++ b/clib/src/crc32_main.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "crc32.h"
+
+void usage(void)
+{
+ printf("Usage: crc32 infile\n");
+}
+
+int main(int argc, const char *argv[])
+{
+ if (argc != 2) {
+ usage();
+ return 1;
+ }
+
+ int fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ printf("open %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ struct stat stat;
+ int rc = fstat(fd, &stat);
+ if (rc < 0) {
+ printf("stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ off_t filesize = stat.st_size;
+
+ void *in_buf = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (in_buf == MAP_FAILED) {
+ /* FIXME could still try to use read().... */
+ printf("mmap %d failed: %s\n", filesize, strerror(errno));
+ return 1;
+ }
+
+ rc = close(fd);
+ if (rc < 0) {
+ printf("close %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ unsigned long crc32 = memcpy32(NULL, in_buf, filesize);
+
+ printf("%#010lx", crc32), fflush(stdout);
+
+ return 0;
+}
diff --git a/clib/src/db.c b/clib/src/db.c
new file mode 100644
index 0000000..e61726c
--- /dev/null
+++ b/clib/src/db.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: db.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: SQlite wrapper
+ * Note:
+ * Date: 02/25/11
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sqlite3.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "assert.h"
+#include "err.h"
+
+#include "db.h"
+
+/* =======================================================================*/
+
+int db_init(db_t * self, const char *path)
+{
+ assert(self != NULL);
+
+ self->path = strdup(path);
+ if (self->path == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ self->db = NULL;
+
+ return 0;
+}
+
+int db_delete(db_t * self)
+{
+ if (self != NULL) {
+ db_close(self);
+ if (self->path != NULL)
+ free((char *)self->path);
+ memset(self, 0, sizeof(*self));
+ }
+
+ return 0;
+}
+
+int db_execute(db_t * self, const char *fmt, ...)
+{
+ assert(self != NULL);
+
+ va_list ap;
+ va_start(ap, fmt);
+ char *sql = NULL;
+ vasprintf(&sql, fmt, ap);
+
+ assert(sql != NULL);
+
+ int rc = sqlite3_exec(self->db, sql, NULL, NULL, NULL);
+ if (sql != NULL)
+ free(sql);
+
+ if (rc != DB_OK) {
+ DBERR(self->db);
+ return -1;
+ }
+
+ return 0;
+}
+
+int db_open(db_t * self, int flags)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->db != NULL)
+ db_close(self);
+
+ rc = sqlite3_open_v2(self->path, &self->db, flags, NULL);
+ if (rc != DB_OK) {
+ DBERR(self->db);
+ return -1;
+ }
+
+ return rc;
+}
+
+int db_close(db_t * self)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->db != NULL) {
+ /* finalize statements */
+ rc = sqlite3_close(self->db);
+ }
+
+ return rc;
+}
+
+int db_register_function(db_t * self, const char *name, int argc, db_f func,
+ void *data)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ rc = sqlite3_create_function(self->db, name, argc, SQLITE_ANY, data,
+ func, NULL, NULL);
+ if (rc != 0) {
+ DBERR(self->db);
+ return -1;
+ }
+
+ return rc;
+}
+
+int db_begin(db_t * self, transaction_type_t type)
+{
+ assert(self != NULL);
+
+ const char *tran_type = NULL;
+
+ switch (type) {
+ case tt_DEFERRED:
+ tran_type = "DEFERRED";
+ break;
+ case tt_IMMEDIATE:
+ tran_type = "IMMEDIATE";
+ break;
+ case tt_EXCLUSIVE:
+ tran_type = "EXCLUSIVE";
+ break;
+ default:
+ UNEXPECTED("'%d' invalid transaction type", type);
+ return -1;
+ }
+
+ return db_execute(self, "BEGIN %s TRANSACTION", tran_type);
+}
+
+int db_commit(db_t * self)
+{
+ assert(self != NULL);
+ return db_execute(self, "COMMIT TRANSACTION");
+}
+
+int db_rollback(db_t * self)
+{
+ assert(self != NULL);
+ return db_execute(self, "ROLLBACK TRANSACTION");
+}
+
+/* =======================================================================*/
+
+int statement_init(statement_t * self, db_t * db)
+{
+ assert(self != NULL);
+ assert(db != NULL);
+
+ self->db = db;
+ self->stmt = NULL;
+
+ return 0;
+}
+
+int statement_delete(statement_t * self)
+{
+ assert(self != NULL);
+
+ if (self->stmt != NULL) {
+ statement_finalize(self);
+ self->stmt = NULL;
+ }
+
+ self->db = NULL;
+ free(self);
+
+ return 0;
+}
+
+int statement_prepare(statement_t * self, const char *sql)
+{
+ assert(self != NULL);
+ assert(sql != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_finalize(self->stmt);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ rc = sqlite3_prepare_v2(self->db->db, sql, -1, &self->stmt, NULL);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+
+ return rc;
+}
+
+int statement_step(statement_t * self)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_step(self->stmt);
+
+ switch (rc) {
+ case DB_DONE:
+ case DB_ROW:
+ break;
+ default:
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+int statement_reset(statement_t * self)
+{
+ self = NULL;
+ return 0;
+}
+
+int statement_finalize(statement_t * self)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_finalize(self->stmt);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ self->stmt = NULL;
+ }
+
+ return rc;
+}
+
+int statement_bind_int(statement_t * self, int pos, int val)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_bind_int(self->stmt, pos, val);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+int statement_bind_int64(statement_t * self, int pos, int64_t val)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_bind_int64(self->stmt, pos, val);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+int statement_bind_text(statement_t * self, int pos, const char *val)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_bind_text(self->stmt, pos, val, -1,
+ SQLITE_TRANSIENT);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+int statement_bind_blob(statement_t * self, int pos, const void *val, int size)
+{
+ assert(self != NULL);
+
+ int rc = DB_OK;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_bind_blob(self->stmt, pos, val, size,
+ SQLITE_TRANSIENT);
+ if (rc != DB_OK) {
+ SQLERR(self->db->db, self->stmt);
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+int statement_column_int(statement_t * self, int pos)
+{
+ assert(self != NULL);
+ return sqlite3_column_int(self->stmt, pos);
+}
+
+int64_t statement_column_int64(statement_t * self, int pos)
+{
+ assert(self != NULL);
+ return sqlite3_column_int64(self->stmt, pos);
+}
+
+int statement_column_bytes(statement_t * self, int pos)
+{
+ assert(self != NULL);
+ return sqlite3_column_bytes(self->stmt, pos);
+}
+
+const unsigned char *statement_column_text(statement_t * self, int pos)
+{
+ assert(self != NULL);
+
+ const unsigned char *rc = NULL;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_column_text(self->stmt, pos);
+ if (rc == NULL) {
+ SQLERR(self->db->db, self->stmt);
+ return NULL;
+ }
+ }
+
+ return rc;
+}
+
+const void *statement_column_blob(statement_t * self, int pos)
+{
+ assert(self != NULL);
+
+ const void *rc = NULL;
+
+ if (self->stmt != NULL) {
+ rc = sqlite3_column_blob(self->stmt, pos);
+ if (rc == NULL) {
+ SQLERR(self->db->db, self->stmt);
+ return NULL;
+ }
+ }
+
+ return rc;
+}
+
+/* =======================================================================*/
diff --git a/clib/src/dispatch.c b/clib/src/dispatch.c
new file mode 100644
index 0000000..4630a70
--- /dev/null
+++ b/clib/src/dispatch.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: dispatch.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/03/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "misc.h"
+#include "nargs.h"
+#include "dispatch.h"
+
+#define DISPATCH_PAGE_SIZE 64
+#define DISPATCH_EVENT_SIZE 10
+
+/* ======================================================================= */
+
+const char *__dispatch_msg[] = {
+ "dispatch: unexpected NULL self pointer",
+ "dispatch: unexpected NULL callback structure",
+};
+
+#define DISPATCH_NULL (__dispatch_msg[0])
+#define DISPATCH_CALLBACK_NULL (__dispatch_msg[1])
+
+/* ======================================================================= */
+
+void dispatch_init(dispatch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+
+ if (self->fd != -1)
+ close(self->fd), self->fd = -1;
+
+ self->fd = epoll_create1(EPOLL_CLOEXEC);
+ if (unlikely(self->fd == -1))
+ throw_errno(errno);
+
+ array_init(&self->events, sizeof(struct epoll_event),
+ DISPATCH_PAGE_SIZE);
+}
+
+void dispatch_delete(dispatch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+ close(self->fd), self->fd = -1;
+ array_delete(&self->events);
+}
+
+int dispatch_fileno(dispatch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+ return self->fd;
+}
+
+int dispatch_add(dispatch_t * self, int fd, uint32_t events,
+ dispatch_callback_t * cb)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+
+ array_status(&self->events, fd, true);
+
+ struct epoll_event *ep;
+ ep = (struct epoll_event *)array_at(&self->events, fd);
+ ep->events = events;
+ ep->data.u64 = (uint64_t) fd << 32;
+ ep->data.ptr = (void *)cb;
+
+ if (unlikely(epoll_ctl(self->fd, EPOLL_CTL_ADD, fd, ep)))
+ throw_errno(errno);
+
+ return fd;
+}
+
+void dispatch_remove(dispatch_t * self, int fd)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+
+ if (unlikely(epoll_ctl(self->fd, EPOLL_CTL_DEL, fd, NULL)))
+ throw_errno(errno);
+
+ array_status(&self->events, fd, false);
+}
+
+void dispatch_wait1(dispatch_t * self)
+{
+ dispatch_wait2(self, -1);
+}
+
+void dispatch_wait2(dispatch_t * self, int timeout)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(DISPATCH_NULL);
+
+ if (-1 < self->fd) {
+ struct epoll_event events[DISPATCH_EVENT_SIZE];
+
+ int rc = epoll_wait(self->fd, events,
+ DISPATCH_EVENT_SIZE, timeout);
+
+ if (0 < rc) {
+ dump_memory(stdout, 0, events, sizeof(events));
+ }
+ for (int n = 0; n < rc; n++) {
+ dispatch_callback_t *cb =
+ (dispatch_callback_t *) events[n].data.ptr;
+ if (cb != NULL) {
+ printf
+ ("cb[%p] fd[%d] data[%p] ep->data.u64[%lld]\n",
+ cb, cb->fd, cb->data, events[n].data.u64);
+ if (cb->func != NULL) {
+ // dispatch_event ev = {events[n].data.u64 >> 32, events[n].events};
+ dispatch_event_t ev =
+ { cb->fd, events[n].events };
+ cb->func(&ev, cb->data);
+ }
+ }
+ }
+ }
+}
+
+/* ======================================================================= */
diff --git a/clib/src/ecc.c b/clib/src/ecc.c
new file mode 100644
index 0000000..2103b00
--- /dev/null
+++ b/clib/src/ecc.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: ecc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: SFC ECC functions
+ * Note:
+ * Date: 08/02/12
+ * Descr: Added New ECC function with correctable bit functionality.
+ * Date: 12/04/13
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+#include <endian.h>
+#include <assert.h>
+
+#include "ecc.h"
+#include "builtin.h"
+#include "attribute.h"
+
+#ifndef be64toh
+#include <byteswap.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define be64toh(x) __bswap_64(x)
+#define htobe64(x) __bswap_64(x)
+#else
+#define be64toh(x) (x)
+#define htobe64(x) (x)
+#endif
+#endif
+
+/*
+ * This is an alternative way to calculate the ECC byte taken
+ * from the SFC spec.
+ */
+uint8_t sfc_ecc2(uint8_t __data[8]) __unused__;
+uint8_t sfc_ecc2(uint8_t __data[8])
+{
+ uint8_t ecc = 0;
+
+ for (int byte = 0; byte < 8; byte++) {
+ for (int bit = 0; bit < 8; bit++) {
+ static unsigned char m[] =
+ { 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99 };
+
+ unsigned char x =
+ __data[byte] & m[(byte + bit + 1) & 7];
+ x = x ^ (x >> 4);
+ x = x ^ (x >> 2);
+ x = x ^ (x >> 1);
+
+ ecc ^= (x & 1) << bit;
+ }
+ }
+
+ return ~ecc;
+}
+
+uint8_t sfc_ecc(uint8_t __data[8])
+{
+/* each bit of the ECC data corresponds to a row in this matrix */
+ static uint8_t __matrix[8][8] = {
+/* 11111111 00000000 00000000 11101000 01000010 00111100 00001111 10011001 */
+ {0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99},
+/* 10011001 11111111 00000000 00000000 11101000 01000010 00111100 00001111 */
+ {0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f},
+/* 00001111 10011001 11111111 00000000 00000000 11101000 01000010 00111100 */
+ {0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c},
+/* 00111100 00001111 10011001 11111111 00000000 00000000 11101000 01000010 */
+ {0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42},
+/* 01000010 00111100 00001111 10011001 11111111 00000000 00000000 11101000 */
+ {0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8},
+/* 11101000 01000010 00111100 00001111 10011001 11111111 00000000 00000000 */
+ {0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00},
+/* 00000000 11101000 01000010 00111100 00001111 10011001 11111111 00000000 */
+ {0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00},
+/* 00000000 00000000 11101000 01000010 00111100 00001111 10011001 11111111 */
+ {0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff},
+ };
+
+ static uint8_t __mask[] = {
+/* 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 */
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+ };
+
+ uint8_t __and[8], __ecc = 0;
+
+ for (uint32_t i = 0; i < sizeof(__matrix) / sizeof(*__matrix); i++) {
+ int __popcount = 0;
+
+ for (uint32_t __byte = 0; __byte < 8; __byte++) {
+ /* compute the AND of the data and ECC matrix */
+ __and[__byte] = __data[__byte] & __matrix[i][__byte];
+ /* count the number of '1' bits in the result (parity) */
+ __popcount += popcount(__and[__byte]);
+ }
+
+ /* if the result is odd parity, turn on corresponding ECC bit */
+ if ((__popcount) & 1) /* odd parity? */
+ __ecc |= __mask[i];
+
+#ifdef DEBUG
+ printf("\nmatrix[");
+ print_binary(NULL, __matrix[i], 8);
+ printf("]\n data[");
+ print_binary(NULL, __data, 8);
+ printf("]\n and[");
+ print_binary(NULL, __and, 8);
+ printf("]\n");
+ printf("popcount: %d ecc %2.2x\n", __popcount, __ecc);
+#endif
+ }
+
+ /* the ECC data is inverted such that */
+ /* 0xFFFFFFFFffffffff => 0xFF for erased NOR flash */
+ return __ecc ^ 0xFF;
+}
+
+
+/*
+ * "aaaaaaaa [wwwwwwww_xxxxxxxx e yyyyyyyy_zzzzzzzz e] [........ ........]"
+ */
+static void __ecc_dump(FILE * __out, uint32_t __addr,
+ void *__restrict __buf, size_t __buf_sz, bool invert)
+{
+ if (__buf_sz <= 0 || __buf == NULL)
+ return;
+ if (__out == NULL)
+ __out = stdout;
+
+ size_t hex_size = (16 + 2) * 2; // 16 bytes per doubleword plus ECC byte
+ size_t ascii_size = (8 + 1) * 2; // 8 bytes per doublewod plus ECC byte
+
+ uint8_t hex[hex_size + 1], ascii[hex_size + 1];
+ memset(hex, '.', hex_size), memset(ascii, '.', ascii_size);
+
+ void print_line(void) {
+ const char *ansi_red = "\033[1;1m\033[1;31m";
+ const char *ansi_norm = "\033[0m";
+
+ uint8_t c1 = sfc_ecc(ascii + 0), e1 = ascii[8];
+ if (invert == true)
+ c1 = ~c1;
+ uint8_t c2 = sfc_ecc(ascii + 9), e2 = ascii[17];
+ if (invert == true)
+ c2 = ~c2;
+
+ for (size_t i = 0; i < ascii_size; i++)
+ ascii[i] = isprint(ascii[i]) ? ascii[i] : '.';
+
+ fprintf(__out, "%08x ", __addr);
+
+ fprintf(__out,
+ "[%.8s_%.8s %.*s%.2s%.*s %.8s_%.8s %.*s%.2s%.*s] ",
+ hex + 0, hex + 8, (c1 != e1) ? strlen(ansi_red) : 0,
+ ansi_red, hex + 16, (c1 != e1) ? strlen(ansi_norm) : 0,
+ ansi_norm, hex + 18, hex + 26,
+ (c2 != e2) ? strlen(ansi_red) : 0, ansi_red, hex + 34,
+ (c2 != e2) ? strlen(ansi_norm) : 0, ansi_norm);
+
+ fprintf(__out, "[%.8s %.8s]\n", ascii + 0, ascii + 9);
+ }
+
+ size_t s = __addr % 16, i = 0;
+ __addr &= ~0xF;
+
+ for (i = s; i < __buf_sz + s; i++) {
+ unsigned char c = ((unsigned char *)__buf)[i - s];
+
+ hex[((i << 1) + 0) % hex_size] = "0123456789abcdef"[c >> 4];
+ hex[((i << 1) + 1) % hex_size] = "0123456789abcdef"[c & 0xf];
+
+ ascii[i % ascii_size] = c;
+
+ if (i == 0)
+ continue;
+
+ if ((i + 1) % ascii_size == 0) {
+ print_line();
+ memset(hex, '.', hex_size), memset(ascii, '.',
+ ascii_size);
+
+ __addr += ascii_size;
+ }
+ }
+
+ if (i % ascii_size)
+ print_line();
+
+ return;
+}
+
+
+
+/* ======================================== */
+static uint64_t ecc_matrix[] = {
+ //0000000000000000111010000100001000111100000011111001100111111111
+ 0x0000e8423c0f99ff,
+ //0000000011101000010000100011110000001111100110011111111100000000
+ 0x00e8423c0f99ff00,
+ //1110100001000010001111000000111110011001111111110000000000000000
+ 0xe8423c0f99ff0000,
+ //0100001000111100000011111001100111111111000000000000000011101000
+ 0x423c0f99ff0000e8,
+ //0011110000001111100110011111111100000000000000001110100001000010
+ 0x3c0f99ff0000e842,
+ //0000111110011001111111110000000000000000111010000100001000111100
+ 0x0f99ff0000e8423c,
+ //1001100111111111000000000000000011101000010000100011110000001111
+ 0x99ff0000e8423c0f,
+ //1111111100000000000000001110100001000010001111000000111110011001
+ 0xff0000e8423c0f99
+};
+
+static uint8_t syndrome_matrix[] = {
+ GD, E7, E6, UE, E5, UE, UE, 47, E4, UE, UE, 37, UE, 35, 39, UE,
+ E3, UE, UE, 48, UE, 30, 29, UE, UE, 57, 27, UE, 31, UE, UE, UE,
+ E2, UE, UE, 17, UE, 18, 40, UE, UE, 58, 22, UE, 21, UE, UE, UE,
+ UE, 16, 49, UE, 19, UE, UE, UE, 23, UE, UE, UE, UE, 20, UE, UE,
+ E1, UE, UE, 51, UE, 46, 9, UE, UE, 34, 10, UE, 32, UE, UE, 36,
+ UE, 62, 50, UE, 14, UE, UE, UE, 13, UE, UE, UE, UE, UE, UE, UE,
+ UE, 61, 8, UE, 41, UE, UE, UE, 11, UE, UE, UE, UE, UE, UE, UE,
+ 15, UE, UE, UE, UE, UE, UE, UE, UE, UE, 12, UE, UE, UE, UE, UE,
+ E0, UE, UE, 55, UE, 45, 43, UE, UE, 56, 38, UE, 1, UE, UE, UE,
+ UE, 25, 26, UE, 2, UE, UE, UE, 24, UE, UE, UE, UE, UE, 28, UE,
+ UE, 59, 54, UE, 42, UE, UE, 44, 6, UE, UE, UE, UE, UE, UE, UE,
+ 5, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+ UE, 63, 53, UE, 0, UE, UE, UE, 33, UE, UE, UE, UE, UE, UE, UE,
+ 3, UE, UE, 52, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+ 7, UE, UE, UE, UE, UE, UE, UE, UE, 60, UE, UE, UE, UE, UE, UE,
+ UE, UE, UE, UE, 4, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE,
+};
+
+
+static uint8_t generate_ecc(uint64_t i_data)
+{
+ uint8_t result = 0;
+
+ for (int i = 0; i < 8; i++)
+ {
+ result |= __builtin_parityll(ecc_matrix[i] & i_data) << i;
+ }
+ return result;
+}
+static uint8_t verify_ecc(uint64_t i_data, uint8_t i_ecc)
+{
+ return syndrome_matrix[generate_ecc(i_data) ^ i_ecc ];
+}
+static uint8_t correct_ecc(uint64_t *io_data, uint8_t *io_ecc)
+{
+ uint8_t bad_bit = verify_ecc(*io_data, *io_ecc);
+
+ if ((bad_bit != GD) && (bad_bit != UE)) // Good is done, UE is hopeless.
+ {
+ // Determine if the ECC or data part is bad, do bit flip.
+ if (bad_bit >= E7)
+ {
+ *io_ecc ^= (1 << (bad_bit - E7));
+ }
+ else
+ {
+ *io_data ^=(1ull << (63 - bad_bit));
+ }
+ }
+ return bad_bit;
+}
+
+static void inject_ecc(const uint8_t* i_src, size_t i_srcSz,
+ uint8_t* o_dst, bool invert)
+{
+ assert(0 == (i_srcSz % sizeof(uint64_t)));
+
+ for(size_t i = 0, o = 0;
+ i < i_srcSz;
+ i += sizeof(uint64_t), o += sizeof(uint64_t) + sizeof(uint8_t))
+ {
+ // Read data word, copy to destination.
+ uint64_t data = *(const uint64_t*)(&i_src[i]);
+
+ *(uint64_t*)(&o_dst[o]) = data;
+ data = be64toh(data);
+
+ // Calculate ECC, copy to destination.
+ uint8_t ecc = generate_ecc(data);
+ o_dst[o + sizeof(uint64_t)] = invert ? ~ecc : ecc;
+ }
+}
+static ecc_status_t remove_ecc(uint8_t* io_src, size_t i_srcSz,
+ uint8_t* o_dst, size_t i_dstSz,
+ bool invert)
+{
+ assert(0 == (i_dstSz % sizeof(uint64_t)));
+
+ ecc_status_t rc = CLEAN;
+
+ for(size_t i = 0, o = 0;
+ i < i_srcSz;
+ i += sizeof(uint64_t) + sizeof(uint8_t), o += sizeof(uint64_t))
+ {
+ // Read data and ECC parts.
+ uint64_t data = *(uint64_t*)(&io_src[i]);
+ data = be64toh(data);
+
+ uint8_t ecc = io_src[i + sizeof(uint64_t)];
+ if(invert)
+ {
+ ecc = ~ecc;
+ }
+ // Calculate failing bit and fix data.
+ uint8_t bad_bit = correct_ecc(&data, &ecc);
+
+ // Return data to big endian.
+ data = htobe64(data);
+
+ // Perform correction and status update.
+ if (bad_bit == UE)
+ {
+ rc = UNCORRECTABLE;
+ }
+ else if (bad_bit != GD)
+ {
+ if (rc != UNCORRECTABLE)
+ {
+ rc = CORRECTED;
+ }
+ *(uint64_t*)(&io_src[i]) = data;
+ io_src[i + sizeof(uint64_t)] = invert ? ~ecc : ecc;
+ }
+
+ // Copy fixed data to destination buffer.
+ *(uint64_t*)(&o_dst[o]) = data;
+ }
+ return rc;
+}
+/* ========================================= */
+
+static ssize_t __ecc_inject(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src, size_t __src_sz,
+ bool invert)
+{
+ int __size = sizeof(uint64_t);
+
+ errno = 0;
+ if (__src_sz & (__size - 1)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (__dst_sz < (__src_sz + (__src_sz / __size))) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ ssize_t rc=0;
+ ssize_t c_sz = __src_sz;
+ for (; c_sz; c_sz -= __size) {
+ rc += __size + 1;
+ }
+
+ inject_ecc(__src, __src_sz, __dst, invert);
+ return (rc);
+}
+
+static ssize_t __ecc_remove(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src, size_t __src_sz,
+ bool invert)
+{
+ int __size = sizeof(uint64_t);
+
+ errno = 0;
+ if ((__src_sz % (__size + 1)) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (__dst_sz < (__src_sz - (__src_sz / __size))) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+
+ int target_size = ((__src_sz / (sizeof(uint64_t) + 1))*sizeof(uint64_t));
+ if( remove_ecc((uint8_t*)__src, __src_sz, __dst, __dst_sz, invert) != CLEAN)
+ {
+ target_size = 0;
+ }
+ return target_size;
+}
+
+void sfc_ecc_dump(FILE * __out, uint32_t __addr,
+ void *__restrict __buf, size_t __buf_sz)
+{
+ return __ecc_dump(__out, __addr, __buf, __buf_sz, false);
+}
+
+/* ========================================= */
+ssize_t sfc_ecc_inject(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src, size_t __src_sz)
+{
+ return __ecc_inject(__dst, __dst_sz, __src, __src_sz, true);
+}
+ssize_t sfc_ecc_remove(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src, size_t __src_sz)
+{
+ return __ecc_remove(__dst, __dst_sz, __src, __src_sz, true);
+
+}
+ssize_t p8_ecc_remove_size (void *__restrict __dst, size_t __dst_sz,
+ void *__restrict __src, size_t __src_sz __unused__)
+{
+ return __ecc_remove(__dst, __dst_sz, __src, __src_sz, false);
+}
+
+/* ========================================= */
+ssize_t p8_ecc_inject(void *__restrict __dst, size_t __dst_sz,
+ const void *__restrict __src, size_t __src_sz)
+{
+ return __ecc_inject(__dst, __dst_sz, __src, __src_sz, false);
+}
+
+ecc_status_t p8_ecc_remove (void *__restrict __dst, size_t __dst_sz,
+ void *__restrict __src, size_t __src_sz __unused__)
+{
+ return remove_ecc(__src, __src_sz, __dst, __dst_sz, false);
+}
+
+void p8_ecc_dump(FILE * __out, uint32_t __addr,
+ void *__restrict __buf, size_t __buf_sz)
+{
+ return __ecc_dump(__out, __addr, __buf, __buf_sz, true);
+}
+
diff --git a/clib/src/err.c b/clib/src/err.c
new file mode 100644
index 0000000..e16f453
--- /dev/null
+++ b/clib/src/err.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: err.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Error container
+ * Date: 04/04/12
+ */
+
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <execinfo.h>
+
+#include "attribute.h"
+#include "min.h"
+
+#include "err.h"
+
+static pthread_key_t __err_key = 0;
+
+static const char *__err_type_name[] = {
+ [ERR_NONE] = "none",
+ [ERR_ERRNO] = "errno",
+ [ERR_UNEXPECTED] = "unexpected",
+ [ERR_VERSION] = "version",
+};
+
+/* =======================================================================*/
+
+void err_delete(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ memset(self, 0, sizeof(*self));
+ free(self);
+
+ return;
+}
+
+err_t *err_get(void)
+{
+ list_t *list = (list_t *) pthread_getspecific(__err_key);
+
+ err_t *self = NULL;
+
+ if (list != NULL) {
+ self = container_of(list_remove_tail(list), err_t, node);
+
+ if (list_empty(list)) {
+ free(list), list = NULL;
+ assert(pthread_setspecific(__err_key, list) == 0);
+ }
+ }
+
+ return self;
+}
+
+void err_put(err_t * self)
+{
+ assert(self != NULL);
+
+ list_t *list = pthread_getspecific(__err_key);
+ if (list == NULL) {
+ list = (list_t *) malloc(sizeof(*list));
+ assert(list != NULL);
+
+ list_init(list);
+ assert(pthread_setspecific(__err_key, list) == 0);
+ }
+
+ list_add_head(list, &self->node);
+
+ return;
+}
+
+int err_type1(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->type;
+}
+
+void err_type2(err_t * self, int type)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ self->type = type;
+}
+
+int err_code1(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->code;
+}
+
+void err_code2(err_t * self, int code)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ self->code = code;
+
+ return;
+}
+
+const char *err_file1(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->file;
+}
+
+void err_file2(err_t * self, const char *file)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ self->file = file;
+
+ return;
+}
+
+int err_line1(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->line;
+}
+
+void err_line2(err_t * self, int line)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ self->line = line;
+
+ return;
+}
+
+const void *err_data1(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->data;
+}
+
+void err_data2(err_t * self, int size, const void *data)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ self->size = min(size, ERR_DATA_SIZE);
+ memcpy(self->data, data, self->size);
+
+ return;
+}
+
+int err_size(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return self->size;
+}
+
+const char *err_type_name(err_t * self)
+{
+ assert(self != NULL);
+ assert(self->magic == ERR_MAGIC);
+
+ return __err_type_name[self->type];
+}
+
+/* =======================================================================*/
+
+__constructor static void __err__ctor__(void)
+{
+ assert(pthread_key_create(&__err_key, NULL) == 0);
+}
diff --git a/clib/src/exception.c b/clib/src/exception.c
new file mode 100644
index 0000000..30b6dc6
--- /dev/null
+++ b/clib/src/exception.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: exc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: {set,long}jmp implementation of exceptions for C code.
+ * Note: using these macros will create an exception context
+ * in each thread.
+ * Date: 7/06/09
+ */
+
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <execinfo.h>
+
+#include "exception.h"
+#include "misc.h"
+
+/* =======================================================================*/
+
+const char *exception_name[] = {
+ "assertion",
+ "unexpected",
+ "errno",
+};
+
+typedef struct exception_context {
+ pthread_once_t once;
+ pthread_key_t key;
+} exception_context;
+
+/* =======================================================================*/
+
+static exception_context __exc_ctx = { PTHREAD_ONCE_INIT, 0 };
+
+static inline void __exc_key_create(void)
+{
+ if (pthread_key_create(&__exc_ctx.key, NULL))
+ __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__);
+}
+
+/* =======================================================================*/
+
+void __exc_init(void)
+{
+ if (pthread_once(&__exc_ctx.once, __exc_key_create))
+ __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__);
+}
+
+exception_frame_t *__exc_get_frame(void)
+{
+ return (exception_frame_t *) (pthread_getspecific(__exc_ctx.key));
+}
+
+void __exc_set_frame(exception_frame_t * f)
+{
+ if (pthread_setspecific(__exc_ctx.key, f))
+ __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__);
+}
+
+void __exc_backtrace(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list va;
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+ }
+
+ fprintf(stderr, "========== backtrace ==========\n");
+ void *bt[1024];
+ int nr = backtrace(bt, sizeof bt);
+ backtrace_symbols_fd(bt, nr, fileno(stderr));
+ fprintf(stderr, "========== backtrace ==========\n");
+}
+
+int __exc_throw(int ex, void *data, int size, const char *file, int line)
+{
+ extern char *program_invocation_short_name;
+
+ exception_frame_t *frame = __exc_get_frame();
+
+ if (frame == NULL) {
+ __exc_backtrace
+ ("*** UNHANDLED EXCEPTION *** -- %s: %s(%d) ex=%d\n\n",
+ program_invocation_short_name, file, line, ex);
+ abort();
+ }
+
+ if (frame->magic != EXC_MAGIC) {
+ __exc_backtrace
+ ("*** CORRUPTED EXCEPTION FRAME *** -- %s: %s(%d) ex=%d\n\n",
+ program_invocation_short_name, file, line, ex);
+ abort();
+ }
+
+ frame->exc.file = file;
+ frame->exc.line = line;
+
+ frame->exc.data = data;
+ frame->exc.size = size;
+
+ longjmp(frame->jmp, ex);
+
+ return -1;
+}
+
+int __exc_rethrow(int ex, void *data, int size, const char *file, int line)
+{
+ exception_frame_t *frame = __exc_get_frame();
+
+ if (frame != NULL)
+ __exc_set_frame(frame->prev);
+
+ return __exc_throw(ex, data, size, file, line);
+}
+
+const char *__exc_name(int exc)
+{
+ return (exc < 0 || EXC_LAST <= exc) ? NULL : exception_name[exc];
+}
+
+__constructor void __exc_ctor(void)
+{
+ __exc_init();
+}
diff --git a/clib/src/heap.c b/clib/src/heap.c
new file mode 100644
index 0000000..2b339a2
--- /dev/null
+++ b/clib/src/heap.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "assert.h"
+#include "misc.h"
+#include "slab.h"
+#include "heap.h"
+#include "bb_trace.h"
+#include "memory_leak_detection.h"
+
+/* ======================================================================= */
+
+static const char *__heap_msg[] = {
+ "heap: unexpected NULL pointer"
+ "heap: unexpected cache and/or alloc size",
+ "heap: unexpected alloc size",
+};
+
+#define HEAP_NULL (__heap_msg[0])
+#define HEAP_SIZE (__heap_msg[1])
+#define HEAP_ALLOC (__heap_msg[2])
+
+/* ======================================================================= */
+
+void heap_dump(heap_t * self, FILE * out)
+{
+ if (self == NULL)
+ return;
+
+ size_t i;
+
+ fprintf(out,
+ "*******************************************************************\n");
+ for (i = 0; i < self->slab_size; i++)
+ if (self->slab[i] != NULL)
+ slab_dump(self->slab[i], out);
+ fprintf(out,
+ "*******************************************************************\n");
+}
+
+heap_t *__heap_new(size_t alloc_size, size_t page_size, const char *file,
+ int line)
+{
+ alloc_size = max(__round_pow2(alloc_size), CACHE_ALLOC_MIN);
+ page_size = max(__round_pow2(page_size), CACHE_SIZE_MIN);
+
+ if (alloc_size < CACHE_ALLOC_MIN || CACHE_ALLOC_MAX < alloc_size)
+ throw_unexpected(HEAP_SIZE);
+ if (page_size < CACHE_SIZE_MIN || CACHE_SIZE_MAX < page_size)
+ throw_unexpected(HEAP_SIZE);
+
+ heap_t *self = (heap_t *) MALLOC(sizeof(*self) +
+ sizeof(*self->slab) * page_size /
+ alloc_size);
+ assert(self != NULL);
+
+ self->page_size = page_size;
+ self->alloc_size = alloc_size;
+ self->slab_size = page_size / alloc_size;
+
+ memset(self->slab, 0, self->slab_size * sizeof(*self->slab));
+
+ return self;
+}
+
+void __heap_delete(heap_t * self, const char *file, int line)
+{
+ if (unlikely(self == NULL))
+ return;
+
+ size_t i;
+ for (i = 0; i < self->slab_size; i++) {
+ if (self->slab[i] != NULL) {
+ slab_delete(self->slab[i]);
+ self->slab[i] = NULL;
+ }
+ }
+
+ memset(self, 0, sizeof *self);
+ FREE(self);
+}
+
+void *__heap_alloc(heap_t * self, size_t size, const char *file, int line)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(HEAP_NULL);
+
+ size = max(align(size, self->alloc_size), self->alloc_size);
+
+ size_t slab_pos = size / self->alloc_size - 1;
+
+ if (unlikely(self->slab_size < slab_pos))
+ throw_unexpected(HEAP_ALLOC);
+
+ if (unlikely(self->slab[slab_pos] == NULL))
+ self->slab[slab_pos] = slab_new(size, 0);
+
+ return slab_alloc(self->slab[slab_pos]);
+}
+
+void __heap_free(heap_t * self, void *ptr, const char *file, int line)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(HEAP_NULL);
+
+ void *data = (void *)((uint32_t) ptr & ~(self->page_size - 1));
+ cache_t *c = (cache_t *) (data + self->page_size - sizeof(cache_t));
+ cache_check(c);
+ slab_free(cache_slab(c), ptr);
+}
+
+/* ======================================================================= */
+
+static uint32_t page_size;
+
+void heap_ctor(void) __constructor;
+void heap_ctor(void)
+{
+ page_size = (const uint32_t)sysconf(_SC_PAGESIZE);
+ assert(0 < page_size);
+}
+
+void heap_dtor(void) __destructor;
+void heap_dtor(void)
+{
+}
+
+/* ======================================================================= */
diff --git a/clib/src/list.c b/clib/src/list.c
new file mode 100644
index 0000000..3c72269
--- /dev/null
+++ b/clib/src/list.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "assert.h"
+#include "misc.h"
+#include "list.h"
+
+/* ======================================================================= */
+
+void list_dump(list_t * self, FILE * out)
+{
+ assert(self != NULL);
+
+ fprintf(out,
+ "===================================================================\n");
+ fprintf(out, "head: %8x node: %8x\n", (uint32_t) self,
+ (uint32_t) & self->node);
+
+ list_node_t *node = &self->node;
+ do {
+ fprintf(out, " node: %8x - prev: %8x - next: %8x\n",
+ (uint32_t) node, (uint32_t) node->prev,
+ (uint32_t) node->next);
+ node = node->next;
+ } while (node != &self->node);
+
+ fprintf(out,
+ "===================================================================\n");
+}
diff --git a/clib/src/list_iter.c b/clib/src/list_iter.c
new file mode 100644
index 0000000..fb1b81e
--- /dev/null
+++ b/clib/src/list_iter.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: list_iter.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: List iterator
+ * Note:
+ * Date: 10/22/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "list_iter.h"
+
+/* ======================================================================= */
+
+static inline list_node_t *__list_iter_bwd(list_iter_t * self)
+{
+ assert(self != NULL);
+
+ assert(self->list != NULL);
+ assert(self->node != NULL);
+
+ if (self->node == &self->list->node)
+ self->node = NULL;
+ else
+ self->node = list_node_prev(self->node);
+
+ return self->node;
+}
+
+static inline list_node_t *__list_iter_fwd(list_iter_t * self)
+{
+ assert(self != NULL);
+ assert(self->list != NULL);
+ assert(self->node != NULL);
+
+ if (self->node == &self->list->node)
+ self->node = NULL;
+ else
+ self->node = list_node_next(self->node);
+
+ return self->node;
+}
+
+void list_iter_init(list_iter_t * self, list_t * list, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(list != NULL);
+
+ self->flags = flags;
+ self->list = list;
+
+ if (self->flags & LI_FLAG_BWD)
+ self->node = list_tail(self->list);
+ else
+ self->node = list_head(self->list);
+}
+
+void list_iter_clear(list_iter_t * self)
+{
+ assert(self != NULL);
+
+ if (self->flags & LI_FLAG_BWD)
+ self->node = list_tail(self->list);
+ else
+ self->node = list_head(self->list);
+}
+
+list_node_t *list_iter_elem(list_iter_t * self)
+{
+ assert(self != NULL);
+ return self->node;
+}
+
+list_node_t *list_iter_inc1(list_iter_t * self)
+{
+ return list_iter_inc2(self, 1);
+}
+
+list_node_t *list_iter_inc2(list_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ for (size_t i = 0; i < count && self->node != NULL; i++) {
+ if (self->flags & LI_FLAG_BWD)
+ __list_iter_bwd(self);
+ else
+ __list_iter_fwd(self);
+ }
+
+ return self->node;
+}
+
+list_node_t *list_iter_dec1(list_iter_t * self)
+{
+ return list_iter_dec2(self, 1);
+}
+
+list_node_t *list_iter_dec2(list_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ for (size_t i = 0; i < count && self->node != NULL; i++) {
+ if (self->flags & LI_FLAG_BWD)
+ __list_iter_fwd(self);
+ else
+ __list_iter_bwd(self);
+ }
+
+ return self->node;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/memory_leak_detection.c b/clib/src/memory_leak_detection.c
new file mode 100644
index 0000000..79dc6fb
--- /dev/null
+++ b/clib/src/memory_leak_detection.c
@@ -0,0 +1,1277 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2011
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: B. Rafanello
+ *
+ * Module: memory_leak_detection.c
+ *
+ * Implements a simple memory leak and buffer overwrite detection
+ * system.
+ *
+ * Version 0.0.0.1
+ */
+
+#define PROGRAM_VERSION "0.0.0.1"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+#include <execinfo.h>
+#include "bb_trace.h"
+
+/*--------------------------------------------------
+ * Private Constants
+ --------------------------------------------------*/
+
+#define LEAK_SIGNATURE 8283471
+#define SENTINEL_VALUE 0xD9B79573
+
+/*--------------------------------------------------
+ * Private Type definitions
+ --------------------------------------------------*/
+
+typedef unsigned int Sentinel_t;
+
+struct _Memory_Leak_Data {
+ unsigned int Signature; /* Indentify this structure in memory */
+ void *Mem_Address; /* The address actually returned by malloc. */
+ unsigned int User_Size; /* The size of the allocation as requested by the user. */
+ unsigned int Alignment; /* The alignment requested by the user. */
+ const char *Module_Name; /* The name of the module containing the function which allocated this memory. */
+ const char *Function_Name; /* The name of the function which allocated this memory. */
+ unsigned int Line_Number; /* The line number of the MALLOC call in the function which allocated this memory. */
+ struct _Memory_Leak_Data *Next; /* The next allocated block of memory (if any). */
+ struct _Memory_Leak_Data *Previous; /* The previous allocated block of memory (if any). */
+ Sentinel_t Starting_Sentinel; /* The starting sentinel used to detect buffer overrun/memory overwrite errors. */
+};
+
+typedef struct _Memory_Leak_Data Memory_Leak_Data_t;
+
+/*--------------------------------------------------
+ Private global variables.
+--------------------------------------------------*/
+
+static pthread_mutex_t Memory_Leak_Lock = PTHREAD_MUTEX_INITIALIZER; /* Mutex to serialize access to the chain of allocations. */
+static Memory_Leak_Data_t *Memory_Chain = NULL; /* The start of our chain of allocations. */
+static unsigned int Memory_Chain_Entries = 0; /* The number of items in our chain. */
+
+/*--------------------------------------------------
+ Local functions
+--------------------------------------------------*/
+
+void print_backtrace(void)
+{
+#define BT_SIZE 20
+ void *bt[BT_SIZE];
+ int nr = backtrace(&bt[0], BT_SIZE);
+
+ fprintf(stderr, "========== backtrace ==========\n");
+ backtrace_symbols_fd(bt, nr, fileno(stderr));
+ fprintf(stderr, "========== backtrace ==========\n");
+
+ return;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Check_Leak_List */
+/* */
+/* Descriptive Name: */
+/* */
+/* Input: */
+/* */
+/* Output: If Success : */
+/* */
+/* If Failure : */
+/* */
+/* Error Handling: */
+/* */
+/* Side Effects: */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+static int Check_Leak_List(const void *p)
+{
+ Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */
+ Memory_Leak_Data_t *Previous_Item; /* Used to check the memory allocation chain. */
+ Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */
+ unsigned int Data_Start; /* The address of the first byte of the data area of the current allocation. */
+ unsigned int Data_End; /* The first byte after the data area of the current allocation. */
+ unsigned int Address_To_Test; /* The address we are looking for. */
+ unsigned int Address_Found = 2; /* Used to track whether the address was found in the allocation list.
+ If 2, then the address was not found. If 0, then the address was found and
+ it is the start of the data area of an allocation. If 1, then the address
+ was found and it lies within the data area of an allocation. */
+ int rc = 0; /* Holds the return code. Assume success. */
+ unsigned int Count = 0;
+
+ FUNCTION_ENTRY()
+
+ PRINT_FUNCTION_PARAMETER("Parameters:\n")
+ PRINT_FUNCTION_PARAMETER(" p = %p\n", p)
+
+ USER3_PRINT_LINE("Memory_Chain = %p", Memory_Chain)
+ Current_Item = Memory_Chain;
+ Previous_Item = NULL;
+
+ if (p != NULL) {
+ USER3_PRINT_LINE
+ ("Request: Check the memory allocation list for p = %p.\n",
+ p);
+ } else {
+ USER3_PRINT_LINE
+ ("Request: Check the integrity of the memory allocation list.\n");
+ USER3_PRINT_LINE(" Items expected in list: %d\n",
+ Memory_Chain_Entries);
+ }
+
+ while (Current_Item != NULL) {
+ /* Perform some simple checks on the items in the memory allocation list. */
+ if (Current_Item->Signature == LEAK_SIGNATURE) {
+
+ if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) {
+
+ /* Check the ending sentinel. */
+ Ending_Sentinel =
+ (Sentinel_t *) ((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t) +
+ Current_Item->User_Size);
+ if (*Ending_Sentinel == SENTINEL_VALUE) {
+
+ /* Check the previous pointer. */
+ if (Current_Item->Previous ==
+ Previous_Item) {
+
+ if (p != NULL) {
+ /* We have an address to check. Does this address lie within this memory allocation? */
+ Data_End =
+ (unsigned long)
+ Ending_Sentinel;
+ Data_Start =
+ (unsigned
+ long)((unsigned
+ int)
+ Current_Item
+ +
+ sizeof
+ (Memory_Leak_Data_t));
+ Address_To_Test =
+ (unsigned long)p;
+
+ if (Address_To_Test ==
+ Data_Start) {
+ /* This address was returned to the user by MALLOC_func. */
+ Address_Found =
+ 0;
+ break;
+ } else {
+
+ if ((Address_To_Test > Data_Start) && (Address_To_Test < Data_End)) {
+ /* The address lies within the data area of the current allocation. */
+ Address_Found
+ = 1;
+ break;
+ }
+
+ }
+
+ }
+
+ } else {
+ /* We have a pointer problem! The previous pointer field does not point to the previous entry in the list! */
+ rc = 3;
+ fprintf(stderr,
+ "Link Error! The previous pointer of the current item does not point to the previous item in the allocation list!\n");
+ fprintf(stderr,
+ "Current_Item->Previous = %p, Actual Previous Item is %p.\n",
+ Current_Item->Previous,
+ Previous_Item);
+ fprintf(stderr,
+ "Item position in list: %d\n",
+ Count);
+ print_backtrace();
+ }
+
+ } else {
+ rc = 3;
+ fprintf(stderr,
+ "Possible overrun error -- ending sentinel corrupted!\n"
+ " Header Address: %p\n"
+ " User Address: %p\n"
+ " Size: %d\n"
+ " Allocated in module %s, function %s, at line %d\n",
+ Current_Item,
+ (void *)((unsigned int)
+ Current_Item +
+ sizeof
+ (Memory_Leak_Data_t)),
+ Current_Item->User_Size,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number);
+ print_backtrace();
+ }
+
+ } else {
+ rc = 3;
+ fprintf(stderr,
+ "Memory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n"
+ " Header Address: %p\n"
+ " Address: %p\n" " Size: %d\n"
+ " Allocated in module %s, function %s, at line %d\n",
+ Current_Item,
+ (void *)((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t)),
+ Current_Item->User_Size,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number);
+ print_backtrace();
+ }
+
+ Count += 1;
+
+ } else {
+ rc = 3;
+ fprintf(stderr,
+ "Memory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n"
+ " Header Address: %p\n"
+ " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n"
+ " User Address: %p\n"
+ " Size: [%u, 0x%x]\n"
+ " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n"
+ " Next: %p\n" " Previous: %p\n"
+ " Module: %p\n" " Function: %p\n"
+ " Line [%u, ox%x]\n", Current_Item,
+ Current_Item->Signature,
+ Current_Item->Signature, LEAK_SIGNATURE,
+ LEAK_SIGNATURE,
+ (void *)((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t)),
+ Current_Item->User_Size,
+ Current_Item->User_Size,
+ Current_Item->Starting_Sentinel,
+ Current_Item->Starting_Sentinel, SENTINEL_VALUE,
+ SENTINEL_VALUE, Current_Item->Next,
+ Current_Item->Previous,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number,
+ Current_Item->Line_Number);
+ fprintf(stderr,
+ "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p, Count = %u\n\n",
+ Memory_Chain, Memory_Chain_Entries,
+ Current_Item, Count);
+ print_backtrace();
+
+ break;
+ }
+
+ Previous_Item = Current_Item;
+ Current_Item = Current_Item->Next;
+ }
+
+ if (p != NULL) {
+
+ if (rc == 0) {
+ rc = Address_Found;
+ }
+
+ } else {
+
+ if ((Count != Memory_Chain_Entries) && (rc == 0)) {
+ fprintf(stderr,
+ "The number of entries found in the allocation list [%u] != list count [%u]!!\n\n",
+ Count, Memory_Chain_Entries);
+ print_backtrace();
+ rc = 3;
+ }
+
+ }
+
+ FUNCTION_RETURN("%i", rc)
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Add_Memory_Allocation_To_Chain */
+/* */
+/* Descriptive Name: Adds a memory tracking object to the list of */
+/* memory tracking objects which represents all */
+/* memory allocations being tracked by this */
+/* module. */
+/* */
+/* Input: Memory_Leak_Data_t * Memory_Leak_Data - The memory */
+/* tracking object to add. */
+/* */
+/* Output: If Success : None. */
+/* */
+/* If Failure : None. */
+/* */
+/* Error Handling: None. */
+/* */
+/* Side Effects: Other memory tracking objects may be modified. */
+/* The count of objects being tracked will be */
+/* incremented. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+static void Add_Memory_Allocation_To_Chain(Memory_Leak_Data_t *
+ Memory_Leak_Data)
+{
+
+ FUNCTION_ENTRY()
+
+ PRINT_FUNCTION_PARAMETER("Parameters:\n")
+ PRINT_FUNCTION_PARAMETER(" Memory_Leak_Data = %p",
+ Memory_Leak_Data)
+
+ if (Memory_Chain) {
+ /* Other allocations exist. Insert at the head of the chain. */
+ Memory_Leak_Data->Next = Memory_Chain;
+ Memory_Chain->Previous = Memory_Leak_Data;
+ } else {
+ /* This is the first allocation. */
+ Memory_Leak_Data->Next = NULL;
+ }
+
+ /* Update the pointer to the start of the allocation list. */
+ Memory_Chain = Memory_Leak_Data;
+
+ /* Update the count of entries in the memory chain. */
+ Memory_Chain_Entries += 1;
+
+ FUNCTION_EXIT()
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Remove_Memory_Allocation_From_Chain */
+/* */
+/* Descriptive Name: Removes a memory tracking object from */
+/* the linked list of memory tracking */
+/* objects representing current memory */
+/* allocations. */
+/* */
+/* Input: Memory_Leak_Data_t * Memory_Leak_Data - the memory */
+/* tracking object to remove. */
+/* */
+/* Output: If Success : None. */
+/* */
+/* If Failure : None. */
+/* */
+/* Error Handling: None. */
+/* */
+/* Side Effects: Other memory tracking objects may be modified. */
+/* The memory tracking object being removed will be */
+/* zeroed out and freed. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+static void Remove_Memory_Allocation_From_Chain(Memory_Leak_Data_t *
+ Memory_Leak_Data)
+{
+ Memory_Leak_Data_t *Temp; /* Used when adjusting the memory allocation chain. */
+ Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */
+
+ FUNCTION_ENTRY()
+
+ /* Are we the first item in the chain? */
+ if (Memory_Leak_Data->Previous == NULL) {
+
+ /* We are at the head of the chain, which means that Memory_Chain points to us!
+ * Point Memory_Chain to the Memory_Leak_Data_t that follows us (if any).
+ */
+ Memory_Chain = Memory_Leak_Data->Next;
+
+ /* Is there another item in the chain? If so, update it. */
+ if (Memory_Chain != NULL)
+ Memory_Chain->Previous = NULL;
+
+ } else {
+
+ /* Are we the last item in the chain? */
+ if (Memory_Leak_Data->Next == NULL) {
+
+ /* We are at the end of the memory chain with at least 1 entry before us. */
+ Temp = Memory_Leak_Data->Previous;
+ Temp->Next = NULL;
+ } else {
+ /* We are in the middle of the chain. */
+ Temp = Memory_Leak_Data->Previous;
+ Temp->Next = Memory_Leak_Data->Next;
+ Temp = Memory_Leak_Data->Next;
+ Temp->Previous = Memory_Leak_Data->Previous;
+ }
+
+ }
+
+ /* Adjust the count of entries in the memory chain. */
+ Memory_Chain_Entries -= 1;
+
+ /* Calculate the address of the ending sentinel so that we can access it. */
+ Ending_Sentinel =
+ (Sentinel_t *) ((unsigned int)Memory_Leak_Data +
+ sizeof(Memory_Leak_Data_t) +
+ Memory_Leak_Data->User_Size);
+
+ /* Clean out the memory leak data. */
+ Memory_Leak_Data->Function_Name = NULL;
+ Memory_Leak_Data->Line_Number = 0;
+ Memory_Leak_Data->Module_Name = NULL;
+ Memory_Leak_Data->Signature = 0;
+ Memory_Leak_Data->Starting_Sentinel = 0;
+ Memory_Leak_Data->User_Size = 0;
+ Memory_Leak_Data->Next = NULL;
+ Memory_Leak_Data->Previous = NULL;
+ *Ending_Sentinel = 0;
+
+ free(Memory_Leak_Data->Mem_Address);
+
+ FUNCTION_EXIT()
+}
+
+/*--------------------------------------------------
+ API Functions
+--------------------------------------------------*/
+
+/*********************************************************************/
+/* */
+/* Function Name: Print_Leak_List */
+/* */
+/* Descriptive Name: This function walks the list of allocated */
+/* memory blocks and prints information about */
+/* each one. If this is done at program exit, */
+/* the resulting list of memory blocks most */
+/* likely represents leaked memory. */
+/* */
+/* Input: None. */
+/* */
+/* Output: If Success : If there are any memory blocks being */
+/* tracked by this module, information about */
+/* block still being tracked will be sent to */
+/* stderr. */
+/* */
+/* If Failure : Error messages may be sent to stderr. */
+/* */
+/* Error Handling: If errors are detected, then error messages are */
+/* output on stderr. */
+/* */
+/* Side Effects: The internal structures of this module are checked*/
+/* for errors with any errors being reported on */
+/* stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Print_Leak_List(void)
+{
+ Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */
+ Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */
+
+ API_FUNCTION_ENTRY()
+
+ /* Get lock. */
+ pthread_mutex_lock(&Memory_Leak_Lock);
+
+ Current_Item = Memory_Chain;
+
+ fprintf(stderr,
+ "\n\nMemory_Chain is %p, and Memory_Chain_Entries is %u.\n",
+ Memory_Chain, Memory_Chain_Entries);
+ fprintf(stderr, "\nCurrent Memory Allocation List\n\n");
+
+ while (Current_Item != NULL) {
+ /* Perform some simple checks on the data before we print it. */
+ if (Current_Item->Signature == LEAK_SIGNATURE) {
+
+ if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) {
+
+ /* Check the ending sentinel. */
+ Ending_Sentinel =
+ (Sentinel_t *) ((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t) +
+ Current_Item->User_Size);
+ if (*Ending_Sentinel == SENTINEL_VALUE) {
+ fprintf(stderr,
+ "Memory Allocation Record\n");
+ } else {
+ fprintf(stderr,
+ "\nPossible overrun error -- ending sentinel corrupted!\n");
+ }
+
+ fprintf(stderr,
+ " Allocation Block: %p\n"
+ " Header Address: %p\n"
+ " User Address: %p\n"
+ " Alignment: %u\n"
+ " Size: %d\n"
+ " Allocated in module %s, function %s, at line %d\n\n",
+ Current_Item->Mem_Address,
+ Current_Item,
+ (void *)((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t)),
+ Current_Item->Alignment,
+ Current_Item->User_Size,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number);
+ } else {
+ fprintf(stderr,
+ "\nMemory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n");
+ fprintf(stderr,
+ " Allocation Block: %p\n"
+ " Header Address: %p\n"
+ " User Address: %p\n"
+ " Alignment: %u\n" " Size: %d\n"
+ " Allocated in module %s, function %s, at line %d\n\n",
+ Current_Item->Mem_Address, Current_Item,
+ (void *)((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t)),
+ Current_Item->Alignment,
+ Current_Item->User_Size,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number);
+ }
+
+ } else {
+ fprintf(stderr,
+ "\n\nMemory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n");
+ fprintf(stderr,
+ " Allocation Block: %p\n"
+ " Header Address: %p\n"
+ " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n"
+ " Address: %p\n" " Alignment: %u\n"
+ " Size: [%u, 0x%x]\n"
+ " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n"
+ " Next: %p\n" " Previous: %p\n"
+ " Module: %p\n" " Function: %p\n"
+ " Line [%u, ox%x]\n",
+ Current_Item->Mem_Address, Current_Item,
+ Current_Item->Signature,
+ Current_Item->Signature, LEAK_SIGNATURE,
+ LEAK_SIGNATURE,
+ (void *)((unsigned int)Current_Item +
+ sizeof(Memory_Leak_Data_t)),
+ Current_Item->Alignment,
+ Current_Item->User_Size,
+ Current_Item->User_Size,
+ Current_Item->Starting_Sentinel,
+ Current_Item->Starting_Sentinel, SENTINEL_VALUE,
+ SENTINEL_VALUE, Current_Item->Next,
+ Current_Item->Previous,
+ Current_Item->Module_Name,
+ Current_Item->Function_Name,
+ Current_Item->Line_Number,
+ Current_Item->Line_Number);
+ fprintf(stderr,
+ "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p\n\n",
+ Memory_Chain, Memory_Chain_Entries,
+ Current_Item);
+ break;
+ }
+
+ Current_Item = Current_Item->Next;
+ }
+
+ /* Release lock. */
+ pthread_mutex_unlock(&Memory_Leak_Lock);
+
+ API_FUNCTION_EXIT()
+
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: MEMORY_func */
+/* */
+/* Descriptive Name: This function acts as a replacement for */
+/* malloc. It allocates memory (using malloc) */
+/* and enters that memory into a tracking */
+/* structure so that memory leaks, if any, may */
+/* be found. */
+/* */
+/* Input: size_t sz - The number of bytes to be allocated. */
+/* unsigned int Alignment - 0 for non-aligned (normal) */
+/* malloc, > 0 to return an */
+/* address aligned on a specific */
+/* memory boundary. If > 0, then */
+/* Alignment must be a power of 2 */
+/* and a multiple of sizeof(void *)*/
+/* void ** Memory_Location - The address of a variable which*/
+/* will hold the address of the */
+/* allocated by this function. */
+/* const char * mod_name - The name of the module from which*/
+/* this function was called. */
+/* const char * func - The name of the function from which */
+/* this function was called. */
+/* const int line - The line number of the code containing */
+/* the call to this function. */
+/* */
+/* Output: If Success : The function return value will be 0. */
+/* *Memory_Location will be set to the address*/
+/* of the first byte of the user portion of */
+/* any memory that was allocated. */
+/* */
+/* If Failure : The function return value will be EINVAL or*/
+/* ENOMEM. Errors may be reported on stderr. */
+/* *Memory_Location will be set to NULL. */
+/* */
+/* Error Handling: This function will abort if an error is */
+/* is detected. Errors could be lack of memory, or*/
+/* corruption of the internal structures used to */
+/* track allocated blocks of memory. */
+/* */
+/* Side Effects: Memory may be allocated, errors may be reported */
+/* on stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+int MEMORY_func(size_t sz,
+ unsigned int Alignment,
+ void **Memory_Location,
+ const char *mod_name, const char *func, const int line)
+{
+ Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */
+ void *ptr = NULL; /* To hold the address that will be returned to the caller. */
+ size_t New_Size; /* Used to adjust the size of the memory allocation. */
+ Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */
+ unsigned int Proposed_User_Address; /* Used to calculate the address to return to the caller. */
+ unsigned int Shift_Amount; /* Used when adjusting the address returned to caller to ensure proper alignment. */
+ int Error = 0;
+ unsigned int i = 0; /* Used when calculating powers of two to check the alignment. */
+ unsigned int Power_Of_Two = 1; /* Used when calculating powers of two to check the alignment. */
+
+#define MAX_POWER_OF_TWO 31
+
+ API_FUNCTION_ENTRY()
+
+ PRINT_API_PARAMETER("Parameters:\n")
+ PRINT_API_PARAMETER(" sz = %d", sz)
+ PRINT_API_PARAMETER(" Alignment = %d", Alignment)
+ PRINT_API_PARAMETER(" Memory_Location = %p", Memory_Location)
+ PRINT_API_PARAMETER(" mod_name = %s", mod_name)
+ PRINT_API_PARAMETER(" func = %s", func)
+ PRINT_API_PARAMETER(" line = %i", line)
+
+ /* Check the size of the request. */
+ if (sz == 0) {
+ Error = EINVAL;
+ fprintf(stderr,
+ "MALLOC: request is invalid - size[%u] in module %s, function %s at line %d\n",
+ sz, mod_name, func, line);
+ print_backtrace();
+ }
+
+ /* Check the alignment, if one was specified. */
+ if (Alignment > 0) {
+ if (Alignment % sizeof(void *)) {
+ /* Improper alignment! */
+ Error = EINVAL;
+ } else {
+ do {
+ Power_Of_Two *= 2;
+ i++;
+ } while ((Power_Of_Two != Alignment)
+ && (i < MAX_POWER_OF_TWO));
+
+ if (Power_Of_Two != Alignment) {
+ Error = EINVAL;
+ }
+
+ }
+
+ if (Error) {
+ fprintf(stderr,
+ "MEMORY_func: request for aligned memory uses invalid alignment! size[%u], alignment [%u] in module %s, function %s at line %d\n",
+ sz, Alignment, mod_name, func, line);
+ }
+
+ }
+
+ if (Memory_Location == NULL) {
+ Error = EINVAL;
+ fprintf(stderr,
+ "MEMORY_func: Location to place address of allocated memory is NULL! size[%u], alignment [%u] in module %s, function %s at line %d\n",
+ sz, Alignment, mod_name, func, line);
+ } else
+ *Memory_Location = NULL;
+
+ if (Error != 0) {
+ API_FUNCTION_RETURN("%i", Error);
+ }
+
+ USER3_PRINT_LINE
+ ("MEMORY_func: processing request - size[%u], alignment[%u] from module %s, function %s at line %d\n",
+ sz, Alignment, mod_name, func, line);
+
+ /* Check */
+
+ /* Get lock. */
+ pthread_mutex_lock(&Memory_Leak_Lock);
+
+ /* Check the memory allocation list. If the list is bad, then abort because we are already screwed ... */
+ if (!Check_Leak_List(NULL)) {
+
+ /* Adjust the size of the request to account for the additional memory we need to track this request. */
+ New_Size =
+ sz + sizeof(Memory_Leak_Data_t) + sizeof(Sentinel_t) +
+ Alignment;
+
+ /* Get the memory. */
+ ptr = malloc(New_Size);
+
+ /* Was the memory available? */
+ if (ptr != NULL) {
+ /* Determine where to put our memory leak data. */
+ if (Alignment == 0) {
+ /* We can place the memory leak data right at the start of the memory block we got from malloc. */
+ Memory_Leak_Data = (Memory_Leak_Data_t *) ptr;
+ } else {
+ Proposed_User_Address =
+ (unsigned int)ptr +
+ sizeof(Memory_Leak_Data_t);
+ Shift_Amount =
+ Alignment -
+ (Proposed_User_Address % Alignment);
+ Memory_Leak_Data =
+ (Memory_Leak_Data_t *) ((unsigned int)ptr +
+ Shift_Amount);
+ }
+
+ /* Save the address returned by malloc() for use with free(). */
+ Memory_Leak_Data->Mem_Address = ptr;
+
+ /* Create the address to return to the caller. This address should be the first byte after
+ our memory leak data. */
+ ptr =
+ (void *)((unsigned int)Memory_Leak_Data +
+ sizeof(Memory_Leak_Data_t));
+
+ /* Calculate the address of the trailing sentinel. */
+ Ending_Sentinel =
+ (Sentinel_t *) ((unsigned int)ptr + sz);
+
+ /* Initialize our memory leak data. */
+ Memory_Leak_Data->Signature = LEAK_SIGNATURE;
+ Memory_Leak_Data->User_Size = sz;
+ Memory_Leak_Data->Alignment = Alignment;
+ Memory_Leak_Data->Module_Name = mod_name;
+ Memory_Leak_Data->Function_Name = func;
+ Memory_Leak_Data->Line_Number = line;
+ Memory_Leak_Data->Previous = NULL;
+ Memory_Leak_Data->Next = NULL;
+ Memory_Leak_Data->Starting_Sentinel = SENTINEL_VALUE;
+ *Ending_Sentinel = SENTINEL_VALUE;
+
+ USER3_PRINT_LINE
+ ("MALLOC: Allocated header at %p ( = user address %p) size[%u] in module %s, function %s at line %d\n",
+ Memory_Leak_Data, ptr, sz, mod_name, func, line);
+ Add_Memory_Allocation_To_Chain(Memory_Leak_Data);
+ USER3_PRINT_LINE
+ ("MALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n",
+ Memory_Chain, Memory_Chain_Entries);
+
+ /* Set up return value. */
+ *Memory_Location = ptr;
+ } else {
+
+ fprintf(stderr,
+ "MALLOC: Memory allocation failed - no more memory available at this time!\n");
+ print_backtrace();
+
+ USER3_PRINT_LINE
+ ("MALLOC: request was issued from module %s, function %s at line %d\n",
+ mod_name, func, line);
+
+ }
+
+ } else {
+ fprintf(stderr,
+ "MALLOC: Found the memory leak data to be corrupted! Aborting!\n");
+ print_backtrace();
+ }
+
+ /* Release lock. */
+ pthread_mutex_unlock(&Memory_Leak_Lock);
+
+ API_FUNCTION_RETURN("%i", Error)
+
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: MALLOC_func */
+/* */
+/* Descriptive Name: This function acts as a replacement for */
+/* malloc. It allocates memory (using malloc) */
+/* and enters that memory into a tracking */
+/* structure so that memory leaks, if any, may */
+/* be found. */
+/* */
+/* Input: size_t sz - The number of bytes to be allocated. */
+/* const char * mod_name - The name of the module from which*/
+/* this function was called. */
+/* const char * func - The name of the function from which */
+/* this function was called. */
+/* const int line - The line number of the code containing */
+/* the call to this function. */
+/* */
+/* Output: If Success : The function return value will be non-NULL.*/
+/* */
+/* If Failure : The function return value will be NULL. */
+/* Errors may be reported on stderr. */
+/* */
+/* Error Handling: This function will abort if an error is */
+/* is detected. Errors could be lack of memory, or*/
+/* corruption of the internal structures used to */
+/* track allocated blocks of memory. */
+/* */
+/* Side Effects: Memory may be allocated, errors may be reported */
+/* on stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void *MALLOC_func(size_t sz, const char *mod_name, const char *func,
+ const int line)
+{
+ void *Return_Value;
+ int Error;
+
+ Error = MEMORY_func(sz, 0, &Return_Value, mod_name, func, line);
+
+ if (Error != 0)
+ return NULL;
+ else
+ return Return_Value;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: FREE_func */
+/* */
+/* Descriptive Name: This function frees a block of memory being */
+/* tracked by this module and removes the block */
+/* from its tracking structures. */
+/* */
+/* Input: const void * p - The address of the block of memory to */
+/* be freed. */
+/* const char * mod_name - The name of the module requesting*/
+/* the block of memory be freed. */
+/* const char * func - The name of the function requesting */
+/* the block of memory be freed. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : None. */
+/* */
+/* If Failure : Errors may be reported to stderr. */
+/* */
+/* Error Handling: This function causes the internal structures */
+/* of this module to be checked as part of the */
+/* process of freeing the address p. This may */
+/* cause errors to be reported on stderr. If any */
+/* errors are found, then the address p may not be */
+/* freed. */
+/* */
+/* Side Effects: The block of memory associated with the address p */
+/* will be freed and available for reallocation. */
+/* Also, the memory tracking structures in this */
+/* module will undergo a series of checks. */
+/* */
+/* Notes: This function was not intended to be called directly but */
+/* rather through the macro FREE. */
+/* */
+/*********************************************************************/
+void FREE_func(const void *p, const char *mod_name, const char *func,
+ const int line)
+{
+ Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */
+
+ API_FUNCTION_ENTRY()
+
+ PRINT_API_PARAMETER("Parameters:\n")
+ PRINT_API_PARAMETER(" p = %p", p)
+ PRINT_API_PARAMETER(" mod_name = %s", mod_name)
+ PRINT_API_PARAMETER(" func = %s", func)
+ PRINT_API_PARAMETER(" line = %i", line)
+
+ /* Check the address passed to us. It must not be NULL, and must be > sizeof(Memory_Leak_Data_t) to
+ * prevent wrap around when we subtract sizeof(Memory_Leak_Data_t) to get the starting address of
+ * the memory leak data associated with p.
+ */
+ if ((p == NULL) || ((unsigned int)p <= sizeof(Memory_Leak_Data_t))) {
+ fprintf(stderr,
+ "FREE: request has invalid user address [%p]. Request came from module %s, function %s, line %d\n",
+ p, mod_name, func, line);
+ print_backtrace();
+ return;
+ } else {
+ USER3_PRINT_LINE
+ ("FREE: request to free [%p] received from module %s, function %s, line %d\n",
+ p, mod_name, func, line);
+ }
+
+ /* Get lock. */
+ pthread_mutex_lock(&Memory_Leak_Lock);
+
+ /* Check the memory allocation list for errors. */
+ if (!Check_Leak_List(NULL)) {
+
+ /* Is the address given to us in the memory allocation list? */
+ if (!Check_Leak_List(p)) {
+ /* Get access to the memory leak data. */
+ Memory_Leak_Data =
+ (Memory_Leak_Data_t *) ((unsigned int)p -
+ sizeof(Memory_Leak_Data_t));
+
+ USER3_PRINT_LINE
+ ("FREE: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n",
+ Memory_Leak_Data, p, mod_name, func, line);
+ Remove_Memory_Allocation_From_Chain(Memory_Leak_Data);
+ USER3_PRINT_LINE
+ ("FREE: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n",
+ Memory_Chain, Memory_Chain_Entries);
+
+ } else {
+ /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */
+ fprintf(stderr,
+ "FREE: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE more than once!\n",
+ p);
+ fprintf(stderr,
+ " Request came from module %s, function %s, line %d\n",
+ mod_name, func, line);
+ print_backtrace();
+ }
+
+ } else {
+ fprintf(stderr,
+ "FREE: Aborting due to errors in the memory leak data!\n");
+ USER3_PRINT_LINE
+ ("FREE: request was submitted by module %s, function %s at line %d\n",
+ mod_name, func, line);
+ print_backtrace();
+
+ }
+
+ pthread_mutex_unlock(&Memory_Leak_Lock);
+
+ USER3_PRINT_LINE("FREE: request is complete.\n");
+
+ API_FUNCTION_EXIT()
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Test_Address_Allocation */
+/* */
+/* Descriptive Name: This function tests the specified address to */
+/* to see if it lies within an allocated block */
+/* tracked by this module. */
+/* */
+/* Input: void * p - The address to be tested. */
+/* */
+/* Output: If Success : If the address p was found, then 0 will be */
+/* returned if the address is the start of */
+/* a block of allocated memory. If the */
+/* address p was found within an allocated */
+/* block of memory, then 1 is returned. */
+/* */
+/* If Failure : If the address p was NOT found, then 2 is */
+/* returned. If there was an error in the */
+/* memory tracking system then 3 will be */
+/* returned. */
+/* */
+/* Error Handling: This function relies on the error handling */
+/* built into the Check_Leak_List function and */
+/* has no error handling of its own. */
+/* */
+/* Side Effects: If the list of memory allocations contains errors */
+/* then those errors will be detected and reported */
+/* on stderr. */
+/* */
+/* Notes: If NULL is passed in as the address to test, then the */
+/* integrity of the internal tracking structures will be */
+/* checked, in which case a return value of 0 signifies */
+/* that the internal tracking structures have passed the */
+/* checks and a return value of 3 indicates that errors */
+/* were found. */
+/* */
+/*********************************************************************/
+unsigned int Test_Address_Allocation(void *p)
+{
+ unsigned int rc = 0;
+
+ API_FUNCTION_ENTRY()
+
+ PRINT_API_PARAMETER("Parameters:\n")
+ PRINT_API_PARAMETER(" p = %p", p)
+
+ /* Get lock. */
+ pthread_mutex_lock(&Memory_Leak_Lock);
+
+ rc = Check_Leak_List(p);
+
+ /* Release lock. */
+ pthread_mutex_unlock(&Memory_Leak_Lock);
+
+ API_FUNCTION_RETURN("%i", rc);
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Duplicate_String */
+/* */
+/* Descriptive Name: This function duplicates a string. The memory*/
+/* allocated for the duplicate is allocated */
+/* using the MALLOC_func routine in this module */
+/* and is thus tracked by this module. */
+/* */
+/* Input: const char * Source - The string to be copied. */
+/* const char * mod_name - The name of the module containing*/
+/* the function which called this */
+/* function. */
+/* const char * func - The name of the function calling */
+/* this function. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : The function return value will be non-NULL */
+/* and will point to a duplicate of the */
+/* string given in Source. */
+/* */
+/* If Failure : The function return value will be NULL. */
+/* */
+/* Error Handling: Any errors detected by this function result in */
+/* a function return value of NULL. */
+/* */
+/* Side Effects: The memory tracking features of this module are */
+/* employed to allocate memory for the duplicate */
+/* string produced by this funciton. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+char *Duplicate_String(const char *Source,
+ const char *mod_name, const char *func, const int line)
+{
+ char *Result;
+
+ Result = (char *)MALLOC_func(strlen(Source) + 1, mod_name, func, line);
+ if (Result != NULL)
+ strcpy(Result, Source);
+
+ return Result;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Realloc_func */
+/* */
+/* Descriptive Name: This function performs the same function as */
+/* the realloc function in the ANSI C library. */
+/* */
+/* Input: const void * p - The address of the block of memory to */
+/* be reallocated. */
+/* size_t size - The size of the memory block to return. */
+/* const char * mod_name - The name of the module requesting*/
+/* the block of memory be freed. */
+/* const char * func - The name of the function requesting */
+/* the block of memory be freed. */
+/* const int line - The line number of the line of code in */
+/* module calling this function. */
+/* */
+/* Output: If Success : The function return value will be a pointer*/
+/* to the new block of memory. */
+/* */
+/* If Failure : NULL will be returned and errno will be set*/
+/* to a non-null error code. */
+/* */
+/* Error Handling: This function causes the internal structures */
+/* of this module to be checked. This may */
+/* cause errors to be reported on stderr. If any */
+/* errors are found, then the address p may not be */
+/* freed. */
+/* */
+/* Side Effects: A new block of memory of size bytes will be */
+/* allocated, the contents of the current block will */
+/* be copied to the new block (at least as much as */
+/* will fit, and the current block will be freed. */
+/* This will cause internal structures in this module*/
+/* to be modified accordingly. */
+/* */
+/* Notes: This function was not intended to be called directly but */
+/* rather through the macro REALLOC. */
+/* */
+/* If p is NULL, then this will cause this function to */
+/* behave like malloc. */
+/* */
+/* If size is 0, then this will cause this function to */
+/* behave like free. */
+/* */
+/*********************************************************************/
+void *Realloc_func(void *p, size_t size, const char *mod_name, const char *func,
+ const int line)
+{
+ Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */
+ int Error = 0;
+ unsigned int Copy_Size = 0;
+ void *Return_Value = NULL;
+
+ API_FUNCTION_ENTRY()
+
+ PRINT_API_PARAMETER("Parameters:\n")
+ PRINT_API_PARAMETER(" p = %p", p)
+ PRINT_API_PARAMETER(" size = %u", size)
+ PRINT_API_PARAMETER(" mod_name = %s", mod_name)
+ PRINT_API_PARAMETER(" func = %s", func)
+ PRINT_API_PARAMETER(" line = %i", line)
+
+ /* Check the address passed to us. If it is NULL and size > 0, then we are merely doing a malloc. */
+ if ((p == NULL) && (size > 0)) {
+ USER3_PRINT_LINE
+ ("REALLOC: p was NULL and size > 0, acting as MALLOC.\n")
+ Return_Value = MALLOC_func(size, mod_name, func, line);
+ if (Return_Value == NULL) {
+ errno = ENOMEM;
+ }
+ } else {
+ /* If size is 0 and p is not NULL, then we are doing a free. */
+ if ((p != NULL) && (size == 0)) {
+ USER3_PRINT_LINE
+ ("REALLOC: p was non-NULL but size is 0, acting as FREE.\n")
+ FREE_func(p, mod_name, func, line);
+ } else {
+ /* Do we have real work to do? */
+ if ((p != NULL) && (size != 0)) {
+ USER3_PRINT_LINE
+ ("REALLOC: p was non-NULL and size > 0, we have actual work to do!.\n")
+
+ /* Get lock. */
+ pthread_mutex_lock(&Memory_Leak_Lock);
+
+ /* Check the memory allocation list for errors. */
+ if (!Check_Leak_List(NULL)) {
+
+ /* Is the address given to us in the memory allocation list? */
+ if (!Check_Leak_List(p)) {
+ /* Get access to the memory leak data. */
+ Memory_Leak_Data =
+ (Memory_Leak_Data_t
+ *) ((unsigned int)p -
+ sizeof
+ (Memory_Leak_Data_t));
+
+ /* Release the lock so that MEMORY_func can get it. */
+ pthread_mutex_unlock
+ (&Memory_Leak_Lock);
+
+ /* Call MEMORY_func to get the memory we need and add it to the list of memory blocks we are tracking. */
+ Error =
+ MEMORY_func(size,
+ Memory_Leak_Data->
+ Alignment,
+ &Return_Value,
+ mod_name, func,
+ line);
+ if (Error != 0) {
+ Return_Value = NULL;
+ errno = ENOMEM;
+ } else {
+
+ /* We have the replacement memory. Now lets copy what we can from the original block to the new block. */
+ if (size >
+ Memory_Leak_Data->
+ User_Size) {
+ Copy_Size =
+ Memory_Leak_Data->
+ User_Size;
+ } else {
+ Copy_Size =
+ size;
+ }
+
+ /* Copy the data to be preserved from the original memory block to the new memory block. */
+ memcpy(Return_Value, p,
+ Copy_Size);
+
+ /* Zero out the original memory block ... this can catch errors as users should not assume that
+ * realloc will leave their original memory block intact.
+ */
+ memset(p, 0x0,
+ Memory_Leak_Data->
+ User_Size);
+
+ /* Get the lock again. */
+ pthread_mutex_lock
+ (&Memory_Leak_Lock);
+
+ USER3_PRINT_LINE
+ ("REALLOC: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n",
+ Memory_Leak_Data,
+ p, mod_name, func,
+ line);
+ Remove_Memory_Allocation_From_Chain
+ (Memory_Leak_Data);
+ USER3_PRINT_LINE
+ ("REALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n",
+ Memory_Chain,
+ Memory_Chain_Entries);
+
+ }
+
+ } else {
+ /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */
+ fprintf(stderr,
+ "REALLOC: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE already!\n",
+ p);
+ fprintf(stderr,
+ " Request came from module %s, function %s, line %d\n",
+ mod_name, func, line);
+ print_backtrace();
+ }
+
+ } else {
+ fprintf(stderr,
+ "REALLOC: Aborting due to errors in the memory leak data!\n");
+ USER3_PRINT_LINE
+ ("REALLOC: request was submitted by module %s, function %s at line %d\n",
+ mod_name, func, line);
+ print_backtrace();
+
+ }
+
+ pthread_mutex_unlock(&Memory_Leak_Lock);
+
+ }
+
+ }
+
+ }
+
+ USER3_PRINT_LINE("REALLOC: request is complete.\n");
+
+ API_FUNCTION_RETURN("%p", Return_Value)
+}
diff --git a/clib/src/misc.c b/clib/src/misc.c
new file mode 100644
index 0000000..c79bd13
--- /dev/null
+++ b/clib/src/misc.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: misc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 07/26/09
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "misc.h"
+
+inline void prefetch(void *addr, size_t len, ...)
+{
+ va_list v;
+ va_start(v, len);
+ int w = va_arg(v, int);
+ int l = va_arg(v, int);
+ va_end(v);
+
+ while (0 < len) {
+ if (w) {
+ switch (l) {
+ case 0:
+ __builtin_prefetch(addr, 1, 0);
+ break;
+ case 1:
+ __builtin_prefetch(addr, 1, 1);
+ break;
+ case 2:
+ __builtin_prefetch(addr, 1, 2);
+ break;
+ }
+ } else {
+ switch (l) {
+ case 0:
+ __builtin_prefetch(addr, 0, 0);
+ break;
+ case 1:
+ __builtin_prefetch(addr, 0, 1);
+ break;
+ case 2:
+ __builtin_prefetch(addr, 0, 2);
+ break;
+ }
+ }
+ len -= sizeof addr;
+ }
+}
+
+unsigned long align(unsigned long size, unsigned long offset)
+{
+ --offset;
+ return (size + offset) & ~offset;
+}
+
+void print_binary(FILE * __out, void *__data, size_t __len)
+{
+ static const char *__ascii[] = {
+ "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
+ "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111",
+ };
+
+ if (__data == NULL)
+ return;
+ if (__out == NULL)
+ __out = stdout;
+
+ size_t i;
+ for (i = 0; i < __len; i++) {
+ unsigned char c = *(unsigned char *)(__data + i);
+
+ fprintf(__out, "%4.4s%4.4s",
+ __ascii[(c & 0xF0) >> 4], __ascii[c & 0x0F]);
+ }
+}
+
+#if 0
+void dump_memory(FILE * file, unsigned long addr, const void *buf, size_t size)
+{
+ if (size <= 0 || buf == NULL)
+ return;
+ if (file == NULL)
+ file = stdout;
+
+ unsigned long *ul = (unsigned long *)buf;
+ char hex[64] = { 0, }, ascii[32] = {
+ 0,}, c;
+ int hl = 0, al = 0;
+
+ int cnt = size / sizeof(unsigned long);
+
+ int i;
+ for (i = 0; i < cnt; i++) {
+ hl += snprintf(hex + hl, sizeof hex, "%08lx", ul[i]);
+
+ c = (ul[i] & 0xFF000000) >> 24;
+ al +=
+ snprintf(ascii + al, sizeof ascii, "%c",
+ (isprint(c) ? c : '.'));
+
+ c = (ul[i] & 0x00FF0000) >> 16;
+ al +=
+ snprintf(ascii + al, sizeof ascii, "%c",
+ (isprint(c) ? c : '.'));
+
+ c = (ul[i] & 0x0000FF00) >> 8;
+ al +=
+ snprintf(ascii + al, sizeof ascii, "%c",
+ (isprint(c) ? c : '.'));
+
+ c = (ul[i] & 0x000000FF) >> 0;
+ al +=
+ snprintf(ascii + al, sizeof ascii, "%c",
+ (isprint(c) ? c : '.'));
+
+ if ((i + 1) % 4 == 0) {
+ fprintf(file, " %08lx [%s] [%s]\n", addr, hex, ascii);
+
+ addr += 4 * sizeof(unsigned long);
+ hl = al = 0;
+
+ memset(hex, 0, sizeof hex);
+ memset(ascii, 0, sizeof ascii);
+ } else {
+ hl += snprintf(hex + hl, sizeof hex, " ");
+ }
+ }
+
+ int j;
+ for (j = 0; j < (i % 4); j++) {
+ hl += snprintf(hex + hl, sizeof hex, "%08lx ", 0ul);
+ al += snprintf(ascii + al, sizeof ascii, "%4.4s", "....");
+ }
+
+ if (i % 4 != 0) {
+ fprintf(file, " %08lx [%s] [%s]\n", addr, hex, ascii);
+ }
+
+ return;
+}
+#endif
+
+/*
+ * "aaaaaaaa [wwwwwwww xxxxxxxx yyyyyyyy zzzzzzzz] [........ ........]"
+ */
+void dump_memory(FILE * __out, uint32_t __addr, const void *__restrict __buf,
+ size_t __buf_sz)
+{
+ if (__buf_sz <= 0 || __buf == NULL)
+ return;
+ if (__out == NULL)
+ __out = stdout;
+
+ size_t hex_size = 16 * 2; // 16 bytes per doubleword
+ size_t ascii_size = 8 * 2; // 8 bytes per doubleword
+
+ uint8_t hex[hex_size + 1], ascii[hex_size + 1];
+ memset(hex, '.', hex_size), memset(ascii, '.', ascii_size);
+
+ void print_line(void) {
+ fprintf(__out, "%08x ", __addr);
+
+ fprintf(__out, "[%.8s %.8s %.8s %.8s] ",
+ hex + 0, hex + 8, hex + 16, hex + 24);
+
+ fprintf(__out, "[%.8s %.8s]\n", ascii + 0, ascii + 8);
+ }
+
+ size_t s = __addr % 16, i = 0;
+ __addr &= ~0xF;
+
+ for (i = s; i < __buf_sz + s; i++) {
+ unsigned char c = ((unsigned char *)__buf)[i - s];
+
+ hex[((i << 1) + 0) % hex_size] = "0123456789abcdef"[c >> 4];
+ hex[((i << 1) + 1) % hex_size] = "0123456789abcdef"[c & 0xf];
+
+ ascii[i % ascii_size] = isprint(c) ? c : '.';
+
+ if (i == 0)
+ continue;
+
+ if ((i + 1) % ascii_size == 0) {
+ print_line();
+ memset(hex, '.', hex_size), memset(ascii, '.',
+ ascii_size);
+
+ __addr += ascii_size;
+ }
+ }
+
+ if (i % ascii_size)
+ print_line();
+
+ return;
+}
+
+int __round_pow2(int size)
+{
+ size--;
+
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+
+ return ++size;
+}
diff --git a/clib/src/mq.c b/clib/src/mq.c
new file mode 100644
index 0000000..6faf108
--- /dev/null
+++ b/clib/src/mq.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: mqueue.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: POSIX message queue wrapper
+ * Note:
+ * Date: 10/07/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "libclib.h"
+#include "mq.h"
+
+#define MQUEUE_ROOT "/dev/mqueue"
+
+/* ======================================================================= */
+
+int mqueue_init(mqueue_t * self, const char *service)
+{
+ assert(self != NULL);
+
+ self->service = strdup(service);
+ self->in = self->out = (mqd_t) - 1;
+
+ return 0;
+}
+
+int mqueue_create(mqueue_t * self, pid_t tid)
+{
+ assert(self != NULL);
+
+ char path[pathconf(MQUEUE_ROOT, _PC_PATH_MAX)];
+
+ sprintf(path, "%s/%d->%s", MQUEUE_ROOT, tid, self->service);
+
+ self->out = open(path, O_WRONLY | O_CREAT, S_IWUSR);
+ if (self->out == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ sprintf(path, "%s/%d<-%s", MQUEUE_ROOT, tid, self->service);
+
+ self->in = open(path, O_RDONLY | O_CREAT, S_IRUSR);
+ if (self->in == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int mqueue_open(mqueue_t * self, char *path)
+{
+ assert(self != NULL);
+
+ if (path != NULL) {
+ char *endp = NULL;
+ (void)strtol(path + 1, &endp, 10);
+
+ if (strncmp(endp, "->", 2) == 0) {
+ self->in = mq_open((char *)path, O_RDONLY,
+ S_IRWXU, NULL);
+ if (self->in == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+ } else if (strncmp(endp, "<-", 2) == 0) {
+ self->out = mq_open((char *)path, O_WRONLY,
+ S_IRWXU, NULL);
+ if (self->out == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+ } else {
+ UNEXPECTED("'%s' invalid service", path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int mqueue_close(mqueue_t * self, char *path)
+{
+ assert(self != NULL);
+
+ if (path != NULL) {
+ char *endp = NULL;
+ (void)strtol(path + 1, &endp, 10);
+
+ if (strncmp(endp, "->", 2) == 0) {
+ if (self->in != (mqd_t) - 1)
+ mq_close(self->in), self->in = (mqd_t) - 1;
+ } else if (strncmp(endp, "<-", 2) == 0) {
+ if (self->out != (mqd_t) - 1)
+ mq_close(self->out), self->out = (mqd_t) - 1;
+ } else {
+ UNEXPECTED("'%s' invalid service", path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+mqueue_attr_t mqueue_getattr(mqueue_t * self)
+{
+ assert(self != NULL);
+
+ mqueue_attr_t attr;
+ mq_getattr(self->in, &attr);
+
+ return attr;
+}
+
+int mqueue_delete(mqueue_t * self)
+{
+ assert(self != NULL);
+
+ char path[pathconf(MQUEUE_ROOT, _PC_PATH_MAX)];
+ if (self->in != (mqd_t) - 1) {
+ sprintf(path, "%s/%d->%s",
+ MQUEUE_ROOT, gettid(), self->service);
+ unlink(path);
+ if (mq_close(self->in) == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+ self->in = (mqd_t) - 1;
+ }
+
+ if (self->out != (mqd_t) - 1) {
+ sprintf(path, "%s/%d<-%s",
+ MQUEUE_ROOT, gettid(), self->service);
+ unlink(path);
+ if (mq_close(self->in) == (mqd_t) - 1) {
+ ERRNO(errno);
+ return -1;
+ }
+ self->out = (mqd_t) - 1;
+ }
+
+ if (self->service) {
+ free((void *)self->service);
+ self->service = NULL;
+ }
+
+ return 0;
+}
+
+int mqueue_send(mqueue_t * self, void *ptr, size_t len)
+{
+ assert(self != NULL);
+
+ int rc = mq_send(self->out, (char *)ptr, len, 0);
+ if (rc == -1) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return rc;
+}
+
+int mqueue_receive(mqueue_t * self, void *ptr, size_t len)
+{
+ assert(self != NULL);
+
+ int rc = mq_receive(self->in, (char *)ptr, len, 0);
+ if (rc == -1) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return rc;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/signal.c b/clib/src/signal.c
new file mode 100644
index 0000000..ed75c05
--- /dev/null
+++ b/clib/src/signal.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: signal.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/03/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "misc.h"
+#include "signal.h"
+
+/* ======================================================================= */
+
+const char *__signal_msg[] = {
+ "signal: unexpected NULL self pointer",
+ "signal: unexpected NULL callback structure",
+};
+
+#define SIGNAL_NULL (__signal_msg[0])
+
+/* ======================================================================= */
+
+void signal_init(signal_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(SIGNAL_NULL);
+
+ if (self->fd != -1)
+ close(self->fd), self->fd = -1;
+
+ sigemptyset(&self->mask);
+ self->flags = SFD_CLOEXEC;
+
+ self->fd = signalfd(self->fd, &self->mask, self->flags);
+ if (unlikely(self->fd == -1))
+ throw_errno(errno);
+}
+
+void signal_delete(signal_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(SIGNAL_NULL);
+
+ close(self->fd), self->fd = -1;
+
+ if (sigprocmask(SIG_UNBLOCK, &self->mask, NULL) == -1)
+ throw_errno(errno);
+
+ sigemptyset(&self->mask);
+ self->flags = 0;
+}
+
+int signal_fileno(signal_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(SIGNAL_NULL);
+ return self->fd;
+}
+
+int signal_setmask(signal_t * self, const sigset_t mask)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(SIGNAL_NULL);
+
+ self->mask = mask;
+
+ if (sigprocmask(SIG_BLOCK, &self->mask, NULL) == -1)
+ throw_errno(errno);
+
+ self->fd = signalfd(self->fd, &self->mask, self->flags);
+ if (unlikely(self->fd == -1))
+ throw_errno(errno);
+
+ return self->fd;
+}
+
+#if 0
+void signal_wait1(signal_t * self)
+{
+ signal_wait2(self, -1);
+}
+
+void signal_wait2(signal_t * self, int timeout)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(SIGNAL_NULL);
+
+ struct epoll_event events[signal_EVENT_SIZE];
+
+ int rc = epoll_wait(self->fd, events,
+ signal_EVENT_SIZE, timeout);
+
+ for (int n = 0; n < rc; n++) {
+ signal_callback *cb = (signal_callback *) events[n].data.ptr;
+ if (cb != NULL)
+ if (cb->func != NULL)
+ cb->func((signal_event[1]) { {
+ events[n].data.u64 >> 32,
+ events[n].events}
+ }
+ , cb->data);
+ }
+}
+#endif
+
+/* ======================================================================= */
diff --git a/clib/src/slab.c b/clib/src/slab.c
new file mode 100644
index 0000000..f9146af
--- /dev/null
+++ b/clib/src/slab.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: slab.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Slab based allocator
+ * Note:
+ * Date: 06/05/10
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "tree_iter.h"
+
+#include "slab.h"
+
+/* ======================================================================= */
+
+/*! @cond */
+#define SLAB_NODE_MAGIC "SLND"
+
+#define SLAB_NODE_MAGIC_CHECK(m) ({ \
+ bool rc = (((m)[0] != SLAB_NODE_MAGIC[0]) || \
+ ((m)[1] != SLAB_NODE_MAGIC[1]) || \
+ ((m)[2] != SLAB_NODE_MAGIC[2]) || \
+ ((m)[3] != SLAB_NODE_MAGIC[3])); \
+ rc; \
+})
+
+typedef struct slab_node slab_node_t;
+
+struct slab_node {
+ uint8_t magic[4];
+
+ size_t free;
+ tree_node_t node;
+
+ uint32_t bitmap[];
+};
+
+#define SLAB_ALLOC_COUNT(s) ((s)->hdr.data_size / (s)->hdr.alloc_size)
+
+#define SLAB_PAGE_MAX UINT16_MAX
+#define SLAB_PAGE_DIVISOR 32
+/*! @endcond */
+
+/* ======================================================================= */
+
+static slab_node_t *__slab_grow(slab_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC));
+
+ slab_node_t *node = NULL;
+ if (posix_memalign((void **)&node, self->hdr.align_size,
+ self->hdr.page_size) < 0) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ memset(node, 0, self->hdr.page_size);
+
+ node->magic[0] = SLAB_NODE_MAGIC[0];
+ node->magic[1] = SLAB_NODE_MAGIC[1];
+ node->magic[2] = SLAB_NODE_MAGIC[2];
+ node->magic[3] = SLAB_NODE_MAGIC[3];
+
+ node->free = SLAB_ALLOC_COUNT(self);
+
+ tree_node_init(&node->node, (const void *)int32_hash1((int32_t) node));
+ splay_insert(&self->tree, &node->node);
+
+ self->hdr.page_count++;
+
+ return node;
+}
+
+/* ======================================================================= */
+
+int slab_init3(slab_t * self, const char *name, size_t alloc_size)
+{
+ size_t page_size = max(sysconf(_SC_PAGESIZE),
+ __round_pow2(alloc_size * SLAB_PAGE_DIVISOR));
+ return slab_init5(self, name, alloc_size, page_size, page_size);
+}
+
+int slab_init4(slab_t * self, const char *name, size_t alloc_size,
+ size_t page_size)
+{
+ size_t align_size = (size_t) sysconf(_SC_PAGESIZE);
+ return slab_init5(self, name, alloc_size, page_size, align_size);
+}
+
+int slab_init5(slab_t * self, const char *name, size_t alloc_size,
+ size_t page_size, size_t align_size)
+{
+ assert(self != NULL);
+
+ if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC) == false))
+ slab_delete(self);
+
+ alloc_size = align(alloc_size, sizeof(void *));
+ if (alloc_size < SLAB_ALLOC_MIN || SLAB_ALLOC_MAX < alloc_size) {
+ UNEXPECTED("alloc_size out of range [%d..%d]",
+ SLAB_ALLOC_MIN, SLAB_ALLOC_MAX);
+ return -1;
+ }
+
+ page_size = __round_pow2(page_size);
+ if (page_size / alloc_size < SLAB_PAGE_DIVISOR) {
+ UNEXPECTED("page_size out of range [%d..%d]",
+ alloc_size * SLAB_PAGE_DIVISOR, SLAB_PAGE_MAX);
+ return -1;
+ }
+
+ size_t __page_size = (size_t) sysconf(_SC_PAGESIZE);
+
+ align_size = __round_pow2(align_size);
+ if (align_size % __page_size) {
+ UNEXPECTED("align_size must be 0x%x aligned", __page_size);
+ return -1;
+ }
+
+ memset(self, 0, sizeof *self);
+
+ self->hdr.id[IDENT_MAGIC_0] = SLAB_MAGIC[IDENT_MAGIC_0];
+ self->hdr.id[IDENT_MAGIC_1] = SLAB_MAGIC[IDENT_MAGIC_1];
+ self->hdr.id[IDENT_MAGIC_2] = SLAB_MAGIC[IDENT_MAGIC_2];
+ self->hdr.id[IDENT_MAGIC_3] = SLAB_MAGIC[IDENT_MAGIC_3];
+
+ self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR;
+ self->hdr.id[IDENT_MINOR] = CLIB_MINOR;
+ self->hdr.id[IDENT_PATCH] = CLIB_PATCH;
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= SLAB_FLAG_LSB;
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= SLAB_FLAG_MSB;
+
+ if (name != NULL && *name != '\0')
+ strncpy(self->hdr.name, name, sizeof(self->hdr.name));
+
+ self->hdr.page_size = page_size;
+ self->hdr.align_size = align_size;
+ self->hdr.alloc_size = alloc_size;
+
+ self->hdr.bitmap_size =
+ align(page_size / alloc_size, CHAR_BIT * sizeof(uint32_t));
+ self->hdr.bitmap_size /= CHAR_BIT;
+
+ self->hdr.data_size =
+ self->hdr.page_size - sizeof(slab_node_t) - self->hdr.bitmap_size;
+
+ tree_init(&self->tree, default_compare);
+
+ return 0;
+}
+
+int slab_delete(slab_t * self)
+{
+ if (unlikely(self == NULL))
+ return 0;
+
+ if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC))) {
+ UNEXPECTED("'%s' invalid or corrupt slab object",
+ self->hdr.name);
+ return -1;
+ }
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ slab_node_t *node;
+ tree_for_each(&it, node, node) {
+ if (SLAB_NODE_MAGIC_CHECK(node->magic)) {
+ UNEXPECTED("'%s' invalid or corrupt slab_node object "
+ "=> '%.4s'", self->hdr.name, node->magic);
+ return -1;
+ }
+
+ if (splay_remove(&self->tree, &node->node) < 0)
+ return -1;
+ memset(node, 0, sizeof(*node));
+
+ free(node);
+ }
+
+ memset(self, 0, sizeof(*self));
+
+ return 0;
+}
+
+void *slab_alloc(slab_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC));
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ slab_node_t *node;
+ tree_for_each(&it, node, node)
+ if (0 < node->free)
+ break;
+
+ if (unlikely(tree_iter_elem(&it) == NULL))
+ node = __slab_grow(self);
+
+ assert(node != NULL);
+ assert(0 < node->free);
+
+ void *ptr = NULL;
+
+ uint32_t map_pos;
+ for (map_pos = 0; map_pos < self->hdr.bitmap_size; map_pos++)
+ if (node->bitmap[map_pos] != UINT32_MAX)
+ break;
+
+ if (unlikely(node->bitmap[map_pos] == UINT32_MAX)) {
+ UNEXPECTED("'%s' cache is corrupted", self->hdr.name);
+ return NULL;
+ }
+ if (unlikely(self->hdr.bitmap_size <= map_pos)) {
+ UNEXPECTED("'%s' cache is corrupted", self->hdr.name);
+ return NULL;
+ }
+
+ uint32_t bit = clzl(~node->bitmap[map_pos]);
+ uint32_t mask = 0x80000000 >> bit;
+
+ if (unlikely((node->bitmap[map_pos] & mask) == mask)) {
+ UNEXPECTED("'%s' cache is corrupted", self->hdr.name);
+ return NULL;
+ }
+
+ node->bitmap[map_pos] |= mask;
+ node->free--;
+
+ ptr = (void *)node->bitmap + self->hdr.bitmap_size +
+ (map_pos * INT32_BIT + bit) * self->hdr.alloc_size;
+
+ return ptr;
+}
+
+int slab_free(slab_t * self, void *ptr)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC));
+
+ if (unlikely(ptr == NULL))
+ return 0;
+
+ slab_node_t *node = (slab_node_t *) ((uintptr_t) ptr &
+ ~(self->hdr.page_size - 1));
+ assert(node != NULL);
+
+ if (unlikely(SLAB_NODE_MAGIC_CHECK(node->magic))) {
+ int32_t hash = int32_hash1((int32_t) node);
+ if (splay_find(&self->tree, (const void *)hash) == NULL) {
+ UNEXPECTED("'%s' invalid slab_node pointer, %p",
+ self->hdr.name, ptr);
+ return -1;
+ }
+ }
+
+ void *data = (void *)node->bitmap + self->hdr.bitmap_size;
+ assert(data != NULL);
+
+ if (unlikely(ptr < data)) {
+ UNEXPECTED("'%s' pointer out-of-range, %p",
+ self->hdr.name, ptr);
+ return -1;
+ }
+
+ size_t slot = (ptr - data) / self->hdr.alloc_size;
+ uint32_t mask = 0x80000000 >> slot;
+ size_t map_pos = slot / INT32_BIT;
+
+ if (unlikely((node->bitmap[map_pos] & mask) != mask)) {
+ UNEXPECTED("'%s' double free detected, %p",
+ self->hdr.name, ptr);
+ return -1;
+ }
+
+ node->bitmap[map_pos] &= ~mask;
+ node->free++;
+
+ if (SLAB_ALLOC_COUNT(self) - node->free <= 0) {
+ splay_remove(&self->tree, &node->node);
+ self->hdr.page_count = max(0UL, self->hdr.page_count - 1);
+ memset(node, 0, sizeof(*node));
+ free(node);
+ }
+
+ return 0;
+}
+
+size_t slab_alloc_size(slab_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.alloc_size;
+}
+
+size_t slab_page_size(slab_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.page_size;
+}
+
+size_t slab_data_size(slab_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.data_size;
+}
+
+size_t slab_bitmap_size(slab_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.bitmap_size;
+}
+
+size_t slab_align_size(slab_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.align_size;
+}
+
+void slab_dump(slab_t * self, FILE * out)
+{
+ if (out == NULL)
+ out = stdout;
+
+ if (self != NULL) {
+ if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC))) {
+ UNEXPECTED("'%s' invalid or corrupt slab object",
+ self->hdr.name);
+ return;
+ }
+
+ fprintf(out, "%s: page_size: %d bitmap_size: %d data_size: "
+ "%d alloc_size: %d -- page_count: %d\n",
+ self->hdr.name, self->hdr.page_size,
+ self->hdr.bitmap_size, self->hdr.data_size,
+ self->hdr.alloc_size, self->hdr.page_count);
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ slab_node_t *node;
+ tree_for_each(&it, node, node) {
+ fprintf(out, "magic[%.4s] node: %p bitmap: %p data: "
+ "%p -- alloc: %d free: %d\n",
+ node->magic, &node->node, node->bitmap,
+ (void *)node->bitmap + self->hdr.bitmap_size,
+ self->hdr.data_size / self->hdr.alloc_size -
+ node->free, node->free);
+
+ dump_memory(out, (unsigned long)node, node,
+ self->hdr.page_size);
+ }
+ }
+}
+
+/* ======================================================================= */
diff --git a/clib/src/table.c b/clib/src/table.c
new file mode 100644
index 0000000..108763e
--- /dev/null
+++ b/clib/src/table.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: table.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 08/21/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "table.h"
+#include "vector_iter.h"
+
+#define TABLE_PAGE_SIZE 4096
+#define TABLE_PAGE_DIVISOR 32
+
+/* ======================================================================= */
+int table_init(table_t * self, const char *name, size_t col_nr)
+{
+ assert(self != NULL);
+ assert(name != NULL);
+
+ if (col_nr == 0) {
+ UNEXPECTED("'%d' invalid column number", col_nr);
+ return -1;
+ }
+
+ self->hdr.id[IDENT_MAGIC_0] = TABLE_MAGIC[IDENT_MAGIC_0];
+ self->hdr.id[IDENT_MAGIC_1] = TABLE_MAGIC[IDENT_MAGIC_1];
+ self->hdr.id[IDENT_MAGIC_2] = TABLE_MAGIC[IDENT_MAGIC_2];
+ self->hdr.id[IDENT_MAGIC_3] = TABLE_MAGIC[IDENT_MAGIC_3];
+
+ self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR;
+ self->hdr.id[IDENT_MINOR] = CLIB_MINOR;
+ self->hdr.id[IDENT_PATCH] = CLIB_PATCH;
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= TABLE_FLAG_LSB;
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= TABLE_FLAG_MSB;
+
+ /* FIX ME -- handle this more elegantly */
+ //assert((col_nr * sizeof(value_t)) < TABLE_PAGE_SIZE);
+
+ self->hdr.col_nr = col_nr;
+
+ if (name == NULL || *name == '\0')
+ memset(self->hdr.name, 0, sizeof(self->hdr.name));
+ else
+ strncpy(self->hdr.name, name, sizeof(self->hdr.name));
+
+ char name_vector[strlen(self->hdr.name) + 5];
+
+ size_t row_size = self->hdr.col_nr * sizeof(value_t);
+
+ sprintf(name_vector, "%s.table", self->hdr.name);
+ vector_init(&self->table, name_vector, row_size,
+ __round_pow2(row_size * TABLE_PAGE_DIVISOR));
+
+ sprintf(name_vector, "%s.string", self->hdr.name);
+ vector_init(&self->string, name_vector, 1, TABLE_PAGE_SIZE);
+
+ sprintf(name_vector, "%s.blob", self->hdr.name);
+ vector_init(&self->blob, name_vector, 1, TABLE_PAGE_SIZE);
+
+ return 0;
+}
+
+int table_delete(table_t * self)
+{
+ if (unlikely(self == NULL))
+ return 0;
+
+ if (MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)) {
+ UNEXPECTED("'%2.2x%2.2x%2.2x%2.2x' invalid table magic "
+ "'%2.2x%2.2x%2.2x%2.2x",
+ self->hdr.id[IDENT_MAGIC_0],
+ self->hdr.id[IDENT_MAGIC_1],
+ self->hdr.id[IDENT_MAGIC_2],
+ self->hdr.id[IDENT_MAGIC_3],
+ TABLE_MAGIC[IDENT_MAGIC_0],
+ TABLE_MAGIC[IDENT_MAGIC_2],
+ TABLE_MAGIC[IDENT_MAGIC_3],
+ TABLE_MAGIC[IDENT_MAGIC_3]);
+ return -1;
+ }
+
+ if (0 < vector_size(&self->table)) {
+ vector_iter_t it;
+ if (vector_iter_init(&it, &self->table, VI_FLAG_FWD) < 0)
+ return -1;
+
+ value_t *v;
+ vector_for_each(&it, v)
+ for (size_t c = 0; c < self->hdr.col_nr; c++)
+ value_clear(v + c);
+ }
+
+ if (vector_delete(&self->table) < 0)
+ return -1;
+ if (vector_delete(&self->string) < 0)
+ return -1;
+ if (vector_delete(&self->blob) < 0)
+ return -1;
+
+ return 0;
+}
+
+value_t *table_get(table_t * self, size_t row, size_t col)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ row++; /* hide the column names */
+
+ return (value_t *) vector_at(&self->table,
+ row * self->hdr.col_nr + col);
+}
+
+value_t *table_row2(table_t * self, size_t row_nr)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ row_nr++; /* hide the column names */
+
+ return (value_t *) vector_at(&self->table, row_nr * self->hdr.col_nr);
+}
+
+int table_row3(table_t * self, size_t row_nr, value_t * row)
+{
+ assert(self != NULL);
+ assert(row != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ row_nr++; /* hide the column names */
+
+ if (vector_size(&self->table) <= row_nr + 1) {
+ if (vector_size(&self->table, row_nr + 1) < 0)
+ return -1;
+ } else {
+ value_t *old;
+ old = (value_t *) vector_at(&self->table, row_nr);
+ assert(old != NULL);
+
+ for (size_t col_nr = 0; col_nr < self->hdr.col_nr; col_nr++)
+ value_clear(old + col_nr);
+ }
+
+ if (vector_put(&self->table, row_nr, row) < 0)
+ return -1;
+
+ for (size_t col_nr = 0; col_nr < self->hdr.col_nr; col_nr++)
+ if (value_type(row + col_nr) == VT_STR ||
+ value_type(row + col_nr) == VT_BLOB)
+ value_type(row + col_nr, VT_UNKNOWN);
+
+ return 0;
+}
+
+value_t *table_column(table_t * self, value_t * row, size_t col)
+{
+ assert(self != NULL);
+ assert(row != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+ assert(col < self->hdr.col_nr);
+
+ return (void *)row + (vector_elem_size(&self->table) * col);
+}
+
+int table_put(table_t * self, size_t row, size_t col, value_t * val)
+{
+ assert(self != NULL);
+ assert(val != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ row++; /* hide the column names */
+
+ size_t size = (row + 1) * self->hdr.col_nr;
+
+ if (vector_size(&self->table) <= size)
+ if (vector_size(&self->table, size) < 0)
+ return -1;
+
+ /* free existing pointer data */
+ value_t *old = (value_t *) vector_at(&self->table,
+ row * self->hdr.col_nr + col);
+ assert(old != NULL);
+ value_clear(old);
+
+ vector_put(&self->table, row * self->hdr.col_nr + col, (void *)val, 1);
+
+ if ((value_type(val) == VT_STR) || (value_type(val) == VT_BLOB))
+ value_type(val, VT_UNKNOWN);
+
+ return 0;
+}
+
+const char *table_name2(table_t * self, size_t col_nr)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+ assert(col_nr < self->hdr.col_nr);
+
+ const char *rc = NULL;
+
+ value_t *row = (value_t *) vector_at(&self->table, 0);
+ if (row != NULL) {
+ value_t *col = row + col_nr;
+ switch (value_type(col)) {
+ case VT_STR_OFF:
+ rc = vector_at(&self->string, col->u64);
+ break;
+ case VT_STR_INLINE:
+ rc = (const char *)col->data;
+ break;
+ case VT_STR_CONST:
+ case VT_STR:
+ rc = value_string(col);
+ break;
+ default:
+ rc = NULL;
+ }
+ }
+
+ return rc;
+}
+
+int _table_name3(table_t * self, size_t col_nr, const char *name)
+{
+ return _table_name4(self, col_nr, name, strlen(name));
+}
+
+int _table_name4(table_t * self, size_t col_nr, const char *name, size_t len)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+ assert(col_nr < self->hdr.col_nr);
+
+ if (vector_size(&self->table) <= 0)
+ if (vector_size(&self->table, 1) < 0)
+ return -1;
+
+ value_t *row = (value_t *) vector_at(&self->table, 0);
+ if (row != NULL)
+ value_string(row + col_nr, name, len);
+
+ return 0;
+}
+
+size_t table_rows(table_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+ return vector_size(&self->table) - 1;
+}
+
+size_t table_columns(table_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+ return self->hdr.col_nr;
+}
+
+int table_serialize(table_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ vector_iter_t it;
+ vector_iter_init(&it, &self->table, VI_FLAG_FWD);
+
+ value_t *row;
+ vector_for_each(&it, row) {
+ for (size_t c = 0; c < self->hdr.col_nr; c++) {
+ value_t *col = row + c;
+
+ value_type_t type = value_type(col);
+ size_t len = value_size(col);
+
+ vector_t *vec = NULL;
+ if (type == VT_STR)
+ vec = &self->string, type = VT_STR_OFF, len++;
+ else if (type == VT_BLOB)
+ vec = &self->blob, type = VT_BLOB_OFF;
+ else
+ continue;
+
+ uint64_t off = vector_size(vec);
+
+ if (vector_size(vec, off + len) < 0)
+ return -1;
+ if (vector_put(vec, off, col->ptr, len) < 0)
+ return -1;
+
+ free(col->ptr), col->ptr = NULL;
+
+ col->u64 = off;
+ value_type(col, type);
+ }
+ }
+
+ return 0;
+}
+
+ssize_t table_save(table_t * self, FILE * out)
+{
+ assert(self != NULL);
+ assert(out != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ /* ============= */
+
+ int header_swap(table_header_t * hdr) {
+ assert(hdr != NULL);
+
+ if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_MSB) {
+ hdr->col_nr = htobe32(hdr->col_nr);
+ } else if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_LSB) {
+ hdr->col_nr = htole32(hdr->col_nr);
+ } else {
+ UNEXPECTED("'%s' invalid or corrupt table object => "
+ "'%x'", hdr->name, hdr->id[IDENT_FLAGS]);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /* ============= */
+
+ ssize_t len = 0;
+
+ table_header_t hdr = self->hdr;
+ if (header_swap(&hdr) < 0)
+ return -1;
+
+ clearerr(out);
+ len = fwrite(&self->hdr, 1, sizeof(self->hdr), out);
+ if (len != sizeof(self->hdr)) {
+ if (ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ ssize_t rc = vector_save(&self->table, out);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ rc = vector_save(&self->string, out);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ rc = vector_save(&self->blob, out);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ return len;
+}
+
+int table_deserialize(table_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ vector_iter_t it;
+ vector_iter_init(&it, &self->table, VI_FLAG_FWD);
+
+ value_t *row;
+ vector_for_each(&it, row) {
+ for (size_t c = 0; c < self->hdr.col_nr; c++) {
+ value_t *col = row + c;
+
+ value_type_t type = value_type(col);
+ size_t len = value_size(col);
+
+ switch (type) {
+ uint64_t off;
+ case VT_STR_OFF:
+ len++;
+ value_type(col, VT_STR);
+ case VT_BLOB_OFF:
+ off = col->u64;
+ col->ptr = malloc(len);
+
+ if (col->ptr == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (vector_get(&self->string, off, col->ptr,
+ len) < 0)
+ return -1;
+
+ if (value_type(col) == VT_BLOB_OFF)
+ value_type(col, VT_BLOB);
+ break;
+ case VT_STR:
+ case VT_BLOB:
+ case VT_STR_CONST:
+ UNEXPECTED("'%s' invalid or corrupt type %d",
+ self->hdr.name, type);
+ return -1;
+ default:
+ ;
+ }
+ }
+ }
+
+ if (vector_delete(&self->string) < 0)
+ return -1;
+ if (vector_delete(&self->blob) < 0)
+ return -1;
+
+ return 0;
+}
+
+ssize_t table_load(table_t * self, FILE * in)
+{
+ assert(self != NULL);
+
+ /* ============= */
+
+ int header_swap(table_header_t * hdr) {
+ assert(hdr != NULL);
+ if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_MSB) {
+ hdr->col_nr = be32toh(hdr->col_nr);
+ } else if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_LSB) {
+ hdr->col_nr = le32toh(hdr->col_nr);
+ } else {
+ UNEXPECTED("'%s' invalid or corrupt table object => "
+ "'%x'", hdr->name, hdr->id[IDENT_FLAGS]);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ int table_swap(table_t * tbl) {
+ vector_iter_t it;
+ if (vector_iter_init(&it, &tbl->table, VI_FLAG_FWD) < 0)
+ return -1;
+
+ value_t *row;
+ vector_for_each(&it, row) {
+ for (size_t c = 0; c < self->hdr.col_nr; c++) {
+ value_t *col = row + c;
+
+ value_type_t type = value_type(row);
+
+#define iBE(s) value_i##s(col, be##s##toh(value_i##s(col)))
+#define uBE(s) value_u##s(col, be##s##toh(value_u##s(col)))
+#define iLE(s) value_i##s(col, le##s##toh(value_i##s(col)))
+#define uLE(s) value_u##s(col, le##s##toh(value_u##s(col)))
+
+ if (tbl->hdr.id[IDENT_FLAGS] & TABLE_FLAG_MSB) {
+ switch (type) {
+ case VT_I16:
+ iBE(16);
+ break;
+ case VT_U16:
+ uBE(16);
+ break;
+ case VT_I32:
+ iBE(32);
+ break;
+ case VT_U32:
+ uBE(32);
+ break;
+ case VT_I64:
+ iBE(64);
+ break;
+ case VT_U64:
+ uBE(64);
+ break;
+ case VT_STR_OFF:
+ case VT_BLOB_OFF:
+ col->u64 = be64toh(col->u64);
+ break;
+ default:
+ ;
+ }
+ } else if (tbl->hdr.id[IDENT_FLAGS] &
+ TABLE_FLAG_LSB) {
+ switch (type) {
+ case VT_I16:
+ iLE(16);
+ break;
+ case VT_U16:
+ uLE(16);
+ break;
+ case VT_I32:
+ iLE(32);
+ break;
+ case VT_U32:
+ uLE(32);
+ break;
+ case VT_I64:
+ iLE(64);
+ break;
+ case VT_U64:
+ uLE(64);
+ break;
+ case VT_STR_OFF:
+ case VT_BLOB_OFF:
+ col->u64 = le64toh(col->u64);
+ break;
+ default:
+ ;
+ }
+ } else {
+ UNEXPECTED("'%s' invalid or corrupt "
+ "table object => '%x'",
+ tbl->hdr.name,
+ tbl->hdr.id[IDENT_FLAGS]);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /* ============= */
+
+ // zero'd table will cause a magic check
+ (void)table_delete(self);
+
+ clearerr(in);
+ size_t len = fread(&self->hdr, 1, sizeof(self->hdr), in);
+ if (len != sizeof(self->hdr)) {
+ if (feof(in)) {
+ UNEXPECTED("'%s' end-of-file encountered",
+ self->hdr.name);
+ return -1;
+ }
+ if (ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC));
+
+ if (header_swap(&self->hdr) < 0)
+ return -1;
+
+ ssize_t rc = vector_load(&self->table, in);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ if (table_swap(self) < 0)
+ return -1;
+
+ rc = vector_load(&self->string, in);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ rc = vector_load(&self->blob, in);
+ if (rc < 0)
+ return -1;
+ len += rc;
+
+ return len;
+}
+
+void table_print(table_t * self, FILE * out)
+{
+ if (self != NULL) {
+ if (unlikely(MAGIC_CHECK(self->hdr.id, TABLE_MAGIC))) {
+ UNEXPECTED("'%s' invalid or corrupt table object",
+ self->hdr.name);
+ return;
+ }
+
+ vector_iter_t it;
+ vector_iter_init(&it, &self->table, VI_FLAG_FWD);
+
+ value_t *row;
+ vector_for_each(&it, row)
+ for (size_t c = 0; c < self->hdr.col_nr; c++)
+ value_dump(row + c, out);
+
+ vector_dump(&self->string, out);
+ }
+}
+
+void table_dump(table_t * self, FILE * out)
+{
+ if (self != NULL) {
+ if (unlikely(MAGIC_CHECK(self->hdr.id, TABLE_MAGIC))) {
+ UNEXPECTED("'%s' invalid or corrupt table object",
+ self->hdr.name);
+ return;
+ }
+
+ fprintf(out,
+ "table: [ size: %d cols: %d rows: %d name: '%s']\n",
+ sizeof(value_t), table_columns(self), table_rows(self),
+ self->hdr.name);
+
+ dump_memory(out, (unsigned long)&self->hdr, &self->hdr,
+ sizeof(self->hdr));
+
+ vector_dump(&self->table, out);
+ vector_dump(&self->string, out);
+ vector_dump(&self->blob, out);
+ }
+}
+
+#if 0
+void table_sort(table_t * self, compare_f cmp)
+{
+ /* The exchange function swaps two rows within the table
+ * exchange(table_t * self, size_t i, size_t j)
+ */
+
+ /* The partition method receives a list or sublist, and places the first element
+ * in its correct position within the list. It also ensures that all elements to
+ * the left of this are smaller, and all to the right are larger.
+ *
+ * partition(a[], p, r)
+ * i = p
+ * j = r + 1
+ * pivot = a[p]
+ * do {
+ * do i = i + 1 while (a[i]<pivot)
+ * do j = j - 1 while (a[j]>pivot)
+ * if (i < j) exchange(a[i], a[j])
+ * } while (i<j)
+ * exchange(a[p], a[j])
+ * return j
+ */
+
+ /*
+ * quicksort(a[], p, r)
+ * if r > p then
+ * j = partition(a[], p, r)
+ * quicksort(a[], p, j-1)
+ * quicksort(a[], j+1, r)
+ *
+ */
+}
+#endif
+
+/* ======================================================================= */
diff --git a/clib/src/table_iter.c b/clib/src/table_iter.c
new file mode 100644
index 0000000..cb73573
--- /dev/null
+++ b/clib/src/table_iter.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: table_iter.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/22/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "assert.h"
+#include "misc.h"
+
+#include "table_iter.h"
+
+/* ======================================================================= */
+
+int table_iter_init(table_iter_t * self, table_t * table, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(table != NULL);
+
+ self->flags = flags;
+ self->table = table;
+
+ if (self->flags & TI_FLAG_BWD) {
+ if (vector_iter_init(&self->it, &table->table, VI_FLAG_BWD) < 0)
+ return -1;
+ } else {
+ if (vector_iter_init(&self->it, &table->table, VI_FLAG_FWD) < 0)
+ return -1;
+ }
+
+ return vector_iter_inc(&self->it) != NULL;
+}
+
+int table_iter_clear(table_iter_t * self)
+{
+ assert(self != NULL);
+
+ if (vector_iter_clear(&self->it) < 0)
+ return -1;
+
+ self->table = NULL;
+
+ return 0;
+}
+
+const value_t *table_iter_elem(table_iter_t * self)
+{
+ assert(self != NULL);
+ return (value_t *) vector_iter_elem(&self->it);
+}
+
+const value_t *table_iter_inc1(table_iter_t * self)
+{
+ return table_iter_inc2(self, 1);
+}
+
+const value_t *table_iter_inc2(table_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ const value_t *v = NULL;
+
+ for (size_t i = 0; i < count; i++) {
+ if (self->flags & TI_FLAG_BWD) {
+ v = (value_t *) vector_iter_dec(&self->it); // columns
+ } else {
+ v = (value_t *) vector_iter_inc(&self->it); // columns
+ }
+ }
+
+ return v;
+}
+
+const value_t *table_iter_dec1(table_iter_t * self)
+{
+ return table_iter_dec2(self, 1);
+}
+
+const value_t *table_iter_dec2(table_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ const value_t *v = NULL;
+
+ for (size_t i = 0; i < count; i++) {
+ if (self->flags & TI_FLAG_BWD) {
+ v = (value_t *) vector_iter_inc(&self->it); // columns
+ } else {
+ v = (value_t *) vector_iter_dec(&self->it); // columns
+ }
+ }
+
+ return v;
+}
+
+size_t table_iter_pos1(table_iter_t * self)
+{
+ assert(self != NULL);
+ return vector_iter_pos(&self->it) + 1;
+}
+
+int table_iter_pos2(table_iter_t * self, size_t pos)
+{
+ assert(self != NULL);
+ assert(pos < table_rows(self->table));
+ return vector_iter_pos(&self->it, pos + 1);
+}
+
+/* ======================================================================= */
diff --git a/clib/src/timer.c b/clib/src/timer.c
new file mode 100644
index 0000000..cb4d260
--- /dev/null
+++ b/clib/src/timer.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: timer.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/03/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "misc.h"
+#include "timer.h"
+
+#define TIMER_PAGE_SIZE 64
+#define TIMER_EVENT_SIZE 64
+
+/* ======================================================================= */
+
+const char *__timer_msg[] = {
+ "timer: unexpected NULL self pointer",
+ "timer: unexpected NULL callback structure",
+};
+
+#define TIMER_NULL (__timer_msg[0])
+#define TIMER_CALLBACK_NULL (__timer_msg[1])
+
+/* ======================================================================= */
+
+void timer_init(timer * self, int clock)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+
+ if (self->fd != -1)
+ close(self->fd), self->fd = -1;
+
+ self->fd = timerfd_create(clock, TFD_CLOEXEC);
+ if (unlikely(self->fd == -1))
+ throw_errno(errno);
+
+ vector_init(&self->callbacks, "callbacks",
+ sizeof(timer_callback), TIMER_PAGE_SIZE);
+}
+
+void timer_delete(timer * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+ close(self->fd), self->fd = -1;
+}
+
+int timer_fileno(timer * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+ return self->fd;
+}
+
+uint32_t timer_add(timer * self, timer_callback * cb)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+
+ if (access(path, F_OK) != 0)
+ throw_errno(errno);
+
+ uint32_t wd = inotify_add_timer(self->fd, path, events);
+ if (unlikely((int)wd == -1))
+ throw_errno(errno);
+
+ if (cb != NULL)
+ vcetor_put(&self->callbacks, wd, cb);
+
+ return wd;
+}
+
+void timer_remove(timer * self, uint32_t wd)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+
+ int rc = inotify_rm_timer(self->fd, wd);
+ if (unlikely(rc == -1))
+ throw_errno(errno);
+
+ array_status(&self->callbacks, wd, false);
+}
+
+void timer_wait(timer * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(TIMER_NULL);
+
+ /* FIX ME */
+
+ timer_event events[timer_EVENT_SIZE];
+
+ int n = read(self->fd, events, sizeof events);
+ printf("n[%d]\n", n);
+
+ for (int i = 0; i < (n / sizeof *events); i++)
+ printf("%d: wd[%d] mask[%x] cookie[%x] name[%.*s]\n",
+ i, events[i].wd, events[i].mask, events[i].cookie,
+ events[i].len, events[i].name);
+}
+
+/* ======================================================================= */
diff --git a/clib/src/trace_indent.c b/clib/src/trace_indent.c
new file mode 100644
index 0000000..c1dd280
--- /dev/null
+++ b/clib/src/trace_indent.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2011
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: B. Rafanello
+ *
+ * Module: trace_indent.c
+ *
+ * Implements indentation for the trace output from bb_trace
+ *
+ * Version 0.0.0.1
+ */
+
+#include "trace_indent.h"
+#include "stdio.h"
+
+/*--------------------------------------------------
+ * Private Constants
+ --------------------------------------------------*/
+
+#define DEFAULT_INDENT_SIZE 5
+#define MAX_INDENT_SIZE 129
+#define DEFAULT_INDENT " "
+
+/*--------------------------------------------------
+ * Global variables.
+ --------------------------------------------------*/
+
+/* The current trace level. */
+unsigned char BB_TRACE_LEVEL = 0;
+unsigned char BB_PREVIOUS_TRACE_LEVEL = 0;
+
+/*--------------------------------------------------
+ * Private global variables.
+ --------------------------------------------------*/
+
+static unsigned int Current_Indent_Level = 0;
+static unsigned int Current_Indent_Size = DEFAULT_INDENT_SIZE;
+static char Indent[MAX_INDENT_SIZE] = DEFAULT_INDENT;
+
+/*--------------------------------------------------
+ * Public Functions
+ --------------------------------------------------*/
+
+/*********************************************************************/
+/* */
+/* Function Name: Set_Indent_Size */
+/* */
+/* Descriptive Name: Sets the number of spaces to use per indent. */
+/* If the current indent level is 5 and this */
+/* function is called to set the indent size to */
+/* 10, then 50 spaces will be printed by the */
+/* trace macros before each line of trace */
+/* output at this indent level. */
+/* */
+/* Input: unsigned int Spaces_Per_Indent - A value less than 128. */
+/* */
+/* Output: If Success : The specified value will be used for the */
+/* size of an indent. */
+/* */
+/* If Failure : This should only happen if the value */
+/* specified is >= 128, in which case it is */
+/* ignored and the previous value is retained.*/
+/* */
+/* Error Handling: Bad values for Spaces_Per_Indent are ignored. */
+/* */
+/* Side Effects: The number of spaces per indent may be changed. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Set_Indent_Size(unsigned int Spaces_Per_Indent)
+{
+ unsigned int i;
+
+ if (Spaces_Per_Indent <= MAX_INDENT_SIZE) {
+ Current_Indent_Size = Spaces_Per_Indent;
+ for (i = 0; i < Current_Indent_Size; i++) {
+ Indent[i] = ' ';
+ }
+ Indent[i] = 0x00;
+ }
+
+ return;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Indent_Trace_Output */
+/* */
+/* Descriptive Name: This function increases the current indent */
+/* level by one. */
+/* */
+/* Input: None. */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: None */
+/* */
+/* Side Effects: The current indent level for trace output will */
+/* be increased by 1. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Indent_Trace_Output(void)
+{
+ Current_Indent_Level += 1;
+ return;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Outdent_Trace_Output */
+/* */
+/* Descriptive Name: This function reduces the current indent */
+/* level if it is greater than 0. */
+/* */
+/* Input: None */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: If the current indent level is zero, then this */
+/* function does nothing. */
+/* */
+/* Side Effects: The current indent level for trace output may be */
+/* decreased by 1. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Outdent_Trace_Output(void)
+{
+ if (Current_Indent_Level > 0)
+ Current_Indent_Level -= 1;
+ return;
+}
+
+/*********************************************************************/
+/* */
+/* Function Name: Do_Indent */
+/* */
+/* Descriptive Name: This function prints to stderr the number of */
+/* spaces corresponding to the current indent */
+/* level. */
+/* */
+/* Input: None */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: None */
+/* */
+/* Side Effects: Some number of space may be output to stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Do_Indent(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < Current_Indent_Level; i++) {
+ fprintf(stderr, "%s", Indent);
+ }
+
+}
diff --git a/clib/src/tree.c b/clib/src/tree.c
new file mode 100644
index 0000000..820f288
--- /dev/null
+++ b/clib/src/tree.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: tree.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 08/21/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "tree.h"
+#include "slab.h"
+
+/* ======================================================================= */
+
+tree_node_t *tree_node_prev(tree_node_t * self)
+{
+ assert(self != NULL);
+
+ if (tree_node_left(self) != NULL) {
+ self = tree_node_left(self);
+
+ while (tree_node_right(self) != NULL)
+ self = tree_node_right(self);
+ } else {
+ tree_node_t *parent = tree_node_parent(self);
+
+ while (parent != NULL && self == tree_node_left(parent))
+ self = parent, parent = tree_node_parent(parent);
+
+ self = parent;
+ }
+
+ return self;
+}
+
+tree_node_t *tree_node_next(tree_node_t * self)
+{
+ assert(self != NULL);
+
+ if (tree_node_right(self) != NULL) {
+ self = tree_node_right(self);
+
+ while (self != NULL && tree_node_left(self) != NULL)
+ self = tree_node_left(self);
+ } else {
+ tree_node_t *parent = tree_node_parent(self);
+
+ while (parent != NULL && self == tree_node_right(parent))
+ self = parent, parent = tree_node_parent(parent);
+
+ self = parent;
+ }
+
+ return self;
+}
+
+/* ======================================================================= */
+
+int tree_init(tree_t * self, compare_f compare)
+{
+ assert(self != NULL);
+ assert(compare != NULL);
+
+ self->root = NULL;
+ self->min = self->max = NULL;
+ self->compare = compare;
+ self->size = 0;
+
+ return 0;
+}
+
+static inline void __tree_new_min(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ if (self->min == NULL)
+ self->min = node;
+ else if (self->compare(node->key, self->min->key) < 0)
+ self->min = node;
+}
+
+static inline void __tree_new_max(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ if (self->max == NULL)
+ self->max = node;
+ else if (self->compare(node->key, self->max->key) > 0)
+ self->max = node;
+}
+
+static inline void
+__tree_new_root(tree_t * self, tree_node_t * node, tree_node_t * left,
+ tree_node_t * right)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ node->left = left;
+ node->right = right;
+ node->parent = NULL;
+
+ if (unlikely(right != NULL))
+ right->parent = node;
+ if (unlikely(left != NULL))
+ left->parent = node;
+
+ self->root = node;
+}
+
+int tree_insert(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ __tree_new_min(self, node);
+ __tree_new_max(self, node);
+
+ if (unlikely(self->root == NULL)) {
+ __tree_new_root(self, node, NULL, NULL);
+ self->size++;
+ } else {
+ tree_node_t *root = self->root;
+
+ while (root != NULL) {
+ int rc = self->compare(node->key, root->key);
+
+ if (rc < 0) {
+ if (root->left == NULL) {
+ node->parent = root;
+ root->left = node;
+ self->size++;
+ return 0;
+ } else {
+ root = root->left;
+ }
+ } else if (0 < rc) {
+ if (root->right == NULL) {
+ node->parent = root;
+ root->right = node;
+ self->size++;
+ return 0;
+ } else {
+ root = root->right;
+ }
+ } else {
+ UNEXPECTED("duplicate key detected during "
+ "insert");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline tree_node_t *__tree_find_min(tree_node_t * node)
+{
+ tree_node_t *min = node;
+
+ if (node != NULL && node->right != NULL) {
+ min = node->right;
+
+ while (min != NULL && min->left != NULL)
+ min = min->left;
+ }
+
+ return min;
+}
+
+static inline tree_node_t *__tree_find_max(tree_node_t * node)
+{
+ tree_node_t *max = node;
+
+ if (node != NULL && node->left != NULL) {
+ max = node->left;
+
+ while (max != NULL && max->right != NULL)
+ max = max->right;
+ }
+
+ return max;
+}
+
+int tree_remove(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ if (unlikely(self->root == NULL) || unlikely(node == NULL))
+ return 0;
+
+ /* =========================== */
+
+ inline tree_node_t *__tree_find_min(tree_node_t * node) {
+ tree_node_t *min = node;
+
+ if (node != NULL && node->right != NULL) {
+ min = node->right;
+
+ while (min != NULL && min->left != NULL)
+ min = min->left;
+ }
+
+ return min;
+ }
+
+ inline tree_node_t *__tree_find_max(tree_node_t * node) {
+ tree_node_t *max = node;
+
+ if (node != NULL && node->left != NULL) {
+ max = node->left;
+
+ while (max != NULL && max->right != NULL)
+ max = max->right;
+ }
+
+ return max;
+ }
+
+ inline void remove_single_child(tree_node_t * node, bool left) {
+ tree_node_t *new_root = node->left;
+
+ if (left == false)
+ new_root = node->right;
+
+ if (node->parent != NULL) {
+ new_root->parent = node->parent;
+
+ if (node->parent->left == node) // handle zig-zag
+ node->parent->left = new_root;
+ else if (node->parent->right == node)
+ node->parent->right = new_root;
+ }
+
+ if (node == self->root) {
+ self->root = new_root;
+ self->root->parent = NULL;
+ }
+ }
+
+ /* =========================== */
+
+ if (node == self->min)
+ self->min = tree_node_next(node);
+ if (node == self->max)
+ self->max = tree_node_prev(node);
+
+ if (tree_node_leaf(node)) {
+ if (node->parent != NULL) {
+ if (node->parent->left == node) // left or right child?
+ node->parent->left = NULL;
+ else if (node->parent->right == node)
+ node->parent->right = NULL;
+ }
+ } else if (tree_node_internal(node)) { // two children!
+ tree_node_t *root = __tree_find_min(node);
+ assert(root != NULL); // must have a right child
+
+ if (node->right == root) { // new 'root' is largest child
+ root->left = node->left;
+ if (node->left != NULL)
+ root->left->parent = root;
+ } else { // new 'root' is smallest grandchild
+ root->parent->left = root->right;
+ if (root->right != NULL)
+ root->right->parent = root->parent;
+
+ root->right = node->right;
+ root->left = node->left;
+
+ node->right->parent = root;
+ node->left->parent = root;
+ }
+
+ root->parent = node->parent;
+
+ if (self->root == node) { // find new parent
+ self->root = root;
+ } else {
+ if (node->parent->left == node)
+ node->parent->left = root;
+ else if (node->parent->right == node)
+ node->parent->right = root;
+ }
+ } else if (node->left != NULL) { // single left child
+ remove_single_child(node, true);
+ } else if (node->right != NULL) { // single right child
+ remove_single_child(node, false);
+ }
+
+ node->right = node->left = node->parent = NULL;
+
+ if (0 < self->size)
+ self->size--;
+
+ if (self->size <= 0)
+ self->root = self->min = self->max = NULL;
+
+ return 0;
+}
+
+tree_node_t *tree_find(tree_t * self, const void *key)
+{
+ assert(self != NULL);
+ assert(key != NULL);
+
+ tree_node_t *root = self->root;
+
+ while (root != NULL) {
+ int rc = self->compare(key, root->key);
+
+ if (rc < 0)
+ root = root->left;
+ else if (0 < rc)
+ root = root->right;
+ else
+ break;
+ }
+
+ return root;
+}
+
+int tree_walk(tree_t * self, tree_walk_f walk_func)
+{
+ assert(self != NULL);
+ assert(walk_func != NULL);
+
+ int __tree_walk(tree_node_t * root) {
+ int rc = 0;
+
+ if (likely(root != NULL)) {
+ __tree_walk(root->left);
+ rc = walk_func(root);
+ __tree_walk(root->right);
+ }
+
+ return rc;
+ }
+
+ return __tree_walk(self->root);
+}
+
+void tree_dump(tree_t * self, FILE * out)
+{
+ assert(self != NULL);
+
+ int __tree_node_dump(tree_node_t * node) {
+ if (node != NULL)
+ return fprintf(out, "node[%p] left[%p] right[%p] "
+ "parent[%p] -- key[%d]\n",
+ node, node->left, node->right,
+ node->parent, (intptr_t) node->key);
+ else
+ return 0;
+ }
+
+ if (out == NULL)
+ out = stdout;
+
+ fprintf(out, "root[%p] min[%p] max[%p] compare[%p] size[%d]\n",
+ self->root, self->min, self->max, self->compare, self->size);
+
+ tree_walk(self, __tree_node_dump);
+}
+
+void tree_node_dump(tree_node_t * node, FILE * out)
+{
+ if (node == NULL)
+ return;
+
+ void __tree_node_dump(tree_node_t * root, int level) {
+ if (likely(root != NULL)) {
+ if (0 < level) {
+ for (int i = 0; i < level; i++)
+ fprintf(out, " ");
+ }
+
+ fprintf(out, "node:[%p] left[%p] right[%p] parent[%p] "
+ "key[%d]\n", root, root->left, root->right,
+ root->parent, (intptr_t) node->key);
+
+ level++;
+ __tree_node_dump(root->left, level);
+ __tree_node_dump(root->right, level);
+ level--;
+ }
+ }
+
+ if (out == NULL)
+ out = stdout;
+
+ __tree_node_dump(node, 0);
+}
+
+/* ======================================================================= */
+
+static tree_node_t *splay(tree_node_t * node, const void *key,
+ compare_f compare)
+{
+ if (node == NULL)
+ return node;
+
+ tree_node_t N = { NULL, NULL, NULL, NULL }
+ , *l = &N, *r = &N, *y;
+
+ for (;;) {
+ int rc = compare(key, node->key);
+ if (rc < 0) {
+ if (node->left == NULL)
+ break;
+
+ /* rotate right */
+ rc = compare(key, node->left->key);
+ if (rc < 0) {
+ y = node->left;
+ node->left = y->right;
+ if (y->right != NULL)
+ y->right->parent = node;
+ y->right = node;
+ node->parent = y;
+ y->parent = node->parent;
+ node = y;
+
+ if (node->left == NULL)
+ break;
+ }
+
+ /* link right */
+ r->left = node;
+ node->parent = r;
+
+ r = node;
+ node = node->left;
+ } else if (0 < rc) {
+ if (node->right == NULL)
+ break;
+
+ /* rotate left */
+ rc = compare(key, node->right->key);
+ if (0 < rc) {
+ y = node->right;
+ node->right = y->left;
+ if (y->left != NULL)
+ y->left->parent = node;
+ y->left = node;
+ node->parent = y;
+ y->parent = node->parent;
+ node = y;
+
+ if (node->right == NULL)
+ break;
+ }
+
+ /* link left */
+ l->right = node;
+ node->parent = l;
+
+ l = node;
+ node = node->right;
+ } else {
+ break;
+ }
+ }
+
+ /* assemble */
+ l->right = node->left;
+ if (node->left != NULL)
+ node->left->parent = l;
+ r->left = node->right;
+ if (node->right != NULL)
+ node->right->parent = r;
+
+ node->left = N.right;
+ if (N.right != NULL)
+ N.right->parent = node;
+
+ node->right = N.left;
+ if (N.left != NULL)
+ N.left->parent = node;
+
+ node->parent = NULL;
+
+ return node;
+}
+
+int splay_insert(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ __tree_new_min(self, node);
+ __tree_new_max(self, node);
+
+ if (unlikely(self->root == NULL)) {
+ node->left = node->right = node->parent = NULL;
+ self->root = node;
+ self->size = 1;
+ return 0;
+ }
+
+ self->root = splay(self->root, node->key, self->compare);
+
+ int rc = self->compare(node->key, self->root->key);
+
+ if (rc < 0) {
+ node->left = self->root->left;
+ if (node->left != NULL)
+ node->left->parent = node;
+ node->right = self->root;
+ self->root->left = NULL;
+ self->root->parent = node;
+ } else if (0 < rc) {
+ node->right = self->root->right;
+ if (node->right != NULL)
+ node->right->parent = node;
+ node->left = self->root;
+ self->root->right = NULL;
+ self->root->parent = node;
+ } else {
+ UNEXPECTED("duplicate key detected during insert");
+ return -1;
+ }
+
+ self->size++;
+ self->root = node;
+
+ return 0;
+}
+
+int splay_remove(tree_t * self, tree_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ if (unlikely(self->root == NULL) || unlikely(node == NULL))
+ return 0;
+
+ if (node == self->min)
+ self->min = tree_node_next(node);
+ if (node == self->max)
+ self->max = tree_node_prev(node);
+
+ self->root = splay(self->root, node->key, self->compare);
+
+ if (node->key == self->root->key) { /* found it */
+ tree_node_t *x;
+#if SAVE
+ if (self->root->left == NULL) {
+ x = self->root->right;
+ } else {
+ x = splay(self->root->left, node->key, self->compare);
+ x->right = self->root->right;
+ }
+#else
+ if (self->root->left != NULL && self->root->right != NULL) {
+ if (parity(int32_hash1((int32_t) self->root))) {
+ x = splay(self->root->left, node->key,
+ self->compare);
+ x->right = self->root->right;
+ } else {
+ x = splay(self->root->right, node->key,
+ self->compare);
+ x->left = self->root->left;
+ }
+ } else if (self->root->left == NULL) {
+ x = self->root->right;
+ } else {
+ x = splay(self->root->left, node->key, self->compare);
+ x->right = self->root->right;
+ }
+#endif
+
+ self->root->left = self->root->right = NULL;
+ self->root->parent = NULL;
+ self->root = x;
+
+ if (0 < self->size)
+ self->size--;
+ if (0 < self->size)
+ self->root->parent = NULL;
+ }
+
+ return 0;
+}
+
+tree_node_t *splay_find(tree_t * self, const void *key)
+{
+ assert(self != NULL);
+ assert(key != NULL);
+
+ self->root = splay(self->root, key, self->compare);
+
+ if (self->root != NULL && self->compare(key, self->root->key))
+ return NULL;
+
+ return self->root;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/tree_iter.c b/clib/src/tree_iter.c
new file mode 100644
index 0000000..3474b71
--- /dev/null
+++ b/clib/src/tree_iter.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: tree_iter.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/22/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "vector.h"
+#include "tree_iter.h"
+
+/* ======================================================================= */
+
+int tree_iter_init(tree_iter_t * self, tree_t * tree, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(tree != NULL);
+
+ self->node = NULL;
+ self->safe = NULL;
+
+ self->tree = tree;
+ self->flags = flags;
+
+ return tree_iter_clear(self);
+}
+
+int tree_iter_clear(tree_iter_t * self)
+{
+ assert(self != NULL);
+
+ if (self->flags & TI_FLAG_FWD) {
+ self->node = tree_min(self->tree);
+ if (self->node != NULL)
+ self->safe = tree_node_next(self->node);
+ } else if (self->flags & TI_FLAG_BWD) {
+ self->node = tree_max(self->tree);
+ if (self->node != NULL)
+ self->safe = tree_node_prev(self->node);
+ } else {
+ UNEXPECTED("invalid tree_iter flags");
+ return -1;
+ }
+
+ return 0;
+}
+
+tree_node_t *tree_iter_elem(tree_iter_t * self)
+{
+ assert(self != NULL);
+ return self->node;
+}
+
+tree_node_t *tree_iter_inc1(tree_iter_t * self)
+{
+ return tree_iter_inc2(self, 1);
+}
+
+tree_node_t *tree_iter_inc2(tree_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ for (size_t i = 0; i < count && self->node != NULL; i++) {
+ if (self->flags & TI_FLAG_FWD) {
+ self->node = self->safe;
+ if (self->node != NULL)
+ self->safe = tree_node_next(self->node);
+ } else if (self->flags & TI_FLAG_BWD) {
+ self->node = self->safe;
+ if (self->node != NULL)
+ self->safe = tree_node_prev(self->node);
+ } else {
+ UNEXPECTED("invalid tree_iter flags");
+ return NULL;
+ }
+ }
+
+ return self->node;
+}
+
+tree_node_t *tree_iter_dec1(tree_iter_t * self)
+{
+ return tree_iter_dec2(self, 1);
+}
+
+tree_node_t *tree_iter_dec2(tree_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ for (size_t i = 0; i < count && self->node != NULL; i++) {
+ if (self->flags & TI_FLAG_FWD) {
+ self->node = self->safe;
+ if (self->node != NULL)
+ self->safe = tree_node_prev(self->node);
+ } else if (self->flags & TI_FLAG_BWD) {
+ self->node = self->safe;
+ if (self->node != NULL)
+ self->safe = tree_node_next(self->node);
+ } else {
+ UNEXPECTED("invalid tree_iter flags");
+ return NULL;
+ }
+ }
+
+ return self->node;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/value.c b/clib/src/value.c
new file mode 100644
index 0000000..ad90d81
--- /dev/null
+++ b/clib/src/value.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: value.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 08/23/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "value.h"
+
+/* ======================================================================= */
+
+void value_dump(const value_t * self, FILE * out)
+{
+ assert(self != NULL);
+
+ static const char *type_name[] = {
+ "<unknown>",
+ "int8_t", "int16_t", "int32_t", "int64_t",
+ "uint8_t", "uint16_t", "uint32_t", "uint64_t",
+ "float", "double",
+ "string", "string-inline", "string-offset", "string-const",
+ "blob", "blob-inline", "blob-offset", "blob-file",
+ };
+
+ switch (self->type) {
+ case VT_I8:
+ fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n",
+ self->i8, self->size, type_name[self->type], self);
+ break;
+ case VT_I16:
+ fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n",
+ self->i16, self->size, type_name[self->type], self);
+ break;
+ case VT_I32:
+ fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n",
+ self->i32, self->size, type_name[self->type], self);
+ break;
+ case VT_I64:
+ fprintf(out, "value: [ data: %lld size: %d type: %s ] %p\n",
+ self->i64, self->size, type_name[self->type], self);
+ break;
+ case VT_U8:
+ fprintf(out, "value: [ data: %ud size: %d type: %s ] %p\n",
+ self->u8, self->size, type_name[self->type], self);
+ break;
+ case VT_U16:
+ fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n",
+ self->u16, self->size, type_name[self->type], self);
+ break;
+ case VT_U32:
+ fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n",
+ self->u32, self->size, type_name[self->type], self);
+ break;
+ case VT_U64:
+ fprintf(out, "value: [ data: %lld size: %d type: %s ] %p\n",
+ self->u64, self->size, type_name[self->type], self);
+ break;
+ case VT_REAL32:
+ fprintf(out, "value: [ data: %f size: %d type: %s ] %p\n",
+ self->r32, self->size, type_name[self->type], self);
+ break;
+ case VT_REAL64:
+ fprintf(out, "value: [ data: %g size: %d type: %s ] %p\n",
+ self->r64, self->size, type_name[self->type], self);
+ break;
+ case VT_STR:
+ fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n",
+ (char *)self->ptr, self->size, type_name[self->type],
+ self);
+ break;
+ case VT_STR_INLINE:
+ fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n",
+ (char *)self->data, self->size, type_name[self->type],
+ self);
+ break;
+ case VT_STR_OFF:
+ fprintf(out, "value: [ data: %llu size: %d type: %s ] %p\n",
+ self->u64, self->size, type_name[self->type], self);
+ break;
+ case VT_STR_CONST:
+ fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n",
+ (const char *)self->ptr, self->size,
+ type_name[self->type], self);
+ break;
+ case VT_BLOB:
+ fprintf(out, "value: [ size: %d type: %s ] %p\n",
+ self->size, type_name[self->type], self);
+ dump_memory(out, (unsigned long)self->ptr, self->ptr,
+ self->size);
+ break;
+ case VT_BLOB_INLINE:
+ fprintf(out, "value: [ size: %d type: %s ] %p\n",
+ self->size, type_name[self->type], self);
+ dump_memory(out, (unsigned long)self->data, self->data,
+ self->size);
+ break;
+ case VT_BLOB_OFF:
+ fprintf(out, "value: [ data: %llu size: %d type: %s ] %p\n",
+ self->u64, self->size, type_name[self->type], self);
+ break;
+ case VT_BLOB_FILE:
+ fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n",
+ (char *)self->data, self->size, type_name[self->type],
+ self);
+ break;
+ default:
+ // throw_unexpected(VALUE_TYPE);
+ fprintf(out, "value: [ size: %d type: %s ] %p\n",
+ self->size, type_name[self->type], self);
+ }
+}
+
+/* ======================================================================= */
diff --git a/clib/src/vector.c b/clib/src/vector.c
new file mode 100644
index 0000000..54f9981
--- /dev/null
+++ b/clib/src/vector.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: vector.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: dynamic vector
+ * Note:
+ * Date: 08/29/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+
+#include "vector.h"
+#include "tree.h"
+#include "tree_iter.h"
+#include "mq.h"
+
+/* ======================================================================= */
+
+/*! @cond */
+#define VECTOR_NODE_MAGIC "VCND"
+
+#define VECTOR_NODE_MAGIC_CHECK(m) ({ \
+ bool rc = (((m)[0] != VECTOR_NODE_MAGIC[0]) || \
+ ((m)[1] != VECTOR_NODE_MAGIC[1]) || \
+ ((m)[2] != VECTOR_NODE_MAGIC[2]) || \
+ ((m)[3] != VECTOR_NODE_MAGIC[3])); \
+ rc; \
+ })
+
+typedef struct vector_node vector_node_t;
+
+struct vector_node {
+ uint8_t magic[4];
+
+ uint32_t address;
+ tree_node_t node;
+
+ uint8_t data[];
+};
+
+#define VECTOR_PAGE_MAX UINT16_MAX
+#define VECTOR_PAGE_DIVISOR 32
+
+#define __index_to_page(i,s) \
+({ \
+ typeof(i) _p = ((i) / (s)); \
+ _p; \
+})
+
+#define __index_to_page_hashed(i,s) \
+({ \
+ typeof(i) _h = int32_hash1(__index_to_page((i),(s))); \
+ _h; \
+})
+/*! @endcond */
+
+/* ======================================================================= */
+
+static vector_node_t *__vector_find_page(vector_t * self, size_t idx)
+{
+ const void *hash;
+ hash = (const void *)__index_to_page_hashed(idx, self->hdr.elem_num);
+
+ tree_node_t *node = tree_find(&self->tree, hash);
+ if (unlikely(node == NULL)) {
+ UNEXPECTED("'%d' index out of range", idx);
+ return NULL;
+ }
+
+ return container_of(node, vector_node_t, node);
+}
+
+static int __vector_shrink(vector_t * self)
+{
+ assert(self != NULL);
+
+ vector_node_t *node = __vector_find_page(self,
+ vector_capacity(self) - 1);
+ assert(node != NULL);
+
+ int rc = splay_remove(&self->tree, &node->node);
+
+ free(node);
+ self->hdr.page_count--;
+
+ return rc;
+}
+
+static vector_node_t *__vector_grow(vector_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ vector_node_t *node = NULL;
+ int rc = posix_memalign((void **)&node, sizeof(void *),
+ self->hdr.page_size);
+ if (rc != 0) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ memset(node, 0, self->hdr.page_size);
+
+ node->magic[0] = VECTOR_NODE_MAGIC[0];
+ node->magic[1] = VECTOR_NODE_MAGIC[1];
+ node->magic[2] = VECTOR_NODE_MAGIC[2];
+ node->magic[3] = VECTOR_NODE_MAGIC[3];
+
+ node->address = (uint32_t) node;
+
+ size_t hash = __index_to_page_hashed(vector_capacity(self),
+ self->hdr.elem_num);
+
+ tree_node_init(&node->node, (const void *)hash);
+ if (splay_insert(&self->tree, &node->node) < 0) {
+ free(node);
+ return NULL;
+ }
+ self->hdr.page_count++;
+
+ return node;
+}
+
+static int __vector_compare(const int i1, const int i2)
+{
+ return i1 - i2;
+}
+
+/* ======================================================================= */
+
+int vector_init3(vector_t * self, const char *name, size_t elem_size)
+{
+ size_t page_size = max(sysconf(_SC_PAGESIZE),
+ __round_pow2(elem_size * VECTOR_PAGE_DIVISOR));
+ return vector_init4(self, name, elem_size, page_size);
+}
+
+int vector_init4(vector_t * self, const char *name, size_t elem_size,
+ size_t page_size)
+{
+ assert(self != NULL);
+
+ if (unlikely(MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)) == false)
+ vector_delete(self);
+
+ if (elem_size < VECTOR_ELEM_MIN || VECTOR_ELEM_MAX < elem_size) {
+ UNEXPECTED("'%d' elem_size out of range [%d..%d]",
+ elem_size, VECTOR_ELEM_MIN, VECTOR_ELEM_MAX);
+ return -1;
+ }
+
+ page_size = __round_pow2(page_size);
+ if (page_size / elem_size < VECTOR_PAGE_DIVISOR) {
+ UNEXPECTED("'%d' page_size out of range [%d..%d]",
+ page_size, elem_size * VECTOR_PAGE_DIVISOR,
+ VECTOR_PAGE_MAX);
+ return -1;
+ }
+
+ memset(self, 0, sizeof *self);
+
+ self->hdr.id[IDENT_MAGIC_0] = VECTOR_MAGIC[IDENT_MAGIC_0];
+ self->hdr.id[IDENT_MAGIC_1] = VECTOR_MAGIC[IDENT_MAGIC_1];
+ self->hdr.id[IDENT_MAGIC_2] = VECTOR_MAGIC[IDENT_MAGIC_2];
+ self->hdr.id[IDENT_MAGIC_3] = VECTOR_MAGIC[IDENT_MAGIC_3];
+
+ self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR;
+ self->hdr.id[IDENT_MINOR] = CLIB_MINOR;
+ self->hdr.id[IDENT_PATCH] = CLIB_PATCH;
+
+ if (__BYTE_ORDER == __LITTLE_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= VECTOR_FLAG_LSB;
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ self->hdr.id[IDENT_FLAGS] |= VECTOR_FLAG_MSB;
+
+ self->hdr.page_size = page_size;
+ self->hdr.elem_size = elem_size;
+ self->hdr.elem_num = (self->hdr.page_size - sizeof(vector_node_t)) /
+ self->hdr.elem_size;
+
+ if (name != NULL && *name != '\0')
+ strncpy(self->hdr.name, name, sizeof(self->hdr.name));
+
+ tree_init(&self->tree, (compare_f) __vector_compare);
+
+ return 0;
+}
+
+int vector_delete(vector_t * self)
+{
+ if (unlikely(self == NULL))
+ return 0;
+
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ vector_node_t *node;
+ tree_for_each(&it, node, node) {
+ if (VECTOR_NODE_MAGIC_CHECK(node->magic)) {
+ UNEXPECTED("'%s' invalid or corrupt vector_node"
+ "object => '%.4s'", self->hdr.name,
+ node->magic);
+ return -1;
+ }
+
+ if (splay_remove(&self->tree, &node->node) < 0)
+ return -1;
+
+ memset(node, 0, sizeof(*node));
+ free(node);
+ }
+
+ self->hdr.page_count = self->hdr.size = 0;
+
+ return 0;
+}
+
+const void *vector_at(vector_t * self, size_t idx)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+ assert(idx < self->hdr.size);
+
+ vector_node_t *node = __vector_find_page(self, idx);
+ return node->data + (self->hdr.elem_size * (idx % self->hdr.elem_num));
+}
+
+int vector_get3(vector_t * self, size_t idx, void *ptr)
+{
+ return vector_get4(self, idx, ptr, 1);
+}
+
+int vector_get4(vector_t * self, size_t idx, void *ptr, size_t count)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ while (0 < count) {
+ memcpy(ptr, vector_at(self, idx), self->hdr.elem_size);
+
+ idx++;
+ count--;
+
+ ptr += self->hdr.elem_size;
+ }
+
+ return 0;
+}
+
+static inline int __vector_put(vector_t * self, size_t idx, const void *ptr)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ vector_node_t *node = __vector_find_page(self, idx);
+ assert(node != NULL);
+
+ if (VECTOR_NODE_MAGIC_CHECK(node->magic)) {
+ UNEXPECTED("'%s' invalid or corrupt vector_node object => "
+ "'%.4s'", self->hdr.name, node->magic);
+ return -1;
+ }
+
+ memcpy(node->data + (self->hdr.elem_size * (idx % self->hdr.elem_num)),
+ ptr, self->hdr.elem_size);
+
+ return 0;
+}
+
+int vector_put3(vector_t * self, size_t idx, const void *ptr)
+{
+ return vector_put4(self, idx, ptr, 1);
+}
+
+int vector_put4(vector_t * self, size_t idx, const void *ptr, size_t count)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+ assert(idx < self->hdr.size);
+
+ while (0 < count) {
+ if (__vector_put(self, idx, ptr) < 0)
+ return -1;
+
+ idx++;
+ count--;
+
+ ptr += self->hdr.elem_size;
+ }
+
+ return 0;
+}
+
+size_t vector_size1(vector_t * self)
+{
+ assert(self != NULL);
+ return self->hdr.size;
+}
+
+int vector_size2(vector_t * self, size_t size)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ size_t pages = __index_to_page(size, self->hdr.elem_num) + 1;
+
+ if (vector_pages(self) < pages) {
+ while (vector_pages(self) < pages)
+ (void)__vector_grow(self);
+ } else if (pages < vector_pages(self)) {
+ if (size <= 0)
+ vector_delete(self);
+ else
+ while (pages < vector_pages(self))
+ if (__vector_shrink(self) < 0)
+ return -1;
+ }
+
+ return self->hdr.size = size;
+}
+
+size_t vector_pages(vector_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+ return self->hdr.page_count;
+}
+
+size_t vector_capacity(vector_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+ return self->hdr.page_count * self->hdr.elem_num;
+}
+
+size_t vector_elem_size(vector_t * self)
+{
+ assert(self != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+ return self->hdr.elem_size;
+}
+
+ssize_t vector_save(vector_t * self, FILE * out)
+{
+ assert(self != NULL);
+ assert(out != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ int header_swap(vector_header_t * hdr) {
+ assert(hdr != NULL);
+
+ if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_MSB) {
+ hdr->page_size = htobe32(hdr->page_size);
+ hdr->elem_size = htobe16(hdr->elem_size);
+ hdr->elem_num = htobe16(hdr->elem_num);
+ hdr->size = htobe32(hdr->size);
+ hdr->page_count = htobe32(hdr->page_count);
+ } else if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_LSB) {
+ hdr->page_size = htole32(hdr->page_size);
+ hdr->elem_size = htole16(hdr->elem_size);
+ hdr->elem_num = htole16(hdr->elem_num);
+ hdr->size = htole32(hdr->size);
+ hdr->page_count = htole32(hdr->page_count);
+ } else {
+ UNEXPECTED("'%s' invalid or corrupt flash object => "
+ "'%x'", hdr->name, hdr->id[IDENT_FLAGS]);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ ssize_t save(vector_t * self, FILE * out) {
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ ssize_t len = 0;
+
+ vector_node_t *node;
+ tree_for_each(&it, node, node) {
+ if (VECTOR_NODE_MAGIC_CHECK(node->magic)) {
+ UNEXPECTED("'%s' invalid or corrupt vector_node"
+ "object => '%.4s'", self->hdr.name,
+ node->magic);
+ return -1;
+ }
+
+ size_t rc;
+
+ vector_node_t copy = *node;
+
+ copy.address = 0;
+ copy.node.left = copy.node.right = NULL;
+ copy.node.parent = NULL;
+
+ rc = fwrite((char *)&copy, 1, sizeof(copy), out);
+ if (rc != sizeof(copy)) {
+ if (ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+ len += rc;
+
+ rc = fwrite((char *)node->data, 1,
+ self->hdr.page_size - sizeof(*node), out);
+ if (rc != self->hdr.page_size - sizeof(*node)) {
+ if (ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+ len += rc;
+ }
+
+ return len;
+ }
+
+ ssize_t total = 0;
+
+ vector_header_t hdr = self->hdr;
+ if (header_swap(&hdr) < 0)
+ return -1;
+
+ clearerr(out);
+ total = fwrite(&hdr, 1, sizeof(hdr), out);
+ if (total != sizeof(hdr)) {
+ if (ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ total += save(self, out);
+
+ return total;
+}
+
+ssize_t vector_load(vector_t * self, FILE * in)
+{
+ assert(self != NULL);
+ assert(in != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ int header_swap(vector_header_t * hdr) {
+ assert(hdr != NULL);
+
+ if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_MSB) {
+ hdr->page_size = be32toh(hdr->page_size);
+ hdr->elem_size = be16toh(hdr->elem_size);
+ hdr->elem_num = be16toh(hdr->elem_num);
+ hdr->size = be32toh(hdr->size);
+ hdr->page_count = be32toh(hdr->page_count);
+ } else if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_LSB) {
+ hdr->page_size = le32toh(hdr->page_size);
+ hdr->elem_size = le16toh(hdr->elem_size);
+ hdr->elem_num = le16toh(hdr->elem_num);
+ hdr->size = le32toh(hdr->size);
+ hdr->page_count = le32toh(hdr->page_count);
+ } else {
+ UNEXPECTED("'%s' invalid or corrupt flash object => "
+ "'%x'", hdr->name, hdr->id[IDENT_FLAGS]);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ vector_delete(self);
+
+ clearerr(in);
+ ssize_t len = fread(&self->hdr, 1, sizeof(self->hdr), in);
+ if (len != sizeof(self->hdr)) {
+ if (feof(in)) {
+ UNEXPECTED("'%s' end-of-file encountered",
+ self->hdr.name);
+ return -1;
+ }
+ if (ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ if (header_swap(&self->hdr) < 0)
+ return -1;
+
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ tree_init(&self->tree, (compare_f) __vector_compare);
+ vector_node_t *node = NULL;
+
+ for (size_t i = 0; i < vector_pages(self); i++) {
+ size_t rc = posix_memalign((void **)&node, sizeof(void *),
+ self->hdr.page_size);
+ if (rc != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ memset(node, 0, self->hdr.page_size);
+
+ rc = fread((void *)node, 1, self->hdr.page_size, in);
+ if (rc != self->hdr.page_size) {
+ if (feof(in)) {
+ UNEXPECTED("'%s' end-of-file encountered",
+ self->hdr.name);
+ return -1;
+ }
+ if (ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ len += rc;
+
+ if (VECTOR_NODE_MAGIC_CHECK(node->magic)) {
+ UNEXPECTED("'%s' invalid or corrupt vector_node "
+ "object => '%.4s'", self->hdr.name,
+ node->magic);
+ return -1;
+ }
+
+ node->address = (uint32_t) node;
+ tree_node_init(&node->node, node->node.key);
+ splay_insert(&self->tree, &node->node);
+
+ node = NULL;
+ }
+
+ return len;
+}
+
+int vector_send(vector_t * self, mqueue_t * mq)
+{
+ assert(self != NULL);
+ assert(mq != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ mqueue_send(mq, (char *)self, sizeof(*self));
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ vector_node_t *node;
+ tree_for_each(&it, node, node) {
+ assert(!VECTOR_NODE_MAGIC_CHECK(node->magic));
+ mqueue_send(mq, (char *)node, self->hdr.page_size);
+ }
+
+ return 0;
+}
+
+int vector_receive(vector_t * self, mqueue_t * mq)
+{
+ assert(self != NULL);
+ assert(mq != NULL);
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ vector_delete(self);
+
+ mqueue_attr_t attr = mqueue_getattr(mq);
+
+ vector_node_t *node = NULL;
+ size_t rc = posix_memalign((void **)&node, attr.mq_msgsize,
+ attr.mq_msgsize);
+ if (rc != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ssize_t len = mqueue_receive(mq, (void *)node, attr.mq_msgsize);
+ assert(0 < len);
+
+ assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC));
+
+ memcpy(self, (void *)node, sizeof(*self));
+ tree_init(&self->tree, (compare_f) __vector_compare);
+
+ for (size_t i = 0; i < vector_pages(self); i++) {
+ rc = posix_memalign((void **)&node, attr.mq_msgsize,
+ attr.mq_msgsize);
+ if (rc != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ len = mqueue_receive(mq, (void *)node, attr.mq_msgsize);
+ assert(0 < len);
+
+ assert(!VECTOR_NODE_MAGIC_CHECK(node->magic));
+
+ node->address = (uint32_t) node;
+ tree_node_init(&node->node, node->node.key);
+ splay_insert(&self->tree, &node->node);
+
+ node = NULL;
+ }
+
+ return 0;
+}
+
+void vector_dump(vector_t * self, FILE * out)
+{
+ if (out == NULL)
+ out = stdout;
+
+ if (self != NULL) {
+ assert(!unlikely(MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)));
+
+ fprintf(out, "%s: page_size: %d elem_size: %d elem_num: %d -- "
+ "size: %d capacity: %d -- page_count: %d\n",
+ self->hdr.name, self->hdr.page_size,
+ self->hdr.elem_size, self->hdr.elem_num,
+ vector_size(self), vector_capacity(self),
+ self->hdr.page_count);
+
+ tree_iter_t it;
+ tree_iter_init(&it, &self->tree, TI_FLAG_FWD);
+
+ vector_node_t *node;
+ tree_for_each(&it, node, node) {
+ fprintf(out, "magic[%.4s] node: %p data: %p -- "
+ "address: %x\n", node->magic, &node->node,
+ node->data, node->address);
+
+ dump_memory(out, (unsigned long)node, node,
+ self->hdr.page_size);
+ }
+ }
+}
+
+/* ======================================================================= */
diff --git a/clib/src/vector_iter.c b/clib/src/vector_iter.c
new file mode 100644
index 0000000..4c95fd8
--- /dev/null
+++ b/clib/src/vector_iter.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: vector_iter.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: dynamic array
+ * Note:
+ * Date: 10/22/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "libclib.h"
+#include "vector_iter.h"
+
+/* ======================================================================= */
+
+static inline const void *__vector_iter_bwd(vector_iter_t * self)
+{
+ size_t low = 0;
+ const void *ret = NULL;
+
+ if (low < self->idx)
+ self->idx--;
+
+ if (low < self->idx)
+ ret = vector_at(self->vector, self->idx);
+
+ return ret;
+}
+
+static inline const void *__vector_iter_fwd(vector_iter_t * self)
+{
+ size_t high = vector_size(self->vector);
+ const void *ret = NULL;
+
+ if (self->idx < high)
+ self->idx++;
+
+ if (self->idx < high)
+ ret = vector_at(self->vector, self->idx);
+
+ return ret;
+}
+
+int vector_iter_init(vector_iter_t * self, vector_t * vector, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(vector != NULL);
+
+ self->flags = flags;
+ self->vector = vector;
+
+ if (self->flags & VI_FLAG_BWD) {
+ self->idx = vector_size(self->vector);
+ __vector_iter_bwd(self);
+ } else {
+ self->idx = 0;
+ }
+
+ return 0;
+}
+
+int vector_iter_clear(vector_iter_t * self)
+{
+ assert(self != NULL);
+
+ if (self->flags & VI_FLAG_BWD)
+ self->idx = vector_size(self->vector);
+ else
+ self->idx = 0;
+
+ self->vector = NULL;
+ return 0;
+}
+
+const void *vector_iter_elem(vector_iter_t * self)
+{
+ assert(self != NULL);
+
+ if (vector_capacity(self->vector) <= self->idx) {
+ UNEXPECTED("'%d' index out-of-range", self->idx);
+ return NULL;
+ }
+
+ if (vector_size(self->vector) <= self->idx)
+ return NULL;
+
+ return vector_at(self->vector, self->idx);
+}
+
+const void *vector_iter_inc1(vector_iter_t * self)
+{
+ return vector_iter_inc2(self, 1);
+}
+
+const void *vector_iter_inc2(vector_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ const void *ret = NULL;
+
+ for (size_t i = 0; i < count; i++) {
+ if (self->flags & VI_FLAG_BWD)
+ ret = __vector_iter_bwd(self);
+ else
+ ret = __vector_iter_fwd(self);
+ }
+
+ return ret;
+}
+
+const void *vector_iter_dec1(vector_iter_t * self)
+{
+ return vector_iter_dec2(self, 1);
+}
+
+const void *vector_iter_dec2(vector_iter_t * self, size_t count)
+{
+ assert(self != NULL);
+
+ const void *ret = NULL;
+
+ for (size_t i = 0; i < count; i++) {
+ if (self->flags & VI_FLAG_BWD)
+ ret = __vector_iter_fwd(self);
+ else
+ ret = __vector_iter_bwd(self);
+ }
+
+ return ret;
+}
+
+size_t vector_iter_pos1(vector_iter_t * self)
+{
+ assert(self != NULL);
+ return self->idx;
+}
+
+int vector_iter_pos2(vector_iter_t * self, size_t pos)
+{
+ assert(self != NULL);
+
+ if (vector_size(self->vector) <= pos) {
+ UNEXPECTED("'%d' index out-of-range", pos);
+ return -1;
+ }
+
+ self->idx = pos;
+ return 0;
+}
+
+/* ======================================================================= */
diff --git a/clib/src/watch.c b/clib/src/watch.c
new file mode 100644
index 0000000..6b78533
--- /dev/null
+++ b/clib/src/watch.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: watch.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr:
+ * Note:
+ * Date: 10/03/10
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "misc.h"
+#include "nargs.h"
+#include "watch.h"
+
+#define WATCH_PAGE_SIZE 64
+#define WATCH_EVENT_SIZE 64
+
+/* ======================================================================= */
+
+const char *__watch_msg[] = {
+ "watch: unexpected NULL self pointer",
+ "watch: unexpected NULL callback structure",
+};
+
+#define WATCH_NULL (__watch_msg[0])
+#define WATCH_CALLBACK_NULL (__watch_msg[1])
+
+/* ======================================================================= */
+
+void watch_init(watch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+
+ if (self->fd != -1)
+ close(self->fd), self->fd = -1;
+
+ self->fd = inotify_init1(IN_CLOEXEC);
+ if (unlikely(self->fd == -1))
+ throw_errno(errno);
+
+ array_init(&self->callbacks, sizeof(watch_callback_t), WATCH_PAGE_SIZE);
+}
+
+void watch_delete(watch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+ close(self->fd), self->fd = -1;
+ array_delete(&self->callbacks);
+}
+
+int watch_fileno(watch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+ return self->fd;
+}
+
+uint32_t watch_add(watch_t * self, const char *path, uint32_t events,
+ watch_callback_t * cb)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+
+ if (access(path, F_OK) != 0)
+ throw_errno(errno);
+
+ uint32_t wd = inotify_add_watch(self->fd, path, events);
+ if (unlikely((int)wd == -1))
+ throw_errno(errno);
+
+ if (cb != NULL)
+ array_put(&self->callbacks, wd, cb, 1);
+
+ return wd;
+}
+
+void watch_remove(watch_t * self, uint32_t wd)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+
+ int rc = inotify_rm_watch(self->fd, wd);
+ if (unlikely(rc == -1))
+ throw_errno(errno);
+
+ array_status(&self->callbacks, wd, false);
+}
+
+void watch_wait(watch_t * self)
+{
+ if (unlikely(self == NULL))
+ throw_unexpected(WATCH_NULL);
+
+ /* FIX ME */
+
+ watch_event_t events[WATCH_EVENT_SIZE];
+
+ ssize_t n = read(self->fd, events, sizeof events);
+ printf("n[%d]\n", n);
+
+ for (ssize_t i = 0; i < (ssize_t) (n / sizeof *events); i++)
+ printf("%d: wd[%d] mask[%x] cookie[%x] name[%.*s]\n",
+ i, events[i].wd, events[i].mask, events[i].cookie,
+ events[i].len, events[i].name);
+}
+
+/* ======================================================================= */
diff --git a/clib/stack.h b/clib/stack.h
new file mode 100644
index 0000000..521d55b
--- /dev/null
+++ b/clib/stack.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file stack.h
+ * @brief stack container
+ * @details
+ * A stack is a data structure (container) used for collecting a sequence of elements.
+ * stack allow for efficient insertion, removal and retreival of elements.
+ *
+ * @details For example,
+ * @code
+ * #include <clib/stack.h>
+ * #include <clib/stack_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * typedef struct {
+ * stack_node_t node;
+ * int i;
+ * float f;
+ * } data_t;
+ *
+ * slab_t s;
+ * slab_init(&s, sizeof(data_t), 0);
+ *
+ * stack_t a;
+ * stack_init(&a);
+ *
+ * int i;
+ * for (i=0; i<10; i++) {
+ * data_t * d = (data_t *)slab_alloc(&s);
+ *
+ * d->i = i;
+ * d->f = (float)i;
+ *
+ * stack_add_tail(&l, &d->node);
+ * }
+ *
+ * data_t * d;
+ * stack_for_each(&l, d, node) {
+ * printf("i: %d f: %f\n", d->i, d->f);
+ * }
+ *
+ * stack_dump(&l, stdout);
+ * slab_delete(&s);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __STACK_H__
+#define __STACK_H__
+
+#include "list.h"
+#include "type.h"
+
+typedef list stack;
+typedef list_node stack_node;
+
+#define stack_init(s) list_init((list *)(s))
+#define stack_push(s,n) list_add_tail((list *)(s),(n))
+#define stack_pop(s) list_remove_tail((list *)(s))
+#define stack_empty(s) list_empty((list *)(s))
+#define stack_dump(s,o) list_dump((list *)(s),(o))
+
+#define stack_entry(n, t, m) list_entry((n),(t),(m))
+#define stack_top(s) list_head((list *)(s))
+#define stack_bottom(s) list_tail((list *)(s))
+
+#define stack_for_each(s, i, m) \
+ for (i = container_of_var(s->node.next, i, m); \
+ &i->m != &(s)->node; \
+ i = container_of_var(i->m.next, i, m))
+
+#define stack_for_each_safe(s, i, n, m) \
+ for (i = container_of_var((s)->node.next, i, m), \
+ n = container_of_var(i->m.next, i, m); \
+ &i->m != &(s)->node; \
+ i = n, n = container_of_var(i->m.next, i, m))
+
+#endif /* __STACK_H__ */
diff --git a/clib/table.h b/clib/table.h
new file mode 100644
index 0000000..b2190b5
--- /dev/null
+++ b/clib/table.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file table.h
+ * @brief Table Container
+ * @details For example,
+ * @code
+ * #include <clib/array.h>
+ * #include <clib/array_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * table t;
+ * table_init(&t, COLS);
+ *
+ * const char * str = "column0 is a really long string";
+ *
+ * table_name(&t, "table name");
+ * table_name(&t, 0, str);
+ * table_name(&t, 1, "column1");
+ * table_name(&t, 2, "column2");
+ * table_name(&t, 3, "column3");
+ * table_name(&t, 4, "column4");
+ *
+ * printf("%s\n", table_name(&t, 0));
+ *
+ * size_t r, c;
+ * value v;
+ *
+ * for (r=0; r<COLS; r++) {
+ * for (c=0; c<COLS; c++) {
+ * table_put(&t, r, c,
+ * value_set(&v, r * COLS + c));
+ *
+ * printf("t[%d][%d] --> %d\n", r, c, r * COLS + c);
+ * }
+ * }
+ *
+ * table_iter it;
+ * table_iter_init(&it, &t);
+ *
+ * value * val;
+ * table_for_each(&it, val)
+ * value_dump(val, stdout);
+ *
+ * table_delete(&t);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __TABLE_H__
+#define __TABLE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "compare.h"
+#include "vector.h"
+#include "value.h"
+#include "mqueue.h"
+
+/* ======================================================================= */
+
+#define TABLE_NAME_SIZE 52 //!< Maximum table name size (in bytes)
+
+#define TABLE_MAGIC "TBLE" //!< Table magic number
+
+#define TABLE_FLAG_LSB 0x01 //!< little-endian header data
+#define TABLE_FLAG_MSB 0x02 //!< big-endian header data
+
+#define INIT_TABLE_HEADER {INIT_IDENT,{0,},0}
+#define INIT_TABLE {INIT_TABLE_HEADER,INIT_VECTOR,INIT_VECTOR,INIT_VECTOR}
+
+/*!
+ * @brief table container header
+ */
+struct table_header {
+ ident_t id; //!< Identification
+ char name[TABLE_NAME_SIZE]; //!< Table name
+
+ size_t col_nr; //!< Number of columns
+};
+typedef struct table_header table_header_t; //!< Alias for the @em table_header class
+
+/*!
+ * @brief table container
+ */
+struct table { //!< The Table class
+ table_header_t hdr; //!< Table metadata
+
+ vector_t table; //!< @private
+ vector_t string; //!< @private
+ vector_t blob; //!< @private
+};
+typedef struct table table_t; //!< Alias for the @em table class
+
+/* ==================================================================== */
+
+/*!
+ * @brief Constructs a @em table object
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @param name [in] table object @em name string
+ * @param col_nr [in] Number of columns
+ * @return None
+ */
+extern int table_init(table_t *, const char *, size_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Destructs an @em table container object
+ * @details Deallocate all backing storage associated with this \em table object
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @return None
+ */
+extern int table_delete(table_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Copy content from the @em table
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @param row [in] Row number
+ * @param col [out] Column number
+ * @return Reference to @em value on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern value_t *table_get(table_t *, size_t, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn const void * table_row(table_iter_t * self, size_t row_nr,
+ * [ value_t * row ])
+ */
+/*! @cond */
+#define table_row(...) STRCAT(table_row, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern value_t *table_row2(table_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern int table_row3(table_t *, size_t, value_t *)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/* !endcond */
+
+extern value_t *table_column(table_t *, value_t *, size_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Assign new content to the @em table
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @param row [in] Row number
+ * @param col [in] Column number
+ * @param value [in] Reference to source @em value
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int table_put(table_t *, size_t, size_t, value_t *)
+/*! @cond */
+__nonnull((1, 4)) /*! @endcond */ ;
+
+/*!
+ * @fn const char * table_name(table_t * self, size_t col, const char * name,
+ * size_t len)
+ * @brief Set or return the name of a column within a @em table
+ * @note If the fourth parameter is omitted, the @em name is assumed to contain
+ * a trailing NULL-byte
+ * @note If the third parameter is omitted, the column's current name is
+ * returned
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @param col [in] Column number
+ * @param name [in] New column name string (optional)
+ * @param len [in] New column name string length (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+
+/*! @cond */
+#define table_name(...) STRCAT(table_name, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const char *table_name2(table_t *, size_t) __nonnull((1));
+
+#define table_name3(t,c,s) \
+ choose_expr(const_expr(s), \
+ _table_name3((t),(c),(const char *)(s)), \
+ _table_name3((t),(c),(const char *)(s)))
+
+extern int _table_name3(table_t *, size_t, const char *) __nonnull((1, 3));
+
+#define table_name4(t,c,s,l) \
+ choose_expr(const_expr(s), \
+ _table_name4((t),(c),(const char *)(s),(l)), \
+ _table_name4((t),(c),(const char *)(s),(l)))
+
+extern int _table_name4(table_t *, size_t, const char *, size_t)
+__nonnull((1, 3));
+/*! @endcond */
+
+/*!
+ * @brief Return the number of rows in the @em table
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @return None
+ */
+extern size_t table_rows(table_t *) /*! @cond */ __nonnull((1)) /*! @endcond */
+;
+
+/*!
+ * @brief Return the number of column in the @em table
+ * @memberof table
+ * @param self [in] table object @em self pointer
+ * @return None
+ */
+extern size_t table_columns(table_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern int table_serialize(table_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern int table_deserialize(table_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern ssize_t table_save(table_t *, FILE *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+extern ssize_t table_load(table_t *, FILE *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+extern void table_print(table_t *, FILE *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern void table_dump(table_t *, FILE *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern int table_sort(table_t *, compare_f)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/* ==================================================================== */
+
+#endif /* __TABLE_H__ */
diff --git a/clib/table_iter.h b/clib/table_iter.h
new file mode 100644
index 0000000..1b01e0d
--- /dev/null
+++ b/clib/table_iter.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file table_iter.h
+ * @brief Table Iterator
+ * @code
+ * ...
+ * table_iter_t it;
+ * table_iter_init(&it, &t);
+ *
+ * value_t * val;
+ * table_for_each(&it, val) {
+ * if (value_type(val) != VT_UNKNOWN)
+ * value_dump(val, stdout);
+ * }
+ * ...
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __TABLE_ITER_H__
+#define __TABLE_ITER_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "attribute.h"
+
+#include "value.h"
+#include "table.h"
+#include "vector_iter.h"
+
+/* ======================================================================= */
+
+typedef struct table_iter table_iter_t; //!< Alias for the @em table class
+
+/*!
+ * @brief Table iterator
+ */
+struct table_iter {
+ table_t *table; //!< Reference to the target table object
+ vector_iter_t it; //!< Current position of the table
+ uint32_t flags; //!< Iterator configuration flags
+};
+
+/* ==================================================================== */
+
+#define TI_FLAG_NONE 0x00000000 //!< No flag mask
+#define TI_FLAG_FWD 0x00000001 //!< Forward (FWD) flag mask
+#define TI_FLAG_BWD 0x00000002 //!< Backwards (BWD) flag mask
+#define TI_FLAG_MASK 0x00000003 //!< All flag mask
+
+/*!
+ * @brief Initializes an @em table_iter iterator object
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @param table [in] table container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em table pointer is NULL
+ */
+extern int table_iter_init(table_iter_t *, table_t *, uint32_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets an @em table iterator object
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int table_iter_clear(table_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to @em value element at the current iterator position
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern const value_t *table_iter_elem(table_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Increment the position of an @em table iterator
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @param count [in] Number of positions to increment
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+#define table_iter_inc(...) STRCAT(table_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const value_t *table_iter_inc1(table_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const value_t *table_iter_inc2(table_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Decrement the position of an @em table iterator
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @param count [in] Number of positions to decrement
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+#define table_iter_dec(...) STRCAT(table_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const value_t *table_iter_dec1(table_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const value_t *table_iter_dec2(table_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn const void * table_iter_pos(table_iter_t * self, size_t pos)
+ * @brief Return or set the iterator position witin the @em table
+ * @details If the second (2nd) parameter is omitted, this functions returns
+ * the current position.
+ * @memberof table_iter
+ * @param self [in] table_iter object @em self pointer
+ * @param pos [in] new iterator position (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define table_iter_pos(...) STRCAT(table_iter_pos, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern size_t table_iter_pos1(table_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern int table_iter_pos2(table_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @def table_for_each_row(it, i)
+ * @hideinitializer
+ * @brief Table for-each-row element algorithm
+ * @param it [in] Table iterator object
+ * @param r [in] Value element variable (row)
+ */
+#define table_for_each(it,r) \
+ for (r = (typeof(r))table_iter_elem((it)); \
+ r != NULL; \
+ r = (typeof(r))table_iter_inc((it)))
+
+/* ==================================================================== */
+
+#endif /* __TABLE_ITER_H__ */
diff --git a/clib/test/array.c b/clib/test/array.c
new file mode 100644
index 0000000..825db06
--- /dev/null
+++ b/clib/test/array.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/array.h>
+#include <clib/array_iter.h>
+
+#define SIZE 2550
+
+int main(const int argc, const char * argv[]) {
+ array_t a;
+ array_init(&a, 4, 1024);
+
+printf("size[%d]\n", array_size(&a));
+printf("pages[%d]\n", array_pages(&a));
+printf("capacity[%d]\n", array_capacity(&a));
+printf("low[%u] high[%u]\n", array_low(&a), array_high(&a));
+exit(1);
+
+ array_dump(&a, stdout);
+
+ array_put(&a, 52, (uint32_t[]){52});
+ array_put(&a, 53, (uint32_t[]){53});
+ array_put(&a, 167, (uint32_t[]){167});
+ array_put(&a, 223, (uint32_t[]){223});
+ array_put(&a, 78, (uint32_t[]){78});
+ array_put(&a, 205, (uint32_t[]){205});
+ array_put(&a, 183, (uint32_t[]){183});
+ array_put(&a, 150, (uint32_t[]){150});
+ array_put(&a, 90, (uint32_t[]){90});
+ array_put(&a, 66, (uint32_t[]){66});
+ array_put(&a, 91, (uint32_t[]){91});
+ array_put(&a, 45, (uint32_t[]){45});
+ array_put(&a, 211, (uint32_t[]){211});
+
+ uint32_t arr[] = {985,986,987,988,990,991,992,993,994};
+ array_put(&a, 985, arr, 9);
+
+ array_iter_t it;
+ array_iter_init(&it, &a, AI_FLAG_FWD);
+
+ uint32_t * j;
+ array_for_each(&it, j) {
+ printf("arr[%d]\n", *j);
+ }
+
+ array_dump(&a, stdout);
+ array_delete(&a);
+
+ return 0;
+}
diff --git a/clib/test/checksum.c b/clib/test/checksum.c
new file mode 100644
index 0000000..c615cca
--- /dev/null
+++ b/clib/test/checksum.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <clib/checksum.h>
+#include <clib/exception.h>
+
+int main(void)
+{
+ unsigned int i;
+ uint8_t *buf = malloc(32);
+ uint8_t align[8] = { 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 };
+ uint8_t unaligned1[9] = { 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x33 };
+ uint8_t unaligned2[10] = { 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x33, 0x33 };
+ uint8_t unaligned3[11] = { 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33 };
+
+ memcpy(buf, align, sizeof(align));
+ uint32_t csum = memcpy_checksum(NULL, buf, sizeof(align));
+ if (csum != 0x11224488) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x11224488);
+ return 1;
+ }
+
+ memcpy(buf, unaligned1, sizeof(unaligned1));
+ csum = memcpy_checksum(NULL, buf, sizeof(unaligned1));
+ if (csum != 0x00333333) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00333333);
+ return 1;
+ }
+
+ memcpy(buf, unaligned2, sizeof(unaligned2));
+ csum = memcpy_checksum(NULL, buf, sizeof(unaligned2));
+ if (csum != 0x00003333) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00003333);
+ return 1;
+ }
+
+ memcpy(buf, unaligned3, sizeof(unaligned3));
+ csum = memcpy_checksum(NULL, buf, sizeof(unaligned3));
+ if (csum != 0x00000033) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00000033);
+ return 1;
+ }
+
+ char dst[16];
+ csum = memcpy_checksum(dst, align, sizeof(align));
+ if (csum != 0x11224488) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x11224488);
+ return 1;
+ }
+
+ csum = memcpy_checksum(dst, unaligned1, sizeof(unaligned1));
+ for (i = 0; i < sizeof(unaligned1); i++) {
+ if (dst[i] != unaligned1[i])
+ printf("fail %d byte %i a:%08x e:%08x\n",
+ __LINE__, i, dst[i], unaligned1[i]);
+ }
+ if (csum != 0x00333333) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00333333);
+ return 1;
+ }
+
+ csum = memcpy_checksum(dst, unaligned2, sizeof(unaligned2));
+ for (i = 0; i < sizeof(unaligned2); i++) {
+ if (dst[i] != unaligned2[i])
+ printf("fail %d byte %i a:%08x e:%08x\n",
+ __LINE__, i, dst[i], unaligned2[i]);
+ }
+ if (csum != 0x00003333) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00003333);
+ return 1;
+ }
+
+ csum = memcpy_checksum(dst, unaligned3, sizeof(unaligned3));
+ for (i = 0; i < sizeof(unaligned3); i++) {
+ if (dst[i] != unaligned3[i])
+ printf("fail %d byte %i a:%08x e:%08x\n",
+ __LINE__, i, dst[i], unaligned3[i]);
+ }
+ if (csum != 0x00000033) {
+ printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00000033);
+ return 1;
+ }
+
+#if 0
+ exception_t ex;
+ try {
+ (void)memcpy_checksum(NULL, &align[1], sizeof(align) - 1);
+ } else (ex) {
+ printf("Hey, what's this in the weeds? It's a baby, awesome!\n");
+ } end_try;
+
+#endif
+
+ return 0;
+}
diff --git a/clib/test/dispatch.c b/clib/test/dispatch.c
new file mode 100644
index 0000000..8851c55
--- /dev/null
+++ b/clib/test/dispatch.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <clib/watch.h>
+#include <clib/dispatch.h>
+
+int callback(dispatch_event_t * ev, void * ptr) {
+ char buf[256];
+ int rc;
+
+ rc = read(ev->fd, buf, sizeof buf);
+ printf("rc[%d]\n", rc);
+
+ printf("fd[%x]\n", ev->fd);
+ printf("events[%x]\n", ev->events);
+
+ return 0;
+}
+
+int main(const int argc, const char * argv[]) {
+ watch_t w;
+ watch_init(&w);
+
+ watch_callback_t wc = {
+ .data = NULL,
+ .func = NULL,
+ };
+
+ watch_add(&w, "/dev/mqueue", IN_CREATE | IN_ATTRIB, &wc);
+
+ dispatch_t d;
+ dispatch_init(&d);
+
+ dispatch_callback_t dc = {
+ .data = NULL,
+ .func = callback,
+ };
+
+ dispatch_add(&d, watch_fileno(&w), EPOLLIN, &dc);
+ dispatch_wait(&d);
+
+ return 0;
+}
diff --git a/clib/test/ecc.c b/clib/test/ecc.c
new file mode 100644
index 0000000..90760d7
--- /dev/null
+++ b/clib/test/ecc.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/assert.h>
+#include <clib/type.h>
+#include <clib/misc.h>
+
+#include <clib/ecc.h>
+int test_sfc_ecc()
+{
+ uint64_t in[] = { 0x3131313131313131, 0x3232323232323232,
+ 0x3333333333333333, 0x3434343434343434,
+ 0x3535353535353535, 0x3636363636363636,
+ 0x3737373737373737, 0x3838383838383838,
+ 0x3132333435363738, 0x3938373635343332,
+ };
+ printf("Function: test_sfc_ecc\n");
+ printf("IN [\n");
+ for (uint i=0; i<sizeof(in); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)in)[i]);
+ }
+ printf("]\n");
+
+ uint8_t out_ecc[sizeof in + sizeof in / 8];
+ int rc = sfc_ecc_inject(out_ecc, sizeof out_ecc, in, sizeof in);
+ printf("sz_in(%d) sz_out(%d) rc(%d)\n", sizeof in, sizeof out_ecc, rc);
+
+ printf("OUT_ECC [\n");
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n");
+
+ uint8_t keep_out_ecc[sizeof in + sizeof in / 8];
+ memcpy(keep_out_ecc, out_ecc, sizeof keep_out_ecc);
+ printf("sz_in(%d) sz_keep_out(%d) sz_out(%d)\n", sizeof in, sizeof keep_out_ecc, sizeof out_ecc);
+
+ printf("KEEP_OUT_ECC [\n");
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n");
+
+
+
+ uint8_t out_no_ecc[sizeof in];
+ int myrc = sfc_ecc_remove(out_no_ecc, sizeof out_no_ecc,
+ out_ecc, sizeof out_ecc);
+ printf("ECC_STATUS:%d \n",myrc);
+ printf("OUT_NO_ECC [\n");
+ for (uint i=0; i<sizeof(out_no_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_no_ecc)[i]);
+ }
+ printf("]\n");
+
+ for (uint x=0; x<sizeof(out_ecc);x++)
+ {
+ for (int c=0; c<8; c++)
+ {
+ out_ecc[x] ^= 1<<c;
+ printf("OUT_CORRUPTED_ECC[%d,%d] [\n",x,c);
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n");
+ myrc = sfc_ecc_remove(out_no_ecc, sizeof out_no_ecc,
+ out_ecc, sizeof out_ecc);
+ printf("ECC_STATUS:%d \n",myrc);
+ printf("OUT_NO_ECC [\n");
+ for (uint i=0; i<sizeof(out_no_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_no_ecc)[i]);
+ }
+ printf("]\n");
+
+ printf("IN_OUT_ECC [\n");
+ for (uint i=0; i<sizeof(out_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n\n");
+ if (myrc == 0)
+ {
+ printf("For SFC Uncorrectable event\n");
+ memcpy(out_ecc, keep_out_ecc, sizeof out_ecc);
+
+ //return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int test_p8_ecc()
+{
+ uint64_t in[] = { 0x3131313131313131, 0x3333333333333333,};
+ printf("Function: test_p8_ecc\n");
+ printf("IN [\n");
+ for (uint i=0; i<sizeof(in); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)in)[i]);
+ }
+ printf("]\n");
+
+ uint8_t out_ecc[sizeof in + sizeof in / 8];
+ int rc = p8_ecc_inject(out_ecc, sizeof out_ecc, in, sizeof in);
+ printf("sz_in(%d) sz_out(%d) rc(%d)\n", sizeof in, sizeof out_ecc, rc);
+
+ printf("OUT_ECC [\n");
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n");
+
+ uint8_t out_no_ecc[sizeof in];
+ ecc_status_t myrc = p8_ecc_remove(out_no_ecc, sizeof out_no_ecc,
+ out_ecc, sizeof out_ecc);
+ printf("ECC_STATUS:%d \n",myrc);
+ printf("OUT_NO_ECC [\n");
+ for (uint i=0; i<sizeof(out_no_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_no_ecc)[i]);
+ }
+ printf("]\n");
+
+ for (uint x=0; x<sizeof(out_ecc);x++)
+ {
+ for (int c=0; c<8; c++)
+ {
+ out_ecc[x] ^= 1<<c;
+ printf("OUT_CORRUPTED_ECC[%d,%d] [\n",x,c);
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n");
+ myrc = p8_ecc_remove(out_no_ecc, sizeof out_no_ecc,
+ out_ecc, sizeof out_ecc);
+ printf("ECC_STATUS:%d \n",myrc);
+ printf("OUT_NO_ECC [\n");
+ for (uint i=0; i<sizeof(out_no_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_no_ecc)[i]);
+ }
+ printf("]\n");
+
+ printf("IN_OUT_ECC [\n");
+ for (uint i=0; i<sizeof(out_ecc); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out_ecc)[i]);
+ }
+ printf("]\n\n");
+ if (myrc == UNCORRECTABLE)
+ {
+ printf("Uncorrectable event\n");
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int main (void) {
+
+ uint64_t in[] = { 0x3131313131313131, 0x3232323232323232,
+ 0x3333333333333333, 0x3434343434343434, };
+
+ printf("IN [\n");
+ for (uint i=0; i<sizeof(in); i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)in)[i]);
+ }
+ printf("]\n");
+
+ uint8_t out[sizeof in + sizeof in / 8];
+
+ int rc = sfc_ecc_inject(out, sizeof out, in, sizeof in);
+
+ printf("sz_in(%d) sz_out(%d) rc(%d)\n", sizeof in, sizeof out, rc);
+
+ printf("OUT [\n");
+ for (int i=0; i<rc; i++) {
+ if (i && i % 8 == 0) printf(" ");
+ printf("%2.2x", ((uint8_t *)out)[i]);
+ }
+ printf("]\n\n");
+
+ sfc_ecc_dump(stdout, 0, out, rc);
+ test_sfc_ecc();
+ test_p8_ecc();
+
+ return 0;
+}
diff --git a/clib/test/err.c b/clib/test/err.c
new file mode 100644
index 0000000..9ad6420
--- /dev/null
+++ b/clib/test/err.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+
+#include <clib/assert.h>
+#include <clib/version.h>
+#include <clib/err.h>
+
+extern char * program_invocation_short_name;
+
+#define FOO_MAJOR 0x01
+#define FOO_MINOR 0x00
+#define FOO_PATCH 0x00
+#define FOO_VER VER(FOO_MAJOR, FOO_MINOR, FOO_PATCH)
+
+#define FOO_UNEXPECTED(f, ...) ({ \
+ UNEXPECTED(f, ##__VA_ARGS__); \
+ VERSION(FOO_VER, "%s", program_invocation_short_name); \
+ })
+
+
+int main (int argc, char * argv[]) {
+ ERRNO(EINVAL);
+ FOO_UNEXPECTED("cannot frob the ka-knob");
+
+ goto error;
+
+ if (false) {
+ err_t * err = NULL;
+error:
+ while ((err = err_get()) != NULL) {
+ switch (err_type(err)) {
+ case ERR_VERSION:
+ fprintf(stderr, "%s: %s : %s(%d) : v%d.%02d.%04d %.*s\n",
+ basename((char *)argv[0]),
+ err_type_name(err), basename(err_file(err)), err_line(err),
+ VER_TO_MAJOR(err_code(err)), VER_TO_MINOR(err_code(err)),
+ VER_TO_PATCH(err_code(err)),
+ err_size(err), (char *)err_data(err));
+ break;
+ default:
+ fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ basename((char *)argv[0]),
+ err_type_name(err), basename(err_file(err)), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+ }
+ }
+ }
+
+ return 0;
+}
+
diff --git a/clib/test/exc_throw.c b/clib/test/exc_throw.c
new file mode 100644
index 0000000..de06241
--- /dev/null
+++ b/clib/test/exc_throw.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+
+#include <clib/exception.h>
+
+#define EXCEPTION_FOO 10
+
+void foo() {
+ throw(EXCEPTION_FOO, ex.data, ex.size);
+}
+
+int main() {
+ exception_t ex;
+
+ try {
+ printf("try block: BEFORE foo\n");
+ foo();
+ printf("try block: AFTER foo <-- should not get here\n");
+ } catch (EXCEPTION_FOO, ex) {
+ printf("main: CAUGHT %s(%d) EXCEPTION_FOO data[%d]\n",
+ ex.file, ex.line, *(int *)ex.data);
+ } else (ex) {
+ printf("main: CAUGHT %s(%d) data[%d]\n",
+ ex.file, ex.line, *(int *)ex.data);
+ } end
+
+ return 0;
+}
+
diff --git a/clib/test/exception.c b/clib/test/exception.c
new file mode 100644
index 0000000..8a8e16f
--- /dev/null
+++ b/clib/test/exception.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+
+#include <clib/exception.h>
+#include <clib/memory_leak_detection.h>
+
+#define EXCEPTION_FOO 1
+#define EXCEPTION_BAR 2
+#define EXCEPTION_TAZ 3
+
+int bar(int i) {
+ int * data = MALLOC(sizeof *data);
+ *data = i;
+
+ throw_bytes(EXCEPTION_BAR, data, sizeof *data);
+
+ return 0;
+}
+
+int foo(int i) {
+
+ exception_t ex;
+ try {
+ printf("%s: %d\n", __func__, __LINE__);
+ bar(i);
+ } catch (EXCEPTION_BAR, ex) {
+ printf("foo: CAUGHT %s(%d) EXCEPTION_BAR data[%d]\n",
+ ex.file, ex.line, *(int *)ex.data);
+ throw_bytes(EXCEPTION_FOO, ex.data, ex.size);
+ } end_try
+
+ throw_bytes(EXCEPTION_FOO, "this is a test", 14);
+
+ printf("%s: %d\n", __func__, __LINE__);
+
+ return 0;
+}
+
+int main(void) {
+ exception_t ex;
+
+ try {
+ printf("%s: %d\n", __func__, __LINE__);
+ foo(1);
+ printf("try block: AFTER foo <-- should not get here\n");
+ } catch (EXCEPTION_FOO, ex) {
+ printf("main: CAUGHT %s(%d) EXCEPTION_FOO data[%d]\n",
+ ex.file, ex.line, *(int *)ex.data);
+ } else (ex) {
+ printf("main: CAUGHT %s(%d) data[%d]\n",
+ ex.file, ex.line, *(int *)ex.data);
+ } end_try
+
+ return 0;
+}
diff --git a/clib/test/heap.c b/clib/test/heap.c
new file mode 100644
index 0000000..db7008e
--- /dev/null
+++ b/clib/test/heap.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/heap.h>
+
+int main(const int argc, const char * argv[]) {
+ heap_t h;
+ heap_init(h, 0, 0);
+
+ void * p5 = heap_alloc(&h, 5);
+ void * p9 = heap_alloc(&h, 9);
+ void * p13 = heap_alloc(&h, 13);
+
+ heap_free(&h, p13);
+ heap_free(&h, p5);
+ heap_free(&h, p9);
+
+ return 0;
+}
diff --git a/clib/test/list.c b/clib/test/list.c
new file mode 100644
index 0000000..a93f6a7
--- /dev/null
+++ b/clib/test/list.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/assert.h>
+#include <clib/slab.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+
+struct data {
+ list_node_t node;
+ int i;
+ float f;
+};
+typedef struct data data_t;
+
+int main (void) {
+// slab_t s = INIT_SLAB;
+// slab_init(&s, "my_slab", sizeof(data_t), 0);
+
+ list_t l = INIT_LIST;
+ list_init(&l);
+
+ int i;
+ for (i=0; i<10; i++) {
+// data_t * d = (data_t *)slab_alloc(&s);
+ data_t * d = (data_t *)malloc(sizeof(*d));
+
+ d->i = i;
+ d->f = (float)i;
+
+ list_add_tail(&l, &d->node);
+ }
+
+ list_iter_t it;
+ list_iter_init(&it, &l, LI_FLAG_FWD);
+
+ data_t * d;
+ list_for_each(&it, d, node) {
+ printf("i: %d f: %f\n", d->i, d->f);
+ }
+
+ while (list_empty(&l) == false) {
+ data_t * d = container_of(list_remove_tail(&l), data_t, node);
+ printf("i: %d f: %f\n", d->i, d->f);
+ }
+
+// slab_dump(&s, stdout);
+// slab_delete(&s);
+
+ return 0;
+}
+
diff --git a/clib/test/map.c b/clib/test/map.c
new file mode 100644
index 0000000..2501a32
--- /dev/null
+++ b/clib/test/map.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/assert.h>
+#include <clib/slab.h>
+#include <clib/type.h>
+#include <clib/map.h>
+
+struct data {
+ map_node_t node;
+ int i;
+ float f;
+};
+typedef struct data data_t;
+
+int main (void) {
+ slab_t s = INIT_SLAB;
+ slab_init(&s, "my_slab", sizeof(data_t), 0);
+
+ int compare(const int i1, const int i2) {
+ return i1 - i2;
+ }
+
+ map_t m;
+ map_init(&m, (compare_f)compare);
+
+ int i;
+ for (i=0; i<100; i++) {
+ data_t * d = (data_t *)slab_alloc(&s);
+
+ printf("i[%d]\n", i);
+
+ d->i = i;
+ d->f = (float)i;
+
+ map_node_init(&d->node, (const void *)(d->i));
+ map_insert(&m, &d->node);
+ }
+
+#if 1
+ i = 6;
+ map_node_t * n = map_find(&m, (const void *)(i));
+ map_remove(&m, n);
+ n = map_find(&m, (const void *)i);
+
+ i = 2;
+ map_find(&m, (const void *)i);
+ i = 8;
+ map_find(&m, (const void *)i);
+#endif
+
+ map_dump(&m, stdout);
+ slab_delete(&s);
+
+ return 0;
+}
+
diff --git a/clib/test/mq.c b/clib/test/mq.c
new file mode 100644
index 0000000..aaa9eab
--- /dev/null
+++ b/clib/test/mq.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <clib/vector.h>
+#include <clib/vector_iter.h>
+
+#include <clib/misc.h>
+#include <clib/mq.h>
+
+#define SIZE 2550
+
+int main(void) {
+ vector_t a = INIT_VECTOR;
+ vector_init(&a, "my_vector", 4, 1024);
+
+ vector_put(&a, 52, (uint32_t[]){52});
+ vector_put(&a, 53, (uint32_t[]){53});
+ vector_put(&a, 167, (uint32_t[]){167});
+ vector_put(&a, 223, (uint32_t[]){223});
+ vector_put(&a, 78, (uint32_t[]){78});
+ vector_put(&a, 205, (uint32_t[]){205});
+ vector_put(&a, 183, (uint32_t[]){183});
+ vector_put(&a, 150, (uint32_t[]){150});
+ vector_put(&a, 90, (uint32_t[]){90});
+ vector_put(&a, 66, (uint32_t[]){66});
+ vector_put(&a, 91, (uint32_t[]){91});
+ vector_put(&a, 45, (uint32_t[]){45});
+ vector_put(&a, 211, (uint32_t[]){211});
+ uint32_t arr[] = {55,56,57,58,59,60,61,62,63};
+ vector_put(&a, 985, arr, 9);
+
+ vector_iter_t it;
+ vector_iter_init(&it, &a, VI_FLAG_FWD);
+
+ uint32_t * j;
+ vector_for_each(&it, j) {
+ printf("XXX i[%d]\n", *j);
+ }
+
+ vector_dump(&a, stdout);
+
+ mqueue_t mq = INIT_MQUEUE;
+ mqueue_init(&mq, "dbs");
+ mqueue_create(&mq, gettid());
+
+ vector_send(&a, &mq);
+ vector_delete(&a);
+
+ sleep(1);
+
+ mqueue_delete(&mq);
+
+ return 0;
+}
diff --git a/clib/test/slab.c b/clib/test/slab.c
new file mode 100644
index 0000000..721c98f
--- /dev/null
+++ b/clib/test/slab.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/slab.h>
+
+#define SIZE 6500
+#define ALLOC 16
+#define PAGE 4096
+
+int main(void) {
+ slab_t s = INIT_SLAB;
+ slab_init(&s, "my_slab", ALLOC, PAGE);
+
+ void * ptr[SIZE] = {NULL,};
+
+ int i;
+ for (i=0; i<SIZE; i++) {
+ ptr[i] = slab_alloc(&s);
+ memset(ptr[i], i % 256, ALLOC);
+ }
+
+ slab_dump(&s, stdout);
+
+ for (i=0; i<SIZE; i++) {
+ slab_free(&s, ptr[i]);
+ }
+
+ slab_dump(&s, stdout);
+ slab_delete(&s);
+
+ return 0;
+}
diff --git a/clib/test/splay.c b/clib/test/splay.c
new file mode 100644
index 0000000..c62b87b
--- /dev/null
+++ b/clib/test/splay.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/assert.h>
+#include <clib/slab.h>
+#include <clib/type.h>
+#include <clib/tree.h>
+
+struct data {
+ tree_node_t node;
+ int i;
+ float f;
+};
+typedef struct data data_t;
+
+static inline int hash(int key) {
+ key = ~key + (key << 15);
+ key = key ^ (key >> 12);
+ key = key + (key << 2);
+ key = key ^ (key >> 4);
+ key = key * 2057;
+ key = key ^ (key >> 16);
+
+ return key;
+}
+
+int main (void) {
+ slab_t s = INIT_SLAB;
+ slab_init(&s, "my_slab", sizeof(data_t), 4096);
+
+ int compare(const int i1, const int i2) {
+ return i1 - i2;
+ }
+
+ tree_t l = INIT_TREE;
+ tree_init(&l, (compare_f)compare);
+
+ int i;
+ for (i=0; i<10000; i++) {
+ data_t * d = (data_t *)slab_alloc(&s);
+
+ d->i = hash(i);
+ d->f = (float)i;
+
+ tree_node_init(&d->node, (const void *)d->i);
+ splay_insert(&l, &d->node);
+ }
+
+ tree_dump(&l, stdout);
+
+ i = hash(6);
+ tree_node_t * n = tree_find(&l, (const void *)i);
+ printf("n[%p]\n", n);
+
+ data_t * d = container_of(n, data_t, node);
+ printf("d->i[%d]\n", d->i);
+
+ splay_remove(&l, n);
+ n = tree_find(&l, (const void *)i);
+ printf("n[%p]\n", n);
+
+ i = 2;
+ splay_find(&l, (const void *)i);
+ i = 8;
+ splay_find(&l, (const void *)i);
+
+#if 0
+ slab_dump(&s, stdout);
+ tree_delete(&l, d, node);
+#endif
+
+ tree_dump(&l, stdout);
+ slab_delete(&s);
+
+ return 0;
+}
+
diff --git a/clib/test/table.c b/clib/test/table.c
new file mode 100644
index 0000000..62253df
--- /dev/null
+++ b/clib/test/table.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/table.h>
+#include <clib/table_iter.h>
+
+#define COLS 5
+
+int main(void) {
+ table_t t = INIT_TABLE;
+ table_init(&t, "table", COLS);
+
+ const char * str = "column4 is a really long assed string";
+
+ table_name(&t, 0, "column0");
+ table_name(&t, 1, "column1");
+ table_name(&t, 2, "column2");
+ table_name(&t, 3, "column3");
+ table_name(&t, 4, str);
+
+ printf("%s\n", table_name(&t, 4));
+ printf("cols: %d\n", table_columns(&t));
+ printf("rows: %d\n", table_rows(&t));
+
+ table_dump(&t, stdout);
+
+ table_iter_t it;
+ table_iter_init(&it, &t, TI_FLAG_FWD);
+
+ size_t r, c;
+
+ value_t * val;
+ table_for_each(&it, val) {
+ for (c=0; c<COLS; c++)
+ value_dump(val+c, stdout);
+ }
+ printf("xxx =================\n");
+
+ value_t v[COLS];
+
+ for (r=0; r<COLS; r++) {
+ for (c=0; c<COLS; c++) {
+ printf("t[%d][%d] --> %d\n", r, c, r * COLS + c);
+ value_i32(v+c, r * COLS + c);
+ value_dump(v+c, stdout);
+ }
+ table_row(&t, r, v);
+ }
+ table_dump(&t, stdout);
+ printf("yyy =================\n");
+
+ table_for_each(&it, val) {
+ for (c=0; c<COLS; c++)
+ value_dump(val+c, stdout);
+ }
+ printf("zzz =================\n");
+
+ FILE * f = fopen("table.bin", "w+");
+ table_serialize(&t);
+ table_save(&t, f);
+ table_delete(&t);
+ fclose(f);
+
+ printf("111 =================\n");
+
+ f = fopen("table.bin", "r+");
+ table_load(&t, f);
+ table_deserialize(&t);
+ table_dump(&t, stdout);
+ fclose(f);
+
+ table_iter_init(&it, &t, TI_FLAG_FWD);
+ table_for_each(&it, val) {
+ for (c=0; c<COLS; c++)
+ value_dump(val+c, stdout);
+ }
+ printf("aaa =================\n");
+
+ table_dump(&t, stdout);
+ table_delete(&t);
+
+ return 0;
+}
diff --git a/clib/test/tree.c b/clib/test/tree.c
new file mode 100644
index 0000000..52f6c20
--- /dev/null
+++ b/clib/test/tree.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/assert.h>
+#include <clib/type.h>
+#include <clib/slab.h>
+
+#include <clib/tree.h>
+#include <clib/tree_iter.h>
+
+struct data {
+ tree_node_t node;
+ int i;
+ float f;
+};
+typedef struct data data_t;
+
+int main (void) {
+ slab_t s = INIT_SLAB;
+ slab_init(&s, "my_slab", sizeof(data_t), 4096);
+
+ tree_t t = INIT_TREE;
+ tree_init(&t, default_compare);
+
+ int i;
+ for (i=0; i<25; i++) {
+ data_t * d = (data_t *)slab_alloc(&s);
+
+ printf("insert i[%d] --> %p\n", i, d);
+ d->i = i;
+ d->f = (float)i; /* key */
+
+ tree_node_init(&d->node, (const void *)(d->i));
+ tree_insert(&t, &d->node);
+ }
+
+#if 1
+ i = 6;
+ tree_node_t * n = tree_find(&t, (const void *)(i));
+ tree_remove(&t, n);
+ n = tree_find(&t, (const void *)i);
+
+ i = 2;
+ tree_find(&t, (const void *)i);
+ i = 8;
+ tree_find(&t, (const void *)i);
+#endif
+
+ tree_dump(&t, stdout);
+
+ data_t * d;
+
+ tree_iter_t it;
+ tree_iter_init(&it, &t, TI_FLAG_FWD);
+
+ tree_for_each(&it, d, node) {
+ printf("depth first (FWD) i[%d] f[%f]\n", d->i, d->f);
+ }
+
+// tree_dump(&t, stdout);
+ slab_delete(&s);
+
+ return 0;
+}
+
diff --git a/clib/test/vector.c b/clib/test/vector.c
new file mode 100644
index 0000000..2925fbe
--- /dev/null
+++ b/clib/test/vector.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <clib/vector.h>
+#include <clib/vector_iter.h>
+
+int main(void) {
+ vector_t a = INIT_VECTOR;
+ vector_init(&a, "foo", 4, 1024);
+
+ vector_size(&a, 1000);
+
+ vector_put(&a, 0, (uint32_t[]){0xffffffff});
+ vector_put(&a, 1, (uint32_t[]){0xffffffff});
+ vector_put(&a, 52, (uint32_t[]){52});
+ vector_put(&a, 53, (uint32_t[]){53});
+ vector_put(&a, 167, (uint32_t[]){167});
+ vector_put(&a, 223, (uint32_t[]){223});
+ vector_put(&a, 78, (uint32_t[]){78});
+ vector_put(&a, 205, (uint32_t[]){205});
+ vector_put(&a, 183, (uint32_t[]){183});
+ vector_put(&a, 150, (uint32_t[]){150});
+ vector_put(&a, 90, (uint32_t[]){90});
+ vector_put(&a, 66, (uint32_t[]){66});
+ vector_put(&a, 91, (uint32_t[]){91});
+ vector_put(&a, 45, (uint32_t[]){45});
+ vector_put(&a, 211, (uint32_t[]){211});
+
+ uint32_t arr[] = {985,986,987,988,990,991,992,993,994};
+ vector_put(&a, 985, arr, 9);
+
+ vector_dump(&a, stdout);
+
+ vector_size(&a, 200);
+
+ vector_dump(&a, stdout);
+
+ FILE *f = fopen("vector.bin", "w+");
+ vector_save(&a, f);
+ fclose(f);
+
+ vector_delete(&a);
+
+ f = fopen("vector.bin", "r");
+ vector_load(&a, f);
+ fclose(f);
+
+#if 1
+ vector_iter_t it;
+ vector_iter_init(&it, &a, VI_FLAG_FWD);
+
+ uint32_t * j;
+ vector_for_each(&it, j) {
+ printf("arr[%d] = %d\n", it.idx, *j);
+ }
+#endif
+
+ vector_dump(&a, stdout);
+
+ vector_delete(&a);
+
+ return 0;
+}
diff --git a/clib/test/watch.c b/clib/test/watch.c
new file mode 100644
index 0000000..07e68cf
--- /dev/null
+++ b/clib/test/watch.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <clib/watch.h>
+
+int callback(watch_event_t * we) {
+ printf("hello\n");
+ return 0;
+}
+
+int main(const int argc, const char * argv[]) {
+ watch_t w;
+ watch_init(&w);
+
+ watch_callback_t cb = {
+ .data = NULL,
+ .func = callback,
+ };
+
+ watch_add(&w, "/dev/mqueue", IN_CREATE | IN_ATTRIB, &cb);
+ watch_wait(&w);
+
+ return 0;
+}
diff --git a/clib/timer.h b/clib/timer.h
new file mode 100644
index 0000000..48dc748
--- /dev/null
+++ b/clib/timer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: timer.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Timer
+ * Note:
+ * Date: 10/03/10
+ */
+
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <sys/timerfd.h>
+
+#include "attribute.h"
+#include "builtin.h"
+#include "assert.h"
+#include "vector.h"
+
+/* ==================================================================== */
+
+typedef struct timer_struct timer_t;
+typedef struct timer_callback_struct timer_callback_t;
+typedef struct timer_event_struct timer_event_t;
+
+typedef int (*timer_f) (timer_event *);
+
+struct timer_struct {
+ int fd;
+ vector_t callbacks;
+};
+
+struct timer_callback_struct {
+ void *data;
+ timer_f func;
+};
+
+struct timer_event_struct {
+};
+
+/* ======================================================================= */
+
+extern void timer_init(timer *, int) __nonnull((1));
+extern void timer_delete(timer *) __nonnull((1));
+
+extern int timer_fileno(timer *) __nonnull((1));
+
+extern uint32_t timer_add(timer *, const char *, uint32_t,
+ timer_callback *) __nonnull((1, 2));
+extern void timer_remove(timer *, uint32_t) __nonnull((1));
+
+extern void timer_wait(timer *) __nonnull((1));
+
+/* ======================================================================= */
+
+#endif /* __timer_H__ */
diff --git a/clib/trace_indent.h b/clib/trace_indent.h
new file mode 100644
index 0000000..94ccb7e
--- /dev/null
+++ b/clib/trace_indent.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2011
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: B. Rafanello
+ *
+ * Module: trace_indent.h
+ *
+ * Implements indentation for the trace output from bb_trace
+ *
+ * Version 0.0.0.1
+ */
+
+#ifndef TRACE_INDENT_H
+
+#define TRACE_INDENT_H
+
+/*********************************************************************/
+/* */
+/* Function Name: Set_Indent_Size */
+/* */
+/* Descriptive Name: Sets the number of spaces to use per indent. */
+/* If the current indent level is 5 and this */
+/* function is called to set the indent size to */
+/* 10, then 50 spaces will be printed by the */
+/* trace macros before each line of trace */
+/* output at this indent level. */
+/* */
+/* Input: unsigned int Spaces_Per_Indent - A value less than 128. */
+/* */
+/* Output: If Success : The specified value will be used for the */
+/* size of an indent. */
+/* */
+/* If Failure : This should only happen if the value */
+/* specified is >= 128, in which case it is */
+/* ignored and the previous value is retained.*/
+/* */
+/* Error Handling: Bad values for Spaces_Per_Indent are ignored. */
+/* */
+/* Side Effects: The number of spaces per indent may be changed. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Set_Indent_Size(unsigned int Spaces_Per_Indent);
+
+/*********************************************************************/
+/* */
+/* Function Name: Indent_Trace_Output */
+/* */
+/* Descriptive Name: This function increases the current indent */
+/* level by one. */
+/* */
+/* Input: None. */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: None */
+/* */
+/* Side Effects: The current indent level for trace output will */
+/* be increased by 1. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Indent_Trace_Output(void);
+
+/*********************************************************************/
+/* */
+/* Function Name: Outdent_Trace_Output */
+/* */
+/* Descriptive Name: This function reduces the current indent */
+/* level if it is greater than 0. */
+/* */
+/* Input: None */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: If the current indent level is zero, then this */
+/* function does nothing. */
+/* */
+/* Side Effects: The current indent level for trace output may be */
+/* decreased by 1. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Outdent_Trace_Output(void);
+
+/*********************************************************************/
+/* */
+/* Function Name: Do_Indent */
+/* */
+/* Descriptive Name: This function prints to stderr the number of */
+/* spaces corresponding to the current indent */
+/* level. */
+/* */
+/* Input: None */
+/* */
+/* Output: If Success : None */
+/* */
+/* If Failure : None */
+/* */
+/* Error Handling: None */
+/* */
+/* Side Effects: Some number of space may be output to stderr. */
+/* */
+/* Notes: */
+/* */
+/*********************************************************************/
+void Do_Indent(void);
+
+#endif
diff --git a/clib/tree.h b/clib/tree.h
new file mode 100644
index 0000000..747ec3b
--- /dev/null
+++ b/clib/tree.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file tree.h
+ * @brief Binary Tree Container
+ * @details Trees are a kind of associative container that stores elements formed
+ * by the conbination of a @em key and a @em tree_node
+ * @details For example,
+ * @code
+ * ...
+ * int main (int argc, char *argv[]) {
+ * typedef struct {
+ * int i;
+ * float f;
+ * tree_node_t node;
+ * } data_t;
+ *
+ * slab_t s;
+ * slab_init(&s, "my_slab", sizeof(data_t), 4096);
+ *
+ * tree_t t;
+ * tree_init(&t, default_compare);
+ *
+ * int i;
+ * for (i=0; i<25; i++) {
+ * data_t * d = (data_t *)slab_alloc(&s);
+ *
+ * d->i = i;
+ * d->f = (float)i // key
+ *
+ * tree_node_init(&d->node, (const void *)(d->i));
+ * tree_insert(&t, &d->node);
+ *
+ * printf("insert i[%d] --> %p\n", i, d);
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __TREE_H__
+#define __TREE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "exception.h"
+#include "builtin.h"
+#include "compare.h"
+#include "type.h"
+
+#define INIT_TREE_NODE {NULL,}
+#define INIT_TREE {NULL,NULL,NULL,NULL,0}
+
+/* ==================================================================== */
+
+typedef struct tree_node tree_node_t; //!< Alias for the @em tree_node class
+
+typedef int (*tree_walk_f) (tree_node_t *); //!< Tree walk callback function
+
+/*!
+ * @brief tree node
+ * @details Primitive types cannot be stored in the @em tree container, instead the user must
+ * embed a @em tree_node object within the stored object.
+ */
+struct tree_node {
+ tree_node_t *left; //!< Reference the left tree_node (a.k.a. left sub-tree)
+ tree_node_t *right; //!< Reference the right tree_node (a.k.a. right sub-tree)
+ tree_node_t *parent; //!< Reference the parent tree_node
+
+ const void *key; //!< Reference to the key bytes for this tree_node
+};
+
+/*!
+ * @brief tree container
+ * @details Primitive types cannot be stored in the @em tree container, instead the user must
+ * embed a @em tree_node object within the stored object.
+ */
+struct tree {
+ tree_node_t *root; //!< Reference to the root tree_node of the @em tree
+
+ tree_node_t *min; //!< Reference to the node with smallest 'key' in the tree
+ tree_node_t *max; //!< reference to the node with largest 'key' in the tree
+
+ compare_f compare; //!< Reference to the function used to distinguish tree_nodes
+ size_t size; //!< Cache of the number of tree_node's contained in the @em tree
+};
+typedef struct tree tree_t; //!< Alias for the @em tree class
+
+/* ==================================================================== */
+
+/*!
+ * @brief Constructs a @em tree_node object
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @param key [in] pointer to key bytes
+ * @return Reference to an initialized tree_node object on SUCCESS
+ */
+static inline tree_node_t *tree_node_init(tree_node_t * self, const void *key)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Check if the @em tree_node object is a leaf node
+ * @details A leaf node is one where both @em .left and @em .right are NULL
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return True if the @em tree_node is a leaf node, false otherwise
+ */
+static inline bool tree_node_leaf(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Check if the @em tree_node object is an internal node
+ * @details An internal node is one where either (or both) @em .left and @em .right are non-NULL
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return True if the @em tree_node is an internal node, false otherwise
+ */
+static inline bool tree_node_internal(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the left sub-tree of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_node_left(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the right sub-tree of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_node_right(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the parent tree_node of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_node_parent(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the key pointer of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to the key bytes if @em self is non-NULL, NULL otherwise
+ */
+static inline const void *tree_node_key(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the prev tree_node of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+extern tree_node_t *tree_node_prev(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the next tree_node of a @em tree_node
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+extern tree_node_t *tree_node_next(tree_node_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Dump the contents of a @em tree to @em out output stream
+ * @memberof tree_node
+ * @param self [in] tree_node object @em self pointer
+ * @param out [in] Reference to the @em out output stream
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void tree_node_dump(tree_node_t * self, FILE * out)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================== */
+
+static inline tree_node_t *tree_node_init(tree_node_t * self, const void *key)
+{
+ self->left = self->right = NULL;
+ self->parent = NULL;
+ self->key = key;
+ return self;
+}
+
+static inline bool tree_node_leaf(tree_node_t * self)
+{
+ return self ? self->left == NULL && self->right == NULL : false;
+}
+
+static inline bool tree_node_internal(tree_node_t * self)
+{
+ return self ? self->left != NULL && self->right != NULL : false;
+}
+
+static inline tree_node_t *tree_node_left(tree_node_t * self)
+{
+ return self ? self->left : NULL;
+}
+
+static inline tree_node_t *tree_node_right(tree_node_t * self)
+{
+ return self ? self->right : NULL;
+}
+
+static inline tree_node_t *tree_node_parent(tree_node_t * self)
+{
+ return self ? self->parent : NULL;
+}
+
+static inline const void *tree_node_key(tree_node_t * self)
+{
+ return self ? self->key : NULL;
+}
+
+/* ======================================================== */
+
+/*!
+ * @brief Constructs a @em tree object
+ * @memberof tree
+ * @param self [in] tree_node object @em self pointer
+ * @param compare [in] Reference to the @em tree_node compare function
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int tree_init(tree_t * self, compare_f compare)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Insert a new @em tree_node into the @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @param node [in] Reference to the @em tree_node to insert
+ * @return @em true if the @em tree_node was inserted, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em tree_node.key points to a duplicate key
+ */
+extern int tree_insert(tree_t * self, tree_node_t * node)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Removes a @em tree_node from the @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @param node [in] Reference to the @em tree_node to remove
+ * @return @em true if the @em tree_node was removed, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int tree_remove(tree_t * self, tree_node_t * node)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Find a @em tree_node within the @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @param key [in] Reference to the @em key to find
+ * @return Reference to a @em tree_node on SUCCESS, false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern tree_node_t *tree_find(tree_t * self, const void *key)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Walk the @em tree and call walk_func for each node
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @param walk_func [in] Reference to the walk function callback
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int tree_walk(tree_t * self, tree_walk_f walk_func)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Hexdump the contents of a @em tree to @em out output stream
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @param out [in] Reference to the @em out output stream
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void tree_dump(tree_t * self, FILE * out)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the root @em tree_node of a @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_root(tree_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return whether a @em tree container is empty
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @return @em true if @em tree is empty, false otherwise
+ */
+static inline bool tree_empty(tree_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the node count of a @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @return @em 0 if @em tree is empty, @em non-0 otherwise
+ */
+static inline size_t tree_size(tree_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the minimum @em tree_node of a @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_min(tree_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return the maximum @em tree_node of a @em tree container
+ * @memberof tree
+ * @param self [in] tree object @em self pointer
+ * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise
+ */
+static inline tree_node_t *tree_max(tree_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================== */
+
+static inline tree_node_t *tree_root(tree_t * self)
+{
+ return self ? self->root : NULL;
+}
+
+static inline bool tree_empty(tree_t * self)
+{
+ return self ? self->root == NULL : true;
+}
+
+static inline size_t tree_size(tree_t * self)
+{
+ return self ? self->size : 0;
+}
+
+static inline tree_node_t *tree_min(tree_t * self)
+{
+ return self ? self->min : NULL;
+}
+
+static inline tree_node_t *tree_max(tree_t * self)
+{
+ return self ? self->max : NULL;
+}
+
+/* ======================================================== */
+
+/*!
+ * @brief Insert a new @em tree_node into the @em tree container
+ * @details @em splay_insert is similar to @em tree_insert except
+ * it will self-adjust using the splay algorithm
+ * @memberof tree
+ * @param self [in] splay object @em self pointer
+ * @param node [in] Reference to the @em tree_node to insert
+ * @return @em true if the @em tree_node was inserted, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em tree_node.key points to a duplicate key
+ */
+extern int splay_insert(tree_t * self, tree_node_t * node)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Removes a @em tree_node from the splay @em tree container
+ * @details @em splay_remove is similar to @em tree_remove except
+ * it will self-adjust using the splay algorithm
+ * @memberof tree
+ * @param self [in] splay object @em self pointer
+ * @param node [in] Reference to the @em tree_node to remove
+ * @return @em true if the @em tree_node was removed, @em false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em tree_node.key points to a duplicate key
+ */
+extern int splay_remove(tree_t * self, tree_node_t * node)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Find a @em tree_node within the splay @em tree container
+ * @details @em splay_find is similar to @em tree_find except it will
+ * self-adjust using the splay algorithm
+ * @memberof tree
+ * @param self [in] splay object @em self pointer
+ * @param key [in] Reference to the @em key to find
+ * @return Reference to a @em tree_node on SUCCESS, false otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern tree_node_t *splay_find(tree_t * self, const void *key)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/* ======================================================== */
+
+#endif /* __TREE_H__ */
diff --git a/clib/tree_iter.h b/clib/tree_iter.h
new file mode 100644
index 0000000..2f410ca
--- /dev/null
+++ b/clib/tree_iter.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file tree_iter.h
+ * @brief Binary Tree Iterator
+ * @details For example,
+ * @code
+ * ...
+ * data_t * d;
+ * ...
+ * tree_iter_t it;
+ * tree_iter_init(&it, &t, TI_FLAG_DFT_FWD);
+ * ...
+ * tree_for_each(&it, d, node) {
+ * printf("depth first (FWD) i[%d] f[%f]\n", d->i, d->f);
+ * }
+ * ...
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2012
+ */
+
+#ifndef __TREE_ITER_H__
+#define __TREE_ITER_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "builtin.h"
+#include "compare.h"
+#include "type.h"
+
+#include "tree.h"
+
+/* ======================================================== */
+
+typedef struct tree_iter tree_iter_t; //!< Alias for the @em tree_iter class
+
+/*!
+ * @brief tree iterator
+ * @details Binary tree container iterator
+ */
+struct tree_iter {
+ tree_t *tree; //!< Tree container to iterate
+
+ tree_node_t *node; //!< Current position in the iteration
+ tree_node_t *safe;
+
+ uint32_t flags; //!< Iterator configuration flags
+};
+
+/* ======================================================== */
+
+#define TI_FLAG_NONE 0x00000000 //!< All flag mask
+#define TI_FLAG_FWD 0x00000001 //!< Depth-first-traversal Forward (FWD) flag mask
+#define TI_FLAG_BWD 0x00000002 //!< Depth-first-traversal Backward (BWD) flag mask
+#define TI_FLAG_MASK 0x00000003 //!< Depth-first-traversal All flag mask
+
+/*!
+ * @brief Initializes an @em tree_iter iterator object
+ * @memberof tree_iter
+ * @param self [in] tree_iter object @em self pointer
+ * @param tree [in] tree container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em tree pointer is NULL
+ */
+extern int tree_iter_init(tree_iter_t * self, tree_t * tree, uint32_t flags)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets an @em tree iterator object
+ * @memberof tree_iter
+ * @param self [in] tree_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int tree_iter_clear(tree_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to a @em tree_node element at the current
+ * iterator position
+ * @memberof tree_iter
+ * @param self [in] tree_iter object @em self pointer
+ * @return non-NULL on success, NULL otherwise
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern tree_node_t *tree_iter_elem(tree_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn tree_node_t * tree_iter_inc(tree_iter_t * self, size_t count = 1)
+ * @brief Increment the position of an @em tree iterator
+ * @details If the second (2nd) parameter is omitted, the iterator is
+ * incremented by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * tree_t l;
+ * tree_init(&l);
+ * ...
+ * tree_iter_t it;
+ * tree_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * tree_iter_inc(&it);
+ * ...
+ * @endcode
+ * @memberof tree_iter
+ * @param self [in] tree_iter object @em self pointer
+ * @param count [in] Number of positions to increment (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+#define tree_iter_inc(...) STRCAT(tree_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern tree_node_t *tree_iter_inc1(tree_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern tree_node_t *tree_iter_inc2(tree_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn tree_node_td * tree_iter_dec(tree_iter_t * self, size_t count = 1)
+ * @brief decrement the position of an @em list iterator
+ * @note If the second (2nd) parameter is omitted, the iterator is decremented
+ * by one (1) position.
+ * @details For example,
+ * @code
+ * ...
+ * tree_t l;
+ * tree_init(&l);
+ * ...
+ * tree_iter_t it;
+ * tree_iter_init(&it, &l, LI_FLAG_FWD);
+ * ...
+ * tree_iter_dec(&it, 3);
+ * ...
+ * @endcode
+ * @memberof tree_iter
+ * @param self [in] tree_iter object @em self pointer
+ * @param count [in] Number of positions to decrement (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+#define tree_iter_dec(...) STRCAT(tree_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern tree_node_t *tree_iter_dec1(tree_iter_t * self)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern tree_node_t *tree_iter_dec2(tree_iter_t * self, size_t count)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @def tree_for_each(it, i, m)
+ * @hideinitializer
+ * @brief Tree for-each algorithm
+ * @param it [in] Tree iterator object
+ * @param i [in] Tree element variable
+ * @param m [in] Member name
+ */
+#define tree_for_each(it, i, m) \
+ for (tree_iter_clear(it), i = container_of_var(tree_iter_elem(it), i, m); \
+ tree_iter_elem(it) != NULL; \
+ i = container_of_var(tree_iter_inc(it), i, m))
+
+/* ======================================================== */
+
+#endif /* __TREE_ITER_H__ */
diff --git a/clib/type.h b/clib/type.h
new file mode 100644
index 0000000..1e259c0
--- /dev/null
+++ b/clib/type.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file type.h
+ * @brief Type helpers
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __TYPE_H__
+#define __TYPE_H__
+
+#include "align.h"
+
+/*!
+ * @def cast_type(e,T)
+ * @hideinitializer
+ * @brief Casts @em p to type @em T
+ * @param e [in] Expression
+ * @param T [in] Type name
+ */
+#define cast_type(e,T) \
+ ((T)(e))
+
+/*!
+ * @def check_type(e,T)
+ * @hideinitializer
+ * @brief Check the type of expression @em e is equal to type @em T
+ * @param e [in] Expression
+ * @param T [in] Type name
+ */
+#define check_type(e,T) \
+ ((typeof(e) *)0 != (T *)0)
+
+/*!
+ * @def check_types_match(e,T)
+ * @hideinitializer
+ * @brief Check the types of expressions @em e1 and @em e2 are equal
+ * @param e1 [in] Expression
+ * @param e2 [in] Expression
+ */
+#define check_types_match(e1,e2) \
+ ((typeof(e1) *) 0 != (typeof(e2) *)0)
+
+/*!
+ * @def container_of(member_ptr, containing_type, member)
+ * @hideinitializer
+ * @brief Returns a pointer to the containing structure of a member
+ * @param p [in] Pointer to a strucure member
+ * @param T [in] Type of the containing structure
+ * @param m [in] Name of the member @em p references
+ */
+#define container_of(p,T,m) \
+ ((T *)((char *)(p) - offsetof(T,m)) - check_types_match(*(p),((T*)0)->m))
+
+/*!
+ * @def container_of_var(member_ptr, var, member)
+ * @hideinitializer
+ * @brief Returns a pointer to the containing structure of a member
+ * @param p [in] Pointer to a strucure member
+ * @param v [in] Pointer to a variable with the same of type as the containing structure
+ * @param m [in] Name of the member @em p references
+ */
+#define container_of_var(p, v, m) \
+ container_of(p, typeof(*v), m)
+
+#endif /* __TYPE_H__ */
diff --git a/clib/value.h b/clib/value.h
new file mode 100644
index 0000000..255ff0b
--- /dev/null
+++ b/clib/value.h
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file value.h
+ * @brief Value Container
+ * @details Values are a kind of variant structure that can contain
+ * values of different types
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __VALUE_H__
+#define __VALUE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libclib.h"
+
+/* ======================================================================= */
+
+typedef enum value_type_enum value_type_t; //!< Alias for the @em value_type_enum enum
+typedef struct value value_t; //!< Alias for the @em value class
+
+/*!
+ * @brief Value types
+ * @details Supported value type
+ */
+enum value_type_enum {
+ VT_UNKNOWN = 0, //!< Uninitialized or unknown type
+ VT_I8, //!< 8-bit signed integer type
+ VT_I16, //!< 16-bit signed integer type
+ VT_I32, //!< 32-bit signed integer type
+ VT_I64, //!< 64-bit signed integer type
+ VT_U8, //!< 8-bit unsigned integer type
+ VT_U16, //!< 16-bit unsigned integer type
+ VT_U32, //!< 32-bit unsigned integer type
+ VT_U64, //!< 64-bit unsigned integer type
+ VT_REAL32, //!< 32-bit float type
+ VT_REAL64, //!< 64-bit float type
+ VT_STR, //!< Character string type
+ VT_STR_INLINE, //!< Character string inline type
+ VT_STR_OFF, //!< Character string offset type
+ VT_STR_CONST, //!< Constant character string type
+ VT_BLOB, //!< Blob type
+ VT_BLOB_INLINE, //!< Blob inline type
+ VT_BLOB_OFF, //!< Blob offset type
+ VT_BLOB_FILE, //!< Blob file type
+} /*! @cond */ __packed /*! @endcond */ ;
+
+/*! @cond */
+#ifndef VALUE_PAD_DEFAULT
+#define VALUE_PAD_DEFAULT 12
+#endif
+/*! @endcond */ ;
+
+/*!
+ * @brief value container
+ */
+struct value {
+ uint32_t type:8, //!< Value type
+ size:24; //!< Value size (in bytes)
+
+ union {
+ int8_t i8; //!< 8-bit signed integer
+ int16_t i16; //!< 16-bit signed integer
+ int32_t i32; //!< 32-bit signed integer
+ int64_t i64; //!< 64-bit signed integer
+
+ uint8_t u8; //!< 8-bit unsigned integer
+ uint16_t u16; //!< 16-bit unsigned integer
+ uint32_t u32; //!< 32-bit unsigned integer
+ uint64_t u64; //!< 64-bit unsigned integer
+
+ float r32; //!< 32-bit float
+ double r64; //!< 64-bit float
+
+ void *ptr; //!< pointer
+
+ uint8_t data[VALUE_PAD_DEFAULT];
+ }; //!< Anonymous variant record
+};
+
+/* ==================================================================== */
+
+/*! @cond */
+#define value_type(...) STRCAT(value_type, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline value_type_t value_type1(const value_t * self)
+{
+ return self ? self->type : VT_UNKNOWN;
+}
+
+static inline void value_type2(value_t * self, value_type_t type)
+{
+ if (self != NULL)
+ self->type = type;
+}
+
+/*! @endcond */
+
+/*! @cond */
+#define value_size(...) STRCAT(value_size, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline uint32_t value_size1(const value_t * self)
+{
+ return self ? self->size : 0;
+}
+
+static inline void value_size2(value_t * self, uint32_t size)
+{
+ if (self != NULL)
+ self->size = size;
+}
+
+/*! @endcond */
+
+/*!
+ * @brief Clear the contents of a @em value
+ * @memberof value
+ * @param self [in] value object @em self pointer
+ */
+static inline void value_clear(value_t * self)
+{
+ if (self != NULL) {
+ if (self->type == VT_BLOB || self->type == VT_STR)
+ if (self->ptr != NULL)
+ free(self->ptr);
+ memset(self, 0, sizeof *self);
+ }
+}
+
+#define value_i8(...) STRCAT(value_i8_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline int8_t value_i8_1(const value_t * v)
+{
+ assert(value_type(v) == VT_I8);
+ return v->i8;
+}
+
+static inline value_t *value_i8_2(value_t * v, const int8_t i8)
+{
+ value_clear(v), v->type = VT_I8, v->i8 = i8, v->size = sizeof(i8);
+ return v;
+}
+
+#define value_i16(...) STRCAT(value_i16_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline int16_t value_i16_1(const value_t * v)
+{
+ assert(value_type(v) == VT_I16);
+ return v->i16;
+}
+
+static inline value_t *value_i16_2(value_t * v, const int16_t i16)
+{
+ value_clear(v), v->type = VT_I16, v->i16 = i16, v->size = sizeof(i16);
+ return v;
+}
+
+#define value_i32(...) STRCAT(value_i32_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline int32_t value_i32_1(const value_t * v)
+{
+ assert(value_type(v) == VT_I32);
+ return v->i32;
+}
+
+static inline value_t *value_i32_2(value_t * v, const int32_t i32)
+{
+ value_clear(v), v->type = VT_I32, v->i32 = i32, v->size = sizeof(i32);
+ return v;
+}
+
+#define value_i64(...) STRCAT(value_i64_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline int64_t value_i64_1(const value_t * v)
+{
+ assert(value_type(v) == VT_I64);
+ return v->i64;
+}
+
+static inline value_t *value_i64_2(value_t * v, const int64_t i64)
+{
+ value_clear(v), v->type = VT_I64, v->i64 = i64, v->size = sizeof(i64);
+ return v;
+}
+
+#define value_u8(...) STRCAT(value_u8_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline uint8_t value_u8_1(const value_t * v)
+{
+ assert(value_type(v) == VT_U8);
+ return v->u8;
+}
+
+static inline value_t *value_u8_2(value_t * v, const uint8_t u8)
+{
+ value_clear(v), v->type = VT_U8, v->u8 = u8, v->size = sizeof(u8);
+ return v;
+}
+
+#define value_u16(...) STRCAT(value_u16_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline uint16_t value_u16_1(const value_t * v)
+{
+ assert(value_type(v) == VT_U16);
+ return v->u16;
+}
+
+static inline value_t *value_u16_2(value_t * v, const uint16_t u16)
+{
+ value_clear(v), v->type = VT_U16, v->u16 = u16, v->size = sizeof(u16);
+ return v;
+}
+
+#define value_u32(...) STRCAT(value_u32_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline uint32_t value_u32_1(const value_t * v)
+{
+ assert(value_type(v) == VT_U32);
+ return v->u32;
+}
+
+static inline value_t *value_u32_2(value_t * v, const uint32_t u32)
+{
+ value_clear(v), v->type = VT_U32, v->u32 = u32, v->size = sizeof(u32);
+ return v;
+}
+
+#define value_u64(...) STRCAT(value_u64_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline uint64_t value_u64_1(const value_t * v)
+{
+ assert(value_type(v) == VT_U64);
+ return v->u64;
+}
+
+static inline value_t *value_u64_2(value_t * v, const uint64_t u64)
+{
+ value_clear(v), v->type = VT_U64, v->u64 = u64, v->size = sizeof(u64);
+ return v;
+}
+
+#define value_float(...) STRCAT(value_float_, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline float value_float1(const value_t * v)
+{
+ assert(value_type(v) == VT_REAL32);
+ return v->r32;
+}
+
+static inline value_t *value_float2(value_t * v, const float r32)
+{
+ value_clear(v), v->type = VT_REAL32, v->r32 = r32, v->size =
+ sizeof(r32);
+ return v;
+}
+
+#define value_double(...) STRCAT(value_double, NARGS(__VA_ARGS__))(__VA_ARGS__)
+static inline double value_double1(const value_t * v)
+{
+ assert(value_type(v) == VT_REAL64);
+ return v->r64;
+}
+
+static inline value_t *value_double2(value_t * v, const double r64)
+{
+ value_clear(v), v->type = VT_REAL64, v->r64 = r64, v->size =
+ sizeof(r64);
+ return v;
+}
+
+#define value_blob(...) STRCAT(value_blob, NARGS(__VA_ARGS__))(__VA_ARGS__)
+/*! @cond */
+static inline void *value_blob1(const value_t * v)
+{
+ if (value_type(v) == VT_BLOB)
+ return v->ptr;
+ else if (value_type(v) == VT_BLOB_INLINE)
+ return (void *)v->data;
+ else if (value_type(v) == VT_BLOB_OFF)
+ return NULL;
+ else if (value_type(v) == VT_BLOB_FILE) ;
+ else {
+ UNEXPECTED("invalid value_t blob type '%d'", value_type(v));
+ }
+
+ return NULL;
+}
+
+static inline value_t *value_blob4(value_t * self, const void *blob, size_t len,
+ size_t pad)
+{
+ value_clear(self);
+
+ if (pad <= len) {
+ self->ptr = malloc(len + 1);
+ self->type = VT_BLOB;
+
+ memcpy(self->ptr, blob, len);
+ memset(self->ptr + len, 0, 1);
+ } else {
+ self->type = VT_BLOB_INLINE;
+
+ memcpy(self->data, blob, len);
+ self->data[len] = '\0';
+ }
+
+ self->size = len;
+
+ return self;
+}
+
+static inline value_t *value_blob3(value_t * self, const void *blob, size_t len)
+{
+ return value_blob4(self, blob, len, VALUE_PAD_DEFAULT);
+}
+
+static inline value_t *value_blob2(value_t * self, const char *path)
+{
+ value_t *rc = value_blob4(self, path, strlen(path), VALUE_PAD_DEFAULT);
+ if (rc != NULL)
+ value_type(rc, VT_BLOB_FILE);
+ return rc;
+}
+
+/*! @endcond */
+
+#define value_string(...) STRCAT(value_string, NARGS(__VA_ARGS__))(__VA_ARGS__)
+/*! @cond */
+static inline char *value_string1(const value_t * v)
+{
+ if (value_type(v) == VT_STR)
+ return (char *)v->ptr;
+ else if (value_type(v) == VT_STR_INLINE)
+ return (char *)v->data;
+ else if (value_type(v) == VT_STR_OFF) ;
+ else {
+ UNEXPECTED("invalid value string type '%d'", value_type(v));
+ }
+
+ return NULL;
+}
+
+static inline value_t *value_string4(value_t * self, const char *str,
+ size_t len, size_t pad)
+{
+ value_blob(self, (const char *)str, len, pad);
+
+ if (self->type == VT_BLOB)
+ self->type = VT_STR;
+ else if (self->type == VT_BLOB_INLINE)
+ self->type = VT_STR_INLINE;
+
+ return self;
+}
+
+static inline value_t *value_string3(value_t * self, const char *str,
+ size_t len)
+{
+ return value_string4(self, str, len, VALUE_PAD_DEFAULT);
+}
+
+static inline value_t *value_string2(value_t * self, const char *str)
+{
+ return value_string4(self, str, strlen(str), VALUE_PAD_DEFAULT);
+}
+
+static inline value_t *value_set_string_const(value_t * self, const char *str,
+ size_t len)
+{
+ value_clear(self);
+
+ self->type = VT_STR_CONST;
+ self->ptr = (void *)str;
+ self->size = len;
+
+ return self;
+}
+
+static inline value_t *value_set_string_offset(value_t * self, size_t off,
+ size_t len)
+{
+ value_clear(self);
+
+ self->type = VT_STR_OFF;
+ self->u32 = off;
+ self->size = len;
+
+ return self;
+}
+
+/*! @endcond */
+
+/*!
+ * @brief Pretty print the contents of a @em value to stdout
+ * @memberof value
+ * @param self [in] value object @em self pointer
+ * @param out [in] output stream
+ */
+extern void value_dump(const value_t * self, FILE * out)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*! @cond */
+#define __choose__ __builtin_choose_expr
+#define __compat__ __builtin_types_compatible_p
+#define __const__ __builtin_constant_p
+/*! @endcond */
+
+/*!
+ * @fn T value_get(const value * self, T)
+ * @brief Return a @em value as primitive type @em T
+ * @note @em T can be one fo the following types:
+ * - @em char, @em int8_t
+ * - @em short, @em int16_t
+ * - @em int, @em long @em int32_t
+ * - @em long @em long, @em int64_t
+ * - @em unsigned @em char, @em uint8_t
+ * - @em unsigned @em short, @em uint16_t
+ * - @em unsigned @em int, @em long @em uint32_t
+ * - @em unsigned @em long @em long, @em uint64_t
+ * - @em float, @em double
+ * - @em char @em *, @em unsigned @em char @em *
+ * @memberof value
+ * @param self [in] value object @em self pointer
+ * @param T [in] Type to cast
+ * @throws ASSERTION if T is not equal to the object's type
+ */
+
+#if DELETE
+/*! @cond */
+#define value_get(v, T) ({ \
+ __choose__(__compat__(T, char), \
+ value_get_int8((v)), \
+ __choose__(__compat__(T, int8_t), \
+ value_get_int8((v)), \
+ __choose__(__compat__(T, short), \
+ value_get_int16((v)), \
+ __choose__(__compat__(T, int16_t), \
+ value_get_int16((v)), \
+ __choose__(__compat__(T, int), \
+ value_get_int32((v)), \
+ __choose__(__compat__(T, long), \
+ value_get_int32((v)), \
+ __choose__(__compat__(T, int32_t), \
+ value_get_int32((v)), \
+ __choose__(__compat__(T, long long), \
+ value_get_int64((v)), \
+ __choose__(__compat__(T, int64_t), \
+ value_get_int64((v)), \
+ __choose__(__compat__(T, unsigned char), \
+ value_get_uint8((v)), \
+ __choose__(__compat__(T, uint8_t), \
+ value_get_uint8((v)), \
+ __choose__(__compat__(T, unsigned short), \
+ value_get_uint16((v)), \
+ __choose__(__compat__(T, uint16_t), \
+ value_get_uint16((v)), \
+ __choose__(__compat__(T, unsigned int), \
+ value_get_uint32((v)), \
+ __choose__(__compat__(T, unsigned long), \
+ value_get_uint32((v)), \
+ __choose__(__compat__(T, uint32_t), \
+ value_get_uint32((v)), \
+ __choose__(__compat__(T, unsigned long long), \
+ value_get_uint64((v)), \
+ __choose__(__compat__(T, uint64_t), \
+ value_get_uint64((v)), \
+ __choose__(__compat__(T, float), \
+ value_get_float((v)), \
+ __choose__(__compat__(T, double), \
+ value_get_double((v)), \
+ __choose__(__compat__(T, char *), \
+ value_get_string((v)), \
+ __choose__(__compat__(T, unsigned char *), \
+ value_get_string((v)), \
+ 0xDEADBEEF)))))))))))))))))))))); \
+})
+/*! @endcond */
+#endif
+/*!
+ * @fn void value_set(const value * self, T v)
+ * @brief Assign a @em value to @em T v
+ * @memberof value
+ * @param self [in] value object @em self pointer
+ * @param v [in] value to assign
+ */
+
+#if 0
+/*! @cond */
+#define value_set(v, x) ({ \
+ __choose__(__compat__(typeof (x), char), \
+ value_set_int8((v),(int8_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), int8_t), \
+ value_set_int8((v),(int8_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), short), \
+ value_set_int16((v),(int16_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), int16_t), \
+ value_set_int16((v),(int16_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), int), \
+ value_set_int32((v),(int32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), long), \
+ value_set_int32((v),(int32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), int32_t), \
+ value_set_int32((v),(int32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), long long), \
+ value_set_int64((v),(int64_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), int64_t), \
+ value_set_int64((v),(int64_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), unsigned char), \
+ value_set_uint8((v),(uint8_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), uint8_t), \
+ value_set_uint8((v),(uint8_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), unsigned short), \
+ value_set_uint16((v),(uint16_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), uint16_t), \
+ value_set_uint16((v),(uint16_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), unsigned int), \
+ value_set_uint32((v),(uint32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), unsigned long), \
+ value_set_uint32((v),(uint32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), uint32_t), \
+ value_set_uint32((v),(uint32_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), unsigned long long), \
+ value_set_uint64((v),(uint64_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), uint64_t), \
+ value_set_uint64((v),(uint64_t)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), float), \
+ value_set_float((v),(float)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), double), \
+ value_set_double((v),(double)(intptr_t)(x)), \
+ __choose__(__compat__(typeof (x), char *), \
+ __choose__(__const__((x)), \
+ value_set_string_const((v),(const char *)(intptr_t)(x), \
+ strlen((const char *)(intptr_t)(x))),\
+ value_set_string((v),(char *)(intptr_t)(x), \
+ strlen((const char *)(intptr_t)(x)))), \
+ __choose__(__compat__(typeof (x), unsigned char *), \
+ __choose__(__const__((x)), \
+ value_set_string_const((v),(const char *)(intptr_t)(x), \
+ strlen((const char *)(intptr_t)(x))),\
+ value_set_string((v),(const char *)(intptr_t)(x), \
+ strlen((const char *)(intptr_t)(x)))), \
+ ((void)0))))))))))))))))))))))); \
+})
+/*! @endcond */
+#endif
+
+/* ==================================================================== */
+
+#endif /* __VALUE_H__ */
diff --git a/clib/vector.h b/clib/vector.h
new file mode 100644
index 0000000..c67c155
--- /dev/null
+++ b/clib/vector.h
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*!
+ * @file vector.h
+ * @brief Vector container
+ * @details Vectors are a container whose elements are in sequential order
+ * @details For example,
+ * @code
+ * #include <clib/vector.h>
+ * #include <clib/vector_iter.h>
+ *
+ * int main(const int argc, const char * argv[]) {
+ * vector_t a;
+ * vector_init(&a, 4, 1024);
+ *
+ * vector_size(&a, 10);
+ *
+ * int i;
+ * for (i=0; i<10; i++)
+ * vector_put(&a, i, &i);
+ * vector_put(&a, 223, (uint32_t[]){223});
+ *
+ * vector_iter_t it;
+ * vector_iter_init(&it, &a, VI_FLAG_FWD);
+ *
+ * uint32_t * j;
+ * vector_for_each(&it, j) {
+ * printf("vec[%d]\n", *j);
+ * }
+ *
+ * vector_dump(&a, stdout);
+ * vector_delete(&a);
+ *
+ * return 0;
+ * }
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __VECTOR_H__
+#define __VECTOR_H__
+
+#include <sys/uio.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "version.h"
+#include "nargs.h"
+#include "ident.h"
+#include "mq.h"
+#include "tree.h"
+
+/* ======================================================================= */
+
+#define VECTOR_MAGIC "VCTR"
+
+#define VECTOR_NAME_SIZE 40 //!< Maximum vector name size (in bytes)
+
+#define VECTOR_FLAG_LSB 0x01 //!< Little-endian header data
+#define VECTOR_FLAG_MSB 0x02 //!< Big-endian header data
+
+#define VECTOR_ELEM_MIN 1 //!< Minimum element size (in bytes)
+#define VECTOR_ELEM_MAX 8192 //!< Maximum element size (in bytes)
+
+#define INIT_VECTOR_HEADER {INIT_IDENT,{0,},0,0,0,0,0}
+#define INIT_VECTOR {INIT_VECTOR_HEADER, INIT_TREE}
+
+/*!
+ * @brief vector container header
+ */
+struct vector_header {
+ ident_t id; //!< identification
+ char name[VECTOR_NAME_SIZE]; //!< vector name
+
+ size_t page_size; //!< data page size (in bytes)
+ size_t page_count; //!< number of data pages allocated (currently)
+
+ size_t elem_size; //!< element size (in bytes)
+ size_t elem_num; //!< element count (per page)
+
+ size_t size; //!< number of initialized elements
+};
+typedef struct vector_header vector_header_t; //!< Alias for the @em vector_header class
+
+/*!
+ * @brief vector container
+ */
+struct vector { //! The vector class
+ vector_header_t hdr; //!< Table metadata
+
+ tree_t tree; //!< @private
+};
+typedef struct vector vector_t; //!< Alias for the @em vector class
+
+/* ======================================================================= */
+
+/*!
+ * @fn void vector_init(vector_t * self, const char * name, size_t elem_size [, size_t page_size])
+ * @brief Constructs an @em vector container object
+ * @details For example,
+ * @code
+ * ...
+ * vector_t ar;
+ * vector_init(&ar, 4, 1024);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param name [in] vector object @em name string
+ * @param elem_size [in] vector element size, in bytes
+ * @param page_size [in] size of page, in bytes
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_init(...) STRCAT(vector_init, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int vector_init3(vector_t *, const char *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern int vector_init4(vector_t *, const char *, size_t, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Destructs an @em vector container object
+ * @details Deallocate all backing storage associated with this \em vector object
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 524, &count);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return None
+ */
+extern int vector_delete(vector_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Returns a reference to the element at position @em idx in the @em vector
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 524, &count);
+ * printf("ar[524] = %d\n", *(int *)vector_at(&ar, 524));
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param idx [in] vector element index
+ * @return Reference to vector element at @em idx on SUCCESS
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if vector element at @em idx is uninitialized
+ */
+extern const void *vector_at(vector_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn void vector_get(vector_t * self, size_t idx, const void * ptr, size_t count=1)
+ * @brief Copy content from the @em vector
+ * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em vector to destination pointer @em ptr
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 524, &count);
+ * vector_get(&ar, 524, &count);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param idx [in] vector element index
+ * @param ptr [out] Destination storage pointer
+ * @param count [in] Desgination element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_get(...) STRCAT(vector_get, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int vector_get3(vector_t *, size_t, void *)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern int vector_get4(vector_t *, size_t, void *, size_t)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn void vector_put(vector_t * self, size_t idx, const void * ptr, size_t count=1)
+ * @brief Assign new content to the @em vector
+ * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em vector starting at position @em elem_off
+ * @note If the fourth parameter is omitted, it defaults to 1
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 524, &count);
+ * vector_get(&ar, 524, &count);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param idx [in] vector element index
+ * @param ptr [in] Source storage pointer
+ * @param count [in] Source element count (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_put(...) STRCAT(vector_put, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern int vector_put3(vector_t *, size_t, const void *)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+extern int vector_put4(vector_t *, size_t, const void *, size_t)
+/*! @cond */
+__nonnull((1, 3)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn size_t vector_size(vector_t * self, size_t size = 1)
+ * @brief Return or set the size of the @em vector
+ * @details Return or set the number of allocated elements in the @em vector
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_size(&ar, 2040);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param size [in] New vector size
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_size(...) STRCAT(vector_size, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern size_t vector_size1(vector_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern int vector_size2(vector_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @brief Return pages of the @em vector container
+ * @details Return the number of pages in the @em vector container
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_size(&ar, 2040);
+ * printf("pages = %d\n", vector_pages(&ar));
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return The number of pages that conform the vector's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t vector_pages(vector_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return capacity of the @em vector
+ * @details Return the number of allocated and unallocated elements in the @em vector container
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_size(&ar, 2040);
+ * printf("capacity = %d\n", vector_capacity(&ar));
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return The number of total elements that conform the vector's content
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern size_t vector_capacity(vector_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+extern size_t vector_elem_size(vector_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Save (write) an @em vector object to a stream
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 7, &count);
+ * vector_put(&ar, 7000, &count);
+ * ...
+ * FILE * f = fopen("...", "w");
+ * ...
+ * vector_save(&ar, f);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param out [in] save destination stream
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern ssize_t vector_save(vector_t *, FILE *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Load (read) a @em vector object from a stream
+ * @details For example,
+ * @code
+ * ...
+ * vector ar;
+ * ...
+ * FILE * f = fopen("...", "r");
+ * ...
+ * vector_load(&ar, f);
+ * vector_dump(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @param in [in] load source stream
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern ssize_t vector_load(vector_t *, FILE *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Send (write) an @em vector object to a message queue
+ * @details For example,
+ * @code
+ * ...
+ * vector_init(&ar, 4, 1024);
+ * vector_put(&ar, 7, &count);
+ * vector_put(&ar, 7000, &count);
+ * ...
+ * mqueue mq;
+ * mqueue_init(&mq, "my_server");
+ * mqueue_create(&mq, gettid());
+ * ...
+ * vector_send(&ar, &mq);
+ * vector_delete(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int vector_send(vector_t *, mqueue_t *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief receive (read) an @em vector object from a message queue
+ * @details For example,
+ * @code
+ * ...
+ * vector ar;
+ * ...
+ * mqueue mq;
+ * mqueue_open(&mq, path);
+ * ...
+ * vector_receive(&ar, &mq);
+ * vector_dump(&ar);
+ * ...
+ * @endcode
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return non-0 on success
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int vector_receive(vector_t *, mqueue_t *)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print the contents of an @em vector to stdout
+ * @memberof vector
+ * @param self [in] vector object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern void vector_dump(vector_t *, FILE *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/* ======================================================================= */
+
+#endif /* __VECTOR_H__ */
diff --git a/clib/vector_iter.h b/clib/vector_iter.h
new file mode 100644
index 0000000..37708d0
--- /dev/null
+++ b/clib/vector_iter.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*! @file vector_iter.h
+ * @brief Vector Iterator
+ * @details Vectors are a kind of sequence container. As such, their elements
+ * are ordered following a strict linear sequence.
+ * @code
+ * ...
+ * vector_t vec;
+ * vector_init(&vec, sizeof(uint32_t), 1024);
+ * ...
+ * vector_iter_t it;
+ * vector_iter_init(&it, &vec, VI_FLAG_FWD);
+ *
+ * uint32_t * j;
+ * vector_for_each(&it, j) {
+ * printf("vec[%d] = %d\n", it.idx, *j);
+ * }
+ * ...
+ * @endcode
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2011
+ */
+
+#ifndef __VECTOR_ITER_H__
+#define __VECTOR_ITER_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "builtin.h"
+#include "vector.h"
+
+/* ======================================================================= */
+
+typedef struct vector_iter vector_iter_t; //!< Alias for the @em vector_iter class
+
+/*!
+ * @brief vector iterator
+ * @details Vector iterator class
+ */
+struct vector_iter {
+ vector_t *vector; //!< Reference to the target vector object
+ size_t idx; //!< Current position of the iterator
+ uint32_t flags; //!< Iterator configuration flags
+};
+
+/* ======================================================================= */
+
+#define VI_FLAG_NONE 0x00000000 //!< No flag mask
+#define VI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask
+#define VI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask
+#define VI_FLAG_MASK 0x00000003 //!< All flags mask
+
+/*!
+ * @brief Initializes an @em vector_iter iterator object
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @param vector [in] vector container object to iterate
+ * @param flags [in] iterator configuration flags
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ * @throws UNEXPECTED if @em array pointer is NULL
+ */
+extern int vector_iter_init(vector_iter_t *, vector_t *, uint32_t)
+/*! @cond */
+__nonnull((1, 2)) /*! @endcond */ ;
+
+/*!
+ * @brief Resets an @em vector iterator object
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern int vector_iter_clear(vector_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Return a pointer to @em vector element bytes at the current iterator position
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+extern const void *vector_iter_elem(vector_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+
+/*!
+ * @fn const void * vector_iter_inc(vector_iter_t * self, size_t count)
+ * @brief Increment the position of an @em vector iterator
+ * @details If the second (2nd) parameter is omitted, the iterator is
+ * incremented by one (1) position.
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @param count [in] Number of positions to increment (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_iter_inc(...) STRCAT(vector_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const void *vector_iter_inc1(vector_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const void *vector_iter_inc2(vector_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn const void * vector_iter_dec(vector_iter_t * self, size_t count)
+ * @brief decrement the position of an @em vector iterator
+ * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position.
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @param count [in] Number of positions to decrement (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_iter_dec(...) STRCAT(vector_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern const void *vector_iter_dec1(vector_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern const void *vector_iter_dec2(vector_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @fn const void * vector_iter_pos(vector_iter_t * self, size_t pos)
+ * @brief Return or set the iterator position witin the @em vector
+ * @details If the second (2nd) parameter is omitted, this functions returns the current position.
+ * @memberof vector_iter
+ * @param self [in] vector_iter object @em self pointer
+ * @param pos [in] new iterator position (optional)
+ * @return None
+ * @throws UNEXPECTED if @em self pointer is NULL
+ */
+/*! @cond */
+#define vector_iter_pos(...) STRCAT(vector_iter_pos, NARGS(__VA_ARGS__))(__VA_ARGS__)
+extern size_t vector_iter_pos1(vector_iter_t *)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+extern int vector_iter_pos2(vector_iter_t *, size_t)
+/*! @cond */
+__nonnull((1)) /*! @endcond */ ;
+/*! @endcond */
+
+/*!
+ * @def vector_for_each(it, i)
+ * @hideinitializer
+ * @brief Vector for-each algorithm
+ * @param it [in] Tree iterator object
+ * @param i [in] Tree element variable
+ */
+#define vector_for_each(it, i) \
+ for (i = (typeof(i))vector_iter_elem(it); \
+ i != NULL; \
+ i = (typeof(i))vector_iter_inc(it))
+
+/* ======================================================================= */
+
+#endif /* __VECTOR_ITER_H__ */
diff --git a/clib/version.h b/clib/version.h
new file mode 100644
index 0000000..1c990b2
--- /dev/null
+++ b/clib/version.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+#ifndef __VERSION__H__
+#define __VERSION__H__
+
+#define VER_TO_MAJOR(x) (((x) & 0xFF000000) >> 24)
+#define VER_TO_MINOR(x) (((x) & 0x00FF0000) >> 16)
+#define VER_TO_PATCH(x) (((x) & 0x0000FF00) >> 8)
+
+#define MAJOR_TO_VER(x) ((0xFF & (x)) << 24)
+#define MINOR_TO_VER(x) ((0xFF & (x)) << 16)
+#define PATCH_TO_VER(x) ((0xFF & (x)) << 8)
+
+#define VER(x,y,z) (MAJOR_TO_VER(x) | MINOR_TO_VER(y) | PATCH_TO_VER(z))
+
+#endif /* __VERSION__H__ */
diff --git a/clib/watch.h b/clib/watch.h
new file mode 100644
index 0000000..7f09f3e
--- /dev/null
+++ b/clib/watch.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * File: watch.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Directory activity callback monitor
+ * Note:
+ * Date: 10/03/10
+ */
+
+#ifndef __WATCH_H__
+#define __WATCH_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <sys/inotify.h>
+
+#include "attribute.h"
+#include "builtin.h"
+#include "assert.h"
+
+#include "array.h"
+
+/* ==================================================================== */
+
+#define WATCH_ACCESS IN_ACCESS
+#define WATCH_ATTRIB IN_ATTRIB
+#define WATCH_CLOSE_WRITE IN_CLOSE_WRITE
+#define WATCH_CLOSE_NOWRITE IN_CLOSE_NOWRITE
+#define WATCH_CREATE IN_CREATE
+#define WATCH_DELETE IN_DELETE
+#define WATCH_DELETE_SELF IN_DELETE_SELF
+#define WATCH_MODIFY IN_MODIFY
+#define WATCH_MOVE_SELF IN_MOVE_SELF
+#define WATCH_MOVE_FROM IN_MOVE_FROM
+#define WATCH_MOVE_TO IN_MOVE_TO
+#define WATCH_OPEN IN_OPEN
+
+typedef struct watch watch_t;
+typedef struct watch_callback watch_callback_t;
+typedef struct inotify_event watch_event_t;
+
+typedef int (*watch_f) (watch_event_t *);
+
+struct watch {
+ int fd;
+ array_t callbacks;
+};
+
+struct watch_callback {
+ void *data;
+ watch_f func;
+};
+
+/* ======================================================================= */
+
+extern void watch_init(watch_t * self) __nonnull((1));
+extern void watch_delete(watch_t * self) __nonnull((1));
+
+extern int watch_fileno(watch_t * self) __nonnull((1));
+
+extern uint32_t watch_add(watch_t * self, const char *path, uint32_t events,
+ watch_callback_t * cb) __nonnull((1, 2));
+extern void watch_remove(watch_t * self, uint32_t wd) __nonnull((1));
+
+extern void watch_wait(watch_t * self) __nonnull((1));
+
+/* ======================================================================= */
+
+#endif /* __watch_H__ */
diff --git a/clib/x86/Makefile b/clib/x86/Makefile
new file mode 100644
index 0000000..af7b947
--- /dev/null
+++ b/clib/x86/Makefile
@@ -0,0 +1,5 @@
+DEPTH = ..
+
+CLIB_INSTALL = $(INST_USR_X86)
+
+include ../Rules.mk
diff --git a/clib/x86/cunit/Makefile b/clib/x86/cunit/Makefile
new file mode 100644
index 0000000..5748fb1
--- /dev/null
+++ b/clib/x86/cunit/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ../../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.x86.mk
+
+CLIB_INSTALL = $(INST_TESTS_X86)/clib
+
+include ../../Rules.cunit.mk
diff --git a/clib/x86/test/Makefile b/clib/x86/test/Makefile
new file mode 100644
index 0000000..e410eee
--- /dev/null
+++ b/clib/x86/test/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ../../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.x86.mk
+
+CLIB_INSTALL = $(INST_TESTS_X86)/clib
+
+include ../../Rules.test.mk
diff --git a/ecc/.gitignore b/ecc/.gitignore
new file mode 100644
index 0000000..b879775
--- /dev/null
+++ b/ecc/.gitignore
@@ -0,0 +1 @@
+*/ecc
diff --git a/ecc/Makefile b/ecc/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/ecc/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/ecc/Rules.mk b/ecc/Rules.mk
new file mode 100644
index 0000000..a2e6caf
--- /dev/null
+++ b/ecc/Rules.mk
@@ -0,0 +1,20 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/.. -iquote..
+LDFLAGS += -L. -L$(DEPTH)/../clib/x86 -m32
+
+OBJS=main.o
+
+TARGETS=ecc
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+ecc: main.o $(DEPTH)/../clib/x86/libclib.a
+ $(CC) $(LDFLAGS) -o $@ $^ -lpthread
+
+install: $(TARGETS)
+ $(INSTALL) ecc $(ECC_INSTALL)/bin
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/ecc/src/main.c b/ecc/src/main.c
new file mode 100644
index 0000000..dc6899c
--- /dev/null
+++ b/ecc/src/main.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: main.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cmdline tool for ECC
+ * Date: 08/31/2012
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/ecc.h>
+#include <clib/err.h>
+
+#include "main.h"
+
+#define ECC_SIZE 8
+
+args_t args;
+
+static const char copyright[] __unused__ =
+ "Licensed Internal Code - Property of IBM\n"
+ "IBM Support Processor Licensed Internal Code\n"
+ "(c) Copyright IBM Corp 2012 All Rights Reserved\n"
+ "US Government Users Restricted Rights - Use, duplication\n"
+ "or disclosure restricted by GSA ADP Schedule Contract\n" "with IBM Corp.";
+
+static void usage(const char *short_name, bool verbose)
+{
+ const char *n = short_name;
+ FILE *e = stderr;
+
+ fprintf(e, "FSP NOR ECC Tool v%d.%d.%d -- Authors: <shaun@us.ibm.com>\n"
+ "(c) Copyright IBM Corp 2012 All Rights Reserved\n",
+ ECC_MAJOR, ECC_MINOR, ECC_PATCH);
+
+ fprintf(e, "\nUsage:\n");
+ fprintf(e, " %s <command> <path> <options>...\n", n);
+
+ fprintf(e, "\nExamples:\n");
+ fprintf(e, " %s --inject sample.nor --output sample.nor.ecc\n", n);
+ fprintf(e, " %s --remove sample.nor.ecc --output sample.nor\n", n);
+ fprintf(e, " %s --hexdump sample.nor.ecc\n", n);
+
+ fprintf(e, "\nCommands:\n");
+ fprintf(e, " -I, --inject <path> [options]\n");
+ if (verbose)
+ fprintf(e,
+ "\n Make a copy of the input file <path> and inject an ECC syndrom byte for\n"
+ " every 8 bytes of input data.\n\n");
+
+ fprintf(e, " -R, --remove <path> [options]\n");
+ if (verbose)
+ fprintf(e,
+ "\n Make a copy of the input file <path> and remove the ECC syndrom byte for\n"
+ " for every 9 byte of input data.\n\n");
+
+ fprintf(e, " -H, --hexdump <path> [options]\n");
+ if (verbose)
+ fprintf(e,
+ "\n Hex dump the contents of file <path> to stdout.\n\n");
+
+ fprintf(e, "\nOptions:\n");
+ fprintf(e, " -o, --output <path>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the output file path name.\n\n");
+
+ fprintf(e, " -h, --help\n");
+ if (verbose)
+ fprintf(e, "\n Write this help text to stderr and exit\n");
+
+ fprintf(e, "\nFlags:\n");
+ fprintf(e, " -p, --p8\n");
+ if (verbose)
+ fprintf(e,
+ "\n Invert the ECC bits for the P8 ECC engine.\n\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e,
+ "Report bugs to <https://bugzilla.linux.ibm.com/> (vendor='MCP for FSP*')\n");
+}
+
+static int process_argument(args_t * args, int opt, const char *optarg)
+{
+ assert(args != NULL);
+
+ switch (opt) {
+ case c_INJECT: /* inject */
+ case c_REMOVE: /* remove */
+ case c_HEXDUMP: /* hexdump */
+ if (args->cmd != c_ERROR) {
+ UNEXPECTED("commands '%c' and '%c' are mutually "
+ "exclusive", args->cmd, opt);
+ return -1;
+ }
+
+ args->cmd = (cmd_t) opt;
+ args->path = strdup(optarg);
+
+ break;
+ case o_OUTPUT: /* offset */
+ args->file = strdup(optarg);
+ break;
+ case f_FORCE: /* force */
+ args->force = (flag_t) opt;
+ break;
+ case f_VERBOSE: /* verbose */
+ args->verbose = (flag_t) opt;
+ break;
+ case f_P8: /* p8 */
+ args->p8 = (flag_t) opt;
+ break;
+ case f_HELP: /* help */
+ usage(args->short_name, true);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?': /* error */
+ default:
+ usage(args->short_name, false);
+ UNEXPECTED("unknown option '%c', please see "
+ "--help for details\n", opt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_option(args_t * args, const char *opt)
+{
+ assert(args != NULL);
+ assert(opt != NULL);
+
+ if (args->opt_sz <= args->opt_nr) {
+ args->opt_sz += 5;
+ args->opt = (const char **)realloc(args->opt,
+ sizeof(*args->opt) *
+ args->opt_sz);
+ memset(args->opt + args->opt_nr, 0,
+ sizeof(*args->opt) * (args->opt_sz - args->opt_nr));
+ }
+
+ args->opt[args->opt_nr] = strdup(opt);
+ args->opt_nr++;
+
+ return 0;
+}
+
+bool check_extension(const char *path, const char *ext)
+{
+ int len = strlen(path), ext_len = strlen(ext);
+ return (ext_len < len)
+ && (strncasecmp(path + len - ext_len, ext, ext_len) == 0);
+}
+
+static int validate_args(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->cmd == c_ERROR) {
+ usage(args->short_name, false);
+ UNEXPECTED("no command specified, please see --help "
+ "for details\n");
+ return -1;
+ }
+
+ if (args->path == NULL) {
+ UNEXPECTED("no path specified, please see --help "
+ "for details");
+ return -1;
+ }
+
+ #define REQUIRED(name,cmd) ({ \
+ if (args->name == NULL) { \
+ UNEXPECTED("--%s is required for the --%s command", \
+ #name, #cmd); \
+ return -1; \
+ } \
+ })
+
+ #define UNSUPPORTED(name,cmd) ({ \
+ if (args->name != NULL) { \
+ UNEXPECTED("--%s is unsupported for the --%s command", \
+ #name, #cmd); \
+ return -1; \
+ } \
+ })
+
+
+ if (args->cmd == c_INJECT) {
+ if (check_extension(args->path, ECC_EXT)) {
+ UNEXPECTED("'%s' extension '%s' already exists "
+ "-- ignored", args->path, ECC_EXT);
+ return -1;
+ }
+
+ if (args->file == NULL) {
+ args->file = (char *)malloc(strlen(args->path) + 8);
+ assert(args->file != NULL);
+ sprintf((char *)args->file, "%s%s", args->path,
+ ECC_EXT);
+
+ if (args->force != f_FORCE)
+ fprintf(stderr, "%s: --output <file> missing, "
+ "writing output to '%s'\n",
+ args->short_name, args->file);
+ }
+ } else if (args->cmd == c_REMOVE) {
+ if (!check_extension(args->path, ECC_EXT)) {
+ UNEXPECTED("'%s' unknown extension, must be '%s' -- "
+ "ignored", args->path, ECC_EXT);
+ return -1;
+ }
+
+ if (args->file == NULL) {
+ args->file = strdup(args->path);
+ assert(args->file != NULL);
+ sprintf((char *)args->file, "%*s",
+ strlen(args->path) - strlen(ECC_EXT),
+ args->path);
+
+ fprintf(stderr, "%s: --output <file> missing, writing "
+ "output to '%s'\n",
+ args->short_name, args->file);
+ }
+ } else if (args->cmd == c_HEXDUMP) {
+ if (!check_extension(args->path, ECC_EXT)) {
+ UNEXPECTED("'%s' unknown extension, must be '%s' -- "
+ "ignored", args->path, ECC_EXT);
+ return -1;
+ }
+ } else {
+ UNEXPECTED("'%c' invalid command", args->cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int command_inject(args_t * args)
+{
+ assert(args != NULL);
+
+ struct stat st;
+ if (stat(args->path, &st) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ FILE *i = fopen(args->path, "r");
+ if (i == NULL) {
+ ERRNO(errno);
+ return-1;
+ }
+
+ FILE *o = fopen(args->file, "w");
+ if (o == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+#define INPUT_SIZE (4096 - (4096 / ECC_SIZE))
+ char input[INPUT_SIZE]; // 4KB less 512 ECC bytes
+#undef INPUT_SIZE
+
+ size_t count = 0;
+ while (count < st.st_size) {
+ clearerr(i);
+
+ size_t rc = fread(input, 1, sizeof input, i);
+ if (rc == 0) {
+ int err = ferror(i);
+ if (err) {
+ ERRNO(errno);
+ return -1;
+ } else
+ break;
+ }
+
+ count += rc;
+
+#define OUTPUT_SIZE 4096
+ char output[OUTPUT_SIZE];
+#undef OUTPUT_SIZE
+
+ memset(output + sizeof input, 0, sizeof output - sizeof input);
+
+ rc = (rc + 7) & ~7; // 8-byte alignment
+
+ ssize_t injected_size = 0;
+ if (args->p8 == f_P8)
+ injected_size =
+ p8_ecc_inject(output, sizeof output, input, rc);
+ else
+ injected_size =
+ sfc_ecc_inject(output, sizeof output, input, rc);
+ if (injected_size < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ clearerr(o);
+ rc = fwrite(output, 1, injected_size, o);
+ if (rc == 0) {
+ int err = ferror(o);
+ if (err) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+ }
+
+ if (fclose(i) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fclose(o) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int command_remove(args_t * args)
+{
+ assert(args != NULL);
+
+ struct stat st;
+ if (stat(args->path, &st) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ FILE *i = fopen(args->path, "r");
+ if (i == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ FILE *o = fopen(args->file, "w");
+ if (o == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+#define INPUT_SIZE 4086
+ char input[INPUT_SIZE]; // multiple of 9-bytes
+#undef INPUT_SIZE
+
+ size_t count = 0;
+ while (count < st.st_size) {
+ clearerr(i);
+
+ size_t rc = fread(input, 1, sizeof input, i);
+ if (rc == 0) {
+ int err = ferror(i);
+ if (err) {
+ ERRNO(errno);
+ return -1;
+ } else
+ break;
+ }
+
+ count += rc;
+
+ char output[(((sizeof input)/(ECC_SIZE+1))*ECC_SIZE) ];
+
+ ssize_t removed_size;
+ if (args->p8 == f_P8)
+ removed_size =
+ p8_ecc_remove_size(output, sizeof output, input, rc);
+ else
+ removed_size =
+ sfc_ecc_remove(output, sizeof output, input, rc);
+ if (removed_size < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ clearerr(o);
+ rc = fwrite(output, 1, removed_size, o);
+ if (rc == 0) {
+ int err = ferror(o);
+ if (err) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+ }
+
+ if (fclose(i) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fclose(o) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int command_hexdump(args_t * args)
+{
+ assert(args != NULL);
+
+ struct stat st;
+ if (stat(args->path, &st) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ FILE *i = fopen(args->path, "r");
+ if (i == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ FILE *o = stdout;
+ if (args->file != NULL) {
+ o = fopen(args->file, "w");
+ if (o == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+#define INPUT_SIZE 4086
+ char input[INPUT_SIZE]; // multiple of 9-bytes
+#undef INPUT_SIZE
+
+ if (setvbuf(i, NULL, _IOFBF, __round_pow2(sizeof input)) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t count = 0;
+ while (count < st.st_size) {
+ clearerr(i);
+
+ size_t rc = fread(input, 1, sizeof input, i);
+ if (rc == 0) {
+ int err = ferror(i);
+ if (err) {
+ ERRNO(errno);
+ return -1;
+ } else
+ break;
+ }
+
+ if (args->p8 == f_P8)
+ p8_ecc_dump(o, count, input, rc);
+ else
+ sfc_ecc_dump(o, count, input, rc);
+
+ count += rc;
+ }
+
+ if (fclose(i) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (o != stdout) {
+ if (fclose(o) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int process_args(args_t * args)
+{
+ assert(args != NULL);
+
+ switch (args->cmd) {
+ case c_INJECT:
+ command_inject(args);
+ break;
+ case c_REMOVE:
+ command_remove(args);
+ break;
+ case c_HEXDUMP:
+ command_hexdump(args);
+ break;
+ default:
+ UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void args_dump(args_t * args)
+{
+ assert(args != NULL);
+
+ printf("short_name[%s]\n", args->short_name);
+ printf("path[%s]\n", args->path);
+ printf("cmd[%d]\n", args->cmd);
+ printf("output[%s]\n", args->file);
+ printf("force[%d]\n", args->force);
+ printf("p8[%d]\n", args->p8);
+ printf("verbose[%d]\n", args->force);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_opts[] = {
+ /* commands */
+ {"inject", required_argument, NULL, c_INJECT},
+ {"remove", required_argument, NULL, c_REMOVE},
+ {"hexdump", required_argument, NULL, c_HEXDUMP},
+ /* options */
+ {"output", required_argument, NULL, o_OUTPUT},
+ /* flags */
+ {"force", no_argument, NULL, f_FORCE},
+ {"p8", no_argument, NULL, f_P8},
+ {"verbose", no_argument, NULL, f_VERBOSE},
+ {"help", no_argument, NULL, f_HELP},
+ {0, 0, 0, 0}
+ };
+
+ static const char *short_opts = "I:R:H:o:fpvh";
+
+ int rc = EXIT_FAILURE;
+
+ if (argc == 1)
+ usage(args.short_name, false), exit(rc);
+
+ int opt = 0, idx = 0;
+ while ((opt = getopt_long(argc, argv, short_opts, long_opts,
+ &idx)) != -1)
+ if (process_argument(&args, opt, optarg) < 0)
+ goto error;
+
+ /* getopt_long doesn't know what to do with orphans, */
+ /* so we'll scoop them up here, and deal with them later */
+
+ while (optind < argc)
+ if (process_option(&args, argv[optind++]) < 0)
+ goto error;
+
+ if (args.verbose == f_VERBOSE)
+ args_dump(&args);
+
+ if (validate_args(&args) < 0)
+ goto error;
+ if (process_args(&args) < 0)
+ goto error;
+
+ rc = EXIT_SUCCESS;
+
+ if (false) {
+ err_t *err;
+error:
+ err = err_get();
+ assert(err != NULL);
+
+ fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+ }
+
+ return rc;
+}
+
+static void __ctor__(void) __constructor;
+static void __ctor__(void)
+{
+ /* early initialization before main() is called the crt0 */
+ args.short_name = program_invocation_short_name;
+}
diff --git a/ecc/src/main.h b/ecc/src/main.h
new file mode 100644
index 0000000..c4f04a0
--- /dev/null
+++ b/ecc/src/main.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: main.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cmdline tool for ECC
+ * Date: 08/30/2012
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include <stdio.h>
+
+typedef enum {
+ c_ERROR = 0,
+ c_INJECT = 'I',
+ c_REMOVE = 'R',
+ c_HEXDUMP = 'H',
+} cmd_t;
+
+typedef enum {
+ o_ERROR = 0,
+ o_OUTPUT = 'o',
+} option_t;
+
+typedef enum {
+ f_ERROR = 0,
+ f_FORCE = 'f',
+ f_P8 = 'p',
+ f_VERBOSE = 'v',
+ f_HELP = 'h',
+} flag_t;
+
+typedef struct {
+ const char * short_name;
+
+ /* target */
+ const char * path;
+
+ /* command */
+ cmd_t cmd;
+
+ /* options */
+ const char * file;
+
+ /* flags */
+ flag_t force, p8, verbose;
+
+ const char ** opt;
+ int opt_sz, opt_nr;
+} args_t;
+
+extern args_t args;
+
+#define ECC_MAJOR 0x02
+#define ECC_MINOR 0x00
+#define ECC_PATCH 0x00
+
+#define ECC_EXT ".ecc"
+
+#endif /* __MAIN_H__ */
diff --git a/ecc/x86/Makefile b/ecc/x86/Makefile
new file mode 100644
index 0000000..e111ae8
--- /dev/null
+++ b/ecc/x86/Makefile
@@ -0,0 +1,6 @@
+DEPTH = ..
+
+ARCH=x86
+ECC_INSTALL = $(INST_USR_X86)
+
+include ../Rules.mk
diff --git a/fcp/.gitignore b/fcp/.gitignore
new file mode 100644
index 0000000..3a1f0a1
--- /dev/null
+++ b/fcp/.gitignore
@@ -0,0 +1,3 @@
+*/ffs
+*/fcp
+test/test_libffs
diff --git a/fcp/Makefile b/fcp/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/fcp/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/fcp/Rules.mk b/fcp/Rules.mk
new file mode 100644
index 0000000..490afd4
--- /dev/null
+++ b/fcp/Rules.mk
@@ -0,0 +1,30 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64
+CFLAGS += -I$(DEPTH)/.. -iquote..
+
+LDFLAGS += -L. -m32
+
+NAME=fcp
+
+CLIB=$(DEPTH)/../clib/x86/libclib.a
+FFS=$(DEPTH)/../ffs/x86/libffs.a
+
+OBJS=cmd_list.o cmd_read.o cmd_write.o cmd_erase.o \
+ cmd_copy.o cmd_trunc.o cmd_user.o misc.o main.o
+
+TARGETS=$(NAME)
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+$(NAME): $(OBJS) $(FFS) $(CLIB)
+ $(CC) $(LDFLAGS) -Wl,-no-whole-archive \
+ -o $@ $^ -lpthread -ldl -lrt
+
+install: $(TARGETS)
+ $(INSTALL) fcp $(FCP_INSTALL)/bin
+ $(INSTALL) ../fcp.sh $(FCP_INSTALL_TEST)
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/fcp/fcp.sh b/fcp/fcp.sh
new file mode 100755
index 0000000..6fa56a1
--- /dev/null
+++ b/fcp/fcp.sh
@@ -0,0 +1,313 @@
+#!/bin/bash
+# ===================================================================
+# Copyright (c) International Business Machines Corp., 2012
+#
+# 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
+#
+# File: ffs cmdline test script
+# Author: Shaun wetzstein <shaun@us.ibm.com>
+# Date: 02/06/13
+#
+# Todo:
+# 1) function to check syntax of each command
+
+CP=cp
+RM=rm
+FFS=ffs
+FCP=fcp
+MKDIR=mkdir
+GREP=grep
+HEAD=head
+HEX=hexdump
+CRC=crc32
+DD=dd
+DIFF=diff
+CAT=cat
+CP=cp
+TRUNC=truncate
+
+TARGET=test.nor
+COPY=copy.nor
+TMP=/tmp/fcp.$$
+URANDOM=/dev/urandom
+POFFSET="0x00000,0x10000"
+
+FAIL=1
+PASS=0
+
+KB=$((1*1024))
+MB=$(($KB*1024))
+
+function expect()
+{
+ local expect=${1}
+ shift
+ local command=${*}
+ eval $command
+ local actual=${?}
+
+ if [[ ${expect} -eq ${actual} ]]; then
+ echo "[PASSED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ else
+ echo "[FAILED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ exit 1
+ fi
+}
+
+function pass()
+{
+ expect ${PASS} ${*}
+}
+
+function fail()
+{
+ expect ${FAIL} ${*}
+}
+
+function size()
+{
+ local size=$(stat -L -c %s ${2})
+ if [[ ${1} == ${size} ]]; then
+ echo "[PASSED] size: '${2}' ===> expect=${1}, actual=${size}"
+ else
+ echo "[FAILED] size: '${2}' ===> expect=${1}, actual=${size}"
+ exit 1
+ fi
+}
+
+function crc()
+{
+ local crc=$(${CRC} ${2})
+ if [[ ${1} == ${crc} ]]; then
+ echo "[PASSED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ else
+ echo "[FAILED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ exit 1
+ fi
+}
+
+function setup()
+{
+ local target=${TMP}/${TARGET}
+ local output=${TMP}/${TARGET}.txt
+
+ pass ${RM} -rf ${TMP}
+ pass ${MKDIR} -p ${TMP}
+ pass ${RM} -f ${target}
+
+ pass ${FFS} -t ${target} -s 64M -b 64K -p 0x3F0000 -C
+ pass ${FFS} -t ${target} -s 64M -b 64K -p 0x7F0000 -C
+
+ for ((j=0; j<2; j++)); do
+ local name="logical${j}"
+ local base=$((${j}*$MB*4))
+
+ pass ${FFS} -t ${target} -n ${name} -g 0 -l -A
+ pass ${FFS} -t ${target} -n ${name} -L > ${output}
+
+ pass ${GREP} ${name} ${output} > /dev/null
+ pass ${GREP} "l-----" ${output} > /dev/null
+
+ for ((i=0; i<4; i++)); do
+ local full="${name}/entry${i}"
+ local offset=$((${base}+${i}*$MB))
+
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ pass ${FFS} -t ${target} -o ${offset} -s ${size} \
+ -g 0 -n ${full} -a ${i} -A
+
+ pass ${FFS} -t ${target} -n ${full} -L > ${output}
+ pass ${GREP} ${full} ${output} > /dev/null
+ pass ${GREP} "d-----" ${output} > /dev/null
+
+ local range=$(printf "%.8x-%.8x" ${offset} \
+ $((${offset}+${size}-1)))
+
+ pass ${GREP} ${range} ${output} > /dev/null
+ pass ${GREP} $(printf "%x" ${size}) ${output} > \
+ /dev/null
+ pass ${RM} -f ${output}
+ done
+ done
+}
+
+function cleanup()
+{
+ pass ${RM} -rf ${TMP}
+}
+
+function erase()
+{
+ local target=${TMP}/${TARGET}
+ local name="logical"
+
+ for ((i=0; i<4; i++)); do
+ local output=${TMP}/entry${i}.hex
+ local full=${name}0/entry${i}
+
+ pass ${FCP} ${target}:${full} -E ${i}
+ pass ${FCP} -T ${target}:${full}
+ pass ${FCP} -R ${target}:${full} - | ${HEX} -C > ${output}
+
+ local p=$(printf "%2.2x %2.2x %2.2x %2.2x" $i $i $i $i)
+ pass ${GREP} \"${p} ${p}\" ${output}
+
+ pass ${RM} -f ${output}
+ done
+
+ pass ${FCP} ${target}:${name}0 -E 0x00
+ pass ${FCP} ${target}:${name}1 -E 0x00
+}
+
+function write()
+{
+ local target=${TMP}/${TARGET}
+ local output=${TMP}/${TARGET}.txt
+
+ local name="logical"
+ local input=${TMP}/write.in
+ local output=${TMP}/write.out
+
+ if [[ -z "${1}" ]]; then
+ local block=$((64*KB))
+ else
+ local block=${1}
+ fi
+
+ for ((i=0; i<4; i++)); do
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ local count=$((${size}/$block))
+
+ for ((c=1; c<=${count}; c++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} \
+ count=${c} 2> /dev/null
+
+ pass ${CRC} ${input} > ${output}
+ local crc=$(${CAT} ${output})
+ local full=${name}0/entry${i}
+
+ pass ${FCP} ${target}:${full} -E 0x00
+ pass ${FCP} -W ${input} ${target}:${full}
+ pass ${FCP} ${target}:${full} -U 0=${crc}
+ pass ${FCP} ${target}:${full} -R - -f | cat > ${output}
+
+ size $((${c}*${block})) ${output}
+ crc ${crc} ${output}
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FCP} ${target}:${full} -o 0x3F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+ pass "${FCP} ${target}:${full} -o 0x7F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input} ${output}
+ done
+ done
+}
+
+function copy()
+{
+ local src=${TMP}/${TARGET}
+ local dst=${TMP}/${TARGET}.copy
+
+ local input=${TMP}/copy.in
+ local output=${TMP}/copy.out
+
+ if [[ -z "${1}" ]]; then
+ local block=$((64*KB))
+ else
+ local block=${1}
+ fi
+
+ pass ${TRUNC} -s $(stat -L -c %s ${src}) ${dst}
+
+ for ((i=0; i<4; i++)); do
+ if [[ ${i} -eq 3 ]]; then
+ local size=$(($MB-64*${KB}))
+ else
+ local size=$MB
+ fi
+
+ local count=$((${size}/$block))
+
+ for ((c=1; c<=${count}; c++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${c} 2> /dev/null
+ local crc=$(${CRC} ${input})
+
+ local name="logical0/entry${i}"
+
+ pass ${FCP} ${src}:${name} -E 0x00
+ pass ${FCP} ${input} ${src}:${name} -W
+ pass ${FCP} ${src}:${name} ${output}.src -f -R
+ pass ${FCP} ${src}:${name} -U 0 0=${crc}
+
+ pass ${FCP} ${src}:${name} ${dst}:${name} -C -v -f
+ pass ${FCP} ${src}:${name} ${dst}:${name} -M -v
+
+ pass ${FCP} ${dst}:${name} ${output}.dst -R -f
+ size $((${c}*${block})) ${output}.src
+ size $((${c}*${block})) ${output}.dst
+ crc ${crc} ${output}.src
+ crc ${crc} ${output}.dst
+ pass ${DIFF} ${output}.src ${output}.dst
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FCP} ${dst}:${name} -o 0x3F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+ pass "${FCP} ${dst}:${name} -o 0x7F0000 -U 0 | \
+ ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input}* ${output}*
+ done
+ done
+
+ pass ${FCP} ${src}":logical0" ${src}":logical1" -C -v # logical mirror
+ pass ${FCP} ${src}":logical1" ${dst}":logical1" -C -v # logical copy
+
+ expect 2 ${DIFF} ${src} ${dst}
+}
+
+function main()
+{
+ erase
+ write $((21*$KB))
+ write $((64*$KB))
+ copy $((21*$KB))
+ copy $((64*$KB))
+}
+
+setup
+if [[ -z "${1:-}" ]]; then
+ main
+else
+ case "$1" in
+ erase ) erase ;;
+ write ) write ;;
+ copy ) copy ;;
+ * ) echo "$1 not implemented"; exit 1 ;;
+ esac
+ exit 0;
+fi
+cleanup
diff --git a/fcp/ppc/Makefile b/fcp/ppc/Makefile
new file mode 100644
index 0000000..65df0dc
--- /dev/null
+++ b/fcp/ppc/Makefile
@@ -0,0 +1,9 @@
+DEPTH = ../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.ppc.mk
+
+ARCH=ppc
+FCP_INSTALL = $(INST_USR)
+FCP_INSTALL_TEST = $(INST_TESTS)
+
+include ../Rules.mk
diff --git a/fcp/src/cmd_copy.c b/fcp/src/cmd_copy.c
new file mode 100644
index 0000000..4b496b6
--- /dev/null
+++ b/fcp/src/cmd_copy.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_copy.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: copy & compare implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int validate_files(ffs_t * src, ffs_t * dst)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ size_t s, d;
+
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' block "
+ "size differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_BLOCK_COUNT, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_COUNT, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' block "
+ "count differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_ENTRY_SIZE, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_ENTRY_SIZE, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' entry "
+ "size differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ if (__ffs_info(src, FFS_INFO_VERSION, &s) < 0)
+ return -1;
+ if (__ffs_info(dst, FFS_INFO_VERSION, &d) < 0)
+ return -1;
+ if (s != d) {
+ UNEXPECTED("source '%s' and destination '%s' version "
+ "differs, use --force to overwrite\n",
+ src->path, dst->path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __copy_entry(args_t * args,
+ ffs_t * src_ffs, ffs_entry_t * src_entry,
+ ffs_t * dst_ffs, ffs_entry_t * dst_entry,
+ entry_list_t * done_list)
+{
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ return -1;
+ }
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0) {
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (dst_entry->type == FFS_TYPE_PARTITION) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: partition table (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_PARTITION) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: partition table (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED) {
+ if (dst_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", dst_ffs->offset,
+ full_dst_name);
+ return 0;
+ }
+ if (src_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", src_ffs->offset,
+ full_src_name);
+ return 0;
+ }
+ }
+
+ if (src_entry->size != dst_entry->size) {
+ UNEXPECTED("%8llx: %s: source partition '%s' size mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (src_entry->type != dst_entry->type) {
+ UNEXPECTED("%8llx: %s: source partition '%s' type mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (__ffs_entry_truncate(dst_ffs, full_dst_name,
+ src_entry->actual) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n",
+ dst_ffs->offset, full_dst_name, src_entry->actual);
+
+ uint32_t src_val, dst_val;
+ for (uint32_t i=0; i<FFS_USER_WORDS; i++) {
+ __ffs_entry_user_get(src_ffs, full_src_name, i, &src_val);
+ __ffs_entry_user_get(dst_ffs, full_dst_name, i, &dst_val);
+
+ if (args->force != f_FORCE && i == USER_DATA_VOL)
+ continue;
+
+ if (src_val != dst_val) {
+ if (__ffs_entry_user_put(dst_ffs, full_dst_name,
+ i, src_val) < 0)
+ return -1;
+ }
+ }
+ if (args->verbose == f_VERBOSE)
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy user[] from '%s' "
+ "(done)\n", dst_ffs->offset, full_dst_name,
+ src_ffs->path);
+
+ if (entry_list_exists(done_list, src_entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy from '%s' (skip)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, src_entry) < 0)
+ return -1;
+
+ if (fcp_copy_entry(src_ffs, full_src_name, dst_ffs, full_dst_name) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: copy from '%s' (done)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+
+ return 0;
+}
+
+static int __compare_entry(args_t * args,
+ ffs_t * src_ffs, ffs_entry_t * src_entry,
+ ffs_t * dst_ffs, ffs_entry_t * dst_entry,
+ entry_list_t * done_list)
+{
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ return -1;
+ }
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0) {
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ dst_ffs->offset, full_dst_name);
+ return 0;
+ }
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical partition (skip)\n",
+ src_ffs->offset, full_src_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED) {
+ if (dst_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", dst_ffs->offset,
+ full_dst_name);
+ return 0;
+ }
+ if (src_entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "partition (skip)\n", src_ffs->offset,
+ full_src_name);
+ return 0;
+ }
+ }
+
+ if (src_entry->size != dst_entry->size) {
+ UNEXPECTED("%8llx: %s: source partition '%s' size mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (src_entry->type != dst_entry->type) {
+ UNEXPECTED("%8llx: %s: source partition '%s' type mismatch "
+ "'%x', use --force to overwrite\n", dst_ffs->offset,
+ full_dst_name, full_src_name, dst_entry->size);
+ return -1;
+ }
+
+ if (entry_list_exists(done_list, src_entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: compare from '%s' (skip)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, src_entry) < 0)
+ return -1;
+
+ if (fcp_compare_entry(src_ffs, full_src_name,
+ dst_ffs, full_dst_name) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: compare from '%s' (done)\n",
+ dst_ffs->offset, full_dst_name, src_ffs->path);
+
+ return 0;
+}
+
+static int __force_part(ffs_t * src, FILE * dst)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ char part[block_size];
+ memset(part, 0, block_size);
+
+ if (__ffs_entry_read(src, FFS_PARTITION_NAME, part,
+ 0, block_size) < 0)
+ return -1;
+
+ uint32_t offset;
+ if (__ffs_info(src, FFS_INFO_OFFSET, &offset) < 0)
+ return -1;
+
+ if (fseek(dst, offset, SEEK_SET) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fwrite(part, 1, block_size, dst) != block_size) {
+ if (ferror(dst)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ if (fflush(dst) == EOF) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __copy_compare(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+ assert(done_list != NULL);
+
+ char * src_type = args->src_type;
+ char * src_target = args->src_target;
+ char * src_name = args->src_name;
+
+ char * dst_type = args->dst_type;
+ char * dst_target = args->dst_target;
+ char * dst_name = src_name;
+
+ if (args->dst_name != NULL && *args->dst_name != '\0')
+ dst_name = args->dst_name;
+
+ if (src_name == NULL)
+ src_name = "*";
+ if (dst_name == NULL)
+ dst_name = "*";
+
+ RAII(FILE*, src_file, __fopen(src_type, src_target, "r", debug),
+ fclose);
+ if (src_file == NULL)
+ return -1;
+ if (check_file(src_target, src_file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, src_ffs, __ffs_fopen(src_file, offset), __ffs_fclose);
+ if (src_ffs == NULL)
+ return -1;
+
+ src_ffs->path = basename(src_target);
+ done_list->ffs = src_ffs;
+
+ RAII(FILE*, dst_file, __fopen(dst_type, dst_target, "r+", debug),
+ fclose);
+ if (dst_file == NULL)
+ return -1;
+
+ if (args->force == f_FORCE && args->cmd == c_COPY) {
+ if (__force_part(src_ffs, dst_file) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: partition table '%s' => '%s' "
+ "(done)\n", offset, src_target, dst_target);
+ }
+
+ if (check_file(dst_target, dst_file, offset) < 0)
+ return -1;
+
+ RAII(ffs_t*, dst_ffs, __ffs_fopen(dst_file, offset), __ffs_fclose);
+ if (dst_ffs == NULL)
+ return -1;
+
+ dst_ffs->path = basename(dst_target);
+
+ if (validate_files(src_ffs, dst_ffs) < 0)
+ return -1;
+
+ if (src_ffs->count <= 0) // fix me
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(src_ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(src_ffs, buffer) < 0)
+ return -1;
+ if (__ffs_buffer(dst_ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t src_parent;
+ ffs_entry_t dst_parent;
+
+ if (strcmp(src_name, "*") == 0 && strcmp(dst_name, "*") == 0) {
+ src_parent.type = FFS_TYPE_LOGICAL;
+ src_parent.id = -1;
+ dst_parent.type = FFS_TYPE_LOGICAL;
+ dst_parent.id = -1;
+ } else if ((src_name != NULL) && (dst_name != NULL)) {
+ if (__ffs_entry_find(src_ffs, src_name, &src_parent) == false) {
+ UNEXPECTED("%8llx: partition entry '%s' not found in "
+ "'%s'\n", src_ffs->offset, src_name,
+ src_target);
+ return -1;
+ }
+
+ if (__ffs_entry_find(dst_ffs, dst_name, &dst_parent) == false) {
+ UNEXPECTED("%8llx: partition entry '%s' not found in "
+ "'%s'\n", dst_ffs->offset, dst_name,
+ dst_target);
+ return -1;
+ }
+ }
+
+ if (src_parent.type == FFS_TYPE_DATA &&
+ dst_parent.type == FFS_TYPE_DATA) {
+ if (args->cmd == c_COPY)
+ return __copy_entry(args, src_ffs, &src_parent,
+ dst_ffs, &dst_parent, done_list);
+ else
+ return __compare_entry(args, src_ffs, &src_parent,
+ dst_ffs, &dst_parent, done_list);
+ } else if (src_parent.type == FFS_TYPE_LOGICAL &&
+ dst_parent.type == FFS_TYPE_LOGICAL) {
+
+ RAII(entry_list_t*, src_list, entry_list_create(src_ffs),
+ entry_list_delete);
+ if (src_list == NULL)
+ return -1;
+ if (entry_list_add_child(src_list, &src_parent) < 0)
+ return -1;
+
+ RAII(entry_list_t*, dst_list, entry_list_create(dst_ffs),
+ entry_list_delete);
+ if (dst_list == NULL)
+ return -1;
+ if (entry_list_add_child(dst_list, &dst_parent) < 0)
+ return -1;
+
+ while (list_empty(&src_list->list) == 0) {
+ RAII(entry_node_t*, src_node,
+ container_of(list_head(&src_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(src_list, src_node);
+ ffs_entry_t * src_entry = &src_node->entry;
+
+ char full_src_name[page_size];
+ if (__ffs_entry_name(src_ffs, src_entry, full_src_name,
+ sizeof full_src_name) < 0) {
+ free(src_node);
+ return -1;
+ }
+
+ if (list_empty(&dst_list->list)) {
+ UNEXPECTED("source entry '%s' not a child of "
+ "destination entry '%s'\n",
+ full_src_name, dst_name);
+ return -1;
+ }
+
+ RAII(entry_node_t*, dst_node,
+ container_of(list_head(&dst_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(dst_list, dst_node);
+ ffs_entry_t * dst_entry = &dst_node->entry;
+
+ char full_dst_name[page_size];
+ if (__ffs_entry_name(dst_ffs, dst_entry, full_dst_name,
+ sizeof full_dst_name) < 0)
+ return -1;
+
+ if (src_entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(src_list,
+ src_entry) < 0)
+ return -1;
+ }
+
+ if (dst_entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(dst_list,
+ dst_entry) < 0)
+ return -1;
+ }
+
+ if (args->cmd == c_COPY) {
+ if (__copy_entry(args, src_ffs, src_entry,
+ dst_ffs, dst_entry,
+ done_list) < 0)
+ return -1;
+ } else if (args->cmd == c_COMPARE) {
+ if (__compare_entry(args, src_ffs, src_entry,
+ dst_ffs, dst_entry,
+ done_list) < 0) {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int command_copy_compare(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __copy_compare(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_erase.c b/fcp/src/cmd_erase.c
new file mode 100644
index 0000000..972f0be
--- /dev/null
+++ b/fcp/src/cmd_erase.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_erase.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: erase implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int __erase(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ size_t fill;
+ if (args->opt_nr == 1) {
+ fill = 0xFF;
+ } else if (args->opt_nr == 2) {
+ if (parse_number(args->opt[1], &fill) < 0)
+ return -1;
+ }
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ ffs->path = basename(target);
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t parent;
+ if (__ffs_entry_find(ffs, name, &parent) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ RAII(entry_list_t*, entry_list, entry_list_create(ffs),
+ entry_list_delete);
+ if (entry_list == NULL)
+ return -1;
+ if (entry_list_add(entry_list, &parent) < 0)
+ return -1;
+
+ while (list_empty(&entry_list->list) == 0) {
+ RAII(entry_node_t*, entry_node,
+ container_of(list_head(&entry_list->list),
+ entry_node_t, node), free);
+
+ entry_list_remove(entry_list, entry_node);
+ ffs_entry_t * entry = &entry_node->entry;
+
+ if (entry->type == FFS_TYPE_LOGICAL) {
+ if (entry_list_add_child(entry_list, entry) < 0)
+ return -1;
+ continue;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (args->protected != f_PROTECTED) {
+ if (entry->flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected "
+ "(skip)\n", offset, full_name);
+ continue;
+ }
+ }
+
+ if (__ffs_entry_truncate(ffs, full_name, 0ULL) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n",
+ offset, full_name, 0);
+
+ if (entry_list_exists(done_list, entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: erase partition "
+ "(skip)\n", offset, full_name);
+ continue;
+ }
+
+ if (entry_list_add(done_list, entry) < 0)
+ return -1;
+
+ if (fcp_erase_entry(ffs, full_name, (char)fill) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: erase partition (done)\n",
+ offset, full_name);
+ }
+
+ return 0;
+}
+
+int command_erase(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __erase(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_list.c b/fcp/src/cmd_list.c
new file mode 100644
index 0000000..069407a
--- /dev/null
+++ b/fcp/src/cmd_list.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_list.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: list implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int list(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ fprintf(stdout, "========================[ PARTITION TABLE"
+ " 0x%llx ]=======================\n", ffs->offset);
+ fprintf(stdout, "vers:%04x size:%04x * blk:%06x blk(s):"
+ "%06x * entsz:%06x ent(s):%06x\n",
+ ffs->hdr->version, ffs->hdr->size, ffs->hdr->block_size,
+ ffs->hdr->block_count, ffs->hdr->entry_size,
+ ffs->hdr->entry_count);
+ fprintf(stdout, "----------------------------------"
+ "----------------------------------"
+ "-------\n");
+
+ RAII(entry_list_t*, entry_list, entry_list_create_by_regex(ffs, name),
+ entry_list_delete);
+ if (entry_list == NULL)
+ return -1;
+
+ list_iter_t it;
+ entry_node_t * entry_node;
+
+ list_iter_init(&it, &entry_list->list, LI_FLAG_FWD);
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+ char full_name[page_size];
+
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ size_t offset = entry->base * ffs->hdr->block_size;
+ size_t size = entry->size * ffs->hdr->block_size;
+
+ char type;
+ if (entry->type == FFS_TYPE_LOGICAL)
+ type ='l';
+ else if (entry->type == FFS_TYPE_DATA)
+ type ='d';
+ else if (entry->type == FFS_TYPE_PARTITION)
+ type ='p';
+ fprintf(stdout, "%3d [%08x-%08x] [%8x:%8x] ",
+ entry->id, offset, offset+size-1, size, entry->actual);
+
+ fprintf(stdout, "[%c%c%c%c%c%c] %s\n",
+ type, '-', '-', '-',
+ entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-',
+ entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-',
+ full_name);
+
+ if (args->verbose == f_VERBOSE) {
+ for (int i=0; i<FFS_USER_WORDS; i++) {
+ fprintf(stdout, "[%2d] %8x ", i,
+ entry->user.data[i]);
+ if ((i+1) % 4 == 0)
+ fprintf(stdout, "\n");
+ }
+ }
+ }
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+int command_list(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = list(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_probe.c b/fcp/src/cmd_probe.c
new file mode 100644
index 0000000..b0e55a8
--- /dev/null
+++ b/fcp/src/cmd_probe.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_probe.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: probe implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include <spinor/spinor.h>
+#include <spinor/aardvark.h>
+
+#include <dbgx/rwflash.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int aa_probe(int verbose)
+{
+ int size = 16;
+
+ uint16_t ports[size];
+ uint32_t unique_ids[size];
+
+ int rc = aa_find_devices_ext(size, ports, size, unique_ids);
+
+ fprintf(stdout, "%d aardvark(s) found:\n", rc);
+
+ for (int i = 0; i < min(rc, 16); i++) {
+ if (ports[i] & AA_PORT_NOT_FREE) {
+ fprintf(stdout, " %d: [%04d-%06d] ===> BUSY",
+ ports[i] & ~AA_PORT_NOT_FREE,
+ unique_ids[i] / 1000000,
+ unique_ids[i] % 1000000);
+
+ continue;
+ }
+
+ spinor_t * nor = spinor_open(ports[i]);
+ if (nor == NULL) {
+ debug("unable to open aaflash on port '%d'\n", ports[i]);
+ continue;
+ }
+
+ if (spinor_configure(nor) < 0) {
+ debug("unable to configure Aardvark on port '%d'\n",
+ ports[i]);
+ continue;
+ }
+
+ fprintf(stdout, " %d: [%04d-%06d] => ",
+ ports[i] & ~AA_PORT_NOT_FREE,
+ unique_ids[i] / 1000000, unique_ids[i] % 1000000);
+
+ spinor_id_t id;
+ if (spinor_read_id(nor, id) < 0) {
+ debug("unable to read device id\n");
+ continue;
+ }
+
+ fprintf(stdout, "%s %s %s",
+ spinor_id_mfg(id), spinor_id_type(id),
+ spinor_id_capacity(id));
+
+ fprintf(stdout, " [%2.2x %2.2x %2.2x]\n",
+ id[0], id[1], id[2]);
+
+ if (spinor_close(nor) < 0) {
+ debug("unable to read device id\n");
+ continue;
+ }
+
+ nor = NULL;
+ }
+
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+static int rw_probe(const char * host, int verbose)
+{
+ assert(host != NULL);
+
+ rwflash_cookie_t * cookie = rwflash_cookie_open(host, verbose);
+ if (cookie == NULL) {
+ debug("unable to open rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ if (rwflash_cookie_init(cookie) < 0) {
+ debug("unable to init rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ if (rwflash_cookie_probe(cookie) < 0) {
+ debug("unable to probe rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ uint8_t * id = spinor_id(rwflash_cookie_spinor(cookie));
+ if (id == NULL) {
+ debug("invalid id rwflash on host '%s'\n", host);
+ return -1;
+ }
+
+ fprintf(stdout, " %s ===> %s %s %s",
+ host,
+ spinor_id_mfg(id), spinor_id_type(id),
+ spinor_id_capacity(id));
+
+ fprintf(stdout, " [%2.2x %2.2x %2.2x]\n",
+ id[0], id[1], id[2]);
+
+ return 0;
+}
+
+int command_probe(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+
+ int verbose = args->verbose == f_VERBOSE;
+
+ if (strcasecmp(type, TYPE_AA) == 0) {
+ rc = aa_probe(verbose);
+ } else if (strcasecmp(type, TYPE_RW) == 0) {
+ rc = rw_probe(target, verbose);
+ } else {
+ UNEXPECTED("NOT IMPLEMENTED YET!");
+ rc = -1;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_read.c b/fcp/src/cmd_read.c
new file mode 100644
index 0000000..7923b46
--- /dev/null
+++ b/fcp/src/cmd_read.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_read.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: read implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int __read(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * type = args->src_type;
+ char * target = args->src_target;
+ char * name = args->src_name;
+
+ char * out_path = args->dst_target;
+
+ RAII(FILE*, file, __fopen(type, target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ if (entry.actual == 0) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: is empty (skip)\n",
+ offset, name);
+ return 0;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, name);
+ return 0;
+ }
+
+ if (entry_list_exists(done_list, &entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: write to '%s' (skip)\n",
+ offset, full_name, out_path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, &entry) < 0)
+ return -1;
+
+ if (strcmp(out_path, "-") == 0) {
+ if (fcp_read_entry(ffs, full_name, stdout) < 0)
+ return -1;
+ } else {
+ if (access(out_path, F_OK) == 0 && args->force != f_FORCE) {
+ UNEXPECTED("output file '%s' already exists, "
+ "use --force to overwrite\n", out_path);
+ return -1;
+ }
+
+ RAII(FILE*, out, fopen(out_path, "w+"), fclose);
+ if (out == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fcp_read_entry(ffs, full_name, out) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: write to '%s' (done)\n",
+ offset, full_name, out_path);
+ }
+
+ return 0;
+}
+
+int command_read(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __read(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_trunc.c b/fcp/src/cmd_trunc.c
new file mode 100644
index 0000000..cf65788
--- /dev/null
+++ b/fcp/src/cmd_trunc.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_trunc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: trunc implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int __trunc(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags && FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ size_t size = 0;
+ if (args->opt_nr == 1) {
+ size = entry.size * block_size;
+ } else if (args->opt_nr == 2) {
+ if (parse_number(args->opt[1], &size) < 0)
+ return -1;
+ }
+
+ if (__ffs_entry_truncate(ffs, full_name, size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: truncate '%x' (done)\n",
+ offset, full_name, size);
+
+ return 0;
+}
+
+int command_trunc(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __trunc(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_user.c b/fcp/src/cmd_user.c
new file mode 100644
index 0000000..46637ce
--- /dev/null
+++ b/fcp/src/cmd_user.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_user.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: user implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+#define DELIM '='
+
+static int __user(args_t * args, off_t offset)
+{
+ assert(args != NULL);
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags && FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ // parse <word>[=<value>]
+
+ if (args->opt_nr <= 1) {
+ for (uint32_t word=0; word<FFS_USER_WORDS; word++) {
+ uint32_t value = 0;
+
+ if (__ffs_entry_user_get(ffs, name, word, &value) < 0)
+ return -1;
+
+ fprintf(stdout, "%8llx: %s: [%02d] = %08x\n",
+ offset, full_name, word, value);
+ }
+ fprintf(stdout, "\n");
+ } else {
+ for (int i=1; i<args->opt_nr; i++) {
+ if (args->opt[i] == '\0') {
+ UNEXPECTED("invalid user '%s', use form "
+ "'<word>[=<value>]'\n",
+ args->opt[i]);
+ return -1;
+ }
+
+ char * __value = strrchr(args->opt[i], DELIM);
+ if (__value != NULL)
+ *__value = '\0';
+
+ size_t word = 0;
+ if (parse_number(args->opt[i], &word) < 0)
+ return -1;
+
+ uint32_t value = 0;
+ if (__value != NULL) { // write
+ if (parse_number(__value+1, &value) < 0)
+ return -1;
+
+ *__value = DELIM;
+
+ if (__ffs_entry_user_put(ffs, full_name,
+ word, value) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: [%02d] = "
+ "%08x\n", offset, full_name,
+ word, value);
+ } else { // read
+ if (__ffs_entry_user_get(ffs, full_name,
+ word, &value) < 0)
+ return -1;
+
+ if (isatty(fileno(stdout)))
+ fprintf(stdout, "%8llx: %s: [%02d] = "
+ "%08x\n", offset, full_name,
+ word, value);
+ else
+ fprintf(stdout, "%x", value);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int command_user(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __user(args, offset);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/cmd_write.c b/fcp/src/cmd_write.c
new file mode 100644
index 0000000..5152532
--- /dev/null
+++ b/fcp/src/cmd_write.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: cmd_write.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: write implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+static int __write(args_t * args, off_t offset, entry_list_t * done_list)
+{
+ assert(args != NULL);
+
+ char * in_path = args->src_target;
+
+ char * type = args->dst_type;
+ char * target = args->dst_target;
+ char * name = args->dst_name;
+
+ RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ if (check_file(target, file, offset) < 0)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ ffs->path = basename(target);
+ done_list->ffs = ffs;
+
+ if (ffs->count <= 0)
+ return 0;
+
+ size_t block_size;
+ if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ if (args->buffer != NULL) {
+ size_t buffer;
+ if (parse_size(args->buffer, &buffer) < 0)
+ return -1;
+ if (__ffs_buffer(ffs, buffer) < 0)
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, name, &entry) == false) {
+ UNEXPECTED("partition entry '%s' not found\n", name);
+ return -1;
+ }
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, &entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (entry.type == FFS_TYPE_LOGICAL) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: logical (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ if (args->protected != f_PROTECTED &&
+ entry.flags & FFS_FLAGS_PROTECTED) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: protected (skip)\n",
+ offset, full_name);
+ return 0;
+ }
+
+ struct stat st;
+ if (stat(in_path, &st) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (entry.actual < st.st_size) {
+ if (__ffs_entry_truncate(ffs, full_name,
+ st.st_size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: trunc size '%llx' (done)\n",
+ offset, full_name, st.st_size);
+ }
+
+ if (entry_list_exists(done_list, &entry) == 1) {
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: read from '%s' (skip)\n",
+ offset, full_name, in_path);
+ return 0;
+ }
+
+ if (entry_list_add(done_list, &entry) < 0)
+ return -1;
+
+ if (strcmp(in_path, "-") == 0) {
+ if (fcp_write_entry(ffs, full_name, stdin) < 0)
+ return -1;
+ } else {
+ RAII(FILE*, in, fopen(in_path, "r"), fclose);
+ if (in == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fcp_write_entry(ffs, full_name, in) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ fprintf(stderr, "%8llx: %s: read from '%s' (done)\n",
+ offset, full_name, in_path);
+ }
+
+ return 0;
+}
+
+int command_write(args_t * args)
+{
+ assert(args != NULL);
+
+ int rc = 0;
+
+ RAII(entry_list_t*, done_list, entry_list_create(NULL),
+ entry_list_delete);
+ if (done_list == NULL)
+ return -1;
+
+ char * end = (char *)args->offset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t offset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --offset specified '%s'",
+ args->offset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ rc = __write(args, offset, done_list);
+ if (rc < 0)
+ break;
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
diff --git a/fcp/src/main.c b/fcp/src/main.c
new file mode 100644
index 0000000..c122c3f
--- /dev/null
+++ b/fcp/src/main.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: fcp_main.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cp for FFS files
+ * Date: 04/25/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+args_t args;
+size_t page_size;
+
+int verbose;
+int debug;
+
+static const char copyright[] __unused__ =
+ "Licensed Internal Code - Property of IBM\n"
+ "IBM Support Processor Licensed Internal Code\n"
+ "(c) Copyright IBM Corp 2013 All Rights Reserved\n"
+ "US Government Users Restricted Rights - Use, duplication\n"
+ "or disclosure restricted by GSA ADP Schedule Contract\n"
+ "with IBM Corp.";
+
+static void usage(bool verbose)
+{
+ FILE *e = stderr;
+
+ fprintf(e,
+ "fcp - FFS copy v%d.%d.%d -- Authors: "
+ "<shaun@us.ibm.com>\n (c) Copyright IBM Corp 2013 All Rights "
+ "Reserved\n", FCP_MAJOR, FCP_MINOR, FCP_PATCH);
+
+ fprintf(e, "\n");
+ fprintf(e, "Usage:\n");
+ fprintf(e," fcp [<src_type>:]<src_target>[:<src_name>] -PLETU"
+ "\n [-b <size>] [-o <offset,...>] [-fpvdh]\n");
+ fprintf(e," fcp [<src_type>:]<src_target>[:<src_name>] "
+ "[<dst_type>:]<dst_target>[:<dst_name>] -RWCM"
+ "\n [-b <size>] [-o <offset,...>] [-fpvdh]\n");
+ fprintf(e, "\n");
+ fprintf(e, " <type>\n");
+ fprintf(e, " 'aa' : Aardvark USB probe\n");
+ fprintf(e, " 'rw' : RISCWatch Ethernet probe\n");
+ fprintf(e, " 'sfc' : FSP SFC character device\n");
+ fprintf(e, " 'file' : UNIX regular file\n");
+ fprintf(e, " <target>\n");
+ fprintf(e, " 'aa' : <number> USB device number [0..9]\n");
+ fprintf(e, " 'rw' : <hostname>@<port> RISCwatch probe\n");
+ fprintf(e, " 'sfc' : <path> to SFC char device\n");
+ fprintf(e, " 'file' : <path> to UNIX regular file\n");
+ fprintf(e, " <name>\n");
+ fprintf(e, " : FFS name, e.g. bank0/bootenv/card\n");
+ fprintf(e, "\n");
+
+ if (verbose) {
+ fprintf(e, "Examples:\n");
+ fprintf(e, " fcp -P aa:0\n");
+ fprintf(e, " fcp -P rw:riscwatch.ibm.com\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -L aa:0\n");
+ fprintf(e, " fcp -L rw:riscwatch.ibm.com\n");
+ fprintf(e, " fcp -L nor.mif\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -E 0 nor.mif:bank0\n");
+ fprintf(e, " fcp -E 0xff nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -W ipl.bin file:nor:bank0/ipl\n");
+ fprintf(e, " fcp -R file:nor:bank0/ipl ipl.bin\n");
+ fprintf(e, "\n");
+ fprintf(e, " cat ipl.bin | fcp -R file:nor:bank0/ipl -\n");
+ fprintf(e, " fcp -W file:nor:bank0/ipl - | cat > ipl.bin\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -C file:nor:bank0 rw:host.ibm.com:bank0\n");
+ fprintf(e, " fcp -C file:nor rw:host.ibm.com@6470\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -M file:nor:bank0 rw:host.ibm.com:bank0\n");
+ fprintf(e, " fcp -M file:nor:* rw:host.ibm.com@6470:*\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -C nor.mif:bank0 nor.mif:bank1\n");
+ fprintf(e, " fcp -C rw:host:bank0/ipl aa:0:bank1/ipl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -M nor.mif:bank0 nor.mif:bank1\n");
+ fprintf(e, " fcp -M rw:host:bank0/ipl aa:0:bank1/ipl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -T nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -T 0 nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ fprintf(e, " fcp -U nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -U 0 1 2 nor.mif:bank0/spl\n");
+ fprintf(e, " fcp -U 0=0xffffffff 1=0 nor.mif:bank0/spl\n");
+ fprintf(e, "\n");
+ }
+
+ /* =============================== */
+
+ fprintf(e, "Commands:\n");
+
+ fprintf(e, " -P, --probe\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the SPI NOR device Id\n\n");
+
+ fprintf(e, " -L, --list\n");
+ if (verbose)
+ fprintf(e,
+ "\n List the contents of a partition table\n\n");
+
+ fprintf(e, " -R, --read\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the contents of a partition and write "
+ "the data to an output file (use\n '-' for stdout)."
+ "\n\n");
+
+ fprintf(e, " -W, --write\n");
+ if (verbose)
+ fprintf(e,
+ "\n Read the contents of an input file (use '-' for "
+ "stdin) and write the data\n to a partition.\n\n");
+
+ fprintf(e, " -E, --erase <value>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Fill the contents of a partition with <value>. "
+ "<value> is a decimal (or hex)\n number, default is "
+ "0xFF.\n\n");
+
+ fprintf(e, " -C, --copy\n");
+ if (verbose)
+ fprintf(e,
+ "\n Copy source partition(s) to destination "
+ "partition(s). Both source and\n destination name(s) " "can specify either 'data' or 'logical' partitions."
+ "\n\n");
+
+ fprintf(e, " -T, --trunc <size>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Truncate the actual size of partition(s) to "
+ "<size> size bytes. <size> is\n a decimal (or hex) "
+ "number, default is the partition entry size\n\n");
+
+ fprintf(e, " -M, --compare\n");
+ if (verbose)
+ fprintf(e,
+ "\n Compare source partition(s) to destination "
+ "partition(s). Both source and\n destination name(s) "
+ "can specify either 'data' or 'logical' partitions."
+ "\n\n");
+
+ fprintf(e, " -U, --user [<word>[=<value>] ...]\n");
+ if (verbose)
+ fprintf(e,
+ "\n Get or set a user word. <word> and <value> are "
+ "decimal (or hex) numbers.\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e, "Options:\n");
+ fprintf(e, " -o, --offset <offset[,offset]>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies a comma (,) separated list of"
+ " partition table offsets, in bytes\n from the start"
+ " of the target file (or device).\n\n");
+
+ fprintf(e, " -b, --buffer <value>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Specifies the buffer size used by --read, --write,"
+ " --copy and --compare\n commands <value> is a "
+ "decimal (or hex) number, default is buffer size.\n\n");
+ fprintf(e, "\n");
+
+ /* =============================== */
+
+ fprintf(e, "Flags:\n");
+
+ fprintf(e, " -f, --force\n");
+ if (verbose)
+ fprintf(e, "\n Override command safe guards\n\n");
+
+ fprintf(e, " -p, --protected\n");
+ if (verbose)
+ fprintf(e, "\n Do not ignore protected partition "
+ "entries\n\n");
+
+ fprintf(e, " -v, --verbose\n");
+ if (verbose)
+ fprintf(e, "\n Write progress messages to stdout\n\n");
+
+ fprintf(e, " -d, --debug\n");
+ if (verbose)
+ fprintf(e, "\n Write debug messages to stdout\n\n");
+
+ fprintf(e, " -h, --help\n");
+ if (verbose)
+ fprintf(e, "\n Write this help text to stdout and exit\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e, "Report bugs to <https://bugzilla.linux.ibm.com/>"
+ " (vendor='MCP for FSP*')\n");
+}
+
+static int process_argument(args_t * args, int opt, const char *optarg)
+{
+ assert(args != NULL);
+
+ switch (opt) {
+ case c_PROBE: /* probe */
+ case c_LIST: /* list */
+ case c_READ: /* read */
+ case c_WRITE: /* write */
+ case c_ERASE: /* erase */
+ case c_COPY: /* copy */
+ case c_TRUNC: /* trunc */
+ case c_COMPARE: /* compare */
+ case c_USER: /* user */
+ if (args->cmd != c_ERROR) {
+ UNEXPECTED("commands '%c' and '%c' are mutually "
+ "exclusive", args->cmd, opt);
+ return -1;
+ }
+ args->cmd = (cmd_t) opt;
+ break;
+ case o_OFFSET: /* offset */
+ args->offset = strdup(optarg);
+ break;
+ case o_BUFFER: /* buffer */
+ args->buffer = strdup(optarg);
+ break;
+ case f_FORCE: /* force */
+ args->force = (flag_t) opt;
+ break;
+ case f_PROTECTED: /* protected */
+ args->protected = (flag_t) opt;
+ break;
+ case f_VERBOSE: /* verbose */
+ verbose = 1;
+ args->verbose = (flag_t) opt;
+ break;
+ case f_DEBUG: /* debug */
+ debug = 1;
+ args->debug = (flag_t) opt;
+ break;
+ case f_HELP: /* help */
+ usage(true);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?': /* error */
+ default:
+ UNEXPECTED("unknown option '%c', please see --help for "
+ "details\n", opt);
+ return -1;
+ }
+ return 0;
+}
+
+static int process_option(args_t * args, const char *opt)
+{
+ assert(args != NULL);
+ assert(opt != NULL);
+
+ if (args->opt_sz <= args->opt_nr) {
+ size_t size = sizeof(*args->opt);
+
+ args->opt_sz += 5;
+ args->opt = (const char **)realloc(args->opt,
+ size * args->opt_sz);
+ if (args->opt == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memset(args->opt + args->opt_nr, 0,
+ size * (args->opt_sz - args->opt_nr));
+ }
+
+ args->opt[args->opt_nr] = strdup(opt);
+ args->opt_nr++;
+
+ return 0;
+}
+
+/*
+ * probe:
+ * fcp [<type>:]<target>[:<path>] -P
+ * list:
+ * fcp <path> -L
+ * fcp [<type>:]<target>[:<path>] -L
+ * erase:
+ * fcp [<type>:]<target>[:<path>] -E <pad>
+ * trunc:
+ * fcp [<type>:]<target>[:<path>] -T <size>
+ * user:
+ * fcp [<type>:]<target>[:<path>] -U <word>[=<value>] ...
+ * write:
+ * fcp <path> [<type>:]<target>:<path> -W
+ * read:
+ * fcp [<type>:]<target>:<path> <path> -R
+ * copy:
+ * fcp <type>:]<target>:<source> [<type>:]<target>:<dest> -C
+ * fcp [<type>:]<source>:<path> [<type>:]<destination>:<path> -C
+ * compare:
+ * fcp <type>:]<target>:<source> [<type>:]<target>:<dest> -M
+ * fcp [<type>:]<source>:<path> [<type>:]<destination>:<path> -M
+ */
+static int validate_args(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->offset == NULL) {
+ UNEXPECTED("--offset is required for all '%s' "
+ "commands", args->short_name);
+ return -1;
+ }
+
+ switch (args->cmd) {
+ case c_PROBE:
+ case c_LIST:
+ case c_ERASE:
+ case c_TRUNC:
+ case c_USER:
+ if (args->opt_nr < 1) {
+ UNEXPECTED("invalid options, please see --help for "
+ "details");
+ return -1;
+ }
+
+ if (parse_path(args->opt[0], &args->dst_type,
+ &args->dst_target, &args->dst_name) < 0)
+ return -1;
+
+ break;
+ case c_READ:
+ case c_WRITE:
+ case c_COPY:
+ case c_COMPARE:
+ if (args->opt_nr < 2) {
+ UNEXPECTED("invalid options, please see --help for "
+ "details");
+ return -1;
+ }
+
+ if (parse_path(args->opt[0], &args->src_type,
+ &args->src_target, &args->src_name) < 0)
+ return -1;
+ if (parse_path(args->opt[1], &args->dst_type,
+ &args->dst_target, &args->dst_name) < 0)
+ return -1;
+
+ break;
+ case 0:
+ UNEXPECTED("specify a command, or please see --help "
+ "for details");
+ return -1;
+ default:
+ UNEXPECTED("invalid command '%c', please see --help for "
+ "details", args->cmd);
+ return -1;
+
+ }
+
+ debug("cmd[%c]\n", args->cmd);
+
+ debug(" src_type: '%s'\n", args->src_type);
+ debug("src_target: '%s'\n", args->src_target);
+ debug(" src_name: '%s'\n", args->src_name);
+
+ debug(" dst_type: '%s'\n", args->dst_type);
+ debug("dst_target: '%s'\n", args->dst_target);
+ debug(" dst_name: '%s'\n", args->dst_name);
+
+ #define REQ_OPT(name,cmd) ({ \
+ if (args->name == 0) { \
+ UNEXPECTED("--%s is required for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ #define REQ_FIELD(name,cmd) ({ \
+ if (args->name == 0) { \
+ UNEXPECTED("<%s> is required for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ #define UNSUP_OPT(name,cmd) ({ \
+ if (args->name != 0) { \
+ UNEXPECTED("--%s is unsupported for the --%s command", \
+ #name, #cmd); \
+ syntax(); \
+ return -1; \
+ } \
+ })
+
+ if (args->cmd == c_PROBE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --probe [--verbose]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 1) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ UNSUP_OPT(buffer, probe);
+ } else if (args->cmd == c_LIST) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --list [--verbose]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 1) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ UNSUP_OPT(buffer, list);
+ } else if (args->cmd == c_READ) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_source>"
+ ":<src_name> <path> --read [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(src_name, read);
+ } else if (args->cmd == c_WRITE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s <path> [<dst_type>:]"
+ "<dst_target>:<dst_name> --write [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+
+ REQ_FIELD(dst_name, write);
+ } else if (args->cmd == c_ERASE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --erase <value> [--verbose] "
+ "[--force] [--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(dst_name, erase);
+ } else if (args->cmd == c_TRUNC) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --trunc <value> [--verbose] "
+ "[--force] [--protected]\n", args->short_name);
+ }
+ if (2 < args->opt_nr) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+
+ REQ_FIELD(dst_name, trunc);
+
+ UNSUP_OPT(buffer, trunc);
+ } else if (args->cmd == c_USER) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<dst_type>:]<dst_target>"
+ ":<dst_name> --user <value>[=<value] "
+ "[--verbose] [--force] [--protected]\n",
+ args->short_name);
+ }
+
+ REQ_FIELD(dst_name, user);
+
+ UNSUP_OPT(buffer, user);
+ } else if (args->cmd == c_COPY) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_target>"
+ "[:<src_name>] [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --copy [--verbose] [--force] "
+ "[--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+ } else if (args->cmd == c_COMPARE) {
+ void syntax(void) {
+ fprintf(stderr, "Syntax: %s [<src_type>:]<src_target>"
+ "[:<src_name>] [<dst_type>:]<dst_target>"
+ "[:<dst_name>] --compare [--verbose] [--force] "
+ "[--protected] [--buffer <value>]\n",
+ args->short_name);
+ }
+ if (args->opt_nr != 2) {
+ syntax();
+ UNEXPECTED("syntax error");
+ return -1;
+ }
+ } else {
+ UNEXPECTED("invalid command '%c'", args->cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_args(args_t * args)
+{
+ assert(args != NULL);
+ int rc = 0;
+
+ switch (args->cmd) {
+ case c_PROBE:
+ //rc = command_probe(args);
+ break;
+ case c_LIST:
+ rc = command_list(args);
+ break;
+ case c_READ:
+ rc = command_read(args);
+ break;
+ case c_WRITE:
+ rc = command_write(args);
+ break;
+ case c_ERASE:
+ rc = command_erase(args);
+ break;
+ case c_TRUNC:
+ rc = command_trunc(args);
+ break;
+ case c_USER:
+ rc = command_user(args);
+ break;
+ case c_COPY:
+ case c_COMPARE:
+ rc = command_copy_compare(args);
+ break;
+ default:
+ UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void args_dump(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->short_name != NULL)
+ printf("short_name[%s]\n", args->short_name);
+ printf("cmd[%c]\n", args->cmd);
+ if (args->offset != NULL)
+ printf("offset[%s]\n", args->offset);
+ if (args->buffer != NULL)
+ printf("buffer[%s]\n", args->buffer);
+ if (args->force != 0)
+ printf("force[%c]\n", args->force);
+ if (args->protected != 0)
+ printf("protected[%c]\n", args->protected);
+ if (args->verbose != 0)
+ printf("verbose[%c]\n", args->verbose);
+ if (args->debug != 0)
+ printf("debug[%c]\n", args->debug);
+
+ for (int i = 0; i < args->opt_nr; i++) {
+ if (args->opt[i] != NULL)
+ printf("opt%d[%s]\n", i, args->opt[i]);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_opt[] = {
+ /* commands */
+ {"probe", no_argument, NULL, c_PROBE},
+ {"list", no_argument, NULL, c_LIST},
+ {"read", no_argument, NULL, c_READ},
+ {"write", no_argument, NULL, c_WRITE},
+ {"erase", no_argument, NULL, c_ERASE},
+ {"copy", no_argument, NULL, c_COPY},
+ {"trunc", no_argument, NULL, c_TRUNC},
+ {"compare", no_argument, NULL, c_COMPARE},
+ {"user", no_argument, NULL, c_USER},
+ /* options */
+ {"offset", required_argument, NULL, o_OFFSET},
+ {"buffer", required_argument, NULL, o_BUFFER},
+ /* flags */
+ {"force", no_argument, NULL, f_FORCE},
+ {"protected", no_argument, NULL, f_PROTECTED},
+ {"verbose", no_argument, NULL, f_VERBOSE},
+ {"debug", no_argument, NULL, f_DEBUG},
+ {"help", no_argument, NULL, f_HELP},
+ {0, 0, 0, 0}
+ };
+
+ static const char *short_opt;
+ short_opt = "PLRWECTMUo:b:fpvdh";
+
+ int rc = EXIT_FAILURE;
+
+ setlinebuf(stdout);
+
+ if (argc == 1)
+ usage(false), exit(rc);
+
+ int opt = 0, idx = 0;
+ while ((opt = getopt_long(argc, argv, short_opt, long_opt, &idx)) != -1)
+ if (process_argument(&args, opt, optarg) < 0)
+ goto error;
+
+ /* getopt_long doesn't know what to do with orphans, */
+ /* so we'll scoop them up here, and deal with them later */
+
+ while (optind < argc)
+ if (process_option(&args, argv[optind++]) < 0)
+ goto error;
+
+ if (args.debug == f_DEBUG)
+ args_dump(&args);
+
+ if (validate_args(&args) < 0)
+ goto error;
+ if (process_args(&args) < 0)
+ goto error;
+
+ rc = EXIT_SUCCESS;
+
+ if (false) {
+error:
+ dump_errors(args.short_name, stderr);
+ }
+
+ return rc;
+}
+
+static void __ctor__(void) __constructor;
+static void __ctor__(void)
+{
+ page_size = sysconf(_SC_PAGESIZE);
+
+ args.short_name = program_invocation_short_name;
+ args.offset = "0x3F0000,0x7F0000";
+}
diff --git a/fcp/src/main.h b/fcp/src/main.h
new file mode 100644
index 0000000..93d8f59
--- /dev/null
+++ b/fcp/src/main.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: main.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cmdline tool for libffs.so
+ * Date: 05/12/2012
+ */
+
+#ifndef __FCP_H__
+#define __FCP_H__
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include <ffs/libffs.h>
+
+#define FCP_MAJOR 0x01
+#define FCP_MINOR 0x00
+#define FCP_PATCH 0x00
+
+#define TYPE_FILE "file"
+#define TYPE_RW "rw"
+#define TYPE_AA "aa"
+#define TYPE_SFC "sfc"
+
+#define verbose(fmt, args...) \
+ ({if (verbose) printf("%s: " fmt, __func__, ##args); })
+#define debug(fmt, args...) \
+ ({if (debug) printf("%s: " fmt, __func__, ##args); })
+
+typedef enum {
+ c_ERROR = 0,
+ c_PROBE = 'P',
+ c_LIST = 'L',
+ c_READ = 'R',
+ c_WRITE = 'W',
+ c_ERASE = 'E',
+ c_COPY = 'C',
+ c_TRUNC = 'T',
+ c_COMPARE = 'M',
+ c_USER = 'U',
+} cmd_t;
+
+typedef enum {
+ o_ERROR = 0,
+ o_OFFSET = 'o',
+ o_BUFFER = 'b',
+} option_t;
+
+typedef enum {
+ f_ERROR = 0,
+ f_FORCE = 'f',
+ f_PROTECTED = 'p',
+ f_VERBOSE = 'v',
+ f_DEBUG = 'd',
+ f_HELP = 'h',
+} flag_t;
+
+typedef struct {
+ const char *short_name;
+
+ cmd_t cmd;
+ char *src_type, *src_target, *src_name;
+ char *dst_type, *dst_target, *dst_name;
+
+ /* options */
+ const char *offset;
+ const char *buffer;
+
+ /* flags */
+ flag_t force;
+ flag_t protected;
+ flag_t verbose;
+ flag_t debug;
+
+ const char **opt;
+ int opt_sz, opt_nr;
+} args_t;
+
+extern args_t args;
+extern size_t page_size;
+
+extern int verbose;
+extern int debug;
+
+extern int fcp_read_entry(ffs_t *, const char *, FILE *);
+extern int fcp_write_entry(ffs_t *, const char *, FILE *);
+extern int fcp_erase_entry(ffs_t *, const char *, char);
+extern int fcp_copy_entry(ffs_t *, const char *, ffs_t *, const char *);
+extern int fcp_compare_entry(ffs_t *, const char *, ffs_t *, const char *);
+
+extern int command_probe(args_t *);
+extern int command_list(args_t *);
+extern int command_read(args_t *);
+extern int command_write(args_t *);
+extern int command_erase(args_t *);
+extern int command_copy_compare(args_t *);
+extern int command_trunc(args_t *);
+extern int command_compare(args_t *);
+extern int command_user(args_t *);
+
+#endif /* __FCP_H__ */
diff --git a/fcp/src/misc.c b/fcp/src/misc.c
new file mode 100644
index 0000000..5fdfbfd
--- /dev/null
+++ b/fcp/src/misc.c
@@ -0,0 +1,937 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: fcp_misc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: misc helpers
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/version.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "misc.h"
+#include "main.h"
+
+#define COMPARE_SIZE 256UL
+
+static regex_t * regex_create(const char * str)
+{
+ assert(str != NULL);
+
+ regex_t * self = (regex_t *)malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ return self;
+ }
+
+ if (regcomp(self, str, REG_ICASE | REG_NOSUB) != 0) {
+ free(self);
+ ERRNO(errno);
+ return NULL;
+ }
+
+ return self;
+}
+
+static int regex_free(regex_t * self)
+{
+ if (self == NULL)
+ return 0;
+
+ regfree(self);
+ free(self);
+
+ return 0;
+}
+
+int parse_offset(const char *str, off_t *offset)
+{
+ assert(offset != NULL);
+
+ if (str == NULL) {
+ *offset = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *offset = strtoull(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") ||
+ !strcasecmp(end, "KB") ||
+ !strcasecmp(end, "K"))
+ *offset <<= 10;
+ else if (!strcmp(end, "MiB") ||
+ !strcasecmp(end, "MB") ||
+ !strcasecmp(end, "M"))
+ *offset <<= 20;
+ else if (!strcmp(end, "GiB") ||
+ !strcasecmp(end, "GB") ||
+ !strcasecmp(end, "G"))
+ *offset <<= 30;
+ else {
+ UNEXPECTED("invalid offset specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_size(const char *str, size_t *size)
+{
+ assert(size != NULL);
+
+ if (str == NULL) {
+ *size = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *size = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") || !strcasecmp(end, "K") ||
+ !strcasecmp(end, "KB"))
+ *size <<= 10;
+ else if (!strcmp(end, "MiB") || !strcasecmp(end, "M") ||
+ !strcasecmp(end, "MB"))
+ *size <<= 20;
+ else if (!strcmp(end, "GiB") || !strcasecmp(end, "G") ||
+ !strcasecmp(end, "GB"))
+ *size <<= 30;
+ else {
+ UNEXPECTED("invalid size specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_number(const char *str, size_t *num)
+{
+ assert(num != NULL);
+
+ if (str == NULL) {
+ *num = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *num = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ UNEXPECTED("invalid number specified '%s'", end);
+ return -1;
+ }
+
+ return 0;
+}
+
+int parse_path(const char * path, char ** type, char ** target, char ** name)
+{
+ assert(path != NULL);
+
+ *type = *target = *name = NULL;
+
+ char * delim1 = strchr(path, ':');
+ char * delim2 = strrchr(path, ':');
+
+ if (delim1 == NULL && delim2 == NULL) { // <target>
+ if (asprintf(target, "%s", path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ } else if (delim1 == delim2) { // <target>:<name>
+ if (asprintf(target, "%.*s", delim1 - path, path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim1++;
+ if (asprintf(name, "%s", delim1) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (valid_type(*target) == true) {
+ *type = *target;
+ *target = *name;
+ *name = NULL;
+ }
+ } else if (delim1 != delim2) { // <type>:<target>:<name>
+ if (asprintf(type, "%.*s", delim1 - path, path) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim1++;
+ if (asprintf(target, "%.*s", delim2 - delim1, delim1) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ delim2++;
+ if (asprintf(name, "%s", delim2) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int dump_errors(const char * name, FILE * out)
+{
+ assert(name != NULL);
+
+ if (out == NULL)
+ out = stderr;
+
+ err_t * err = NULL;
+
+ while ((err = err_get()) != NULL) {
+ switch (err_type(err)) {
+ case ERR_VERSION:
+ fprintf(out, "%s: %s : %s(%d) : v%d.%02d.%04d %.*s\n",
+ basename((char *)name), err_type_name(err),
+ basename(err_file(err)), err_line(err),
+ VER_TO_MAJOR(err_code(err)),
+ VER_TO_MINOR(err_code(err)),
+ VER_TO_PATCH(err_code(err)),
+ err_size(err), (char *)err_data(err));
+ break;
+ default:
+ fprintf(out, "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ basename((char *)name), err_type_name(err),
+ basename(err_file(err)), err_line(err),
+ err_code(err), err_size(err),
+ (char *)err_data(err));
+ }
+ }
+
+ return 0;
+}
+
+int check_file(const char * path, FILE * file, off_t offset) {
+ assert(file != NULL);
+
+ switch (__ffs_fcheck(file, offset)) {
+ case 0:
+ return 0;
+ case FFS_CHECK_HEADER_MAGIC:
+ UNEXPECTED("'%s' no partition table found at offset '%llx'\n",
+ path, offset);
+ return -1;
+ case FFS_CHECK_HEADER_CHECKSUM:
+ UNEXPECTED("'%s' partition table at offset '%llx', is "
+ "corrupted\n", path, offset);
+ return -1;
+ case FFS_CHECK_ENTRY_CHECKSUM:
+ UNEXPECTED("'%s' partition table at offset '%llx', has "
+ "corrupted entries\n", path, offset);
+ return -1;
+ default:
+ return -1;
+ }
+}
+
+entry_list_t * entry_list_create(ffs_t * ffs)
+{
+ entry_list_t * self = (entry_list_t *)malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ list_init(&self->list);
+
+ self->ffs = ffs;
+
+ return self;
+}
+
+entry_list_t * entry_list_create_by_regex(ffs_t * ffs, const char * name)
+{
+ assert(ffs != NULL);
+
+ if (name == NULL)
+ name = ".*";
+
+ RAII(regex_t*, rx, regex_create(name), regex_free);
+ if (rx == NULL)
+ return NULL;
+
+ entry_list_t * self = entry_list_create(ffs);
+ if (self == NULL)
+ return NULL;
+
+ int entry_list(ffs_entry_t * entry)
+ {
+ assert(entry != NULL);
+
+ char full_name[page_size];
+ if (__ffs_entry_name(ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+ if (regexec(rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, entry, sizeof(*entry));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+ }
+
+ if (__ffs_iterate_entries(ffs, entry_list) < 0) {
+ entry_list_delete(self);
+ return NULL;
+ }
+
+ return self;
+}
+
+int entry_list_add(entry_list_t * self, ffs_entry_t * entry)
+{
+ assert(self != NULL);
+ assert(entry != NULL);
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, entry, sizeof(entry_node->entry));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+}
+
+int entry_list_add_child(entry_list_t * self, ffs_entry_t * parent)
+{
+ assert(self != NULL);
+ assert(parent != NULL);
+
+ int child_entry_list(ffs_entry_t * child)
+ {
+ assert(child != NULL);
+
+ if (child->pid != parent->id)
+ return 0;
+
+ entry_node_t * entry_node;
+ entry_node = (entry_node_t *)malloc(sizeof(*entry_node));
+ if (entry_node == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memcpy(&entry_node->entry, child, sizeof(*child));
+ list_add_tail(&self->list, &entry_node->node);
+
+ return 0;
+ }
+
+ if (__ffs_iterate_entries(self->ffs, child_entry_list) < 0)
+ return -1;
+
+ return 0;
+}
+
+int entry_list_remove(entry_list_t * self, entry_node_t * node)
+{
+ assert(self != NULL);
+ assert(node != NULL);
+
+ list_remove_node(&self->list, &node->node);
+
+ return 0;
+}
+
+int entry_list_delete(entry_list_t * self)
+{
+ if (self == NULL)
+ return 0;
+
+ while (!list_empty(&self->list)) {
+ free(container_of(list_remove_head(&self->list), entry_node_t,
+ node));
+ }
+
+ return 0;
+}
+
+int entry_list_exists(entry_list_t * self, ffs_entry_t * entry)
+{
+ assert(self != NULL);
+ assert(entry != NULL);
+
+ list_iter_t it;
+ entry_node_t * entry_node;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * __entry = &entry_node->entry;
+
+ if (__entry->base == entry->base &&
+ __entry->size == entry->size)
+ return 1;
+ }
+
+ return 0;
+}
+
+ffs_entry_t * entry_list_find(entry_list_t * self, const char * name)
+{
+ assert(self != NULL);
+ assert(name != NULL);
+
+ list_iter_t it;
+ entry_node_t * entry_node = NULL;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+
+ if (strcmp(entry->name, name) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+int entry_list_dump(entry_list_t * self, FILE * out)
+{
+ assert(self != NULL);
+ if (out == NULL)
+ out = stdout;
+
+ list_iter_t it;
+ entry_node_t * entry_node = NULL;
+
+ list_iter_init(&it, &self->list, LI_FLAG_FWD);
+
+ list_for_each(&it, entry_node, node) {
+ ffs_entry_t * entry = &entry_node->entry;
+
+ fprintf(stderr, "id[%d] pid[%d] name[%s]\n",
+ entry->id, entry->pid, entry->name);
+ }
+
+ return 0;
+}
+
+FILE *__fopen(const char * type, const char * target, const char * mode,
+ int debug)
+{
+ assert(target != NULL);
+ assert(mode != NULL);
+
+ FILE *file = NULL;
+ size_t port = 0;
+
+ if (type == NULL)
+ type = TYPE_FILE;
+
+ if (strcasecmp(type, TYPE_AA) == 0) {
+ if (parse_number(target, &port) < 0)
+ return NULL;
+ UNEXPECTED("Removed support");
+ //file = fopen_aaflash(port, mode, debug);
+ } else if (strcasecmp(type, TYPE_RW) == 0) {
+ UNEXPECTED("Removed support");
+ //file = fopen_rwflash(target, mode, debug);
+ } else if (strcasecmp(type, TYPE_SFC) == 0) {
+ UNEXPECTED("FIX ME");
+ return NULL;
+ } else if (strcasecmp(type, TYPE_FILE) == 0) {
+ file = fopen(target, mode);
+ if (file == NULL)
+ ERRNO(errno);
+ } else {
+ errno = EINVAL;
+ ERRNO(errno);
+ }
+
+ return file;
+}
+
+int is_file(const char * type, const char * target, const char * name)
+{
+ return type == NULL && target != NULL && name == NULL;
+}
+
+int valid_type(const char * type)
+{
+ return type == NULL ? 0 :
+ strcasecmp(type, TYPE_FILE) == 0 ||
+ strcasecmp(type, TYPE_RW) == 0 ||
+ strcasecmp(type, TYPE_AA) == 0 ||
+ strcasecmp(type, TYPE_SFC) == 0;
+}
+
+int fcp_read_entry(ffs_t * src, const char * name, FILE * out)
+{
+ assert(src != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(src, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(src, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(src, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: read partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, name, buffer, offset, count);
+
+
+ rc = fwrite(buffer, 1, rc, out);
+ if (rc <= 0 && ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_write_entry(ffs_t * dst, const char * name, FILE * in)
+{
+ assert(dst != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(dst, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: write partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = fread(buffer, 1, count, in);
+ if (rc <= 0) {
+ if (feof(in)) {
+ break;
+ } else if (ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ rc = __ffs_entry_write(dst, name, buffer, offset, rc);
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_erase_entry(ffs_t * dst, const char * name, char fill)
+{
+ assert(dst != NULL);
+ assert(name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memset(buffer, fill, buffer_size);
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(dst, name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, name);
+ return -1;
+ }
+
+ uint32_t poffset;
+ if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0)
+ return -1;
+
+ size_t total = 0;
+ size_t size = entry.size * block_size;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8x: %s: erase partition %8x/%8x",
+ poffset, name, entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_write(dst, name, buffer, offset, count);
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", entry.size * block_size,
+ total);
+ }
+ }
+
+ if (__ffs_entry_truncate(dst, name, 0ULL) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_copy_entry(ffs_t * src, const char * src_name,
+ ffs_t * dst, const char * dst_name)
+{
+ assert(src != NULL);
+ assert(src_name != NULL);
+ assert(dst != NULL);
+ assert(dst_name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+ RAII(void*, buffer, malloc(buffer_size), free);
+ if (buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t src_entry;
+ if (__ffs_entry_find(src, src_name, &src_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, src_name);
+ return -1;
+ }
+
+ ffs_entry_t dst_entry;
+ if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, dst_name);
+ return -1;
+ }
+
+ size_t total = 0;
+ size_t size = src_entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8llx: %s: copy partition %8x/%8x",
+ src->offset, dst_name, src_entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, src_name, buffer, offset, count);
+ if (rc < 0)
+ return -1;
+ rc = __ffs_entry_write(dst, dst_name, buffer, offset, rc);
+ if (rc < 0)
+ return -1;
+
+ if (__ffs_fsync(dst) < 0)
+ return -1;
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", src_entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
+
+int fcp_compare_entry(ffs_t * src, const char * src_name,
+ ffs_t * dst, const char * dst_name)
+{
+ assert(src != NULL);
+ assert(src_name != NULL);
+ assert(dst != NULL);
+ assert(dst_name != NULL);
+
+ size_t block_size;
+ if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0)
+ return -1;
+
+ size_t buffer_count;
+ if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0)
+ return -1;
+
+ size_t buffer_size = block_size * buffer_count;
+
+ RAII(void*, src_buffer, malloc(buffer_size), free);
+ if (src_buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ RAII(void*, dst_buffer, malloc(buffer_size), free);
+ if (dst_buffer == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t src_entry;
+ if (__ffs_entry_find(src, src_name, &src_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ src->path, src_name);
+ return -1;
+ }
+
+ ffs_entry_t dst_entry;
+ if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ dst->path, dst_name);
+ return -1;
+ }
+
+ size_t total = 0;
+ size_t size = src_entry.actual;
+ off_t offset = 0;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "%8llx: %s: compare partition %8x/%8x",
+ src->offset, dst_name, src_entry.actual, total);
+ }
+
+ while (0 < size) {
+ size_t count = min(buffer_size, size);
+
+ ssize_t rc;
+ rc = __ffs_entry_read(src, src_name, src_buffer, offset, count);
+ if (rc < 0)
+ return -1;
+ rc = __ffs_entry_read(dst, dst_name, dst_buffer, offset, rc);
+ if (rc < 0)
+ return -1;
+
+ char * src_ptr = src_buffer;
+ char * dst_ptr = dst_buffer;
+ size_t cnt = 0;
+
+ while (cnt < count) {
+ size_t cmp_sz = min(count - cnt, COMPARE_SIZE);
+
+ if (memcmp(src_ptr, dst_ptr, cmp_sz) != 0) {
+ UNEXPECTED("MISCOMPARE! '%s' != '%s' at "
+ "offset '%llx'\n", src_name,
+ dst_name, offset + cnt);
+
+ if (isatty(fileno(stderr)))
+ fprintf(stderr, " <== [ERROR]\n");
+
+ return -1;
+ }
+
+ src_ptr += cmp_sz;
+ dst_ptr += cmp_sz;
+ cnt += cmp_sz;
+ }
+
+ size -= rc;
+ total += rc;
+ offset += rc;
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fprintf(stderr, "%8x/%8x", src_entry.actual, total);
+ }
+ }
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\n");
+ }
+
+ return total;
+}
diff --git a/fcp/src/misc.h b/fcp/src/misc.h
new file mode 100644
index 0000000..c96eb11
--- /dev/null
+++ b/fcp/src/misc.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: fcp_misc.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: Misc. helpers
+ * Date: 05/12/2012
+ */
+
+#ifndef __MISC__H__
+#define __MISC__H__
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <regex.h>
+
+#include <clib/list.h>
+
+#include <ffs/libffs.h>
+
+typedef struct entry_list entry_list_t;
+struct entry_list {
+ list_t list;
+ ffs_t * ffs;
+};
+
+typedef struct entry_node entry_node_t;
+struct entry_node {
+ list_node_t node;
+ ffs_entry_t entry;
+};
+
+extern entry_list_t * entry_list_create(ffs_t *);
+extern entry_list_t * entry_list_create_by_regex(ffs_t *, const char *);
+extern int entry_list_add(entry_list_t *, ffs_entry_t *);
+extern int entry_list_add_child(entry_list_t *, ffs_entry_t *);
+extern int entry_list_remove(entry_list_t *, entry_node_t *);
+extern int entry_list_delete(entry_list_t *);
+extern int entry_list_exists(entry_list_t *, ffs_entry_t *);
+extern ffs_entry_t * entry_list_find(entry_list_t *, const char *);
+extern int entry_list_dump(entry_list_t *, FILE *);
+
+extern int parse_offset(const char *, off_t *);
+extern int parse_size(const char *, size_t *);
+extern int parse_number(const char *, size_t *);
+extern int parse_path(const char *, char **, char **, char **);
+
+extern int dump_errors(const char *, FILE *);
+extern int check_file(const char *, FILE *, off_t);
+extern int is_file(const char *, const char *, const char *);
+extern int valid_type(const char *);
+
+extern FILE *__fopen(const char *, const char *, const char *, int);
+
+#endif /* __MISC__H__ */
diff --git a/fcp/x86/Makefile b/fcp/x86/Makefile
new file mode 100644
index 0000000..2a5a240
--- /dev/null
+++ b/fcp/x86/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ..
+
+ARCH=x86
+FCP_INSTALL = $(INST_USR_X86)
+FCP_INSTALL_TEST = $(INST_TESTS_X86)
+
+include ../Rules.mk
diff --git a/ffs/.gitignore b/ffs/.gitignore
new file mode 100644
index 0000000..3a1f0a1
--- /dev/null
+++ b/ffs/.gitignore
@@ -0,0 +1,3 @@
+*/ffs
+*/fcp
+test/test_libffs
diff --git a/ffs/Makefile b/ffs/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/ffs/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/ffs/Rules.mk b/ffs/Rules.mk
new file mode 100644
index 0000000..eb8b736
--- /dev/null
+++ b/ffs/Rules.mk
@@ -0,0 +1,30 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64
+CFLAGS += -I$(DEPTH)/.. -I$(DEPTH)/boot/fsp2_ipl -iquote..
+
+LDFLAGS += -L. -m32
+
+NAME=libffs
+
+CLIB=$(DEPTH)/../clib/x86/libclib.a
+
+OBJS=$(NAME).o $(NAME)2.o
+
+TARGETS=$(NAME).so $(NAME).a
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+$(NAME).so: $(OBJS)
+ $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $^
+
+$(NAME).a: $(OBJS)
+ $(AR) -r $@ $^
+
+install: $(TARGETS)
+ $(INSTALL) libffs.so libffs.a $(FFS_INSTALL)/lib
+ $(INSTALL) ../ffs.h ../libffs.h ../libffs2.h $(FFS_INSTALL)/include
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/ffs/Rules.test.mk b/ffs/Rules.test.mk
new file mode 100644
index 0000000..9b63533
--- /dev/null
+++ b/ffs/Rules.test.mk
@@ -0,0 +1,25 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/apps -iquote..
+LDFLAGS += -L. -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR)
+
+OBJS=test_libffs.o
+
+TARGETS=test_libffs
+
+vpath %.c ../test
+vpath %.h ..
+
+all: $(TARGETS)
+
+#libffs.a: $(OBJS)
+# $(AR) -r $@ $^
+
+test_libffs: test_libffs.o ../ppc/libffs.a $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a
+ $(CC) $(LDFLAGS) -o $@ $^ -lpthread
+
+install: $(TARGETS)
+ $(INSTALL) ../test/test_libffs $(TEST_LIBFFS_INSTALL)
+ $(INSTALL) ../test/libffs_test.sh $(TEST_LIBFFS_INSTALL)
+ $(INSTALL) ../test/ffs_tool_test.sh $(TEST_LIBFFS_INSTALL)
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/ffs/ffs.h b/ffs/ffs.h
new file mode 100644
index 0000000..3c113bc
--- /dev/null
+++ b/ffs/ffs.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * FSP Flash Structure
+ *
+ * This header defines the layout for the FSP Flash Structure.
+ */
+
+#ifndef __FFS_H__
+#define __FFS_H__
+
+/* Pull in the correct header depending on what is being built */
+#if defined(__KERNEL__)
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/* The version of this partition implementation */
+#define FFS_VERSION_1 1
+
+/* Magic number for the partition header (ASCII 'PART') */
+#define FFS_MAGIC 0x50415254
+
+/* The maximum length of the partition name */
+#define PART_NAME_MAX 15
+
+/*
+ * Sizes of the data structures
+ */
+#define FFS_HDR_SIZE sizeof(struct ffs_hdr)
+#define FFS_ENTRY_SIZE sizeof(struct ffs_entry)
+
+/*
+ * Sizes of the data structures w/o checksum
+ */
+#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t))
+#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t))
+
+/* pid of logical partitions/containers */
+#define FFS_PID_TOPLEVEL 0xFFFFFFFF
+
+/*
+ * Type of image contained w/in partition
+ */
+enum type {
+ FFS_TYPE_DATA = 1,
+ FFS_TYPE_LOGICAL = 2,
+ FFS_TYPE_PARTITION = 3,
+};
+
+/*
+ * Flag bit definitions
+ */
+#define FFS_FLAGS_PROTECTED 0x0001
+#define FFS_FLAGS_U_BOOT_ENV 0x0002
+
+/*
+ * Number of user data words
+ */
+#define FFS_USER_WORDS 16
+
+/*
+ * Define layout of user.data in struct ffs_entry
+ */
+enum user_data {
+ USER_DATA_VOL = 0,
+ USER_DATA_SIZE = 1,
+ USER_DATA_CRC = 2,
+};
+
+/**
+ * struct ffs_entry - Partition entry
+ *
+ * @name: Opaque null terminated string
+ * @base: Starting offset of partition in flash (in hdr.block_size)
+ * @size: Partition size (in hdr.block_size)
+ * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel)
+ * @id: Partition entry ID [1..65536]
+ * @type: Describe type of partition
+ * @flags: Partition attributes (optional)
+ * @actual: Actual partition size (in bytes)
+ * @resvd: Reserved words for future use
+ * @user: User data (optional)
+ * @checksum: Partition entry checksum (includes all above)
+ */
+struct ffs_entry {
+ char name[PART_NAME_MAX + 1];
+ uint32_t base;
+ uint32_t size;
+ uint32_t pid;
+ uint32_t id;
+ uint32_t type;
+ uint32_t flags;
+ uint32_t actual;
+ uint32_t resvd[4];
+ struct {
+ uint32_t data[FFS_USER_WORDS];
+ } user;
+ uint32_t checksum;
+} __attribute__ ((packed));
+
+/**
+ * struct ffs_hdr - FSP Flash Structure header
+ *
+ * @magic: Eye catcher/corruption detector
+ * @version: Version of the structure
+ * @size: Size of partition table (in block_size)
+ * @entry_size: Size of struct ffs_entry element (in bytes)
+ * @entry_count: Number of struct ffs_entry elements in @entries array
+ * @block_size: Size of block on device (in bytes)
+ * @block_count: Number of blocks on device
+ * @resvd: Reserved words for future use
+ * @checksum: Header checksum
+ * @entries: Pointer to array of partition entries
+ */
+struct ffs_hdr {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t size;
+ uint32_t entry_size;
+ uint32_t entry_count;
+ uint32_t block_size;
+ uint32_t block_count;
+ uint32_t resvd[4];
+ uint32_t checksum;
+ struct ffs_entry entries[];
+} __attribute__ ((packed));
+
+#endif /* __FFS_H__ */
diff --git a/ffs/libffs.h b/ffs/libffs.h
new file mode 100644
index 0000000..9772cac
--- /dev/null
+++ b/ffs/libffs.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+#ifndef __LIBFFS_H__
+#define __LIBFFS_H__
+
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <clib/exception.h>
+
+#include "ffs.h"
+
+typedef struct ffs_entry ffs_entry_t;
+typedef struct ffs_hdr ffs_hdr_t;
+typedef enum type ffs_type_t;
+
+#define FFS_EXCEPTION_DATA 1024
+
+/* ============================================================ */
+
+/*!
+ * @brief ffs I/O interface
+ */
+struct ffs {
+ ffs_hdr_t * hdr;
+
+ char * path;
+ off_t offset;
+
+ FILE * file;
+ uint32_t count;
+
+ void * buf; // FILE* buffer
+ uint32_t buf_count; // multiple of hdr->block_size
+
+ bool dirty;
+};
+
+typedef struct ffs ffs_t;
+
+struct ffs_exception {
+ int rc;
+ char data[FFS_EXCEPTION_DATA];
+};
+
+typedef struct ffs_exception ffs_exception_t;
+
+#define FFS_PARTITION_NAME "part"
+
+#define FFS_INFO_ERROR 0
+#define FFS_INFO_MAGIC 1
+#define FFS_INFO_VERSION 2
+#define FFS_INFO_ENTRY_SIZE 3
+#define FFS_INFO_ENTRY_COUNT 4
+#define FFS_INFO_BLOCK_SIZE 5
+#define FFS_INFO_BLOCK_COUNT 6
+#define FFS_INFO_BUFFER_COUNT 7
+#define FFS_INFO_OFFSET 8
+
+#define FFS_CHECK_PATH -3
+#define FFS_CHECK_HEADER_MAGIC -4
+#define FFS_CHECK_HEADER_CHECKSUM -5
+#define FFS_CHECK_ENTRY_CHECKSUM -6
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __ffs_fcheck(FILE *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_check(const char *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern ffs_t * __ffs_fcreate(FILE *, off_t, uint32_t, uint32_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern ffs_t * __ffs_create(const char *, off_t, uint32_t, uint32_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern ffs_t * __ffs_fopen(FILE *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern ffs_t * __ffs_open(const char *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_info(ffs_t *, int, uint32_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_buffer(ffs_t *, size_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_close(ffs_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_fclose(ffs_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_fsync(ffs_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_list_entries(ffs_t *, const char *, bool, FILE *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+extern int __ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*))
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern int __ffs_entry_find(ffs_t *, const char *, ffs_entry_t *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern int __ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern int __ffs_entry_name(ffs_t *, ffs_entry_t *, char *, size_t)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern int __ffs_entry_add(ffs_t *, const char *, off_t,
+ size_t, ffs_type_t, uint32_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern int __ffs_entry_delete(ffs_t *, const char *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern int __ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *)
+/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ;
+
+extern int __ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_hexdump(ffs_t *, const char *, FILE *)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_truncate(ffs_t *, const char *, size_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_write(ffs_t *, const char *, const void *,
+ off_t, size_t)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_copy(ffs_t *, ffs_t *, const char *)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern ssize_t __ffs_entry_compare(ffs_t *, ffs_t *, const char *)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+extern int __ffs_entry_list(ffs_t *, ffs_entry_t ** list)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+/* ============================================================ */
+
+#endif /* __LIBFFS_H__ */
diff --git a/ffs/libffs2.h b/ffs/libffs2.h
new file mode 100644
index 0000000..79bc856
--- /dev/null
+++ b/ffs/libffs2.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*!
+ * @file libffs2.h
+ * @brief FSP Flash File Structure API
+ * @details This library provides an API to the FFS partitioning scheme.
+ * @author Shaun Wetzstein <shaun@us.ibm.com>
+ * @date 2010-2012
+ */
+
+#ifndef __LIBFFS2_H__
+#define __LIBFFS2_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libffs.h"
+
+/* FFS_OPEN_* are deprecated, use FFS_CHECK_* instead */
+#define FFS_OPEN_INVALID_PATH FFS_CHECK_PATH
+#define FFS_OPEN_FOPEN_FAILURE -1
+#define FFS_OPEN_MAGIC_CHECK FFS_CHECK_HEADER_MAGIC
+#define FFS_OPEN_CRC_CHECK FFS_CHECK_HEADER_CHECKSUM
+
+/*!
+ * @brief Clears the (global) FFS error indicator
+ * @memberof ffs
+ * @return None
+ */
+extern void ffs_errclr(void);
+
+/*!
+ * @brief Return the error number of an FFS error
+ * @memberof ffs
+ * @return non-0 on success, '0' if no pending error
+ */
+extern int ffs_errnum(void);
+
+/*!
+ * @brief Return the error string of an FFS error
+ * @memberof ffs
+ * @return non-NULL on success, 'NULL' if no pending error
+ */
+extern const char * ffs_errstr(void);
+
+/*!
+ * @brief Open the file named 'path' and check the @em FFS partition table
+ * at 'offset' bytes from the beginning of the file (or device).
+ * @memberof ffs
+ * @param path [in] Path of target file or device
+ * @param offset [in] Byte offset, from the beginning of the file (or device),
+ * of the ffs_hdr_t structure
+ * @return 0 on success, non-0 otherwise
+ * FFS_CHECK_PATH if path is NULL or ""
+ * FFS_CHECK_HEADER_MAGIC for header magic corruption
+ * FFS_CHECK_HEADER_CHECKSUM for header data corruption
+ * FFS_CHECK_ENTRY_CHECKSUM for [an] entry data corruption
+ */
+extern int ffs_check(const char *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Create an empty @em FFS partition table and store it at 'offset'
+ * bytes from the beginning of the file (or device) named 'path'
+ * @memberof ffs
+ * @param path [in] Path of target file or device
+ * @param offset [in] Byte offset, from beginning of file (or device),
+ * of the ffs_hdr_t structure
+ * @param block_size [in] Block size in bytes
+ * @param block_count [in] Number of blocks in the device size
+ * @return Pointer to ffs_t (allocated on the heap) on success,
+ * NULL otherwise
+ */
+extern ffs_t * ffs_create(const char *, off_t, uint32_t, uint32_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Open the file name 'path' and read the @em FFS partition table
+ * at 'offset' bytes from the beggining of the file (or device).
+ * @memberof ffs
+ * @param path [in] Path of target file or device
+ * @param offset [in] Byte offset, from beginning of file (or device),
+ * of the ffs_hdr_t structure
+ * @return Pointer to ffs_t (allocated on the heap) on success,
+ * NULL otherwise
+ */
+extern ffs_t * ffs_open(const char *, off_t)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Query a @em FFS object for header metadata.
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param name [in] Field number, see ffs.h for details
+ * FFS_INFO_MAGIC - ffs_hdr::magic
+ * FFS_INFO_VERSION - ffs_hdr::version
+ * FFS_INFO_ENTRY_SIZE - ffs_hdr::entry_size
+ * FFS_INFO_ENTRY_COUNT - ffs_hdr::entry_count
+ * FFS_INFO_BLOCK_SIZE - ffs_hdr::block_size
+ * FFS_INFO_BLOCK_COUNT - ffs_hdr::block_count
+ * @param value [out] Pointer to output data
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_info(ffs_t *, int, uint32_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Close a @em FFS object, after writting any modified in-core
+ * data to the file (or device).
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_close(ffs_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Flush all modified in-core metadata of a @em FFS object,
+ * to the underlying file (or device).
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_fsync(ffs_t *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Pretty print the entries of a @em FFS partition table to
+ * stream 'out'
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param out [in] Pointer the output stream
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_list_entries(ffs_t *, FILE *)
+/*! @cond */ __nonnull ((1)) /*! @endcond */ ;
+
+/*!
+ * @brief Iterate over the entries of a @em FFS partition table and
+ * call a callback function 'func'
+ * @note If the callback function returns non-0, the iteration function
+ * will return immediately.
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param func [in] Pointer the callback function
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*))
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Find an entry in a @em FFS partition table and return
+ * a copy of the in 'entry'
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param path [in] Name of a partition entry
+ * @param entry [out] Target entry object
+ * @return '1' == found, '0' == not-found, error otherwise
+ */
+extern int ffs_entry_find(ffs_t *, const char *, ffs_entry_t *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Find the parent entry in a @em FFS partition table and
+ * return a copy of the in 'entry'
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param path [in] Name of a partition entry
+ * @param parent [out] Target entry object
+ * @return '1' == found, '0' == not-found, error otherwise
+ */
+extern int ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Add a partition entry to a @em FFS partition table
+ * @memberof ffs
+ * @param self [in] Pointer to ffs object
+ * @param path [in] Name of a partition entry
+ * @param offset [in] Offset, in blocks, of the partition entry, from
+ * the beginning of the file (or device)
+ * @param size [in] Size, in blocks, of the partition entry
+ * @param type [in] Partition type. FFS_TYPE_LOGICAL can be used to
+ * to create a container for a set of partitions. A logical partition
+ * can be thought of as a directory. Use FFS_TYPE_DATA for data
+ * partitions.
+ * @param flags [in] Partition flags. FFS_FLAG_PROTECTED can be used to
+ * protect a partition from inadvertant updates. The ffs related tools
+ * should *not* overwrite protected partitions unless a --protected
+ * flag is specified.
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_entry_add(ffs_t *, const char *, off_t, size_t, ffs_type_t, uint32_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Delete a partition entry from the @em FFS partition table
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param path [in] Name of a partition entry
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_entry_delete(ffs_t *, const char *)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Get the value of a meta-data user word
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param user [in] User word number, in the range [0..FFS_USER_WORDS]
+ * @param word [in] User word value, in the range [0..UINT32_MAX] (optional)
+ * @note 'word' is optional, if omitted, the current value is returned
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *)
+/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ;
+
+/*!
+ * @brief Set the value of a meta-data user word
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param user [in] User word number, in the range [0..FFS_USER_WORDS]
+ * @param word [in] User word value, in the range [0..UINT32_MAX] (optional)
+ * @note 'word' is optional, if omitted, the current value is returned
+ * @return '0' on success, non-0 otherwise
+ */
+extern int ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Hexdump the data contents of a partition entry to output stream
+ * 'out'
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param out [in] Output stream
+ * @return Negative on success, number of bytes written otherwise
+ */
+extern ssize_t ffs_entry_hexdump(ffs_t *, const char *, FILE *)
+/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ;
+
+/*!
+ * @brief Change the actual size of partition entry 'name'
+ * to 'offset' bytes from the beginning of the entry.
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param offset [in] Offset from the beginning of the partition (in bytes)
+ * @return Negative on failure, zero otherwise
+ */
+extern ssize_t ffs_entry_truncate_no_pad(ffs_t *, const char *, off_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Change the actual size of partition entry 'name'
+ * to 'offset' bytes from the beginning of the entry.
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param offset [in] Offset from the beginning of the partition (in bytes)
+ * @param pad [in] Pad character
+ * @return Negative on failure, zero otherwise
+ */
+extern ssize_t ffs_entry_truncate(ffs_t *, const char *, off_t, uint8_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Read 'count' data bytes of partition entry 'name' to into 'buf' at
+ * offset 'offset' bytes from the beginning of the entry
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param buf [out] Output data buffer
+ * @param offset [in] Offset from the beginning of the partition
+ * @param count [in] Number of bytes to read
+ * @return Negative on failure, number of bytes read otherwise
+ */
+extern ssize_t ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Write 'count' data bytes to partition entry 'name' at offset
+ * 'offset' bytes from the beginning of the entry, from input
+ * buffer 'buf'
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param name [in] Name of a partition entry
+ * @param buf [in] Input data buffer
+ * @param offset [in] Offset from the beginning of the partition
+ * @param count [in] Number of bytes to write
+ * @return Negative on failure, else number of bytes written otherwise
+ */
+extern ssize_t ffs_entry_write(ffs_t *, const char *, const void *, off_t, size_t)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+/*!
+ * @brief Return an array of entry_t structures, one each partition that
+ * exists in the partition table
+ * @memberof ffs
+ * @param self [in] Pointer to an ffs object
+ * @param list [out] Array of entry_t structures allocated on the heap
+ * @return Negative on failure, else number of entry_t's in the output
+ * array with a call to free()
+ * @note Caller is responsible for freeing array with a call to free()
+ */
+extern ssize_t ffs_entry_list(ffs_t *, ffs_entry_t **)
+/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBFFS2_H__ */
diff --git a/ffs/src/ffs-fsp.h b/ffs/src/ffs-fsp.h
new file mode 100644
index 0000000..f1b3374
--- /dev/null
+++ b/ffs/src/ffs-fsp.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2014
+ *
+ * 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
+ */
+
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * FSP Flash Structure, Boot specific adaptations
+ */
+
+#ifndef __FFS_BOOT_H__
+#define __FFS_BOOT_H__
+
+#include "ffs.h"
+
+/*
+ * Values to use in USER_DATA_VOL
+ *
+ * @note: Side 0/1 must be defined as even/odd values. Code in the IPL depends
+ * on this to be able to use the appropriate volume based on the boot bank.
+ */
+enum vol {
+ FFS_VOL_IPL0 = 0,
+ FFS_VOL_IPL1 = 1,
+ FFS_VOL_SPL0 = 2,
+ FFS_VOL_SPL1 = 3,
+ FFS_VOL_BOOTENV0 = 4,
+ FFS_VOL_BOOTENV1 = 5,
+ FFS_VOL_KERNEL0 = 6,
+ FFS_VOL_KERNEL1 = 7,
+ FFS_VOL_INITRAMFS0 = 8,
+ FFS_VOL_INITRAMFS1 = 9,
+ FFS_VOL_DTB0 = 10,
+ FFS_VOL_DTB1 = 11,
+ FFS_VOL_SERIES0 = 12,
+ FFS_VOL_SERIES1 = 13,
+ FFS_VOL_CARD0 = 14,
+ FFS_VOL_CARD1 = 15,
+ FFS_VOL_DUMP0 = 16,
+ FFS_VOL_DUMP1 = 17,
+ FFS_VOL_DUMP2 = 18,
+ FFS_VOL_DUMP3 = 19,
+};
+
+#endif /* __FFS_BOOT_H__ */
diff --git a/ffs/src/libffs.c b/ffs/src/libffs.c
new file mode 100644
index 0000000..b912ede
--- /dev/null
+++ b/ffs/src/libffs.c
@@ -0,0 +1,1520 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: libffs.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: FFS IO interface
+ * Note:
+ * Date: 05/07/12
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <endian.h>
+#include <libgen.h>
+#include <regex.h>
+
+#include "libffs.h"
+
+#include <clib/assert.h>
+#include <clib/builtin.h>
+#include <clib/checksum.h>
+#include <clib/misc.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#ifndef be32toh
+#include <byteswap.h>
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define be32toh(x) __bswap_32(x)
+#define htobe32(x) __bswap_32(x)
+#else
+#define be32toh(x) (x)
+#define htobe32(x) (x)
+#endif
+#endif
+
+#define FFS_ENTRY_EXTENT 10UL
+
+/* ============================================================ */
+
+static void __hdr_be32toh(ffs_hdr_t * hdr)
+{
+ assert(hdr != NULL);
+
+ hdr->magic = be32toh(hdr->magic);
+ hdr->version = be32toh(hdr->version);
+ hdr->size = be32toh(hdr->size);
+ hdr->entry_size = be32toh(hdr->entry_size);
+ hdr->entry_count = be32toh(hdr->entry_count);
+ hdr->block_size = be32toh(hdr->block_size);
+ hdr->block_count = be32toh(hdr->block_count);
+ hdr->checksum = be32toh(hdr->checksum);
+}
+
+static void __hdr_htobe32(ffs_hdr_t * hdr)
+{
+ assert(hdr != NULL);
+
+ hdr->magic = htobe32(hdr->magic);
+ hdr->version = htobe32(hdr->version);
+ hdr->size = htobe32(hdr->size);
+ hdr->entry_size = htobe32(hdr->entry_size);
+ hdr->entry_count = htobe32(hdr->entry_count);
+ hdr->block_size = htobe32(hdr->block_size);
+ hdr->block_count = htobe32(hdr->block_count);
+ hdr->checksum = htobe32(hdr->checksum);
+}
+
+static void __entry_be32toh(ffs_entry_t * entry)
+{
+ assert(entry != NULL);
+
+ entry->base = be32toh(entry->base);
+ entry->size = be32toh(entry->size);
+ entry->pid = be32toh(entry->pid);
+ entry->id = be32toh(entry->id);
+ entry->type = be32toh(entry->type);
+ entry->flags = be32toh(entry->flags);
+ entry->actual = be32toh(entry->actual);
+ entry->checksum = be32toh(entry->checksum);
+
+ entry->resvd[0] = be32toh(entry->resvd[0]);
+ entry->resvd[1] = be32toh(entry->resvd[1]);
+ entry->resvd[2] = be32toh(entry->resvd[2]);
+ entry->resvd[3] = be32toh(entry->resvd[3]);
+
+ for (int j = 0; j < FFS_USER_WORDS; j++)
+ entry->user.data[j] = be32toh(entry->user.data[j]);
+}
+
+static void __entry_htobe32(ffs_entry_t * entry)
+{
+ assert(entry != NULL);
+
+ entry->base = htobe32(entry->base);
+ entry->size = htobe32(entry->size);
+ entry->pid = htobe32(entry->pid);
+ entry->id = htobe32(entry->id);
+ entry->type = htobe32(entry->type);
+ entry->flags = htobe32(entry->flags);
+ entry->actual = htobe32(entry->actual);
+ entry->checksum = htobe32(entry->checksum);
+
+ entry->resvd[0] = htobe32(entry->resvd[0]);
+ entry->resvd[1] = htobe32(entry->resvd[1]);
+ entry->resvd[2] = htobe32(entry->resvd[2]);
+ entry->resvd[3] = htobe32(entry->resvd[3]);
+
+ for (int j = 0; j < FFS_USER_WORDS; j++)
+ entry->user.data[j] = htobe32(entry->user.data[j]);
+}
+
+static int __hdr_read(ffs_hdr_t * hdr, FILE * file, off_t offset)
+{
+ assert(hdr != NULL);
+
+ if (fseeko(file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t rc = fread(hdr, 1, sizeof(*hdr), file);
+ if (rc <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ uint32_t ck = memcpy_checksum(NULL, (void *)hdr,
+ offsetof(ffs_hdr_t, checksum));
+
+ __hdr_be32toh(hdr);
+
+ if (hdr->magic != FFS_MAGIC) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC,
+ "magic number mismatch '%x' != '%x'",
+ hdr->magic, FFS_MAGIC);
+ return -1;
+ }
+
+ if (hdr->checksum != ck) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM,
+ "header checksum mismatch '%x' != '%x'",
+ hdr->checksum, ck);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int __hdr_write(ffs_hdr_t * hdr, FILE * file, off_t offset)
+{
+ assert(hdr != NULL);
+ assert(hdr->magic == FFS_MAGIC);
+
+ hdr->checksum = memcpy_checksum(NULL, (void *)hdr,
+ offsetof(ffs_hdr_t, checksum));
+ hdr->checksum = htobe32(hdr->checksum);
+
+ if (fseeko(file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t size = sizeof(*hdr);
+
+ if (0 < hdr->entry_count) {
+ size += hdr->entry_count * hdr->entry_size;
+
+ for (size_t i=0; i<hdr->entry_count; i++) {
+ ffs_entry_t *e = hdr->entries + i;
+
+ __entry_htobe32(e);
+
+ e->checksum = memcpy_checksum(NULL, (void *)e,
+ offsetof(ffs_entry_t,
+ checksum));
+ e->checksum = htobe32(e->checksum);
+ }
+ }
+
+ __hdr_htobe32(hdr);
+
+ size_t rc = fwrite(hdr, 1, size, file);
+ if (rc <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ __hdr_be32toh(hdr);
+ if (0 < hdr->entry_count)
+ for (size_t i=0; i<hdr->entry_count; i++)
+ __entry_be32toh(hdr->entries + i);
+
+ return 0;
+}
+
+static int __entries_read(ffs_hdr_t * hdr, FILE * file, off_t offset)
+{
+ assert(hdr != NULL);
+ assert(hdr->magic == FFS_MAGIC);
+
+ if (0 < hdr->entry_count) {
+ if (fseeko(file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t size = hdr->entry_count * hdr->entry_size;
+
+ size_t rc = fread(hdr->entries, 1, size, file);
+ if (rc <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ for (size_t i=0; i<hdr->entry_count; i++) {
+ ffs_entry_t *e = hdr->entries + i;
+
+ uint32_t ck = memcpy_checksum(NULL, (void *)e,
+ offsetof(ffs_entry_t,
+ checksum));
+ __entry_be32toh(e);
+
+ if (e->checksum != ck) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM,
+ "'%s' entry checksum mismatch '%x' != "
+ "'%x'", e->name, e->checksum, ck);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+static void __entries_write(ffs_hdr_t * hdr, FILE * file, off_t offset)
+{
+ if (unlikely(hdr == NULL))
+ ffs_throw(UNEX, 10400, "NULL hdr pointer");
+ if (hdr->magic != FFS_MAGIC)
+ ffs_throw(UNEX, 10401, "magic number mismatch '%x' != "
+ "'%x'", hdr->magic, FFS_MAGIC);
+
+ if (0 < hdr->entry_count) {
+ size_t size = hdr->entry_count * hdr->entry_size;
+
+ for (size_t i = 0; i < hdr->entry_count; i++) {
+ ffs_entry_t *e = hdr->entries + i;
+ __entry_htobe32(e);
+ e->checksum = memcpy_checksum(NULL, (void *)e,
+ offsetof(ffs_entry_t,
+ checksum));
+ e->checksum = htobe32(e->checksum);
+ }
+
+ if (fseeko(file, offset, SEEK_SET) != 0)
+ ffs_throw(ERR, 10402, "%s (errno=%d)",
+ strerror(errno), errno);
+
+ size_t rc = fwrite(hdr->entries, 1, size, file);
+ if (rc <= 0 && ferror(file))
+ ffs_throw(ERR, 10403, "%s (errno=%d)",
+ strerror(errno), errno);
+
+ fflush(file);
+
+ for (size_t i = 0; i < hdr->entry_count; i++)
+ __entry_be32toh(hdr->entries + i);
+ }
+}
+#endif
+
+static ffs_entry_t *__iterate_entries(ffs_hdr_t * self,
+ int (*func) (ffs_entry_t *))
+{
+ assert(self != NULL);
+ assert(func != NULL);
+
+ for (uint32_t i = 0; i < self->entry_count; i++) {
+ if (func(self->entries + i) != 0)
+ return self->entries + i;
+ }
+
+ return NULL;
+}
+
+static ffs_entry_t *__find_entry(ffs_hdr_t * self, const char *path)
+{
+ assert(self != NULL);
+
+ if (path == NULL || *path == '\0')
+ return NULL;
+
+ if (*path == '/')
+ path++;
+
+ char __path[strlen(path) + 1];
+ strcpy(__path, path);
+ path = __path;
+
+ ffs_entry_t root = {.id = FFS_PID_TOPLEVEL }, *parent = &root;
+
+ char *name;
+ while (parent != NULL && (name = strtok((char *)path, "/")) != NULL) {
+ path = NULL;
+
+ int find_entry(ffs_entry_t * child) {
+ return (parent->id == child->pid &&
+ strncmp(name, child->name,
+ sizeof(child->name)) == 0);
+ }
+
+ parent = __iterate_entries(self, find_entry);
+ }
+
+ return parent;
+}
+
+/* ============================================================ */
+
+int __ffs_fcheck(FILE *file, off_t offset)
+{
+ assert(file != NULL);
+
+ RAII(ffs_hdr_t*, hdr, malloc(sizeof(*hdr)), free);
+ if (hdr == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (fseeko(file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fread(hdr, 1, sizeof(*hdr), file) <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ uint32_t ck = memcpy_checksum(NULL, (void *)hdr,
+ offsetof(ffs_hdr_t, checksum));
+ __hdr_be32toh(hdr);
+
+ if (hdr->magic != FFS_MAGIC) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC,
+ "header magic mismatch '%x' != '%x'",
+ hdr->magic, FFS_MAGIC);
+ return FFS_CHECK_HEADER_MAGIC;
+ }
+ if (hdr->checksum != ck) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM,
+ "header checksum mismatch '%x' != '%x'",
+ hdr->checksum, ck);
+ return FFS_CHECK_HEADER_CHECKSUM;
+ }
+
+ size_t size = hdr->entry_count * hdr->entry_size;
+
+ hdr = (ffs_hdr_t *)realloc(hdr, sizeof(*hdr) + size);
+ if (hdr == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ memset(hdr->entries, 0, size);
+
+ if (0 < hdr->entry_count) {
+ if (fread(hdr->entries, 1, size, file) <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ for (size_t i = 0; i < hdr->entry_count; i++) {
+ ffs_entry_t *e = hdr->entries + i;
+
+ uint32_t ck = memcpy_checksum(NULL, (void *)e,
+ offsetof(ffs_entry_t,
+ checksum));
+
+ __entry_be32toh(e);
+
+ if (e->checksum != ck) {
+ ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM,
+ "'%s' entry checksum mismatch '%x' != "
+ "'%x'", e->name, e->checksum, ck);
+ return FFS_CHECK_ENTRY_CHECKSUM;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int __ffs_check(const char *path, off_t offset)
+{
+ if (path == NULL || *path == '\0') {
+ UNEXPECTED("invalid path '%s'\n", path);
+ return -1;
+ }
+
+ RAII(FILE*, file, fopen(path, "r"), fclose);
+ if (file == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return __ffs_fcheck(file, offset);
+}
+
+ffs_t *__ffs_fcreate(FILE *file, off_t offset, uint32_t block_size,
+ uint32_t block_count)
+{
+ assert(file != NULL);
+
+ if (!is_pow2(block_size)) {
+ UNEXPECTED("'%d' invalid block size (must be non-0 and a "
+ "power of 2)", block_size);
+ return NULL;
+ }
+ if (!is_pow2(block_count)) {
+ UNEXPECTED("'%d' invalid block count (must be non-0 and a "
+ "power of 2)", block_count);
+ return NULL;
+ }
+ if (offset & (block_size - 1)) {
+ UNEXPECTED("'%lld' invalid offset (must be 'block size' "
+ "aligned)", offset);
+ return NULL;
+ }
+
+ ffs_t *self = (ffs_t *) malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+
+ memset(self, 0, sizeof(*self));
+ self->file = file;
+ self->offset = offset;
+ self->count = FFS_ENTRY_EXTENT;
+ self->dirty = true;
+
+ self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr));
+ if (self->hdr == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+
+ self->hdr->magic = FFS_MAGIC;
+ self->hdr->version = FFS_VERSION_1;
+ self->hdr->size = 1;
+ self->hdr->entry_size = sizeof(*self->hdr->entries);
+ self->hdr->entry_count = 0;
+ self->hdr->block_size = block_size;
+ self->hdr->block_count = block_count;
+ self->hdr->checksum = 0;
+
+ size_t size = self->count * self->hdr->entry_size;
+
+ self->hdr = (ffs_hdr_t *) realloc(self->hdr, sizeof(*self->hdr) + size);
+ if (self->hdr == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+ memset(self->hdr->entries, 0, size);
+
+ if (__ffs_entry_add(self, FFS_PARTITION_NAME, offset, block_size,
+ FFS_TYPE_PARTITION, FFS_FLAGS_PROTECTED) < 0)
+ goto error;
+ if (__ffs_entry_truncate(self, FFS_PARTITION_NAME, block_size) < 0)
+ goto error;
+
+ if (false) {
+ error:
+ if (self != NULL) {
+ if (self->file != NULL)
+ fclose(self->file), self->file = NULL;
+ if (self->path != NULL)
+ free(self->path), self->path = NULL;
+ if (self->hdr != NULL)
+ free(self->hdr), self->hdr = NULL;
+ free(self), self = NULL;
+ }
+ }
+
+ return self;
+}
+
+ffs_t *__ffs_create(const char *path, off_t offset, uint32_t block_size,
+ uint32_t block_count)
+{
+ assert(path != NULL);
+
+ FILE * file = fopen(path, "r+");
+ if (file == NULL) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ ffs_t * self = __ffs_fcreate(file, offset, block_size, block_count);
+ if (self != NULL)
+ self->path = strdup(path);
+
+ return self;
+}
+
+ffs_t *__ffs_fopen(FILE * file, off_t offset)
+{
+ assert(file != NULL);
+
+ ffs_t *self = (ffs_t *) malloc(sizeof(*self));
+ if (self == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+
+ memset(self, 0, sizeof(*self));
+ self->file = file;
+ self->count = 0;
+ self->offset = offset;
+ self->dirty = false;
+
+ self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr));
+ if (self->hdr == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+ memset(self->hdr, 0, sizeof(*self->hdr));
+
+ if (__hdr_read(self->hdr, self->file, self->offset) < 0)
+ goto error;
+
+ self->count = max(self->hdr->entry_count, FFS_ENTRY_EXTENT);
+ size_t size = self->count * self->hdr->entry_size;
+
+ self->hdr = (ffs_hdr_t *)realloc(self->hdr, sizeof(*self->hdr) + size);
+ if (self->hdr == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+ memset(self->hdr->entries, 0, size);
+
+ if (0 < self->hdr->entry_count) {
+ if (__entries_read(self->hdr, self->file,
+ self->offset + sizeof(*self->hdr)) < 0)
+ goto error;
+ }
+
+ self->buf_count = 1; // default to 1
+
+ self->buf = malloc(self->buf_count * self->hdr->block_size);
+ if (self->hdr == NULL) {
+ ERRNO(errno);
+ goto error;
+ }
+
+ if (setvbuf(self->file, self->buf, _IOFBF,
+ self->buf_count * self->hdr->block_size) != 0) {
+ ERRNO(errno);
+ goto error;
+ }
+
+ if (false) {
+ error:
+ if (self != NULL) {
+ if (self->buf != NULL)
+ free(self->buf), self->buf = NULL;
+ if (self->hdr != NULL)
+ free(self->hdr), self->hdr = NULL;
+
+ free(self), self = NULL;
+ }
+ }
+
+ return self;
+}
+
+ffs_t *__ffs_open(const char *path, off_t offset)
+{
+ assert(path != NULL);
+
+ FILE *file = fopen(path, "r+");
+ if (file == NULL) {
+ ERRNO(errno);
+ return NULL;
+ }
+
+ ffs_t *self = __ffs_fopen(file, offset);
+ if (self != NULL)
+ self->path = strdup(path);
+
+ return self;
+}
+
+static int ffs_flush(ffs_t * self)
+{
+ assert(self != NULL);
+
+ if (__hdr_write(self->hdr, self->file, self->offset) < 0)
+ return -1;
+
+ if (fflush(self->file) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fileno(self->file) != -1) {
+ if (fsync(fileno(self->file)) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+
+ self->dirty = false;
+
+ return 0;
+}
+
+int __ffs_info(ffs_t * self, int name, uint32_t *value)
+{
+ assert(self != NULL);
+ assert(value != NULL);
+
+ switch (name) {
+ case FFS_INFO_MAGIC:
+ *value = self->hdr->magic;
+ break;
+ case FFS_INFO_VERSION:
+ *value = self->hdr->version;
+ break;
+ case FFS_INFO_ENTRY_SIZE:
+ *value = self->hdr->entry_size;
+ break;
+ case FFS_INFO_ENTRY_COUNT:
+ *value = self->hdr->entry_count;
+ break;
+ case FFS_INFO_BLOCK_SIZE:
+ *value = self->hdr->block_size;
+ break;
+ case FFS_INFO_BLOCK_COUNT:
+ *value = self->hdr->block_count;
+ break;
+ case FFS_INFO_BUFFER_COUNT:
+ *value = self->buf_count;
+ break;
+ case FFS_INFO_OFFSET:
+ *value = self->offset;
+ break;
+ default:
+ UNEXPECTED("'%d' invalid info field", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int __ffs_buffer(ffs_t * self, size_t size)
+{
+ assert(self != NULL);
+
+ if (size == 0)
+ size = self->hdr->block_size;
+
+ if (self->buf != NULL) {
+ free(self->buf);
+ self->buf_count = 0;
+ }
+
+ self->buf_count = size / self->hdr->block_size;
+ size = self->buf_count * self->hdr->block_size;
+
+ self->buf = malloc(size);
+ if (self->buf == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (setvbuf(self->file, self->buf, _IOFBF, size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int __ffs_fclose(ffs_t * self)
+{
+ if (self == NULL)
+ return 0;
+
+ if (self->dirty == true)
+ if (ffs_flush(self) < 0)
+ return -1;
+
+ if (self->buf != NULL)
+ free(self->buf), self->buf = NULL;
+ if (self->hdr != NULL)
+ free(self->hdr), self->hdr = NULL;
+
+ memset(self, 0, sizeof(*self));
+ free(self);
+
+ return 0;
+}
+
+int __ffs_close(ffs_t * self)
+{
+ if (unlikely(self == NULL))
+ return 0;
+
+ if (self->dirty == true)
+ if (ffs_flush(self) < 0)
+ return -1;
+
+ if (self->path != NULL)
+ free(self->path), self->path = NULL;
+ if (self->file != NULL)
+ fclose(self->file), self->file = NULL;
+
+ return __ffs_fclose(self);
+}
+
+int __ffs_fsync(ffs_t * self)
+{
+ assert(self != NULL);
+
+ if (fflush(self->file) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fileno(self->file) != -1)
+ if (fsync(fileno(self->file)) < 0)
+ return -1;
+
+ return 0;
+}
+
+static ffs_entry_t *__add_entry_check(ffs_hdr_t * self, off_t offset,
+ size_t size)
+{
+ assert(self != NULL);
+
+ int find_overlap(ffs_entry_t * entry) {
+ if (entry->type == FFS_TYPE_LOGICAL)
+ return 0;
+
+ off_t entry_start = entry->base;
+ off_t entry_end = entry_start + entry->size - 1;
+
+ off_t new_start = offset / self->block_size;
+ off_t new_end = new_start + (size / self->block_size) - 1;
+
+ return !(new_start < entry_start && new_end < entry_start) &&
+ !(entry_end < new_start && entry_end < new_end);
+ }
+
+ return __iterate_entries(self, find_overlap);
+}
+
+int __ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *))
+{
+ return __iterate_entries(self->hdr, func) != NULL;
+}
+
+int __ffs_list_entries(ffs_t * self, const char * name, bool user, FILE * out)
+{
+ assert(self != NULL);
+
+ if (out == NULL)
+ out = stdout;
+
+ char full_name[4096];
+ regex_t rx;
+
+ int print_entry(ffs_entry_t * entry)
+ {
+ size_t offset = entry->base * self->hdr->block_size;
+ size_t size = entry->size * self->hdr->block_size;
+
+ if (__ffs_entry_name(self, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ fprintf(stdout, "%3d [%08x-%08x:%8x] "
+ "[%c%c%c%c%c%c%c%c%c%c] %s\n",
+ entry->id, offset, offset+size-1, entry->actual,
+ entry->type == FFS_TYPE_LOGICAL ? 'l' : 'd',
+ /* reserved */ '-', '-', '-', '-', '-', '-', '-',
+ entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-',
+ entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-',
+ full_name);
+
+ if (user == true) {
+ for (int i=0; i<FFS_USER_WORDS; i++) {
+ fprintf(stdout, "[%2d] %8x ", i,
+ entry->user.data[i]);
+ if ((i+1) % 4 == 0)
+ fprintf(stdout, "\n");
+ }
+ }
+
+ return 0;
+ }
+
+ if (0 < self->count) {
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return-1;
+ }
+
+ fprintf(out, "========================[ PARTITION TABLE 0x%llx "
+ "]=======================\n", self->offset);
+ fprintf(out, "vers:%04x size:%04x * blk:%06x blk(s):%06x * "
+ "entsz:%06x ent(s):%06x\n",
+ self->hdr->version, self->hdr->size,
+ self->hdr->block_size, self->hdr->block_count,
+ self->hdr->entry_size, self->hdr->entry_count);
+ fprintf(out, "------------------------------------------------"
+ "---------------------------\n");
+
+ (void)__iterate_entries(self->hdr, print_entry);
+
+ fprintf(stdout, "\n");
+
+ regfree(&rx);
+ }
+
+ return 0;
+}
+
+int __ffs_entry_find(ffs_t *self, const char *path, ffs_entry_t *entry)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ ffs_entry_t *__entry = __find_entry(self->hdr, path);
+ if (__entry != NULL && entry != NULL)
+ *entry = *__entry;
+
+ return __entry != NULL;
+}
+
+int __ffs_entry_find_parent(ffs_t *self, const char *path, ffs_entry_t *entry)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ if (*path == '/')
+ path++;
+
+ char __path[strlen(path) + 1];
+ strcpy(__path, path);
+ char *parent_path = dirname(__path);
+
+ int found = 0;
+
+ if (strcmp(parent_path, ".") != 0) {
+ ffs_entry_t parent;
+
+ found = __ffs_entry_find(self, parent_path, &parent);
+
+ if (found && entry != NULL)
+ *entry = parent;
+ }
+
+ return found;
+}
+
+int __ffs_entry_name(ffs_t *self, ffs_entry_t *entry, char *name, size_t size)
+{
+ assert(self != NULL);
+ assert(entry != NULL);
+
+ ffs_hdr_t *hdr = self->hdr;
+
+ int __entry_name(ffs_entry_t *parent, char *name, size_t size) {
+ assert(parent != NULL);
+ assert(name != NULL);
+
+ if (parent->pid != FFS_PID_TOPLEVEL) {
+ for (uint32_t i = 0; i < hdr->entry_count; i++) {
+ if (hdr->entries[i].id == parent->pid) {
+ __entry_name(hdr->entries + i, name,
+ size);
+ break;
+ }
+ }
+ }
+
+ if (strlen(name) + strlen(parent->name) < size)
+ strcat(name, parent->name);
+
+ if (parent->id != entry->id) {
+ if (strlen(name) + strlen("/") < size)
+ strcat(name, "/");
+ }
+
+ return 0;
+ }
+
+ memset(name, 0, size);
+
+ return __entry_name(entry, name, size);
+}
+
+int __ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size,
+ ffs_type_t type, uint32_t flags)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ if (__ffs_entry_find(self, path, NULL) == true) {
+ UNEXPECTED("'%s' entry already exists", path);
+ return -1;
+ }
+
+ ffs_entry_t parent = {.id = FFS_PID_TOPLEVEL };
+ (void)__ffs_entry_find_parent(self, path, &parent);
+
+ ffs_hdr_t *hdr = self->hdr;
+
+ if (type != FFS_TYPE_LOGICAL) {
+ ffs_entry_t *overlap = __add_entry_check(hdr, offset, size);
+ if (overlap != NULL) {
+ UNEXPECTED("'%s' at offset %lld and size %d overlaps "
+ "'%s' at offset %d and size %d",
+ path, offset, size, overlap->name,
+ overlap->base * hdr->block_size,
+ overlap->size * hdr->block_size);
+ return -1;
+ }
+ }
+
+ int find_empty(ffs_entry_t * empty) {
+ return empty->type == 0;
+ }
+
+ ffs_entry_t *entry = __iterate_entries(hdr, find_empty);
+ if (entry == NULL) {
+ if (self->count <= hdr->entry_count) {
+ size_t new_size;
+ new_size = hdr->entry_size *
+ (self->count + FFS_ENTRY_EXTENT);
+
+ self->hdr = (ffs_hdr_t *) realloc(self->hdr,
+ sizeof(*self->hdr) +
+ new_size);
+ assert(self->hdr != NULL);
+
+ if (hdr != self->hdr)
+ hdr = self->hdr;
+
+ memset(hdr->entries + self->count, 0,
+ FFS_ENTRY_EXTENT * hdr->entry_size);
+
+ self->count += FFS_ENTRY_EXTENT;
+ }
+
+ entry = hdr->entries + hdr->entry_count;
+ }
+
+ uint32_t max_id = 0;
+
+ int find_max_id(ffs_entry_t * max) {
+ if (max_id < max->id)
+ max_id = max->id;
+ return 0;
+ }
+
+ (void)__iterate_entries(hdr, find_max_id);
+
+ char name[strlen(path) + 1];
+ strcpy(name, path);
+ strncpy(entry->name, basename(name), sizeof(entry->name));
+ entry->id = max_id + 1;
+ entry->pid = parent.id;
+ entry->base = offset / hdr->block_size;
+ entry->size = size / hdr->block_size;
+ entry->type = type;
+ entry->flags = flags;
+ entry->checksum = 0;
+
+ hdr->entry_count++;
+
+ self->dirty = true;
+
+ return 0;
+}
+
+int __ffs_entry_delete(ffs_t * self, const char *path)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(self, path, &entry) == false) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, self->offset);
+ return -1;
+ }
+
+ if (entry.type == FFS_TYPE_PARTITION) {
+ UNEXPECTED("'%s' cannot --delete partition type entries", path);
+ return -1;
+ }
+
+ uint32_t children = 0;
+
+ int find_children(ffs_entry_t * child) {
+ if (entry.id == child->pid)
+ children++;
+ return 0;
+ }
+
+ ffs_hdr_t *hdr = self->hdr;
+
+ (void)__iterate_entries(hdr, find_children);
+
+ if (0 < children) {
+ UNEXPECTED("'%s' has '%d' children, --delete those first",
+ path, children);
+ return -1;
+ }
+
+ int find_entry_id(ffs_entry_t * __entry) {
+ return entry.id == __entry->id;
+ }
+
+ ffs_entry_t *entry_p = __iterate_entries(hdr, find_entry_id);
+ assert(entry_p != NULL);
+
+ int start = entry_p - hdr->entries;
+ int count = hdr->entry_count - start;
+
+ memmove(entry_p, entry_p + 1, hdr->entry_size * count);
+
+ hdr->entry_count = max(0UL, hdr->entry_count - 1);
+ memset(hdr->entries + hdr->entry_count, 0, hdr->entry_size);
+
+ self->dirty = true;
+
+ return 0;
+}
+
+int __ffs_entry_user_get(ffs_t *self, const char *path, uint32_t word,
+ uint32_t *value)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+ assert(value != NULL);
+
+ if (FFS_USER_WORDS <= word) {
+ UNEXPECTED("word '%d' outside range [0..%d]",
+ word, FFS_USER_WORDS - 1);
+ return -1;
+ }
+
+ ffs_entry_t *entry = __find_entry(self->hdr, path);
+ if (entry == NULL) {
+ UNEXPECTED("entry '%s' not found in partition table at "
+ "offset '%llx'", path, self->offset);
+ return -1;
+ }
+
+ *value = entry->user.data[word];
+
+ return 0;
+}
+
+int __ffs_entry_user_put(ffs_t *self, const char *path, uint32_t word,
+ uint32_t value)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ if (FFS_USER_WORDS <= word) {
+ UNEXPECTED("word '%d' outside range [0..%d]",
+ word, FFS_USER_WORDS - 1);
+ return -1;
+ }
+
+ ffs_entry_t *entry = __find_entry(self->hdr, path);
+ if (entry == NULL) {
+ UNEXPECTED("entry '%s' not found in partition table at "
+ "offset '%llx'", path, self->offset);
+ return -1;
+ }
+
+ entry->user.data[word] = value;
+ self->dirty = true;
+
+ return 0;
+}
+
+ssize_t __ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(self, path, &entry) == false) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, self->offset);
+ return -1;
+ }
+
+ size_t size = entry.size * self->hdr->block_size;
+ if (entry.actual < size)
+ size = entry.actual;
+
+ off_t offset = entry.base * self->hdr->block_size;
+
+ if (fseeko(self->file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ssize_t total = 0;
+
+ size_t block_size = self->hdr->block_size;
+ char block[block_size];
+ while (0 < size) {
+ size_t rc = fread(block, 1, min(block_size, size),
+ self->file);
+ if (rc <= 0) {
+ if (ferror(self->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+ break;
+ }
+
+ dump_memory(out, offset + total, block, rc);
+
+ total += rc;
+ size -= rc;
+ }
+
+ return total;
+}
+
+ssize_t __ffs_entry_truncate(ffs_t * self, const char *path, size_t size)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+
+ ffs_entry_t * entry = __find_entry(self->hdr, path);
+ if (entry == NULL) {
+ UNEXPECTED("entry '%s' not found in partition table at "
+ "offset '%llx'", path, self->offset);
+ return -1;
+ }
+
+ if ((entry->size * self->hdr->block_size) < size) {
+ errno = EFBIG;
+ ERRNO(errno);
+ return -1;
+ } else {
+ entry->actual = size;
+ self->dirty = true;
+ }
+
+ return 0;
+}
+
+ssize_t __ffs_entry_read(ffs_t * self, const char *path, void *buf,
+ off_t offset, size_t count)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+ assert(buf != NULL);
+
+ if (count == 0)
+ return 0;
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(self, path, &entry) == false) {
+ UNEXPECTED("entry '%s' not found in partition table at "
+ "offset '%llx'", path, self->offset);
+ return -1;
+ }
+
+ size_t entry_size = entry.size * self->hdr->block_size;
+ if (entry.actual < entry_size)
+ entry_size = entry.actual;
+ off_t entry_offset = entry.base * self->hdr->block_size;
+
+ if (entry_size <= offset)
+ return 0;
+ else
+ count = min(count, (entry_offset + entry_size) - offset);
+
+ ssize_t total = 0;
+
+ if (fseeko(self->file, entry_offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fseeko(self->file, offset, SEEK_CUR) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ while (0 < count) {
+ size_t rc = fread(buf + total, 1, count, self->file);
+ if (rc <= 0) {
+ if (ferror(self->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+ break;
+ }
+
+ total += rc;
+ count -= rc;
+ }
+
+ return total;
+}
+
+ssize_t __ffs_entry_write(ffs_t * self, const char *path, const void *buf,
+ off_t offset, size_t count)
+{
+ assert(self != NULL);
+ assert(path != NULL);
+ assert(buf != NULL);
+
+ if (count == 0)
+ return 0;
+
+ ffs_entry_t *entry = __find_entry(self->hdr, path);
+ if (entry == NULL) {
+ UNEXPECTED("entry '%s' not found in partition table at "
+ "offset '%llx'", path, self->offset);
+ return -1;
+ }
+
+ size_t entry_size = entry->size * self->hdr->block_size;
+ off_t entry_offset = entry->base * self->hdr->block_size;
+
+ if (entry_size <= offset)
+ return 0;
+ else
+ count = min(count, (entry_offset + entry_size) - offset);
+
+ ssize_t total = 0;
+
+ if (fseeko(self->file, entry_offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fseeko(self->file, offset, SEEK_CUR) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ while (0 < count) {
+ size_t rc = fwrite(buf + total, 1, count, self->file);
+ if (rc <= 0) {
+ if (ferror(self->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+ break;
+ }
+ total += rc;
+ count -= rc;
+ }
+
+ fflush(self->file);
+
+ if (entry->actual < (uint32_t) total) {
+ entry->actual = (uint32_t) total;
+ self->dirty = true;
+ }
+
+ return total;
+}
+
+#if 0
+ssize_t __ffs_entry_copy(ffs_t *self, ffs_t *in, const char *path)
+{
+ assert(self != NULL);
+ assert(in != NULL);
+ assert(path != NULL);
+
+ if (unlikely(*path == '\0'))
+ return 0;
+
+ ffs_entry_t *src = __find_entry(in->hdr, path);
+ if (src == NULL) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, in->offset);
+ return -1;
+ }
+
+ ffs_entry_t *dest = __find_entry(self->hdr, path);
+ if (dest == NULL) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, self->offset);
+ return -1;
+ }
+
+ if (src->base != dest->base) {
+ UNEXPECTED("partition '%s' offsets differ '%x' != '%x'",
+ path, src->base, dest->base);
+ return -1;
+ }
+
+ if (src->size != dest->size) {
+ UNEXPECTED("partition '%s' sizes differ '%x' != '%x'",
+ path, src->size, dest->size);
+ return -1;
+ }
+
+ size_t block_size = self->hdr->block_size;
+ off_t src_offset = src->base * in->hdr->block_size;
+ size_t src_actual = src->actual;
+ off_t dest_offset = dest->base * self->hdr->block_size;
+
+ if (fseeko(in->file, src_offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fseeko(self->file, dest_offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ssize_t total = 0;
+ char block[block_size];
+
+ while (0 < src_actual) {
+ size_t count = min(src_actual, block_size);
+
+ size_t rc = fread(block, 1, count, in->file);
+ if (rc <= 0 && ferror(in->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ rc = fwrite(block, 1, rc, self->file);
+ if (rc <= 0 && ferror(self->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ total += rc;
+ src_actual -= rc;
+ }
+
+ if (dest->actual != (uint32_t)total) {
+ dest->actual = (uint32_t) total;
+ self->dirty = true;
+ }
+
+ return total;
+}
+
+ssize_t __ffs_entry_compare(ffs_t * self, ffs_t * in, const char *path)
+{
+ assert(self != NULL);
+ assert(in != NULL);
+ assert(path != NULL);
+
+ if (unlikely(*path == '\0'))
+ return 0;
+
+ ffs_entry_t *src = __find_entry(in->hdr, path);
+ if (src == NULL) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, in->offset);
+ return -1;
+ }
+
+ ffs_entry_t *dest = __find_entry(self->hdr, path);
+ if (dest == NULL) {
+ UNEXPECTED("entry '%s' not found in table at offset '%llx'",
+ path, self->offset);
+ return -1;
+ }
+
+ if (src->base != dest->base) {
+ UNEXPECTED("partition '%s' offsets differ '%x' != '%x'",
+ path, src->base, dest->base);
+ return -1;
+ }
+
+ if (src->size != dest->size) {
+ UNEXPECTED("partition '%s' sizes differ '%x' != '%x'",
+ path, src->size, dest->size);
+ return -1;
+ }
+
+ if (src->actual != dest->actual) {
+ UNEXPECTED("partition '%s' actual sizes differ '%x' != '%x'",
+ path, src->actual, dest->actual);
+ return -1;
+ }
+
+ off_t offset = src->base * self->hdr->block_size;
+ size_t actual = src->actual;
+
+ if (fseeko(in->file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fseeko(self->file, offset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ssize_t total = 0;
+ size_t size = 256;
+ char __src[size], __dest[size];
+
+ while (0 < actual) {
+ size = min(actual, size);
+
+ size_t rc = fread(__src, 1, size, in->file);
+ if (rc <= 0 && ferror(in->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ rc = fread(__dest, 1, size, self->file);
+ if (rc <= 0 && ferror(self->file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (memcmp(__src, __dest, size) != 0) {
+ printf("==========> '%s'\n", self->path);
+ dump_memory(stdout, offset + total, __dest, size);
+
+ printf("==========> '%s'\n", in->path);
+ dump_memory(stdout, offset + total, __src, size);
+
+ break;
+ }
+
+ actual -= size;
+ total += size;
+ }
+
+ return total;
+}
+#endif
+
+int __ffs_entry_list(ffs_t * self, ffs_entry_t ** list)
+{
+ assert(self != NULL);
+ assert(list != NULL);
+
+ size_t size = 0, count = 0;
+ *list = NULL;
+
+ int name_list(ffs_entry_t * entry) {
+ if (size <= count) {
+ size += 10;
+
+ *list = realloc(*list, size * sizeof(**list));
+ if (*list == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ (*list)[count++] = *entry;
+
+ return 0;
+ }
+
+ if (__ffs_iterate_entries(self, name_list) != 0) {
+ if (*list != NULL)
+ free(*list), *list = NULL;
+ count = 0;
+ return -1;
+ }
+
+ return count;
+}
+
+/* ============================================================ */
diff --git a/ffs/src/libffs2.c b/ffs/src/libffs2.c
new file mode 100644
index 0000000..0c83cf0
--- /dev/null
+++ b/ffs/src/libffs2.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: libffs2.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: FFS IO interface
+ * Note:
+ * Date: 05/07/12
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <endian.h>
+#include <libgen.h>
+
+#include "libffs2.h"
+
+#include <clib/assert.h>
+#include <clib/builtin.h>
+#include <clib/checksum.h>
+#include <clib/misc.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#define FFS_ERRSTR_MAX 1024
+
+struct ffs_error {
+ int errnum;
+ char errstr[FFS_ERRSTR_MAX];
+};
+
+typedef struct ffs_error ffs_error_t;
+
+static ffs_error_t __error;
+
+/* ============================================================ */
+
+extern void ffs_errclr(void)
+{
+ __error.errnum = __error.errstr[0] = 0;
+}
+
+extern int ffs_errnum(void)
+{
+ return __error.errnum;
+}
+
+extern const char *ffs_errstr(void)
+{
+ return __error.errnum ? __error.errstr : NULL;
+}
+
+int ffs_check(const char *path, off_t offset)
+{
+ RAII(FILE*, file, fopen(path, "r"), fclose);
+ if (file == NULL) {
+ __error.errnum = -1;
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : %s (errno=%d)\n",
+ program_invocation_short_name, "errno",
+ __FILE__, __LINE__, strerror(errno), errno);
+
+ return -1;
+ }
+
+ int rc = __ffs_fcheck(file, offset);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ // __ffs_check will return FFS_CHECK_* const's
+ }
+
+ return rc;
+}
+
+ffs_t *ffs_create(const char *path, off_t offset, uint32_t block_size,
+ uint32_t block_count)
+{
+ ffs_t *self = __ffs_create(path, offset, block_size, block_count);
+ if (self == NULL) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+ }
+
+ return self;
+}
+
+ffs_t *ffs_open(const char *path, off_t offset)
+{
+ FILE * file = fopen(path, "r+");
+ if (file == NULL) {
+ __error.errnum = -1;
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : %s (errno=%d)\n",
+ program_invocation_short_name, "errno",
+ __FILE__, __LINE__, strerror(errno), errno);
+
+ return NULL;
+ }
+
+ ffs_t *self = __ffs_fopen(file, offset);
+ if (self == NULL) {
+ fclose(file);
+
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+ }
+
+ return self;
+}
+
+int ffs_info(ffs_t *self, int name, uint32_t *value)
+{
+ int rc = __ffs_info(self, name, value);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_close(ffs_t * self)
+{
+ int rc = __ffs_close(self);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_fsync(ffs_t * self)
+{
+ int rc = __ffs_fsync(self);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_list_entries(ffs_t * self, FILE * out)
+{
+ int rc = __ffs_list_entries(self, ".*", true, out);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *))
+{
+ int rc = __ffs_iterate_entries(self, func);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_find(ffs_t * self, const char *path, ffs_entry_t * entry)
+{
+ int rc = __ffs_entry_find(self, path, entry);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_find_parent(ffs_t * self, const char *path, ffs_entry_t * entry)
+{
+ int rc = __ffs_entry_find_parent(self, path, entry);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size,
+ ffs_type_t type, uint32_t flags)
+{
+ int rc = __ffs_entry_add(self, path, offset, size, type, flags);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_delete(ffs_t * self, const char *path)
+{
+ int rc = __ffs_entry_delete(self, path);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_user_get(ffs_t * self, const char *path, uint32_t word,
+ uint32_t * value)
+{
+ int rc = __ffs_entry_user_get(self, path, word, value);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int ffs_entry_user_put(ffs_t * self, const char *path, uint32_t word,
+ uint32_t value)
+{
+ int rc = __ffs_entry_user_put(self, path, word, value);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+ssize_t ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out)
+{
+ ssize_t rc = __ffs_entry_hexdump(self, path, out);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+ssize_t ffs_entry_truncate(ffs_t * self, const char *path, off_t offset,
+ uint8_t pad __unused__)
+{
+ return ffs_entry_truncate_no_pad(self, path, offset);
+}
+
+ssize_t ffs_entry_truncate_no_pad(ffs_t * self, const char *path, off_t offset)
+{
+ ssize_t rc = __ffs_entry_truncate(self, path, offset);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+ssize_t ffs_entry_read(ffs_t * self, const char *path, void *buf, off_t offset,
+ size_t count)
+{
+ ssize_t rc = __ffs_entry_read(self, path, buf, offset, count);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+ssize_t ffs_entry_write(ffs_t * self, const char *path, const void *buf,
+ off_t offset, size_t count)
+{
+ ssize_t rc = __ffs_entry_write(self, path, buf, offset, count);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+ssize_t ffs_entry_list(ffs_t * self, ffs_entry_t ** list)
+{
+ ssize_t rc = __ffs_entry_list(self, list);
+ if (rc < 0) {
+ err_t *err = err_get();
+ assert(err != NULL);
+
+ __error.errnum = err_code(err);
+ snprintf(__error.errstr, sizeof __error.errstr,
+ "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+/* ============================================================ */
diff --git a/ffs/test/Makefile b/ffs/test/Makefile
new file mode 100644
index 0000000..37a4d0d
--- /dev/null
+++ b/ffs/test/Makefile
@@ -0,0 +1,8 @@
+DEPTH = ../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.ppc.mk
+
+ARCH=ppc
+TEST_LIBFFS_INSTALL = $(INST_TESTS)
+
+include ../Rules.test.mk
diff --git a/ffs/test/ffs_tool_test.sh b/ffs/test/ffs_tool_test.sh
new file mode 100755
index 0000000..37f5fe0
--- /dev/null
+++ b/ffs/test/ffs_tool_test.sh
@@ -0,0 +1,244 @@
+#!/bin/bash
+# ffs_tool_test.sh
+#
+# Test case to perform tests for supported options in ffs tool
+#
+# Author: Shekar Babu <shekbabu@in.ibm.com>
+#
+
+FFS_TOOL="ffs"
+NOR_IMAGE="/tmp/pnor"
+OFFSET="0x3F0000"
+SIZE="8MiB"
+BLOCK="64KiB"
+LOGICAL="logical"
+DATA="data"
+PAD="0xFF"
+
+create_dummy_file() {
+ echo Creating a dummy file $1 of size $2 with sample data $3
+ yes $3 | head -$2 > $1
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, creating dummy file $1
+ exit $RC
+ fi
+ echo Success, creating $1
+}
+
+create_nor_image() {
+
+ # Check if nor image already exist
+ if [ -f $1 ];then
+ rm $1
+ fi
+ echo Creating nor image $1
+ $FFS_TOOL --create $1 -p $2 -s $3 -b $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, creating $1 image
+ exit $RC
+ fi
+ echo Success, creating $1 image
+}
+
+add_logical_partition() {
+ echo Adding logical partition $3
+ echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4
+ $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, adding $4 partition $3
+ exit $RC
+ fi
+ echo Success, adding $4 partition $3
+}
+
+add_data_partition() {
+ echo Adding data partition $3
+ echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4
+ $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, adding $4 partition $3
+ exit $RC
+ fi
+ echo Success, adding $4 partition $3
+}
+
+read_partition_entry() {
+ echo Reading partition entry $3
+ echo $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force
+ $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, reading partition entry $3
+ exit $RC
+ fi
+ echo Success, reading partition entry $3
+}
+
+write_partition_entry() {
+ echo Writing to partition entry $3
+ echo $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force
+ $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, writing to partition entry $3
+ exit $RC
+ fi
+ echo Success, writing to partition entry $3
+}
+
+list_partition_table_entries() {
+ echo Listing partition table entries in $1
+ echo $FFS_TOOL --list $1 -O $2
+ $FFS_TOOL --list $1 -O $2
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Listing partition table entries in $1
+ exit $RC
+ fi
+ echo Success, Listing partition table entries in $1
+}
+hexdump_partition_entry() {
+ echo Hexdump partition entry $3 into $4
+ echo "$FFS_TOOL --hexdump $1 -O $2 --name $3 > $4"
+ $FFS_TOOL --hexdump $1 -O $2 --name $3 > $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, hexdump partition entry $3 into $4
+ exit $RC
+ fi
+ echo Success, hexdump partition entry $3 into $4
+}
+
+delete_partition_entry() {
+ echo Delete partition entry $3
+ echo $FFS_TOOL --delete $1 -O $2 --name $3
+ $FFS_TOOL --delete $1 -O $2 --name $3
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, deleting partition entry $3
+ exit $RC
+ fi
+ echo Success, deleting partition entry $3
+}
+
+get_partition_entry_user_word() {
+ echo Get user word from partition entry $3
+ echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4
+ $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 > /tmp/GETUW
+ sed 's/^\(.\)\{7\}//g' /tmp/GETUW > /tmp/chop_GETUW
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Getting user word from partition entry $3
+ exit $RC
+ fi
+ echo Success, Getting user word from partition entry $3
+}
+
+put_partition_entry_user_word() {
+ echo Put user word to partition entry $3
+ echo $5 > /tmp/PUTUW
+ echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5
+ $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Putting user word to partition entry $3
+ exit $RC
+ fi
+ echo Success, Putting user word to partition entry $3
+}
+
+read_write_part_entry() {
+ write_partition_entry $1 $2 $3 $4
+ read_partition_entry $1 $2 $3 $5
+ cmp $4 $5 > /dev/null
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo FAIL, data read/write mismatch -- entry $3
+ exit $RC
+ fi
+ echo PASS, data read/write matches -- entry $3
+}
+
+get_put_user_word() {
+ put_partition_entry_user_word $1 $2 $3 $4 $5
+ get_partition_entry_user_word $1 $2 $3 $4
+ cmp /tmp/chop_GETUW /tmp/PUTUW > /dev/null
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo FAIL, user word get/put mismatch -- entry $3
+ exit $RC
+ fi
+ echo PASS, user word get/put matches -- entry $3
+ rm /tmp/GETUW /tmp/PUTUW /tmp/chop_GETUW
+}
+
+compare_hexdump() {
+ hexdump_partition_entry $1 $2 $3 $4
+ HEXFILE=/tmp/hex_sz0
+ stat -c %s $4 > $HEXFILE
+ if [[ -s $HEXFILE ]] ; then
+ echo PASS, hexdump test on entry $3
+ else
+ echo FAIL, hexdump test on entry $3
+ exit $RC
+ fi
+ rm $4 $HEXFILE
+}
+
+clean_data() {
+ rm $NOR_IMAGE /tmp/in_file /tmp/out_file
+ exit 0
+}
+
+# Main program starts
+
+# Create a dummy file as 'filename size data'
+create_dummy_file /tmp/in_file 131072 WELCOME
+
+# Create nor image
+create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK
+
+# Add logical partition
+add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL
+
+# Creating data partition
+add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1MiB 0M
+add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1MiB 2M
+add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 960K 3M
+# Add logical partition
+add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL
+# Creating data partition
+add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1MiB 4M
+add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1MiB 6M
+add_data_partition $NOR_IMAGE $OFFSET boot1/bootfsp $DATA 960K 7M
+# Listing all created partition entries (logical+data)
+list_partition_table_entries $NOR_IMAGE $OFFSET
+
+# Perform read and write operations on all partition entries
+read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file
+
+# Perform get and put user words on all partition entries
+get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 0x0000000a
+get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 0x0000000b
+get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 0x0000000c
+get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 0x0000000d
+get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 0x0000000f
+
+# Hexdump partition entry
+compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump
+
+# Delete a partition entry
+delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv
+
+# Listing all created partition entries (logical+data)
+list_partition_table_entries $NOR_IMAGE $OFFSET
+
+# Clean/remove all temporary files
+clean_data
diff --git a/ffs/test/libffs_test.sh b/ffs/test/libffs_test.sh
new file mode 100755
index 0000000..a7e3adc
--- /dev/null
+++ b/ffs/test/libffs_test.sh
@@ -0,0 +1,248 @@
+#!/bin/bash
+# libffs_test.sh
+#
+# Test case to perform unit tests for all api's in libffs.so
+#
+# Author: Shekar Babu <shekbabu@in.ibm.com>
+#
+
+
+NOR_IMAGE=/tmp/sunray2.nor
+OFFSET=0
+#OFFSET=4128768
+SIZE=8388608
+#SIZE=67108864 #For 64MB nor
+BLOCK=65536
+LOGICAL=logical
+DATA=data
+
+create_dummy_file() {
+ echo Creating a dummy file $1 of size $2 with sample data $3
+ yes $3 | head -$2 > $1
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, creating dummy file $1
+ exit $RC
+ fi
+ echo Success, creating $1
+}
+
+create_nor_image() {
+ echo Creating nor image $1
+ echo test_libffs -c $1 -O $2 -s $3 -b $4
+ ./test_libffs -c $1 -O $2 -s $3 -b $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, creating $1 image
+ exit $RC
+ fi
+ echo Success, creating $1 image
+}
+
+add_logical_partition() {
+ echo Adding logical partition $3
+ echo test_libffs -a $1 -O $2 -n $3 -t $4
+ ./test_libffs -a $1 -O $2 -n $3 -t $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, adding $4 partition $3
+ exit $RC
+ fi
+ echo Success, adding $4 partition $3
+}
+
+add_data_partition() {
+ echo Adding data partition $3
+ echo test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6
+ ./test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, adding $4 partition $3
+ exit $RC
+ fi
+ echo Success, adding $4 partition $3
+}
+
+read_partition_entry() {
+ echo Reading partition entry $3
+ echo test_libffs -r $1 -O $2 -n $3 -o $4
+ ./test_libffs -r $1 -O $2 -n $3 -o $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, reading partition entry $3
+ exit $RC
+ fi
+ echo Success, reading partition entry $3
+}
+
+write_partition_entry() {
+ echo Writing to partition entry $3
+ echo test_libffs -w $1 -O $2 -n $3 -i $4
+ ./test_libffs -w $1 -O $2 -n $3 -i $4 > /dev/null
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, writing to partition entry $3
+ exit $RC
+ fi
+ echo Success, writing to partition entry $3
+}
+
+list_partition_table_entries() {
+ echo Listing partition table entries in $1
+ echo test_libffs -l $1 -O $2
+ ./test_libffs -l $1 -O $2
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Listing partition table entries in $1
+ exit $RC
+ fi
+ echo Success, Listing partition table entries in $1
+}
+hexdump_partition_entry() {
+ echo Hexdump partition entry $3 into $4
+ echo "test_libffs -h $1 -O $2 -n $3 > $4"
+ ./test_libffs -h $1 -O $2 -n $3 > $4
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, hexdump partition entry $3 into $4
+ exit $RC
+ fi
+ echo Success, hexdump partition entry $3 into $4
+}
+
+delete_partition_entry() {
+ echo Delete partition entry $3
+ echo test_libffs -d $1 -O $2 -n $3
+ ./test_libffs -d $1 -O $2 -n $3
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, deleting partition entry $3
+ exit $RC
+ fi
+ echo Success, deleting partition entry $3
+}
+
+get_partition_entry_user_word() {
+ echo Get user word from partition entry $3
+ echo test_libffs -m $1 -O $2 -n $3 -u $4 -g
+ ./test_libffs -m $1 -O $2 -n $3 -u $4 -g > /tmp/GETUW
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Getting user word from partition entry $3
+ exit $RC
+ fi
+ echo Success, Getting user word from partition entry $3
+}
+
+put_partition_entry_user_word() {
+ echo Put user word to partition entry $3
+ echo test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5
+ ./test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5 > /tmp/PUTUW
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo Error, Putting user word to partition entry $3
+ exit $RC
+ fi
+ echo Success, Putting user word to partition entry $3
+}
+
+read_write_part_entry() {
+ write_partition_entry $1 $2 $3 $4
+ read_partition_entry $1 $2 $3 $5
+ cmp $4 $5 > /dev/null
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo FAIL, data read/write mismatch -- entry $3
+ exit $RC
+ fi
+ echo PASS, data read/write matches -- entry $3
+}
+
+get_put_user_word() {
+ put_partition_entry_user_word $1 $2 $3 $4 $5
+ get_partition_entry_user_word $1 $2 $3 $4
+ cmp /tmp/GETUW /tmp/PUTUW > /dev/null
+ RC=$?
+ if [ $RC -ne 0 ]; then
+ echo FAIL, user word get/put mismatch -- entry $3
+ exit $RC
+ fi
+ echo PASS, user word get/put matches -- entry $3
+ rm /tmp/GETUW /tmp/PUTUW
+}
+
+compare_hexdump() {
+ hexdump_partition_entry $1 $2 $3 $4
+ HEXFILE=/tmp/hex_sz0
+ stat -c %s $4 > $HEXFILE
+ if [[ -s $HEXFILE ]] ; then
+ echo PASS, hexdump test on entry $3
+ else
+ echo FAIL, hexdump test on entry $3
+ exit $RC
+ fi
+ rm $4 $HEXFILE
+}
+
+clean_data() {
+ rm $NOR_IMAGE /tmp/in_file /tmp/out_file
+ exit 0
+}
+
+# Main program starts
+
+# Create a dummy file as 'filename size data'
+create_dummy_file /tmp/in_file 131072 WELCOME
+
+# Create nor image
+create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK
+
+# Add logical partition
+add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL
+# Creating data partition
+add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1048576 65536
+add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1048576 2097152
+add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 1048576 3145728
+# Add logical partition
+add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL
+# Creating data partition
+add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1048576 4194304
+add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1048576 5242880
+# Add logical partition
+add_logical_partition $NOR_IMAGE $OFFSET linux0 $LOGICAL
+# Creating data partition
+add_data_partition $NOR_IMAGE $OFFSET linux0/vpd $DATA 1048576 6291456
+add_data_partition $NOR_IMAGE $OFFSET linux0/hostboot $DATA 1048576 7340032
+
+# Listing all created partition entries (logical+data)
+list_partition_table_entries $NOR_IMAGE $OFFSET
+
+# Perform read and write operations on all partition entries
+read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot0/spl /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET linux0/vpd /tmp/in_file /tmp/out_file
+read_write_part_entry $NOR_IMAGE $OFFSET linux0/hostboot /tmp/in_file /tmp/out_file
+
+# Perform get and put user words on all partition entries
+get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 28
+get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 56
+get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 96
+get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 16
+get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 84
+get_put_user_word $NOR_IMAGE $OFFSET linux0/vpd 8 64
+get_put_user_word $NOR_IMAGE $OFFSET linux0/hostboot 15 42
+
+# Hexdump partition entry
+compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump
+
+# Delete a partition entry
+delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv
+
+# Listing all created partition entries (logical+data)
+list_partition_table_entries $NOR_IMAGE $OFFSET
+
+# Clean/remove all temporary files
+clean_data
diff --git a/ffs/test/test_libffs.c b/ffs/test/test_libffs.c
new file mode 100644
index 0000000..f8c7966
--- /dev/null
+++ b/ffs/test/test_libffs.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: test_libffs.c
+ * Author: Shekar Babu S <shekbabu@in.ibm.com>
+ * Descr: unit test tool for api's in libffs.so
+ * Date: 06/26/2012
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <clib/exception.h>
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/min.h>
+#include <sys/xattr.h>
+
+#include <clib/bb_trace.h>
+
+#include "test_libffs.h"
+
+FILE*
+log_open(void) {
+
+ FILE *logfile = NULL;
+ logfile = fopen("test_libffs.log", "w");
+ if (logfile == NULL) {
+ perror("logfile");
+ exit(EXIT_FAILURE);
+ }
+ setvbuf(logfile, NULL, _IOLBF, 0);
+ return logfile;
+}
+
+void
+create_regular_file(ffs_ops_t *ffs_ops) {
+ int rc = 0;
+ size_t size = ffs_ops->device_size;
+ mode_t mode = (S_IRUSR | S_IWUSR) | (S_IRGRP | S_IRGRP) | S_IROTH;
+
+ fprintf(ffs_ops->log, "%s: Creating regular file\n", __func__);
+
+ int fd = open(ffs_ops->nor_image, O_RDWR | O_CREAT | O_TRUNC, mode);
+ if (fd == -1) {
+ fprintf(ffs_ops->log, "%s: Error creating regular file '%s'",
+ __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ return;
+ }
+
+ if (ftruncate(fd, size) != 0) {
+ fprintf(ffs_ops->log, "%s: Error truncating '%s'",
+ __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ return;
+ }
+
+ uint32_t page_size = sysconf(_SC_PAGESIZE);
+ char buf[page_size];
+ memset(buf, 0xFF, page_size);
+
+ while (0 < size) {
+ ssize_t rc = write(fd, buf, min(sizeof(buf), size));
+ if (rc == -1) {
+ fprintf(ffs_ops->log, "%s: Error writing to '%s'",
+ __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ return;
+ } else if(rc == 0) {
+ break;
+ }
+ size -= rc;
+ }
+
+ if(fd == 0) {
+ close(fd);
+ }
+}
+
+int
+create_partition(ffs_ops_t *ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t *ffs = NULL;
+
+ create_regular_file(ffs_ops);
+
+ ffs = __ffs_create(ffs_ops->nor_image, ffs_ops->part_off,
+ ffs_ops->blk_sz,
+ ffs_ops->device_size / ffs_ops->blk_sz);
+
+ if(ffs != NULL) {
+ __ffs_close(ffs);
+ fprintf(ffs_ops->log, "%s: Creating pnor image '%s' success\n",
+ __func__, ffs_ops->nor_image);
+ } else {
+ fprintf(ffs_ops->log, "%s: Creating pnor image '%s' failed\n",
+ __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ }
+
+ return rc;
+}
+
+int
+add_partition_entry(ffs_ops_t *ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t *ffs = NULL;
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs != NULL) {
+ __ffs_entry_add(ffs, ffs_ops->part_entry, ffs_ops->entry_off,
+ ffs_ops->entry_sz, ffs_ops->type, 0x00);
+ __ffs_close(ffs);
+ fprintf(ffs_ops->log, "%s: Adding partition entry '%s' "
+ "success\n", __func__, ffs_ops->part_entry);
+ } else {
+ fprintf(ffs_ops->log, "%s: Adding partition entry '%s' "
+ "failed\n", __func__, ffs_ops->part_entry);
+ }
+ return rc;
+}
+
+uint32_t
+read_partition(ffs_ops_t *ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t *ffs = NULL;
+
+ FILE *fp = fopen(ffs_ops->o_file, "w+");
+ if (fp == NULL) {
+ fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__,
+ ffs_ops->o_file);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ goto error;
+ }
+ fprintf(ffs_ops->log, "%s: Successfully opened "
+ "nor image\n", __func__);
+ ffs_entry_t entry;
+
+ if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) {
+ fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__,
+ ffs_ops->part_entry);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ if(false) {
+error:
+ if(fp != NULL)
+ fclose(fp);
+ if(ffs != NULL)
+ __ffs_close(ffs);
+ return rc;
+ }
+
+ fprintf(ffs_ops->log, "%s: Finding entry '%s' success\n", __func__,
+ ffs_ops->part_entry);
+
+ uint32_t block_size = ffs->hdr->block_size;
+ char block[block_size];
+ memset(block, 0, block_size);
+
+ if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) {
+ fprintf(ffs_ops->log, "%s: Error, setvbuf failed "
+ "%s (errno=%d)", __func__, strerror(errno), errno);
+ rc = FFS_ERROR;
+ goto out;
+ }
+
+ for (uint32_t i=0; i<entry.size; i++) {
+ size_t rc = __ffs_entry_read(ffs, ffs_ops->part_entry, block,
+ i * block_size, block_size);
+ if(rc != block_size) {
+ fprintf(ffs_ops->log, "%s: Error, ffs_entry_read"
+ " '%s'\n", __func__, ffs_ops->part_entry);
+ rc = FFS_ERROR;
+ goto out;
+ }
+
+ rc = fwrite(block, rc, 1, fp);
+ if (rc != 1) {
+ fprintf(ffs_ops->log, "%s: Error, fwrite "
+ "%s (errno=%d)\n", __func__,
+ strerror(ferror(fp)), ferror(fp));
+ rc = FFS_ERROR;
+ goto out;
+ }
+ else if (rc == 0)
+ break;
+ }
+
+ uint32_t bytes_read = entry.size * block_size;
+
+ fprintf(ffs_ops->log, "%s: Read %d bytes from partition '%s' Success\n",
+ __func__, bytes_read, ffs_ops->part_entry);
+
+ if (fclose(fp) == EOF) {
+ fprintf(ffs_ops->log, "%s: Error, flose '%s' "
+ "=> %s (errno=%d)", __func__, ffs_ops->o_file,
+ strerror(errno), errno);
+ fp = NULL;
+ rc = FFS_ERROR;
+ goto out;
+ }
+
+ __ffs_close(ffs);
+
+ fprintf(ffs_ops->log, "%s: Writing %d bytes from partition '%s' to "
+ "file '%s' Success\n", __func__, bytes_read,
+ ffs_ops->part_entry, ffs_ops->o_file);
+
+ if(false) {
+out:
+ if(fp != NULL)
+ fclose(fp);
+ if(ffs != NULL)
+ __ffs_close(ffs);
+ }
+ return rc;
+}
+
+uint32_t
+write_partition(ffs_ops_t * ffs_ops) {
+
+ struct stat st;
+ uint32_t rc = 0;
+ ffs_t * ffs = NULL;
+
+
+ if (stat(ffs_ops->i_file, &st) != 0) {
+ fprintf(ffs_ops->log, "%s: '%s' => %s (errno=%d)", __func__,
+ ffs_ops->i_file, strerror(errno), errno);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ fprintf(ffs_ops->log, "%s: Successfully opened nor image '%s' for "
+ "writing\n", __func__, ffs_ops->nor_image);
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) {
+ fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__,
+ ffs_ops->part_entry);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ size_t entry_size = entry.size * ffs->hdr->block_size;
+
+
+ fprintf(ffs_ops->log, "%s: Found entry '%s' with size=%d\n", __func__,
+ ffs_ops->part_entry, entry_size);
+
+ if (entry_size < st.st_size) {
+ fprintf(ffs_ops->log, "%s: '%s' of size '%lld' too big for "
+ "partition '%s' of size '%d'\n", __func__,
+ ffs_ops->i_file, st.st_size, ffs_ops->nor_image,
+ entry_size);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ FILE * fp = fopen(ffs_ops->i_file, "r+");
+ if (fp == NULL) {
+ fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__,
+ ffs_ops->i_file);
+ rc = FFS_ERROR;
+ goto error;
+ }
+
+ if(false) {
+error:
+ if(ffs != NULL)
+ __ffs_close(ffs);
+ return rc;
+ }
+
+ uint32_t block_size = ffs->hdr->block_size;
+ char block[block_size];
+
+ if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) {
+ fprintf(ffs_ops->log, "%s: Error, setvbuf failed "
+ "%s (errno=%d)", __func__, strerror(errno), errno);
+ rc = FFS_ERROR;
+ goto out;
+ }
+
+ fprintf(ffs_ops->log, "%s: Writing data file into partition\n",
+ __func__);
+
+ for (uint32_t j=0; j<entry.size; j++) {
+ clearerr(fp);
+
+ size_t bytes_read = fread(block, 1, block_size, fp);
+ __ffs_entry_write(ffs, ffs_ops->part_entry, block,
+ j * block_size, bytes_read);
+ if (bytes_read == 0) {
+ int err = ferror(fp);
+ if (err) {
+ fprintf(ffs_ops->log, "%s: Error, setvbuf "
+ "failed %s (errno=%d)", __func__,
+ strerror(errno), errno);
+ rc = FFS_ERROR;
+ goto out;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ if (fclose(fp) == EOF) {
+ fprintf(ffs_ops->log, "%s: Error, flose '%s' "
+ "=> %s (errno=%d)", __func__, ffs_ops->o_file,
+ strerror(errno), errno);
+ fp = NULL;
+ rc = FFS_ERROR;
+ goto out;
+ }
+ fprintf(ffs_ops->log, "%s: Writing to partition '%s' from data file "
+ "'%s' Success\n", __func__, ffs_ops->part_entry,
+ ffs_ops->i_file);
+
+ if(false) {
+out:
+ __ffs_close(ffs);
+ }
+ return rc;
+}
+
+uint32_t
+list_partition(ffs_ops_t * ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t * ffs = NULL;
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ fprintf(ffs_ops->log, "%s: Listing partition entries in '%s'"
+ " image failed\n", __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ return rc;
+ }
+ //!< List all entries in the partition table
+ __ffs_list_entries(ffs, ".*", true, stdout);
+ fprintf(ffs_ops->log, "%s: Listing partition entries in '%s' image "
+ "success\n", __func__, ffs_ops->nor_image);
+
+ __ffs_close(ffs);
+
+ return rc;
+}
+
+uint32_t
+hexdump_entry(ffs_ops_t * ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t * ffs = NULL;
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s'"
+ " image failed\n", __func__, ffs_ops->nor_image);
+ rc = FFS_ERROR;
+ return rc;
+ }
+ //!< Hexdump the entry in the partition table
+ __ffs_entry_hexdump(ffs, ffs_ops->part_entry, stdout);
+ fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s' image "
+ "success\n", __func__, ffs_ops->nor_image);
+ __ffs_close(ffs);
+
+ return rc;
+}
+
+uint32_t
+delete_entry(ffs_ops_t * ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t * ffs = NULL;
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ fprintf(ffs_ops->log, "%s: Delete entry '%s' failed\n",
+ __func__, ffs_ops->part_entry);
+ rc = FFS_ERROR;
+ return rc;
+ }
+ //!< Delete the entry from the partition table
+ __ffs_entry_delete(ffs, ffs_ops->part_entry);
+ fprintf(ffs_ops->log, "%s: Delete entry '%s' success\n", __func__,
+ ffs_ops->part_entry);
+
+ __ffs_close(ffs);
+
+ return rc;
+}
+
+uint32_t
+modify_entry_get(ffs_ops_t * ffs_ops) {
+
+ uint32_t rc = 0;
+ uint32_t value = 0;
+ ffs_t * ffs = NULL;
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ fprintf(ffs_ops->log, "%s: Get user word at '%d' failed\n",
+ __func__, ffs_ops->user);
+ rc = FFS_ERROR;
+ return rc;
+ }
+ //!< Get the user word at index
+ __ffs_entry_user_get(ffs, ffs_ops->part_entry, ffs_ops->user, &value);
+ fprintf(ffs_ops->log, "%s: Get user word at '%d' --> %x success\n",
+ __func__, ffs_ops->user, value);
+ //!< Required to check what is put
+ fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, value);
+
+ __ffs_close(ffs);
+
+ return rc;
+}
+
+uint32_t
+modify_entry_put(ffs_ops_t * ffs_ops) {
+
+ uint32_t rc = 0;
+ ffs_t * ffs = NULL;
+
+ ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off);
+ if(ffs == NULL) {
+ fprintf(ffs_ops->log, "%s: Error, opening nor "
+ "image '%s'\n", __func__, ffs_ops->nor_image);
+ fprintf(ffs_ops->log, "%s: Put user word at '%d' failed\n",
+ __func__, ffs_ops->user);
+ rc = FFS_ERROR;
+ return rc;
+ }
+ //!< Put the user word at index
+ __ffs_entry_user_put(ffs, ffs_ops->part_entry, ffs_ops->user,
+ ffs_ops->value);
+ fprintf(ffs_ops->log, "%s: Put user word at '%d' --> %d success\n",
+ __func__, ffs_ops->user, ffs_ops->value);
+ //!< Required to check what is get
+ fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, ffs_ops->value);
+
+ __ffs_close(ffs);
+
+ return rc;
+}
+
+void
+usage(void) {
+ printf("This program is a unit test tool, its callers responsibility "
+ "to pass the correct parameters.\nNote: No usage errors are "
+ "displayed, any mistake in params may result in unexpected "
+ "results\n");
+}
+
+int
+main(int argc, char * argv[]) {
+
+ int32_t rc = 0;
+ ffs_ops_t ffs_ops;
+
+ memset(&ffs_ops, 0, sizeof(ffs_ops_t));
+ ffs_ops.part_off = PART_OFFSET;
+ ffs_ops.log = log_open();
+
+ while ((argc > 1) && (argv[1][0] == '-'))
+ {
+ switch (argv[1][1])
+ {
+
+//test_libffs -c pnor -O part_offset -s dev_size -b block size
+//test_libffs -c pnor -O 4128768 -s 67108864 -b 65536
+ case 'c':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.device_size = atoi(argv[6]);
+ ffs_ops.blk_sz = atoi(argv[8]);
+ rc = create_partition(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -a pnor -O part_offset -n part_name -t part_type
+//test_libffs -a sunray.pnor -O 4128768 -n boot0 -t logical
+//test_libffs -a pnor -O part_off -n part_name -t type -s size -o entry_off
+//test_libffs -a sunray.pnor -O 4128768 -n boot0/bootenv -t data -s 1048576 -o 0
+ case 'a':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6];
+ if (!strcasecmp(argv[8], "logical")) {
+ ffs_ops.type = FFS_TYPE_LOGICAL;
+ ffs_ops.entry_sz = 0;
+ ffs_ops.entry_off = 0;
+ }
+ else if (!strcasecmp(argv[8], "data")) {
+ ffs_ops.type = FFS_TYPE_DATA;
+ ffs_ops.entry_sz = atol(argv[10]);
+ ffs_ops.entry_off = atoll(argv[12]);
+ }
+ rc = add_partition_entry(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -r pnor -O part_off -n part_name -o out_file
+//test_libffs -r sunray.pnor -O 4128768 -n boot0/bootenv -o out_file
+ case 'r':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6]; //!< part entry
+ ffs_ops.o_file = argv[8]; //!< out put file
+ ffs_ops.i_file = NULL;
+ rc = read_partition(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -w pnor -O part_off -n part_name -i in_file
+//test_libffs -w sunray.pnor -O 4128768 -n boot0/bootenv -i in_file
+ case 'w':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6]; //!< part entry
+ ffs_ops.i_file = argv[8]; //!< out put file
+ ffs_ops.o_file = NULL;
+ rc = write_partition(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+//test_libffs -l pnor -O part_off
+//test_libffs -l sunray.pnor -O 4128768
+ case 'l':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ rc = list_partition(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -h pnor -O part_off -n part_entry
+//test_libffs -h sunray.pnor -O 4128768 -n boot0/bootenv
+ case 'h':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6]; //!< part entry
+ rc = hexdump_entry(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -d pnor -O part_off -n part_entry
+//test_libffs -d sunray.pnor -O 4128768 -n boot0/bootenv
+ case 'd':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6]; //!< part entry
+ rc = delete_entry(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ break;
+//test_libffs -m pnor -O part_off -n part_name -u index -g
+//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -g
+//test_libffs -m pnor -O part_off -n part_name -u index -p -v some_value
+//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -p -v 1024
+ case 'm':
+ ffs_ops.nor_image = argv[2]; //!< nor image
+ ffs_ops.part_off = atoll(argv[4]);
+ ffs_ops.part_entry = argv[6]; //!< part entry
+ ffs_ops.user = atol(argv[8]);
+ if(!strcmp(argv[9], "-g")) {
+ rc = modify_entry_get(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ } else if(!strcmp(argv[9], "-p")) {
+ ffs_ops.value = atol(argv[11]);
+ rc = modify_entry_put(&ffs_ops);
+ if(rc == FFS_ERROR) {
+ goto out;
+ }
+ }
+ break;
+ default:
+ usage();
+ break;
+ }
+ break;
+ }
+
+out:
+ if(ffs_ops.log != NULL) {
+ fclose(ffs_ops.log);
+ }
+ return rc;
+}
diff --git a/ffs/test/test_libffs.h b/ffs/test/test_libffs.h
new file mode 100644
index 0000000..c9ec613
--- /dev/null
+++ b/ffs/test/test_libffs.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: test_libffs.c
+ * Author: Shekar Babu S <shekbabu@in.ibm.com>
+ * Descr: unit test tool for api's in libffs.so
+ * Date: 06/26/2012
+ */
+
+#include "libffs.h"
+
+#define FFS_ERROR -1
+#define PART_OFFSET 0x3F0000
+
+typedef struct ffs_operations{
+ const char * nor_image; //!< Path to nor image special/regular file
+ const char * part_entry; //!< Logical partition/entry name
+ const char * i_file; //!< Input file
+ const char * o_file; //!< Output file
+ FILE * log; //!< Log file
+ size_t device_size;//!< Size of the nor flash
+ off_t part_off; //!< Offset of partition table
+ size_t blk_sz; //!< Block size
+ size_t entry_sz; //!< Partition entry size
+ off_t entry_off; //!< Offset of partition entry
+ uint32_t user; //!< Index to user word in any entry
+ uint32_t value; //!< User word at index in entry
+ ffs_type_t type; //!< Partition type
+} ffs_ops_t;
diff --git a/ffs/x86/Makefile b/ffs/x86/Makefile
new file mode 100644
index 0000000..af907da
--- /dev/null
+++ b/ffs/x86/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ..
+
+ARCH=x86
+FFS_INSTALL = $(INST_USR_X86)
+FFS_INSTALL_TEST = $(INST_TESTS_X86)
+
+include ../Rules.mk
diff --git a/fpart/.gitignore b/fpart/.gitignore
new file mode 100644
index 0000000..d09ad9f
--- /dev/null
+++ b/fpart/.gitignore
@@ -0,0 +1 @@
+*/fpart
diff --git a/fpart/Makefile b/fpart/Makefile
new file mode 100644
index 0000000..e08925f
--- /dev/null
+++ b/fpart/Makefile
@@ -0,0 +1,10 @@
+SUBDIRS=x86
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+$(SUBDIRS)::
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+all clean install: $(SUBDIRS)
diff --git a/fpart/Rules.mk b/fpart/Rules.mk
new file mode 100644
index 0000000..c5aa849
--- /dev/null
+++ b/fpart/Rules.mk
@@ -0,0 +1,30 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64
+CFLAGS += -I$(DEPTH)/.. -iquote..
+
+LDFLAGS += -L. -m32
+
+NAME=fpart
+
+CLIB=$(DEPTH)/../clib/x86/libclib.a
+FFS=$(DEPTH)/../ffs/x86/libffs.a
+
+OBJS=cmd_create.o cmd_add.o cmd_delete.o cmd_list.o \
+ cmd_erase.o cmd_trunc.o cmd_user.o command.o main.o
+
+TARGETS=$(NAME)
+
+vpath %.c ../src
+vpath %.h ..
+
+all: $(TARGETS)
+
+$(NAME): $(OBJS) $(FFS) $(DBGX) $(SPINOR) $(CLIB)
+ $(CC) $(LDFLAGS) -Wl,-whole-archive $(DBGX) -Wl,-no-whole-archive \
+ -o $@ $^ -lpthread -ldl -lrt
+
+install: $(TARGETS)
+ $(INSTALL) fpart $(FPART_INSTALL)/bin
+ $(INSTALL) ../fpart.sh $(FPART_INSTALL_TEST)
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/fpart/Rules.test.mk b/fpart/Rules.test.mk
new file mode 100644
index 0000000..9b63533
--- /dev/null
+++ b/fpart/Rules.test.mk
@@ -0,0 +1,25 @@
+CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/apps -iquote..
+LDFLAGS += -L. -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR)
+
+OBJS=test_libffs.o
+
+TARGETS=test_libffs
+
+vpath %.c ../test
+vpath %.h ..
+
+all: $(TARGETS)
+
+#libffs.a: $(OBJS)
+# $(AR) -r $@ $^
+
+test_libffs: test_libffs.o ../ppc/libffs.a $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a
+ $(CC) $(LDFLAGS) -o $@ $^ -lpthread
+
+install: $(TARGETS)
+ $(INSTALL) ../test/test_libffs $(TEST_LIBFFS_INSTALL)
+ $(INSTALL) ../test/libffs_test.sh $(TEST_LIBFFS_INSTALL)
+ $(INSTALL) ../test/ffs_tool_test.sh $(TEST_LIBFFS_INSTALL)
+
+clean distclean:
+ $(RM) -f $(TARGETS) $(OBJS)
diff --git a/fpart/fpart.sh b/fpart/fpart.sh
new file mode 100755
index 0000000..af73d6e
--- /dev/null
+++ b/fpart/fpart.sh
@@ -0,0 +1,398 @@
+#!/bin/bash
+# ===================================================================
+# Copyright (c) International Business Machines Corp., 2012
+#
+# 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
+#
+# File: ffs cmdline test script
+# Author: Shaun wetzstein <shaun@us.ibm.com>
+# Date: 02/06/13
+#
+# Todo:
+# 1) function to check syntax of each command
+
+CP=cp
+RM=rm
+FPART=fpart
+MKDIR=mkdir
+GREP=grep
+HEAD=head
+HEX=hexdump
+CRC=crc32
+DD=dd
+DIFF=diff
+
+IMG=sunray2_nor64M_flash.mif
+TMP=/tmp/ffs.$$
+ORIG=${TMP}/nor.orig
+URANDOM=/dev/urandom
+
+FAIL=1
+PASS=0
+
+KB=$((1*1024))
+MB=$(($KB*1024))
+
+function expect()
+{
+ local expect=${1}
+ shift
+ local command=${*}
+ eval $command
+ local actual=${?}
+
+ if [[ ${expect} -eq ${actual} ]]; then
+ echo "[PASSED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ else
+ echo "[FAILED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2
+ exit 1
+ fi
+}
+
+function pass()
+{
+ expect ${PASS} ${*}
+}
+
+function fail()
+{
+ expect ${FAIL} ${*}
+}
+
+function size()
+{
+ local size=$(stat -L -c %s ${2})
+ if [[ ${1} == ${size} ]]; then
+ echo "[PASSED] size: '${2}' ===> expect=${1}, actual=${size}"
+ else
+ echo "[FAILED] size: '${2}' ===> expect=${1}, actual=${size}"
+ exit 1
+ fi
+}
+
+function crc()
+{
+ local crc=$(${CRC} ${2})
+ if [[ ${1} == ${crc} ]]; then
+ echo "[PASSED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ else
+ echo "[FAILED] crc: '${2}' ===> expect=${1}, actual=${crc}"
+ exit 1
+ fi
+}
+
+function setup()
+{
+ pass ${RM} -rf ${TMP}
+ pass ${MKDIR} -p ${TMP}
+}
+
+function cleanup()
+{
+ pass ${RM} -rf ${TMP}
+}
+
+function create()
+{
+ local target=${TMP}/create.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K -p 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb \
+ --partition-offset 0x7f0000 --create
+
+ local output=${TMP}/create.txt
+ pass ${FPART} -t ${target} -L > ${output}
+ pass ${GREP} \'0x3f0000\' ${output} > /dev/null
+ pass ${GREP} \'0x7f0000\' ${output} > /dev/null
+ pass ${GREP} \'blk:010000\' ${output} > /dev/null
+ pass ${GREP} \'blk\(s\):000400\' ${output} > /dev/null
+ pass ${GREP} \'entsz:000080\' ${output} > /dev/null
+ pass ${GREP} \'ent\(s\):000001\' ${output} > /dev/null
+ pass ${RM} -f ${output}
+
+ pass ${RM} -f ${target}
+}
+
+function add()
+{
+ local target=${TMP}/add.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create
+
+ local output=${TMP}/add.txt
+ local name="logical"
+ pass ${FPART} -t ${target} -l -n ${name} -g 0 -A
+ pass ${FPART} -t ${target} -n ${name} -L > ${output}
+ pass ${GREP} ${name} ${output} > /dev/null
+ pass ${GREP} "l-----" ${output} > /dev/null
+
+ for ((i=0; i<9; i++)); do
+ local full="${name}/test${i}"
+ local offset=$((${i}*$MB))
+ local output=${TMP}/add.txt
+
+ # avoid clobbering 'part'
+ if [[ ${i} -eq 4 ]]; then
+ local size=$MB
+ else
+ local size=$(($MB-64*KB))
+ fi
+
+ pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A
+ pass ${FPART} -t ${target} -n ${full} -T
+ pass ${FPART} -t ${target} -n ${full} -L > ${output}
+ pass ${GREP} ${full} ${output} > /dev/null
+ pass ${GREP} "d-----" ${output} > /dev/null
+ local range=$(printf "%.8x-%.8x" ${offset} $((${offset}+${size}-1)))
+ pass ${GREP} ${range} ${output} > /dev/null
+ pass ${GREP} $(printf "%x" ${size}) ${output} > /dev/null
+ pass ${RM} -f ${output}
+ done
+
+ pass ${RM} -f ${target}
+}
+
+function delete()
+{
+ local target=${TMP}/delete.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create
+
+ local output=${TMP}/delete.txt
+
+ for ((i=0; i<9; i++)); do
+ local full="test${i}"
+ local offset=$((${i}*$MB))
+ local output=${TMP}/add.txt
+
+ # avoid clobbering 'part'
+ if [[ ${i} -eq 4 ]]; then
+ local size=$MB
+ else
+ local size=$(($MB-64*KB))
+ fi
+
+ pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A
+ pass ${FPART} -t ${target} -n ${full} -T
+ pass ${FPART} -t ${target} -n ${full} -L > ${output}
+ pass ${GREP} ${full} ${output} > /dev/null
+ pass ${FPART} -t ${target} -n ${full} -D
+ pass ${FPART} -t ${target} -n ${full} -L > ${output}
+ fail ${GREP} ${full} ${output} > /dev/null
+
+ pass ${RM} -f ${output}
+ done
+
+ pass ${RM} -f ${target}
+}
+
+function hex()
+{
+ local target=${TMP}/hexdump.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create
+
+ local name="logical"
+ pass ${FPART} -t ${target} -l -n ${name} -g 0 -A
+
+ for ((i=0; i<9; i++)); do
+ local full="${name}/test${i}"
+ local offset=$((${i}*$MB))
+ local output=${TMP}/hexdump.txt
+
+ # avoid clobbering 'part'
+ if [[ ${i} -eq 4 ]]; then
+ local size=$MB
+ else
+ local size=$(($MB-64*KB))
+ fi
+
+ pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A
+ pass ${FPART} -t ${target} -n ${full} -T
+ pass ${FPART} -t ${target} -n ${full} -H > ${output}
+ local p=$(printf "%2.2x%2.2x%2.2x%2.2x" $i $i $i $i)
+ pass ${GREP} \'$p $p $p $p\' ${output} > /dev/null
+ pass ${RM} -f ${output}
+ done
+
+ pass ${RM} -f ${target}
+}
+
+function read()
+{
+ local target=${TMP}/read.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create
+
+ local name="logical"
+ pass ${FPART} -t ${target} -l -n ${name} -g 0 -A
+
+ for ((i=0; i<9; i++)); do
+ local full="${name}/test${i}"
+ local offset=$((${i}*$MB))
+ local output=${TMP}/${i}.bin
+
+ # avoid clobbering 'part'
+ if [[ ${i} -eq 4 ]]; then
+ local size=$MB
+ else
+ local size=$(($MB-64*KB))
+ fi
+
+ pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A
+ pass ${FPART} -t ${target} -n ${full} -T
+ pass ${FPART} -t ${target} -n ${full} -R ${output}
+ pass ${HEX} -C ${output} > ${output}.hex
+ local p=$(printf "%2.2x %2.2x %2.2x %2.2x" $i $i $i $i)
+ pass ${GREP} \"${p} ${p}\" ${output}.hex
+ pass ${RM} -f ${output} ${output}.hex
+ done
+
+ pass ${RM} -f ${target}
+}
+
+function write()
+{
+ local target=${TMP}/write.nor
+ pass ${RM} -f ${target}
+
+ pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C
+ pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create
+
+ local name="write"
+ local input=${TMP}/write.in
+ local output=${TMP}/write.out
+ local block=${1}
+ local count=$(($MB/$block))
+
+ pass ${FPART} -t ${target} -o 1M -s 1M -g 0 -n ${name} -a 0xFF -A
+ for ((i=1; i<${count}; i++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${i} 2> /dev/null
+ local crc=$(${CRC} ${input})
+
+ pass ${FPART} -t ${target} -n ${name} -E -a 0xFF
+ pass ${FPART} -t ${target} -n ${name} -W ${input}
+ pass ${FPART} -t ${target} -n ${name} -U 0 -u ${crc}
+ pass ${FPART} -t ${target} -n ${name} -R ${output}
+
+ size $((${i}*${block})) ${output}
+ crc ${crc} ${output}
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FPART} -t ${target} -n ${name} -U 0 | ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input} ${output}
+ done
+ pass ${FPART} -t ${target} -n ${name} -D
+
+ pass ${RM} -f ${target}
+}
+
+function copy()
+{
+ local src=${TMP}/copy.src
+ local dst=${TMP}/copy.dst
+ pass ${RM} -f ${src} ${dst}
+
+ local part="0x0,0x20000"
+ pass ${FPART} -t ${src} -s 64M -b 64K -p ${part} -C
+ pass ${FPART} -t ${dst} -s 64M -b 64K -p ${part} -C
+
+ local name="copy"
+ pass ${FPART} -t ${src} -o 1M -s 1M -g 0 -n ${name} -a 0xFF -p ${part} -A
+ pass ${FPART} -t ${dst} -o 1M -s 1M -g 0 -n ${name} -a 0x00 -p ${part} -A
+
+ local input=${TMP}/copy.in
+ local output=${TMP}/copy.out
+ if [[ -z "${1}" ]]; then
+ local block=64
+ else
+ local block=${1}
+ fi
+ local count=$(($MB/$block))
+
+ for ((i=1; i<${count}; i++)); do
+ pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${i} 2> /dev/null
+ local crc=$(${CRC} ${input})
+
+ pass ${FPART} -t ${src} -n ${name} -p ${part} -E -a 0xFF
+ pass ${FPART} -t ${src} -n ${name} -p ${part} -W ${input}
+ pass ${FPART} -t ${src} -n ${name} -p ${part} -R ${output}.src
+ pass ${FPART} -t ${src} -n ${name} -p ${part} -U 0 -u ${crc}
+ pass ${FPART} -t ${dst} -n ${name} -p ${part} -O ${src}
+ pass ${FPART} -t ${dst} -n ${name} -p ${part} -M ${src}
+ pass ${FPART} -t ${src} -n ${name} -p ${part} -R ${output}.dst
+
+ size $((${i}*${block})) ${output}.src
+ size $((${i}*${block})) ${output}.dst
+ crc ${crc} ${output}.src
+ crc ${crc} ${output}.dst
+ pass ${DIFF} ${output}.src ${output}.dst
+
+ local crc=$(printf "%x" ${crc})
+ pass "${FPART} -t ${src} -n ${name} -p ${part} -U 0 | ${GREP} ${crc}" > /dev/null
+ pass "${FPART} -t ${dst} -n ${name} -p ${part} -U 0 | ${GREP} ${crc}" > /dev/null
+
+ pass ${RM} -f ${input} ${output}.*
+
+ done
+
+ expect 2 ${DIFF} ${src} ${dst}
+
+ pass ${RM} -f ${src} ${dst}
+}
+
+function main()
+{
+ create
+ add
+ delete
+# hex
+# read
+# copy $((15*$KB))
+# copy $((21*$KB))
+# copy $((64*$KB))
+# write $((15*$KB))
+# write $((21*$KB))
+# write $((64*$KB))
+}
+
+setup
+if [[ -z "${1:-}" ]]; then
+ main
+else
+ case "$1" in
+ create ) create ;;
+ add ) add ;;
+ delete ) delete ;;
+# hex ) hex ;;
+# read ) read ;;
+# copy ) copy $((${2}*$KB)) ;;
+# write ) write $((${2}*$KB)) ;;
+ * ) echo "$1 not implemented"; exit 1 ;;
+ esac
+
+ exit 0;
+fi
+cleanup
diff --git a/fpart/ppc/Makefile b/fpart/ppc/Makefile
new file mode 100644
index 0000000..99e6198
--- /dev/null
+++ b/fpart/ppc/Makefile
@@ -0,0 +1,9 @@
+DEPTH = ../../..
+include $(DEPTH)/integration/Rules.mk
+include $(DEPTH)/integration/Rules.ppc.mk
+
+ARCH=ppc
+FPART_INSTALL = $(INST_USR)
+FPART_INSTALL_TEST = $(INST_TESTS)
+
+include ../Rules.mk
diff --git a/fpart/src/cmd_add.c b/fpart/src/cmd_add.c
new file mode 100644
index 0000000..342df05
--- /dev/null
+++ b/fpart/src/cmd_add.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: add.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --add implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/exception.h>
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_add(args_t * args)
+{
+ assert(args != NULL);
+
+ /* ========================= */
+
+ int add(args_t * args, off_t poffset)
+ {
+ int rc = 0;
+
+ off_t offset = 0;
+ size_t size = 0;
+ uint32_t flags = 0;
+
+ rc = parse_offset(args->offset, &offset);
+ if (rc < 0)
+ return rc;
+ rc = parse_size(args->size, &size);
+ if (rc < 0)
+ return rc;
+ rc = parse_size(args->flags, &flags);
+ if (rc < 0)
+ return rc;
+
+ ffs_type_t type = FFS_TYPE_DATA;
+ if (args->logical == f_LOGICAL)
+ type = FFS_TYPE_LOGICAL;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE *, file, fopen_generic(target, "r+", debug), fclose);
+ RAII(ffs_t *, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+
+ rc = __ffs_entry_add(ffs, args->name, offset, size,
+ type, flags);
+ if (rc < 0)
+ return rc;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: add partition at offset '%llx' size "
+ "'%x' type '%d' flags '%x'\n", poffset,
+ args->name, offset, size, type, flags);
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ return command(args, add);
+}
diff --git a/fpart/src/cmd_compare.c b/fpart/src/cmd_compare.c
new file mode 100644
index 0000000..95b64d0
--- /dev/null
+++ b/fpart/src/cmd_compare.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: compare.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --compare implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_compare(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ char full_name[page_size];
+ regex_t rx;
+
+ off_t __poffset;
+ ffs_t * __in, * __out;
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int compare_entry(ffs_entry_t * src)
+ {
+ if (__ffs_entry_name(__in, src, full_name,
+ sizeof full_name) < 0)
+ return -1;
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (src->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: protected, skipping "
+ "compare\n", __poffset, full_name);
+ return 0;
+ }
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ if (node->entry.base == src->base &&
+ node->entry.size == src->size)
+ return 0;
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, src, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ ffs_entry_t dst;
+ if (__ffs_entry_find(__out, full_name, &dst) == false) {
+ UNEXPECTED("'%s' entry not found '%s'",
+ args->target, args->name);
+ return -1;
+ }
+
+ if (verify_operation(full_name, __in, src, __out, &dst) < 0)
+ return -1;
+
+ size_t block_size = __in->hdr->block_size;
+
+ if (args->block != NULL) {
+ if (parse_size(args->block, &block_size) < 0)
+ return -1;
+ if (block_size & (__in->hdr->block_size-1)) {
+ UNEXPECTED("'%x' block size must be multiple "
+ "of target block size '%x'",
+ block_size, __in->hdr->block_size);
+ return -1;
+ }
+ }
+
+ if (__ffs_buffer(__in, block_size) < 0)
+ return -1;
+ if (__ffs_buffer(__out, block_size) < 0)
+ return -1;
+
+ RAII(void*, src_block, malloc(block_size), free);
+ if (src_block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ RAII(void*, dst_block, malloc(block_size), free);
+ if (dst_block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t data_size = src->actual;
+ off_t data_offset = 0;
+
+ while (0 < data_size) {
+ ssize_t rc;
+
+ rc = __ffs_entry_read(__in, full_name, src_block,
+ data_offset,
+ min(block_size, data_size));
+ if (rc < 0)
+ return -1;
+
+ rc = __ffs_entry_read(__out, full_name, dst_block,
+ data_offset, rc);
+ if (rc < 0)
+ return -1;
+
+ if (memcmp(src_block, dst_block, block_size) != 0) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: miscompare at offset"
+ "'%llx'\n", __poffset, full_name,
+ data_offset);
+ UNEXPECTED("data miscompare at offset range "
+ "[%llx..%llx]", data_offset,
+ data_offset + block_size - 1);
+ return -1;
+ }
+
+
+ data_offset += rc;
+ data_size -= rc;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: compare to '%s'\n", __poffset,
+ full_name, args->path);
+
+ return 0;
+ }
+
+ int compare(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * path = args->path;
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, inf, fopen_generic(path, "r", debug), fclose);
+ if (inf == NULL)
+ return -1;
+ RAII(FILE*, outf, fopen_generic(target, "r", debug), fclose);
+ if (outf == NULL)
+ return -1;
+
+ RAII(ffs_t*, in, __ffs_fopen(inf, poffset), __ffs_fclose);
+ if (in == NULL)
+ return -1;
+ RAII(ffs_t*, out, __ffs_fopen(outf, poffset), __ffs_fclose);
+ if (out == NULL)
+ return -1;
+
+ __in = in;
+ __out = out;
+
+ int rc = __ffs_iterate_entries(in, compare_entry);
+ if (rc == 1)
+ rc = -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, compare);
+
+ regfree(&rx);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
diff --git a/fpart/src/cmd_copy.c b/fpart/src/cmd_copy.c
new file mode 100644
index 0000000..9bbb8bf
--- /dev/null
+++ b/fpart/src/cmd_copy.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: copy.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --copy implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_copy(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ char full_name[page_size];
+ regex_t rx;
+
+ ffs_t *__in, *__out;
+ off_t __poffset;
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int copy_entry(ffs_entry_t * src)
+ {
+ if (__ffs_entry_name(__in, src, full_name, sizeof full_name)< 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (src->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: protected, skipping copy\n",
+ __poffset, full_name);
+ return 0;
+ }
+
+ __ffs_entry_truncate(__out, full_name, src->actual);
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: truncate size '%x'\n",
+ __poffset, full_name, src->actual);
+
+ uint32_t src_val, dst_val;
+ for (uint32_t i=0; i<FFS_USER_WORDS; i++) {
+ __ffs_entry_user_get(__in, full_name, i, &src_val);
+ __ffs_entry_user_get(__out, full_name, i, &dst_val);
+ if (src_val != dst_val)
+ if (__ffs_entry_user_put(__out, full_name,
+ i, src_val) < 0)
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: user[] copy from '%s'\n", __poffset,
+ full_name, args->path);
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ ffs_entry_t * e = &node->entry;
+ if (e->base == src->base && e->size == src->size) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: skipping copy from "
+ "'%s'\n", __poffset, full_name,
+ args->path);
+ return 0;
+ }
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, src, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ ffs_entry_t dst;
+ if (__ffs_entry_find(__out, full_name, &dst) == false) {
+ UNEXPECTED("'%s' entry not found '%s'",
+ args->target, args->name);
+ return -1;
+ }
+
+ if (verify_operation(full_name, __in, src, __out, &dst) < 0)
+ return -1;
+
+ size_t block_size = __in->hdr->block_size;
+
+ if (args->block != NULL) {
+ if (parse_size(args->block, &block_size) < 0)
+ return -1;
+ if (block_size & (__in->hdr->block_size-1)) {
+ UNEXPECTED("'%x' block size must be multiple "
+ "of target block size '%x'",
+ block_size, __in->hdr->block_size);
+ return -1;
+ }
+ }
+
+ if (__ffs_buffer(__in, block_size) < 0)
+ return -1;
+ if (__ffs_buffer(__out, block_size) < 0)
+ return -1;
+
+ RAII(void*, block, malloc(block_size), free);
+ if (block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t data_size = src->actual;
+ off_t data_offset = 0;
+
+ while (0 < data_size) {
+ ssize_t rc;
+
+ rc = __ffs_entry_read(__in, full_name, block,
+ data_offset,
+ min(block_size, data_size));
+ if (rc < 0)
+ return -1;
+
+ rc = __ffs_entry_write(__out, full_name, block,
+ data_offset, rc);
+ if (rc < 0)
+ return -1;
+
+ data_offset += rc;
+ data_size -= rc;
+ }
+
+ __ffs_fsync(__out);
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: copy from '%s'\n", __poffset,
+ full_name, args->path);
+
+ return 0;
+ }
+
+ int copy(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * path = args->path;
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, inf, fopen_generic(path, "r", debug), fclose);
+ if (inf == NULL)
+ return -1;
+
+ RAII(ffs_t*, in, __ffs_fopen(inf, poffset), __ffs_fclose);
+ if (in == NULL)
+ return -1;
+
+ if (args->force == f_FORCE) {
+ size_t block_size = in->hdr->block_size;
+ char block[block_size];
+
+ size_t rc;
+ rc = __ffs_entry_read(in, FFS_PARTITION_NAME, block,
+ 0, block_size);
+ if (rc != block_size) {
+ UNEXPECTED("'%s' unable to read '%s' from "
+ "offset '%llx'", args->path,
+ FFS_PARTITION_NAME, poffset);
+ return -1;
+ }
+
+ RAII(FILE*, outf,
+ fopen_generic(target, "r+", debug), fclose);
+ if (outf == NULL)
+ return -1;
+
+ if (fseeko(outf, poffset, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ rc = fwrite(block, 1, block_size, outf);
+ if (rc <= 0) {
+ if (ferror(outf)) {
+ ERRNO(errno);
+ return -1;
+ }
+ }
+
+ fflush(outf);
+ if (fileno(outf) != -1)
+ fsync(fileno(outf));
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: force partition table\n",
+ poffset, FFS_PARTITION_NAME);
+ }
+
+ RAII(FILE*, outf, fopen_generic(target, "r+", debug), fclose);
+ if (outf == NULL)
+ return -1;
+ RAII(ffs_t*, out, __ffs_fopen(outf, poffset), __ffs_fclose);
+ if (in == NULL)
+ return -1;
+
+ __in = in;
+ __out = out;
+
+ int rc = __ffs_iterate_entries(in, copy_entry);
+ if (rc == 1)
+ rc = -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, copy);
+
+ regfree(&rx);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
diff --git a/fpart/src/cmd_create.c b/fpart/src/cmd_create.c
new file mode 100644
index 0000000..f7448ec
--- /dev/null
+++ b/fpart/src/cmd_create.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: create.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --create implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_create(args_t * args)
+{
+ assert(args != NULL);
+
+ size_t block = 0;
+ size_t size = 0;
+ size_t pad = 0xff;
+
+ if (parse_size(args->block, &block) < 0)
+ return -1;
+ if (parse_size(args->size, &size) < 0)
+ return -1;
+ if (args->pad != NULL)
+ if (parse_size(args->pad, &pad) < 0)
+ return -1;
+
+ struct stat st;
+ if (stat(args->target, &st) < 0) {
+ if (errno == ENOENT) {
+ create_regular_file(args->target, size, (uint8_t)pad);
+ } else {
+ ERRNO(errno);
+ return -1;
+ }
+ } else {
+ if (st.st_size != size) {
+ create_regular_file(args->target, size, (uint8_t)pad);
+ } else {
+ if (args->force != f_FORCE && st.st_size != size) {
+ UNEXPECTED("--size '%d' differs from actual "
+ "size '%lld', use --force to "
+ "override", size, st.st_size);
+ return -1;
+ }
+ }
+ }
+
+ /* ========================= */
+
+ int create(args_t * args, off_t poffset)
+ {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: create partition table\n", poffset);
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fcreate(file, poffset, block,
+ size / block), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ return 0;
+ }
+
+ /* ========================= */
+
+ return command(args, create);
+}
diff --git a/fpart/src/cmd_delete.c b/fpart/src/cmd_delete.c
new file mode 100644
index 0000000..26033da
--- /dev/null
+++ b/fpart/src/cmd_delete.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: delete.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --delete implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_delete(args_t * args)
+{
+ assert(args != NULL);
+
+ /* ========================= */
+
+ int delete(args_t * args, off_t poffset)
+ {
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+
+ if (__ffs_entry_delete(ffs, args->name) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: delete\n", poffset, args->name);
+
+ return 0;
+ }
+
+ /* ========================= */
+
+ return command(args, delete);
+}
diff --git a/fpart/src/cmd_erase.c b/fpart/src/cmd_erase.c
new file mode 100644
index 0000000..aac75fb
--- /dev/null
+++ b/fpart/src/cmd_erase.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: erase.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --erase implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_erase(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ size_t pad = 0xff;
+ if (args->pad != NULL)
+ if (parse_size(args->pad, &pad) < 0)
+ return -1;
+
+ char full_name[page_size];
+ regex_t rx;
+
+ ffs_t * __ffs;
+ off_t __poffset;
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int erase_entry(ffs_entry_t * entry)
+ {
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (entry->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: protected, skipping erase\n",
+ __poffset, full_name);
+ return 0;
+ }
+
+ if (__ffs_entry_truncate(__ffs, full_name, 0) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: truncate size '%x'\n", __poffset,
+ full_name, 0);
+
+ for (uint32_t i=0; i<FFS_USER_WORDS; i++)
+ if (__ffs_entry_user_put(__ffs, full_name, i, 0) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: user[] zero\n", __poffset, full_name);
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ if (node->entry.base == entry->base) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: skipping fill with "
+ "'%x'\n", __poffset, full_name,
+ (uint8_t)pad);
+ return 0;
+ }
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, entry, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ uint32_t block_size = __ffs->hdr->block_size;
+
+ if (args->block != NULL) {
+ if (parse_size(args->block, &block_size) < 0)
+ return -1;
+ if (block_size & (__ffs->hdr->block_size-1)) {
+ UNEXPECTED("'%x' block size must be multiple "
+ "of target block size '%x'",
+ block_size, __ffs->hdr->block_size);
+ return -1;
+ }
+ }
+
+ if (__ffs_buffer(__ffs, block_size) < 0)
+ return -1;
+
+ RAII(void*, block, malloc(block_size), free);
+ if (block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+ memset(block, pad, block_size);
+
+ for (uint32_t i = 0; i < entry->size; i++)
+ if(__ffs_entry_write(__ffs, full_name, block, i * block_size, block_size) < 0)
+ return -1;
+
+ __ffs_fsync(__ffs);
+
+ if (__ffs_entry_truncate(__ffs, full_name, 0) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: filled with '%x'\n", __poffset,
+ full_name, (uint8_t)pad);
+
+ return 0;
+ }
+
+ int erase(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ __ffs = ffs;
+
+ int rc = __ffs_iterate_entries(ffs, erase_entry);
+ if (rc == 1)
+ rc = -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, erase);
+
+ regfree(&rx);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
diff --git a/fpart/src/cmd_hexdump.c b/fpart/src/cmd_hexdump.c
new file mode 100644
index 0000000..1a0086e
--- /dev/null
+++ b/fpart/src/cmd_hexdump.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: hexdump.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --hexdump implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_hexdump(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ char full_name[page_size];
+ regex_t rx;
+
+ off_t __poffset;
+ ffs_t * __ffs;
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int hexdump_entry(ffs_entry_t * entry)
+ {
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (entry->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ printf("%llx: %s: protected, skipping hexdump\n",
+ __poffset, full_name);
+ return 0;
+ }
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ if (node->entry.base == entry->base &&
+ node->entry.size == entry->size)
+ return 0;
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, entry, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ if (__ffs_entry_hexdump(__ffs, full_name, stdout) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: partition actual '%x'\n",
+ __poffset, full_name, entry->actual);
+
+ return 0;
+ }
+
+ int hexdump(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ __ffs = ffs;
+
+ int rc = __ffs_iterate_entries(ffs, hexdump_entry);
+ if (rc == 1)
+ rc = -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, hexdump);
+
+ regfree(&rx);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
diff --git a/fpart/src/cmd_list.c b/fpart/src/cmd_list.c
new file mode 100644
index 0000000..b9a4cbd
--- /dev/null
+++ b/fpart/src/cmd_list.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: list.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --list implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_list(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ ffs_t *__ffs;
+
+ char full_name[page_size];
+ regex_t rx;
+
+ /* ========================= */
+
+ int __list_entry(ffs_entry_t * entry)
+ {
+
+ size_t offset = entry->base * __ffs->hdr->block_size;
+ size_t size = entry->size * __ffs->hdr->block_size;
+
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ char type;
+ if (entry->type == FFS_TYPE_LOGICAL) {
+ type ='l';
+ } else if (entry->type == FFS_TYPE_DATA) {
+ type ='d';
+ } else if (entry->type == FFS_TYPE_PARTITION) {
+ type ='p';
+ }
+ fprintf(stdout, "%3d [%08x-%08x] [%8x:%8x] ",
+ entry->id, offset, offset+size-1,
+ size, entry->actual);
+
+ fprintf(stdout, "[%c%c%c%c%c%c] %s\n",
+ type, '-', '-', '-',
+ entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-',
+ entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-',
+ full_name);
+
+ if (args->verbose == f_VERBOSE) {
+ for (int i=0; i<FFS_USER_WORDS; i++) {
+ fprintf(stdout, "[%2d] %8x ", i,
+ entry->user.data[i]);
+ if ((i+1) % 4 == 0)
+ fprintf(stdout, "\n");
+ }
+ }
+
+ return 0;
+ }
+
+ int list(args_t * args, off_t poffset)
+ {
+ const char * target = args->target;
+ int debug = args->debug;
+ int rc = 0;
+
+ RAII(FILE*, file, fopen_generic(target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+
+ if (__ffs_fcheck(file, poffset) < 0)
+ return -1;
+
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ if (0 < ffs->count) {
+ printf("========================[ PARTITION TABLE"
+ " 0x%llx ]=======================\n",
+ ffs->offset);
+ printf("vers:%04x size:%04x * blk:%06x blk(s):"
+ "%06x * entsz:%06x ent(s):%06x\n",
+ ffs->hdr->version,
+ ffs->hdr->size,
+ ffs->hdr->block_size,
+ ffs->hdr->block_count,
+ ffs->hdr->entry_size,
+ ffs->hdr->entry_count);
+ printf("----------------------------------"
+ "----------------------------------"
+ "-------\n");
+
+ __ffs = ffs;
+
+ rc = __ffs_iterate_entries(ffs, __list_entry);
+ if (rc == 1)
+ rc = -1;
+
+ printf("\n");
+ }
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, list);
+
+ regfree(&rx);
+
+ return rc;
+}
diff --git a/fpart/src/cmd_read.c b/fpart/src/cmd_read.c
new file mode 100644
index 0000000..e058377
--- /dev/null
+++ b/fpart/src/cmd_read.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: read.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --read implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_read(args_t * args)
+{
+ assert(args != NULL);
+
+ if (access(args->path, F_OK) == 0 && args->force != f_FORCE) {
+ UNEXPECTED("output file '%s' already exists, use --force "
+ "to overwrite\n", args->path);
+ return -1;
+ }
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int read(args_t * args, off_t poffset)
+ {
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ size_t block_size = ffs->hdr->block_size;
+
+ if (args->block != NULL) {
+ if (parse_size(args->block, &block_size) < 0)
+ return -1;
+ if (block_size & (ffs->hdr->block_size-1)) {
+ UNEXPECTED("'%x' block size must be multiple "
+ "of target block size '%x'",
+ block_size, ffs->hdr->block_size);
+ return -1;
+ }
+ }
+
+ if (__ffs_buffer(ffs, block_size) < 0)
+ return -1;
+
+ RAII(void*, block, malloc(block_size), free);
+ if (block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ ffs_entry_t entry;
+ if (__ffs_entry_find(ffs, args->name, &entry) == false) {
+ UNEXPECTED("'%s' partition not found => %s",
+ args->target, args->name);
+ return -1;
+ }
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ if (node->entry.base == entry.base &&
+ node->entry.size == entry.size)
+ return 0;
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, &entry, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ RAII(FILE*, out, fopen(args->path, "w+"), fclose);
+ if (out == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size_t data_size = entry.actual;
+ off_t data_offset = 0;
+
+ while (0 < data_size) {
+ ssize_t rc;
+ rc = __ffs_entry_read(ffs, args->name,
+ block, data_offset,
+ min(block_size, data_size));
+
+ rc = fwrite(block, 1, rc, out);
+ if (rc <= 0 && ferror(out)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ data_size -= rc;
+ data_offset += rc;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: wrote '%x' bytes to file '%s'\n",
+ poffset, args->name, entry.actual, args->path);
+
+ return 0;
+ }
+
+ /* ========================= */
+
+ int rc = command(args, read);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
+
diff --git a/fpart/src/cmd_trunc.c b/fpart/src/cmd_trunc.c
new file mode 100644
index 0000000..b946bdc
--- /dev/null
+++ b/fpart/src/cmd_trunc.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: trunc.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --trunc implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_trunc(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ off_t __poffset;
+ ffs_t * __ffs;
+
+ char full_name[page_size];
+ regex_t rx;
+
+ /* ========================= */
+
+ int trunc_entry(ffs_entry_t * entry)
+ {
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (entry->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ printf("%llx: %s: protected, skipping truncate\n",
+ __poffset, full_name);
+ return 0;
+ }
+
+ size_t size = entry->size * __ffs->hdr->block_size;
+ if (args->size != NULL)
+ if (parse_size(args->size, &size) < 0)
+ return -1;
+
+ if (__ffs_entry_truncate(__ffs, full_name, size) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: truncate size '%x'\n", __poffset,
+ full_name, size);
+
+ return 0;
+ }
+
+ int trunc(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ __ffs = ffs;
+
+ int rc = __ffs_iterate_entries(ffs, trunc_entry);
+ if (rc == 1)
+ return -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, trunc);
+
+ regfree(&rx);
+
+ return rc;
+}
diff --git a/fpart/src/cmd_user.c b/fpart/src/cmd_user.c
new file mode 100644
index 0000000..67abc50
--- /dev/null
+++ b/fpart/src/cmd_user.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: user.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --user implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_user(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ char full_name[page_size];
+ regex_t rx;
+
+ off_t __poffset;
+ ffs_t * __ffs;
+
+ uint32_t user = 0;
+ if (args->user != NULL)
+ if (parse_size(args->user, &user) < 0)
+ return -1;
+
+ if (FFS_USER_WORDS <= user) {
+ UNEXPECTED("invalid user word '%d', valid range [0..%d]",
+ user, FFS_USER_WORDS);
+ return -1;
+ }
+
+ int user_entry(ffs_entry_t * entry)
+ {
+ assert(entry != NULL);
+
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (entry->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ printf("%llx: %s: protected, skipping user[%d]\n",
+ __poffset, full_name, user);
+ return 0;
+ }
+
+ uint32_t value = 0;
+ if (args->value != NULL) {
+ if (args->value != NULL)
+ if (parse_size(args->value, &value) < 0)
+ return -1;
+
+ if (__ffs_entry_user_put(__ffs, full_name,
+ user, value) < 0)
+ return -1;
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: user[%d] = '%x'\n", __poffset,
+ args->name, user, value);
+ } else {
+ if (__ffs_entry_user_get(__ffs, args->name,
+ user, &value) < 0)
+ return -1;
+
+ printf("%llx: %s: user[%d] = '%x'\n", __poffset,
+ args->name, user, value);
+ }
+
+ return 0;
+ }
+
+ int __user(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ __ffs = ffs;
+
+ int rc = __ffs_iterate_entries(ffs, user_entry);
+ if (rc == 1)
+ return -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return-1;
+ }
+
+ int rc = command(args, __user);
+
+ regfree(&rx);
+
+ return rc;
+}
diff --git a/fpart/src/cmd_write.c b/fpart/src/cmd_write.c
new file mode 100644
index 0000000..83f71d3
--- /dev/null
+++ b/fpart/src/cmd_write.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: write.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: --write implementation
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+int command_write(args_t * args)
+{
+ assert(args != NULL);
+
+ char name[strlen(args->name) + 2];
+ strcpy(name, args->name);
+ if (strrchr(name, '$') == NULL)
+ strcat(name, "$");
+
+ char full_name[page_size];
+ struct stat st;
+ regex_t rx;
+
+ off_t __poffset;
+ ffs_t *__ffs;
+
+ RAII(FILE*, in, fopen(args->path, "r"), fclose);
+
+ list_t list;
+ list_init(&list);
+
+ list_iter_t it;
+ ffs_entry_node_t * node;
+
+ /* ========================= */
+
+ int write_entry(ffs_entry_t * entry)
+ {
+ assert(entry != NULL);
+
+ if (__ffs_entry_name(__ffs, entry, full_name,
+ sizeof full_name) < 0)
+ return -1;
+
+ if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH)
+ return 0;
+
+ if (entry->flags & FFS_FLAGS_PROTECTED &&
+ args->force != f_FORCE) {
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: protected, skipping write\n",
+ __poffset, full_name);
+ return 0;
+ }
+
+ if (entry->actual < st.st_size) {
+ if (__ffs_entry_truncate(__ffs, full_name,
+ st.st_size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: truncate size '%llx'\n",
+ __poffset, full_name, st.st_size);
+ }
+
+ list_iter_init(&it, &list, LI_FLAG_FWD);
+ list_for_each(&it, node, node) {
+ if (node->entry.base == entry->base &&
+ node->entry.size == entry->size)
+ return 0;
+ }
+
+ node = (ffs_entry_node_t *)malloc(sizeof(*node));
+ assert(node != NULL);
+
+ memcpy(&node->entry, entry, sizeof(node->entry));
+ list_add_tail(&list, &node->node);
+
+ size_t block_size = __ffs->hdr->block_size;
+ size_t entry_size = entry->size * block_size;
+ size_t data_size = st.st_size;
+ off_t data_offset = 0;
+
+ if (entry_size < data_size) {
+ UNEXPECTED("'%s' of size '%x' too big for partition "
+ "'%s' of size '%x'", args->path, data_size,
+ args->name, entry_size);
+ return -1;
+ }
+
+ if (args->block != NULL) {
+ if (parse_size(args->block, &block_size) < 0)
+ return -1;
+ if (block_size & (__ffs->hdr->block_size-1)) {
+ UNEXPECTED("'%x' block size must be multiple "
+ "of target block size '%x'",
+ block_size, __ffs->hdr->block_size);
+ return -1;
+ }
+ }
+
+ if (__ffs_buffer(__ffs, block_size) < 0)
+ return -1;
+
+ RAII(void*, block, malloc(block_size), free);
+ if (block == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (fseeko(in, 0, SEEK_SET) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ while (0 < data_size) {
+ ssize_t rc = fread(block, 1,
+ min(block_size, data_size), in);
+ if (rc <= 0 && ferror(in)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ rc = __ffs_entry_write(__ffs, full_name, block,
+ data_offset, rc);
+ if (rc < 0)
+ return -1;
+
+ __ffs_fsync(__ffs);
+
+ data_offset += rc;
+ data_size -= rc;
+ }
+
+ if (args->verbose == f_VERBOSE)
+ printf("%llx: %s: read '%llx' bytes from file '%s'\n",
+ __poffset, full_name, st.st_size, args->path);
+
+ return 0;
+ }
+
+ int write(args_t * args, off_t poffset)
+ {
+ __poffset = poffset;
+
+ const char * target = args->target;
+ int debug = args->debug;
+
+ RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose);
+ if (file == NULL)
+ return -1;
+ RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose);
+ if (ffs == NULL)
+ return -1;
+
+ __ffs = ffs;
+
+ int rc = __ffs_iterate_entries(ffs, write_entry);
+ if (rc == 1)
+ rc = -1;
+
+ return rc;
+ }
+
+ /* ========================= */
+
+ if (stat(args->path, &st) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ int rc = command(args, write);
+
+ regfree(&rx);
+
+ while (!list_empty(&list))
+ free(container_of(list_remove_head(&list),
+ ffs_entry_node_t, node));
+
+ return rc;
+}
diff --git a/fpart/src/command.c b/fpart/src/command.c
new file mode 100644
index 0000000..9f53bde
--- /dev/null
+++ b/fpart/src/command.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: command.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: generic --<command> wrapper
+ * Date: 01/30/2013
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+//#include <spinor/aaflash.h> // --target aa:
+//#include <dbgx/rwflash.h> // --target rw:
+
+#include "main.h"
+
+int parse_offset(const char *str, off_t *offset)
+{
+ assert(offset != NULL);
+
+ if (str == NULL) {
+ *offset = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *offset = strtoull(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") ||
+ !strcasecmp(end, "KB") ||
+ !strcasecmp(end, "K"))
+ *offset <<= 10;
+ else if (!strcmp(end, "MiB") ||
+ !strcasecmp(end, "MB") ||
+ !strcasecmp(end, "M"))
+ *offset <<= 20;
+ else if (!strcmp(end, "GiB") ||
+ !strcasecmp(end, "GB") ||
+ !strcasecmp(end, "G"))
+ *offset <<= 30;
+ else {
+ UNEXPECTED("invalid offset specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_size(const char *str, size_t *size)
+{
+ assert(size != NULL);
+
+ if (str == NULL) {
+ *size = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *size = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ if (!strcmp(end, "KiB") || !strcasecmp(end, "K") ||
+ !strcasecmp(end, "KB"))
+ *size <<= 10;
+ else if (!strcmp(end, "MiB") || !strcasecmp(end, "M") ||
+ !strcasecmp(end, "MB"))
+ *size <<= 20;
+ else if (!strcmp(end, "GiB") || !strcasecmp(end, "G") ||
+ !strcasecmp(end, "GB"))
+ *size <<= 30;
+ else {
+ UNEXPECTED("invalid size specified '%s'", end);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int parse_number(const char *str, size_t *num)
+{
+ assert(num != NULL);
+
+ if (str == NULL) {
+ *num = 0;
+ return 0;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ *num = strtoul(str, &end, 0);
+ if (errno != 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (*end != '\0') {
+ UNEXPECTED("invalid number specified '%s'", end);
+ return -1;
+ }
+
+ return 0;
+}
+
+bool check_extension(const char *path, const char *ext)
+{
+ int len = strlen(path), ext_len = strlen(ext);
+ return (ext_len < len)
+ && (strncasecmp(path + len - ext_len, ext, ext_len) == 0);
+}
+
+int create_regular_file(const char *path, size_t size, char pad)
+{
+ assert(path != NULL);
+
+ RAII(FILE*, file, fopen(path, "w"), fclose);
+ if (file == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ if (ftruncate(fileno(file), size) < 0) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ uint32_t page_size = sysconf(_SC_PAGESIZE);
+ char buf[page_size];
+ memset(buf, pad, page_size);
+
+ while (0 < size) {
+ ssize_t rc = fwrite(buf, 1, min(sizeof(buf), size), file);
+ if (rc <= 0 && ferror(file)) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ size -= rc;
+ }
+
+ return 0;
+}
+
+int command(args_t * args, int (*cmd)(args_t *, off_t))
+{
+ assert(args != NULL);
+ assert(cmd != NULL);
+ int rc = 0;
+
+ char * end = (char *)args->poffset;
+ while (rc == 0 && end != NULL && *end != '\0') {
+ errno = 0;
+ off_t poffset = strtoull(end, &end, 0);
+ if (end == NULL || errno != 0) {
+ UNEXPECTED("invalid --partition-offset specified '%s'",
+ args->poffset);
+ return -1;
+ }
+
+ if (*end != ',' && *end != ':' && *end != '\0') {
+ UNEXPECTED("invalid --partition-offset separator "
+ "character '%c'", *end);
+ return -1;
+ }
+
+ if (cmd != NULL)
+ rc = cmd(args, poffset);
+
+ if (*end == '\0')
+ break;
+ end++;
+ }
+
+ return rc;
+}
+
+FILE *fopen_generic(const char *path, const char *mode, int debug)
+{
+ assert(path != NULL);
+ assert(mode != NULL);
+
+ FILE *file = NULL;
+ size_t port = 0;
+
+ if (strncasecmp(path, "aa:", 3) == 0) {
+ if (parse_number(path + 3, &port) == 0)
+ assert(0);
+ //file = fopen_aaflash(port, mode, debug);
+ } else if (strncasecmp(path, "rw:", 3) == 0) {
+ assert(0);
+ //file = fopen_rwflash(path + 3, mode, debug);
+ } else {
+ file = fopen(path, mode);
+ }
+
+ if (file == NULL)
+ ERRNO(errno);
+
+ return file;
+}
+
+int verify_operation(const char * name, ffs_t * in, ffs_entry_t * in_e,
+ ffs_t * out, ffs_entry_t * out_e)
+{
+ if (in->hdr->block_size != out->hdr->block_size) {
+ UNEXPECTED("block sizes differ '%x' != '%x'",
+ in->hdr->block_size, out->hdr->block_size);
+ return -1;
+ }
+
+ if (in->hdr->block_count != out->hdr->block_count) {
+ UNEXPECTED("block counts differ '%x' != '%x'",
+ in->hdr->block_size, out->hdr->block_size);
+ return -1;
+ }
+
+ if (in->hdr->entry_size != out->hdr->entry_size) {
+ UNEXPECTED("entry sizes differ '%x' != '%x'",
+ in->hdr->entry_size, out->hdr->entry_size);
+ return -1;
+ }
+
+ if (in->hdr->entry_count != out->hdr->entry_count) {
+ UNEXPECTED("entry counts differ '%x' != '%x'",
+ in->hdr->entry_size, out->hdr->entry_size);
+ return -1;
+ }
+
+ if (in_e->base != out_e->base) {
+ UNEXPECTED("partition '%s' offsets differ '%x' != '%x'",
+ name, in_e->base, out_e->base);
+ return -1;
+ }
+
+ if (in_e->size != out_e->size) {
+ UNEXPECTED("partition '%s' sizes differ '%x' != '%x'",
+ name, in_e->size, out_e->size);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/fpart/src/main.c b/fpart/src/main.c
new file mode 100644
index 0000000..a90b5f5
--- /dev/null
+++ b/fpart/src/main.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: main.c
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: FFS partition tool
+ * Date: 05/12/2012
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <clib/attribute.h>
+#include <clib/assert.h>
+#include <clib/list.h>
+#include <clib/list_iter.h>
+#include <clib/misc.h>
+#include <clib/min.h>
+#include <clib/err.h>
+#include <clib/raii.h>
+
+#include "main.h"
+
+args_t args;
+size_t page_size;
+
+static const char copyright[] __unused__ =
+ "Licensed Internal Code - Property of IBM\n"
+ "IBM Support Processor Licensed Internal Code\n"
+ "(c) Copyright IBM Corp 2012 All Rights Reserved\n"
+ "US Government Users Restricted Rights - Use, duplication\n"
+ "or disclosure restricted by GSA ADP Schedule Contract\n"
+ "with IBM Corp.";
+
+static void usage(bool verbose)
+{
+ FILE *e = stderr;
+
+ fprintf(e,
+ "fpart - FFS Partition Tool v%d.%d.%d -- Authors: "
+ "<shaun@us.ibm.com>\n (c) Copyright IBM Corp 2012 All Rights "
+ "Reserved\n", FPART_MAJOR, FPART_MINOR, FPART_PATCH);
+
+ fprintf(e, "\nUsage:\n");
+ fprintf(e, " fpart <command> <options>...\n");
+
+ fprintf(e, "\nExamples:\n");
+ fprintf(e, " fpart -C -t nor -s 64MiB -b 64k -p 0x3f0000,0x7f0000\n");
+ fprintf(e, " fpart -A -t nor -p 0x3f0000,0x7f0000 -s 1Mb -o 0M -g 0 "
+ "-n boot0 -l\n");
+ fprintf(e, " fpart --add --target nor --size 1mb --offset 1MiB "
+ "--flags 0x0 --name boot0/ipl\n");
+ fprintf(e, " fpart --delete --target nor nor --name boot0/ipl\n");
+ fprintf(e, " fpart --target nor --write ipl.bin --name boot1/ipl\n");
+ fprintf(e, " fpart --user 0 -t nor -n boot0/ipl --value 0xFF500FF5\n");
+ fprintf(e, " fpart --copy new_nor -t nor -n ipl\n");
+ fprintf(e, " fpart --compare new_nor -t nor -n bank0\n");
+
+ fprintf(e, "\nCommands:\n");
+ fprintf(e, " -C, --create [options]\n");
+ if (verbose)
+ fprintf(e, "\n Create an empty partition table at each"
+ " specified partition offest.\n\n");
+
+ fprintf(e, " -A, --add [options]\n");
+ if (verbose)
+ fprintf(e, "\n Add partition entry(s) to each specified"
+ " partition offset.\n\n");
+
+ fprintf(e, " -D, --delete [options]\n");
+ if (verbose)
+ fprintf(e, "\n Delete partition entry(s) from each specified"
+ " partition offset.\n\n");
+
+ fprintf(e, " -E, --erase [options]\n");
+ if (verbose)
+ fprintf(e, "\n Fill partition entry(s) from each specified"
+ " partition offset.\n\n");
+
+ fprintf(e, " -L, --list [options]\n");
+ if (verbose)
+ fprintf(e, "\n Display the list of matching partition"
+ " entry(s) of each specified\n"
+ " partition offset.\n\n");
+
+ fprintf(e, " -T, --trunc [options]\n");
+ if (verbose)
+ fprintf(e, "\n Truncate the actual data size of matching"
+ " partition entry(s) of each\n specified partition"
+ " offset.\n\n");
+
+ fprintf(e, " -U, --user <num> [options]\n");
+ if (verbose)
+ fprintf(e, "\n Read or write user words of matching partition"
+ " entry(s) for each\n specified partition offset.\n");
+
+ /* =============================== */
+
+ fprintf(e, "\nOptions:\n");
+ fprintf(e, " -p, --partition-offset <offset[,offset]>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies a comma (,) separated list of"
+ " partition table offsets, in\n bytes from the start"
+ " of the target file (or device).\n\n");
+
+ fprintf(e, " -t, --target <target>\n");
+ if (verbose) {
+ fprintf(e, "\n Specifies the target of the operation.");
+ fprintf(e, " <target> can be one of:\n");
+ fprintf(e, "\t <port> : Aardvard USB port number [0..9]\n");
+ fprintf(e, "\t<hostname> : RISCWatch probe hostname\n");
+ fprintf(e, "\t <path> : SFC device file, e.g. "
+ "/dev/mtdblock/sfc.of\n\n");
+ }
+
+ fprintf(e, " -n, --name <name>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the name of a partition entry(s)."
+ " For some commands\n (--hexdump, --add)"
+ " <name> must be a fully qualified name.\n For other"
+ " commands, name can specify a regular "
+ "expression.\n\n");
+
+ fprintf(e, " -o, --offset <offset>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the offset of a partition entry,"
+ " in bytes from the beginning\n of the target file"
+ " (or device).\n\n");
+
+ fprintf(e, " -s, --size <size>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the size of a partition entry (in"
+ " bytes).\n <size> is a decimal (or hex) number"
+ " (optionally) followed by 'k', 'K',\n 'KB' or 'KiB'"
+ " to specify.\n\n");
+
+ fprintf(e, " -b, --block-size <size>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the block size, in bytes. <size>"
+ " is a decimal (or hex)\n number (optionally)"
+ " followed by 'k', 'K', 'KB' or 'KiB' to"
+ " specify.\n\n");
+
+ fprintf(e, " -u, --value <value>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the user word value. <value> is a"
+ " decimal (or hex) number.\n\n");
+
+ fprintf(e, " -g, --flags <value>\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the partition flags value."
+ " <value> is a decimal (or hex)\n number.\n\n");
+
+ fprintf(e, " -a, --pad <value>\n");
+ if (verbose)
+ fprintf(e,
+ "\n Specifies the partition content initial value."
+ " <value> is a decimal\n (or hex) number, default is"
+ " 0xFF.\n\n");
+
+ /* =============================== */
+
+ fprintf(e, "\nFlags:\n");
+ fprintf(e, " -f, --force\n");
+ if (verbose)
+ fprintf(e, "\n Override command safe guards.\n\n");
+
+ fprintf(e, " -l, --logical\n");
+ if (verbose)
+ fprintf(e, "\n Specifies the partition entry is a logical"
+ " partition instead of a\n data partition.\n\n");
+
+ fprintf(e, " -v, --verbose\n");
+ if (verbose)
+ fprintf(e, "\n Write progress messages to stdout\n\n");
+
+ fprintf(e, " -d, --debug\n");
+ if (verbose)
+ fprintf(e, "\n Write debug messages to stdout\n\n");
+
+ fprintf(e, " -h, --help\n");
+ if (verbose)
+ fprintf(e, "\n Write this help text to stdout and exit\n");
+
+ fprintf(e, "\n");
+
+ fprintf(e, "Report bugs to <https://bugzilla.linux.ibm.com/>"
+ " (vendor='MCP for FSP*')\n");
+}
+
+static int process_argument(args_t * args, int opt, const char *optarg)
+{
+ assert(args != NULL);
+
+ switch (opt) {
+ case c_CREATE: /* create */
+ case c_ADD: /* add */
+ case c_DELETE: /* delete */
+ case c_LIST: /* list */
+ case c_TRUNC: /* trunc */
+ case c_ERASE: /* erase */
+ case c_USER: /* user */
+ if (args->cmd != c_ERROR) {
+ UNEXPECTED("commands '%c' and '%c' are mutually "
+ "exclusive", args->cmd, opt);
+ return -1;
+ }
+
+ args->cmd = (cmd_t) opt;
+ if (args->cmd == c_USER)
+ args->user = strdup(optarg);
+ break;
+ case o_POFFSET: /* partition-offset */
+ args->poffset = strdup(optarg);
+ break;
+ case o_TARGET: /* target */
+ args->target = strdup(optarg);
+ break;
+ case o_NAME: /* name */
+ args->name = strdup(optarg);
+ break;
+ case o_OFFSET: /* offset */
+ args->offset = strdup(optarg);
+ break;
+ case o_SIZE: /* size */
+ args->size = strdup(optarg);
+ break;
+ case o_BLOCK: /* block */
+ args->block = strdup(optarg);
+ break;
+ case o_VALUE: /* value */
+ args->value = strdup(optarg);
+ break;
+ case o_FLAGS: /* flags */
+ args->flags = strdup(optarg);
+ break;
+ case o_PAD: /* pad */
+ args->pad = strdup(optarg);
+ break;
+ case f_FORCE: /* force */
+ args->force = (flag_t) opt;
+ break;
+ case f_LOGICAL: /* logical */
+ args->logical = (flag_t) opt;
+ break;
+ case f_VERBOSE: /* verbose */
+ args->verbose = (flag_t) opt;
+ break;
+ case f_DEBUG: /* debug */
+ args->debug = (flag_t) opt;
+ break;
+ case f_HELP: /* help */
+ usage(true);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?': /* error */
+ default:
+ usage(false);
+ UNEXPECTED("unknown option '%c', please see --help for "
+ "details\n", opt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_option(args_t * args, const char *opt)
+{
+ assert(args != NULL);
+ assert(opt != NULL);
+
+ if (args->opt_sz <= args->opt_nr) {
+ size_t size = sizeof(*args->opt);
+
+ args->opt_sz += 5;
+ args->opt = (const char **)realloc(args->opt,
+ size * args->opt_sz);
+ if (args->opt == NULL) {
+ ERRNO(errno);
+ return -1;
+ }
+
+ memset(args->opt + args->opt_nr, 0,
+ size * (args->opt_sz - args->opt_nr));
+ }
+
+ args->opt[args->opt_nr] = strdup(opt);
+ args->opt_nr++;
+
+ return 0;
+}
+
+static int validate_args(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->cmd == c_ERROR) {
+ usage(false);
+ UNEXPECTED("no command specified, please "
+ "see --help for details\n");
+ return -1;
+ }
+
+ if (args->poffset == NULL) {
+ UNEXPECTED("--partition-offset is required for all '%s' "
+ "commands", args->short_name);
+ return -1;
+ }
+
+ if (args->target == NULL) {
+ UNEXPECTED("--target is required for all '%s' commands",
+ args->short_name);
+ return -1;
+ }
+
+ #define REQUIRED(name,cmd) ({ \
+ if (args->name == NULL) { \
+ UNEXPECTED("--%s is required for the --%s command", \
+ #name, #cmd); \
+ return -1; \
+ } \
+ })
+
+ #define UNSUPPORTED(name,cmd) ({ \
+ if (args->name != NULL) { \
+ UNEXPECTED("--%s is unsupported for the --%s command", \
+ #name, #cmd); \
+ return -1; \
+ } \
+ })
+
+ if (args->cmd == c_CREATE) {
+ REQUIRED(size, create);
+ REQUIRED(block, create);
+ REQUIRED(target, create);
+
+ UNSUPPORTED(offset, create);
+ UNSUPPORTED(flags, create);
+ UNSUPPORTED(value, create);
+ UNSUPPORTED(pad, create);
+ } else if (args->cmd == c_ADD) {
+ REQUIRED(name, add);
+ REQUIRED(flags, add);
+
+ if (args->logical != f_LOGICAL) {
+ REQUIRED(size, add);
+ REQUIRED(offset, add);
+ }
+
+ UNSUPPORTED(block, add);
+ UNSUPPORTED(value, add);
+ } else if (args->cmd == c_DELETE) {
+ REQUIRED(name, delete);
+
+ UNSUPPORTED(size, delete);
+ UNSUPPORTED(offset, delete);
+ UNSUPPORTED(block, delete);
+ UNSUPPORTED(flags, delete);
+ UNSUPPORTED(value, delete);
+ UNSUPPORTED(pad, delete);
+ } else if (args->cmd == c_LIST) {
+ UNSUPPORTED(size, list);
+ UNSUPPORTED(offset, list);
+ UNSUPPORTED(block, list);
+ UNSUPPORTED(flags, list);
+ UNSUPPORTED(value, list);
+ UNSUPPORTED(pad, list);
+ } else if (args->cmd == c_TRUNC) {
+ REQUIRED(name, trunc);
+
+ UNSUPPORTED(offset, trunc);
+ UNSUPPORTED(block, trunc);
+ UNSUPPORTED(flags, trunc);
+ UNSUPPORTED(value, trunc);
+ UNSUPPORTED(pad, trunc);
+ } else if (args->cmd == c_ERASE) {
+ REQUIRED(name, erase);
+
+ UNSUPPORTED(size, erase);
+ UNSUPPORTED(offset, erase);
+ UNSUPPORTED(flags, erase);
+ UNSUPPORTED(value, erase);
+ } else if (args->cmd == c_USER) {
+ REQUIRED(name, user);
+
+ UNSUPPORTED(size, user);
+ UNSUPPORTED(offset, user);
+ UNSUPPORTED(block, user);
+ UNSUPPORTED(flags, user);
+ UNSUPPORTED(pad, user);
+ } else {
+ UNEXPECTED("invalid command '%c'", args->cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int process_args(args_t * args)
+{
+ assert(args != NULL);
+ int rc = 0;
+
+ switch (args->cmd) {
+ case c_CREATE:
+ rc = command_create(args);
+ break;
+ case c_ADD:
+ rc = command_add(args);
+ if (rc < 0)
+ break;
+ if (args->pad != NULL)
+ rc = command_erase(args);
+ break;
+ case c_DELETE:
+ rc = command_delete(args);
+ break;
+ case c_LIST:
+ rc = command_list(args);
+ break;
+ case c_TRUNC:
+ rc = command_trunc(args);
+ break;
+ case c_ERASE:
+ rc = command_erase(args);
+ break;
+ case c_USER:
+ rc = command_user(args);
+ break;
+ default:
+ UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void args_dump(args_t * args)
+{
+ assert(args != NULL);
+
+ if (args->short_name != NULL)
+ printf("short_name[%s]\n", args->short_name);
+ if (args->cmd != 0)
+ printf("cmd[%c]\n", args->cmd);
+ if (args->path != NULL)
+ printf("path[%s]\n", args->path);
+ if (args->target != NULL)
+ printf("target[%s]\n", args->target);
+ if (args->poffset != NULL)
+ printf("poffset[%s]\n", args->poffset);
+ if (args->name != NULL)
+ printf("name[%s]\n", args->name);
+ if (args->offset != NULL)
+ printf("offset[%s]\n", args->offset);
+ if (args->size != NULL)
+ printf("size[%s]\n", args->size);
+ if (args->block != NULL)
+ printf("block[%s]\n", args->block);
+ if (args->flags != NULL)
+ printf("flags[%s]\n", args->flags);
+ if (args->value != NULL)
+ printf("value[%s]\n", args->value);
+ if (args->pad != NULL)
+ printf("pad[%s]\n", args->pad);
+ for (int i = 0; i < args->opt_nr; i++) {
+ if (args->opt[i] != NULL)
+ printf("opt%d[%s]\n", i, args->opt[i]);
+ }
+ if (args->force != 0)
+ printf("force[%c]\n", args->force);
+ if (args->protected != 0)
+ printf("protected[%c]\n", args->protected);
+ if (args->logical != 0)
+ printf("logical[%c]\n", args->logical);
+ if (args->verbose != 0)
+ printf("verbose[%c]\n", args->verbose);
+ if (args->verbose != 0)
+ printf("debug[%c]\n", args->debug);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_opt[] = {
+ /* commands */
+ {"create", no_argument, NULL, c_CREATE},
+ {"add", no_argument, NULL, c_ADD},
+ {"delete", no_argument, NULL, c_DELETE},
+ {"list", no_argument, NULL, c_LIST},
+ {"trunc", no_argument, NULL, c_TRUNC},
+ {"erase", no_argument, NULL, c_ERASE},
+ {"user", required_argument, NULL, c_USER},
+ /* options */
+ {"partition-offset", required_argument, NULL, o_POFFSET},
+ {"target", required_argument, NULL, o_TARGET},
+ {"name", required_argument, NULL, o_NAME},
+ {"offset", required_argument, NULL, o_OFFSET},
+ {"size", required_argument, NULL, o_SIZE},
+ {"block-size", required_argument, NULL, o_BLOCK},
+ {"value", required_argument, NULL, o_VALUE},
+ {"flags", required_argument, NULL, o_FLAGS},
+ {"pad", required_argument, NULL, o_PAD},
+ /* flags */
+ {"force", no_argument, NULL, f_FORCE},
+ {"protected", no_argument, NULL, f_PROTECTED},
+ {"logical", no_argument, NULL, f_LOGICAL},
+ {"verbose", no_argument, NULL, f_VERBOSE},
+ {"debug", no_argument, NULL, f_DEBUG},
+ {"help", no_argument, NULL, f_HELP},
+ {0, 0, 0, 0}
+ };
+
+ static const char *short_opt;
+ short_opt = "CADLTEU:p:t:n:o:s:b:u:g:a:frlvdh";
+
+ int rc = EXIT_FAILURE;
+
+ setlinebuf(stdout);
+
+ if (argc == 1)
+ usage(false), exit(rc);
+
+ int opt = 0, idx = 0;
+ while ((opt = getopt_long(argc, argv, short_opt, long_opt, &idx)) != -1)
+ if (process_argument(&args, opt, optarg) < 0)
+ goto error;
+
+ /* getopt_long doesn't know what to do with orphans, */
+ /* so we'll scoop them up here, and deal with them later */
+
+ while (optind < argc)
+ if (process_option(&args, argv[optind++]) < 0)
+ goto error;
+
+ if (args.debug == f_DEBUG)
+ args_dump(&args);
+
+ if (validate_args(&args) < 0)
+ goto error;
+ if (process_args(&args) < 0)
+ goto error;
+
+ rc = EXIT_SUCCESS;
+
+ if (false) {
+ err_t *err;
+error:
+ err = err_get();
+ assert(err != NULL);
+
+ fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n",
+ program_invocation_short_name,
+ err_type_name(err), err_file(err), err_line(err),
+ err_code(err), err_size(err), (char *)err_data(err));
+ }
+
+ return rc;
+}
+
+static void __ctor__(void) __constructor;
+static void __ctor__(void)
+{
+ page_size = sysconf(_SC_PAGESIZE);
+
+ args.short_name = program_invocation_short_name;
+ args.poffset = "0x3F0000,0x7F0000";
+ args.target = "/dev/mtdblock/sfc.of";
+ args.name = ".*";
+}
diff --git a/fpart/src/main.h b/fpart/src/main.h
new file mode 100644
index 0000000..eeb3b39
--- /dev/null
+++ b/fpart/src/main.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * 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
+ */
+
+/*
+ * File: main.h
+ * Author: Shaun Wetzstein <shaun@us.ibm.com>
+ * Descr: cmdline tool for libffs.so
+ * Date: 05/12/2012
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include <stdio.h>
+
+#include <ffs/libffs.h>
+
+#define FPART_MAJOR 0x01
+#define FPART_MINOR 0x00
+#define FPART_PATCH 0x00
+
+typedef enum {
+ c_ERROR = 0,
+ c_CREATE = 'C',
+ c_ADD = 'A',
+ c_DELETE = 'D',
+ c_ERASE = 'E',
+ c_LIST = 'L',
+ c_TRUNC = 'T',
+ c_USER = 'U',
+} cmd_t;
+
+typedef enum {
+ o_ERROR = 0,
+ o_POFFSET = 'p',
+ o_TARGET = 't',
+ o_NAME = 'n',
+ o_OFFSET = 'o',
+ o_SIZE = 's',
+ o_BLOCK = 'b',
+ o_VALUE = 'u',
+ o_FLAGS = 'g',
+ o_PAD = 'a',
+} option_t;
+
+typedef enum {
+ f_ERROR = 0,
+ f_FORCE = 'f',
+ f_PROTECTED = 'r',
+ f_LOGICAL = 'l',
+ f_VERBOSE = 'v',
+ f_DEBUG = 'd',
+ f_HELP = 'h',
+} flag_t;
+
+typedef struct {
+ const char *short_name;
+
+ /* target */
+ const char *path;
+
+ /* command */
+ cmd_t cmd;
+
+ /* options */
+ const char *name, *target;
+ const char *offset, *poffset;
+ const char *size, *block;
+ const char *user, *value;
+ const char *flags, *pad;
+
+ /* flags */
+ flag_t force, logical;
+ flag_t verbose, debug;
+ flag_t protected;
+
+ const char **opt;
+ int opt_sz, opt_nr;
+} args_t;
+
+typedef struct ffs_entry_node ffs_entry_node_t;
+struct ffs_entry_node {
+ list_node_t node;
+ ffs_entry_t entry;
+};
+
+extern args_t args;
+extern size_t page_size;
+
+extern int parse_offset(const char *, off_t *);
+extern int parse_size(const char *, size_t *);
+extern int parse_number(const char *, size_t *);
+
+extern bool check_extension(const char *, const char *);
+extern int create_regular_file(const char *, size_t, char);
+extern FILE *fopen_generic(const char *, const char *, int);
+
+extern int command(args_t *, int (*)(args_t *, off_t));
+extern int verify_operation(const char *, ffs_t *, ffs_entry_t *,
+ ffs_t *, ffs_entry_t *);
+
+extern int command_create(args_t *);
+extern int command_add(args_t *);
+extern int command_delete(args_t *);
+extern int command_list(args_t *);
+extern int command_trunc(args_t *);
+extern int command_erase(args_t *);
+extern int command_user(args_t *);
+
+#endif /* __MAIN_H__ */
diff --git a/fpart/x86/Makefile b/fpart/x86/Makefile
new file mode 100644
index 0000000..9ce2196
--- /dev/null
+++ b/fpart/x86/Makefile
@@ -0,0 +1,7 @@
+DEPTH = ..
+
+ARCH=x86
+FPART_INSTALL = $(INST_USR_X86)
+FPART_INSTALL_TEST = $(INST_TESTS_X86)
+
+include ../Rules.mk
OpenPOWER on IntegriCloud