]> git.sur5r.net Git - freertos/commitdiff
Update Reliance Edge fail safe file system to the latest version.
authorrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Tue, 24 Jan 2017 00:20:35 +0000 (00:20 +0000)
committerrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>
Tue, 24 Jan 2017 00:20:35 +0000 (00:20 +0000)
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2484 1d2547de-c912-0410-9cb9-b8ca96c0e9e2

30 files changed:
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.c
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/ConfigurationFiles/redconf.h
FreeRTOS-Plus/Demo/FreeRTOS_Plus_Reliance_Edge_and_CLI_Windows_Simulator/File-Related-CLI-commands.c
FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/Include/trcHardwarePort.h
FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/trcBase.c
FreeRTOS-Plus/Source/Reliance-Edge/CREDITS.txt
FreeRTOS-Plus/Source/Reliance-Edge/LICENSE.txt
FreeRTOS-Plus/Source/Reliance-Edge/README.md
FreeRTOS-Plus/Source/Reliance-Edge/README.txt
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/core.c
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/dir.c
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/imap.c
FreeRTOS-Plus/Source/Reliance-Edge/core/driver/volume.c
FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.md
FreeRTOS-Plus/Source/Reliance-Edge/doc/release_notes.txt
FreeRTOS-Plus/Source/Reliance-Edge/include/redconfigchk.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redexclude.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redfs.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redtests.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redtoolcmn.h
FreeRTOS-Plus/Source/Reliance-Edge/include/redtools.h [new file with mode: 0644]
FreeRTOS-Plus/Source/Reliance-Edge/include/redver.h
FreeRTOS-Plus/Source/Reliance-Edge/os/freertos/services/osbdev.c
FreeRTOS-Plus/Source/Reliance-Edge/posix/path.c
FreeRTOS-Plus/Source/Reliance-Edge/posix/posix.c
FreeRTOS-Plus/Source/Reliance-Edge/tests/posix/fsstress.c
FreeRTOS-Plus/Source/Reliance-Edge/tests/util/atoi.c
FreeRTOS-Plus/Source/Reliance-Edge/tests/util/math.c
FreeRTOS-Plus/Source/Reliance-Edge/tests/util/printf.c
FreeRTOS-Plus/Source/Reliance-Edge/tests/util/rand.c

index 65feb5546701fd388578df511238dab061f478b0..129697fb887cfdf9e456c9dcde033caaece64885 100644 (file)
@@ -1,5 +1,7 @@
 /*  THIS FILE WAS GENERATED BY THE DATALIGHT RELIANCE EDGE CONFIGURATION\r
     UTILITY.  DO NOT MODIFY.\r
+\r
+    Generated by configuration utility version 2.0\r
 */\r
 /** @file\r
 */\r
@@ -11,5 +13,5 @@
 \r
 const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] =\r
 {\r
-    { 512U, 65536U, false, 256U, 0, "" }\r
+    { 512U, 65536U, false, 256U, 0U, "" }\r
 };\r
index 0805a6cdc128861e05cfa25b94db1a19e3d2596c..c0d2f2a6b2fde0a7f09b62f380b9817eb2d50a8b 100644 (file)
@@ -1,5 +1,7 @@
 /*  THIS FILE WAS GENERATED BY THE DATALIGHT RELIANCE EDGE CONFIGURATION\r
     UTILITY.  DO NOT MODIFY.\r
+\r
+    Generated by configuration utility version 2.0\r
 */\r
 /** @file\r
 */\r
 \r
 #define REDCONF_IMAP_EXTERNAL 1\r
 \r
+#define REDCONF_DISCARDS 0\r
+\r
 #define REDCONF_IMAGE_BUILDER 0\r
 \r
 #define REDCONF_CHECKER 0\r
 \r
+#define RED_CONFIG_UTILITY_VERSION 0x2000000U\r
+\r
+#define RED_CONFIG_MINCOMPAT_VER 0x1000200U\r
+\r
 #endif\r
index 47102ab92c4674a94592799a90943646957005d0..209b2e3c35a219e40bd7c873e358dc1016022032 100644 (file)
@@ -1563,5 +1563,5 @@ const char *pcAttrib;
 \r
        /* Create a string that includes the file name, the file size and the\r
        attributes string. */\r
-       sprintf( pcBuffer, "%s [%s] [size=%d]", pxDirent->d_name, pcAttrib, pxDirent->d_stat.st_size );\r
+       sprintf( pcBuffer, "%s [%s] [size=%lld]", pxDirent->d_name, pcAttrib, pxDirent->d_stat.st_size );\r
 }\r
index ecbf4975212e6236ee0b08f69c4102fcd07628b7..50177daeabd50daaec549cceec1ce8e825db4bed 100644 (file)
@@ -45,9 +45,6 @@
 /* If Win32 port */\r
 #ifdef WIN32\r
 \r
-       #undef _WIN32_WINNT\r
-       #define _WIN32_WINNT 0x0600\r
-\r
        /* Standard includes. */\r
        #include <stdio.h>\r
        #include <windows.h>\r
     #define HWTC_COUNT (*((uint32_t*)0xE000E018))\r
     #define HWTC_PERIOD ((*(uint32_t*)0xE000E014) + 1)\r
     #define HWTC_DIVISOR 2\r
-       \r
+\r
     #define IRQ_PRIORITY_ORDER 0 // lower IRQ priority values are more significant\r
-       \r
+\r
 #elif (SELECTED_PORT == PORT_Renesas_RX600)\r
 \r
        #include "iodefine.h"\r
index 47f025854727330adc64257e1324c6d8f182c9ac..8534c118e9b422fd26da8619874267ee73ffeeaa 100644 (file)
@@ -222,7 +222,7 @@ static void vInitStartMarkers()
 volatile int recorder_busy = 0;\r
 \r
 /* Gives the last error message of the recorder. NULL if no error message. */\r
-char* traceErrorMessage = NULL;\r
+const char* traceErrorMessage = NULL;\r
 \r
 void* xTraceNextFreeEventBufferSlot(void)\r
 {\r
@@ -468,7 +468,7 @@ void vTraceError(const char* msg)
 \r
        if (traceErrorMessage == NULL)\r
        {\r
-               traceErrorMessage = (char*)msg;\r
+               traceErrorMessage = msg;\r
                (void)strncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);\r
                RecorderDataPtr->internalErrorOccured = 1;\r
        }\r
index ad14dda000cc52ee8d9f6e1089d82e53564df01c..7b2f6080764c746bec9baa4bbae0068ba6e4ca9a 100644 (file)
@@ -1,18 +1,21 @@
-Reliance Edge Credits\r
-=====================\r
-\r
-This is a list (or partial list) of people who have made non-trivial or\r
-noteworthy contributions to the Reliance Edge project.  It is sorted by name.\r
-Entries are formatted as below:\r
-\r
-Real Name (githubaccount)\r
-Short description of how Real Name contributed to Reliance Edge.\r
-\r
-The real name may be withheld by request and the GitHub account name might be\r
-missing if the contributor does not use GitHub.\r
-\r
-Credits\r
--------\r
-\r
-None yet! ;)\r
-\r
+Reliance Edge Credits
+=====================
+
+This is a list (or partial list) of people who have made non-trivial or
+noteworthy contributions to the Reliance Edge project.  It is sorted by name.
+Entries are formatted as below:
+
+Real Name (githubaccount)
+Short description of how Real Name contributed to Reliance Edge.
+
+The real name may be withheld by request and the GitHub account name might be
+missing if the contributor does not use GitHub.
+
+Credits
+-------
+
+Jean-Christophe Dubois (jcdubois)
+Created a user-space Linux port of Reliance Edge, ported the open-source host
+tools (including the formatter and image builder) to Linux, and created a FUSE
+(File System in User Space) implementation for Reliance Edge.
+
index 6eb1fc85586c2121665625825ac88cb73ef15313..1f963da0d1ca40ea60730c03befcbbc6771740c3 100644 (file)
-                    GNU GENERAL PUBLIC LICENSE\r
-                       Version 2, June 1991\r
-\r
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
- Everyone is permitted to copy and distribute verbatim copies\r
- of this license document, but changing it is not allowed.\r
-\r
-                            Preamble\r
-\r
-  The licenses for most software are designed to take away your\r
-freedom to share and change it.  By contrast, the GNU General Public\r
-License is intended to guarantee your freedom to share and change free\r
-software--to make sure the software is free for all its users.  This\r
-General Public License applies to most of the Free Software\r
-Foundation's software and to any other program whose authors commit to\r
-using it.  (Some other Free Software Foundation software is covered by\r
-the GNU Lesser General Public License instead.)  You can apply it to\r
-your programs, too.\r
-\r
-  When we speak of free software, we are referring to freedom, not\r
-price.  Our General Public Licenses are designed to make sure that you\r
-have the freedom to distribute copies of free software (and charge for\r
-this service if you wish), that you receive source code or can get it\r
-if you want it, that you can change the software or use pieces of it\r
-in new free programs; and that you know you can do these things.\r
-\r
-  To protect your rights, we need to make restrictions that forbid\r
-anyone to deny you these rights or to ask you to surrender the rights.\r
-These restrictions translate to certain responsibilities for you if you\r
-distribute copies of the software, or if you modify it.\r
-\r
-  For example, if you distribute copies of such a program, whether\r
-gratis or for a fee, you must give the recipients all the rights that\r
-you have.  You must make sure that they, too, receive or can get the\r
-source code.  And you must show them these terms so they know their\r
-rights.\r
-\r
-  We protect your rights with two steps: (1) copyright the software, and\r
-(2) offer you this license which gives you legal permission to copy,\r
-distribute and/or modify the software.\r
-\r
-  Also, for each author's protection and ours, we want to make certain\r
-that everyone understands that there is no warranty for this free\r
-software.  If the software is modified by someone else and passed on, we\r
-want its recipients to know that what they have is not the original, so\r
-that any problems introduced by others will not reflect on the original\r
-authors' reputations.\r
-\r
-  Finally, any free program is threatened constantly by software\r
-patents.  We wish to avoid the danger that redistributors of a free\r
-program will individually obtain patent licenses, in effect making the\r
-program proprietary.  To prevent this, we have made it clear that any\r
-patent must be licensed for everyone's free use or not licensed at all.\r
-\r
-  The precise terms and conditions for copying, distribution and\r
-modification follow.\r
-\r
-                    GNU GENERAL PUBLIC LICENSE\r
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
-\r
-  0. This License applies to any program or other work which contains\r
-a notice placed by the copyright holder saying it may be distributed\r
-under the terms of this General Public License.  The "Program", below,\r
-refers to any such program or work, and a "work based on the Program"\r
-means either the Program or any derivative work under copyright law:\r
-that is to say, a work containing the Program or a portion of it,\r
-either verbatim or with modifications and/or translated into another\r
-language.  (Hereinafter, translation is included without limitation in\r
-the term "modification".)  Each licensee is addressed as "you".\r
-\r
-Activities other than copying, distribution and modification are not\r
-covered by this License; they are outside its scope.  The act of\r
-running the Program is not restricted, and the output from the Program\r
-is covered only if its contents constitute a work based on the\r
-Program (independent of having been made by running the Program).\r
-Whether that is true depends on what the Program does.\r
-\r
-  1. You may copy and distribute verbatim copies of the Program's\r
-source code as you receive it, in any medium, provided that you\r
-conspicuously and appropriately publish on each copy an appropriate\r
-copyright notice and disclaimer of warranty; keep intact all the\r
-notices that refer to this License and to the absence of any warranty;\r
-and give any other recipients of the Program a copy of this License\r
-along with the Program.\r
-\r
-You may charge a fee for the physical act of transferring a copy, and\r
-you may at your option offer warranty protection in exchange for a fee.\r
-\r
-  2. You may modify your copy or copies of the Program or any portion\r
-of it, thus forming a work based on the Program, and copy and\r
-distribute such modifications or work under the terms of Section 1\r
-above, provided that you also meet all of these conditions:\r
-\r
-    a) You must cause the modified files to carry prominent notices\r
-    stating that you changed the files and the date of any change.\r
-\r
-    b) You must cause any work that you distribute or publish, that in\r
-    whole or in part contains or is derived from the Program or any\r
-    part thereof, to be licensed as a whole at no charge to all third\r
-    parties under the terms of this License.\r
-\r
-    c) If the modified program normally reads commands interactively\r
-    when run, you must cause it, when started running for such\r
-    interactive use in the most ordinary way, to print or display an\r
-    announcement including an appropriate copyright notice and a\r
-    notice that there is no warranty (or else, saying that you provide\r
-    a warranty) and that users may redistribute the program under\r
-    these conditions, and telling the user how to view a copy of this\r
-    License.  (Exception: if the Program itself is interactive but\r
-    does not normally print such an announcement, your work based on\r
-    the Program is not required to print an announcement.)\r
-\r
-These requirements apply to the modified work as a whole.  If\r
-identifiable sections of that work are not derived from the Program,\r
-and can be reasonably considered independent and separate works in\r
-themselves, then this License, and its terms, do not apply to those\r
-sections when you distribute them as separate works.  But when you\r
-distribute the same sections as part of a whole which is a work based\r
-on the Program, the distribution of the whole must be on the terms of\r
-this License, whose permissions for other licensees extend to the\r
-entire whole, and thus to each and every part regardless of who wrote it.\r
-\r
-Thus, it is not the intent of this section to claim rights or contest\r
-your rights to work written entirely by you; rather, the intent is to\r
-exercise the right to control the distribution of derivative or\r
-collective works based on the Program.\r
-\r
-In addition, mere aggregation of another work not based on the Program\r
-with the Program (or with a work based on the Program) on a volume of\r
-a storage or distribution medium does not bring the other work under\r
-the scope of this License.\r
-\r
-  3. You may copy and distribute the Program (or a work based on it,\r
-under Section 2) in object code or executable form under the terms of\r
-Sections 1 and 2 above provided that you also do one of the following:\r
-\r
-    a) Accompany it with the complete corresponding machine-readable\r
-    source code, which must be distributed under the terms of Sections\r
-    1 and 2 above on a medium customarily used for software interchange; or,\r
-\r
-    b) Accompany it with a written offer, valid for at least three\r
-    years, to give any third party, for a charge no more than your\r
-    cost of physically performing source distribution, a complete\r
-    machine-readable copy of the corresponding source code, to be\r
-    distributed under the terms of Sections 1 and 2 above on a medium\r
-    customarily used for software interchange; or,\r
-\r
-    c) Accompany it with the information you received as to the offer\r
-    to distribute corresponding source code.  (This alternative is\r
-    allowed only for noncommercial distribution and only if you\r
-    received the program in object code or executable form with such\r
-    an offer, in accord with Subsection b above.)\r
-\r
-The source code for a work means the preferred form of the work for\r
-making modifications to it.  For an executable work, complete source\r
-code means all the source code for all modules it contains, plus any\r
-associated interface definition files, plus the scripts used to\r
-control compilation and installation of the executable.  However, as a\r
-special exception, the source code distributed need not include\r
-anything that is normally distributed (in either source or binary\r
-form) with the major components (compiler, kernel, and so on) of the\r
-operating system on which the executable runs, unless that component\r
-itself accompanies the executable.\r
-\r
-If distribution of executable or object code is made by offering\r
-access to copy from a designated place, then offering equivalent\r
-access to copy the source code from the same place counts as\r
-distribution of the source code, even though third parties are not\r
-compelled to copy the source along with the object code.\r
-\r
-  4. You may not copy, modify, sublicense, or distribute the Program\r
-except as expressly provided under this License.  Any attempt\r
-otherwise to copy, modify, sublicense or distribute the Program is\r
-void, and will automatically terminate your rights under this License.\r
-However, parties who have received copies, or rights, from you under\r
-this License will not have their licenses terminated so long as such\r
-parties remain in full compliance.\r
-\r
-  5. You are not required to accept this License, since you have not\r
-signed it.  However, nothing else grants you permission to modify or\r
-distribute the Program or its derivative works.  These actions are\r
-prohibited by law if you do not accept this License.  Therefore, by\r
-modifying or distributing the Program (or any work based on the\r
-Program), you indicate your acceptance of this License to do so, and\r
-all its terms and conditions for copying, distributing or modifying\r
-the Program or works based on it.\r
-\r
-  6. Each time you redistribute the Program (or any work based on the\r
-Program), the recipient automatically receives a license from the\r
-original licensor to copy, distribute or modify the Program subject to\r
-these terms and conditions.  You may not impose any further\r
-restrictions on the recipients' exercise of the rights granted herein.\r
-You are not responsible for enforcing compliance by third parties to\r
-this License.\r
-\r
-  7. If, as a consequence of a court judgment or allegation of patent\r
-infringement or for any other reason (not limited to patent issues),\r
-conditions are imposed on you (whether by court order, agreement or\r
-otherwise) that contradict the conditions of this License, they do not\r
-excuse you from the conditions of this License.  If you cannot\r
-distribute so as to satisfy simultaneously your obligations under this\r
-License and any other pertinent obligations, then as a consequence you\r
-may not distribute the Program at all.  For example, if a patent\r
-license would not permit royalty-free redistribution of the Program by\r
-all those who receive copies directly or indirectly through you, then\r
-the only way you could satisfy both it and this License would be to\r
-refrain entirely from distribution of the Program.\r
-\r
-If any portion of this section is held invalid or unenforceable under\r
-any particular circumstance, the balance of the section is intended to\r
-apply and the section as a whole is intended to apply in other\r
-circumstances.\r
-\r
-It is not the purpose of this section to induce you to infringe any\r
-patents or other property right claims or to contest validity of any\r
-such claims; this section has the sole purpose of protecting the\r
-integrity of the free software distribution system, which is\r
-implemented by public license practices.  Many people have made\r
-generous contributions to the wide range of software distributed\r
-through that system in reliance on consistent application of that\r
-system; it is up to the author/donor to decide if he or she is willing\r
-to distribute software through any other system and a licensee cannot\r
-impose that choice.\r
-\r
-This section is intended to make thoroughly clear what is believed to\r
-be a consequence of the rest of this License.\r
-\r
-  8. If the distribution and/or use of the Program is restricted in\r
-certain countries either by patents or by copyrighted interfaces, the\r
-original copyright holder who places the Program under this License\r
-may add an explicit geographical distribution limitation excluding\r
-those countries, so that distribution is permitted only in or among\r
-countries not thus excluded.  In such case, this License incorporates\r
-the limitation as if written in the body of this License.\r
-\r
-  9. The Free Software Foundation may publish revised and/or new versions\r
-of the General Public License from time to time.  Such new versions will\r
-be similar in spirit to the present version, but may differ in detail to\r
-address new problems or concerns.\r
-\r
-Each version is given a distinguishing version number.  If the Program\r
-specifies a version number of this License which applies to it and "any\r
-later version", you have the option of following the terms and conditions\r
-either of that version or of any later version published by the Free\r
-Software Foundation.  If the Program does not specify a version number of\r
-this License, you may choose any version ever published by the Free Software\r
-Foundation.\r
-\r
-  10. If you wish to incorporate parts of the Program into other free\r
-programs whose distribution conditions are different, write to the author\r
-to ask for permission.  For software which is copyrighted by the Free\r
-Software Foundation, write to the Free Software Foundation; we sometimes\r
-make exceptions for this.  Our decision will be guided by the two goals\r
-of preserving the free status of all derivatives of our free software and\r
-of promoting the sharing and reuse of software generally.\r
-\r
-                            NO WARRANTY\r
-\r
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\r
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\r
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\r
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
-REPAIR OR CORRECTION.\r
-\r
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
-POSSIBILITY OF SUCH DAMAGES.\r
-\r
-                     END OF TERMS AND CONDITIONS\r
-\r
-            How to Apply These Terms to Your New Programs\r
-\r
-  If you develop a new program, and you want it to be of the greatest\r
-possible use to the public, the best way to achieve this is to make it\r
-free software which everyone can redistribute and change under these terms.\r
-\r
-  To do so, attach the following notices to the program.  It is safest\r
-to attach them to the start of each source file to most effectively\r
-convey the exclusion of warranty; and each file should have at least\r
-the "copyright" line and a pointer to where the full notice is found.\r
-\r
-    <one line to give the program's name and a brief idea of what it does.>\r
-    Copyright (C) <year>  <name of author>\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; either version 2 of the License, or\r
-    (at your option) any later version.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-\r
-Also add information on how to contact you by electronic and paper mail.\r
-\r
-If the program is interactive, make it output a short notice like this\r
-when it starts in an interactive mode:\r
-\r
-    Gnomovision version 69, Copyright (C) year name of author\r
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
-    This is free software, and you are welcome to redistribute it\r
-    under certain conditions; type `show c' for details.\r
-\r
-The hypothetical commands `show w' and `show c' should show the appropriate\r
-parts of the General Public License.  Of course, the commands you use may\r
-be called something other than `show w' and `show c'; they could even be\r
-mouse-clicks or menu items--whatever suits your program.\r
-\r
-You should also get your employer (if you work as a programmer) or your\r
-school, if any, to sign a "copyright disclaimer" for the program, if\r
-necessary.  Here is a sample; alter the names:\r
-\r
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
-\r
-  <signature of Ty Coon>, 1 April 1989\r
-  Ty Coon, President of Vice\r
-\r
-This General Public License does not permit incorporating your program into\r
-proprietary programs.  If your program is a subroutine library, you may\r
-consider it more useful to permit linking proprietary applications with the\r
-library.  If this is what you want to do, use the GNU Lesser General\r
-Public License instead of this License.\r
-\r
+                    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.
+
index aaff78437af0bb63bd89ef8c0b2c685f6eba5e7e..0c2e22833a01ba3dba9fc451eb20d8bbcb6cbfe0 100644 (file)
-# Reliance Edge\r
-\r
-Reliance Edge is a small, portable, highly reliable power-fail safe file system\r
-for resource-constrained embedded systems like microcontrollers.  It is written\r
-in C and provides a familiar POSIX-like file system API, making it easy to use\r
-in your application; or an alternate minimalist API if your application has\r
-simple storage needs.  Reliance Edge is highly configurable and can be tuned to\r
-the precise needs of your application.\r
-\r
-## Documentation\r
-\r
-The complete documentation for Reliance Edge is distributed separately.  It\r
-includes an API reference and detailed discussions of various aspects of using\r
-Reliance Edge, including porting, building, configuring, and testing.  This\r
-complete documentation, called the _Developer's Guide_, can be obtained for free\r
-from here:\r
-\r
-<http://www.datalight.com/reliance-edge>\r
-\r
-In addition this README, see [doc/release_notes.md](doc/release_notes.md) for a\r
-list of updates to Reliance Edge and a list of known issues.  There is also a\r
-quick-start guide in the doc/ directory that describes step-by-step how to\r
-compile and run Reliance Edge in a simulated Windows environment.\r
-\r
-## Why Use Reliance Edge?\r
-\r
-Reliance Edge is ideal for small embedded systems with data storage\r
-requirements, especially if there is a chance of sudden power loss or other\r
-system failures.  Compared to "raw" disk access, using a file system like\r
-Reliance Edge removes the burden of tracking which sectors belong to which\r
-objects, and allows data to be updated more reliably.  Compared to the FAT file\r
-system, using Reliance Edge eliminates the possibility that file system data\r
-will be left in an inconsistent state, corrupting the disk; Reliance Edge does\r
-not need a fsck/CHKDSK utility.  Compared to journaling file systems, Reliance\r
-Edge has less overhead and results in less storage media wear for longer device\r
-lifetimes.\r
-\r
-Reliance Edge uses a unique transactional model that not only prevents file\r
-system corruption but also allows a set of changes to be made in an atomic "all\r
-or nothing" fashion.  This is very useful for applications that make sets of\r
-interrelated changes.  By using the features of Reliance Edge, a set of changes\r
-can be incorporated into a single atomic transaction, which is committed in its\r
-entirety or not at all even if interrupted by power loss; this means the\r
-application does not need code to recover from partially-finished updates.\r
-\r
-## Hardware\r
-\r
-The typical hardware for Reliance Edge is a 32-bit microcontroller, but other\r
-targets are possible.  In its typical configurations, Reliance Edge needs at\r
-least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the ROM or NOR flash),\r
-and 500 to 700 bytes of stack.\r
-\r
-Reliance Edge is not designed for high-end embedded systems that run complicated\r
-operating systems like Linux or Windows Embedded Compact.  Embedded systems of\r
-that variety are better served by other file systems, like Datalight's\r
-[Reliance Nitro](http://www.datalight.com/products/embedded-file-systems/reliance-nitro).\r
-\r
-## Getting Reliance Edge Working\r
-\r
-Before you can use Reliance Edge, it must be ported and configured.  At a\r
-minimum, porting includes filling-in functions so that Reliance Edge can issue\r
-commands to your storage medium; depending on your needs, other functions may\r
-need to be filled in as well.  These functions reside in a subdirectory in the\r
-os/ directory; see os/stub/ for a blank set of functions.  Configuring includes\r
-creating a project directory (start by copying projects/newproj) and creating\r
-the two configuration files (redconf.h/redconf.c) using the Reliance Edge\r
-Configuration Utility (which can be downloaded from\r
-<http://www.datalight.com/reliance-edge>).\r
-\r
-These topics are covered in much greater detail in the _Developer's Guide_,\r
-linked above.\r
-\r
-## Using Reliance Edge\r
-\r
-Using Reliance Edge is a simple matter of including the primary Reliance Edge\r
-application header in your application (either include/redposix.h or\r
-include/redfse.h) and compiling and linking against Reliance Edge binaries.\r
-The Reliance Edge driver must be initialized before it is used (via the\r
-red\_init() or RedFseInit() functions) and then volumes can be mounted and file\r
-and directory functions invoked.  The Reliance Edge API is documented in the\r
-_Developer's Guide_ (linked above) and also via comments in the source code.\r
-\r
-## Licensing\r
-\r
-Reliance Edge is an open-source project licensed under the GNU General Public\r
-License v2 (GPLv2).  Businesses and individuals that for commercial or other\r
-reasons cannot comply with the terms of the GPLv2 license may obtain a\r
-commercial license before incorporating Reliance Edge into proprietary software\r
-for distribution in any form.  Visit <http://www.datalight.com/reliance-edge>\r
-for more information.  The commercial distribution also includes extra tests and\r
-tools not distributed with the GPLv2 version.\r
-\r
-See LICENSE.txt for the full license terms of this distribution of the product.\r
-\r
-## Getting Help\r
-\r
-If you need assistance using Reliance Edge, and you have already consulted the\r
-_Developer's Guide_, contact <RelianceEdgeSupport@datalight.com>.\r
-\r
-In the near future, a community forum or message board will be set up to\r
-facilitate discussion of Reliance Edge and allow users to get help from\r
-Datalight and from each other.  In the meantime, please use the email address\r
-given above.\r
-\r
-## Contributing\r
-\r
-Contributions to Reliance Edge are welcome.  Our policy is that Datalight must\r
-own the copyright of all code incorporated into Reliance Edge; if contributing a\r
-significant amount of code, you will be asked to file a copyright assignment\r
-agreement.  See CONTRIBUTING.txt for further details and contribution\r
-guidelines.\r
-\r
-To report bugs, please create a GitHub issue or contact\r
-<RelianceEdgeSupport@datalight.com>.\r
-\r
+# Reliance Edge
+
+Reliance Edge is a small, portable, highly reliable power-fail safe file system
+for resource-constrained embedded systems like microcontrollers.  It is written
+in C and provides a familiar POSIX-like file system API, making it easy to use
+in your application; or an alternate minimalist API if your application has
+simple storage needs.  Reliance Edge is highly configurable and can be tuned to
+the precise needs of your application.
+
+## Getting Help
+
+You will find this section later in our readme as well - but we wanted to tell
+you up front: DATALIGHT WANTS TO HEAR FROM YOU!  Whether it's just to make
+comments about the product, to suggest new features, or to tell us what you
+don't like - reach out!  All comments and inquiries can be directed to
+<RelianceEdgeSupport@datalight.com>.
+
+## Documentation
+
+The complete documentation for Reliance Edge is distributed separately.  It
+includes an API reference and detailed discussions of various aspects of using
+Reliance Edge, including porting, building, configuring, and testing.  This
+complete documentation, called the _Developer's Guide_, can be obtained for free
+from here:
+
+<http://www.datalight.com/reliance-edge>
+
+In addition this README, see doc/release_notes.md for a list of updates
+to Reliance Edge and a list of known issues. There is also a quick-start
+guide in the doc/ directory that describes step-by-step how to compile
+and run Reliance Edge in a simulated Windows environment.
+
+## Why Use Reliance Edge?
+
+Reliance Edge is ideal for small embedded systems with data storage
+requirements, especially if there is a chance of sudden power loss or other
+system failures.  Compared to "raw" disk access, using a file system like
+Reliance Edge removes the burden of tracking which sectors belong to which
+objects, and allows data to be updated more reliably.  Compared to the FAT file
+system, using Reliance Edge eliminates the possibility that file system data
+will be left in an inconsistent state, corrupting the disk; Reliance Edge does
+not need a fsck/CHKDSK utility.  Compared to journaling file systems, Reliance
+Edge has less overhead and results in less storage media wear for longer device
+lifetimes.
+
+Reliance Edge uses a unique transactional model that not only prevents file
+system corruption but also allows a set of changes to be made in an atomic "all
+or nothing" fashion.  This is very useful for applications that make sets of
+interrelated changes.  By using the features of Reliance Edge, a set of changes
+can be incorporated into a single atomic transaction, which is committed in its
+entirety or not at all even if interrupted by power loss; this means the
+application does not need code to recover from partially-finished updates.
+
+## Hardware
+
+The typical hardware for Reliance Edge is a 32-bit microcontroller, but other
+targets are possible.  In its typical configurations, Reliance Edge needs at
+least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the ROM or NOR flash),
+and 500 to 700 bytes of stack.
+
+Reliance Edge is not designed for high-end embedded systems that run complicated
+operating systems like Linux or Windows Embedded Compact.  Embedded systems of
+that variety are better served by other file systems, like Datalight's
+[Reliance Nitro](http://www.datalight.com/products/embedded-file-systems/reliance-nitro).
+
+## Getting Reliance Edge Working
+
+Before you can use Reliance Edge, it must be ported and configured.  At a
+minimum, porting includes filling-in functions so that Reliance Edge can issue
+commands to your storage medium; depending on your needs, other functions may
+need to be filled in as well.  These functions reside in a subdirectory in the
+os/ directory; see os/stub/ for a blank set of functions.  Configuring includes
+creating a project directory (start by copying projects/newproj) and creating
+the two configuration files (redconf.h/redconf.c) using the Reliance Edge
+Configuration Utility (which can be downloaded from
+<http://www.datalight.com/reliance-edge>).
+
+These topics are covered in much greater detail in the _Developer's Guide_,
+linked above.
+
+## Using Reliance Edge
+
+Using Reliance Edge is a simple matter of including the primary Reliance Edge
+application header in your application (either include/redposix.h or
+include/redfse.h) and compiling and linking against Reliance Edge binaries.
+The Reliance Edge driver must be initialized before it is used (via the
+red\_init() or RedFseInit() functions) and then volumes can be mounted and file
+and directory functions invoked.  The Reliance Edge API is documented in the
+_Developer's Guide_ (linked above) and also via comments in the source code.
+
+## Licensing
+
+Reliance Edge is an open-source project licensed under the GNU General Public
+License v2 (GPLv2).  Businesses and individuals that for commercial or other
+reasons cannot comply with the terms of the GPLv2 license may obtain a
+commercial license before incorporating Reliance Edge into proprietary software
+for distribution in any form.  Visit <http://www.datalight.com/reliance-edge>
+for more information.  The commercial distribution also includes extra tests and
+tools not distributed with the GPLv2 version.
+
+See LICENSE.txt for the full license terms of this distribution of the product.
+
+## Getting Help
+
+If you need assistance using Reliance Edge, and you have already consulted the
+_Developer's Guide_, contact <RelianceEdgeSupport@datalight.com>.
+
+In the near future, a community forum or message board will be set up to
+facilitate discussion of Reliance Edge and allow users to get help from
+Datalight and from each other.  In the meantime, please use the email address
+given above.
+
+## Contributing
+
+Contributions to Reliance Edge are welcome.  Our policy is that Datalight must
+own the copyright of all code incorporated into Reliance Edge; if contributing a
+significant amount of code, you will be asked to file a copyright assignment
+agreement.  See CONTRIBUTING.txt for further details and contribution
+guidelines.
+
+To report bugs, please create a GitHub issue or contact
+<RelianceEdgeSupport@datalight.com>.
+
index 1199093600b23e941a63610e3bdca9e7d14f00f9..106d923b01e493c96e0e87c1035953dbcd05c69e 100644 (file)
-\r
-\r
-RELIANCE EDGE\r
-\r
-\r
-Reliance Edge is a small, portable, highly reliable power-fail safe file\r
-system for resource-constrained embedded systems like microcontrollers.\r
-It is written in C and provides a familiar POSIX-like file system API,\r
-making it easy to use in your application; or an alternate minimalist\r
-API if your application has simple storage needs. Reliance Edge is\r
-highly configurable and can be tuned to the precise needs of your\r
-application.\r
-\r
-\r
-Documentation\r
-\r
-The complete documentation for Reliance Edge is distributed separately.\r
-It includes an API reference and detailed discussions of various aspects\r
-of using Reliance Edge, including porting, building, configuring, and\r
-testing. This complete documentation, called the _Developer's Guide_,\r
-can be obtained for free from here:\r
-\r
-http://www.datalight.com/reliance-edge\r
-\r
-In addition this README, see doc/release_notes.md for a list of updates\r
-to Reliance Edge and a list of known issues. There is also a quick-start\r
-guide in the doc/ directory that describes step-by-step how to compile\r
-and run Reliance Edge in a simulated Windows environment.\r
-\r
-\r
-Why Use Reliance Edge?\r
-\r
-Reliance Edge is ideal for small embedded systems with data storage\r
-requirements, especially if there is a chance of sudden power loss or\r
-other system failures. Compared to "raw" disk access, using a file\r
-system like Reliance Edge removes the burden of tracking which sectors\r
-belong to which objects, and allows data to be updated more reliably.\r
-Compared to the FAT file system, using Reliance Edge eliminates the\r
-possibility that file system data will be left in an inconsistent state,\r
-corrupting the disk; Reliance Edge does not need a fsck/CHKDSK utility.\r
-Compared to journaling file systems, Reliance Edge has less overhead and\r
-results in less storage media wear for longer device lifetimes.\r
-\r
-Reliance Edge uses a unique transactional model that not only prevents\r
-file system corruption but also allows a set of changes to be made in an\r
-atomic "all or nothing" fashion. This is very useful for applications\r
-that make sets of interrelated changes. By using the features of\r
-Reliance Edge, a set of changes can be incorporated into a single atomic\r
-transaction, which is committed in its entirety or not at all even if\r
-interrupted by power loss; this means the application does not need code\r
-to recover from partially-finished updates.\r
-\r
-\r
-Hardware\r
-\r
-The typical hardware for Reliance Edge is a 32-bit microcontroller, but\r
-other targets are possible. In its typical configurations, Reliance Edge\r
-needs at least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the\r
-ROM or NOR flash), and 500 to 700 bytes of stack.\r
-\r
-Reliance Edge is not designed for high-end embedded systems that run\r
-complicated operating systems like Linux or Windows Embedded Compact.\r
-Embedded systems of that variety are better served by other file\r
-systems, like Datalight's Reliance Nitro.\r
-\r
-\r
-Getting Reliance Edge Working\r
-\r
-Before you can use Reliance Edge, it must be ported and configured. At a\r
-minimum, porting includes filling-in functions so that Reliance Edge can\r
-issue commands to your storage medium; depending on your needs, other\r
-functions may need to be filled in as well. These functions reside in a\r
-subdirectory in the os/ directory; see os/stub/ for a blank set of\r
-functions. Configuring includes creating a project directory (start by\r
-copying projects/newproj) and creating the two configuration files\r
-(redconf.h/redconf.c) using the Reliance Edge Configuration Utility\r
-(which can be downloaded from http://www.datalight.com/reliance-edge).\r
-\r
-These topics are covered in much greater detail in the _Developer's\r
-Guide_, linked above.\r
-\r
-\r
-Using Reliance Edge\r
-\r
-Using Reliance Edge is a simple matter of including the primary Reliance\r
-Edge application header in your application (either include/redposix.h\r
-or include/redfse.h) and compiling and linking against Reliance Edge\r
-binaries. The Reliance Edge driver must be initialized before it is used\r
-(via the red_init() or RedFseInit() functions) and then volumes can be\r
-mounted and file and directory functions invoked. The Reliance Edge API\r
-is documented in the _Developer's Guide_ (linked above) and also via\r
-comments in the source code.\r
-\r
-\r
-Licensing\r
-\r
-Reliance Edge is an open-source project licensed under the GNU General\r
-Public License v2 (GPLv2). Businesses and individuals that for\r
-commercial or other reasons cannot comply with the terms of the GPLv2\r
-license may obtain a commercial license before incorporating Reliance\r
-Edge into proprietary software for distribution in any form. Visit\r
-http://www.datalight.com/reliance-edge for more information. The\r
-commercial distribution also includes extra tests and tools not\r
-distributed with the GPLv2 version.\r
-\r
-See LICENSE.txt for the full license terms of this distribution of the\r
-product.\r
-\r
-\r
-Getting Help\r
-\r
-If you need assistance using Reliance Edge, and you have already\r
-consulted the _Developer's Guide_, contact\r
-RelianceEdgeSupport@datalight.com.\r
-\r
-In the near future, a community forum or message board will be set up to\r
-facilitate discussion of Reliance Edge and allow users to get help from\r
-Datalight and from each other. In the meantime, please use the email\r
-address given above.\r
-\r
-\r
-Contributing\r
-\r
-Contributions to Reliance Edge are welcome. Our policy is that Datalight\r
-must own the copyright of all code incorporated into Reliance Edge; if\r
-contributing a significant amount of code, you will be asked to file a\r
-copyright assignment agreement. See CONTRIBUTING.txt for further details\r
-and contribution guidelines.\r
-\r
-To report bugs, please create a GitHub issue or contact\r
-RelianceEdgeSupport@datalight.com.\r
+
+
+RELIANCE EDGE
+
+
+Reliance Edge is a small, portable, highly reliable power-fail safe file
+system for resource-constrained embedded systems like microcontrollers.
+It is written in C and provides a familiar POSIX-like file system API,
+making it easy to use in your application; or an alternate minimalist
+API if your application has simple storage needs. Reliance Edge is
+highly configurable and can be tuned to the precise needs of your
+application.
+
+
+Getting Help
+
+You will find this section later in our readme as well - but we wanted
+to tell you up front: DATALIGHT WANTS TO HEAR FROM YOU! Whether it's
+just to make comments about the product, to suggest new features, or to
+tell us what you don't like - reach out! All comments and inquiries can
+be directed to RelianceEdgeSupport@datalight.com.
+
+
+Documentation
+
+The complete documentation for Reliance Edge is distributed separately.
+It includes an API reference and detailed discussions of various aspects
+of using Reliance Edge, including porting, building, configuring, and
+testing. This complete documentation, called the _Developer's Guide_,
+can be obtained for free from here:
+
+http://www.datalight.com/reliance-edge
+
+In addition this README, see doc/release_notes.md for a list of updates
+to Reliance Edge and a list of known issues. There is also a quick-start
+guide in the doc/ directory that describes step-by-step how to compile
+and run Reliance Edge in a simulated Windows environment.
+
+
+Why Use Reliance Edge?
+
+Reliance Edge is ideal for small embedded systems with data storage
+requirements, especially if there is a chance of sudden power loss or
+other system failures. Compared to "raw" disk access, using a file
+system like Reliance Edge removes the burden of tracking which sectors
+belong to which objects, and allows data to be updated more reliably.
+Compared to the FAT file system, using Reliance Edge eliminates the
+possibility that file system data will be left in an inconsistent state,
+corrupting the disk; Reliance Edge does not need a fsck/CHKDSK utility.
+Compared to journaling file systems, Reliance Edge has less overhead and
+results in less storage media wear for longer device lifetimes.
+
+Reliance Edge uses a unique transactional model that not only prevents
+file system corruption but also allows a set of changes to be made in an
+atomic "all or nothing" fashion. This is very useful for applications
+that make sets of interrelated changes. By using the features of
+Reliance Edge, a set of changes can be incorporated into a single atomic
+transaction, which is committed in its entirety or not at all even if
+interrupted by power loss; this means the application does not need code
+to recover from partially-finished updates.
+
+
+Hardware
+
+The typical hardware for Reliance Edge is a 32-bit microcontroller, but
+other targets are possible. In its typical configurations, Reliance Edge
+needs at least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the
+ROM or NOR flash), and 500 to 700 bytes of stack.
+
+Reliance Edge is not designed for high-end embedded systems that run
+complicated operating systems like Linux or Windows Embedded Compact.
+Embedded systems of that variety are better served by other file
+systems, like Datalight's Reliance Nitro.
+
+
+Getting Reliance Edge Working
+
+Before you can use Reliance Edge, it must be ported and configured. At a
+minimum, porting includes filling-in functions so that Reliance Edge can
+issue commands to your storage medium; depending on your needs, other
+functions may need to be filled in as well. These functions reside in a
+subdirectory in the os/ directory; see os/stub/ for a blank set of
+functions. Configuring includes creating a project directory (start by
+copying projects/newproj) and creating the two configuration files
+(redconf.h/redconf.c) using the Reliance Edge Configuration Utility
+(which can be downloaded from http://www.datalight.com/reliance-edge).
+
+These topics are covered in much greater detail in the _Developer's
+Guide_, linked above.
+
+
+Using Reliance Edge
+
+Using Reliance Edge is a simple matter of including the primary Reliance
+Edge application header in your application (either include/redposix.h
+or include/redfse.h) and compiling and linking against Reliance Edge
+binaries. The Reliance Edge driver must be initialized before it is used
+(via the red_init() or RedFseInit() functions) and then volumes can be
+mounted and file and directory functions invoked. The Reliance Edge API
+is documented in the _Developer's Guide_ (linked above) and also via
+comments in the source code.
+
+
+Licensing
+
+Reliance Edge is an open-source project licensed under the GNU General
+Public License v2 (GPLv2). Businesses and individuals that for
+commercial or other reasons cannot comply with the terms of the GPLv2
+license may obtain a commercial license before incorporating Reliance
+Edge into proprietary software for distribution in any form. Visit
+http://www.datalight.com/reliance-edge for more information. The
+commercial distribution also includes extra tests and tools not
+distributed with the GPLv2 version.
+
+See LICENSE.txt for the full license terms of this distribution of the
+product.
+
+
+Getting Help
+
+If you need assistance using Reliance Edge, and you have already
+consulted the _Developer's Guide_, contact
+RelianceEdgeSupport@datalight.com.
+
+In the near future, a community forum or message board will be set up to
+facilitate discussion of Reliance Edge and allow users to get help from
+Datalight and from each other. In the meantime, please use the email
+address given above.
+
+
+Contributing
+
+Contributions to Reliance Edge are welcome. Our policy is that Datalight
+must own the copyright of all code incorporated into Reliance Edge; if
+contributing a significant amount of code, you will be asked to file a
+copyright assignment agreement. See CONTRIBUTING.txt for further details
+and contribution guidelines.
+
+To report bugs, please create a GitHub issue or contact
+RelianceEdgeSupport@datalight.com.
index 22c44f466d22ca8033405529863ceeb9496bf186..06c3a23eb0eb4552ba23075b53ddb94b281f4c61 100644 (file)
@@ -65,7 +65,7 @@ VOLUME         * CONST_IF_ONE_VOLUME gpRedVolume = &gaRedVolume[0U];
 COREVOLUME     * CONST_IF_ONE_VOLUME gpRedCoreVol = &gaCoreVol[0U];\r
 METAROOT       *gpRedMR = &gaCoreVol[0U].aMR[0U];\r
 \r
-CONST_IF_ONE_VOLUME uint8_t gbRedVolNum;\r
+CONST_IF_ONE_VOLUME uint8_t gbRedVolNum = 0;\r
 \r
 \r
 /** @brief Initialize the Reliance Edge file system driver.\r
@@ -1235,7 +1235,7 @@ REDSTATUS RedCoreLookup(
     @param pszSrcName   The name of the file or directory to rename.\r
     @param ulDstPInode  The new parent directory inode number of the file or\r
                         directory after the rename.\r
-    @param pszNewPath   The new name of the file or directory after the rename.\r
+    @param pszDstName   The new name of the file or directory after the rename.\r
 \r
     @return A negated ::REDSTATUS code indicating the operation result.\r
 \r
@@ -1314,7 +1314,7 @@ REDSTATUS RedCoreRename(
     @param pszSrcName   The name of the file or directory to rename.\r
     @param ulDstPInode  The new parent directory inode number of the file or\r
                         directory after the rename.\r
-    @param pszNewPath   The new name of the file or directory after the rename.\r
+    @param pszDstName   The new name of the file or directory after the rename.\r
 \r
     @return A negated ::REDSTATUS code indicating the operation result.\r
 \r
index 66b5072ed1c3b26a5fc62c3a26881a4c982b0323..cfda1c1aa235011b3db8604f865a3c72f4483fd5 100644 (file)
@@ -199,21 +199,23 @@ REDSTATUS RedDirEntryDelete(
     }\r
     else if((DirEntryIndexToOffset(ulDeleteIdx) + DIRENT_SIZE) == pPInode->pInodeBuf->ullSize)\r
     {\r
-        uint32_t ulTruncIdx = ulDeleteIdx;\r
+        /*  Start searching one behind the index to be deleted.\r
+        */\r
+        uint32_t ulTruncIdx = ulDeleteIdx - 1U;\r
         bool     fDone = false;\r
 \r
         /*  We are deleting the last dirent in the directory, so search\r
             backwards to find the last populated dirent, allowing us to truncate\r
             the directory to that point.\r
         */\r
-        while((ret == 0) && (ulTruncIdx > 0U) && !fDone)\r
+        while((ret == 0) && (ulTruncIdx != UINT32_MAX) && !fDone)\r
         {\r
             ret = RedInodeDataSeekAndRead(pPInode, ulTruncIdx / DIRENTS_PER_BLOCK);\r
 \r
             if(ret == 0)\r
             {\r
                 const DIRENT *pDirents = CAST_CONST_DIRENT_PTR(pPInode->pbData);\r
-                uint32_t      ulBlockIdx = (ulTruncIdx - 1U) % DIRENTS_PER_BLOCK;\r
+                uint32_t      ulBlockIdx = ulTruncIdx % DIRENTS_PER_BLOCK;\r
 \r
                 do\r
                 {\r
@@ -242,6 +244,12 @@ REDSTATUS RedDirEntryDelete(
             }\r
         }\r
 \r
+        /*  Currently ulTruncIdx represents the last valid dirent index, or\r
+            UINT32_MAX if the directory is now empty.  Increment it so that it\r
+            represents the first invalid entry, which will be truncated.\r
+        */\r
+        ulTruncIdx++;\r
+\r
         /*  Truncate the directory, deleting the requested entry and any empty\r
             dirents at the end of the directory.\r
         */\r
@@ -449,7 +457,7 @@ REDSTATUS RedDirEntryLookup(
 #if (REDCONF_API_POSIX_READDIR == 1) || (REDCONF_CHECKER == 1)\r
 /** @brief Read the next entry from a directory, given a starting index.\r
 \r
-    @param pInode   A pointer to the cached inode structure of the directory to\r
+    @param pPInode  A pointer to the cached inode structure of the directory to\r
                     read from.\r
     @param pulIdx   On entry, the directory index to start reading from.  On\r
                     successful return, populated with the directory index to use\r
index 6375988b3046c311897804a43dd0010a774b449c..87664d12dc351e4bb9211b9d20784b7fb2e5f749 100644 (file)
@@ -158,6 +158,7 @@ REDSTATUS RedImapBlockSet(
     }\r
 \r
     /*  Adjust the free/almost free block count if the block was allocable.\r
+        Discard the block if required.\r
     */\r
     if((ret == 0) && (ulBlock >= gpRedCoreVol->ulFirstAllocableBN))\r
     {\r
index acd101a495d1796ee82f5500010e497a8a362847..976a2f01a37000e9f6cb68f74965f6b4f6587020 100644 (file)
@@ -63,6 +63,10 @@ REDSTATUS RedVolMount(void)
 \r
         if(ret != 0)\r
         {\r
+            /*  If we fail to mount, invalidate the buffers to prevent any\r
+                confusion that could be caused by stale or corrupt metadata.\r
+            */\r
+            (void)RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount);\r
             (void)RedOsBDevClose(gbRedVolNum);\r
         }\r
     }\r
index bcc9bff710c1bb336b8a07fb612fc3ec7bff3d9e..2a3d9370a3164dd3c97526fd45b603ea46a28770 100644 (file)
@@ -5,6 +5,39 @@ recent releases and a list of known issues.
 
 ## Release History and Changes
 
+### Reliance Edge v2.0, January 2017
+
+- Added support for Linux as a host environment
+  - All "host" projects may now be built in either Windows or Linux using the
+    `make` command.  The formatter and image builder are built, and the checker
+    and image copier are also built in the commercial kit.
+  - An additional host tool has been added for Linux only: `redfuse`.  It is a
+    File System in User Space (FUSE) implementation, allowing a Reliance Edge
+    volume to be mounted directly on Linux for easy access.  It is built from
+    the host project folder using the command `make redfuse`.
+  - The OS-specific API test (commercial kit only) is now ported to run on Linux
+    for the purpose of verifying the FUSE implementation.
+- Fixed a bug that could leave a directory in an invalid state after removing
+  files.  For example, an affected directory might report a non-zero length even
+  after all files had been deleted.
+- Fixed a bug that would leave the driver in a bad state if a mount operation
+  failed due to missing or corrupt metaroot blocks.
+
+### Reliance Edge v1.1 (Beta), November 2016
+
+- Added support for a discard (trim) interface in the commercial kit.  While
+  discards are not integral to the behavior of the filesystem, they allow
+  certain types of Flash drivers and media to perform at optimal speed and
+  efficiency.  The commercial version of Reliance Edge now allows the user to
+  implement this interface for compatible storage media.
+  - This change added new fields to the configuration files redconf.h and
+    redconf.c. The configuration utility has been updated to version 1.1 and
+    existing configuration files must be updated using the updated utility.
+- The configuration utility now has keyboard shortcuts for opening and saving
+  the configuration.
+- The configuration utility now adds version macros to easily identify when an
+  outdated configuration file is used with Reliance Edge or vice versa.
+
 ### Reliance Edge v1.0.4, July 2016
 
 - Added ARM mbed and ARM mbed OS support in the commercial kit, with an example
@@ -23,7 +56,7 @@ recent releases and a list of known issues.
   number of times to retry a block device read, write or flush operation before
   returning a failure.  The configuration tool has been updated to version 1.0.2
   with this change.
-  - This added a new field to the volume configuration in to redconf.c: existing
+  - This added a new field to the volume configuration in redconf.c: existing
     redconf.c files from v1.0.1 and earlier must be updated to work with v1.0.2.
     Open redconf.h and redconf.c with the configuration tool, enable
     "Retry block device I/O on failure" for any volumes if desired, and save the
index dedfdbb2aa5cdb4803c0f87cf49e9d20053d0302..341eb5d27073a262f8a1d5649d04c0d61cfef236 100644 (file)
@@ -9,6 +9,44 @@ course of recent releases and a list of known issues.
 
 Release History and Changes
 
+Reliance Edge v2.0, January 2017
+
+-   Added support for Linux as a host environment
+-   All "host" projects may now be built in either Windows or Linux
+    using the make command. The formatter and image builder are built,
+    and the checker and image copier are also built in the
+    commercial kit.
+-   An additional host tool has been added for Linux only: redfuse. It
+    is a File System in User Space (FUSE) implementation, allowing a
+    Reliance Edge volume to be mounted directly on Linux for
+    easy access. It is built from the host project folder using the
+    command make redfuse.
+-   The OS-specific API test (commercial kit only) is now ported to run
+    on Linux for the purpose of verifying the FUSE implementation.
+-   Fixed a bug that could leave a directory in an invalid state after
+    removing files. For example, an affected directory might report a
+    non-zero length even after all files had been deleted.
+-   Fixed a bug that would leave the driver in a bad state if a mount
+    operation failed due to missing or corrupt metaroot blocks.
+
+Reliance Edge v1.1 (Beta), November 2016
+
+-   Added support for a discard (trim) interface in the commercial kit.
+    While discards are not integral to the behavior of the filesystem,
+    they allow certain types of Flash drivers and media to perform at
+    optimal speed and efficiency. The commercial version of Reliance
+    Edge now allows the user to implement this interface for compatible
+    storage media.
+-   This change added new fields to the configuration files redconf.h
+    and redconf.c. The configuration utility has been updated to version
+    1.1 and existing configuration files must be updated using the
+    updated utility.
+-   The configuration utility now has keyboard shortcuts for opening and
+    saving the configuration.
+-   The configuration utility now adds version macros to easily identify
+    when an outdated configuration file is used with Reliance Edge or
+    vice versa.
+
 Reliance Edge v1.0.4, July 2016
 
 -   Added ARM mbed and ARM mbed OS support in the commercial kit, with
@@ -29,7 +67,7 @@ Common Code Changes
     specify a number of times to retry a block device read, write or
     flush operation before returning a failure. The configuration tool
     has been updated to version 1.0.2 with this change.
--   This added a new field to the volume configuration in to redconf.c:
+-   This added a new field to the volume configuration in redconf.c:
     existing redconf.c files from v1.0.1 and earlier must be updated to
     work with v1.0.2. Open redconf.h and redconf.c with the
     configuration tool, enable "Retry block device I/O on failure" for
index a1d86fda8ca8b3e6ef3d3230ec199037c5f4a081..8576a420d27df5f19e956c0f58f5165d8dbfe537 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Compile-time validity checks for the REDCONF macros.\r
-*/\r
-#ifndef REDCONFIGCHK_H\r
-#define REDCONFIGCHK_H\r
-\r
-#ifndef REDCONF_READ_ONLY\r
-  #error "Configuration error: REDCONF_READ_ONLY must be defined."\r
-#endif\r
-#ifndef REDCONF_API_POSIX\r
-  #error "Configuration error: REDCONF_API_POSIX must be defined."\r
-#endif\r
-#ifndef REDCONF_API_FSE\r
-  #error "Configuration error: REDCONF_API_FSE must be defined."\r
-#endif\r
-\r
-#if REDCONF_API_POSIX == 1\r
-  #ifndef REDCONF_API_POSIX_FORMAT\r
-    #error "Configuration error: REDCONF_API_POSIX_FORMAT must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_UNLINK\r
-    #error "Configuration error: REDCONF_API_POSIX_UNLINK must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_MKDIR\r
-    #error "Configuration error: REDCONF_API_POSIX_MKDIR must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_RMDIR\r
-    #error "Configuration error: REDCONF_API_POSIX_RMDIR must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_RENAME\r
-    #error "Configuration error: REDCONF_API_POSIX_RENAME must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_LINK\r
-    #error "Configuration error: REDCONF_API_POSIX_LINK must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_FTRUNCATE\r
-    #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_POSIX_READDIR\r
-    #error "Configuration error: REDCONF_API_POSIX_READDIR must be defined."\r
-  #endif\r
-  #ifndef REDCONF_NAME_MAX\r
-    #error "Configuration error: REDCONF_NAME_MAX must be defined."\r
-  #endif\r
-  #ifndef REDCONF_PATH_SEPARATOR\r
-    #error "Configuration error: REDCONF_PATH_SEPARATOR must be defined."\r
-  #endif\r
-  #ifndef REDCONF_RENAME_ATOMIC\r
-    #error "Configuration error: REDCONF_RENAME_ATOMIC must be defined."\r
-  #endif\r
-  #ifndef REDCONF_HANDLE_COUNT\r
-    #error "Configuration error: REDCONF_HANDLE_COUNT must be defined."\r
-  #endif\r
-#endif\r
-#if REDCONF_API_FSE == 1\r
-  #ifndef REDCONF_API_FSE_FORMAT\r
-    #error "Configuration error: REDCONF_API_FSE_FORMAT must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_FSE_TRUNCATE\r
-    #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_FSE_TRANSMASKSET\r
-    #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be defined."\r
-  #endif\r
-  #ifndef REDCONF_API_FSE_TRANSMASKGET\r
-    #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be defined."\r
-  #endif\r
-#endif\r
-\r
-#ifndef REDCONF_TASK_COUNT\r
-  #error "Configuration error: REDCONF_TASK_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_ENDIAN_BIG\r
-  #error "Configuration error: REDCONF_ENDIAN_BIG must be defined."\r
-#endif\r
-#ifndef REDCONF_ALIGNMENT_SIZE\r
-  #error "Configuration error: REDCONF_ALIGNMENT_SIZE must be defined."\r
-#endif\r
-#ifndef REDCONF_CRC_ALGORITHM\r
-  #error "Configuration error: REDCONF_CRC_ALGORITHM must be defined."\r
-#endif\r
-#ifndef REDCONF_INODE_TIMESTAMPS\r
-  #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be defined."\r
-#endif\r
-#ifndef REDCONF_ATIME\r
-  #error "Configuration error: REDCONF_ATIME must be defined."\r
-#endif\r
-#ifndef REDCONF_DIRECT_POINTERS\r
-  #error "Configuration error: REDCONF_DIRECT_POINTERS must be defined."\r
-#endif\r
-#ifndef REDCONF_INDIRECT_POINTERS\r
-  #error "Configuration error: REDCONF_INDIRECT_POINTERS must be defined."\r
-#endif\r
-#ifndef REDCONF_INODE_BLOCKS\r
-  #error "Configuration error: REDCONF_INODE_BLOCKS must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAP_EXTERNAL\r
-  #error "Configuration error: REDCONF_IMAP_EXTERNAL must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAP_INLINE\r
-  #error "Configuration error: REDCONF_IMAP_INLINE must be defined."\r
-#endif\r
-#ifndef REDCONF_OUTPUT\r
-  #error "Configuration error: REDCONF_OUTPUT must be defined."\r
-#endif\r
-#ifndef REDCONF_ASSERTS\r
-  #error "Configuration error: REDCONF_ASSERTS must be defined."\r
-#endif\r
-#ifndef REDCONF_TRANSACT_DEFAULT\r
-  #error "Configuration error: REDCONF_TRANSACT_DEFAULT must be defined."\r
-#endif\r
-#ifndef REDCONF_BUFFER_COUNT\r
-  #error "Configuration error: REDCONF_BUFFER_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_BLOCK_SIZE\r
-  #error "Configuration error: REDCONF_BLOCK_SIZE must be defined."\r
-#endif\r
-#ifndef REDCONF_VOLUME_COUNT\r
-  #error "Configuration error: REDCONF_VOLUME_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAGE_BUILDER\r
-  #error "Configuration error: REDCONF_IMAGE_BUILDER must be defined."\r
-#endif\r
-#ifndef REDCONF_CHECKER\r
-  #error "Configuration error: REDCONF_CHECKER must be defined."\r
-#endif\r
-\r
-\r
-#if (REDCONF_READ_ONLY != 0) && (REDCONF_READ_ONLY != 1)\r
-  #error "Configuration error: REDCONF_READ_ONLY must be either 0 or 1"\r
-#endif\r
-\r
-#if (REDCONF_API_POSIX != 0) && (REDCONF_API_POSIX != 1)\r
-  #error "Configuration error: REDCONF_API_POSIX must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_API_FSE != 0) && (REDCONF_API_FSE != 1)\r
-  #error "Configuration error: REDCONF_API_FSE must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_API_FSE == 0) && (REDCONF_API_POSIX == 0)\r
-  #error "Configuration error: either REDCONF_API_FSE or REDCONF_API_POSIX must be set to 1."\r
-#endif\r
-\r
-#if REDCONF_API_POSIX == 1\r
-  #if REDCONF_API_FSE != 0\r
-    #error "Configuration error: REDCONF_API_FSE must be 0 if REDCONF_API_POSIX is 1"\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_FORMAT != 0) && (REDCONF_API_POSIX_FORMAT != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_FORMAT must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_UNLINK != 0) && (REDCONF_API_POSIX_UNLINK != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_UNLINK must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_MKDIR != 0) && (REDCONF_API_POSIX_MKDIR != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_MKDIR must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_RMDIR != 0) && (REDCONF_API_POSIX_RMDIR != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_RMDIR must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_RENAME != 0) && (REDCONF_API_POSIX_RENAME != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_RENAME must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_LINK != 0) && (REDCONF_API_POSIX_LINK != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_LINK must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_FTRUNCATE != 0) && (REDCONF_API_POSIX_FTRUNCATE != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_POSIX_READDIR != 0) && (REDCONF_API_POSIX_READDIR != 1)\r
-    #error "Configuration error: REDCONF_API_POSIX_READDIR must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_NAME_MAX < 1U) || (REDCONF_NAME_MAX > (REDCONF_BLOCK_SIZE - 4U))\r
-    #error "Configuration error: invalid value of REDCONF_NAME_MAX"\r
-  #endif\r
-\r
-  #if (REDCONF_PATH_SEPARATOR < 1) || (REDCONF_PATH_SEPARATOR > 127)\r
-    #error "Configuration error: invalid value of REDCONF_PATH_SEPARATOR"\r
-  #endif\r
-\r
-  #if (REDCONF_RENAME_ATOMIC != 0) && (REDCONF_RENAME_ATOMIC != 1)\r
-    #error "Configuration error: REDCONF_RENAME_ATOMIC must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_HANDLE_COUNT < 1U) || (REDCONF_HANDLE_COUNT > 4096U)\r
-    #error "Configuration error: invalid value of REDCONF_HANDLE_COUNT"\r
-  #endif\r
-#endif\r
-#if REDCONF_API_FSE == 1\r
-  #if (REDCONF_API_FSE_FORMAT != 0) && (REDCONF_API_FSE_FORMAT != 1)\r
-    #error "Configuration error: REDCONF_API_FSE_FORMAT must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_FSE_TRUNCATE != 0) && (REDCONF_API_FSE_TRUNCATE != 1)\r
-    #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_FSE_TRANSMASKSET != 0) && (REDCONF_API_FSE_TRANSMASKSET != 1)\r
-    #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be either 0 or 1."\r
-  #endif\r
-\r
-  #if (REDCONF_API_FSE_TRANSMASKGET != 0) && (REDCONF_API_FSE_TRANSMASKGET != 1)\r
-    #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be either 0 or 1."\r
-  #endif\r
-#endif\r
-\r
-#if REDCONF_TASK_COUNT < 1U\r
-  #error "Configuration error: invalid value of REDCONF_TASK_COUNT"\r
-#endif\r
-\r
-#if (REDCONF_ENDIAN_BIG != 0) && (REDCONF_ENDIAN_BIG != 1)\r
-  #error "Configuration error: REDCONF_ENDIAN_BIG must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ALIGNMENT_SIZE != 1U) && (REDCONF_ALIGNMENT_SIZE != 2U) && (REDCONF_ALIGNMENT_SIZE != 4U) && (REDCONF_ALIGNMENT_SIZE != 8U)\r
-  #error "Configuration error: invalid value REDCONF_ALIGNMENT_SIZE"\r
-#endif\r
-\r
-/*  REDCONF_CRC_ALGORITHM checked in crc.c\r
-*/\r
-\r
-#if (REDCONF_INODE_TIMESTAMPS != 0) && (REDCONF_INODE_TIMESTAMPS != 1)\r
-  #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ATIME != 0) && (REDCONF_ATIME != 1)\r
-  #error "Configuration error: REDCONF_ATIME must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_INODE_TIMESTAMPS == 0) && (REDCONF_ATIME == 1)\r
-  #error "Configuration error: REDCONF_ATIME must be 0 when REDCONF_INODE_TIMESTAMPS is 0."\r
-#endif\r
-\r
-/*  REDCONF_DIRECT_POINTERS and REDCONF_INDIRECT_POINTERS checked in rednodes.h\r
-*/\r
-\r
-#if (REDCONF_INODE_BLOCKS != 0) && (REDCONF_INODE_BLOCKS != 1)\r
-  #error "Configuration error: REDCONF_INODE_BLOCKS must be either 0 or 1."\r
-#endif\r
-\r
-/*  Further validity checking of imap specs done in RelCoreInit()\r
-*/\r
-#if (REDCONF_IMAP_EXTERNAL != 0) && (REDCONF_IMAP_EXTERNAL != 1)\r
-  #error "Configuration error: REDCONF_IMAP_EXTERNAL must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_IMAP_INLINE != 0) && (REDCONF_IMAP_INLINE != 1)\r
-  #error "Configuration error: REDCONF_IMAP_INLINE must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_IMAP_INLINE == 0) && (REDCONF_IMAP_EXTERNAL == 0)\r
-  #error "Configuration error: At least one of REDCONF_IMAP_INLINE and REDCONF_IMAP_EXTERNAL must be set"\r
-#endif\r
-\r
-#if (REDCONF_OUTPUT != 0) && (REDCONF_OUTPUT != 1)\r
-  #error "Configuration error: REDCONF_OUTPUT must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ASSERTS != 0) && (REDCONF_ASSERTS != 1)\r
-  #error "Configuration error: REDCONF_ASSERTS must be either 0 or 1."\r
-#endif\r
-\r
-/*  REDCONF_BLOCK_SIZE checked in redmacs.h\r
-*/\r
-\r
-#if (REDCONF_VOLUME_COUNT < 1U) || (REDCONF_VOLUME_COUNT > 255U)\r
-  #error "REDCONF_VOLUME_COUNT must be an integer between 1 and 255"\r
-#endif\r
-\r
-/*  REDCONF_BUFFER_COUNT lower limit checked in buffer.c\r
-*/\r
-#if REDCONF_BUFFER_COUNT > 255U\r
-  #error "REDCONF_BUFFER_COUNT cannot be greater than 255"\r
-#endif\r
-\r
-#if (REDCONF_IMAGE_BUILDER != 0) && (REDCONF_IMAGE_BUILDER != 1)\r
-  #error "Configuration error: REDCONF_IMAGE_BUILDER must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_CHECKER != 0) && (REDCONF_CHECKER != 1)\r
-  #error "Configuration error: REDCONF_CHECKER must be either 0 or 1."\r
-#endif\r
-\r
-\r
-#endif\r
-\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Compile-time validity checks for the REDCONF macros.
+*/
+#ifndef REDCONFIGCHK_H
+#define REDCONFIGCHK_H
+
+#ifdef RED_CONFIG_MINCOMPAT_VER
+  #if (RED_VERSION_VAL & 0xFFFFFF00U) < (RED_CONFIG_MINCOMPAT_VER & 0xFFFFFF00U)
+    /*  This indicates that your configuration files were generated by a
+        version of the Reliance Edge Configuration Utility that is designed
+        for a more recent version of Reliance Edge and is no longer compatible
+        with this version.  You can update to the most recent version of
+        Reliance Edge or contact RelianceEdgeSupport@datalight.com to obtain
+        the correct legacy version of the Configuration Utility.
+    */
+    #error "Your configuration is not compatible with this version of Reliance Edge. Please download the latest version of Reliance Edge or recreate your configuration with an older version of the Configuration Utility."
+  #endif
+#endif
+
+#ifndef REDCONF_READ_ONLY
+  #error "Configuration error: REDCONF_READ_ONLY must be defined."
+#endif
+#ifndef REDCONF_API_POSIX
+  #error "Configuration error: REDCONF_API_POSIX must be defined."
+#endif
+#ifndef REDCONF_API_FSE
+  #error "Configuration error: REDCONF_API_FSE must be defined."
+#endif
+
+#if REDCONF_API_POSIX == 1
+  #ifndef REDCONF_API_POSIX_FORMAT
+    #error "Configuration error: REDCONF_API_POSIX_FORMAT must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_UNLINK
+    #error "Configuration error: REDCONF_API_POSIX_UNLINK must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_MKDIR
+    #error "Configuration error: REDCONF_API_POSIX_MKDIR must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_RMDIR
+    #error "Configuration error: REDCONF_API_POSIX_RMDIR must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_RENAME
+    #error "Configuration error: REDCONF_API_POSIX_RENAME must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_LINK
+    #error "Configuration error: REDCONF_API_POSIX_LINK must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_FTRUNCATE
+    #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be defined."
+  #endif
+  #ifndef REDCONF_API_POSIX_READDIR
+    #error "Configuration error: REDCONF_API_POSIX_READDIR must be defined."
+  #endif
+  #ifndef REDCONF_NAME_MAX
+    #error "Configuration error: REDCONF_NAME_MAX must be defined."
+  #endif
+  #ifndef REDCONF_PATH_SEPARATOR
+    #error "Configuration error: REDCONF_PATH_SEPARATOR must be defined."
+  #endif
+  #ifndef REDCONF_RENAME_ATOMIC
+    #error "Configuration error: REDCONF_RENAME_ATOMIC must be defined."
+  #endif
+  #ifndef REDCONF_HANDLE_COUNT
+    #error "Configuration error: REDCONF_HANDLE_COUNT must be defined."
+  #endif
+#endif
+#if REDCONF_API_FSE == 1
+  #ifndef REDCONF_API_FSE_FORMAT
+    #error "Configuration error: REDCONF_API_FSE_FORMAT must be defined."
+  #endif
+  #ifndef REDCONF_API_FSE_TRUNCATE
+    #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be defined."
+  #endif
+  #ifndef REDCONF_API_FSE_TRANSMASKSET
+    #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be defined."
+  #endif
+  #ifndef REDCONF_API_FSE_TRANSMASKGET
+    #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be defined."
+  #endif
+#endif
+
+#ifndef REDCONF_TASK_COUNT
+  #error "Configuration error: REDCONF_TASK_COUNT must be defined."
+#endif
+#ifndef REDCONF_ENDIAN_BIG
+  #error "Configuration error: REDCONF_ENDIAN_BIG must be defined."
+#endif
+#ifndef REDCONF_ALIGNMENT_SIZE
+  #error "Configuration error: REDCONF_ALIGNMENT_SIZE must be defined."
+#endif
+#ifndef REDCONF_CRC_ALGORITHM
+  #error "Configuration error: REDCONF_CRC_ALGORITHM must be defined."
+#endif
+#ifndef REDCONF_INODE_TIMESTAMPS
+  #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be defined."
+#endif
+#ifndef REDCONF_ATIME
+  #error "Configuration error: REDCONF_ATIME must be defined."
+#endif
+#ifndef REDCONF_DIRECT_POINTERS
+  #error "Configuration error: REDCONF_DIRECT_POINTERS must be defined."
+#endif
+#ifndef REDCONF_INDIRECT_POINTERS
+  #error "Configuration error: REDCONF_INDIRECT_POINTERS must be defined."
+#endif
+#ifndef REDCONF_INODE_BLOCKS
+  #error "Configuration error: REDCONF_INODE_BLOCKS must be defined."
+#endif
+#ifndef REDCONF_IMAP_EXTERNAL
+  #error "Configuration error: REDCONF_IMAP_EXTERNAL must be defined."
+#endif
+#ifndef REDCONF_IMAP_INLINE
+  #error "Configuration error: REDCONF_IMAP_INLINE must be defined."
+#endif
+#ifndef REDCONF_OUTPUT
+  #error "Configuration error: REDCONF_OUTPUT must be defined."
+#endif
+#ifndef REDCONF_ASSERTS
+  #error "Configuration error: REDCONF_ASSERTS must be defined."
+#endif
+#ifndef REDCONF_TRANSACT_DEFAULT
+  #error "Configuration error: REDCONF_TRANSACT_DEFAULT must be defined."
+#endif
+#ifndef REDCONF_BUFFER_COUNT
+  #error "Configuration error: REDCONF_BUFFER_COUNT must be defined."
+#endif
+#ifndef REDCONF_BLOCK_SIZE
+  #error "Configuration error: REDCONF_BLOCK_SIZE must be defined."
+#endif
+#ifndef REDCONF_VOLUME_COUNT
+  #error "Configuration error: REDCONF_VOLUME_COUNT must be defined."
+#endif
+#ifndef REDCONF_DISCARDS
+    /*  Reliance Edge 1.0.5 and below did not have REDCONF_DISCARDS. You can
+        fix this error by downloading the latest version of the Configuration
+        Utility (assuming you are using the latest version of Reliance Edge)
+        from http://www.datalight.com/reliance-edge, loading your redconf.c
+        and redconf.h files, and saving them again, replacing the original
+        files.
+    */
+  #error "Configuration error: your redconf.h is not compatible. Update your redconf files with a compatible version of the configuration utility."
+#endif
+#ifndef REDCONF_IMAGE_BUILDER
+  #error "Configuration error: REDCONF_IMAGE_BUILDER must be defined."
+#endif
+#ifndef REDCONF_CHECKER
+  #error "Configuration error: REDCONF_CHECKER must be defined."
+#endif
+
+
+#if (REDCONF_READ_ONLY != 0) && (REDCONF_READ_ONLY != 1)
+  #error "Configuration error: REDCONF_READ_ONLY must be either 0 or 1"
+#endif
+
+#if (REDCONF_API_POSIX != 0) && (REDCONF_API_POSIX != 1)
+  #error "Configuration error: REDCONF_API_POSIX must be either 0 or 1."
+#endif
+#if (REDCONF_API_FSE != 0) && (REDCONF_API_FSE != 1)
+  #error "Configuration error: REDCONF_API_FSE must be either 0 or 1."
+#endif
+
+#if (REDCONF_API_FSE == 0) && (REDCONF_API_POSIX == 0)
+  #error "Configuration error: either REDCONF_API_FSE or REDCONF_API_POSIX must be set to 1."
+#endif
+
+#if REDCONF_API_POSIX == 1
+  #if REDCONF_API_FSE != 0
+    #error "Configuration error: REDCONF_API_FSE must be 0 if REDCONF_API_POSIX is 1"
+  #endif
+
+  #if (REDCONF_API_POSIX_FORMAT != 0) && (REDCONF_API_POSIX_FORMAT != 1)
+    #error "Configuration error: REDCONF_API_POSIX_FORMAT must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_UNLINK != 0) && (REDCONF_API_POSIX_UNLINK != 1)
+    #error "Configuration error: REDCONF_API_POSIX_UNLINK must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_MKDIR != 0) && (REDCONF_API_POSIX_MKDIR != 1)
+    #error "Configuration error: REDCONF_API_POSIX_MKDIR must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_RMDIR != 0) && (REDCONF_API_POSIX_RMDIR != 1)
+    #error "Configuration error: REDCONF_API_POSIX_RMDIR must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_RENAME != 0) && (REDCONF_API_POSIX_RENAME != 1)
+    #error "Configuration error: REDCONF_API_POSIX_RENAME must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_LINK != 0) && (REDCONF_API_POSIX_LINK != 1)
+    #error "Configuration error: REDCONF_API_POSIX_LINK must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_FTRUNCATE != 0) && (REDCONF_API_POSIX_FTRUNCATE != 1)
+    #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_POSIX_READDIR != 0) && (REDCONF_API_POSIX_READDIR != 1)
+    #error "Configuration error: REDCONF_API_POSIX_READDIR must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_NAME_MAX < 1U) || (REDCONF_NAME_MAX > (REDCONF_BLOCK_SIZE - 4U))
+    #error "Configuration error: invalid value of REDCONF_NAME_MAX"
+  #endif
+
+  #if (REDCONF_PATH_SEPARATOR < 1) || (REDCONF_PATH_SEPARATOR > 127)
+    #error "Configuration error: invalid value of REDCONF_PATH_SEPARATOR"
+  #endif
+
+  #if (REDCONF_RENAME_ATOMIC != 0) && (REDCONF_RENAME_ATOMIC != 1)
+    #error "Configuration error: REDCONF_RENAME_ATOMIC must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_HANDLE_COUNT < 1U) || (REDCONF_HANDLE_COUNT > 4096U)
+    #error "Configuration error: invalid value of REDCONF_HANDLE_COUNT"
+  #endif
+#endif
+#if REDCONF_API_FSE == 1
+  #if (REDCONF_API_FSE_FORMAT != 0) && (REDCONF_API_FSE_FORMAT != 1)
+    #error "Configuration error: REDCONF_API_FSE_FORMAT must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_FSE_TRUNCATE != 0) && (REDCONF_API_FSE_TRUNCATE != 1)
+    #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_FSE_TRANSMASKSET != 0) && (REDCONF_API_FSE_TRANSMASKSET != 1)
+    #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be either 0 or 1."
+  #endif
+
+  #if (REDCONF_API_FSE_TRANSMASKGET != 0) && (REDCONF_API_FSE_TRANSMASKGET != 1)
+    #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be either 0 or 1."
+  #endif
+#endif
+
+#if REDCONF_TASK_COUNT < 1U
+  #error "Configuration error: invalid value of REDCONF_TASK_COUNT"
+#endif
+
+#if (REDCONF_ENDIAN_BIG != 0) && (REDCONF_ENDIAN_BIG != 1)
+  #error "Configuration error: REDCONF_ENDIAN_BIG must be either 0 or 1."
+#endif
+
+#if (REDCONF_ALIGNMENT_SIZE != 1U) && (REDCONF_ALIGNMENT_SIZE != 2U) && (REDCONF_ALIGNMENT_SIZE != 4U) && (REDCONF_ALIGNMENT_SIZE != 8U)
+  #error "Configuration error: invalid value REDCONF_ALIGNMENT_SIZE"
+#endif
+
+/*  REDCONF_CRC_ALGORITHM checked in crc.c
+*/
+
+#if (REDCONF_INODE_TIMESTAMPS != 0) && (REDCONF_INODE_TIMESTAMPS != 1)
+  #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be either 0 or 1."
+#endif
+
+#if (REDCONF_ATIME != 0) && (REDCONF_ATIME != 1)
+  #error "Configuration error: REDCONF_ATIME must be either 0 or 1."
+#endif
+
+#if (REDCONF_INODE_TIMESTAMPS == 0) && (REDCONF_ATIME == 1)
+  #error "Configuration error: REDCONF_ATIME must be 0 when REDCONF_INODE_TIMESTAMPS is 0."
+#endif
+
+/*  REDCONF_DIRECT_POINTERS and REDCONF_INDIRECT_POINTERS checked in rednodes.h
+*/
+
+#if (REDCONF_INODE_BLOCKS != 0) && (REDCONF_INODE_BLOCKS != 1)
+  #error "Configuration error: REDCONF_INODE_BLOCKS must be either 0 or 1."
+#endif
+
+/*  Further validity checking of imap specs done in RelCoreInit()
+*/
+#if (REDCONF_IMAP_EXTERNAL != 0) && (REDCONF_IMAP_EXTERNAL != 1)
+  #error "Configuration error: REDCONF_IMAP_EXTERNAL must be either 0 or 1."
+#endif
+#if (REDCONF_IMAP_INLINE != 0) && (REDCONF_IMAP_INLINE != 1)
+  #error "Configuration error: REDCONF_IMAP_INLINE must be either 0 or 1."
+#endif
+#if (REDCONF_IMAP_INLINE == 0) && (REDCONF_IMAP_EXTERNAL == 0)
+  #error "Configuration error: At least one of REDCONF_IMAP_INLINE and REDCONF_IMAP_EXTERNAL must be set"
+#endif
+
+#if (REDCONF_OUTPUT != 0) && (REDCONF_OUTPUT != 1)
+  #error "Configuration error: REDCONF_OUTPUT must be either 0 or 1."
+#endif
+
+#if (REDCONF_ASSERTS != 0) && (REDCONF_ASSERTS != 1)
+  #error "Configuration error: REDCONF_ASSERTS must be either 0 or 1."
+#endif
+
+/*  REDCONF_BLOCK_SIZE checked in redmacs.h
+*/
+
+#if (REDCONF_VOLUME_COUNT < 1U) || (REDCONF_VOLUME_COUNT > 255U)
+  #error "REDCONF_VOLUME_COUNT must be an integer between 1 and 255"
+#endif
+
+#if (REDCONF_DISCARDS != 0) && (REDCONF_DISCARDS != 1)
+  #error "Configuration error: REDCONF_DISCARDS must be either 0 or 1."
+#endif
+
+/*  REDCONF_BUFFER_COUNT lower limit checked in buffer.c
+*/
+#if REDCONF_BUFFER_COUNT > 255U
+  #error "REDCONF_BUFFER_COUNT cannot be greater than 255"
+#endif
+
+#if (REDCONF_IMAGE_BUILDER != 0) && (REDCONF_IMAGE_BUILDER != 1)
+  #error "Configuration error: REDCONF_IMAGE_BUILDER must be either 0 or 1."
+#endif
+
+#if (REDCONF_CHECKER != 0) && (REDCONF_CHECKER != 1)
+  #error "Configuration error: REDCONF_CHECKER must be either 0 or 1."
+#endif
+
+
+#if (REDCONF_DISCARDS == 1) && (RED_KIT == RED_KIT_GPL)
+  #error "REDCONF_DISCARDS not supported in Reliance Edge under GPL. Contact sales@datalight.com to upgrade."
+#endif
+
+
+#endif
+
+
index 6d149a3e55872b9477c487c8b24aaa6b72820890..01a003c870b8aefcb0c2b1a7d08a94d7dadbb5cc 100644 (file)
@@ -49,6 +49,5 @@
            || ((REDCONF_API_FSE == 1) && (REDCONF_API_FSE_FORMAT == 1)) \\r
            || (REDCONF_IMAGE_BUILDER == 1)))\r
 \r
-\r
 #endif\r
 \r
index 8875afbe975e7990994ff45669bef30f87bda5b0..512d703fb7ddd877b0bfc2873828633ac65520ec 100644 (file)
@@ -29,6 +29,7 @@
 \r
 \r
 #include <redconf.h>\r
+#include "redver.h"\r
 #include "redconfigchk.h"\r
 #include <redtypes.h>\r
 #include "rederrno.h"\r
@@ -38,7 +39,6 @@
 #include "redutils.h"\r
 #include "redosserv.h"\r
 #include "redmisc.h"\r
-#include "redver.h"\r
 #include "redexclude.h"\r
 \r
 \r
index cbe768336eac9dac977a3fd5491b0fb9b444ad50..6cd776953a441d473de79aeb4925fda0e9cc66bb 100644 (file)
@@ -64,7 +64,7 @@
 #define STOCH_POSIX_TEST_SUPPORTED \
     (    ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
       && (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \
-      && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \
+      && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \
       && (REDCONF_API_POSIX_MKDIR == 1) && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_UNLINK == 1) \
       && (REDCONF_API_POSIX_RENAME == 1))
 
index 6a1f14ff007f5e5efc3f1d43e18d9d1046989794..7772552c582966d89fa7a332217d4b3156d54582 100644 (file)
@@ -30,6 +30,7 @@
 \r
 \r
 uint8_t RedFindVolumeNumber(const char *pszVolume);\r
+bool RedConfirmOperation(const char *pszMessage);\r
 \r
 \r
 #endif\r
diff --git a/FreeRTOS-Plus/Source/Reliance-Edge/include/redtools.h b/FreeRTOS-Plus/Source/Reliance-Edge/include/redtools.h
new file mode 100644 (file)
index 0000000..f80ffb1
--- /dev/null
@@ -0,0 +1,183 @@
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+#ifndef REDTOOLS_H
+#define REDTOOLS_H
+
+
+#ifdef _WIN32
+  #include <Windows.h>
+  #define HOST_PATH_MAX MAX_PATH
+#else
+  #include <linux/limits.h>
+  #define HOST_PATH_MAX PATH_MAX
+#endif
+
+
+#if REDCONF_IMAGE_BUILDER == 1
+
+#define MACRO_NAME_MAX_LEN 32
+
+typedef struct
+{
+    uint8_t     bVolNumber;
+    const char *pszInputDir;
+    const char *pszOutputFile;
+  #if REDCONF_API_POSIX == 1
+    const char *pszVolName;
+  #else
+    const char *pszMapFile;
+    const char *pszDefineFile;
+    bool        fNowarn;
+  #endif
+} IMGBLDPARAM;
+
+
+void ImgbldParseParams(int argc, char *argv [], IMGBLDPARAM *pParam);
+int ImgbldStart(IMGBLDPARAM *pParam);
+
+
+typedef struct
+{
+  #if REDCONF_API_POSIX == 1
+    char     asOutFilePath[HOST_PATH_MAX];
+  #else
+    uint32_t ulOutFileIndex;
+  #endif
+    char     asInFilePath[HOST_PATH_MAX];
+} FILEMAPPING;
+
+
+extern void *gpCopyBuffer;
+extern uint32_t gulCopyBufferSize;
+
+
+/*  Implemented in ibposix.c
+*/
+#if REDCONF_API_POSIX == 1
+REDSTATUS IbPosixCopyDir(const char *pszVolName, const char *pszInDir);
+int IbPosixCreateDir(const char *pszVolName, const char *pszFullPath, const char *pszBasePath);
+int IbConvertPath(const char *pszVolName, const char *pszFullPath, const char *pszBasePath, char *szOutPath);
+#endif
+
+
+/*  Implemented in ibfse.c
+*/
+#if REDCONF_API_FSE == 1
+typedef struct sFILELISTENTRY FILELISTENTRY;
+struct sFILELISTENTRY
+{
+    FILEMAPPING     fileMapping;
+    FILELISTENTRY  *pNext;
+};
+
+
+void FreeFileList(FILELISTENTRY **ppsFileList);
+
+int IbFseGetFileList(const char *pszPath, const char *pszIndirPath, FILELISTENTRY **ppFileListHead);
+int IbFseOutputDefines(FILELISTENTRY *pFileList, const IMGBLDPARAM *pOptions);
+int IbFseCopyFiles(int volNum, const FILELISTENTRY *pFileList);
+#endif
+
+
+/*  Implemented in os-specific space (ibwin.c and iblinux.c)
+*/
+#if REDCONF_API_POSIX == 1
+int IbPosixCopyDirRecursive(const char *pszVolName, const char *pszInDir);
+#endif
+#if REDCONF_API_FSE == 1
+int IbFseBuildFileList(const char *pszDirPath, FILELISTENTRY **ppFileListHead);
+#endif
+#if REDCONF_API_FSE == 1
+int IbSetRelativePath(char *pszPath, const char *pszParentPath);
+#endif
+bool IsRegularFile(const char *pszPath);
+
+
+/*  Implemented in ibcommon.c
+*/
+int IbCopyFile(int volNum, const FILEMAPPING *pFileMapping);
+int IbCheckFileExists(const char *pszPath, bool *pfExists);
+
+
+/*  Implemented separately in ibfse.c and ibposix.c
+*/
+int IbApiInit(void);
+int IbApiUninit(void);
+int IbWriteFile(int volNum, const FILEMAPPING *pFileMapping, uint64_t ullOffset, void *pData, uint32_t ulDataLen);
+
+#endif /* IMAGE_BUILDER */
+
+/*  For image copier tool
+*/
+
+#ifdef _WIN32
+  #define HOST_PSEP '\\'
+  #if !__STDC__
+    #define snprintf _snprintf
+    #define stat _stat
+    #define S_IFDIR _S_IFDIR
+    #define rmdir _rmdir
+  #endif
+#else
+  #define HOST_PSEP '/'
+#endif
+
+typedef struct
+{
+    uint8_t     bVolNumber;
+    const char *pszOutputDir;
+    const char *pszBDevSpec;
+  #if REDCONF_API_POSIX == 1
+    const char *pszVolName;
+  #endif
+    bool        fNoWarn;
+} IMGCOPYPARAM;
+
+typedef struct
+{
+  #if REDCONF_API_POSIX == 1
+    const char *pszVolume;      /* Volume path prefix. */
+    uint32_t    ulVolPrefixLen; /* strlen(COPIER::pszVolume) */
+  #else
+    uint8_t     bVolNum;        /* Volume number. */
+  #endif
+    const char *pszOutputDir;   /* Output directory path. */
+    bool        fNoWarn;        /* If true, no warning to overwrite. */
+    uint8_t    *pbCopyBuffer;   /* Buffer for copying file data. */
+} COPIER;
+
+
+void ImgcopyParseParams(int argc, char *argv [], IMGCOPYPARAM *pParam);
+int ImgcopyStart(IMGCOPYPARAM *pParam);
+
+/*  Implemented separately in imgcopywin.c and imgcopylinux.c.  These functions
+    print an error message and abort on failure.
+*/
+void ImgcopyMkdir(const char *pszDir);
+void ImgcopyRecursiveRmdir(const char *pszDir);
+
+
+#endif /* REDTOOLS_H */
+
index 9cfb8425914c0208dea627e212663abb0124397a..28793a6e4b34d7317b2cee1bd271372369237553 100644 (file)
@@ -33,7 +33,7 @@
 
     <!-- This macro is updated automatically: do not edit! -->
 */
-#define RED_BUILD_NUMBER "677"
+#define RED_BUILD_NUMBER "700"
 
 #define RED_KIT_GPL         0U  /* Open source GPL kit. */
 #define RED_KIT_COMMERCIAL  1U  /* Commercially-licensed kit. */
 
 /** @brief Version number to display in output.
 */
-#define RED_VERSION "v1.0.4"
+#define RED_VERSION "v2.0"
 
+/** @brief Version number in hex.
+
+    The most significant byte is the major version number, etc.
+*/
+#define RED_VERSION_VAL 0x02000000U
 
 /** @brief On-disk version number.
 
@@ -67,7 +72,7 @@
 /*  Specifies whether the product is in alpha stage, beta stage, or neither.
 */
 #if 0
-  #if 1
+  #if 0
     #define ALPHABETA   " (Alpha)"
   #else
     #define ALPHABETA   " (Beta)"
@@ -83,7 +88,7 @@
 
 /** @brief Product copyright.
 */
-#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2016 Datalight, Inc.  All Rights Reserved Worldwide."
+#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2017 Datalight, Inc.  All Rights Reserved Worldwide."
 
 
 /** @brief Product patents.
index 8fa1fcdaf891b422f903c7fe1aa4df6e098b19f5..bb420876333435f68248fafabbec454fb773b2de 100644 (file)
@@ -1061,6 +1061,7 @@ static REDSTATUS DiskFlush(
   #error "The STM32 StdPeriph driver is not supported. Please use the HAL driver or modify the Reliance Edge block device interface."
 #endif
 
+
 /** @brief Number of times to call BSP_SD_GetStatus() before timing out and
            returning an error.
 
index 2e562ec0dba3108a8fd68003d84835f68b83d16f..ecdf0a7b8bee39f25d874cc307197d1bc3b307fd 100644 (file)
@@ -32,7 +32,7 @@
 #include <redcoreapi.h>\r
 #include <redvolume.h>\r
 #include <redposix.h>\r
-#include "redpath.h"\r
+#include <redpath.h>\r
 \r
 \r
 static bool IsRootDir(const char *pszLocalPath);\r
@@ -405,11 +405,11 @@ static bool IsRootDir(
     Example | Result\r
     ------- | ------\r
     ""        false\r
-    "\"       false\r
-    "\\"      false\r
+    "/"       false\r
+    "//"      false\r
     "a"       true\r
-    "\a"      true\r
-    "\\a"     true\r
+    "/a"      true\r
+    "//a"     true\r
 \r
     @param pszPathIdx   The path to examine, incremented to the point of\r
                         interest.\r
index 34c49869f8ad70e71b510d9a705f9ef9f42258c0..7a21f0696447e8409c207ed9c43ba351ad734c8b 100644 (file)
@@ -37,7 +37,7 @@
 #include <redvolume.h>\r
 #include <redcoreapi.h>\r
 #include <redposix.h>\r
-#include "redpath.h"\r
+#include <redpath.h>\r
 \r
 \r
 /*-------------------------------------------------------------------\r
index a440dfe656427125a03251f5f6069a396b29d09f..abc2aa960bacfdf74665bb05956d29f00de6de63 100644 (file)
-/*\r
- * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.\r
- *\r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of version 2 of the GNU General Public License as\r
- * published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it would be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
- *\r
- * Further, this software is distributed without any warranty that it is\r
- * free of the rightful claim of any third person regarding infringement\r
- * or the like.  Any license provided herein, whether implied or\r
- * otherwise, applies only to this software file.  Patent licenses, if\r
- * any, provided herein do not apply to combinations of this program with\r
- * other software, or any other product whatsoever.\r
- *\r
- * You should have received a copy of the GNU General Public License along\r
- * with this program; if not, write the Free Software Foundation, Inc.,\r
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
- *\r
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,\r
- * Mountain View, CA  94043, or:\r
- *\r
- * http://www.sgi.com\r
- *\r
- * For further information regarding this notice, see:\r
- *\r
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/\r
- */\r
-/** @file\r
-    @brief File system stress test.\r
-\r
-    This version of SGI fsstress has been modified to be single-threaded and to\r
-    work with the Reliance Edge POSIX-like API.\r
-*/\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <limits.h>\r
-#include <string.h>\r
-#include <stdarg.h>\r
-#include <time.h>\r
-\r
-#include <redposix.h>\r
-#include <redtests.h>\r
-\r
-#if FSSTRESS_SUPPORTED\r
-\r
-#include "redposixcompat.h"\r
-\r
-#include <redosserv.h>\r
-#include <redutils.h>\r
-#include <redmacs.h>\r
-#include <redvolume.h>\r
-#include <redgetopt.h>\r
-#include <redtoolcmn.h>\r
-\r
-#if REDCONF_CHECKER == 1\r
-#include <redcoreapi.h>\r
-#endif\r
-\r
-\r
-/*  Create POSIX types.  Use #define to avoid name conflicts in those\r
-    environments where the type names already exist.\r
-*/\r
-#define off_t int64_t\r
-#define off64_t off_t\r
-#define ino_t uint32_t\r
-#define mode_t uint16_t\r
-#define __int64_t int64_t\r
-\r
-\r
-/** @brief Generate a random number.\r
-\r
-    @return A nonnegative random number.\r
-*/\r
-#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))\r
-\r
-\r
-/** @brief Seed the random number generator.\r
-*/\r
-#define srandom(seed) RedRandSeed(seed)\r
-\r
-\r
-#define _exit(status) exit(status)\r
-#define getpagesize() 4096U\r
-#define getpid() 1\r
-\r
-\r
-/** @brief Determine the maximum file size.\r
-\r
-    This is used for the MAXFSSIZE macro.\r
-*/\r
-static uint64_t MaxFileSize(void)\r
-{\r
-    REDSTATFS   info;\r
-    int32_t     iStatus;\r
-    REDSTATUS   errnoSave = errno;\r
-    uint64_t    ullMaxFileSize;\r
-\r
-    iStatus = red_statvfs("", &info);\r
-    if(iStatus == 0)\r
-    {\r
-        ullMaxFileSize = info.f_maxfsize;\r
-    }\r
-    else\r
-    {\r
-        /*  This function does not change errno.\r
-        */\r
-        errno = errnoSave;\r
-\r
-        ullMaxFileSize = 0x7FFFFFFFU;\r
-    }\r
-\r
-    return ullMaxFileSize;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    Simulated current working directory support\r
--------------------------------------------------------------------*/\r
-\r
-\r
-/*  Forward declaration for red_chdir().\r
-*/\r
-static int red_stat(const char *pszPath, REDSTAT *pStat);\r
-\r
-/*  The simulated CWD functions.\r
-*/\r
-#undef chdir\r
-#undef getcwd\r
-#define chdir(path) red_chdir(path)\r
-#define getcwd(buf, size) red_getcwd(buf, size)\r
-\r
-\r
-/*  Redefine the path-based APIs to call MakeFullPath() on their arguments\r
-    since there is no CWD support in the red_*() APIs.\r
-*/\r
-#undef open\r
-#undef unlink\r
-#undef mkdir\r
-#undef rmdir\r
-#undef rename\r
-#undef link\r
-#undef opendir\r
-#define open(path, oflag) red_open(MakeFullPath(path), oflag)\r
-#define unlink(path) red_unlink(MakeFullPath(path))\r
-#define mkdir(path) red_mkdir(MakeFullPath(path))\r
-#define rmdir(path) red_rmdir(MakeFullPath(path))\r
-#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))\r
-#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))\r
-#define opendir(path) red_opendir(MakeFullPath(path))\r
-\r
-\r
-/*  Stores the simulated current working directory.\r
-*/\r
-static char szLocalCwd[1024U] = "/";\r
-\r
-\r
-/** @brief Change the current working directory.\r
-\r
-    This function only supports a subset of what is possible with POSIX chdir().\r
-\r
-    @param pszPath  The new current working directory.\r
-\r
-    @return Upon successful completion, 0 shall be returned.  Otherwise, -1\r
-            shall be returned, and errno shall be set to indicate the error.\r
-*/\r
-static int red_chdir(\r
-    const char *pszPath)\r
-{\r
-    uint32_t    ulIdx;\r
-    int         iErrno = 0;\r
-\r
-    if(strcmp(pszPath, "..") == 0)\r
-    {\r
-        uint32_t ulLastSlashIdx = 0U;\r
-\r
-        /*  Chop off the last path separator and everything after it, so that\r
-            "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.\r
-        */\r
-        for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)\r
-        {\r
-            if(szLocalCwd[ulIdx] == '/')\r
-            {\r
-                ulLastSlashIdx = ulIdx;\r
-            }\r
-        }\r
-\r
-        if(ulLastSlashIdx != 0U)\r
-        {\r
-            szLocalCwd[ulLastSlashIdx] = '\0';\r
-        }\r
-    }\r
-    else\r
-    {\r
-        char    szOldCwd[1024U];\r
-\r
-        /*  chdir() must have no effect on the CWD if it fails, so save the CWD\r
-            so we can revert it if necessary.\r
-        */\r
-        strcpy(szOldCwd, szLocalCwd);\r
-\r
-        if(pszPath[0U] == '/')\r
-        {\r
-            if(strlen(pszPath) >= sizeof(szLocalCwd))\r
-            {\r
-                iErrno = RED_ENAMETOOLONG;\r
-            }\r
-            else\r
-            {\r
-                strcpy(szLocalCwd, pszPath);\r
-            }\r
-        }\r
-        else\r
-        {\r
-            ulIdx = strlen(szLocalCwd);\r
-\r
-            if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))\r
-            {\r
-                iErrno = RED_ENAMETOOLONG;\r
-            }\r
-            else\r
-            {\r
-                if(szLocalCwd[1U] != '\0')\r
-                {\r
-                    szLocalCwd[ulIdx] = '/';\r
-                    ulIdx++;\r
-                }\r
-\r
-                strcpy(&szLocalCwd[ulIdx], pszPath);\r
-            }\r
-        }\r
-\r
-        if(iErrno == 0)\r
-        {\r
-            REDSTAT s;\r
-            int     iStatus;\r
-\r
-            iStatus = red_stat(szLocalCwd, &s);\r
-            if(iStatus != 0)\r
-            {\r
-                iErrno = errno;\r
-            }\r
-            else if(!S_ISDIR(s.st_mode))\r
-            {\r
-                iErrno = RED_ENOTDIR;\r
-            }\r
-            else\r
-            {\r
-                /*  No error, new CWD checks out.\r
-                */\r
-            }\r
-        }\r
-\r
-        if(iErrno != 0)\r
-        {\r
-            strcpy(szLocalCwd, szOldCwd);\r
-        }\r
-    }\r
-\r
-    if(iErrno != 0)\r
-    {\r
-        errno = iErrno;\r
-    }\r
-\r
-    return iErrno == 0 ? 0 : -1;\r
-}\r
-\r
-\r
-/** @brief Retrieve the current working directory.\r
-\r
-    @param pszBuf   On successful return, populated with the current working\r
-                    directory.  If NULL, memory will be allocated for the CWD\r
-                    and returned by this function.\r
-    @param nSize    The size of @p pszBuf.\r
-\r
-    @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if\r
-            @p pszBuf was NULL, returns an allocated buffer populated with the\r
-            CWD which must be freed by the caller.  On failure, returns NULL\r
-            and errno will be set.\r
-*/\r
-static char *red_getcwd(\r
-    char   *pszBuf,\r
-    size_t  nSize)\r
-{\r
-    char   *pszRet;\r
-\r
-    if(pszBuf == NULL)\r
-    {\r
-        pszRet = malloc(strlen(szLocalCwd) + 1U);\r
-        if(pszRet == NULL)\r
-        {\r
-            errno = RED_ENOMEM;\r
-        }\r
-        else\r
-        {\r
-            strcpy(pszRet, szLocalCwd);\r
-        }\r
-    }\r
-    else if(nSize < strlen(szLocalCwd) + 1U)\r
-    {\r
-        errno = RED_ERANGE;\r
-        pszRet = NULL;\r
-    }\r
-    else\r
-    {\r
-        strcpy(pszBuf, szLocalCwd);\r
-        pszRet = pszBuf;\r
-    }\r
-\r
-    return pszRet;\r
-}\r
-\r
-\r
-/** @brief Make a relative path into a fully qualified path.\r
-\r
-    @param pszName  The relative path.\r
-\r
-    @return On success, a pointer to a fully qualified path.  On error, NULL.\r
-*/\r
-static const char *MakeFullPath(\r
-    const char     *pszName)\r
-{\r
-    #define         MAXVOLNAME 64U /* Enough for most configs. */\r
-    static char     aszFullPath[2U][MAXVOLNAME + 1U + 1024U];\r
-    static uint32_t ulWhich = 0U;\r
-\r
-    char           *pszFullPath = aszFullPath[ulWhich];\r
-    const char     *pszVolume = gpRedVolConf->pszPathPrefix;\r
-    int32_t         iLen;\r
-\r
-    if(pszName[0U] == '/')\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);\r
-    }\r
-    else if(strcmp(pszName, ".") == 0U)\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);\r
-    }\r
-    else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);\r
-    }\r
-    else\r
-    {\r
-        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);\r
-    }\r
-\r
-    if(iLen == -1)\r
-    {\r
-        /*  Insufficient path buffer space.\r
-        */\r
-        pszFullPath = NULL;\r
-    }\r
-    else\r
-    {\r
-        /*  Toggle between two full path arrays; a kluge to make rename() and\r
-            link() work correctly.\r
-        */\r
-        ulWhich ^= 1U;\r
-    }\r
-\r
-    return pszFullPath;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    POSIX functions not implemented by the RED POSIX-like API\r
--------------------------------------------------------------------*/\r
-\r
-#define stat(p, s) red_stat(p, s)\r
-#define stat64(p, s) stat(p, s)\r
-#define lstat(p, s) stat(p, s)\r
-#define lstat64(p, s) stat(p, s)\r
-#define truncate(p, s) red_truncate(p, s)\r
-#define truncate64(p, s) truncate(p, s)\r
-\r
-\r
-/** @brief Get the status of a file or directory.\r
-*/\r
-static int red_stat(\r
-    const char *pszPath,\r
-    REDSTAT    *pStat)\r
-{\r
-    int         iFd;\r
-    int         iRet;\r
-\r
-    iFd = open(pszPath, O_RDONLY);\r
-    iRet = iFd;\r
-    if(iFd != -1)\r
-    {\r
-        iRet = fstat(iFd, pStat);\r
-\r
-        (void)close(iFd);\r
-    }\r
-\r
-    return iRet;\r
-}\r
-\r
-\r
-/** @brief Truncate a file to a specified length.\r
-*/\r
-static int red_truncate(\r
-    const char *pszPath,\r
-    off_t       llSize)\r
-{\r
-    int         iFd;\r
-    int         iRet;\r
-\r
-    iFd = open(pszPath, O_WRONLY);\r
-    iRet = iFd;\r
-    if(iFd != -1)\r
-    {\r
-        iRet = ftruncate(iFd, llSize);\r
-\r
-        (void)close(iFd);\r
-    }\r
-\r
-    return iRet;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
-    Begin ported fsstress code\r
--------------------------------------------------------------------*/\r
-\r
-/* Stuff from xfscompat.h */\r
-\r
-#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */\r
-\r
-struct dioattr {\r
-    int d_miniosz, d_maxiosz, d_mem;\r
-};\r
-\r
-#define MIN(a,b) ((a)<(b) ? (a):(b))\r
-#define MAX(a,b) ((a)>(b) ? (a):(b))\r
-\r
-/* End xfscompat.h */\r
-\r
-\r
-typedef enum {\r
-    OP_CREAT,\r
-    OP_FDATASYNC,\r
-    OP_FSYNC,\r
-    OP_GETDENTS,\r
-    OP_LINK,\r
-    OP_MKDIR,\r
-    OP_READ,\r
-    OP_RENAME,\r
-    OP_RMDIR,\r
-    OP_STAT,\r
-    OP_TRUNCATE,\r
-    OP_UNLINK,\r
-    OP_WRITE,\r
-  #if REDCONF_CHECKER == 1\r
-    OP_CHECK,\r
-  #endif\r
-    OP_LAST\r
-} opty_t;\r
-\r
-typedef void (*opfnc_t) (int, long);\r
-\r
-typedef struct opdesc {\r
-    opty_t op;\r
-    const char *name;\r
-    opfnc_t func;\r
-    int freq;\r
-    int iswrite;\r
-} opdesc_t;\r
-\r
-typedef struct fent {\r
-    int id;\r
-    int parent;\r
-} fent_t;\r
-\r
-typedef struct flist {\r
-    int nfiles;\r
-    int nslots;\r
-    int tag;\r
-    fent_t *fents;\r
-} flist_t;\r
-\r
-typedef struct pathname {\r
-    int len;\r
-    char *path;\r
-} pathname_t;\r
-\r
-#define FT_DIR      0\r
-#define FT_DIRm     (1 << FT_DIR)\r
-#define FT_REG      1\r
-#define FT_REGm     (1 << FT_REG)\r
-#define FT_SYM      2\r
-#define FT_SYMm     (1 << FT_SYM)\r
-#define FT_DEV      3\r
-#define FT_DEVm     (1 << FT_DEV)\r
-#define FT_RTF      4\r
-#define FT_RTFm     (1 << FT_RTF)\r
-#define FT_nft      5\r
-#define FT_ANYm     ((1 << FT_nft) - 1)\r
-#define FT_REGFILE  (FT_REGm | FT_RTFm)\r
-#define FT_NOTDIR   (FT_ANYm & ~FT_DIRm)\r
-\r
-#define FLIST_SLOT_INCR 16\r
-#define NDCACHE 64\r
-\r
-#define MAXFSIZE MaxFileSize()\r
-\r
-static void creat_f(int opno, long r);\r
-static void fdatasync_f(int opno, long r);\r
-static void fsync_f(int opno, long r);\r
-static void getdents_f(int opno, long r);\r
-static void link_f(int opno, long r);\r
-static void mkdir_f(int opno, long r);\r
-static void read_f(int opno, long r);\r
-static void rename_f(int opno, long r);\r
-static void rmdir_f(int opno, long r);\r
-static void stat_f(int opno, long r);\r
-static void truncate_f(int opno, long r);\r
-static void unlink_f(int opno, long r);\r
-static void write_f(int opno, long r);\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r);\r
-#endif\r
-\r
-static opdesc_t ops[] = {\r
-    {OP_CREAT, "creat", creat_f, 4, 1},\r
-    {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},\r
-    {OP_FSYNC, "fsync", fsync_f, 1, 1},\r
-    {OP_GETDENTS, "getdents", getdents_f, 1, 0},\r
-    {OP_LINK, "link", link_f, 1, 1},\r
-    {OP_MKDIR, "mkdir", mkdir_f, 2, 1},\r
-    {OP_READ, "read", read_f, 1, 0},\r
-    {OP_RENAME, "rename", rename_f, 2, 1},\r
-    {OP_RMDIR, "rmdir", rmdir_f, 1, 1},\r
-    {OP_STAT, "stat", stat_f, 1, 0},\r
-    {OP_TRUNCATE, "truncate", truncate_f, 2, 1},\r
-    {OP_UNLINK, "unlink", unlink_f, 1, 1},\r
-    {OP_WRITE, "write", write_f, 4, 1},\r
-  #if REDCONF_CHECKER == 1\r
-    {OP_CHECK, "check", check_f, 1, 1},\r
-  #endif\r
-}, *ops_end;\r
-\r
-static flist_t flist[FT_nft] = {\r
-    {0, 0, 'd', NULL},\r
-    {0, 0, 'f', NULL},\r
-    {0, 0, 'l', NULL},\r
-    {0, 0, 'c', NULL},\r
-    {0, 0, 'r', NULL},\r
-};\r
-\r
-static int dcache[NDCACHE];\r
-static opty_t *freq_table;\r
-static int freq_table_size;\r
-static char *homedir;\r
-static int *ilist;\r
-static int ilistlen;\r
-static off64_t maxfsize;\r
-static int namerand;\r
-static int nameseq;\r
-static int nops;\r
-static int operations = 1;\r
-static int procid;\r
-static int rtpct;\r
-static unsigned long seed = 0;\r
-static ino_t top_ino;\r
-static int verbose = 0;\r
-\r
-static int delete_tree(const char *path);\r
-static void add_to_flist(int fd, int it, int parent);\r
-static void append_pathname(pathname_t *name, const char *str);\r
-static void check_cwd(void);\r
-static int creat_path(pathname_t *name, mode_t mode);\r
-static void dcache_enter(int dirid, int slot);\r
-static void dcache_init(void);\r
-static fent_t *dcache_lookup(int dirid);\r
-static void dcache_purge(int dirid);\r
-static void del_from_flist(int ft, int slot);\r
-static void doproc(void);\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);\r
-static void fix_parent(int oldid, int newid);\r
-static void free_pathname(pathname_t *name);\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);\r
-static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);\r
-static void init_pathname(pathname_t *name);\r
-static int link_path(pathname_t *name1, pathname_t *name2);\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static void make_freq_table(void);\r
-static int mkdir_path(pathname_t *name, mode_t mode);\r
-static void namerandpad(int id, char *buf, int len);\r
-static int open_path(pathname_t *name, int oflag);\r
-static DIR *opendir_path(pathname_t *name);\r
-static int rename_path(pathname_t *name1, pathname_t *name2);\r
-static int rmdir_path(pathname_t *name);\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static int truncate64_path(pathname_t *name, off64_t length);\r
-static int unlink_path(pathname_t *name);\r
-static void usage(const char *progname);\r
-\r
-\r
-/** @brief Parse parameters for fsstress.\r
-\r
-    @param argc         The number of arguments from main().\r
-    @param argv         The vector of arguments from main().\r
-    @param pParam       Populated with the fsstress parameters.\r
-    @param pbVolNum     If non-NULL, populated with the volume number.\r
-    @param ppszDevice   If non-NULL, populated with the device name argument or\r
-                        NULL if no device argument is provided.\r
-\r
-    @return The result of parsing the parameters.\r
-*/\r
-PARAMSTATUS FsstressParseParams(\r
-    int             argc,\r
-    char           *argv[],\r
-    FSSTRESSPARAM  *pParam,\r
-    uint8_t        *pbVolNum,\r
-    const char    **ppszDevice)\r
-{\r
-    int             c;\r
-    uint8_t         bVolNum;\r
-    const REDOPTION aLongopts[] =\r
-    {\r
-        { "no-cleanup", red_no_argument, NULL, 'c' },\r
-        { "loops", red_required_argument, NULL, 'l' },\r
-        { "nops", red_required_argument, NULL, 'n' },\r
-        { "namepad", red_no_argument, NULL, 'r' },\r
-        { "seed", red_required_argument, NULL, 's' },\r
-        { "verbose", red_no_argument, NULL, 'v' },\r
-        { "dev", red_required_argument, NULL, 'D' },\r
-        { "help", red_no_argument, NULL, 'H' },\r
-        { NULL }\r
-    };\r
-\r
-    /*  If run without parameters, treat as a help request.\r
-    */\r
-    if(argc <= 1)\r
-    {\r
-        goto Help;\r
-    }\r
-\r
-    /*  Assume no device argument to start with.\r
-    */\r
-    if(ppszDevice != NULL)\r
-    {\r
-        *ppszDevice = NULL;\r
-    }\r
-\r
-    /*  Set default parameters.\r
-    */\r
-    FsstressDefaultParams(pParam);\r
-\r
-    while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)\r
-    {\r
-        switch(c)\r
-        {\r
-            case 'c': /* --no-cleanup */\r
-                pParam->fNoCleanup = true;\r
-                break;\r
-            case 'l': /* --loops */\r
-                pParam->ulLoops = RedAtoI(red_optarg);\r
-                break;\r
-            case 'n': /* --nops */\r
-                pParam->ulNops = RedAtoI(red_optarg);\r
-                break;\r
-            case 'r': /* --namepad */\r
-                pParam->fNamePad = true;\r
-                break;\r
-            case 's': /* --seed */\r
-                pParam->ulSeed = RedAtoI(red_optarg);\r
-                break;\r
-            case 'v': /* --verbose */\r
-                pParam->fVerbose = true;\r
-                break;\r
-            case 'D': /* --dev */\r
-                if(ppszDevice != NULL)\r
-                {\r
-                    *ppszDevice = red_optarg;\r
-                }\r
-                break;\r
-            case 'H': /* --help */\r
-                goto Help;\r
-            case '?': /* Unknown or ambiguous option */\r
-            case ':': /* Option missing required argument */\r
-            default:\r
-                goto BadOpt;\r
-        }\r
-    }\r
-\r
-    /*  RedGetoptLong() has permuted argv to move all non-option arguments to\r
-        the end.  We expect to find a volume identifier.\r
-    */\r
-    if(red_optind >= argc)\r
-    {\r
-        RedPrintf("Missing volume argument\n");\r
-        goto BadOpt;\r
-    }\r
-\r
-    bVolNum = RedFindVolumeNumber(argv[red_optind]);\r
-    if(bVolNum == REDCONF_VOLUME_COUNT)\r
-    {\r
-        RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);\r
-        goto BadOpt;\r
-    }\r
-\r
-    if(pbVolNum != NULL)\r
-    {\r
-        *pbVolNum = bVolNum;\r
-    }\r
-\r
-    red_optind++; /* Move past volume parameter. */\r
-    if(red_optind < argc)\r
-    {\r
-        int32_t ii;\r
-\r
-        for(ii = red_optind; ii < argc; ii++)\r
-        {\r
-            RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);\r
-        }\r
-\r
-        goto BadOpt;\r
-    }\r
-\r
-    return PARAMSTATUS_OK;\r
-\r
-  BadOpt:\r
-\r
-    RedPrintf("%s - invalid parameters\n", argv[0U]);\r
-    usage(argv[0U]);\r
-    return PARAMSTATUS_BAD;\r
-\r
-  Help:\r
-\r
-    usage(argv[0U]);\r
-    return PARAMSTATUS_HELP;\r
-}\r
-\r
-\r
-/** @brief Set default fsstress parameters.\r
-\r
-    @param pParam   Populated with the default fsstress parameters.\r
-*/\r
-void FsstressDefaultParams(\r
-    FSSTRESSPARAM *pParam)\r
-{\r
-    RedMemSet(pParam, 0U, sizeof(*pParam));\r
-    pParam->ulLoops = 1U;\r
-    pParam->ulNops = 10000U;\r
-}\r
-\r
-\r
-/** @brief Start fsstress.\r
-\r
-    @param pParam   fsstress parameters, either from FsstressParseParams() or\r
-                    constructed programatically.\r
-\r
-    @return Zero on success, otherwise nonzero.\r
-*/\r
-int FsstressStart(\r
-    const FSSTRESSPARAM *pParam)\r
-{\r
-    char buf[10];\r
-    int fd;\r
-    int i;\r
-    int cleanup;\r
-    int loops;\r
-    int loopcntr = 1;\r
-\r
-    nops = sizeof(ops) / sizeof(ops[0]);\r
-    ops_end = &ops[nops];\r
-\r
-    /*  Copy the already-parsed parameters into the traditional variables.\r
-    */\r
-    cleanup = pParam->fNoCleanup ? 1 : 0;\r
-    loops = pParam->ulLoops;\r
-    operations = pParam->ulNops;\r
-    namerand = pParam->fNamePad ? 1 : 0;\r
-    seed = pParam->ulSeed;\r
-    verbose = pParam->fVerbose ? 1 : 0;\r
-\r
-    make_freq_table();\r
-\r
-    while ((loopcntr <= loops) || (loops == 0)) {\r
-        RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());\r
-        fd = creat(buf, 0666);\r
-        maxfsize = (off64_t) MAXFSIZE;\r
-        dcache_init();\r
-        if (!seed) {\r
-            seed = (unsigned long)RedOsClockGetTime();\r
-            RedPrintf("seed = %ld\n", seed);\r
-        }\r
-        close(fd);\r
-        unlink(buf);\r
-        procid = 0;\r
-        doproc();\r
-        if (cleanup == 0) {\r
-            delete_tree("/");\r
-            for (i = 0; i < FT_nft; i++) {\r
-                flist[i].nslots = 0;\r
-                flist[i].nfiles = 0;\r
-                free(flist[i].fents);\r
-                flist[i].fents = NULL;\r
-            }\r
-        }\r
-        loopcntr++;\r
-    }\r
-    return 0;\r
-}\r
-\r
-static int delete_tree(const char *path)\r
-{\r
-    REDSTAT sb;\r
-    DIR *dp;\r
-    REDDIRENT *dep;\r
-    char *childpath;\r
-    size_t len;\r
-    int e;\r
-\r
-    e = stat(path, &sb);\r
-    if (e)\r
-        return errno;\r
-\r
-    if (!S_ISDIR(sb.st_mode))\r
-        return unlink(path) ? errno : 0;\r
-\r
-    dp = opendir(path);\r
-    if (dp == NULL)\r
-        return errno;\r
-\r
-    while((dep = readdir(dp)) != NULL) {\r
-        len = strlen(path) + 1 + strlen(dep->d_name) + 1;\r
-        childpath = malloc(len);\r
-\r
-        strcpy(childpath, path);\r
-        if (childpath[strlen(childpath) - 1] != '/')\r
-            strcat(childpath, "/");\r
-        strcat(childpath, dep->d_name);\r
-\r
-        e = delete_tree(childpath);\r
-\r
-        free(childpath);\r
-\r
-        if (e)\r
-            break;\r
-    }\r
-\r
-    if (e == 0 && strcmp(path, "/") != 0) {\r
-        e = rmdir(path) ? errno : 0;\r
-    }\r
-    closedir(dp);\r
-    return e;\r
-}\r
-\r
-static void add_to_flist(int ft, int id, int parent)\r
-{\r
-    fent_t *fep;\r
-    flist_t *ftp;\r
-\r
-    ftp = &flist[ft];\r
-    if (ftp->nfiles == ftp->nslots) {\r
-        ftp->nslots += FLIST_SLOT_INCR;\r
-        ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));\r
-    }\r
-    fep = &ftp->fents[ftp->nfiles++];\r
-    fep->id = id;\r
-    fep->parent = parent;\r
-}\r
-\r
-static void append_pathname(pathname_t *name, const char *str)\r
-{\r
-    int len;\r
-\r
-    len = strlen(str);\r
-#ifdef DEBUG\r
-    if (len && *str == '/' && name->len == 0) {\r
-        RedPrintf("fsstress: append_pathname failure\n");\r
-        chdir(homedir);\r
-        abort();\r
-\r
-    }\r
-#endif\r
-    name->path = realloc(name->path, name->len + 1 + len);\r
-    strcpy(&name->path[name->len], str);\r
-    name->len += len;\r
-}\r
-\r
-static void check_cwd(void)\r
-{\r
-#ifdef DEBUG\r
-    REDSTAT statbuf;\r
-\r
-    if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)\r
-        return;\r
-    chdir(homedir);\r
-    RedPrintf("fsstress: check_cwd failure\n");\r
-    abort();\r
-\r
-#endif\r
-}\r
-\r
-static int creat_path(pathname_t *name, mode_t mode)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = creat(name->path, mode);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = creat_path(&newname, mode);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void dcache_enter(int dirid, int slot)\r
-{\r
-    dcache[dirid % NDCACHE] = slot;\r
-}\r
-\r
-static void dcache_init(void)\r
-{\r
-    int i;\r
-\r
-    for (i = 0; i < NDCACHE; i++)\r
-        dcache[i] = -1;\r
-}\r
-\r
-static fent_t *dcache_lookup(int dirid)\r
-{\r
-    fent_t *fep;\r
-    int i;\r
-\r
-    i = dcache[dirid % NDCACHE];\r
-    if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)\r
-        return fep;\r
-    return NULL;\r
-}\r
-\r
-static void dcache_purge(int dirid)\r
-{\r
-    int *dcp;\r
-\r
-    dcp = &dcache[dirid % NDCACHE];\r
-    if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)\r
-        *dcp = -1;\r
-}\r
-\r
-static void del_from_flist(int ft, int slot)\r
-{\r
-    flist_t *ftp;\r
-\r
-    ftp = &flist[ft];\r
-    if (ft == FT_DIR)\r
-        dcache_purge(ftp->fents[slot].id);\r
-    if (slot != ftp->nfiles - 1) {\r
-        if (ft == FT_DIR)\r
-            dcache_purge(ftp->fents[ftp->nfiles - 1].id);\r
-        ftp->fents[slot] = ftp->fents[--ftp->nfiles];\r
-    } else\r
-        ftp->nfiles--;\r
-}\r
-\r
-static fent_t *dirid_to_fent(int dirid)\r
-{\r
-    fent_t *efep;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-\r
-    if ((fep = dcache_lookup(dirid)))\r
-        return fep;\r
-    flp = &flist[FT_DIR];\r
-    for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {\r
-        if (fep->id == dirid) {\r
-            dcache_enter(dirid, (int)(fep - flp->fents));\r
-            return fep;\r
-        }\r
-    }\r
-    return NULL;\r
-}\r
-\r
-static void doproc(void)\r
-{\r
-    REDSTAT statbuf;\r
-    char buf[10];\r
-    int opno;\r
-    opdesc_t *p;\r
-\r
-    RedSNPrintf(buf, sizeof(buf), "p%x", procid);\r
-    (void)mkdir(buf);\r
-    if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {\r
-        perror(buf);\r
-        _exit(1);\r
-    }\r
-    top_ino = statbuf.st_ino;\r
-    homedir = getcwd(NULL, 0);\r
-    seed += procid;\r
-    srandom(seed);\r
-    if (namerand)\r
-        namerand = random();\r
-    for (opno = 0; opno < operations; opno++) {\r
-        p = &ops[freq_table[random() % freq_table_size]];\r
-        if ((unsigned long)p->func < 4096)\r
-            abort();\r
-\r
-        p->func(opno, random());\r
-    }\r
-    free(homedir);\r
-}\r
-\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    int i;\r
-    fent_t *pfep;\r
-\r
-    if (fep == NULL)\r
-        return;\r
-    if (fep->parent != -1) {\r
-        pfep = dirid_to_fent(fep->parent);\r
-        fent_to_name(name, &flist[FT_DIR], pfep);\r
-        append_pathname(name, "/");\r
-    }\r
-    i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);\r
-    namerandpad(fep->id, buf, i);\r
-    append_pathname(name, buf);\r
-}\r
-\r
-static void fix_parent(int oldid, int newid)\r
-{\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int i;\r
-    int j;\r
-\r
-    for (i = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {\r
-            if (fep->parent == oldid)\r
-                fep->parent = newid;\r
-        }\r
-    }\r
-}\r
-\r
-static void free_pathname(pathname_t *name)\r
-{\r
-    if (name->path) {\r
-        free(name->path);\r
-        name->path = NULL;\r
-        name->len = 0;\r
-    }\r
-}\r
-\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    flist_t *flp;\r
-    int id;\r
-    int j;\r
-    int len;\r
-\r
-    flp = &flist[ft];\r
-    len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);\r
-    namerandpad(id, buf, len);\r
-    if (fep) {\r
-        fent_to_name(name, &flist[FT_DIR], fep);\r
-        append_pathname(name, "/");\r
-    }\r
-    append_pathname(name, buf);\r
-    *idp = id;\r
-    *v = verbose;\r
-    for (j = 0; !*v && j < ilistlen; j++) {\r
-        if (ilist[j] == id) {\r
-            *v = 1;\r
-            break;\r
-        }\r
-    }\r
-    return 1;\r
-}\r
-\r
-static int\r
-get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)\r
-{\r
-    int c;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int i;\r
-    int j;\r
-    int x;\r
-\r
-    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        if (which & (1 << i))\r
-            c += flp->nfiles;\r
-    }\r
-    if (c == 0) {\r
-        if (flpp)\r
-            *flpp = NULL;\r
-        if (fepp)\r
-            *fepp = NULL;\r
-        *v = verbose;\r
-        return 0;\r
-    }\r
-    x = (int)(r % c);\r
-    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
-        if (which & (1 << i)) {\r
-            if (x < c + flp->nfiles) {\r
-                fep = &flp->fents[x - c];\r
-                if (name)\r
-                    fent_to_name(name, flp, fep);\r
-                if (flpp)\r
-                    *flpp = flp;\r
-                if (fepp)\r
-                    *fepp = fep;\r
-                *v = verbose;\r
-                for (j = 0; !*v && j < ilistlen; j++) {\r
-                    if (ilist[j] == fep->id) {\r
-                        *v = 1;\r
-                        break;\r
-                    }\r
-                }\r
-                return 1;\r
-            }\r
-            c += flp->nfiles;\r
-        }\r
-    }\r
-#ifdef DEBUG\r
-    RedPrintf("fsstress: get_fname failure\n");\r
-    abort();\r
-#endif\r
-    return -1;\r
-\r
-}\r
-\r
-static void init_pathname(pathname_t *name)\r
-{\r
-    name->len = 0;\r
-    name->path = NULL;\r
-}\r
-\r
-static int link_path(pathname_t *name1, pathname_t *name2)\r
-{\r
-    char buf1[MAXNAMELEN];\r
-    char buf2[MAXNAMELEN];\r
-    int down1;\r
-    pathname_t newname1;\r
-    pathname_t newname2;\r
-    int rval;\r
-\r
-    rval = link(name1->path, name2->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name1, buf1, &newname1);\r
-    separate_pathname(name2, buf2, &newname2);\r
-    if (strcmp(buf1, buf2) == 0) {\r
-        if (chdir(buf1) == 0) {\r
-            rval = link_path(&newname1, &newname2);\r
-            chdir("..");\r
-        }\r
-    } else {\r
-        if (strcmp(buf1, "..") == 0)\r
-            down1 = 0;\r
-        else if (strcmp(buf2, "..") == 0)\r
-            down1 = 1;\r
-        else if (strlen(buf1) == 0)\r
-            down1 = 0;\r
-        else if (strlen(buf2) == 0)\r
-            down1 = 1;\r
-        else\r
-            down1 = MAX(newname1.len, 3 + name2->len) <=\r
-                MAX(3 + name1->len, newname2.len);\r
-        if (down1) {\r
-            free_pathname(&newname2);\r
-            append_pathname(&newname2, "../");\r
-            append_pathname(&newname2, name2->path);\r
-            if (chdir(buf1) == 0) {\r
-                rval = link_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        } else {\r
-            free_pathname(&newname1);\r
-            append_pathname(&newname1, "../");\r
-            append_pathname(&newname1, name1->path);\r
-            if (chdir(buf2) == 0) {\r
-                rval = link_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        }\r
-    }\r
-    free_pathname(&newname1);\r
-    free_pathname(&newname2);\r
-    return rval;\r
-}\r
-\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = lstat64(name->path, sbuf);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = lstat64_path(&newname, sbuf);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void make_freq_table(void)\r
-{\r
-    int f;\r
-    int i;\r
-    opdesc_t *p;\r
-\r
-    for (p = ops, f = 0; p < ops_end; p++)\r
-        f += p->freq;\r
-    freq_table = malloc(f * sizeof(*freq_table));\r
-    freq_table_size = f;\r
-    for (p = ops, i = 0; p < ops_end; p++) {\r
-        for (f = 0; f < p->freq; f++, i++)\r
-            freq_table[i] = p->op;\r
-    }\r
-}\r
-\r
-static int mkdir_path(pathname_t *name, mode_t mode)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = mkdir(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = mkdir_path(&newname, mode);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void namerandpad(int id, char *buf, int len)\r
-{\r
-    int bucket;\r
-    static int buckets[8] = {0};\r
-    static int bucket_count = 0;\r
-    int bucket_value;\r
-    int i;\r
-    int padlen;\r
-    int padmod;\r
-\r
-    if (namerand == 0)\r
-        return;\r
-\r
-    /*  buckets[] used to be a statically initialized array with the following\r
-        initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }\r
-\r
-        The problem is that with Reliance Edge, the maximum name length might be\r
-        less than 128.  So the below code populates buckets[] in a similar\r
-        fashion but avoids name lengths longer than the maximum.  For example,\r
-        if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.\r
-    */\r
-    if (!bucket_count) {\r
-        bucket_count = sizeof(buckets) / sizeof(buckets[0]);\r
-        bucket_value = 2;\r
-        for (i = 0; i < bucket_count; i++) {\r
-            if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)\r
-                break;\r
-            buckets[i] = bucket_value;\r
-            bucket_value *= 2;\r
-        }\r
-        if (i < bucket_count) {\r
-            buckets[i] = MAXNAMELEN - 1;\r
-            i++;\r
-        }\r
-        bucket_count = i;\r
-    }\r
-\r
-    bucket = (id ^ namerand) % bucket_count;\r
-    padmod = buckets[bucket] + 1 - len;\r
-    if (padmod <= 0)\r
-        return;\r
-    padlen = (id ^ namerand) % padmod;\r
-    if (padlen) {\r
-        memset(&buf[len], 'X', padlen);\r
-        buf[len + padlen] = '\0';\r
-    }\r
-}\r
-\r
-static int open_path(pathname_t *name, int oflag)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = open(name->path, oflag);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = open_path(&newname, oflag);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static DIR *opendir_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    DIR *rval;\r
-\r
-    rval = opendir(name->path);\r
-    if (rval || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = opendir_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int rename_path(pathname_t *name1, pathname_t *name2)\r
-{\r
-    char buf1[MAXNAMELEN];\r
-    char buf2[MAXNAMELEN];\r
-    int down1;\r
-    pathname_t newname1;\r
-    pathname_t newname2;\r
-    int rval;\r
-\r
-    rval = rename(name1->path, name2->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name1, buf1, &newname1);\r
-    separate_pathname(name2, buf2, &newname2);\r
-    if (strcmp(buf1, buf2) == 0) {\r
-        if (chdir(buf1) == 0) {\r
-            rval = rename_path(&newname1, &newname2);\r
-            chdir("..");\r
-        }\r
-    } else {\r
-        if (strcmp(buf1, "..") == 0)\r
-            down1 = 0;\r
-        else if (strcmp(buf2, "..") == 0)\r
-            down1 = 1;\r
-        else if (strlen(buf1) == 0)\r
-            down1 = 0;\r
-        else if (strlen(buf2) == 0)\r
-            down1 = 1;\r
-        else\r
-            down1 = MAX(newname1.len, 3 + name2->len) <=\r
-                MAX(3 + name1->len, newname2.len);\r
-        if (down1) {\r
-            free_pathname(&newname2);\r
-            append_pathname(&newname2, "../");\r
-            append_pathname(&newname2, name2->path);\r
-            if (chdir(buf1) == 0) {\r
-                rval = rename_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        } else {\r
-            free_pathname(&newname1);\r
-            append_pathname(&newname1, "../");\r
-            append_pathname(&newname1, name1->path);\r
-            if (chdir(buf2) == 0) {\r
-                rval = rename_path(&newname1, &newname2);\r
-                chdir("..");\r
-            }\r
-        }\r
-    }\r
-    free_pathname(&newname1);\r
-    free_pathname(&newname2);\r
-    return rval;\r
-}\r
-\r
-static int rmdir_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = rmdir(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = rmdir_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)\r
-{\r
-    char *slash;\r
-\r
-    init_pathname(newname);\r
-    slash = strchr(name->path, '/');\r
-    if (slash == NULL) {\r
-        buf[0] = '\0';\r
-        return;\r
-    }\r
-    *slash = '\0';\r
-    strcpy(buf, name->path);\r
-    *slash = '/';\r
-    append_pathname(newname, slash + 1);\r
-}\r
-\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = stat64(name->path, sbuf);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = stat64_path(&newname, sbuf);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int truncate64_path(pathname_t *name, off64_t length)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = truncate64(name->path, length);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = truncate64_path(&newname, length);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static int unlink_path(pathname_t *name)\r
-{\r
-    char buf[MAXNAMELEN];\r
-    pathname_t newname;\r
-    int rval;\r
-\r
-    rval = unlink(name->path);\r
-    if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
-        return rval;\r
-    separate_pathname(name, buf, &newname);\r
-    if (chdir(buf) == 0) {\r
-        rval = unlink_path(&newname);\r
-        chdir("..");\r
-    }\r
-    free_pathname(&newname);\r
-    return rval;\r
-}\r
-\r
-static void usage(const char *progname)\r
-{\r
-    RedPrintf("usage: %s VolumeID [Options]\n", progname);\r
-    RedPrintf("File system stress test.\n\n");\r
-    RedPrintf("Where:\n");\r
-    RedPrintf("  VolumeID\n");\r
-    RedPrintf("      A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");\r
-    RedPrintf("      of the volume to test.\n");\r
-    RedPrintf("And 'Options' are any of the following:\n");\r
-    RedPrintf("  --no-cleanup, -c\n");\r
-    RedPrintf("      Specifies not to remove files (cleanup) after execution\n");\r
-    RedPrintf("  --loops=count, -l count\n");\r
-    RedPrintf("      Specifies the number of times the entire test should loop.  Use 0 for\n");\r
-    RedPrintf("      infinite.  Default 1.\n");\r
-    RedPrintf("  --nops=count, -n count\n");\r
-    RedPrintf("      Specifies the number of operations to run (default 10000).\n");\r
-    RedPrintf("  --namepad, -r\n");\r
-    RedPrintf("      Specifies to use random name padding (resulting in longer names).\n");\r
-    RedPrintf("  --seed=value, -s value\n");\r
-    RedPrintf("      Specifies the seed for the random number generator (default timestamp).\n");\r
-    RedPrintf("  --verbose, -v\n");\r
-    RedPrintf("      Specifies verbose mode (without this, test is very quiet).\n");\r
-    RedPrintf("  --dev=devname, -D devname\n");\r
-    RedPrintf("      Specifies the device name.  This is typically only meaningful when\n");\r
-    RedPrintf("      running the test on a host machine.  This can be \"ram\" to test on a RAM\n");\r
-    RedPrintf("      disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");\r
-    RedPrintf("      reference to a device (on Windows, a drive letter like G: or a device name\n");\r
-    RedPrintf("      like \\\\.\\PhysicalDrive7).\n");\r
-    RedPrintf("  --help, -H\n");\r
-    RedPrintf("      Prints this usage text and exits.\n\n");\r
-    RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");\r
-}\r
-\r
-static void creat_f(int opno, long r)\r
-{\r
-    int e;\r
-    int e1;\r
-    pathname_t f;\r
-    int fd;\r
-    fent_t *fep;\r
-    int id;\r
-    int parid;\r
-    int type;\r
-    int v;\r
-    int v1;\r
-    int esz = 0;\r
-\r
-    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    init_pathname(&f);\r
-    type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;\r
-    e = generate_fname(fep, type, &f, &id, &v);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: creat - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = creat_path(&f, 0666);\r
-    e = fd < 0 ? errno : 0;\r
-    e1 = 0;\r
-    check_cwd();\r
-    esz = 0;\r
-    if (fd >= 0) {\r
-        add_to_flist(type, id, parid);\r
-        close(fd);\r
-    }\r
-    if (v)\r
-        RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,\r
-               esz, e, e1);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void fdatasync_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fdatasync - no filename\n",\r
-                   procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fdatasync - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = fdatasync(fd) < 0 ? errno : 0;\r
-    if (v)\r
-        RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void fsync_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fsync - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: fsync - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = fsync(fd) < 0 ? errno : 0;\r
-    if (v)\r
-        RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void getdents_f(int opno, long r)\r
-{\r
-    DIR *dir;\r
-    pathname_t f;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))\r
-        append_pathname(&f, ".");\r
-    dir = opendir_path(&f);\r
-    check_cwd();\r
-    if (dir == NULL) {\r
-        if (v)\r
-            RedPrintf("%d/%d: getdents - can't open %s\n",\r
-                   procid, opno, f.path);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    while (readdir64(dir) != NULL)\r
-        continue;\r
-    if (v)\r
-        RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);\r
-    free_pathname(&f);\r
-    closedir(dir);\r
-}\r
-\r
-static void link_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int id;\r
-    pathname_t l;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {\r
-        if (v1)\r
-            RedPrintf("%d/%d: link - no file\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    v |= v1;\r
-    init_pathname(&l);\r
-    e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&l, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: link - no filename from %s\n",\r
-                   procid, opno, l.path);\r
-        }\r
-        free_pathname(&l);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = link_path(&f, &l) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        add_to_flist((int)(flp - flist), id, parid);\r
-    if (v)\r
-        RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,\r
-               e);\r
-    free_pathname(&l);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void mkdir_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    int id;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = fep->id;\r
-    init_pathname(&f);\r
-    e = generate_fname(fep, FT_DIR, &f, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], fep);\r
-            RedPrintf("%d/%d: mkdir - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = mkdir_path(&f, 0777) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        add_to_flist(FT_DIR, id, parid);\r
-    if (v)\r
-        RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void read_f(int opno, long r)\r
-{\r
-    char *buf;\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    uint32_t len;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_RDONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (fstat64(fd, &stb) < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - fstat64 %s failed %d\n",\r
-                   procid, opno, f.path, errno);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    if (stb.st_size == 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: read - %s zero size\n", procid, opno,\r
-                   f.path);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = (off64_t) (lr % stb.st_size);\r
-    lseek64(fd, off, SEEK_SET);\r
-    len = (random() % (getpagesize() * 4)) + 1;\r
-    buf = malloc(len);\r
-    e = read(fd, buf, len) < 0 ? errno : 0;\r
-    free(buf);\r
-    if (v)\r
-        RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",\r
-               procid, opno, f.path, (long long)off, (long int)len, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-static void rename_f(int opno, long r)\r
-{\r
-    fent_t *dfep;\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int id;\r
-    pathname_t newf;\r
-    int oldid;\r
-    int parid;\r
-    int v;\r
-    int v1;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {\r
-        if (v1)\r
-            RedPrintf("%d/%d: rename - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))\r
-        parid = -1;\r
-    else\r
-        parid = dfep->id;\r
-    v |= v1;\r
-    init_pathname(&newf);\r
-    e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);\r
-    v |= v1;\r
-    if (!e) {\r
-        if (v) {\r
-            fent_to_name(&f, &flist[FT_DIR], dfep);\r
-            RedPrintf("%d/%d: rename - no filename from %s\n",\r
-                   procid, opno, f.path);\r
-        }\r
-        free_pathname(&newf);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = rename_path(&f, &newf) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0) {\r
-        if (flp - flist == FT_DIR) {\r
-            oldid = fep->id;\r
-            fix_parent(oldid, id);\r
-        }\r
-        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
-        add_to_flist((int)(flp - flist), id, parid);\r
-    }\r
-    if (v)\r
-        RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,\r
-               newf.path, e);\r
-    free_pathname(&newf);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void rmdir_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = rmdir_path(&f) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));\r
-    if (v)\r
-        RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void stat_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: stat - no entries\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = lstat64_path(&f, &stb) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (v)\r
-        RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void truncate_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: truncate - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = stat64_path(&f, &stb) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e > 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);\r
-    off %= maxfsize;\r
-    e = truncate64_path(&f, off) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (v)\r
-        RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,\r
-               (long long)off, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void unlink_f(int opno, long r)\r
-{\r
-    int e;\r
-    pathname_t f;\r
-    fent_t *fep;\r
-    flist_t *flp;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: unlink - no file\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    e = unlink_path(&f) < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (e == 0)\r
-        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
-    if (v)\r
-        RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);\r
-    free_pathname(&f);\r
-}\r
-\r
-static void write_f(int opno, long r)\r
-{\r
-    char *buf;\r
-    int e;\r
-    pathname_t f;\r
-    int fd;\r
-    uint32_t len;\r
-    __int64_t lr;\r
-    off64_t off;\r
-    REDSTAT stb;\r
-    int v;\r
-\r
-    init_pathname(&f);\r
-    if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - no filename\n", procid, opno);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    fd = open_path(&f, O_WRONLY);\r
-    e = fd < 0 ? errno : 0;\r
-    check_cwd();\r
-    if (fd < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - open %s failed %d\n",\r
-                   procid, opno, f.path, e);\r
-        free_pathname(&f);\r
-        return;\r
-    }\r
-    if (fstat64(fd, &stb) < 0) {\r
-        if (v)\r
-            RedPrintf("%d/%d: write - fstat64 %s failed %d\n",\r
-                   procid, opno, f.path, errno);\r
-        free_pathname(&f);\r
-        close(fd);\r
-        return;\r
-    }\r
-    lr = ((__int64_t) random() << 32) + random();\r
-    off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));\r
-    off %= maxfsize;\r
-    lseek64(fd, off, SEEK_SET);\r
-    len = (random() % (getpagesize() * 4)) + 1;\r
-    buf = malloc(len);\r
-    memset(buf, nameseq & 0xff, len);\r
-    e = write(fd, buf, len) < 0 ? errno : 0;\r
-    free(buf);\r
-    if (v)\r
-        RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",\r
-               procid, opno, f.path, (long long)off, (long int)len, e);\r
-    free_pathname(&f);\r
-    close(fd);\r
-}\r
-\r
-\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r)\r
-{\r
-    int32_t ret;\r
-    const char *pszVolume = gpRedVolConf->pszPathPrefix;\r
-\r
-    (void)r;\r
-\r
-    errno = 0;\r
-\r
-    ret = red_transact(pszVolume);\r
-\r
-    if(ret == 0)\r
-    {\r
-        ret = red_umount(pszVolume);\r
-\r
-        if(ret == 0)\r
-        {\r
-            int32_t ret2;\r
-\r
-            errno = -RedCoreVolCheck();\r
-            if(errno != 0)\r
-            {\r
-                ret = -1;\r
-            }\r
-\r
-            ret2 = red_mount(pszVolume);\r
-\r
-            if(ret == 0)\r
-            {\r
-                ret = ret2;\r
-            }\r
-\r
-            if(ret2 != 0)\r
-            {\r
-                exit(1);\r
-            }\r
-        }\r
-    }\r
-\r
-    if (verbose)\r
-    {\r
-        RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);\r
-    }\r
-}\r
-#endif\r
-\r
-\r
-#endif /* FSSTRESS_SUPPORTED */\r
-\r
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+/** @file
+    @brief File system stress test.
+
+    This version of SGI fsstress has been modified to be single-threaded and to
+    work with the Reliance Edge POSIX-like API.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <redposix.h>
+#include <redtests.h>
+
+#if FSSTRESS_SUPPORTED
+
+#include "redposixcompat.h"
+
+#include <redosserv.h>
+#include <redutils.h>
+#include <redmacs.h>
+#include <redvolume.h>
+#include <redgetopt.h>
+#include <redtoolcmn.h>
+
+#if REDCONF_CHECKER == 1
+#include <redcoreapi.h>
+#endif
+
+
+/*  Create POSIX types.  Use #define to avoid name conflicts in those
+    environments where the type names already exist.
+*/
+#define off_t int64_t
+#define off64_t off_t
+#define ino_t uint32_t
+#define mode_t uint16_t
+#define __int64_t int64_t
+
+
+/** @brief Generate a random number.
+
+    @return A nonnegative random number.
+*/
+#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))
+
+
+/** @brief Seed the random number generator.
+*/
+#define srandom(seed) RedRandSeed(seed)
+
+
+#define _exit(status) exit(status)
+#define getpagesize() 4096U
+#define getpid() 1
+
+
+/** @brief Determine the maximum file size.
+
+    This is used for the MAXFSSIZE macro.
+*/
+static uint64_t MaxFileSize(void)
+{
+    REDSTATFS   info;
+    int32_t     iStatus;
+    REDSTATUS   errnoSave = errno;
+    uint64_t    ullMaxFileSize;
+
+    iStatus = red_statvfs("", &info);
+    if(iStatus == 0)
+    {
+        ullMaxFileSize = info.f_maxfsize;
+    }
+    else
+    {
+        /*  This function does not change errno.
+        */
+        errno = errnoSave;
+
+        ullMaxFileSize = 0x7FFFFFFFU;
+    }
+
+    return ullMaxFileSize;
+}
+
+
+/*-------------------------------------------------------------------
+    Simulated current working directory support
+-------------------------------------------------------------------*/
+
+
+/*  Forward declaration for red_chdir().
+*/
+static int red_stat(const char *pszPath, REDSTAT *pStat);
+
+/*  The simulated CWD functions.
+*/
+#undef chdir
+#undef getcwd
+#define chdir(path) red_chdir(path)
+#define getcwd(buf, size) red_getcwd(buf, size)
+
+
+/*  Redefine the path-based APIs to call MakeFullPath() on their arguments
+    since there is no CWD support in the red_*() APIs.
+*/
+#undef open
+#undef unlink
+#undef mkdir
+#undef rmdir
+#undef rename
+#undef link
+#undef opendir
+#define open(path, oflag) red_open(MakeFullPath(path), oflag)
+#define unlink(path) red_unlink(MakeFullPath(path))
+#define mkdir(path) red_mkdir(MakeFullPath(path))
+#define rmdir(path) red_rmdir(MakeFullPath(path))
+#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))
+#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))
+#define opendir(path) red_opendir(MakeFullPath(path))
+
+#define FSSTRESS_BUF_SIZE 1024U
+
+/*  Stores the simulated current working directory.
+*/
+static char szLocalCwd[FSSTRESS_BUF_SIZE] = "/";
+
+
+/** @brief Change the current working directory.
+
+    This function only supports a subset of what is possible with POSIX chdir().
+
+    @param pszPath  The new current working directory.
+
+    @return Upon successful completion, 0 shall be returned.  Otherwise, -1
+            shall be returned, and errno shall be set to indicate the error.
+*/
+static int red_chdir(
+    const char *pszPath)
+{
+    uint32_t    ulIdx;
+    int         iErrno = 0;
+
+    if(strcmp(pszPath, "..") == 0)
+    {
+        uint32_t ulLastSlashIdx = 0U;
+
+        /*  Chop off the last path separator and everything after it, so that
+            "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.
+        */
+        for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)
+        {
+            if(szLocalCwd[ulIdx] == '/')
+            {
+                ulLastSlashIdx = ulIdx;
+            }
+        }
+
+        if(ulLastSlashIdx != 0U)
+        {
+            szLocalCwd[ulLastSlashIdx] = '\0';
+        }
+    }
+    else
+    {
+        char    szOldCwd[FSSTRESS_BUF_SIZE];
+
+        /*  chdir() must have no effect on the CWD if it fails, so save the CWD
+            so we can revert it if necessary.
+        */
+        strcpy(szOldCwd, szLocalCwd);
+
+        if(pszPath[0U] == '/')
+        {
+            if(strlen(pszPath) >= sizeof(szLocalCwd))
+            {
+                iErrno = RED_ENAMETOOLONG;
+            }
+            else
+            {
+                strcpy(szLocalCwd, pszPath);
+            }
+        }
+        else
+        {
+            ulIdx = strlen(szLocalCwd);
+
+            if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))
+            {
+                iErrno = RED_ENAMETOOLONG;
+            }
+            else
+            {
+                if(szLocalCwd[1U] != '\0')
+                {
+                    szLocalCwd[ulIdx] = '/';
+                    ulIdx++;
+                }
+
+                strcpy(&szLocalCwd[ulIdx], pszPath);
+            }
+        }
+
+        if(iErrno == 0)
+        {
+            REDSTAT s;
+            int     iStatus;
+
+            iStatus = red_stat(szLocalCwd, &s);
+            if(iStatus != 0)
+            {
+                iErrno = errno;
+            }
+            else if(!S_ISDIR(s.st_mode))
+            {
+                iErrno = RED_ENOTDIR;
+            }
+            else
+            {
+                /*  No error, new CWD checks out.
+                */
+            }
+        }
+
+        if(iErrno != 0)
+        {
+            strcpy(szLocalCwd, szOldCwd);
+        }
+    }
+
+    if(iErrno != 0)
+    {
+        errno = iErrno;
+    }
+
+    return iErrno == 0 ? 0 : -1;
+}
+
+
+/** @brief Retrieve the current working directory.
+
+    @param pszBuf   On successful return, populated with the current working
+                    directory.  If NULL, memory will be allocated for the CWD
+                    and returned by this function.
+    @param nSize    The size of @p pszBuf.
+
+    @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if
+            @p pszBuf was NULL, returns an allocated buffer populated with the
+            CWD which must be freed by the caller.  On failure, returns NULL
+            and errno will be set.
+*/
+static char *red_getcwd(
+    char   *pszBuf,
+    size_t  nSize)
+{
+    char   *pszRet;
+
+    if(pszBuf == NULL)
+    {
+        pszRet = malloc(strlen(szLocalCwd) + 1U);
+        if(pszRet == NULL)
+        {
+            errno = RED_ENOMEM;
+        }
+        else
+        {
+            strcpy(pszRet, szLocalCwd);
+        }
+    }
+    else if(nSize < strlen(szLocalCwd) + 1U)
+    {
+        errno = RED_ERANGE;
+        pszRet = NULL;
+    }
+    else
+    {
+        strcpy(pszBuf, szLocalCwd);
+        pszRet = pszBuf;
+    }
+
+    return pszRet;
+}
+
+
+/** @brief Make a relative path into a fully qualified path.
+
+    @param pszName  The relative path.
+
+    @return On success, a pointer to a fully qualified path.  On error, NULL.
+*/
+static const char *MakeFullPath(
+    const char     *pszName)
+{
+    #define         MAXVOLNAME 64U /* Enough for most configs. */
+    static char     aszFullPath[2U][MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE];
+    static uint32_t ulWhich = 0U;
+
+    char           *pszFullPath = aszFullPath[ulWhich];
+    const char     *pszVolume = gpRedVolConf->pszPathPrefix;
+    int32_t         iLen;
+
+    if(pszName[0U] == '/')
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);
+    }
+    else if(strcmp(pszName, ".") == 0U)
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);
+    }
+    else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);
+    }
+    else
+    {
+        iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);
+    }
+
+    if(iLen == -1)
+    {
+        /*  Insufficient path buffer space.
+        */
+        pszFullPath = NULL;
+    }
+    else
+    {
+        /*  Toggle between two full path arrays; a kluge to make rename() and
+            link() work correctly.
+        */
+        ulWhich ^= 1U;
+    }
+
+    return pszFullPath;
+}
+
+
+/*-------------------------------------------------------------------
+    POSIX functions not implemented by the RED POSIX-like API
+-------------------------------------------------------------------*/
+
+#define stat(p, s) red_stat(p, s)
+#define stat64(p, s) stat(p, s)
+#define lstat(p, s) stat(p, s)
+#define lstat64(p, s) stat(p, s)
+#define truncate(p, s) red_truncate(p, s)
+#define truncate64(p, s) truncate(p, s)
+
+
+/** @brief Get the status of a file or directory.
+*/
+static int red_stat(
+    const char *pszPath,
+    REDSTAT    *pStat)
+{
+    int         iFd;
+    int         iRet;
+
+    iFd = open(pszPath, O_RDONLY);
+    iRet = iFd;
+    if(iFd != -1)
+    {
+        iRet = fstat(iFd, pStat);
+
+        (void)close(iFd);
+    }
+
+    return iRet;
+}
+
+
+/** @brief Truncate a file to a specified length.
+*/
+static int red_truncate(
+    const char *pszPath,
+    off_t       llSize)
+{
+    int         iFd;
+    int         iRet;
+
+    iFd = open(pszPath, O_WRONLY);
+    iRet = iFd;
+    if(iFd != -1)
+    {
+        iRet = ftruncate(iFd, llSize);
+
+        (void)close(iFd);
+    }
+
+    return iRet;
+}
+
+
+/*-------------------------------------------------------------------
+    Begin ported fsstress code
+-------------------------------------------------------------------*/
+
+/* Stuff from xfscompat.h */
+
+#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */
+
+struct dioattr {
+    int d_miniosz, d_maxiosz, d_mem;
+};
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+/* End xfscompat.h */
+
+
+typedef enum {
+    OP_CREAT,
+    OP_FDATASYNC,
+    OP_FSYNC,
+    OP_GETDENTS,
+    OP_LINK,
+    OP_MKDIR,
+    OP_READ,
+    OP_RENAME,
+    OP_RMDIR,
+    OP_STAT,
+    OP_TRUNCATE,
+    OP_UNLINK,
+    OP_WRITE,
+  #if REDCONF_CHECKER == 1
+    OP_CHECK,
+  #endif
+    OP_LAST
+} opty_t;
+
+typedef void (*opfnc_t) (int, long);
+
+typedef struct opdesc {
+    opty_t op;
+    const char *name;
+    opfnc_t func;
+    int freq;
+    int iswrite;
+} opdesc_t;
+
+typedef struct fent {
+    int id;
+    int parent;
+} fent_t;
+
+typedef struct flist {
+    int nfiles;
+    int nslots;
+    int tag;
+    fent_t *fents;
+} flist_t;
+
+typedef struct pathname {
+    int len;
+    char *path;
+} pathname_t;
+
+#define FT_DIR      0
+#define FT_DIRm     (1 << FT_DIR)
+#define FT_REG      1
+#define FT_REGm     (1 << FT_REG)
+#define FT_SYM      2
+#define FT_SYMm     (1 << FT_SYM)
+#define FT_DEV      3
+#define FT_DEVm     (1 << FT_DEV)
+#define FT_RTF      4
+#define FT_RTFm     (1 << FT_RTF)
+#define FT_nft      5
+#define FT_ANYm     ((1 << FT_nft) - 1)
+#define FT_REGFILE  (FT_REGm | FT_RTFm)
+#define FT_NOTDIR   (FT_ANYm & ~FT_DIRm)
+
+#define FLIST_SLOT_INCR 16
+#define NDCACHE 64
+
+#define MAXFSIZE MaxFileSize()
+
+static void creat_f(int opno, long r);
+static void fdatasync_f(int opno, long r);
+static void fsync_f(int opno, long r);
+static void getdents_f(int opno, long r);
+static void link_f(int opno, long r);
+static void mkdir_f(int opno, long r);
+static void read_f(int opno, long r);
+static void rename_f(int opno, long r);
+static void rmdir_f(int opno, long r);
+static void stat_f(int opno, long r);
+static void truncate_f(int opno, long r);
+static void unlink_f(int opno, long r);
+static void write_f(int opno, long r);
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r);
+#endif
+
+static opdesc_t ops[] = {
+    {OP_CREAT, "creat", creat_f, 4, 1},
+    {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},
+    {OP_FSYNC, "fsync", fsync_f, 1, 1},
+    {OP_GETDENTS, "getdents", getdents_f, 1, 0},
+    {OP_LINK, "link", link_f, 1, 1},
+    {OP_MKDIR, "mkdir", mkdir_f, 2, 1},
+    {OP_READ, "read", read_f, 1, 0},
+    {OP_RENAME, "rename", rename_f, 2, 1},
+    {OP_RMDIR, "rmdir", rmdir_f, 1, 1},
+    {OP_STAT, "stat", stat_f, 1, 0},
+    {OP_TRUNCATE, "truncate", truncate_f, 2, 1},
+    {OP_UNLINK, "unlink", unlink_f, 1, 1},
+    {OP_WRITE, "write", write_f, 4, 1},
+  #if REDCONF_CHECKER == 1
+    {OP_CHECK, "check", check_f, 1, 1},
+  #endif
+}, *ops_end;
+
+static flist_t flist[FT_nft] = {
+    {0, 0, 'd', NULL},
+    {0, 0, 'f', NULL},
+    {0, 0, 'l', NULL},
+    {0, 0, 'c', NULL},
+    {0, 0, 'r', NULL},
+};
+
+static int dcache[NDCACHE];
+static opty_t *freq_table;
+static int freq_table_size;
+static char *homedir;
+static int *ilist;
+static int ilistlen;
+static off64_t maxfsize;
+static int namerand;
+static int nameseq;
+static int nops;
+static int operations = 1;
+static int procid;
+static int rtpct;
+static unsigned long seed = 0;
+static ino_t top_ino;
+static int verbose = 0;
+
+static int delete_tree(const char *path);
+static void add_to_flist(int fd, int it, int parent);
+static void append_pathname(pathname_t *name, const char *str);
+static void check_cwd(void);
+static int creat_path(pathname_t *name, mode_t mode);
+static void dcache_enter(int dirid, int slot);
+static void dcache_init(void);
+static fent_t *dcache_lookup(int dirid);
+static void dcache_purge(int dirid);
+static void del_from_flist(int ft, int slot);
+static void doproc(void);
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);
+static void fix_parent(int oldid, int newid);
+static void free_pathname(pathname_t *name);
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);
+static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);
+static void init_pathname(pathname_t *name);
+static int link_path(pathname_t *name1, pathname_t *name2);
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf);
+static void make_freq_table(void);
+static int mkdir_path(pathname_t *name, mode_t mode);
+static void namerandpad(int id, char *buf, int len);
+static int open_path(pathname_t *name, int oflag);
+static DIR *opendir_path(pathname_t *name);
+static int rename_path(pathname_t *name1, pathname_t *name2);
+static int rmdir_path(pathname_t *name);
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);
+static int stat64_path(pathname_t *name, REDSTAT *sbuf);
+static int truncate64_path(pathname_t *name, off64_t length);
+static int unlink_path(pathname_t *name);
+static void usage(const char *progname);
+
+
+/** @brief Parse parameters for fsstress.
+
+    @param argc         The number of arguments from main().
+    @param argv         The vector of arguments from main().
+    @param pParam       Populated with the fsstress parameters.
+    @param pbVolNum     If non-NULL, populated with the volume number.
+    @param ppszDevice   If non-NULL, populated with the device name argument or
+                        NULL if no device argument is provided.
+
+    @return The result of parsing the parameters.
+*/
+PARAMSTATUS FsstressParseParams(
+    int             argc,
+    char           *argv[],
+    FSSTRESSPARAM  *pParam,
+    uint8_t        *pbVolNum,
+    const char    **ppszDevice)
+{
+    int             c;
+    uint8_t         bVolNum;
+    const REDOPTION aLongopts[] =
+    {
+        { "no-cleanup", red_no_argument, NULL, 'c' },
+        { "loops", red_required_argument, NULL, 'l' },
+        { "nops", red_required_argument, NULL, 'n' },
+        { "namepad", red_no_argument, NULL, 'r' },
+        { "seed", red_required_argument, NULL, 's' },
+        { "verbose", red_no_argument, NULL, 'v' },
+        { "dev", red_required_argument, NULL, 'D' },
+        { "help", red_no_argument, NULL, 'H' },
+        { NULL }
+    };
+
+    /*  If run without parameters, treat as a help request.
+    */
+    if(argc <= 1)
+    {
+        goto Help;
+    }
+
+    /*  Assume no device argument to start with.
+    */
+    if(ppszDevice != NULL)
+    {
+        *ppszDevice = NULL;
+    }
+
+    /*  Set default parameters.
+    */
+    FsstressDefaultParams(pParam);
+
+    while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)
+    {
+        switch(c)
+        {
+            case 'c': /* --no-cleanup */
+                pParam->fNoCleanup = true;
+                break;
+            case 'l': /* --loops */
+                pParam->ulLoops = RedAtoI(red_optarg);
+                break;
+            case 'n': /* --nops */
+                pParam->ulNops = RedAtoI(red_optarg);
+                break;
+            case 'r': /* --namepad */
+                pParam->fNamePad = true;
+                break;
+            case 's': /* --seed */
+                pParam->ulSeed = RedAtoI(red_optarg);
+                break;
+            case 'v': /* --verbose */
+                pParam->fVerbose = true;
+                break;
+            case 'D': /* --dev */
+                if(ppszDevice != NULL)
+                {
+                    *ppszDevice = red_optarg;
+                }
+                break;
+            case 'H': /* --help */
+                goto Help;
+            case '?': /* Unknown or ambiguous option */
+            case ':': /* Option missing required argument */
+            default:
+                goto BadOpt;
+        }
+    }
+
+    /*  RedGetoptLong() has permuted argv to move all non-option arguments to
+        the end.  We expect to find a volume identifier.
+    */
+    if(red_optind >= argc)
+    {
+        RedPrintf("Missing volume argument\n");
+        goto BadOpt;
+    }
+
+    bVolNum = RedFindVolumeNumber(argv[red_optind]);
+    if(bVolNum == REDCONF_VOLUME_COUNT)
+    {
+        RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);
+        goto BadOpt;
+    }
+
+    if(pbVolNum != NULL)
+    {
+        *pbVolNum = bVolNum;
+    }
+
+    red_optind++; /* Move past volume parameter. */
+    if(red_optind < argc)
+    {
+        int32_t ii;
+
+        for(ii = red_optind; ii < argc; ii++)
+        {
+            RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);
+        }
+
+        goto BadOpt;
+    }
+
+    return PARAMSTATUS_OK;
+
+  BadOpt:
+
+    RedPrintf("%s - invalid parameters\n", argv[0U]);
+    usage(argv[0U]);
+    return PARAMSTATUS_BAD;
+
+  Help:
+
+    usage(argv[0U]);
+    return PARAMSTATUS_HELP;
+}
+
+
+/** @brief Set default fsstress parameters.
+
+    @param pParam   Populated with the default fsstress parameters.
+*/
+void FsstressDefaultParams(
+    FSSTRESSPARAM *pParam)
+{
+    RedMemSet(pParam, 0U, sizeof(*pParam));
+    pParam->ulLoops = 1U;
+    pParam->ulNops = 10000U;
+}
+
+
+/** @brief Start fsstress.
+
+    @param pParam   fsstress parameters, either from FsstressParseParams() or
+                    constructed programatically.
+
+    @return Zero on success, otherwise nonzero.
+*/
+int FsstressStart(
+    const FSSTRESSPARAM *pParam)
+{
+    char buf[10];
+    int fd;
+    int i;
+    int cleanup;
+    int loops;
+    int loopcntr = 1;
+
+    nops = sizeof(ops) / sizeof(ops[0]);
+    ops_end = &ops[nops];
+
+    /*  Copy the already-parsed parameters into the traditional variables.
+    */
+    cleanup = pParam->fNoCleanup ? 1 : 0;
+    loops = pParam->ulLoops;
+    operations = pParam->ulNops;
+    namerand = pParam->fNamePad ? 1 : 0;
+    seed = pParam->ulSeed;
+    verbose = pParam->fVerbose ? 1 : 0;
+
+    make_freq_table();
+
+    while ((loopcntr <= loops) || (loops == 0)) {
+        RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());
+        fd = creat(buf, 0666);
+        maxfsize = (off64_t) MAXFSIZE;
+        dcache_init();
+        if (!seed) {
+            seed = (unsigned long)RedOsClockGetTime();
+            RedPrintf("seed = %ld\n", seed);
+        }
+        close(fd);
+        unlink(buf);
+        procid = 0;
+        doproc();
+        if (cleanup == 0) {
+            delete_tree("/");
+            for (i = 0; i < FT_nft; i++) {
+                flist[i].nslots = 0;
+                flist[i].nfiles = 0;
+                free(flist[i].fents);
+                flist[i].fents = NULL;
+            }
+        }
+        loopcntr++;
+    }
+    return 0;
+}
+
+static int delete_tree(const char *path)
+{
+    REDSTAT sb;
+    DIR *dp;
+    REDDIRENT *dep;
+    char *childpath;
+    size_t len;
+    int e;
+
+    e = stat(path, &sb);
+    if (e)
+        return errno;
+
+    if (!S_ISDIR(sb.st_mode))
+        return unlink(path) ? errno : 0;
+
+    dp = opendir(path);
+    if (dp == NULL)
+        return errno;
+
+    while((dep = readdir(dp)) != NULL) {
+        len = strlen(path) + 1 + strlen(dep->d_name) + 1;
+        childpath = malloc(len);
+
+        strcpy(childpath, path);
+        if (childpath[strlen(childpath) - 1] != '/')
+            strcat(childpath, "/");
+        strcat(childpath, dep->d_name);
+
+        e = delete_tree(childpath);
+
+        free(childpath);
+
+        if (e)
+            break;
+    }
+
+    if (e == 0 && strcmp(path, "/") != 0) {
+        e = rmdir(path) ? errno : 0;
+    }
+    closedir(dp);
+    return e;
+}
+
+static void add_to_flist(int ft, int id, int parent)
+{
+    fent_t *fep;
+    flist_t *ftp;
+
+    ftp = &flist[ft];
+    if (ftp->nfiles == ftp->nslots) {
+        ftp->nslots += FLIST_SLOT_INCR;
+        ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));
+    }
+    fep = &ftp->fents[ftp->nfiles++];
+    fep->id = id;
+    fep->parent = parent;
+}
+
+static void append_pathname(pathname_t *name, const char *str)
+{
+    int len;
+
+    len = strlen(str);
+#ifdef DEBUG
+    if (len && *str == '/' && name->len == 0) {
+        RedPrintf("fsstress: append_pathname failure\n");
+        chdir(homedir);
+        abort();
+
+    }
+#endif
+    name->path = realloc(name->path, name->len + 1 + len);
+    strcpy(&name->path[name->len], str);
+    name->len += len;
+}
+
+static void check_cwd(void)
+{
+#ifdef DEBUG
+    REDSTAT statbuf;
+
+    if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)
+        return;
+    chdir(homedir);
+    RedPrintf("fsstress: check_cwd failure\n");
+    abort();
+
+#endif
+}
+
+static int creat_path(pathname_t *name, mode_t mode)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = creat(name->path, mode);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = creat_path(&newname, mode);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void dcache_enter(int dirid, int slot)
+{
+    dcache[dirid % NDCACHE] = slot;
+}
+
+static void dcache_init(void)
+{
+    int i;
+
+    for (i = 0; i < NDCACHE; i++)
+        dcache[i] = -1;
+}
+
+static fent_t *dcache_lookup(int dirid)
+{
+    fent_t *fep;
+    int i;
+
+    i = dcache[dirid % NDCACHE];
+    if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)
+        return fep;
+    return NULL;
+}
+
+static void dcache_purge(int dirid)
+{
+    int *dcp;
+
+    dcp = &dcache[dirid % NDCACHE];
+    if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)
+        *dcp = -1;
+}
+
+static void del_from_flist(int ft, int slot)
+{
+    flist_t *ftp;
+
+    ftp = &flist[ft];
+    if (ft == FT_DIR)
+        dcache_purge(ftp->fents[slot].id);
+    if (slot != ftp->nfiles - 1) {
+        if (ft == FT_DIR)
+            dcache_purge(ftp->fents[ftp->nfiles - 1].id);
+        ftp->fents[slot] = ftp->fents[--ftp->nfiles];
+    } else
+        ftp->nfiles--;
+}
+
+static fent_t *dirid_to_fent(int dirid)
+{
+    fent_t *efep;
+    fent_t *fep;
+    flist_t *flp;
+
+    if ((fep = dcache_lookup(dirid)))
+        return fep;
+    flp = &flist[FT_DIR];
+    for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {
+        if (fep->id == dirid) {
+            dcache_enter(dirid, (int)(fep - flp->fents));
+            return fep;
+        }
+    }
+    return NULL;
+}
+
+static void doproc(void)
+{
+    REDSTAT statbuf;
+    char buf[10];
+    int opno;
+    opdesc_t *p;
+
+    RedSNPrintf(buf, sizeof(buf), "p%x", procid);
+    (void)mkdir(buf);
+    if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {
+        perror(buf);
+        _exit(1);
+    }
+    top_ino = statbuf.st_ino;
+    homedir = getcwd(NULL, 0);
+    seed += procid;
+    srandom(seed);
+    if (namerand)
+        namerand = random();
+    for (opno = 0; opno < operations; opno++) {
+        p = &ops[freq_table[random() % freq_table_size]];
+        if ((unsigned long)p->func < 4096)
+            abort();
+
+        p->func(opno, random());
+    }
+    free(homedir);
+}
+
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)
+{
+    char buf[MAXNAMELEN];
+    int i;
+    fent_t *pfep;
+
+    if (fep == NULL)
+        return;
+    if (fep->parent != -1) {
+        pfep = dirid_to_fent(fep->parent);
+        fent_to_name(name, &flist[FT_DIR], pfep);
+        append_pathname(name, "/");
+    }
+    i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);
+    namerandpad(fep->id, buf, i);
+    append_pathname(name, buf);
+}
+
+static void fix_parent(int oldid, int newid)
+{
+    fent_t *fep;
+    flist_t *flp;
+    int i;
+    int j;
+
+    for (i = 0, flp = flist; i < FT_nft; i++, flp++) {
+        for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {
+            if (fep->parent == oldid)
+                fep->parent = newid;
+        }
+    }
+}
+
+static void free_pathname(pathname_t *name)
+{
+    if (name->path) {
+        free(name->path);
+        name->path = NULL;
+        name->len = 0;
+    }
+}
+
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
+{
+    char buf[MAXNAMELEN];
+    flist_t *flp;
+    int id;
+    int j;
+    int len;
+
+    flp = &flist[ft];
+    len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);
+    namerandpad(id, buf, len);
+    if (fep) {
+        fent_to_name(name, &flist[FT_DIR], fep);
+        append_pathname(name, "/");
+    }
+    append_pathname(name, buf);
+    *idp = id;
+    *v = verbose;
+    for (j = 0; !*v && j < ilistlen; j++) {
+        if (ilist[j] == id) {
+            *v = 1;
+            break;
+        }
+    }
+    return 1;
+}
+
+static int
+get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)
+{
+    int c;
+    fent_t *fep;
+    flist_t *flp;
+    int i;
+    int j;
+    int x;
+
+    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+        if (which & (1 << i))
+            c += flp->nfiles;
+    }
+    if (c == 0) {
+        if (flpp)
+            *flpp = NULL;
+        if (fepp)
+            *fepp = NULL;
+        *v = verbose;
+        return 0;
+    }
+    x = (int)(r % c);
+    for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+        if (which & (1 << i)) {
+            if (x < c + flp->nfiles) {
+                fep = &flp->fents[x - c];
+                if (name)
+                    fent_to_name(name, flp, fep);
+                if (flpp)
+                    *flpp = flp;
+                if (fepp)
+                    *fepp = fep;
+                *v = verbose;
+                for (j = 0; !*v && j < ilistlen; j++) {
+                    if (ilist[j] == fep->id) {
+                        *v = 1;
+                        break;
+                    }
+                }
+                return 1;
+            }
+            c += flp->nfiles;
+        }
+    }
+#ifdef DEBUG
+    RedPrintf("fsstress: get_fname failure\n");
+    abort();
+#endif
+    return -1;
+
+}
+
+static void init_pathname(pathname_t *name)
+{
+    name->len = 0;
+    name->path = NULL;
+}
+
+static int link_path(pathname_t *name1, pathname_t *name2)
+{
+    char buf1[MAXNAMELEN];
+    char buf2[MAXNAMELEN];
+    int down1;
+    pathname_t newname1;
+    pathname_t newname2;
+    int rval;
+
+    rval = link(name1->path, name2->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name1, buf1, &newname1);
+    separate_pathname(name2, buf2, &newname2);
+    if (strcmp(buf1, buf2) == 0) {
+        if (chdir(buf1) == 0) {
+            rval = link_path(&newname1, &newname2);
+            chdir("..");
+        }
+    } else {
+        if (strcmp(buf1, "..") == 0)
+            down1 = 0;
+        else if (strcmp(buf2, "..") == 0)
+            down1 = 1;
+        else if (strlen(buf1) == 0)
+            down1 = 0;
+        else if (strlen(buf2) == 0)
+            down1 = 1;
+        else
+            down1 = MAX(newname1.len, 3 + name2->len) <=
+                MAX(3 + name1->len, newname2.len);
+        if (down1) {
+            free_pathname(&newname2);
+            append_pathname(&newname2, "../");
+            append_pathname(&newname2, name2->path);
+            if (chdir(buf1) == 0) {
+                rval = link_path(&newname1, &newname2);
+                chdir("..");
+            }
+        } else {
+            free_pathname(&newname1);
+            append_pathname(&newname1, "../");
+            append_pathname(&newname1, name1->path);
+            if (chdir(buf2) == 0) {
+                rval = link_path(&newname1, &newname2);
+                chdir("..");
+            }
+        }
+    }
+    free_pathname(&newname1);
+    free_pathname(&newname2);
+    return rval;
+}
+
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = lstat64(name->path, sbuf);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = lstat64_path(&newname, sbuf);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void make_freq_table(void)
+{
+    int f;
+    int i;
+    opdesc_t *p;
+
+    for (p = ops, f = 0; p < ops_end; p++)
+        f += p->freq;
+    freq_table = malloc(f * sizeof(*freq_table));
+    freq_table_size = f;
+    for (p = ops, i = 0; p < ops_end; p++) {
+        for (f = 0; f < p->freq; f++, i++)
+            freq_table[i] = p->op;
+    }
+}
+
+static int mkdir_path(pathname_t *name, mode_t mode)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = mkdir(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = mkdir_path(&newname, mode);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void namerandpad(int id, char *buf, int len)
+{
+    int bucket;
+    static int buckets[8] = {0};
+    static int bucket_count = 0;
+    int bucket_value;
+    int i;
+    int padlen;
+    int padmod;
+
+    if (namerand == 0)
+        return;
+
+    /*  buckets[] used to be a statically initialized array with the following
+        initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
+
+        The problem is that with Reliance Edge, the maximum name length might be
+        less than 128.  So the below code populates buckets[] in a similar
+        fashion but avoids name lengths longer than the maximum.  For example,
+        if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.
+    */
+    if (!bucket_count) {
+        bucket_count = sizeof(buckets) / sizeof(buckets[0]);
+        bucket_value = 2;
+        for (i = 0; i < bucket_count; i++) {
+            if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)
+                break;
+            buckets[i] = bucket_value;
+            bucket_value *= 2;
+        }
+        if (i < bucket_count) {
+            buckets[i] = MAXNAMELEN - 1;
+            i++;
+        }
+        bucket_count = i;
+    }
+
+    bucket = (id ^ namerand) % bucket_count;
+    padmod = buckets[bucket] + 1 - len;
+    if (padmod <= 0)
+        return;
+    padlen = (id ^ namerand) % padmod;
+    if (padlen) {
+        memset(&buf[len], 'X', padlen);
+        buf[len + padlen] = '\0';
+    }
+}
+
+static int open_path(pathname_t *name, int oflag)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = open(name->path, oflag);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = open_path(&newname, oflag);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static DIR *opendir_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    DIR *rval;
+
+    rval = opendir(name->path);
+    if (rval || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = opendir_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int rename_path(pathname_t *name1, pathname_t *name2)
+{
+    char buf1[MAXNAMELEN];
+    char buf2[MAXNAMELEN];
+    int down1;
+    pathname_t newname1;
+    pathname_t newname2;
+    int rval;
+
+    rval = rename(name1->path, name2->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name1, buf1, &newname1);
+    separate_pathname(name2, buf2, &newname2);
+    if (strcmp(buf1, buf2) == 0) {
+        if (chdir(buf1) == 0) {
+            rval = rename_path(&newname1, &newname2);
+            chdir("..");
+        }
+    } else {
+        if (strcmp(buf1, "..") == 0)
+            down1 = 0;
+        else if (strcmp(buf2, "..") == 0)
+            down1 = 1;
+        else if (strlen(buf1) == 0)
+            down1 = 0;
+        else if (strlen(buf2) == 0)
+            down1 = 1;
+        else
+            down1 = MAX(newname1.len, 3 + name2->len) <=
+                MAX(3 + name1->len, newname2.len);
+        if (down1) {
+            free_pathname(&newname2);
+            append_pathname(&newname2, "../");
+            append_pathname(&newname2, name2->path);
+            if (chdir(buf1) == 0) {
+                rval = rename_path(&newname1, &newname2);
+                chdir("..");
+            }
+        } else {
+            free_pathname(&newname1);
+            append_pathname(&newname1, "../");
+            append_pathname(&newname1, name1->path);
+            if (chdir(buf2) == 0) {
+                rval = rename_path(&newname1, &newname2);
+                chdir("..");
+            }
+        }
+    }
+    free_pathname(&newname1);
+    free_pathname(&newname2);
+    return rval;
+}
+
+static int rmdir_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = rmdir(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = rmdir_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)
+{
+    char *slash;
+
+    init_pathname(newname);
+    slash = strchr(name->path, '/');
+    if (slash == NULL) {
+        buf[0] = '\0';
+        return;
+    }
+    *slash = '\0';
+    strcpy(buf, name->path);
+    *slash = '/';
+    append_pathname(newname, slash + 1);
+}
+
+static int stat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = stat64(name->path, sbuf);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = stat64_path(&newname, sbuf);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int truncate64_path(pathname_t *name, off64_t length)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = truncate64(name->path, length);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = truncate64_path(&newname, length);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static int unlink_path(pathname_t *name)
+{
+    char buf[MAXNAMELEN];
+    pathname_t newname;
+    int rval;
+
+    rval = unlink(name->path);
+    if (rval >= 0 || errno != RED_ENAMETOOLONG)
+        return rval;
+    separate_pathname(name, buf, &newname);
+    if (chdir(buf) == 0) {
+        rval = unlink_path(&newname);
+        chdir("..");
+    }
+    free_pathname(&newname);
+    return rval;
+}
+
+static void usage(const char *progname)
+{
+    RedPrintf("usage: %s VolumeID [Options]\n", progname);
+    RedPrintf("File system stress test.\n\n");
+    RedPrintf("Where:\n");
+    RedPrintf("  VolumeID\n");
+    RedPrintf("      A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");
+    RedPrintf("      of the volume to test.\n");
+    RedPrintf("And 'Options' are any of the following:\n");
+    RedPrintf("  --no-cleanup, -c\n");
+    RedPrintf("      Specifies not to remove files (cleanup) after execution\n");
+    RedPrintf("  --loops=count, -l count\n");
+    RedPrintf("      Specifies the number of times the entire test should loop.  Use 0 for\n");
+    RedPrintf("      infinite.  Default 1.\n");
+    RedPrintf("  --nops=count, -n count\n");
+    RedPrintf("      Specifies the number of operations to run (default 10000).\n");
+    RedPrintf("  --namepad, -r\n");
+    RedPrintf("      Specifies to use random name padding (resulting in longer names).\n");
+    RedPrintf("  --seed=value, -s value\n");
+    RedPrintf("      Specifies the seed for the random number generator (default timestamp).\n");
+    RedPrintf("  --verbose, -v\n");
+    RedPrintf("      Specifies verbose mode (without this, test is very quiet).\n");
+    RedPrintf("  --dev=devname, -D devname\n");
+    RedPrintf("      Specifies the device name.  This is typically only meaningful when\n");
+    RedPrintf("      running the test on a host machine.  This can be \"ram\" to test on a RAM\n");
+    RedPrintf("      disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");
+    RedPrintf("      reference to a device (on Windows, a drive letter like G: or a device name\n");
+    RedPrintf("      like \\\\.\\PhysicalDrive7).\n");
+    RedPrintf("  --help, -H\n");
+    RedPrintf("      Prints this usage text and exits.\n\n");
+    RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");
+}
+
+static void creat_f(int opno, long r)
+{
+    int e;
+    int e1;
+    pathname_t f;
+    int fd;
+    fent_t *fep;
+    int id;
+    int parid;
+    int type;
+    int v;
+    int v1;
+    int esz = 0;
+
+    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))
+        parid = -1;
+    else
+        parid = fep->id;
+    init_pathname(&f);
+    type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;
+    e = generate_fname(fep, type, &f, &id, &v);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: creat - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&f);
+        return;
+    }
+    fd = creat_path(&f, 0666);
+    e = fd < 0 ? errno : 0;
+    e1 = 0;
+    check_cwd();
+    esz = 0;
+    if (fd >= 0) {
+        add_to_flist(type, id, parid);
+        close(fd);
+    }
+    if (v)
+        RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
+               esz, e, e1);
+    free_pathname(&f);
+}
+
+static void fdatasync_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    int fd;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: fdatasync - no filename\n",
+                   procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: fdatasync - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    e = fdatasync(fd) < 0 ? errno : 0;
+    if (v)
+        RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void fsync_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    int fd;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: fsync - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: fsync - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    e = fsync(fd) < 0 ? errno : 0;
+    if (v)
+        RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void getdents_f(int opno, long r)
+{
+    DIR *dir;
+    pathname_t f;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))
+        append_pathname(&f, ".");
+    dir = opendir_path(&f);
+    check_cwd();
+    if (dir == NULL) {
+        if (v)
+            RedPrintf("%d/%d: getdents - can't open %s\n",
+                   procid, opno, f.path);
+        free_pathname(&f);
+        return;
+    }
+    while (readdir64(dir) != NULL)
+        continue;
+    if (v)
+        RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);
+    free_pathname(&f);
+    closedir(dir);
+}
+
+static void link_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int id;
+    pathname_t l;
+    int parid;
+    int v;
+    int v1;
+
+    init_pathname(&f);
+    if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
+        if (v1)
+            RedPrintf("%d/%d: link - no file\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))
+        parid = -1;
+    else
+        parid = fep->id;
+    v |= v1;
+    init_pathname(&l);
+    e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&l, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: link - no filename from %s\n",
+                   procid, opno, l.path);
+        }
+        free_pathname(&l);
+        free_pathname(&f);
+        return;
+    }
+    e = link_path(&f, &l) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        add_to_flist((int)(flp - flist), id, parid);
+    if (v)
+        RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
+               e);
+    free_pathname(&l);
+    free_pathname(&f);
+}
+
+static void mkdir_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    int id;
+    int parid;
+    int v;
+    int v1;
+
+    if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))
+        parid = -1;
+    else
+        parid = fep->id;
+    init_pathname(&f);
+    e = generate_fname(fep, FT_DIR, &f, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], fep);
+            RedPrintf("%d/%d: mkdir - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&f);
+        return;
+    }
+    e = mkdir_path(&f, 0777) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        add_to_flist(FT_DIR, id, parid);
+    if (v)
+        RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void read_f(int opno, long r)
+{
+    char *buf;
+    int e;
+    pathname_t f;
+    int fd;
+    uint32_t len;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: read - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_RDONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: read - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    if (fstat64(fd, &stb) < 0) {
+        if (v)
+            RedPrintf("%d/%d: read - fstat64 %s failed %d\n",
+                   procid, opno, f.path, errno);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    if (stb.st_size == 0) {
+        if (v)
+            RedPrintf("%d/%d: read - %s zero size\n", procid, opno,
+                   f.path);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = (off64_t) (lr % stb.st_size);
+    lseek64(fd, off, SEEK_SET);
+    len = (random() % (getpagesize() * 4)) + 1;
+    buf = malloc(len);
+    e = read(fd, buf, len) < 0 ? errno : 0;
+    free(buf);
+    if (v)
+        RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",
+               procid, opno, f.path, (long long)off, (long int)len, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+static void rename_f(int opno, long r)
+{
+    fent_t *dfep;
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int id;
+    pathname_t newf;
+    int oldid;
+    int parid;
+    int v;
+    int v1;
+
+    init_pathname(&f);
+    if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {
+        if (v1)
+            RedPrintf("%d/%d: rename - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))
+        parid = -1;
+    else
+        parid = dfep->id;
+    v |= v1;
+    init_pathname(&newf);
+    e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);
+    v |= v1;
+    if (!e) {
+        if (v) {
+            fent_to_name(&f, &flist[FT_DIR], dfep);
+            RedPrintf("%d/%d: rename - no filename from %s\n",
+                   procid, opno, f.path);
+        }
+        free_pathname(&newf);
+        free_pathname(&f);
+        return;
+    }
+    e = rename_path(&f, &newf) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0) {
+        if (flp - flist == FT_DIR) {
+            oldid = fep->id;
+            fix_parent(oldid, id);
+        }
+        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+        add_to_flist((int)(flp - flist), id, parid);
+    }
+    if (v)
+        RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
+               newf.path, e);
+    free_pathname(&newf);
+    free_pathname(&f);
+}
+
+static void rmdir_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {
+        if (v)
+            RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = rmdir_path(&f) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));
+    if (v)
+        RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void stat_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: stat - no entries\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = lstat64_path(&f, &stb) < 0 ? errno : 0;
+    check_cwd();
+    if (v)
+        RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void truncate_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: truncate - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = stat64_path(&f, &stb) < 0 ? errno : 0;
+    check_cwd();
+    if (e > 0) {
+        if (v)
+            RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);
+    off %= maxfsize;
+    e = truncate64_path(&f, off) < 0 ? errno : 0;
+    check_cwd();
+    if (v)
+        RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
+               (long long)off, e);
+    free_pathname(&f);
+}
+
+static void unlink_f(int opno, long r)
+{
+    int e;
+    pathname_t f;
+    fent_t *fep;
+    flist_t *flp;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {
+        if (v)
+            RedPrintf("%d/%d: unlink - no file\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    e = unlink_path(&f) < 0 ? errno : 0;
+    check_cwd();
+    if (e == 0)
+        del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+    if (v)
+        RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);
+    free_pathname(&f);
+}
+
+static void write_f(int opno, long r)
+{
+    char *buf;
+    int e;
+    pathname_t f;
+    int fd;
+    uint32_t len;
+    __int64_t lr;
+    off64_t off;
+    REDSTAT stb;
+    int v;
+
+    init_pathname(&f);
+    if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {
+        if (v)
+            RedPrintf("%d/%d: write - no filename\n", procid, opno);
+        free_pathname(&f);
+        return;
+    }
+    fd = open_path(&f, O_WRONLY);
+    e = fd < 0 ? errno : 0;
+    check_cwd();
+    if (fd < 0) {
+        if (v)
+            RedPrintf("%d/%d: write - open %s failed %d\n",
+                   procid, opno, f.path, e);
+        free_pathname(&f);
+        return;
+    }
+    if (fstat64(fd, &stb) < 0) {
+        if (v)
+            RedPrintf("%d/%d: write - fstat64 %s failed %d\n",
+                   procid, opno, f.path, errno);
+        free_pathname(&f);
+        close(fd);
+        return;
+    }
+    lr = ((__int64_t) random() << 32) + random();
+    off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
+    off %= maxfsize;
+    lseek64(fd, off, SEEK_SET);
+    len = (random() % (getpagesize() * 4)) + 1;
+    buf = malloc(len);
+    memset(buf, nameseq & 0xff, len);
+    e = write(fd, buf, len) < 0 ? errno : 0;
+    free(buf);
+    if (v)
+        RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",
+               procid, opno, f.path, (long long)off, (long int)len, e);
+    free_pathname(&f);
+    close(fd);
+}
+
+
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r)
+{
+    int32_t ret;
+    const char *pszVolume = gpRedVolConf->pszPathPrefix;
+
+    (void)r;
+
+    errno = 0;
+
+    ret = red_transact(pszVolume);
+
+    if(ret == 0)
+    {
+        ret = red_umount(pszVolume);
+
+        if(ret == 0)
+        {
+            int32_t ret2;
+
+            errno = -RedCoreVolCheck();
+            if(errno != 0)
+            {
+                ret = -1;
+            }
+
+            ret2 = red_mount(pszVolume);
+
+            if(ret == 0)
+            {
+                ret = ret2;
+            }
+
+            if(ret2 != 0)
+            {
+                exit(1);
+            }
+        }
+    }
+
+    if (verbose)
+    {
+        RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);
+    }
+}
+#endif
+
+
+#endif /* FSSTRESS_SUPPORTED */
+
index e4cd113a5272db925e066d88f17f45eee054f554..ec11e0a96406a41ec04bf135d19196a6eba254b7 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements utilities that convert strings to numbers.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-#define ISHEXDIGITU(c)  (((c) >= 'A') && ((c) <= 'F'))\r
-#define ISHEXDIGITL(c)  (((c) >= 'a') && ((c) <= 'f'))\r
-#define ISHEXDIGIT(c)   (ISHEXDIGITL(c) || ISHEXDIGITU(c))\r
-\r
-\r
-/** @brief Converts an ASCII number into an int32_t.\r
-\r
-    Converts all decimal digit numbers up to the end of the string or to the\r
-    first non-numerical character.\r
-\r
-    @note This function does *not* ignore leading white space.\r
-\r
-    @param pszNum   Pointer to a constant array of characters.\r
-\r
-    @return The integer represented in the string.\r
-*/\r
-int32_t RedAtoI(\r
-    const char *pszNum)\r
-{\r
-    int32_t     lValue = 0;\r
-    int32_t     lNegative = 1;\r
-    uint32_t    ulIdx = 0U;\r
-\r
-    if(pszNum[ulIdx] == '+')\r
-    {\r
-        ulIdx++;\r
-    }\r
-    else if(pszNum[ulIdx] == '-')\r
-    {\r
-        ulIdx++;\r
-        lNegative = -1;\r
-    }\r
-    else\r
-    {\r
-        /*  No sign, implicitly positive.\r
-        */\r
-    }\r
-\r
-    while(ISDIGIT(pszNum[ulIdx]))\r
-    {\r
-        lValue *= 10;\r
-        lValue += pszNum[ulIdx] - '0';\r
-        ulIdx++;\r
-    }\r
-\r
-    lValue *= lNegative;\r
-\r
-    return lValue;\r
-}\r
-\r
-\r
-/** @brief Convert a hexadecimal ASCII number into a uint32_t value.\r
-\r
-    The function processes all hex digits up to a NUL-terminator, or to the\r
-    first non-hex character.  Only hexadecimal digits are processed, so leading\r
-    white space, or a leading "0x" prefix are not allowed.\r
-\r
-    If pachNum points to an empty string (points to a NUL), this function will\r
-    return NULL, and the value at *pulNum will not be modified.\r
-\r
-    @note This function does not check for overflow.  If there are more\r
-          significant digits than can be represented in a uint32_t variable, the\r
-          output is unspecified.\r
-\r
-    @param pachNum  A pointer to a constant array of hex characters.\r
-    @param pulNum   A pointer to the location in which to store the uint32_t\r
-                    result.  Upon return, this value will be modified ONLY if\r
-                    the function succeeds and the returned pointer is valid (not\r
-                    NULL).\r
-\r
-    @return A pointer to the byte following the converted number or NULL to\r
-            indicate failure.\r
-*/\r
-const char *RedHtoUL(\r
-    const char *pszNum,\r
-    uint32_t   *pulNum)\r
-{\r
-    uint64_t    ullValue;\r
-    const char *pszReturn;\r
-\r
-    pszReturn = RedHtoULL(pszNum, &ullValue);\r
-    if(pszReturn != NULL)\r
-    {\r
-        if(ullValue < UINT32_MAX)\r
-        {\r
-            *pulNum = (uint32_t)ullValue;\r
-        }\r
-        else\r
-        {\r
-            pszReturn = NULL;\r
-        }\r
-    }\r
-\r
-    return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert a hexadecimal ASCII number into a D_UINT64 value.\r
-\r
-    The function processes all hex digits up to a NUL-terminator, or to the\r
-    first non-hex character.  Only hexadecimal digits are processed, so leading\r
-    white space, or a leading "0x" prefix are not allowed.\r
-\r
-    If pachNum points to an empty string (points to a NUL), this function will\r
-    return NULL, and the value at *pulNum will not be modified.\r
-\r
-    @note This function does not check for overflow.  If there are more\r
-          significant digits than can be represented in a uint64_t variable, the\r
-          output is unspecified.\r
-\r
-    @param pszNum   A pointer to a constant array of hex characters.\r
-    @param pullNum  A pointer to the location in which to store the uint64_t\r
-                    result.  Upon return, this value will be modified ONLY if\r
-                    the function succeeds and the returned pointer is valid (not\r
-                    NULL).\r
-\r
-    @return A pointer to the byte following the converted number, or NULL to\r
-            indicate failure.\r
-*/\r
-const char *RedHtoULL(\r
-    const char *pszNum,\r
-    uint64_t   *pullNum)\r
-{\r
-    uint64_t    ullValue = 0U;\r
-    const char *pszReturn = NULL;\r
-    uint32_t    ulIdx = 0U;\r
-\r
-    REDASSERT(pszNum != NULL);\r
-    REDASSERT(pullNum != NULL);\r
-\r
-    while(pszNum[ulIdx] != '\0')\r
-    {\r
-        char cDigit = pszNum[ulIdx];\r
-\r
-        if(ISDIGIT(cDigit))\r
-        {\r
-            cDigit -= '0';\r
-        }\r
-        else if(ISHEXDIGITU(cDigit))\r
-        {\r
-            cDigit -= ('A' - 10);\r
-        }\r
-        else if(ISHEXDIGITL(cDigit))\r
-        {\r
-            cDigit -= ('a' - 10);\r
-        }\r
-        else\r
-        {\r
-            break;\r
-        }\r
-\r
-        REDASSERT((ullValue & UINT64_SUFFIX(0xF000000000000000)) == 0U);\r
-\r
-        ullValue <<= 4U;\r
-        ullValue += cDigit;\r
-\r
-        ulIdx++;\r
-        pszReturn = &pszNum[ulIdx];\r
-    }\r
-\r
-    /*  Modify the number returned only if we found one or more valid hex\r
-        digits.\r
-    */\r
-    if(pszReturn != NULL)\r
-    {\r
-        *pullNum = ullValue;\r
-    }\r
-\r
-    return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert the ASCII number to a uint32_t  value.\r
-\r
-    The number may be hex or decimal.  Hex numbers must be prefixed by '0x', and\r
-    they may be upper or lower case.  The conversion process will stop with the\r
-    first non hex or decimal digit.\r
-\r
-    If the number is negative (the first character is a '-' sign), the value\r
-    will be range checked and returned as the equivalent unsigned value.\r
-\r
-    @note   This function will NOT fail for numbers which exceed the size of a\r
-            uint32_t value.\r
-\r
-    @param pszNum   A pointer to the ASCII number to convert\r
-    @param pulNum   A pointer to the uint32_t location to store the result.\r
-                    This value will be modified on return only if the function\r
-                    succeeds and the returned pointer is valid (not NULL).\r
-\r
-    @return A pointer to the byte following the converted number, or NULL to\r
-            indicate failure.\r
-*/\r
-const char *RedNtoUL(\r
-    const char *pszNum,\r
-    uint32_t   *pulNum)\r
-{\r
-    bool        fNegative = false;\r
-    uint32_t    ulIdx = 0U;\r
-    const char *pszReturn;\r
-\r
-    REDASSERT(pszNum != NULL);\r
-    REDASSERT(pulNum != NULL);\r
-\r
-    if(pszNum[ulIdx] == '-')\r
-    {\r
-        fNegative = true;\r
-        ulIdx++;\r
-    }\r
-\r
-    /*  Hex numbers must be prefixed with '0x'.\r
-    */\r
-    if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))\r
-    {\r
-        ulIdx += 2U;\r
-\r
-        if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))\r
-        {\r
-            pszReturn = RedHtoUL(&pszNum[ulIdx], pulNum);\r
-        }\r
-        else\r
-        {\r
-            pszReturn = NULL;\r
-        }\r
-    }\r
-    else if(ISDIGIT(pszNum[ulIdx]))\r
-    {\r
-        uint32_t ulTemp;\r
-\r
-        ulTemp = RedAtoI(&pszNum[ulIdx]);\r
-\r
-        while(ISDIGIT(pszNum[ulIdx]))\r
-        {\r
-            ulIdx++;\r
-        }\r
-\r
-        if(fNegative)\r
-        {\r
-            /*  Fail if the number is out of range.\r
-            */\r
-            if(ulTemp > INT32_MAX)\r
-            {\r
-                pszReturn = NULL;\r
-            }\r
-            else\r
-            {\r
-                *pulNum = -((int32_t)ulTemp);\r
-                pszReturn = &pszNum[ulIdx];\r
-            }\r
-        }\r
-        else\r
-        {\r
-            *pulNum = ulTemp;\r
-            pszReturn = &pszNum[ulIdx];\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  Return an error if there is not at least one hex or decimal digit.\r
-        */\r
-        pszReturn = NULL;\r
-    }\r
-\r
-    return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert the ASCII number pointed to by pachNum to a uint64_t value.\r
-\r
-    The number may be hex or decimal.  Hex numbers must be prefixed by '0x', and\r
-    they may be upper or lower case.  The conversion process will stop with the\r
-    first non hex or decimal digit.\r
-\r
-    If the number is negative (the first character is a '-' sign), the value\r
-    will be range checked and returned as the equivalent unsigned value.\r
-\r
-    @param pszNum   A pointer to the ASCII number to convert.\r
-    @param pullNum  A pointer to the uint64_t location to store the result.\r
-                    This value will be modified on return only if the function\r
-                    succeeds and the returned pointer is valid (not NULL).\r
-\r
-    @return A pointer to the byte following the converted number, or NULL to\r
-            indicate failure.\r
-*/\r
-const char *RedNtoULL(\r
-    const char *pszNum,\r
-    uint64_t   *pullNum)\r
-{\r
-    bool        fNegative = false;\r
-    uint32_t    ulIdx = 0U;\r
-    const char *pszReturn;\r
-\r
-    REDASSERT(pszNum != NULL);\r
-    REDASSERT(pullNum != NULL);\r
-\r
-    if(pszNum[ulIdx] == '-')\r
-    {\r
-        fNegative = true;\r
-        ulIdx++;\r
-    }\r
-\r
-    /*  Hex numbers must be prefixed with '0x'.\r
-    */\r
-    if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))\r
-    {\r
-        ulIdx += 2U;\r
-\r
-        if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))\r
-        {\r
-            pszReturn = RedHtoULL(&pszNum[ulIdx], pullNum);\r
-        }\r
-        else\r
-        {\r
-            pszReturn = NULL;\r
-        }\r
-    }\r
-    else if(ISDIGIT(pszNum[ulIdx]))\r
-    {\r
-        uint64_t ullTemp = 0U;\r
-\r
-        while(ISDIGIT(pszNum[ulIdx]))\r
-        {\r
-            ullTemp *= 10U;\r
-            ullTemp += pszNum[ulIdx] - '0';\r
-            ulIdx++;\r
-        }\r
-\r
-        if(fNegative)\r
-        {\r
-            /*  Fail if the number is out of range.\r
-            */\r
-            if(ullTemp > INT64_MAX)\r
-            {\r
-                pszReturn = NULL;\r
-            }\r
-            else\r
-            {\r
-                *pullNum = (uint64_t)(-((int64_t)ullTemp));\r
-                pszReturn = &pszNum[ulIdx];\r
-            }\r
-        }\r
-        else\r
-        {\r
-            *pullNum = ullTemp;\r
-            pszReturn = &pszNum[ulIdx];\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  Return an error if there is not at least one hex or decimal digit.\r
-        */\r
-        pszReturn = NULL;\r
-    }\r
-\r
-    return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert an ASCII hex or decimal number, which may may have a "B",\r
-           "KB", or "MB" suffix (case insensitive), to a binary value.\r
-\r
-    Hex numbers must be prefixed with "0x".\r
-\r
-    @note If there is no postfix, KB is assumed!\r
-\r
-    May fail due to bad formatting or overflow.\r
-\r
-    @param pszNum       A pointer to the ASCII number to convert.\r
-    @param pulResult    A pointer to a uint32_t in which to place the result.\r
-\r
-    @return A pointer to the byte following the string, or NULL to indicate an\r
-            error.  In the event of an error, *pulResult will not be modified.\r
-*/\r
-const char *RedSizeToUL(\r
-    const char *pszNum,\r
-    uint32_t   *pulResult)\r
-{\r
-    uint32_t    ulResult;\r
-    const char *pszSuffix;\r
-    const char *pszReturn;\r
-    uint32_t    ulIdx = 0U;\r
-\r
-    REDASSERT(pszNum != NULL);\r
-    REDASSERT(pulResult != NULL);\r
-\r
-    /*  Do the basic hex/decimal conversion\r
-    */\r
-    pszSuffix = RedNtoUL(pszNum, &ulResult);\r
-    if(pszSuffix != NULL)\r
-    {\r
-        if((pszSuffix[ulIdx] == 'B') || (pszSuffix[ulIdx] == 'b'))\r
-        {\r
-            ulIdx++;\r
-            pszReturn = &pszSuffix[ulIdx];\r
-        }\r
-        else if(    ((pszSuffix[ulIdx] == 'M') || (pszSuffix[ulIdx] == 'm'))\r
-                 && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))\r
-        {\r
-            ulIdx += 2U;\r
-\r
-            if(ulResult > (UINT32_MAX / (1024U * 1024U)))\r
-            {\r
-                pszReturn = NULL;\r
-            }\r
-            else\r
-            {\r
-                ulResult *= 1024U * 1024U;\r
-                pszReturn = &pszSuffix[ulIdx];\r
-            }\r
-        }\r
-        else\r
-        {\r
-            /*  The number is either postfixed with "KB" or something\r
-                else (we don't care), but we must increment the pointer\r
-                if it is something recognize.\r
-            */\r
-            if(    ((pszSuffix[ulIdx] == 'K') || (pszSuffix[ulIdx] == 'k'))\r
-                && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))\r
-            {\r
-                ulIdx += 2U;\r
-            }\r
-\r
-            /*  "B" or "MB" were not specified, so it must be "KB"\r
-            */\r
-            if(ulResult > (UINT32_MAX / 1024U))\r
-            {\r
-                pszReturn = NULL;\r
-            }\r
-            else\r
-            {\r
-                ulResult *= 1024UL;\r
-                pszReturn = &pszSuffix[ulIdx];\r
-            }\r
-        }\r
-\r
-        if(pszReturn != NULL)\r
-        {\r
-            *pulResult = ulResult;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        pszReturn = NULL;\r
-    }\r
-\r
-    return pszReturn;\r
-}\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements utilities that convert strings to numbers.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+#define ISHEXDIGITU(c)  (((c) >= 'A') && ((c) <= 'F'))
+#define ISHEXDIGITL(c)  (((c) >= 'a') && ((c) <= 'f'))
+#define ISHEXDIGIT(c)   (ISHEXDIGITL(c) || ISHEXDIGITU(c))
+
+
+/** @brief Converts an ASCII number into an int32_t.
+
+    Converts all decimal digit numbers up to the end of the string or to the
+    first non-numerical character.
+
+    @note This function does *not* ignore leading white space.
+
+    @param pszNum   Pointer to a constant array of characters.
+
+    @return The integer represented in the string.
+*/
+int32_t RedAtoI(
+    const char *pszNum)
+{
+    int32_t     lValue = 0;
+    int32_t     lNegative = 1;
+    uint32_t    ulIdx = 0U;
+
+    if(pszNum[ulIdx] == '+')
+    {
+        ulIdx++;
+    }
+    else if(pszNum[ulIdx] == '-')
+    {
+        ulIdx++;
+        lNegative = -1;
+    }
+    else
+    {
+        /*  No sign, implicitly positive.
+        */
+    }
+
+    while(ISDIGIT(pszNum[ulIdx]))
+    {
+        lValue *= 10;
+        lValue += pszNum[ulIdx] - '0';
+        ulIdx++;
+    }
+
+    lValue *= lNegative;
+
+    return lValue;
+}
+
+
+/** @brief Convert a hexadecimal ASCII number into a uint32_t value.
+
+    The function processes all hex digits up to a NUL-terminator, or to the
+    first non-hex character.  Only hexadecimal digits are processed, so leading
+    white space, or a leading "0x" prefix are not allowed.
+
+    If pachNum points to an empty string (points to a NUL), this function will
+    return NULL, and the value at *pulNum will not be modified.
+
+    @note This function does not check for overflow.  If there are more
+          significant digits than can be represented in a uint32_t variable, the
+          output is unspecified.
+
+    @param pszNum   A pointer to a constant array of hex characters.
+    @param pulNum   A pointer to the location in which to store the uint32_t
+                    result.  Upon return, this value will be modified ONLY if
+                    the function succeeds and the returned pointer is valid (not
+                    NULL).
+
+    @return A pointer to the byte following the converted number or NULL to
+            indicate failure.
+*/
+const char *RedHtoUL(
+    const char *pszNum,
+    uint32_t   *pulNum)
+{
+    uint64_t    ullValue;
+    const char *pszReturn;
+
+    pszReturn = RedHtoULL(pszNum, &ullValue);
+    if(pszReturn != NULL)
+    {
+        if(ullValue < UINT32_MAX)
+        {
+            *pulNum = (uint32_t)ullValue;
+        }
+        else
+        {
+            pszReturn = NULL;
+        }
+    }
+
+    return pszReturn;
+}
+
+
+/** @brief Convert a hexadecimal ASCII number into a D_UINT64 value.
+
+    The function processes all hex digits up to a NUL-terminator, or to the
+    first non-hex character.  Only hexadecimal digits are processed, so leading
+    white space, or a leading "0x" prefix are not allowed.
+
+    If pachNum points to an empty string (points to a NUL), this function will
+    return NULL, and the value at *pulNum will not be modified.
+
+    @note This function does not check for overflow.  If there are more
+          significant digits than can be represented in a uint64_t variable, the
+          output is unspecified.
+
+    @param pszNum   A pointer to a constant array of hex characters.
+    @param pullNum  A pointer to the location in which to store the uint64_t
+                    result.  Upon return, this value will be modified ONLY if
+                    the function succeeds and the returned pointer is valid (not
+                    NULL).
+
+    @return A pointer to the byte following the converted number, or NULL to
+            indicate failure.
+*/
+const char *RedHtoULL(
+    const char *pszNum,
+    uint64_t   *pullNum)
+{
+    uint64_t    ullValue = 0U;
+    const char *pszReturn = NULL;
+    uint32_t    ulIdx = 0U;
+
+    REDASSERT(pszNum != NULL);
+    REDASSERT(pullNum != NULL);
+
+    while(pszNum[ulIdx] != '\0')
+    {
+        char cDigit = pszNum[ulIdx];
+
+        if(ISDIGIT(cDigit))
+        {
+            cDigit -= '0';
+        }
+        else if(ISHEXDIGITU(cDigit))
+        {
+            cDigit -= ('A' - 10);
+        }
+        else if(ISHEXDIGITL(cDigit))
+        {
+            cDigit -= ('a' - 10);
+        }
+        else
+        {
+            break;
+        }
+
+        REDASSERT((ullValue & UINT64_SUFFIX(0xF000000000000000)) == 0U);
+
+        ullValue <<= 4U;
+        ullValue += cDigit;
+
+        ulIdx++;
+        pszReturn = &pszNum[ulIdx];
+    }
+
+    /*  Modify the number returned only if we found one or more valid hex
+        digits.
+    */
+    if(pszReturn != NULL)
+    {
+        *pullNum = ullValue;
+    }
+
+    return pszReturn;
+}
+
+
+/** @brief Convert the ASCII number to a uint32_t  value.
+
+    The number may be hex or decimal.  Hex numbers must be prefixed by '0x', and
+    they may be upper or lower case.  The conversion process will stop with the
+    first non hex or decimal digit.
+
+    If the number is negative (the first character is a '-' sign), the value
+    will be range checked and returned as the equivalent unsigned value.
+
+    @note   This function will NOT fail for numbers which exceed the size of a
+            uint32_t value.
+
+    @param pszNum   A pointer to the ASCII number to convert
+    @param pulNum   A pointer to the uint32_t location to store the result.
+                    This value will be modified on return only if the function
+                    succeeds and the returned pointer is valid (not NULL).
+
+    @return A pointer to the byte following the converted number, or NULL to
+            indicate failure.
+*/
+const char *RedNtoUL(
+    const char *pszNum,
+    uint32_t   *pulNum)
+{
+    bool        fNegative = false;
+    uint32_t    ulIdx = 0U;
+    const char *pszReturn;
+
+    REDASSERT(pszNum != NULL);
+    REDASSERT(pulNum != NULL);
+
+    if(pszNum[ulIdx] == '-')
+    {
+        fNegative = true;
+        ulIdx++;
+    }
+
+    /*  Hex numbers must be prefixed with '0x'.
+    */
+    if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))
+    {
+        ulIdx += 2U;
+
+        if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))
+        {
+            pszReturn = RedHtoUL(&pszNum[ulIdx], pulNum);
+        }
+        else
+        {
+            pszReturn = NULL;
+        }
+    }
+    else if(ISDIGIT(pszNum[ulIdx]))
+    {
+        uint32_t ulTemp;
+
+        ulTemp = RedAtoI(&pszNum[ulIdx]);
+
+        while(ISDIGIT(pszNum[ulIdx]))
+        {
+            ulIdx++;
+        }
+
+        if(fNegative)
+        {
+            /*  Fail if the number is out of range.
+            */
+            if(ulTemp > INT32_MAX)
+            {
+                pszReturn = NULL;
+            }
+            else
+            {
+                *pulNum = -((int32_t)ulTemp);
+                pszReturn = &pszNum[ulIdx];
+            }
+        }
+        else
+        {
+            *pulNum = ulTemp;
+            pszReturn = &pszNum[ulIdx];
+        }
+    }
+    else
+    {
+        /*  Return an error if there is not at least one hex or decimal digit.
+        */
+        pszReturn = NULL;
+    }
+
+    return pszReturn;
+}
+
+
+/** @brief Convert the ASCII number pointed to by pachNum to a uint64_t value.
+
+    The number may be hex or decimal.  Hex numbers must be prefixed by '0x', and
+    they may be upper or lower case.  The conversion process will stop with the
+    first non hex or decimal digit.
+
+    If the number is negative (the first character is a '-' sign), the value
+    will be range checked and returned as the equivalent unsigned value.
+
+    @param pszNum   A pointer to the ASCII number to convert.
+    @param pullNum  A pointer to the uint64_t location to store the result.
+                    This value will be modified on return only if the function
+                    succeeds and the returned pointer is valid (not NULL).
+
+    @return A pointer to the byte following the converted number, or NULL to
+            indicate failure.
+*/
+const char *RedNtoULL(
+    const char *pszNum,
+    uint64_t   *pullNum)
+{
+    bool        fNegative = false;
+    uint32_t    ulIdx = 0U;
+    const char *pszReturn;
+
+    REDASSERT(pszNum != NULL);
+    REDASSERT(pullNum != NULL);
+
+    if(pszNum[ulIdx] == '-')
+    {
+        fNegative = true;
+        ulIdx++;
+    }
+
+    /*  Hex numbers must be prefixed with '0x'.
+    */
+    if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))
+    {
+        ulIdx += 2U;
+
+        if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))
+        {
+            pszReturn = RedHtoULL(&pszNum[ulIdx], pullNum);
+        }
+        else
+        {
+            pszReturn = NULL;
+        }
+    }
+    else if(ISDIGIT(pszNum[ulIdx]))
+    {
+        uint64_t ullTemp = 0U;
+
+        while(ISDIGIT(pszNum[ulIdx]))
+        {
+            ullTemp *= 10U;
+            ullTemp += pszNum[ulIdx] - '0';
+            ulIdx++;
+        }
+
+        if(fNegative)
+        {
+            /*  Fail if the number is out of range.
+            */
+            if(ullTemp > INT64_MAX)
+            {
+                pszReturn = NULL;
+            }
+            else
+            {
+                *pullNum = (uint64_t)(-((int64_t)ullTemp));
+                pszReturn = &pszNum[ulIdx];
+            }
+        }
+        else
+        {
+            *pullNum = ullTemp;
+            pszReturn = &pszNum[ulIdx];
+        }
+    }
+    else
+    {
+        /*  Return an error if there is not at least one hex or decimal digit.
+        */
+        pszReturn = NULL;
+    }
+
+    return pszReturn;
+}
+
+
+/** @brief Convert an ASCII hex or decimal number, which may may have a "B",
+           "KB", or "MB" suffix (case insensitive), to a binary value.
+
+    Hex numbers must be prefixed with "0x".
+
+    @note If there is no postfix, KB is assumed!
+
+    May fail due to bad formatting or overflow.
+
+    @param pszNum       A pointer to the ASCII number to convert.
+    @param pulResult    A pointer to a uint32_t in which to place the result.
+
+    @return A pointer to the byte following the string, or NULL to indicate an
+            error.  In the event of an error, *pulResult will not be modified.
+*/
+const char *RedSizeToUL(
+    const char *pszNum,
+    uint32_t   *pulResult)
+{
+    uint32_t    ulResult;
+    const char *pszSuffix;
+    const char *pszReturn;
+    uint32_t    ulIdx = 0U;
+
+    REDASSERT(pszNum != NULL);
+    REDASSERT(pulResult != NULL);
+
+    /*  Do the basic hex/decimal conversion
+    */
+    pszSuffix = RedNtoUL(pszNum, &ulResult);
+    if(pszSuffix != NULL)
+    {
+        if((pszSuffix[ulIdx] == 'B') || (pszSuffix[ulIdx] == 'b'))
+        {
+            ulIdx++;
+            pszReturn = &pszSuffix[ulIdx];
+        }
+        else if(    ((pszSuffix[ulIdx] == 'M') || (pszSuffix[ulIdx] == 'm'))
+                 && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))
+        {
+            ulIdx += 2U;
+
+            if(ulResult > (UINT32_MAX / (1024U * 1024U)))
+            {
+                pszReturn = NULL;
+            }
+            else
+            {
+                ulResult *= 1024U * 1024U;
+                pszReturn = &pszSuffix[ulIdx];
+            }
+        }
+        else
+        {
+            /*  The number is either postfixed with "KB" or something
+                else (we don't care), but we must increment the pointer
+                if it is something recognize.
+            */
+            if(    ((pszSuffix[ulIdx] == 'K') || (pszSuffix[ulIdx] == 'k'))
+                && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))
+            {
+                ulIdx += 2U;
+            }
+
+            /*  "B" or "MB" were not specified, so it must be "KB"
+            */
+            if(ulResult > (UINT32_MAX / 1024U))
+            {
+                pszReturn = NULL;
+            }
+            else
+            {
+                ulResult *= 1024UL;
+                pszReturn = &pszSuffix[ulIdx];
+            }
+        }
+
+        if(pszReturn != NULL)
+        {
+            *pulResult = ulResult;
+        }
+    }
+    else
+    {
+        pszReturn = NULL;
+    }
+
+    return pszReturn;
+}
+
index e8f52015f349df376805e7a24180d279dd38ce09..00f6bc331640b21ffcfb3b3ca1e4eaee79d45ae5 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements routines for certain 64-bit math operations and simulated\r
-           floating point.\r
-\r
-    RedUint64DivMod32() and RedUint64DivMod64() are derived from code at\r
-    http://www.hackersdelight.org.  This web site states explicitly that "You\r
-    are free to use, copy, and distribute any of the code on this web site,\r
-    whether modified by you or not.  You need not give attribution."\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-static uint32_t nlz64(uint64_t ullValue);\r
-\r
-\r
-/** @brief Return a ratio value formatted as a floating point string accurate to\r
-           the specified number of decimal places.\r
-\r
-    The  function exists to provide floating point style output without using\r
-    any actual floating point types.\r
-\r
-    This function may scale the numbers down to avoid overflow at the high end.\r
-    Likewise, potential divide-by-zero errors are internally avoided.  Here are\r
-    some examples:\r
-\r
-    Dividend | Divisor | DecPlaces | Result\r
-    -------- | ------- | --------- | ------\r
-    12133    | 28545   | 2         | "0.42"\r
-    1539     | 506     | 2         | "3.04"\r
-\r
-    To get a number formatted as a percentage, take the take the portion of the\r
-    total (normally the smaller part), multiply it by 100, and pass it to this\r
-    function as the Dividend, pass the "total" value to this function as the\r
-    Divisor, and specify the desired number of decimal places.\r
-\r
-    For example, if you have a disk format overhead value of N blocks out of a\r
-    total of Y blocks on the disk, and you want to display the format overhead\r
-    as a percentage, you would use a function call\r
-    similar to:\r
-\r
-    ~~~{.c}\r
-    RedRatio(szBuffer, sizeof(szBuffer), N*100U, Y, 2U);\r
-    ~~~\r
-\r
-    If N=145, Y=4096, and decimal places is 2, the resulting output would be\r
-    "3.54".\r
-\r
-    The string returned will always be null-terminated, even if it means\r
-    stomping on the least significant decimal digit.\r
-\r
-    If either the dividend or divisor values are zero, the string "0.0" will be\r
-    returned, with the prescribed number of decimal places.\r
-\r
-    @note This function has "reasonable" limits which meet the needs of the\r
-          various supplemental utilities which use this function.  Extremely\r
-          large ratios, or using many decimal places may not function as\r
-          desired.\r
-\r
-    Parameters:\r
-    @param pBuffer      A pointer to the buffer in which to store the null\r
-                        terminated results.\r
-    @param ulBufferLen  The length of the output buffer.\r
-    @param ullDividend  The "total" value to divide.\r
-    @param ullDivisor   The portion of ullDividend for which to calculate the\r
-                        ratio (may be greater than ulDividend).\r
-    @param ulDecPlaces  The number of decimal places to use, from 0 to 9.\r
-\r
-    @return @p pBuffer.\r
-*/\r
-char *RedRatio(\r
-    char       *pBuffer,\r
-    uint32_t    ulBufferLen,\r
-    uint64_t    ullDividend,\r
-    uint64_t    ullDivisor,\r
-    uint32_t    ulDecPlaces)\r
-{\r
-    REDASSERT(pBuffer != NULL);\r
-    REDASSERT(ulBufferLen > 0U);\r
-    REDASSERT(ulDecPlaces <= 9U);   /* arbitrary */\r
-\r
-    if((ullDivisor > 0U) && (ullDividend > 0U))\r
-    {\r
-        uint32_t    ii;\r
-        uint32_t    ulFactor = 1U;\r
-        uint64_t    ullDecimal;\r
-        uint64_t    ullTemp;\r
-\r
-        for(ii = 1U; ii <= ulDecPlaces; ii++)\r
-        {\r
-            ulFactor *= 10U;\r
-        }\r
-\r
-        ullDecimal = RedMulDiv64(ullDividend, ulFactor, ullDivisor);\r
-\r
-        /*  Shouldn't really be calling this function in a situation where we\r
-            can overflow at this point...\r
-        */\r
-        REDASSERT(ullDecimal != UINT64_MAX);\r
-\r
-        if(ullDivisor <= ullDividend)\r
-        {\r
-            uint32_t ulDecimal;\r
-\r
-            (void)RedUint64DivMod32(ullDecimal, ulFactor, &ulDecimal);\r
-            ullDecimal = ulDecimal;\r
-        }\r
-\r
-        ullTemp = RedUint64DivMod64(ullDividend, ullDivisor, NULL);\r
-\r
-        if(ulDecPlaces > 0U)\r
-        {\r
-            RedSNPrintf(pBuffer, ulBufferLen, "%llu.%0*llu", (unsigned long long)ullTemp,\r
-                (unsigned)ulDecPlaces, (unsigned long long)ullDecimal);\r
-        }\r
-        else\r
-        {\r
-            RedSNPrintf(pBuffer, ulBufferLen, "%llu", (unsigned long long)ullTemp);\r
-        }\r
-    }\r
-    else\r
-    {\r
-        /*  If either the dividend or divisor is zero, then just output a "0.0"\r
-            string with the prescribed number of decimal places.\r
-        */\r
-        if(ulDecPlaces > 0U)\r
-        {\r
-            RedSNPrintf(pBuffer, ulBufferLen, "0.%0*u", (unsigned)ulDecPlaces, 0U);\r
-        }\r
-        else\r
-        {\r
-            RedStrNCpy(pBuffer, "0", ulBufferLen);\r
-        }\r
-    }\r
-\r
-    /*  Ensure the returned buffer is always null-terminated\r
-    */\r
-    pBuffer[ulBufferLen - 1U] = '\0';\r
-\r
-    return pBuffer;\r
-}\r
-\r
-\r
-/** @brief Multiply 64-bit and 32-bit numbers, and divide by a 64-bit number,\r
-           returning a 64-bit result.\r
-\r
-    @note This function may return an approximate value if multiplying\r
-          @p ullBase and @p ulMultplier results in a number larger than 64-bits\r
-          _and_ this cannot be avoided by scaling.\r
-\r
-    @param ullBase      The base 64-bit number number.\r
-    @param ulMultiplier The 32-bit number by which to multiply.\r
-    @param ullDivisor   The 64-bit number by which to divide.\r
-\r
-    @return The 64-bit unsigned integer result.  Always returns zero if either\r
-            @p ullBase or @p ulMultiplier are zero (regardless what\r
-            @p ullDivisor is).  Returns UINT64_MAX if an overflow condition\r
-            occurred, or if @p ullDivisor is zero.\r
-*/\r
-uint64_t RedMulDiv64(\r
-    uint64_t ullBase,\r
-    uint32_t ulMultiplier,\r
-    uint64_t ullDivisor)\r
-{\r
-    uint64_t ullTemp;\r
-\r
-    /*  Result would always be zero if either of these are zero.  Specifically\r
-        test this case before looking for a zero divisor.\r
-    */\r
-    if((ullBase == 0U) || (ulMultiplier == 0U))\r
-    {\r
-        return 0U;\r
-    }\r
-\r
-    if(ullDivisor == 0U)\r
-    {\r
-        return UINT64_MAX;\r
-    }\r
-\r
-    /*  Since we don't have the ability (yet) to use 128-bit numbers, we jump\r
-        through the following hoops (in order) to try to determine the proper\r
-        results without losing precision:\r
-\r
-        1) Shift the divisor and one of the multiplicands as many times as is\r
-           necessary to reduce the scale -- only if it can be done without\r
-           losing precision.\r
-        2) Divide one of the multiplicands by the divisor first, but only if it\r
-           divides evenly, preserving precision.\r
-        3) Same as #2, but try it for the other multiplicand.\r
-        4) Last ditch, divide the larger multiplicand by the divisor first, then\r
-           do the multiply.  This <WILL> lose precision.\r
-\r
-        These solutions are identified as CODE-PATHs #1-4 which are used to\r
-        identify the matching tests in dltmain.c.\r
-\r
-        Note that execution might partially include CODE-PATH #1 up until\r
-        shifting can no longer be done without losing precision.  In that case,\r
-        one of the three remaining options will be used.\r
-    */\r
-\r
-    ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);\r
-    while(ullBase > ullTemp)\r
-    {\r
-        uint64_t ullMod;\r
-        uint64_t ullBaseTemp;\r
-        uint64_t ullWideMultiplier;\r
-\r
-        /*  CODE-PATH #1\r
-        */\r
-        /*  So long as ulDivisor, and at least one of the other numbers, are\r
-            evenly divisible by 2, we can scale the numbers so the result does\r
-            not overflow the intermediate 64-bit value.\r
-        */\r
-        if((ullDivisor & 1U) == 0U)\r
-        {\r
-            if((ullBase & 1U) == 0U)\r
-            {\r
-                /*  CODE-PATH #1a\r
-                */\r
-                ullDivisor >>= 1U;\r
-                ullBase >>= 1U;\r
-                continue;\r
-            }\r
-\r
-            if(((ulMultiplier & 1U) == 0U) && ((ullTemp & UINT64_SUFFIX(0x8000000000000000)) == 0U))\r
-            {\r
-                /*  CODE-PATH #1b\r
-                */\r
-                ullDivisor >>= 1U;\r
-                ulMultiplier >>= 1U;\r
-                ullTemp <<= 1U;\r
-                continue;\r
-            }\r
-        }\r
-\r
-        /*  If we get to this point, the above method (#1) cannot be used\r
-            because not enough of the numbers are even long enough to scale the\r
-            operands down.  We'll see if either multiplicand is evenly divisble\r
-            by ulDivisor, and if so, do the divide first, then the multiply.\r
-            (Note that once we get to this point, we will never exercise the\r
-            while{} loop anymore.)\r
-        */\r
-\r
-        /*  CODE-PATH #2\r
-        */\r
-        ullBaseTemp = RedUint64DivMod64(ullBase, ullDivisor, &ullMod);\r
-        if(ullMod == 0U)\r
-        {\r
-            /*  Evenly divides, so check that we won't overflow, and finish up.\r
-            */\r
-            ullBase = ullBaseTemp;\r
-            if(ullBase > ullTemp)\r
-            {\r
-                return UINT64_MAX;\r
-            }\r
-            else\r
-            {\r
-                /*  We've validated that this will not overflow.\r
-                */\r
-                ullBase *= ulMultiplier;\r
-                return ullBase;\r
-            }\r
-        }\r
-\r
-        /*  CODE-PATH #3\r
-        */\r
-        ullWideMultiplier = RedUint64DivMod64(ulMultiplier, ullDivisor, &ullMod);\r
-        if(ullMod == 0U)\r
-        {\r
-            /*  Evenly divides, so check that we won't overflow, and finish up.\r
-            */\r
-\r
-            /*  Must recalculate ullTemp relative to ullBase\r
-            */\r
-            ullTemp = RedUint64DivMod64(UINT64_MAX, ullBase, NULL);\r
-            if(ullWideMultiplier > ullTemp)\r
-            {\r
-                return UINT64_MAX;\r
-            }\r
-            else\r
-            {\r
-                uint32_t ulNarrowMultiplier = (uint32_t)ullWideMultiplier;\r
-\r
-                /*  We've validated that this will not overflow.\r
-                */\r
-                ullBase *= ulNarrowMultiplier;\r
-                return ullBase;\r
-            }\r
-        }\r
-\r
-        /*  CODE-PATH #4\r
-\r
-            Neither of the multipliers is evenly divisible by the divisor, so\r
-            just punt and divide the larger number first, then do the final\r
-            multiply.\r
-\r
-            All the other attempts above would preserve precision -- this is the\r
-            only case where precision may be lost.\r
-        */\r
-\r
-        /*  If necessary reverse the ullBase and ulMultiplier operands so that\r
-            ullBase contains the larger of the two values.\r
-        */\r
-        if(ullBase < ulMultiplier)\r
-        {\r
-            uint32_t ulTemp = ulMultiplier;\r
-\r
-            ulMultiplier = (uint32_t)ullBase;\r
-            ullBase = ulTemp;\r
-        }\r
-\r
-        ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);\r
-        ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);\r
-        if(ullBase > ullTemp)\r
-        {\r
-            return UINT64_MAX;\r
-        }\r
-        else\r
-        {\r
-            ullBase *= ulMultiplier;\r
-            return ullBase;\r
-        }\r
-    }\r
-\r
-    /*  We only get to this point if either there was never any chance of\r
-        overflow, or if the pure shifting mechanism succeeded in reducing\r
-        the scale so overflow is not a problem.\r
-    */\r
-\r
-    ullBase *= ulMultiplier;\r
-    ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);\r
-\r
-    return ullBase;\r
-}\r
-\r
-\r
-/** @brief Divide a 64-bit value by a 32-bit value, returning the quotient and\r
-           the remainder.\r
-\r
-    Essentially this function does the following:\r
-\r
-    ~~~{.c}\r
-    if(pulRemainder != NULL)\r
-    {\r
-        *pulRemainder = (uint32_t)(ullDividend % ulDivisor);\r
-    }\r
-    return ullDividend / ulDivisor;\r
-    ~~~\r
-\r
-    However, it does so without ever actually dividing/modulating a 64-bit\r
-    value, since such operations are not allowed in all environments.\r
-\r
-    @param ullDividend  The value to divide.\r
-    @param ulDivisor    The value to divide by.\r
-    @param pulRemander  Populated with the remainder; may be NULL.\r
-\r
-    @return The quotient (result of the division).\r
-*/\r
-uint64_t RedUint64DivMod32(\r
-    uint64_t    ullDividend,\r
-    uint32_t    ulDivisor,\r
-    uint32_t   *pulRemainder)\r
-{\r
-    uint64_t    ullQuotient;\r
-    uint32_t    ulResultRemainder;\r
-\r
-    /*  Check for divide by zero.\r
-    */\r
-    if(ulDivisor == 0U)\r
-    {\r
-        REDERROR();\r
-\r
-        /*  Nonsense value if no asserts.\r
-        */\r
-        ullQuotient = UINT64_SUFFIX(0xFFFFFFFFFFFFFBAD);\r
-        ulResultRemainder = 0xFFFFFBADU;\r
-    }\r
-    else if(ullDividend <= UINT32_MAX)\r
-    {\r
-        uint32_t ulDividend = (uint32_t)ullDividend;\r
-\r
-        ullQuotient = ulDividend / ulDivisor;\r
-        ulResultRemainder = ulDividend % ulDivisor;\r
-    }\r
-    else\r
-    {\r
-        uint32_t    ulResultHi;\r
-        uint32_t    ulResultLo;\r
-        uint32_t    ulRemainder;\r
-        uint8_t     bIndex;\r
-        uint32_t    ulThisDivision;\r
-        uint32_t    ulMask;\r
-        uint8_t     ucNextValue;\r
-        uint32_t    ulInterimHi, ulInterimLo;\r
-        uint32_t    ulLowDword = (uint32_t)ullDividend;\r
-        uint32_t    ulHighDword = (uint32_t)(ullDividend >> 32U);\r
-\r
-        /*  Compute the high part and get the remainder\r
-        */\r
-        ulResultHi = ulHighDword / ulDivisor;\r
-        ulResultLo = 0U;\r
-        ulRemainder = ulHighDword % ulDivisor;\r
-\r
-        /*  Compute the low part\r
-        */\r
-        ulMask = 0xFF000000U;\r
-        for(bIndex = 0U; bIndex < sizeof(uint32_t); bIndex++)\r
-        {\r
-            ucNextValue = (uint8_t)((ulLowDword & ulMask) >> ((sizeof(uint32_t) - 1U - bIndex) * 8U));\r
-            ulInterimHi = ulRemainder >> 24U;\r
-            ulInterimLo = (ulRemainder << 8U) | ucNextValue;\r
-            ulThisDivision = 0U;\r
-            while(ulInterimHi != 0U)\r
-            {\r
-                uint64_t ullInterim = ((uint64_t)ulInterimHi << 32U) + ulInterimLo;\r
-\r
-                ullInterim -= ulDivisor;\r
-                ulThisDivision++;\r
-\r
-                ulInterimHi = (uint32_t)(ullInterim >> 32U);\r
-                ulInterimLo = (uint32_t)ullInterim;\r
-            }\r
-            ulThisDivision += ulInterimLo / ulDivisor;\r
-            ulRemainder = ulInterimLo % ulDivisor;\r
-            ulResultLo <<= 8U;\r
-            ulResultLo += ulThisDivision;\r
-            ulMask >>= 8U;\r
-        }\r
-\r
-        ullQuotient = ((uint64_t)ulResultHi << 32U) + ulResultLo;\r
-        ulResultRemainder = (uint32_t)(ullDividend - (ullQuotient * ulDivisor));\r
-    }\r
-\r
-    if(pulRemainder != NULL)\r
-    {\r
-        *pulRemainder = ulResultRemainder;\r
-    }\r
-\r
-    return ullQuotient;\r
-}\r
-\r
-\r
-/** @brief Divide a 64-bit value by a 64-bit value, returning the quotient and\r
-           the remainder.\r
-\r
-    Essentially this function does the following:\r
-\r
-    ~~~{.c}\r
-    if(pullRemainder != NULL)\r
-    {\r
-        *pullRemainder = ullDividend % ullDivisor;\r
-    }\r
-    return ullDividend / ullDivisor;\r
-    ~~~\r
-\r
-    However, it does so without ever actually dividing/modulating a 64-bit\r
-    value, since such operations are not allowed in all environments.\r
-\r
-    @param ullDividend  The value to divide.\r
-    @param ullDivisor   The value to divide by.\r
-    @param pullRemander Populated with the remainder; may be NULL.\r
-\r
-    @return The quotient (result of the division).\r
-*/\r
-uint64_t RedUint64DivMod64(\r
-    uint64_t    ullDividend,\r
-    uint64_t    ullDivisor,\r
-    uint64_t   *pullRemainder)\r
-{\r
-    /*  The variables u0, u1, etc. take on only 32-bit values, but they are\r
-        declared uint64_t to avoid some compiler warning messages and to avoid\r
-        some unnecessary EXTRs that the compiler would put in, to convert\r
-        uint64_ts to ints.\r
-    */\r
-    uint64_t    u0;\r
-    uint64_t    u1;\r
-    uint64_t    q0;\r
-    uint64_t    q1;\r
-    uint64_t    ullQuotient;\r
-\r
-    /*  First the procedure takes care of the case in which the divisor is a\r
-        32-bit quantity.  There are two subcases: (1) If the left half of the\r
-        dividend is less than the divisor, one execution of RedUint64DivMod32()\r
-        is all that is required (overflow is not possible). (2) Otherwise it\r
-        does two divisions, using the grade school method.\r
-    */\r
-\r
-    if((ullDivisor >> 32U) == 0U)\r
-    {\r
-        if((ullDividend >> 32U) < ullDivisor)\r
-        {\r
-            /*  If ullDividend/ullDivisor cannot overflow, just do one division.\r
-            */\r
-            ullQuotient = RedUint64DivMod32(ullDividend, (uint32_t)ullDivisor, NULL);\r
-        }\r
-        else\r
-        {\r
-            uint32_t k;\r
-\r
-            /*  If ullDividend/ullDivisor would overflow:\r
-            */\r
-\r
-            /*  Break ullDividend up into two halves.\r
-            */\r
-            u1 = ullDividend >> 32U;\r
-            u0 = ullDividend & 0xFFFFFFFFU;\r
-\r
-            /*  First quotient digit and first remainder.\r
-            */\r
-            q1 = RedUint64DivMod32(u1, (uint32_t)ullDivisor, &k);\r
-\r
-            /*  2nd quot. digit.\r
-            */\r
-            q0 = RedUint64DivMod32(((uint64_t)k << 32U) + u0, (uint32_t)ullDivisor, NULL);\r
-\r
-            ullQuotient = (q1 << 32U) + q0;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        uint64_t n;\r
-        uint64_t v1;\r
-\r
-        n = nlz64(ullDivisor);          /* 0 <= n <= 31. */\r
-        v1 = (ullDivisor << n) >> 32U;  /* Normalize the divisor so its MSB is 1. */\r
-        u1 = ullDividend >> 1U;         /* To ensure no overflow. */\r
-\r
-        /*  Get quotient from divide unsigned insn.\r
-        */\r
-        q1 = RedUint64DivMod32(u1, (uint32_t)v1, NULL);\r
-\r
-        q0 = (q1 << n) >> 31U;  /* Undo normalization and division of ullDividend by 2. */\r
-\r
-        /*  Make q0 correct or too small by 1.\r
-        */\r
-        if(q0 != 0U)\r
-        {\r
-            q0--;\r
-        }\r
-\r
-        if((ullDividend - (q0 * ullDivisor)) >= ullDivisor)\r
-        {\r
-            q0++;   /* Now q0 is correct. */\r
-        }\r
-\r
-        ullQuotient = q0;\r
-    }\r
-\r
-    if(pullRemainder != NULL)\r
-    {\r
-        *pullRemainder = ullDividend - (ullQuotient * ullDivisor);\r
-    }\r
-\r
-    return ullQuotient;\r
-}\r
-\r
-\r
-/** @brief Compute the number of leading zeroes in a 64-bit value.\r
-\r
-    @param ullValue The value for which to compute the NLZ.\r
-\r
-    @return The number of leading zeroes in @p ullValue.\r
-*/\r
-static uint32_t nlz64(\r
-    uint64_t    ullValue)\r
-{\r
-    uint32_t    n;\r
-\r
-    if(ullValue == 0U)\r
-    {\r
-        n = 64U;\r
-    }\r
-    else\r
-    {\r
-        uint64_t x = ullValue;\r
-\r
-        n = 0U;\r
-\r
-        if(x <= UINT64_SUFFIX(0x00000000FFFFFFFF))\r
-        {\r
-            n += 32U;\r
-            x <<= 32U;\r
-        }\r
-\r
-        if(x <= UINT64_SUFFIX(0x0000FFFFFFFFFFFF))\r
-        {\r
-            n += 16U;\r
-            x <<= 16U;\r
-        }\r
-\r
-        if(x <= UINT64_SUFFIX(0x00FFFFFFFFFFFFFF))\r
-        {\r
-            n += 8U;\r
-            x <<= 8U;\r
-        }\r
-\r
-        if(x <= UINT64_SUFFIX(0x0FFFFFFFFFFFFFFF))\r
-        {\r
-            n += 4U;\r
-            x <<= 4U;\r
-        }\r
-\r
-        if(x <= UINT64_SUFFIX(0x3FFFFFFFFFFFFFFF))\r
-        {\r
-            n += 2U;\r
-            x <<= 2U;\r
-        }\r
-\r
-        if(x <= UINT64_SUFFIX(0x7FFFFFFFFFFFFFFF))\r
-        {\r
-            n += 1;\r
-        }\r
-    }\r
-\r
-    return n;\r
-}\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements routines for certain 64-bit math operations and simulated
+           floating point.
+
+    RedUint64DivMod32() and RedUint64DivMod64() are derived from code at
+    http://www.hackersdelight.org.  This web site states explicitly that "You
+    are free to use, copy, and distribute any of the code on this web site,
+    whether modified by you or not.  You need not give attribution."
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+static uint32_t nlz64(uint64_t ullValue);
+
+
+/** @brief Return a ratio value formatted as a floating point string accurate to
+           the specified number of decimal places.
+
+    The  function exists to provide floating point style output without using
+    any actual floating point types.
+
+    This function may scale the numbers down to avoid overflow at the high end.
+    Likewise, potential divide-by-zero errors are internally avoided.  Here are
+    some examples:
+
+    Dividend | Divisor | DecPlaces | Result
+    -------- | ------- | --------- | ------
+    12133    | 28545   | 2         | "0.42"
+    1539     | 506     | 2         | "3.04"
+
+    To get a number formatted as a percentage, take the take the portion of the
+    total (normally the smaller part), multiply it by 100, and pass it to this
+    function as the Dividend, pass the "total" value to this function as the
+    Divisor, and specify the desired number of decimal places.
+
+    For example, if you have a disk format overhead value of N blocks out of a
+    total of Y blocks on the disk, and you want to display the format overhead
+    as a percentage, you would use a function call
+    similar to:
+
+    ~~~{.c}
+    RedRatio(szBuffer, sizeof(szBuffer), N*100U, Y, 2U);
+    ~~~
+
+    If N=145, Y=4096, and decimal places is 2, the resulting output would be
+    "3.54".
+
+    The string returned will always be null-terminated, even if it means
+    stomping on the least significant decimal digit.
+
+    If either the dividend or divisor values are zero, the string "0.0" will be
+    returned, with the prescribed number of decimal places.
+
+    @note This function has "reasonable" limits which meet the needs of the
+          various supplemental utilities which use this function.  Extremely
+          large ratios, or using many decimal places may not function as
+          desired.
+
+    Parameters:
+    @param pBuffer      A pointer to the buffer in which to store the null
+                        terminated results.
+    @param ulBufferLen  The length of the output buffer.
+    @param ullDividend  The "total" value to divide.
+    @param ullDivisor   The portion of ullDividend for which to calculate the
+                        ratio (may be greater than ulDividend).
+    @param ulDecPlaces  The number of decimal places to use, from 0 to 9.
+
+    @return @p pBuffer.
+*/
+char *RedRatio(
+    char       *pBuffer,
+    uint32_t    ulBufferLen,
+    uint64_t    ullDividend,
+    uint64_t    ullDivisor,
+    uint32_t    ulDecPlaces)
+{
+    REDASSERT(pBuffer != NULL);
+    REDASSERT(ulBufferLen > 0U);
+    REDASSERT(ulDecPlaces <= 9U);   /* arbitrary */
+
+    if((ullDivisor > 0U) && (ullDividend > 0U))
+    {
+        uint32_t    ii;
+        uint32_t    ulFactor = 1U;
+        uint64_t    ullDecimal;
+        uint64_t    ullTemp;
+
+        for(ii = 1U; ii <= ulDecPlaces; ii++)
+        {
+            ulFactor *= 10U;
+        }
+
+        ullDecimal = RedMulDiv64(ullDividend, ulFactor, ullDivisor);
+
+        /*  Shouldn't really be calling this function in a situation where we
+            can overflow at this point...
+        */
+        REDASSERT(ullDecimal != UINT64_MAX);
+
+        if(ullDivisor <= ullDividend)
+        {
+            uint32_t ulDecimal;
+
+            (void)RedUint64DivMod32(ullDecimal, ulFactor, &ulDecimal);
+            ullDecimal = ulDecimal;
+        }
+
+        ullTemp = RedUint64DivMod64(ullDividend, ullDivisor, NULL);
+
+        if(ulDecPlaces > 0U)
+        {
+            RedSNPrintf(pBuffer, ulBufferLen, "%llu.%0*llu", (unsigned long long)ullTemp,
+                (unsigned)ulDecPlaces, (unsigned long long)ullDecimal);
+        }
+        else
+        {
+            RedSNPrintf(pBuffer, ulBufferLen, "%llu", (unsigned long long)ullTemp);
+        }
+    }
+    else
+    {
+        /*  If either the dividend or divisor is zero, then just output a "0.0"
+            string with the prescribed number of decimal places.
+        */
+        if(ulDecPlaces > 0U)
+        {
+            RedSNPrintf(pBuffer, ulBufferLen, "0.%0*u", (unsigned)ulDecPlaces, 0U);
+        }
+        else
+        {
+            RedStrNCpy(pBuffer, "0", ulBufferLen);
+        }
+    }
+
+    /*  Ensure the returned buffer is always null-terminated
+    */
+    pBuffer[ulBufferLen - 1U] = '\0';
+
+    return pBuffer;
+}
+
+
+/** @brief Multiply 64-bit and 32-bit numbers, and divide by a 64-bit number,
+           returning a 64-bit result.
+
+    @note This function may return an approximate value if multiplying
+          @p ullBase and @p ulMultplier results in a number larger than 64-bits
+          _and_ this cannot be avoided by scaling.
+
+    @param ullBase      The base 64-bit number number.
+    @param ulMultiplier The 32-bit number by which to multiply.
+    @param ullDivisor   The 64-bit number by which to divide.
+
+    @return The 64-bit unsigned integer result.  Always returns zero if either
+            @p ullBase or @p ulMultiplier are zero (regardless what
+            @p ullDivisor is).  Returns UINT64_MAX if an overflow condition
+            occurred, or if @p ullDivisor is zero.
+*/
+uint64_t RedMulDiv64(
+    uint64_t ullBase,
+    uint32_t ulMultiplier,
+    uint64_t ullDivisor)
+{
+    uint64_t ullTemp;
+
+    /*  Result would always be zero if either of these are zero.  Specifically
+        test this case before looking for a zero divisor.
+    */
+    if((ullBase == 0U) || (ulMultiplier == 0U))
+    {
+        return 0U;
+    }
+
+    if(ullDivisor == 0U)
+    {
+        return UINT64_MAX;
+    }
+
+    /*  Since we don't have the ability (yet) to use 128-bit numbers, we jump
+        through the following hoops (in order) to try to determine the proper
+        results without losing precision:
+
+        1) Shift the divisor and one of the multiplicands as many times as is
+           necessary to reduce the scale -- only if it can be done without
+           losing precision.
+        2) Divide one of the multiplicands by the divisor first, but only if it
+           divides evenly, preserving precision.
+        3) Same as #2, but try it for the other multiplicand.
+        4) Last ditch, divide the larger multiplicand by the divisor first, then
+           do the multiply.  This <WILL> lose precision.
+
+        These solutions are identified as CODE-PATHs #1-4 which are used to
+        identify the matching tests in dltmain.c.
+
+        Note that execution might partially include CODE-PATH #1 up until
+        shifting can no longer be done without losing precision.  In that case,
+        one of the three remaining options will be used.
+    */
+
+    ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);
+    while(ullBase > ullTemp)
+    {
+        uint64_t ullMod;
+        uint64_t ullBaseTemp;
+        uint64_t ullWideMultiplier;
+
+        /*  CODE-PATH #1
+        */
+        /*  So long as ulDivisor, and at least one of the other numbers, are
+            evenly divisible by 2, we can scale the numbers so the result does
+            not overflow the intermediate 64-bit value.
+        */
+        if((ullDivisor & 1U) == 0U)
+        {
+            if((ullBase & 1U) == 0U)
+            {
+                /*  CODE-PATH #1a
+                */
+                ullDivisor >>= 1U;
+                ullBase >>= 1U;
+                continue;
+            }
+
+            if(((ulMultiplier & 1U) == 0U) && ((ullTemp & UINT64_SUFFIX(0x8000000000000000)) == 0U))
+            {
+                /*  CODE-PATH #1b
+                */
+                ullDivisor >>= 1U;
+                ulMultiplier >>= 1U;
+                ullTemp <<= 1U;
+                continue;
+            }
+        }
+
+        /*  If we get to this point, the above method (#1) cannot be used
+            because not enough of the numbers are even long enough to scale the
+            operands down.  We'll see if either multiplicand is evenly divisble
+            by ulDivisor, and if so, do the divide first, then the multiply.
+            (Note that once we get to this point, we will never exercise the
+            while{} loop anymore.)
+        */
+
+        /*  CODE-PATH #2
+        */
+        ullBaseTemp = RedUint64DivMod64(ullBase, ullDivisor, &ullMod);
+        if(ullMod == 0U)
+        {
+            /*  Evenly divides, so check that we won't overflow, and finish up.
+            */
+            ullBase = ullBaseTemp;
+            if(ullBase > ullTemp)
+            {
+                return UINT64_MAX;
+            }
+            else
+            {
+                /*  We've validated that this will not overflow.
+                */
+                ullBase *= ulMultiplier;
+                return ullBase;
+            }
+        }
+
+        /*  CODE-PATH #3
+        */
+        ullWideMultiplier = RedUint64DivMod64(ulMultiplier, ullDivisor, &ullMod);
+        if(ullMod == 0U)
+        {
+            /*  Evenly divides, so check that we won't overflow, and finish up.
+            */
+
+            /*  Must recalculate ullTemp relative to ullBase
+            */
+            ullTemp = RedUint64DivMod64(UINT64_MAX, ullBase, NULL);
+            if(ullWideMultiplier > ullTemp)
+            {
+                return UINT64_MAX;
+            }
+            else
+            {
+                uint32_t ulNarrowMultiplier = (uint32_t)ullWideMultiplier;
+
+                /*  We've validated that this will not overflow.
+                */
+                ullBase *= ulNarrowMultiplier;
+                return ullBase;
+            }
+        }
+
+        /*  CODE-PATH #4
+
+            Neither of the multipliers is evenly divisible by the divisor, so
+            just punt and divide the larger number first, then do the final
+            multiply.
+
+            All the other attempts above would preserve precision -- this is the
+            only case where precision may be lost.
+        */
+
+        /*  If necessary reverse the ullBase and ulMultiplier operands so that
+            ullBase contains the larger of the two values.
+        */
+        if(ullBase < ulMultiplier)
+        {
+            uint32_t ulTemp = ulMultiplier;
+
+            ulMultiplier = (uint32_t)ullBase;
+            ullBase = ulTemp;
+        }
+
+        ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);
+        ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);
+        if(ullBase > ullTemp)
+        {
+            return UINT64_MAX;
+        }
+        else
+        {
+            ullBase *= ulMultiplier;
+            return ullBase;
+        }
+    }
+
+    /*  We only get to this point if either there was never any chance of
+        overflow, or if the pure shifting mechanism succeeded in reducing
+        the scale so overflow is not a problem.
+    */
+
+    ullBase *= ulMultiplier;
+    ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);
+
+    return ullBase;
+}
+
+
+/** @brief Divide a 64-bit value by a 32-bit value, returning the quotient and
+           the remainder.
+
+    Essentially this function does the following:
+
+    ~~~{.c}
+    if(pulRemainder != NULL)
+    {
+        *pulRemainder = (uint32_t)(ullDividend % ulDivisor);
+    }
+    return ullDividend / ulDivisor;
+    ~~~
+
+    However, it does so without ever actually dividing/modulating a 64-bit
+    value, since such operations are not allowed in all environments.
+
+    @param ullDividend  The value to divide.
+    @param ulDivisor    The value to divide by.
+    @param pulRemainder Populated with the remainder; may be NULL.
+
+    @return The quotient (result of the division).
+*/
+uint64_t RedUint64DivMod32(
+    uint64_t    ullDividend,
+    uint32_t    ulDivisor,
+    uint32_t   *pulRemainder)
+{
+    uint64_t    ullQuotient;
+    uint32_t    ulResultRemainder;
+
+    /*  Check for divide by zero.
+    */
+    if(ulDivisor == 0U)
+    {
+        REDERROR();
+
+        /*  Nonsense value if no asserts.
+        */
+        ullQuotient = UINT64_SUFFIX(0xFFFFFFFFFFFFFBAD);
+        ulResultRemainder = 0xFFFFFBADU;
+    }
+    else if(ullDividend <= UINT32_MAX)
+    {
+        uint32_t ulDividend = (uint32_t)ullDividend;
+
+        ullQuotient = ulDividend / ulDivisor;
+        ulResultRemainder = ulDividend % ulDivisor;
+    }
+    else
+    {
+        uint32_t    ulResultHi;
+        uint32_t    ulResultLo;
+        uint32_t    ulRemainder;
+        uint8_t     bIndex;
+        uint32_t    ulThisDivision;
+        uint32_t    ulMask;
+        uint8_t     ucNextValue;
+        uint32_t    ulInterimHi, ulInterimLo;
+        uint32_t    ulLowDword = (uint32_t)ullDividend;
+        uint32_t    ulHighDword = (uint32_t)(ullDividend >> 32U);
+
+        /*  Compute the high part and get the remainder
+        */
+        ulResultHi = ulHighDword / ulDivisor;
+        ulResultLo = 0U;
+        ulRemainder = ulHighDword % ulDivisor;
+
+        /*  Compute the low part
+        */
+        ulMask = 0xFF000000U;
+        for(bIndex = 0U; bIndex < sizeof(uint32_t); bIndex++)
+        {
+            ucNextValue = (uint8_t)((ulLowDword & ulMask) >> ((sizeof(uint32_t) - 1U - bIndex) * 8U));
+            ulInterimHi = ulRemainder >> 24U;
+            ulInterimLo = (ulRemainder << 8U) | ucNextValue;
+            ulThisDivision = 0U;
+            while(ulInterimHi != 0U)
+            {
+                uint64_t ullInterim = ((uint64_t)ulInterimHi << 32U) + ulInterimLo;
+
+                ullInterim -= ulDivisor;
+                ulThisDivision++;
+
+                ulInterimHi = (uint32_t)(ullInterim >> 32U);
+                ulInterimLo = (uint32_t)ullInterim;
+            }
+            ulThisDivision += ulInterimLo / ulDivisor;
+            ulRemainder = ulInterimLo % ulDivisor;
+            ulResultLo <<= 8U;
+            ulResultLo += ulThisDivision;
+            ulMask >>= 8U;
+        }
+
+        ullQuotient = ((uint64_t)ulResultHi << 32U) + ulResultLo;
+        ulResultRemainder = (uint32_t)(ullDividend - (ullQuotient * ulDivisor));
+    }
+
+    if(pulRemainder != NULL)
+    {
+        *pulRemainder = ulResultRemainder;
+    }
+
+    return ullQuotient;
+}
+
+
+/** @brief Divide a 64-bit value by a 64-bit value, returning the quotient and
+           the remainder.
+
+    Essentially this function does the following:
+
+    ~~~{.c}
+    if(pullRemainder != NULL)
+    {
+        *pullRemainder = ullDividend % ullDivisor;
+    }
+    return ullDividend / ullDivisor;
+    ~~~
+
+    However, it does so without ever actually dividing/modulating a 64-bit
+    value, since such operations are not allowed in all environments.
+
+    @param ullDividend   The value to divide.
+    @param ullDivisor    The value to divide by.
+    @param pullRemainder Populated with the remainder; may be NULL.
+
+    @return The quotient (result of the division).
+*/
+uint64_t RedUint64DivMod64(
+    uint64_t    ullDividend,
+    uint64_t    ullDivisor,
+    uint64_t   *pullRemainder)
+{
+    /*  The variables u0, u1, etc. take on only 32-bit values, but they are
+        declared uint64_t to avoid some compiler warning messages and to avoid
+        some unnecessary EXTRs that the compiler would put in, to convert
+        uint64_ts to ints.
+    */
+    uint64_t    u0;
+    uint64_t    u1;
+    uint64_t    q0;
+    uint64_t    q1;
+    uint64_t    ullQuotient;
+
+    /*  First the procedure takes care of the case in which the divisor is a
+        32-bit quantity.  There are two subcases: (1) If the left half of the
+        dividend is less than the divisor, one execution of RedUint64DivMod32()
+        is all that is required (overflow is not possible). (2) Otherwise it
+        does two divisions, using the grade school method.
+    */
+
+    if((ullDivisor >> 32U) == 0U)
+    {
+        if((ullDividend >> 32U) < ullDivisor)
+        {
+            /*  If ullDividend/ullDivisor cannot overflow, just do one division.
+            */
+            ullQuotient = RedUint64DivMod32(ullDividend, (uint32_t)ullDivisor, NULL);
+        }
+        else
+        {
+            uint32_t k;
+
+            /*  If ullDividend/ullDivisor would overflow:
+            */
+
+            /*  Break ullDividend up into two halves.
+            */
+            u1 = ullDividend >> 32U;
+            u0 = ullDividend & 0xFFFFFFFFU;
+
+            /*  First quotient digit and first remainder.
+            */
+            q1 = RedUint64DivMod32(u1, (uint32_t)ullDivisor, &k);
+
+            /*  2nd quot. digit.
+            */
+            q0 = RedUint64DivMod32(((uint64_t)k << 32U) + u0, (uint32_t)ullDivisor, NULL);
+
+            ullQuotient = (q1 << 32U) + q0;
+        }
+    }
+    else
+    {
+        uint64_t n;
+        uint64_t v1;
+
+        n = nlz64(ullDivisor);          /* 0 <= n <= 31. */
+        v1 = (ullDivisor << n) >> 32U;  /* Normalize the divisor so its MSB is 1. */
+        u1 = ullDividend >> 1U;         /* To ensure no overflow. */
+
+        /*  Get quotient from divide unsigned insn.
+        */
+        q1 = RedUint64DivMod32(u1, (uint32_t)v1, NULL);
+
+        q0 = (q1 << n) >> 31U;  /* Undo normalization and division of ullDividend by 2. */
+
+        /*  Make q0 correct or too small by 1.
+        */
+        if(q0 != 0U)
+        {
+            q0--;
+        }
+
+        if((ullDividend - (q0 * ullDivisor)) >= ullDivisor)
+        {
+            q0++;   /* Now q0 is correct. */
+        }
+
+        ullQuotient = q0;
+    }
+
+    if(pullRemainder != NULL)
+    {
+        *pullRemainder = ullDividend - (ullQuotient * ullDivisor);
+    }
+
+    return ullQuotient;
+}
+
+
+/** @brief Compute the number of leading zeroes in a 64-bit value.
+
+    @param ullValue The value for which to compute the NLZ.
+
+    @return The number of leading zeroes in @p ullValue.
+*/
+static uint32_t nlz64(
+    uint64_t    ullValue)
+{
+    uint32_t    n;
+
+    if(ullValue == 0U)
+    {
+        n = 64U;
+    }
+    else
+    {
+        uint64_t x = ullValue;
+
+        n = 0U;
+
+        if(x <= UINT64_SUFFIX(0x00000000FFFFFFFF))
+        {
+            n += 32U;
+            x <<= 32U;
+        }
+
+        if(x <= UINT64_SUFFIX(0x0000FFFFFFFFFFFF))
+        {
+            n += 16U;
+            x <<= 16U;
+        }
+
+        if(x <= UINT64_SUFFIX(0x00FFFFFFFFFFFFFF))
+        {
+            n += 8U;
+            x <<= 8U;
+        }
+
+        if(x <= UINT64_SUFFIX(0x0FFFFFFFFFFFFFFF))
+        {
+            n += 4U;
+            x <<= 4U;
+        }
+
+        if(x <= UINT64_SUFFIX(0x3FFFFFFFFFFFFFFF))
+        {
+            n += 2U;
+            x <<= 2U;
+        }
+
+        if(x <= UINT64_SUFFIX(0x7FFFFFFFFFFFFFFF))
+        {
+            n += 1;
+        }
+    }
+
+    return n;
+}
+
index 146b1fc84fc37523c2c0e1bd0c67b7d3f4e0f9e9..6df0cf4e6fb73c7bc5a6b78a69dd53d50c4a1d21 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements functions for printing.\r
-\r
-    These functions are intended to be used in portable test code, which cannot\r
-    assume the standard I/O functions will be available.  Similar to their ANSI\r
-    C counterparts, these functions allow formatting text strings and (if the\r
-    configuration allows it) outputing formatted text.  The latter ability\r
-    relies on the RedOsOutputString() OS service function.\r
-\r
-    Do *not* use these functions in code which can safely assume the standard\r
-    I/O functions are available (e.g., in host tools code).\r
-\r
-    Do *not* use these functions from within the file system driver.  These\r
-    functions use variable arguments and thus are not MISRA-C:2012 compliant.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-#include <limits.h>\r
-#include <stdarg.h>\r
-\r
-\r
-/** @brief Maximum number of bytes of output supported by RedPrintf().\r
-\r
-    Typically only Datalight code uses these functions, and that could should be\r
-    written to respect this limit, so it should not normally be necessary to\r
-    adjust this value.\r
-*/\r
-#define OUTPUT_BUFFER_SIZE 256U\r
-\r
-\r
-typedef enum\r
-{\r
-    PRFMT_UNKNOWN = 0,\r
-    PRFMT_CHAR,\r
-    PRFMT_ANSISTRING,\r
-    PRFMT_SIGNED8BIT,\r
-    PRFMT_UNSIGNED8BIT,\r
-    PRFMT_SIGNED16BIT,\r
-    PRFMT_UNSIGNED16BIT,\r
-    PRFMT_SIGNED32BIT,\r
-    PRFMT_UNSIGNED32BIT,\r
-    PRFMT_SIGNED64BIT,\r
-    PRFMT_UNSIGNED64BIT,\r
-    PRFMT_HEX8BIT,\r
-    PRFMT_HEX16BIT,\r
-    PRFMT_HEX32BIT,\r
-    PRFMT_HEX64BIT,\r
-    PRFMT_POINTER,\r
-    PRFMT_DOUBLEPERCENT\r
-} PRINTTYPE;\r
-\r
-typedef struct\r
-{\r
-    PRINTTYPE       type;               /* The PRFMT_* type found */\r
-    uint32_t        ulSpecifierIdx;     /* Returns a pointer to the % sign */\r
-    uint32_t        ulFillLen;\r
-    char            cFillChar;\r
-    bool            fLeftJustified;\r
-    bool            fHasIllegalType;    /* TRUE if an illegal sequence was skipped over */\r
-    bool            fHasVarWidth;\r
-} PRINTFORMAT;\r
-\r
-\r
-/*  Our output handlers are written for standard fixed width data types.  Map\r
-    the standard ANSI C data types onto our handlers.  Currently this code has\r
-    the following requirements:\r
-\r
-    1) shorts must be either 16 or 32 bits\r
-    2) ints must be either 16 or 32 bits\r
-    3) longs must be between 32 or 64 bits\r
-    4) long longs must be 64 bits\r
-*/\r
-#if (USHRT_MAX == 0xFFFFU)\r
-  #define MAPSHORT          PRFMT_SIGNED16BIT\r
-  #define MAPUSHORT         PRFMT_UNSIGNED16BIT\r
-  #define MAPHEXUSHORT      PRFMT_HEX16BIT\r
-#elif (USHRT_MAX == 0xFFFFFFFFU)\r
-  #define MAPSHORT          PRFMT_SIGNED32BIT\r
-  #define MAPUSHORT         PRFMT_UNSIGNED32BIT\r
-  #define MAPHEXUSHORT      PRFMT_HEX32BIT\r
-#else\r
-  #error "The 'short' data type does not have a 16 or 32-bit width"\r
-#endif\r
-\r
-#if (UINT_MAX == 0xFFFFU)\r
-  #define MAPINT            PRFMT_SIGNED16BIT\r
-  #define MAPUINT           PRFMT_UNSIGNED16BIT\r
-  #define MAPHEXUINT        PRFMT_HEX16BIT\r
-#elif (UINT_MAX == 0xFFFFFFFFU)\r
-  #define MAPINT            PRFMT_SIGNED32BIT\r
-  #define MAPUINT           PRFMT_UNSIGNED32BIT\r
-  #define MAPHEXUINT        PRFMT_HEX32BIT\r
-#else\r
-  #error "The 'int' data type does not have a 16 or 32-bit width"\r
-#endif\r
-\r
-#if (ULONG_MAX == 0xFFFFFFFFU)\r
-  #define MAPLONG           PRFMT_SIGNED32BIT\r
-  #define MAPULONG          PRFMT_UNSIGNED32BIT\r
-  #define MAPHEXULONG       PRFMT_HEX32BIT\r
-#elif (ULONG_MAX <= UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))\r
-  /*  We've run into unusual environments where "longs" are 40-bits wide.\r
-      In this event, map them to 64-bit types so no data is lost.\r
-  */\r
-  #define MAPLONG         PRFMT_SIGNED64BIT\r
-  #define MAPULONG        PRFMT_UNSIGNED64BIT\r
-  #define MAPHEXULONG     PRFMT_HEX64BIT\r
-#else\r
-  #error "The 'long' data type is not between 32 and 64 bits wide"\r
-#endif\r
-\r
-#if defined(ULLONG_MAX) && (ULLONG_MAX != UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))\r
-  #error "The 'long long' data type is not 64 bits wide"\r
-#else\r
-  #define MAPLONGLONG       PRFMT_SIGNED64BIT\r
-  #define MAPULONGLONG      PRFMT_UNSIGNED64BIT\r
-  #define MAPHEXULONGLONG   PRFMT_HEX64BIT\r
-#endif\r
-\r
-\r
-static uint32_t ProcessFormatSegment(char *pcBuffer, uint32_t ulBufferLen, const char *pszFormat, PRINTFORMAT *pFormat, uint32_t *pulSpecifierLen);\r
-static uint32_t ParseFormatSpecifier(char const *pszFomat, PRINTFORMAT *pFormatType);\r
-static PRINTTYPE ParseFormatType(const char *pszFormat, uint32_t *pulTypeLen);\r
-static uint32_t LtoA(char *pcBuffer, uint32_t ulBufferLen, int32_t lNum, uint32_t ulFillLen, char cFill);\r
-static uint32_t LLtoA(char *pcBuffer, uint32_t ulBufferLen, int64_t llNum, uint32_t ulFillLen, char cFill);\r
-static uint32_t ULtoA(char *pcBuffer, uint32_t ulBufferLen, uint32_t ulNum, bool fHex, uint32_t ulFillLen, char cFill);\r
-static uint32_t ULLtoA(char *pcBuffer, uint32_t ulBufferLen, uint64_t ullNum, bool fHex, uint32_t ulFillLen, char cFill);\r
-static uint32_t FinishToA(const char *pcDigits, uint32_t ulDigits, char *pcOutBuffer, uint32_t ulBufferLen, uint32_t ulFillLen, char cFill);\r
-\r
-\r
-/*  Digits for the *LtoA() routines.\r
-*/\r
-static const char gacDigits[] = "0123456789ABCDEF";\r
-\r
-\r
-#if REDCONF_OUTPUT == 1\r
-/** @brief Print formatted data with a variable length argument list.\r
-\r
-    This function provides a subset of the ANSI C printf() functionality with\r
-    several extensions to support fixed size data types.\r
-\r
-    See RedVSNPrintf() for the list of supported types.\r
-\r
-    @param pszFormat    A pointer to the null-terminated format string.\r
-    @param ...          The variable length argument list.\r
-*/\r
-void RedPrintf(\r
-    const char *pszFormat,\r
-    ...)\r
-{\r
-    va_list     arglist;\r
-\r
-    va_start(arglist, pszFormat);\r
-\r
-    RedVPrintf(pszFormat, arglist);\r
-\r
-    va_end(arglist);\r
-}\r
-\r
-\r
-/** @brief Print formatted data using a pointer to a variable length argument\r
-           list.\r
-\r
-    This function provides a subset of the ANSI C vprintf() functionality.\r
-\r
-    See RedVSNPrintf() for the list of supported types.\r
-\r
-    This function accommodates a maximum output length of #OUTPUT_BUFFER_SIZE.\r
-    If this function must truncate the output, and the original string was\r
-    \n terminated, the truncated output will be \n terminated as well.\r
-\r
-    @param pszFormat    A pointer to the null-terminated format string.\r
-    @param arglist      The variable length argument list.\r
-*/\r
-void RedVPrintf(\r
-    const char *pszFormat,\r
-    va_list     arglist)\r
-{\r
-    char        achBuffer[OUTPUT_BUFFER_SIZE];\r
-\r
-    if(RedVSNPrintf(achBuffer, sizeof(achBuffer), pszFormat, arglist) == -1)\r
-    {\r
-        /*  Ensture the buffer is null terminated.\r
-        */\r
-        achBuffer[sizeof(achBuffer) - 1U] = '\0';\r
-\r
-        /*  If the original string was \n terminated and the new one is not, due to\r
-            truncation, stuff a \n into the new one.\r
-        */\r
-        if(pszFormat[RedStrLen(pszFormat) - 1U] == '\n')\r
-        {\r
-            achBuffer[sizeof(achBuffer) - 2U] = '\n';\r
-        }\r
-    }\r
-\r
-    RedOsOutputString(achBuffer);\r
-}\r
-#endif /* #if REDCONF_OUTPUT == 1 */\r
-\r
-\r
-/** @brief Format arguments into a string using a subset of the ANSI C\r
-           vsprintf() functionality.\r
-\r
-    This function is modeled after the Microsoft _snprint() extension to the\r
-    ANSI C sprintf() function, and allows a buffer length to be specified so\r
-    that overflow is avoided.\r
-\r
-    See RedVSNPrintf() for the list of supported types.\r
-\r
-    @param pcBuffer     A pointer to the output buffer\r
-    @param ulBufferLen  The output buffer length\r
-    @param pszFormat    A pointer to the null terminated format string\r
-    @param ...          Variable argument list\r
-\r
-    @return The length output, or -1 if the buffer filled up.  If -1 is\r
-            returned, the output buffer may not be null-terminated.\r
-*/\r
-int32_t RedSNPrintf(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    const char *pszFormat,\r
-    ...)\r
-{\r
-    int32_t     iLen;\r
-    va_list     arglist;\r
-\r
-    va_start(arglist, pszFormat);\r
-\r
-    iLen = RedVSNPrintf(pcBuffer, ulBufferLen, pszFormat, arglist);\r
-\r
-    va_end(arglist);\r
-\r
-    return iLen;\r
-}\r
-\r
-\r
-/** @brief Format arguments into a string using a subset of the ANSI C\r
-           vsprintf() functionality.\r
-\r
-    This function is modeled after the Microsoft _vsnprint() extension to the\r
-    ANSI C vsprintf() function, and requires a buffer length to be specified so\r
-    that overflow is avoided.\r
-\r
-    The following ANSI C standard formatting codes are supported:\r
-\r
-    | Code | Meaning                            |\r
-    | ---- | ---------------------------------- |\r
-    | %c   | Format a character                 |\r
-    | %s   | Format a null-terminated C string  |\r
-    | %hd  | Format a signed short              |\r
-    | %hu  | Format an unsigned short           |\r
-    | %d   | Format a signed integer            |\r
-    | %u   | Format an unsigned integer         |\r
-    | %ld  | Format a signed long               |\r
-    | %lu  | Format an unsigned long            |\r
-    | %lld | Format a signed long long          |\r
-    | %llu | Format an unsigned long long       |\r
-    | %hx  | Format a short in hex              |\r
-    | %x   | Format an integer in hex           |\r
-    | %lx  | Format a long in hex               |\r
-    | %llx | Format a long long in hex          |\r
-    | %p   | Format a pointer (hex value)       |\r
-\r
-    @note All formatting codes are case-sensitive.\r
-\r
-    Fill characters and field widths are supported per the ANSI standard, as is\r
-    left justification with the '-' character.\r
-\r
-    The only supported fill characters are '0', ' ', and '_'.\r
-\r
-    '*' is supported to specify variable length field widths.\r
-\r
-    Hexidecimal numbers are always displayed in upper case.  Formatting codes\r
-    which specifically request upper case (e.g., "%lX") are not supported.\r
-\r
-    Unsupported behaviors:\r
-      - Precision is not supported.\r
-      - Floating point is not supported.\r
-\r
-    Errata:\r
-    - There is a subtle difference in the return value for this function versus\r
-      the Microsoft implementation.  In the Microsoft version, if the buffer\r
-      exactly fills up, but there is no room for a null-terminator, the return\r
-      value will be the length of the buffer.  In this code, -1 will be returned\r
-      when this happens.\r
-    - When using left justified strings, the only supported fill character is a\r
-      space, regardless of what may be specified.  It is not clear if this is\r
-      ANSI standard or just the way the Microsoft function works, but we emulate\r
-      the Microsoft behavior.\r
-\r
-    @param pcBuffer     A pointer to the output buffer.\r
-    @param ulBufferLen  The output buffer length.\r
-    @param pszFormat    A pointer to the null terminated ANSI format string.\r
-    @param arglist      Variable argument list.\r
-\r
-    @return  The length output, or -1 if the buffer filled up.  If -1 is\r
-             returned, the output buffer may not be null-terminated.\r
-*/\r
-int32_t RedVSNPrintf(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    const char *pszFormat,\r
-    va_list     arglist)\r
-{\r
-    uint32_t    ulBufIdx = 0U;\r
-    uint32_t    ulFmtIdx = 0U;\r
-    int32_t     iLen;\r
-\r
-    while((pszFormat[ulFmtIdx] != '\0') && (ulBufIdx < ulBufferLen))\r
-    {\r
-        PRINTFORMAT fmt;\r
-        uint32_t    ulSpecifierLen;\r
-        uint32_t    ulWidth;\r
-\r
-        /*  Process the next segment of the format string, outputting\r
-            any non-format specifiers, as output buffer space allows,\r
-            and return information about the next format specifier.\r
-        */\r
-        ulWidth = ProcessFormatSegment(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, &pszFormat[ulFmtIdx], &fmt, &ulSpecifierLen);\r
-        if(ulWidth)\r
-        {\r
-            REDASSERT(ulWidth <= (ulBufferLen - ulBufIdx));\r
-\r
-            ulBufIdx += ulWidth;\r
-        }\r
-\r
-        /*  If no specifier was found, or if the output buffer is\r
-            full, we're done -- get out.\r
-        */\r
-        if((ulSpecifierLen == 0U) || (ulBufIdx == ulBufferLen))\r
-        {\r
-            break;\r
-        }\r
-\r
-        /*  Otherwise, the math should add up for these things...\r
-        */\r
-        REDASSERT(&pszFormat[fmt.ulSpecifierIdx] == &pszFormat[ulWidth]);\r
-\r
-        /*  Point past the specifier, to the next piece of the format string.\r
-        */\r
-        ulFmtIdx = ulFmtIdx + fmt.ulSpecifierIdx + ulSpecifierLen;\r
-\r
-        if(fmt.fHasVarWidth)\r
-        {\r
-            int iFillLen = va_arg(arglist, int);\r
-\r
-            if(iFillLen >= 0)\r
-            {\r
-                fmt.ulFillLen = (uint32_t)iFillLen;\r
-            }\r
-            else\r
-            {\r
-                /*  Bogus fill length.  Ignore.\r
-                */\r
-                fmt.ulFillLen = 0U;\r
-            }\r
-        }\r
-\r
-        switch(fmt.type)\r
-        {\r
-            case PRFMT_DOUBLEPERCENT:\r
-            {\r
-                /*  Nothing to do.  A single percent has already been output,\r
-                    and we just finished skipping past the second percent.\r
-                */\r
-                break;\r
-            }\r
-\r
-            /*----------------->  Small int handling  <------------------\r
-             *\r
-             *  Values smaller than "int" will be promoted to "int" by\r
-             *  the compiler, so we must retrieve them using "int" when\r
-             *  calling va_arg().  Once we've done that, we immediately\r
-             *  put the value into the desired data type.\r
-             *---------------------------------------------------------*/\r
-\r
-            case PRFMT_CHAR:\r
-            {\r
-                pcBuffer[ulBufIdx] = (char)va_arg(arglist, int);\r
-                ulBufIdx++;\r
-                break;\r
-            }\r
-            case PRFMT_SIGNED8BIT:\r
-            {\r
-                int8_t num = (int8_t)va_arg(arglist, int);\r
-\r
-                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_UNSIGNED8BIT:\r
-            {\r
-                uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, false, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_HEX8BIT:\r
-            {\r
-                uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, true, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_SIGNED16BIT:\r
-            {\r
-                int16_t num = (int16_t)va_arg(arglist, int);\r
-\r
-                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_UNSIGNED16BIT:\r
-            {\r
-                uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, false, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_HEX16BIT:\r
-            {\r
-                uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, true, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_SIGNED32BIT:\r
-            {\r
-                int32_t lNum = va_arg(arglist, int32_t);\r
-\r
-                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, lNum, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_UNSIGNED32BIT:\r
-            {\r
-                uint32_t ulNum = va_arg(arglist, uint32_t);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, false, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_HEX32BIT:\r
-            {\r
-                uint32_t ulNum = va_arg(arglist, uint32_t);\r
-\r
-                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, true, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_SIGNED64BIT:\r
-            {\r
-                int64_t llNum = va_arg(arglist, int64_t);\r
-\r
-                ulBufIdx += LLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, llNum, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_UNSIGNED64BIT:\r
-            {\r
-                uint64_t ullNum = va_arg(arglist, uint64_t);\r
-\r
-                ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, false, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_HEX64BIT:\r
-            {\r
-                uint64_t ullNum = va_arg(arglist, uint64_t);\r
-\r
-                ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, true, fmt.ulFillLen, fmt.cFillChar);\r
-                break;\r
-            }\r
-            case PRFMT_POINTER:\r
-            {\r
-                const void *ptr = va_arg(arglist, const void *);\r
-\r
-                /*  Assert our assumption.\r
-                */\r
-                REDASSERT(sizeof(void *) <= 8U);\r
-\r
-                /*  Format as either a 64-bit or a 32-bit value.\r
-                */\r
-                if(sizeof(void *) > 4U)\r
-                {\r
-                    /*  Attempt to quiet warnings.\r
-                    */\r
-                    uintptr_t   ptrval = (uintptr_t)ptr;\r
-                    uint64_t    ullPtrVal = (uint64_t)ptrval;\r
-\r
-                    ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullPtrVal, true, fmt.ulFillLen, fmt.cFillChar);\r
-                }\r
-                else\r
-                {\r
-                    /*  Attempt to quiet warnings.\r
-                    */\r
-                    uintptr_t   ptrval = (uintptr_t)ptr;\r
-                    uint32_t    ulPtrVal = (uint32_t)ptrval;\r
-\r
-                    ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulPtrVal, true, fmt.ulFillLen, fmt.cFillChar);\r
-                }\r
-\r
-                break;\r
-            }\r
-            case PRFMT_ANSISTRING:\r
-            {\r
-                const char *pszArg = va_arg(arglist, const char *);\r
-                uint32_t    ulArgIdx = 0U;\r
-\r
-                if(pszArg == NULL)\r
-                {\r
-                    pszArg = "null";\r
-                }\r
-\r
-                if(fmt.ulFillLen > 0U)\r
-                {\r
-                    if(!fmt.fLeftJustified)\r
-                    {\r
-                        uint32_t ulLen = RedStrLen(pszArg);\r
-\r
-                        /*  So long as we are not left justifying, fill as many\r
-                            characters as is necessary to make the string right\r
-                            justified.\r
-                        */\r
-                        while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > ulLen))\r
-                        {\r
-                            pcBuffer[ulBufIdx] = fmt.cFillChar;\r
-                            ulBufIdx++;\r
-                            fmt.ulFillLen--;\r
-                        }\r
-                    }\r
-\r
-                    /*  Move as many characters as we have space for into the\r
-                        output buffer.\r
-                    */\r
-                    while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))\r
-                    {\r
-                        pcBuffer[ulBufIdx] = pszArg[ulArgIdx];\r
-                        ulBufIdx++;\r
-                        ulArgIdx++;\r
-                        if(fmt.ulFillLen > 0U)\r
-                        {\r
-                            fmt.ulFillLen--;\r
-                        }\r
-                    }\r
-\r
-                    /*  If there is any space left to fill, do it (the string\r
-                        must have been left justified).\r
-                    */\r
-                    while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > 0U))\r
-                    {\r
-                        /*  This is NOT a typo -- when using left justified\r
-                            strings, spaces are the only allowed fill character.\r
-                            See the errata.\r
-                        */\r
-                        pcBuffer[ulBufIdx] = ' ';\r
-                        ulBufIdx++;\r
-                        fmt.ulFillLen--;\r
-                    }\r
-                }\r
-                else\r
-                {\r
-                    /*  No fill characters, just move up to as many\r
-                        characters as we have space for in the output\r
-                        buffer.\r
-                    */\r
-                    while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))\r
-                    {\r
-                        pcBuffer[ulBufIdx] = pszArg[ulArgIdx];\r
-                        ulBufIdx++;\r
-                        ulArgIdx++;\r
-                    }\r
-                }\r
-                break;\r
-            }\r
-            default:\r
-            {\r
-                REDERROR();\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    /*  If there is space, tack on a null and return the output length\r
-        processed, not including the null.\r
-    */\r
-    if(ulBufIdx < ulBufferLen)\r
-    {\r
-        pcBuffer[ulBufIdx] = '\0';\r
-        iLen = (int32_t)ulBufIdx;\r
-    }\r
-    else\r
-    {\r
-        /*  Not enough space, just return -1, with no null termination\r
-        */\r
-        iLen = -1;\r
-    }\r
-\r
-    return iLen;\r
-}\r
-\r
-\r
-/** @brief  Process the next segment of the format string, outputting any\r
-            non-format specifiers, as output buffer space allows, and return\r
-            information about the next format specifier.\r
-\r
-    @note   If the returned value is the same as the supplied @p ulBufferLen,\r
-            the output buffer will not be null-terminated.  In all other cases,\r
-            the result will be null-terminated.  The returned length will never\r
-            include the null in the count.\r
-\r
-    @param pcBuffer         The output buffer.\r
-    @param ulBufferLen      The output buffer length.\r
-    @param pszFormat        The format string to process.\r
-    @param pFormat          The PRINTFORMAT structure to fill.\r
-    @param pulSpecifierLen  Returns the length of any format specifier string,\r
-                            or zero if no specifier was found.\r
-\r
-    @return The count of characters from pszFormatt which were processed and\r
-            copied to pcBuffer.\r
-        - If zero is returned and *pulSpecifierLen is non-zero, then\r
-          a format specifier string was found at the start of pszFmt.\r
-        - If non-zero is returned and *pulSpecifierLen is zero, then\r
-          no format specifier string was found, and the entire pszFmt\r
-          string was copied to pBuffer (or as much as will fit).\r
-*/\r
-static uint32_t ProcessFormatSegment(\r
-    char           *pcBuffer,\r
-    uint32_t        ulBufferLen,\r
-    const char     *pszFormat,\r
-    PRINTFORMAT    *pFormat,\r
-    uint32_t       *pulSpecifierLen)\r
-{\r
-    uint32_t        ulWidth = 0U;\r
-\r
-    /*  Find the next format specifier string, and information about it.\r
-    */\r
-    *pulSpecifierLen = ParseFormatSpecifier(pszFormat, pFormat);\r
-\r
-    if(*pulSpecifierLen == 0U)\r
-    {\r
-        /*  If no specifier was found at all, then simply output the full length\r
-            of the string, or as much as will fit.\r
-         */\r
-        ulWidth = REDMIN(ulBufferLen, RedStrLen(pszFormat));\r
-\r
-        RedMemCpy(pcBuffer, pszFormat, ulWidth);\r
-    }\r
-    else\r
-    {\r
-        /*  If we encountered a double percent, skip past one of them so it is\r
-            copied into the output buffer.\r
-        */\r
-        if(pFormat->type == PRFMT_DOUBLEPERCENT)\r
-        {\r
-            pFormat->ulSpecifierIdx++;\r
-\r
-            /*  A double percent specifier always has a length of two.  Since\r
-                we're processing one of those percent signs, reduce the length\r
-                to one.  Assert it so.\r
-            */\r
-            REDASSERT(*pulSpecifierLen == 2U);\r
-\r
-            (*pulSpecifierLen)--;\r
-        }\r
-\r
-        /*  So long as the specifier is not the very first thing in the format\r
-            string...\r
-        */\r
-        if(pFormat->ulSpecifierIdx != 0U)\r
-        {\r
-            /*  A specifier was found, but there is other data preceding it.\r
-                Copy as much as allowed to the output buffer.\r
-            */\r
-            ulWidth = REDMIN(ulBufferLen, pFormat->ulSpecifierIdx);\r
-\r
-            RedMemCpy(pcBuffer, pszFormat, ulWidth);\r
-        }\r
-    }\r
-\r
-    /*  If there is room in the output buffer, null-terminate whatever is there.\r
-        But note that the returned length never includes the null.\r
-    */\r
-    if(ulWidth < ulBufferLen)\r
-    {\r
-        pcBuffer[ulWidth] = 0U;\r
-    }\r
-\r
-    return ulWidth;\r
-}\r
-\r
-\r
-/** @brief Parse the specified format string for a valid RedVSNPrintf() format\r
-           sequence, and return information about it.\r
-\r
-    @param pszFormat    The format string to process.\r
-    @param pFormatType  The PRINTFORMAT structure to fill.  The data is only\r
-                        valid if a non-zero length is returned.\r
-\r
-    @return The length of the full format specifier string, starting at\r
-            pFormat->ulSpecifierIdx.  Returns zero if a valid specifier was\r
-            not found.\r
-*/\r
-static uint32_t ParseFormatSpecifier(\r
-    char const     *pszFomat,\r
-    PRINTFORMAT    *pFormatType)\r
-{\r
-    bool            fContainsIllegalSequence = false;\r
-    uint32_t        ulLen = 0U;\r
-    uint32_t        ulIdx = 0U;\r
-\r
-    while(pszFomat[ulIdx] != '\0')\r
-    {\r
-        uint32_t    ulTypeLen;\r
-\r
-        /*  general output\r
-        */\r
-        if(pszFomat[ulIdx] != '%')\r
-        {\r
-            ulIdx++;\r
-        }\r
-        else\r
-        {\r
-            RedMemSet(pFormatType, 0U, sizeof(*pFormatType));\r
-\r
-            /*  Record the location of the start of the format sequence\r
-            */\r
-            pFormatType->ulSpecifierIdx = ulIdx;\r
-            ulIdx++;\r
-\r
-            if(pszFomat[ulIdx] == '-')\r
-            {\r
-                pFormatType->fLeftJustified = true;\r
-                ulIdx++;\r
-            }\r
-\r
-            if((pszFomat[ulIdx] == '0') || (pszFomat[ulIdx] == '_'))\r
-            {\r
-                pFormatType->cFillChar = pszFomat[ulIdx];\r
-                ulIdx++;\r
-            }\r
-            else\r
-            {\r
-                pFormatType->cFillChar = ' ';\r
-            }\r
-\r
-            if(pszFomat[ulIdx] == '*')\r
-            {\r
-                pFormatType->fHasVarWidth = true;\r
-                ulIdx++;\r
-            }\r
-            else if(ISDIGIT(pszFomat[ulIdx]))\r
-            {\r
-                pFormatType->ulFillLen = (uint32_t)RedAtoI(&pszFomat[ulIdx]);\r
-                while(ISDIGIT(pszFomat[ulIdx]))\r
-                {\r
-                    ulIdx++;\r
-                }\r
-            }\r
-            else\r
-            {\r
-                /*  No fill length.\r
-                */\r
-            }\r
-\r
-            pFormatType->type = ParseFormatType(&pszFomat[ulIdx], &ulTypeLen);\r
-            if(pFormatType->type != PRFMT_UNKNOWN)\r
-            {\r
-                /*  Even though we are returning successfully, keep track of\r
-                    whether an illegal sequence was encountered and skipped.\r
-                */\r
-                pFormatType->fHasIllegalType = fContainsIllegalSequence;\r
-\r
-                ulLen = (ulIdx - pFormatType->ulSpecifierIdx) + ulTypeLen;\r
-                break;\r
-            }\r
-\r
-            /*  In the case of an unrecognized type string, simply ignore\r
-                it entirely.  Reset the pointer to the position following\r
-                the percent sign, so it is not found again.\r
-            */\r
-            fContainsIllegalSequence = false;\r
-            ulIdx = pFormatType->ulSpecifierIdx + 1U;\r
-        }\r
-    }\r
-\r
-    return ulLen;\r
-}\r
-\r
-\r
-/** @brief Parse a RedPrintf() format type string to determine the proper data\r
-           type.\r
-\r
-    @param pszFormat    The format string to process.  This must be a pointer to\r
-                        the character following any width or justification\r
-                        characters.\r
-    @param pulTypeLen   The location in which to store the type length.  The\r
-                        value will be 0 if PRFMT_UNKNOWN is returned.\r
-\r
-    @return Rhe PRFMT_* type value, or PRFMT_UNKNOWN if the type is not\r
-            recognized.\r
-*/\r
-static PRINTTYPE ParseFormatType(\r
-    const char     *pszFormat,\r
-    uint32_t       *pulTypeLen)\r
-{\r
-    PRINTTYPE       fmtType = PRFMT_UNKNOWN;\r
-    uint32_t        ulIdx = 0U;\r
-\r
-    switch(pszFormat[ulIdx])\r
-    {\r
-        case '%':\r
-            fmtType = PRFMT_DOUBLEPERCENT;\r
-            break;\r
-        case 'c':\r
-            fmtType = PRFMT_CHAR;\r
-            break;\r
-        case 's':\r
-            fmtType = PRFMT_ANSISTRING;\r
-            break;\r
-        case 'p':\r
-            fmtType = PRFMT_POINTER;\r
-            break;\r
-        case 'd':\r
-            fmtType = MAPINT;\r
-            break;\r
-        case 'u':\r
-            fmtType = MAPUINT;\r
-            break;\r
-        case 'x':\r
-            fmtType = MAPHEXUINT;\r
-            break;\r
-        case 'h':\r
-        {\r
-            ulIdx++;\r
-            switch(pszFormat[ulIdx])\r
-            {\r
-                case 'd':\r
-                    fmtType = MAPSHORT;\r
-                    break;\r
-                case 'u':\r
-                    fmtType = MAPUSHORT;\r
-                    break;\r
-                case 'x':\r
-                    fmtType = MAPHEXUSHORT;\r
-                    break;\r
-                default:\r
-                    break;\r
-            }\r
-            break;\r
-        }\r
-        case 'l':\r
-        {\r
-            ulIdx++;\r
-            switch(pszFormat[ulIdx])\r
-            {\r
-                case 'd':\r
-                    fmtType = MAPLONG;\r
-                    break;\r
-                case 'u':\r
-                    fmtType = MAPULONG;\r
-                    break;\r
-                case 'x':\r
-                    fmtType = MAPHEXULONG;\r
-                    break;\r
-                case 'l':\r
-                {\r
-                    ulIdx++;\r
-                    switch(pszFormat[ulIdx])\r
-                    {\r
-                        case 'd':\r
-                            fmtType = MAPLONGLONG;\r
-                            break;\r
-                        case 'u':\r
-                            fmtType = MAPULONGLONG;\r
-                            break;\r
-                        case 'x':\r
-                        case 'X':\r
-                            fmtType = MAPHEXULONGLONG;\r
-                            break;\r
-                        default:\r
-                            break;\r
-                    }\r
-                    break;\r
-                }\r
-                default:\r
-                    break;\r
-            }\r
-            break;\r
-        }\r
-        default:\r
-            break;\r
-    }\r
-\r
-    if(fmtType != PRFMT_UNKNOWN)\r
-    {\r
-        *pulTypeLen = ulIdx + 1U;\r
-    }\r
-    else\r
-    {\r
-        *pulTypeLen = 0U;\r
-    }\r
-\r
-    return fmtType;\r
-}\r
-\r
-\r
-/** @brief Format a signed 32-bit integer as a base 10 ASCII string.\r
-\r
-    @note   If the output buffer length is exhausted, the result will *not* be\r
-            null-terminated.\r
-\r
-    @note   If the @p ulFillLen value is greater than or equal to the buffer\r
-            length, the result will not be null-terminated, even if the\r
-            formatted portion of the data is shorter than the buffer length.\r
-\r
-    @param pcBuffer     The output buffer\r
-    @param ulBufferLen  A pointer to the output buffer length\r
-    @param lNum         The 32-bit signed number to convert\r
-    @param ulFillLen    The fill length, if any\r
-    @param cFill        The fill character to use\r
-\r
-    @return The length of the string.\r
-*/\r
-static uint32_t LtoA(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    int32_t     lNum,\r
-    uint32_t    ulFillLen,\r
-    char        cFill)\r
-{\r
-    uint32_t    ulLen;\r
-\r
-    if(pcBuffer == NULL)\r
-    {\r
-        REDERROR();\r
-        ulLen = 0U;\r
-    }\r
-    else\r
-    {\r
-        char                ach[12U]; /* big enough for a int32_t in base 10 */\r
-        uint32_t            ulDigits = 0U;\r
-        uint32_t            ulNum;\r
-        bool                fSign;\r
-\r
-        if(lNum < 0)\r
-        {\r
-            fSign = true;\r
-            ulNum = (uint32_t)-lNum;\r
-        }\r
-        else\r
-        {\r
-            fSign = false;\r
-            ulNum = (uint32_t)lNum;\r
-        }\r
-\r
-        do\r
-        {\r
-            ach[ulDigits] = gacDigits[ulNum % 10U];\r
-            ulNum = ulNum / 10U;\r
-            ulDigits++;\r
-        }\r
-        while(ulNum);\r
-\r
-        if(fSign)\r
-        {\r
-            ach[ulDigits] = '-';\r
-            ulDigits++;\r
-        }\r
-\r
-        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
-    }\r
-\r
-    return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format a signed 64-bit integer as a base 10 ASCII string.\r
-\r
-    @note   If the output buffer length is exhausted, the result will *not* be\r
-            null-terminated.\r
-\r
-    @note   If the @p ulFillLen value is greater than or equal to the buffer\r
-            length, the result will not be null-terminated, even if the\r
-            formatted portion of the data is shorter than the buffer length.\r
-\r
-    @param pcBuffer     The output buffer\r
-    @param ulBufferLen  A pointer to the output buffer length\r
-    @param llNum        The 64-bit signed number to convert\r
-    @param ulFillLen    The fill length, if any\r
-    @param cFill        The fill character to use\r
-\r
-    @return The length of the string.\r
-*/\r
-static uint32_t LLtoA(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    int64_t     llNum,\r
-    uint32_t    ulFillLen,\r
-    char        cFill)\r
-{\r
-    uint32_t    ulLen;\r
-\r
-    if(pcBuffer == NULL)\r
-    {\r
-        REDERROR();\r
-        ulLen = 0U;\r
-    }\r
-    else\r
-    {\r
-        char                ach[12U]; /* big enough for a int32_t in base 10 */\r
-        uint32_t            ulDigits = 0U;\r
-        uint64_t            ullNum;\r
-        bool                fSign;\r
-\r
-        if(llNum < 0)\r
-        {\r
-            fSign = true;\r
-            ullNum = (uint64_t)-llNum;\r
-        }\r
-        else\r
-        {\r
-            fSign = false;\r
-            ullNum = (uint64_t)llNum;\r
-        }\r
-\r
-        /*  Not allowed to assume that 64-bit division is OK, so use a\r
-            software division routine.\r
-        */\r
-        do\r
-        {\r
-            uint64_t ullQuotient;\r
-            uint32_t ulRemainder;\r
-\r
-            /*  Note: RedUint64DivMod32() is smart enough to use normal division\r
-                once ullNumericVal <= UINT32_MAX.\r
-            */\r
-            ullQuotient = RedUint64DivMod32(ullNum, 10U, &ulRemainder);\r
-\r
-            ach[ulDigits] = gacDigits[ulRemainder];\r
-            ullNum = ullQuotient;\r
-            ulDigits++;\r
-        }\r
-        while(ullNum > 0U);\r
-\r
-        if(fSign)\r
-        {\r
-            ach[ulDigits] = '-';\r
-            ulDigits++;\r
-        }\r
-\r
-        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
-    }\r
-\r
-    return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format an unsigned 32-bit integer as an ASCII string as decimal or\r
-           hex.\r
-\r
-    @note If the output buffer length is exhausted, the result will *not* be\r
-          null-terminated.\r
-\r
-    @param pcBuffer     The output buffer\r
-    @param ulBufferLen  The output buffer length\r
-    @param ulNum        The 32-bit unsigned number to convert\r
-    @param fHex         If true, format as hex; if false, decimal.\r
-    @param ulFillLen    The fill length, if any\r
-    @param cFill        The fill character to use\r
-\r
-    @return The length of the string.\r
-*/\r
-static uint32_t ULtoA(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    uint32_t    ulNum,\r
-    bool        fHex,\r
-    uint32_t    ulFillLen,\r
-    char        cFill)\r
-{\r
-    uint32_t    ulLen;\r
-\r
-    if(pcBuffer == NULL)\r
-    {\r
-        REDERROR();\r
-        ulLen = 0U;\r
-    }\r
-    else\r
-    {\r
-        char        ach[11U];   /* Big enough for a uint32_t in radix 10 */\r
-        uint32_t    ulDigits = 0U;\r
-        uint32_t    ulNumericVal = ulNum;\r
-        uint32_t    ulRadix = fHex ? 16U : 10U;\r
-\r
-        do\r
-        {\r
-            ach[ulDigits] = gacDigits[ulNumericVal % ulRadix];\r
-            ulNumericVal = ulNumericVal / ulRadix;\r
-            ulDigits++;\r
-        }\r
-        while(ulNumericVal > 0U);\r
-\r
-        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
-    }\r
-\r
-    return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format an unsigned 64-bit integer as an ASCII string as decimal or\r
-           hex.\r
-\r
-    @note If the output buffer length is exhausted, the result will *not* be\r
-          null-terminated.\r
-\r
-    @param pcBuffer     The output buffer.\r
-    @param ulBufferLen  The output buffer length.\r
-    @param ullNum       The unsigned 64-bit number to convert.\r
-    @param fHex         If true, format as hex; if false, decimal.\r
-    @param ulFillLen    The fill length, if any.\r
-    @param cFill        The fill character to use.\r
-\r
-    @return The length of the string.\r
-*/\r
-static uint32_t ULLtoA(\r
-    char       *pcBuffer,\r
-    uint32_t    ulBufferLen,\r
-    uint64_t    ullNum,\r
-    bool        fHex,\r
-    uint32_t    ulFillLen,\r
-    char        cFill)\r
-{\r
-    uint32_t    ulLen;\r
-\r
-    if(pcBuffer == NULL)\r
-    {\r
-        REDERROR();\r
-        ulLen = 0U;\r
-    }\r
-    else\r
-    {\r
-\r
-        char        ach[21U];   /* Big enough for a uint64_t in radix 10 */\r
-        uint32_t    ulDigits = 0U;\r
-        uint64_t    ullNumericVal = ullNum;\r
-\r
-        if(fHex)\r
-        {\r
-            /*  We can figure out the digits using bit operations.\r
-            */\r
-            do\r
-            {\r
-                ach[ulDigits] = gacDigits[ullNumericVal & 15U];\r
-                ullNumericVal >>= 4U;\r
-                ulDigits++;\r
-            }\r
-            while(ullNumericVal > 0U);\r
-        }\r
-        else\r
-        {\r
-            /*  Not allowed to assume that 64-bit division is OK, so use a\r
-                software division routine.\r
-            */\r
-            do\r
-            {\r
-                uint64_t ullQuotient;\r
-                uint32_t ulRemainder;\r
-\r
-                /*  Note: RedUint64DivMod32() is smart enough to use normal division\r
-                    once ullNumericVal <= UINT32_MAX.\r
-                */\r
-                ullQuotient = RedUint64DivMod32(ullNumericVal, 10U, &ulRemainder);\r
-\r
-                ach[ulDigits] = gacDigits[ulRemainder];\r
-                ullNumericVal = ullQuotient;\r
-                ulDigits++;\r
-            }\r
-            while(ullNumericVal > 0U);\r
-        }\r
-\r
-        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
-    }\r
-\r
-    return ulLen;\r
-}\r
-\r
-\r
-/** @brief Finish converting a number into an ASCII string representing that\r
-           number.\r
-\r
-    This helper function contains common logic that needs to run at the end of\r
-    all the "toA" functions.  It adds the fill character and reverses the digits\r
-    string.\r
-\r
-    @param pcDigits     The digits (and sign) for the ASCII string, in reverse\r
-                        order as they were computed.\r
-    @param ulDigits     The number of digit characters.\r
-    @param pcOutBuffer  The output buffer.\r
-    @param ulBufferLen  The length of the output buffer.\r
-    @param ulFillLen    The fill length.  If the number string is shorter than\r
-                        this, the remaining bytes are filled with @p cFill.\r
-    @param cFill        The fill character.\r
-\r
-    @return The length of @p pcOutBuffer.\r
-*/\r
-static uint32_t FinishToA(\r
-    const char *pcDigits,\r
-    uint32_t    ulDigits,\r
-    char       *pcOutBuffer,\r
-    uint32_t    ulBufferLen,\r
-    uint32_t    ulFillLen,\r
-    char        cFill)\r
-{\r
-    uint32_t    ulIdx = 0U;\r
-    uint32_t    ulDigitIdx = ulDigits;\r
-\r
-    /*  user may have asked for a fill char\r
-    */\r
-    if(ulFillLen > ulDigits)\r
-    {\r
-        uint32_t ulFillRem = ulFillLen - ulDigits;\r
-\r
-        while((ulFillRem > 0U) && (ulIdx < ulBufferLen))\r
-        {\r
-            pcOutBuffer[ulIdx] = cFill;\r
-            ulIdx++;\r
-            ulFillRem--;\r
-        }\r
-    }\r
-\r
-    /*  reverse the string\r
-    */\r
-    while((ulDigitIdx > 0) && (ulIdx < ulBufferLen))\r
-    {\r
-        ulDigitIdx--;\r
-        pcOutBuffer[ulIdx] = pcDigits[ulDigitIdx];\r
-        ulIdx++;\r
-    }\r
-\r
-    if(ulIdx < ulBufferLen)\r
-    {\r
-        pcOutBuffer[ulIdx] = '\0';\r
-    }\r
-\r
-    return ulIdx;\r
-}\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements functions for printing.
+
+    These functions are intended to be used in portable test code, which cannot
+    assume the standard I/O functions will be available.  Similar to their ANSI
+    C counterparts, these functions allow formatting text strings and (if the
+    configuration allows it) outputing formatted text.  The latter ability
+    relies on the RedOsOutputString() OS service function.
+
+    Do *not* use these functions in code which can safely assume the standard
+    I/O functions are available (e.g., in host tools code).
+
+    Do *not* use these functions from within the file system driver.  These
+    functions use variable arguments and thus are not MISRA-C:2012 compliant.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+#include <limits.h>
+#include <stdarg.h>
+
+
+/** @brief Maximum number of bytes of output supported by RedPrintf().
+
+    Typically only Datalight code uses these functions, and that could should be
+    written to respect this limit, so it should not normally be necessary to
+    adjust this value.
+*/
+#define OUTPUT_BUFFER_SIZE 256U
+
+
+typedef enum
+{
+    PRFMT_UNKNOWN = 0,
+    PRFMT_CHAR,
+    PRFMT_ANSISTRING,
+    PRFMT_SIGNED8BIT,
+    PRFMT_UNSIGNED8BIT,
+    PRFMT_SIGNED16BIT,
+    PRFMT_UNSIGNED16BIT,
+    PRFMT_SIGNED32BIT,
+    PRFMT_UNSIGNED32BIT,
+    PRFMT_SIGNED64BIT,
+    PRFMT_UNSIGNED64BIT,
+    PRFMT_HEX8BIT,
+    PRFMT_HEX16BIT,
+    PRFMT_HEX32BIT,
+    PRFMT_HEX64BIT,
+    PRFMT_POINTER,
+    PRFMT_DOUBLEPERCENT
+} PRINTTYPE;
+
+typedef struct
+{
+    PRINTTYPE       type;               /* The PRFMT_* type found */
+    uint32_t        ulSpecifierIdx;     /* Returns a pointer to the % sign */
+    uint32_t        ulFillLen;
+    char            cFillChar;
+    bool            fLeftJustified;
+    bool            fHasIllegalType;    /* TRUE if an illegal sequence was skipped over */
+    bool            fHasVarWidth;
+} PRINTFORMAT;
+
+
+/*  Our output handlers are written for standard fixed width data types.  Map
+    the standard ANSI C data types onto our handlers.  Currently this code has
+    the following requirements:
+
+    1) shorts must be either 16 or 32 bits
+    2) ints must be either 16 or 32 bits
+    3) longs must be between 32 or 64 bits
+    4) long longs must be 64 bits
+*/
+#if (USHRT_MAX == 0xFFFFU)
+  #define MAPSHORT          PRFMT_SIGNED16BIT
+  #define MAPUSHORT         PRFMT_UNSIGNED16BIT
+  #define MAPHEXUSHORT      PRFMT_HEX16BIT
+#elif (USHRT_MAX == 0xFFFFFFFFU)
+  #define MAPSHORT          PRFMT_SIGNED32BIT
+  #define MAPUSHORT         PRFMT_UNSIGNED32BIT
+  #define MAPHEXUSHORT      PRFMT_HEX32BIT
+#else
+  #error "The 'short' data type does not have a 16 or 32-bit width"
+#endif
+
+#if (UINT_MAX == 0xFFFFU)
+  #define MAPINT            PRFMT_SIGNED16BIT
+  #define MAPUINT           PRFMT_UNSIGNED16BIT
+  #define MAPHEXUINT        PRFMT_HEX16BIT
+#elif (UINT_MAX == 0xFFFFFFFFU)
+  #define MAPINT            PRFMT_SIGNED32BIT
+  #define MAPUINT           PRFMT_UNSIGNED32BIT
+  #define MAPHEXUINT        PRFMT_HEX32BIT
+#else
+  #error "The 'int' data type does not have a 16 or 32-bit width"
+#endif
+
+#if (ULONG_MAX == 0xFFFFFFFFU)
+  #define MAPLONG           PRFMT_SIGNED32BIT
+  #define MAPULONG          PRFMT_UNSIGNED32BIT
+  #define MAPHEXULONG       PRFMT_HEX32BIT
+#elif (ULONG_MAX <= UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
+  /*  We've run into unusual environments where "longs" are 40-bits wide.
+      In this event, map them to 64-bit types so no data is lost.
+  */
+  #define MAPLONG         PRFMT_SIGNED64BIT
+  #define MAPULONG        PRFMT_UNSIGNED64BIT
+  #define MAPHEXULONG     PRFMT_HEX64BIT
+#else
+  #error "The 'long' data type is not between 32 and 64 bits wide"
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX != UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
+  #error "The 'long long' data type is not 64 bits wide"
+#else
+  #define MAPLONGLONG       PRFMT_SIGNED64BIT
+  #define MAPULONGLONG      PRFMT_UNSIGNED64BIT
+  #define MAPHEXULONGLONG   PRFMT_HEX64BIT
+#endif
+
+
+static uint32_t ProcessFormatSegment(char *pcBuffer, uint32_t ulBufferLen, const char *pszFormat, PRINTFORMAT *pFormat, uint32_t *pulSpecifierLen);
+static uint32_t ParseFormatSpecifier(char const *pszFormat, PRINTFORMAT *pFormatType);
+static PRINTTYPE ParseFormatType(const char *pszFormat, uint32_t *pulTypeLen);
+static uint32_t LtoA(char *pcBuffer, uint32_t ulBufferLen, int32_t lNum, uint32_t ulFillLen, char cFill);
+static uint32_t LLtoA(char *pcBuffer, uint32_t ulBufferLen, int64_t llNum, uint32_t ulFillLen, char cFill);
+static uint32_t ULtoA(char *pcBuffer, uint32_t ulBufferLen, uint32_t ulNum, bool fHex, uint32_t ulFillLen, char cFill);
+static uint32_t ULLtoA(char *pcBuffer, uint32_t ulBufferLen, uint64_t ullNum, bool fHex, uint32_t ulFillLen, char cFill);
+static uint32_t FinishToA(const char *pcDigits, uint32_t ulDigits, char *pcOutBuffer, uint32_t ulBufferLen, uint32_t ulFillLen, char cFill);
+
+
+/*  Digits for the *LtoA() routines.
+*/
+static const char gacDigits[] = "0123456789ABCDEF";
+
+
+#if REDCONF_OUTPUT == 1
+/** @brief Print formatted data with a variable length argument list.
+
+    This function provides a subset of the ANSI C printf() functionality with
+    several extensions to support fixed size data types.
+
+    See RedVSNPrintf() for the list of supported types.
+
+    @param pszFormat    A pointer to the null-terminated format string.
+    @param ...          The variable length argument list.
+*/
+void RedPrintf(
+    const char *pszFormat,
+    ...)
+{
+    va_list     arglist;
+
+    va_start(arglist, pszFormat);
+
+    RedVPrintf(pszFormat, arglist);
+
+    va_end(arglist);
+}
+
+
+/** @brief Print formatted data using a pointer to a variable length argument
+           list.
+
+    This function provides a subset of the ANSI C vprintf() functionality.
+
+    See RedVSNPrintf() for the list of supported types.
+
+    This function accommodates a maximum output length of #OUTPUT_BUFFER_SIZE.
+    If this function must truncate the output, and the original string was
+    \n terminated, the truncated output will be \n terminated as well.
+
+    @param pszFormat    A pointer to the null-terminated format string.
+    @param arglist      The variable length argument list.
+*/
+void RedVPrintf(
+    const char *pszFormat,
+    va_list     arglist)
+{
+    char        achBuffer[OUTPUT_BUFFER_SIZE];
+
+    if(RedVSNPrintf(achBuffer, sizeof(achBuffer), pszFormat, arglist) == -1)
+    {
+        /*  Ensture the buffer is null terminated.
+        */
+        achBuffer[sizeof(achBuffer) - 1U] = '\0';
+
+        /*  If the original string was \n terminated and the new one is not, due to
+            truncation, stuff a \n into the new one.
+        */
+        if(pszFormat[RedStrLen(pszFormat) - 1U] == '\n')
+        {
+            achBuffer[sizeof(achBuffer) - 2U] = '\n';
+        }
+    }
+
+    RedOsOutputString(achBuffer);
+}
+#endif /* #if REDCONF_OUTPUT == 1 */
+
+
+/** @brief Format arguments into a string using a subset of the ANSI C
+           vsprintf() functionality.
+
+    This function is modeled after the Microsoft _snprint() extension to the
+    ANSI C sprintf() function, and allows a buffer length to be specified so
+    that overflow is avoided.
+
+    See RedVSNPrintf() for the list of supported types.
+
+    @param pcBuffer     A pointer to the output buffer
+    @param ulBufferLen  The output buffer length
+    @param pszFormat    A pointer to the null terminated format string
+    @param ...          Variable argument list
+
+    @return The length output, or -1 if the buffer filled up.  If -1 is
+            returned, the output buffer may not be null-terminated.
+*/
+int32_t RedSNPrintf(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    const char *pszFormat,
+    ...)
+{
+    int32_t     iLen;
+    va_list     arglist;
+
+    va_start(arglist, pszFormat);
+
+    iLen = RedVSNPrintf(pcBuffer, ulBufferLen, pszFormat, arglist);
+
+    va_end(arglist);
+
+    return iLen;
+}
+
+
+/** @brief Format arguments into a string using a subset of the ANSI C
+           vsprintf() functionality.
+
+    This function is modeled after the Microsoft _vsnprint() extension to the
+    ANSI C vsprintf() function, and requires a buffer length to be specified so
+    that overflow is avoided.
+
+    The following ANSI C standard formatting codes are supported:
+
+    | Code | Meaning                            |
+    | ---- | ---------------------------------- |
+    | %c   | Format a character                 |
+    | %s   | Format a null-terminated C string  |
+    | %hd  | Format a signed short              |
+    | %hu  | Format an unsigned short           |
+    | %d   | Format a signed integer            |
+    | %u   | Format an unsigned integer         |
+    | %ld  | Format a signed long               |
+    | %lu  | Format an unsigned long            |
+    | %lld | Format a signed long long          |
+    | %llu | Format an unsigned long long       |
+    | %hx  | Format a short in hex              |
+    | %x   | Format an integer in hex           |
+    | %lx  | Format a long in hex               |
+    | %llx | Format a long long in hex          |
+    | %p   | Format a pointer (hex value)       |
+
+    @note All formatting codes are case-sensitive.
+
+    Fill characters and field widths are supported per the ANSI standard, as is
+    left justification with the '-' character.
+
+    The only supported fill characters are '0', ' ', and '_'.
+
+    '*' is supported to specify variable length field widths.
+
+    Hexidecimal numbers are always displayed in upper case.  Formatting codes
+    which specifically request upper case (e.g., "%lX") are not supported.
+
+    Unsupported behaviors:
+      - Precision is not supported.
+      - Floating point is not supported.
+
+    Errata:
+    - There is a subtle difference in the return value for this function versus
+      the Microsoft implementation.  In the Microsoft version, if the buffer
+      exactly fills up, but there is no room for a null-terminator, the return
+      value will be the length of the buffer.  In this code, -1 will be returned
+      when this happens.
+    - When using left justified strings, the only supported fill character is a
+      space, regardless of what may be specified.  It is not clear if this is
+      ANSI standard or just the way the Microsoft function works, but we emulate
+      the Microsoft behavior.
+
+    @param pcBuffer     A pointer to the output buffer.
+    @param ulBufferLen  The output buffer length.
+    @param pszFormat    A pointer to the null terminated ANSI format string.
+    @param arglist      Variable argument list.
+
+    @return  The length output, or -1 if the buffer filled up.  If -1 is
+             returned, the output buffer may not be null-terminated.
+*/
+int32_t RedVSNPrintf(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    const char *pszFormat,
+    va_list     arglist)
+{
+    uint32_t    ulBufIdx = 0U;
+    uint32_t    ulFmtIdx = 0U;
+    int32_t     iLen;
+
+    while((pszFormat[ulFmtIdx] != '\0') && (ulBufIdx < ulBufferLen))
+    {
+        PRINTFORMAT fmt;
+        uint32_t    ulSpecifierLen;
+        uint32_t    ulWidth;
+
+        /*  Process the next segment of the format string, outputting
+            any non-format specifiers, as output buffer space allows,
+            and return information about the next format specifier.
+        */
+        ulWidth = ProcessFormatSegment(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, &pszFormat[ulFmtIdx], &fmt, &ulSpecifierLen);
+        if(ulWidth)
+        {
+            REDASSERT(ulWidth <= (ulBufferLen - ulBufIdx));
+
+            ulBufIdx += ulWidth;
+        }
+
+        /*  If no specifier was found, or if the output buffer is
+            full, we're done -- get out.
+        */
+        if((ulSpecifierLen == 0U) || (ulBufIdx == ulBufferLen))
+        {
+            break;
+        }
+
+        /*  Otherwise, the math should add up for these things...
+        */
+        REDASSERT(&pszFormat[fmt.ulSpecifierIdx] == &pszFormat[ulWidth]);
+
+        /*  Point past the specifier, to the next piece of the format string.
+        */
+        ulFmtIdx = ulFmtIdx + fmt.ulSpecifierIdx + ulSpecifierLen;
+
+        if(fmt.fHasVarWidth)
+        {
+            int iFillLen = va_arg(arglist, int);
+
+            if(iFillLen >= 0)
+            {
+                fmt.ulFillLen = (uint32_t)iFillLen;
+            }
+            else
+            {
+                /*  Bogus fill length.  Ignore.
+                */
+                fmt.ulFillLen = 0U;
+            }
+        }
+
+        switch(fmt.type)
+        {
+            case PRFMT_DOUBLEPERCENT:
+            {
+                /*  Nothing to do.  A single percent has already been output,
+                    and we just finished skipping past the second percent.
+                */
+                break;
+            }
+
+            /*----------------->  Small int handling  <------------------
+             *
+             *  Values smaller than "int" will be promoted to "int" by
+             *  the compiler, so we must retrieve them using "int" when
+             *  calling va_arg().  Once we've done that, we immediately
+             *  put the value into the desired data type.
+             *---------------------------------------------------------*/
+
+            case PRFMT_CHAR:
+            {
+                pcBuffer[ulBufIdx] = (char)va_arg(arglist, int);
+                ulBufIdx++;
+                break;
+            }
+            case PRFMT_SIGNED8BIT:
+            {
+                int8_t num = (int8_t)va_arg(arglist, int);
+
+                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_UNSIGNED8BIT:
+            {
+                uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, false, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_HEX8BIT:
+            {
+                uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, true, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_SIGNED16BIT:
+            {
+                int16_t num = (int16_t)va_arg(arglist, int);
+
+                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_UNSIGNED16BIT:
+            {
+                uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, false, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_HEX16BIT:
+            {
+                uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, true, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_SIGNED32BIT:
+            {
+                int32_t lNum = va_arg(arglist, int32_t);
+
+                ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, lNum, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_UNSIGNED32BIT:
+            {
+                uint32_t ulNum = va_arg(arglist, uint32_t);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, false, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_HEX32BIT:
+            {
+                uint32_t ulNum = va_arg(arglist, uint32_t);
+
+                ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, true, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_SIGNED64BIT:
+            {
+                int64_t llNum = va_arg(arglist, int64_t);
+
+                ulBufIdx += LLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, llNum, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_UNSIGNED64BIT:
+            {
+                uint64_t ullNum = va_arg(arglist, uint64_t);
+
+                ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, false, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_HEX64BIT:
+            {
+                uint64_t ullNum = va_arg(arglist, uint64_t);
+
+                ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, true, fmt.ulFillLen, fmt.cFillChar);
+                break;
+            }
+            case PRFMT_POINTER:
+            {
+                const void *ptr = va_arg(arglist, const void *);
+
+                /*  Assert our assumption.
+                */
+                REDASSERT(sizeof(void *) <= 8U);
+
+                /*  Format as either a 64-bit or a 32-bit value.
+                */
+                if(sizeof(void *) > 4U)
+                {
+                    /*  Attempt to quiet warnings.
+                    */
+                    uintptr_t   ptrval = (uintptr_t)ptr;
+                    uint64_t    ullPtrVal = (uint64_t)ptrval;
+
+                    ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
+                }
+                else
+                {
+                    /*  Attempt to quiet warnings.
+                    */
+                    uintptr_t   ptrval = (uintptr_t)ptr;
+                    uint32_t    ulPtrVal = (uint32_t)ptrval;
+
+                    ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
+                }
+
+                break;
+            }
+            case PRFMT_ANSISTRING:
+            {
+                const char *pszArg = va_arg(arglist, const char *);
+                uint32_t    ulArgIdx = 0U;
+
+                if(pszArg == NULL)
+                {
+                    pszArg = "null";
+                }
+
+                if(fmt.ulFillLen > 0U)
+                {
+                    if(!fmt.fLeftJustified)
+                    {
+                        uint32_t ulLen = RedStrLen(pszArg);
+
+                        /*  So long as we are not left justifying, fill as many
+                            characters as is necessary to make the string right
+                            justified.
+                        */
+                        while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > ulLen))
+                        {
+                            pcBuffer[ulBufIdx] = fmt.cFillChar;
+                            ulBufIdx++;
+                            fmt.ulFillLen--;
+                        }
+                    }
+
+                    /*  Move as many characters as we have space for into the
+                        output buffer.
+                    */
+                    while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
+                    {
+                        pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
+                        ulBufIdx++;
+                        ulArgIdx++;
+                        if(fmt.ulFillLen > 0U)
+                        {
+                            fmt.ulFillLen--;
+                        }
+                    }
+
+                    /*  If there is any space left to fill, do it (the string
+                        must have been left justified).
+                    */
+                    while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > 0U))
+                    {
+                        /*  This is NOT a typo -- when using left justified
+                            strings, spaces are the only allowed fill character.
+                            See the errata.
+                        */
+                        pcBuffer[ulBufIdx] = ' ';
+                        ulBufIdx++;
+                        fmt.ulFillLen--;
+                    }
+                }
+                else
+                {
+                    /*  No fill characters, just move up to as many
+                        characters as we have space for in the output
+                        buffer.
+                    */
+                    while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
+                    {
+                        pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
+                        ulBufIdx++;
+                        ulArgIdx++;
+                    }
+                }
+                break;
+            }
+            default:
+            {
+                REDERROR();
+                break;
+            }
+        }
+    }
+
+    /*  If there is space, tack on a null and return the output length
+        processed, not including the null.
+    */
+    if(ulBufIdx < ulBufferLen)
+    {
+        pcBuffer[ulBufIdx] = '\0';
+        iLen = (int32_t)ulBufIdx;
+    }
+    else
+    {
+        /*  Not enough space, just return -1, with no null termination
+        */
+        iLen = -1;
+    }
+
+    return iLen;
+}
+
+
+/** @brief  Process the next segment of the format string, outputting any
+            non-format specifiers, as output buffer space allows, and return
+            information about the next format specifier.
+
+    @note   If the returned value is the same as the supplied @p ulBufferLen,
+            the output buffer will not be null-terminated.  In all other cases,
+            the result will be null-terminated.  The returned length will never
+            include the null in the count.
+
+    @param pcBuffer         The output buffer.
+    @param ulBufferLen      The output buffer length.
+    @param pszFormat        The format string to process.
+    @param pFormat          The PRINTFORMAT structure to fill.
+    @param pulSpecifierLen  Returns the length of any format specifier string,
+                            or zero if no specifier was found.
+
+    @return The count of characters from pszFormatt which were processed and
+            copied to pcBuffer.
+        - If zero is returned and *pulSpecifierLen is non-zero, then
+          a format specifier string was found at the start of pszFmt.
+        - If non-zero is returned and *pulSpecifierLen is zero, then
+          no format specifier string was found, and the entire pszFmt
+          string was copied to pBuffer (or as much as will fit).
+*/
+static uint32_t ProcessFormatSegment(
+    char           *pcBuffer,
+    uint32_t        ulBufferLen,
+    const char     *pszFormat,
+    PRINTFORMAT    *pFormat,
+    uint32_t       *pulSpecifierLen)
+{
+    uint32_t        ulWidth = 0U;
+
+    /*  Find the next format specifier string, and information about it.
+    */
+    *pulSpecifierLen = ParseFormatSpecifier(pszFormat, pFormat);
+
+    if(*pulSpecifierLen == 0U)
+    {
+        /*  If no specifier was found at all, then simply output the full length
+            of the string, or as much as will fit.
+         */
+        ulWidth = REDMIN(ulBufferLen, RedStrLen(pszFormat));
+
+        RedMemCpy(pcBuffer, pszFormat, ulWidth);
+    }
+    else
+    {
+        /*  If we encountered a double percent, skip past one of them so it is
+            copied into the output buffer.
+        */
+        if(pFormat->type == PRFMT_DOUBLEPERCENT)
+        {
+            pFormat->ulSpecifierIdx++;
+
+            /*  A double percent specifier always has a length of two.  Since
+                we're processing one of those percent signs, reduce the length
+                to one.  Assert it so.
+            */
+            REDASSERT(*pulSpecifierLen == 2U);
+
+            (*pulSpecifierLen)--;
+        }
+
+        /*  So long as the specifier is not the very first thing in the format
+            string...
+        */
+        if(pFormat->ulSpecifierIdx != 0U)
+        {
+            /*  A specifier was found, but there is other data preceding it.
+                Copy as much as allowed to the output buffer.
+            */
+            ulWidth = REDMIN(ulBufferLen, pFormat->ulSpecifierIdx);
+
+            RedMemCpy(pcBuffer, pszFormat, ulWidth);
+        }
+    }
+
+    /*  If there is room in the output buffer, null-terminate whatever is there.
+        But note that the returned length never includes the null.
+    */
+    if(ulWidth < ulBufferLen)
+    {
+        pcBuffer[ulWidth] = 0U;
+    }
+
+    return ulWidth;
+}
+
+
+/** @brief Parse the specified format string for a valid RedVSNPrintf() format
+           sequence, and return information about it.
+
+    @param pszFormat    The format string to process.
+    @param pFormatType  The PRINTFORMAT structure to fill.  The data is only
+                        valid if a non-zero length is returned.
+
+    @return The length of the full format specifier string, starting at
+            pFormat->ulSpecifierIdx.  Returns zero if a valid specifier was
+            not found.
+*/
+static uint32_t ParseFormatSpecifier(
+    char const     *pszFormat,
+    PRINTFORMAT    *pFormatType)
+{
+    bool            fContainsIllegalSequence = false;
+    uint32_t        ulLen = 0U;
+    uint32_t        ulIdx = 0U;
+
+    while(pszFormat[ulIdx] != '\0')
+    {
+        uint32_t    ulTypeLen;
+
+        /*  general output
+        */
+        if(pszFormat[ulIdx] != '%')
+        {
+            ulIdx++;
+        }
+        else
+        {
+            RedMemSet(pFormatType, 0U, sizeof(*pFormatType));
+
+            /*  Record the location of the start of the format sequence
+            */
+            pFormatType->ulSpecifierIdx = ulIdx;
+            ulIdx++;
+
+            if(pszFormat[ulIdx] == '-')
+            {
+                pFormatType->fLeftJustified = true;
+                ulIdx++;
+            }
+
+            if((pszFormat[ulIdx] == '0') || (pszFormat[ulIdx] == '_'))
+            {
+                pFormatType->cFillChar = pszFormat[ulIdx];
+                ulIdx++;
+            }
+            else
+            {
+                pFormatType->cFillChar = ' ';
+            }
+
+            if(pszFormat[ulIdx] == '*')
+            {
+                pFormatType->fHasVarWidth = true;
+                ulIdx++;
+            }
+            else if(ISDIGIT(pszFormat[ulIdx]))
+            {
+                pFormatType->ulFillLen = (uint32_t)RedAtoI(&pszFormat[ulIdx]);
+                while(ISDIGIT(pszFormat[ulIdx]))
+                {
+                    ulIdx++;
+                }
+            }
+            else
+            {
+                /*  No fill length.
+                */
+            }
+
+            pFormatType->type = ParseFormatType(&pszFormat[ulIdx], &ulTypeLen);
+            if(pFormatType->type != PRFMT_UNKNOWN)
+            {
+                /*  Even though we are returning successfully, keep track of
+                    whether an illegal sequence was encountered and skipped.
+                */
+                pFormatType->fHasIllegalType = fContainsIllegalSequence;
+
+                ulLen = (ulIdx - pFormatType->ulSpecifierIdx) + ulTypeLen;
+                break;
+            }
+
+            /*  In the case of an unrecognized type string, simply ignore
+                it entirely.  Reset the pointer to the position following
+                the percent sign, so it is not found again.
+            */
+            fContainsIllegalSequence = false;
+            ulIdx = pFormatType->ulSpecifierIdx + 1U;
+        }
+    }
+
+    return ulLen;
+}
+
+
+/** @brief Parse a RedPrintf() format type string to determine the proper data
+           type.
+
+    @param pszFormat    The format string to process.  This must be a pointer to
+                        the character following any width or justification
+                        characters.
+    @param pulTypeLen   The location in which to store the type length.  The
+                        value will be 0 if PRFMT_UNKNOWN is returned.
+
+    @return Rhe PRFMT_* type value, or PRFMT_UNKNOWN if the type is not
+            recognized.
+*/
+static PRINTTYPE ParseFormatType(
+    const char     *pszFormat,
+    uint32_t       *pulTypeLen)
+{
+    PRINTTYPE       fmtType = PRFMT_UNKNOWN;
+    uint32_t        ulIdx = 0U;
+
+    switch(pszFormat[ulIdx])
+    {
+        case '%':
+            fmtType = PRFMT_DOUBLEPERCENT;
+            break;
+        case 'c':
+            fmtType = PRFMT_CHAR;
+            break;
+        case 's':
+            fmtType = PRFMT_ANSISTRING;
+            break;
+        case 'p':
+            fmtType = PRFMT_POINTER;
+            break;
+        case 'd':
+            fmtType = MAPINT;
+            break;
+        case 'u':
+            fmtType = MAPUINT;
+            break;
+        case 'x':
+            fmtType = MAPHEXUINT;
+            break;
+        case 'h':
+        {
+            ulIdx++;
+            switch(pszFormat[ulIdx])
+            {
+                case 'd':
+                    fmtType = MAPSHORT;
+                    break;
+                case 'u':
+                    fmtType = MAPUSHORT;
+                    break;
+                case 'x':
+                    fmtType = MAPHEXUSHORT;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        }
+        case 'l':
+        {
+            ulIdx++;
+            switch(pszFormat[ulIdx])
+            {
+                case 'd':
+                    fmtType = MAPLONG;
+                    break;
+                case 'u':
+                    fmtType = MAPULONG;
+                    break;
+                case 'x':
+                    fmtType = MAPHEXULONG;
+                    break;
+                case 'l':
+                {
+                    ulIdx++;
+                    switch(pszFormat[ulIdx])
+                    {
+                        case 'd':
+                            fmtType = MAPLONGLONG;
+                            break;
+                        case 'u':
+                            fmtType = MAPULONGLONG;
+                            break;
+                        case 'x':
+                        case 'X':
+                            fmtType = MAPHEXULONGLONG;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    if(fmtType != PRFMT_UNKNOWN)
+    {
+        *pulTypeLen = ulIdx + 1U;
+    }
+    else
+    {
+        *pulTypeLen = 0U;
+    }
+
+    return fmtType;
+}
+
+
+/** @brief Format a signed 32-bit integer as a base 10 ASCII string.
+
+    @note   If the output buffer length is exhausted, the result will *not* be
+            null-terminated.
+
+    @note   If the @p ulFillLen value is greater than or equal to the buffer
+            length, the result will not be null-terminated, even if the
+            formatted portion of the data is shorter than the buffer length.
+
+    @param pcBuffer     The output buffer
+    @param ulBufferLen  A pointer to the output buffer length
+    @param lNum         The 32-bit signed number to convert
+    @param ulFillLen    The fill length, if any
+    @param cFill        The fill character to use
+
+    @return The length of the string.
+*/
+static uint32_t LtoA(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    int32_t     lNum,
+    uint32_t    ulFillLen,
+    char        cFill)
+{
+    uint32_t    ulLen;
+
+    if(pcBuffer == NULL)
+    {
+        REDERROR();
+        ulLen = 0U;
+    }
+    else
+    {
+        char                ach[12U]; /* big enough for a int32_t in base 10 */
+        uint32_t            ulDigits = 0U;
+        uint32_t            ulNum;
+        bool                fSign;
+
+        if(lNum < 0)
+        {
+            fSign = true;
+            ulNum = (uint32_t)-lNum;
+        }
+        else
+        {
+            fSign = false;
+            ulNum = (uint32_t)lNum;
+        }
+
+        do
+        {
+            ach[ulDigits] = gacDigits[ulNum % 10U];
+            ulNum = ulNum / 10U;
+            ulDigits++;
+        }
+        while(ulNum);
+
+        if(fSign)
+        {
+            ach[ulDigits] = '-';
+            ulDigits++;
+        }
+
+        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+    }
+
+    return ulLen;
+}
+
+
+/** @brief Format a signed 64-bit integer as a base 10 ASCII string.
+
+    @note   If the output buffer length is exhausted, the result will *not* be
+            null-terminated.
+
+    @note   If the @p ulFillLen value is greater than or equal to the buffer
+            length, the result will not be null-terminated, even if the
+            formatted portion of the data is shorter than the buffer length.
+
+    @param pcBuffer     The output buffer
+    @param ulBufferLen  A pointer to the output buffer length
+    @param llNum        The 64-bit signed number to convert
+    @param ulFillLen    The fill length, if any
+    @param cFill        The fill character to use
+
+    @return The length of the string.
+*/
+static uint32_t LLtoA(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    int64_t     llNum,
+    uint32_t    ulFillLen,
+    char        cFill)
+{
+    uint32_t    ulLen;
+
+    if(pcBuffer == NULL)
+    {
+        REDERROR();
+        ulLen = 0U;
+    }
+    else
+    {
+        char                ach[12U]; /* big enough for a int32_t in base 10 */
+        uint32_t            ulDigits = 0U;
+        uint64_t            ullNum;
+        bool                fSign;
+
+        if(llNum < 0)
+        {
+            fSign = true;
+            ullNum = (uint64_t)-llNum;
+        }
+        else
+        {
+            fSign = false;
+            ullNum = (uint64_t)llNum;
+        }
+
+        /*  Not allowed to assume that 64-bit division is OK, so use a
+            software division routine.
+        */
+        do
+        {
+            uint64_t ullQuotient;
+            uint32_t ulRemainder;
+
+            /*  Note: RedUint64DivMod32() is smart enough to use normal division
+                once ullNumericVal <= UINT32_MAX.
+            */
+            ullQuotient = RedUint64DivMod32(ullNum, 10U, &ulRemainder);
+
+            ach[ulDigits] = gacDigits[ulRemainder];
+            ullNum = ullQuotient;
+            ulDigits++;
+        }
+        while(ullNum > 0U);
+
+        if(fSign)
+        {
+            ach[ulDigits] = '-';
+            ulDigits++;
+        }
+
+        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+    }
+
+    return ulLen;
+}
+
+
+/** @brief Format an unsigned 32-bit integer as an ASCII string as decimal or
+           hex.
+
+    @note If the output buffer length is exhausted, the result will *not* be
+          null-terminated.
+
+    @param pcBuffer     The output buffer
+    @param ulBufferLen  The output buffer length
+    @param ulNum        The 32-bit unsigned number to convert
+    @param fHex         If true, format as hex; if false, decimal.
+    @param ulFillLen    The fill length, if any
+    @param cFill        The fill character to use
+
+    @return The length of the string.
+*/
+static uint32_t ULtoA(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    uint32_t    ulNum,
+    bool        fHex,
+    uint32_t    ulFillLen,
+    char        cFill)
+{
+    uint32_t    ulLen;
+
+    if(pcBuffer == NULL)
+    {
+        REDERROR();
+        ulLen = 0U;
+    }
+    else
+    {
+        char        ach[11U];   /* Big enough for a uint32_t in radix 10 */
+        uint32_t    ulDigits = 0U;
+        uint32_t    ulNumericVal = ulNum;
+        uint32_t    ulRadix = fHex ? 16U : 10U;
+
+        do
+        {
+            ach[ulDigits] = gacDigits[ulNumericVal % ulRadix];
+            ulNumericVal = ulNumericVal / ulRadix;
+            ulDigits++;
+        }
+        while(ulNumericVal > 0U);
+
+        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+    }
+
+    return ulLen;
+}
+
+
+/** @brief Format an unsigned 64-bit integer as an ASCII string as decimal or
+           hex.
+
+    @note If the output buffer length is exhausted, the result will *not* be
+          null-terminated.
+
+    @param pcBuffer     The output buffer.
+    @param ulBufferLen  The output buffer length.
+    @param ullNum       The unsigned 64-bit number to convert.
+    @param fHex         If true, format as hex; if false, decimal.
+    @param ulFillLen    The fill length, if any.
+    @param cFill        The fill character to use.
+
+    @return The length of the string.
+*/
+static uint32_t ULLtoA(
+    char       *pcBuffer,
+    uint32_t    ulBufferLen,
+    uint64_t    ullNum,
+    bool        fHex,
+    uint32_t    ulFillLen,
+    char        cFill)
+{
+    uint32_t    ulLen;
+
+    if(pcBuffer == NULL)
+    {
+        REDERROR();
+        ulLen = 0U;
+    }
+    else
+    {
+
+        char        ach[21U];   /* Big enough for a uint64_t in radix 10 */
+        uint32_t    ulDigits = 0U;
+        uint64_t    ullNumericVal = ullNum;
+
+        if(fHex)
+        {
+            /*  We can figure out the digits using bit operations.
+            */
+            do
+            {
+                ach[ulDigits] = gacDigits[ullNumericVal & 15U];
+                ullNumericVal >>= 4U;
+                ulDigits++;
+            }
+            while(ullNumericVal > 0U);
+        }
+        else
+        {
+            /*  Not allowed to assume that 64-bit division is OK, so use a
+                software division routine.
+            */
+            do
+            {
+                uint64_t ullQuotient;
+                uint32_t ulRemainder;
+
+                /*  Note: RedUint64DivMod32() is smart enough to use normal division
+                    once ullNumericVal <= UINT32_MAX.
+                */
+                ullQuotient = RedUint64DivMod32(ullNumericVal, 10U, &ulRemainder);
+
+                ach[ulDigits] = gacDigits[ulRemainder];
+                ullNumericVal = ullQuotient;
+                ulDigits++;
+            }
+            while(ullNumericVal > 0U);
+        }
+
+        ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+    }
+
+    return ulLen;
+}
+
+
+/** @brief Finish converting a number into an ASCII string representing that
+           number.
+
+    This helper function contains common logic that needs to run at the end of
+    all the "toA" functions.  It adds the fill character and reverses the digits
+    string.
+
+    @param pcDigits     The digits (and sign) for the ASCII string, in reverse
+                        order as they were computed.
+    @param ulDigits     The number of digit characters.
+    @param pcOutBuffer  The output buffer.
+    @param ulBufferLen  The length of the output buffer.
+    @param ulFillLen    The fill length.  If the number string is shorter than
+                        this, the remaining bytes are filled with @p cFill.
+    @param cFill        The fill character.
+
+    @return The length of @p pcOutBuffer.
+*/
+static uint32_t FinishToA(
+    const char *pcDigits,
+    uint32_t    ulDigits,
+    char       *pcOutBuffer,
+    uint32_t    ulBufferLen,
+    uint32_t    ulFillLen,
+    char        cFill)
+{
+    uint32_t    ulIdx = 0U;
+    uint32_t    ulDigitIdx = ulDigits;
+
+    /*  user may have asked for a fill char
+    */
+    if(ulFillLen > ulDigits)
+    {
+        uint32_t ulFillRem = ulFillLen - ulDigits;
+
+        while((ulFillRem > 0U) && (ulIdx < ulBufferLen))
+        {
+            pcOutBuffer[ulIdx] = cFill;
+            ulIdx++;
+            ulFillRem--;
+        }
+    }
+
+    /*  reverse the string
+    */
+    while((ulDigitIdx > 0) && (ulIdx < ulBufferLen))
+    {
+        ulDigitIdx--;
+        pcOutBuffer[ulIdx] = pcDigits[ulDigitIdx];
+        ulIdx++;
+    }
+
+    if(ulIdx < ulBufferLen)
+    {
+        pcOutBuffer[ulIdx] = '\0';
+    }
+
+    return ulIdx;
+}
+
index 5f05be7625c5b48c791e885b6de48b91b5195444..6dd443023b7a2bd24250c264bfe80bdf15721d63 100644 (file)
-/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
-                   Copyright (c) 2014-2015 Datalight, Inc.\r
-                       All Rights Reserved Worldwide.\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; use version 2 of the License.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
-    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License along\r
-    with this program; if not, write to the Free Software Foundation, Inc.,\r
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/*  Businesses and individuals that for commercial or other reasons cannot\r
-    comply with the terms of the GPLv2 license may obtain a commercial license\r
-    before incorporating Reliance Edge into proprietary software for\r
-    distribution in any form.  Visit http://www.datalight.com/reliance-edge for\r
-    more information.\r
-*/\r
-/** @file\r
-    @brief Implements a random number generator.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-/*  This is the global seed used by the random number generator when the caller\r
-    has not provided a seed to either the RedRand32() or RedRand64() functions.\r
-*/\r
-static uint64_t ullGlobalRandomNumberSeed;\r
-\r
-/*  Whether the above seed has been initialized.\r
-*/\r
-static bool fGlobalSeedInited;\r
-\r
-\r
-/** @brief Set the global seed used by the random number generator.\r
-\r
-    The global seed gets used when RedRand64() or RedRand32() are called with\r
-    a NULL seed argument.\r
-\r
-    @param ullSeed  The value to use as the global RNG seed.\r
-*/\r
-void RedRandSeed(\r
-    uint64_t ullSeed)\r
-{\r
-    ullGlobalRandomNumberSeed = ullSeed;\r
-    fGlobalSeedInited = true;\r
-}\r
-\r
-\r
-/** @brief Generate a 64-bit pseudo-random number.\r
-\r
-    The period of this random number generator is 2^64 (1.8 x 1019).  These\r
-    parameters are the same as the default one-stream SPRNG lcg64 generator and\r
-    it satisfies the requirements for a maximal period.\r
-\r
-    The tempering value is used and an AND mask and is specifically selected to\r
-    favor the distribution of lower bits.\r
-\r
-    @param pullSeed A pointer to the seed to use.  Set this value to NULL to\r
-                    use the internal global seed value.\r
-\r
-    @return A pseudo-random number in the range [0, UINT64_MAX].\r
-*/\r
-uint64_t RedRand64(\r
-    uint64_t       *pullSeed)\r
-{\r
-    const uint64_t  ullA = UINT64_SUFFIX(2862933555777941757);\r
-    const uint64_t  ullC = UINT64_SUFFIX(3037000493);\r
-    const uint64_t  ullT = UINT64_SUFFIX(4921441182957829599);\r
-    uint64_t        ullN;\r
-    uint64_t       *pullSeedPtr;\r
-    uint64_t        ullLocalSeed;\r
-\r
-    if(pullSeed != NULL)\r
-    {\r
-        ullLocalSeed = *pullSeed;\r
-        pullSeedPtr = pullSeed;\r
-    }\r
-    else\r
-    {\r
-        if(!fGlobalSeedInited)\r
-        {\r
-            /*  Unfortunately, the Reliance Edge OS services don't give us much\r
-                to work with to initialize the global seed.  There is no entropy\r
-                abstraction, no tick count abstraction, and the timestamp\r
-                abstraction uses an opaque type which is not guaranteed to be an\r
-                integer.  The best we can do is use the RTC.\r
-\r
-                Tests using the RNG should be supplying a seed anyway, for\r
-                reproducibility.\r
-            */\r
-            RedRandSeed((uint64_t)RedOsClockGetTime());\r
-        }\r
-\r
-        ullLocalSeed = ullGlobalRandomNumberSeed;\r
-        pullSeedPtr = &ullGlobalRandomNumberSeed;\r
-    }\r
-\r
-    ullN = (ullLocalSeed * ullA) + ullC;\r
-\r
-    *pullSeedPtr = ullN;\r
-\r
-    /*  The linear congruential generator used above produces good psuedo-random\r
-        64-bit number sequences, however, as with any LCG, the period of the\r
-        lower order bits is much shorter resulting in alternately odd/even pairs\r
-        in bit zero.\r
-\r
-        The result of the LGC above is tempered below with a series of XOR and\r
-        shift operations to produce a more acceptable equidistribution of bits\r
-        throughout the 64-bit range.\r
-    */\r
-    ullN ^= (ullN >> 21U) & ullT;\r
-    ullN ^= (ullN >> 43U) & ullT;\r
-    ullN ^= (ullN << 23U) & ~ullT;\r
-    ullN ^= (ullN << 31U) & ~ullT;\r
-\r
-    return ullN;\r
-}\r
-\r
-\r
-/** @brief Generate a 32-bit pseudo-random number.\r
-\r
-    @note   The 32-bit random number generator internally uses the 64-bit random\r
-            number generator, returning the low 32-bits of the pseudo-random\r
-            64-bit value.\r
-\r
-    @param pulSeed  A pointer to the seed to use.  Set this value to NULL to use\r
-                    the internal global seed value.\r
-\r
-    @return A pseudo-random number in the range [0, UINT32_MAX].\r
-*/\r
-uint32_t RedRand32(\r
-    uint32_t   *pulSeed)\r
-{\r
-    uint64_t    ullN;\r
-\r
-    if(pulSeed != NULL)\r
-    {\r
-        uint64_t ullLocalSeed;\r
-\r
-        ullLocalSeed = *pulSeed;\r
-        ullN = RedRand64(&ullLocalSeed);\r
-        *pulSeed = (uint32_t)ullLocalSeed;\r
-    }\r
-    else\r
-    {\r
-        ullN = RedRand64(NULL);\r
-    }\r
-\r
-    return (uint32_t)ullN;\r
-}\r
-\r
+/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+                   Copyright (c) 2014-2015 Datalight, Inc.
+                       All Rights Reserved Worldwide.
+
+    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; use version 2 of the License.
+
+    This program is distributed in the hope that it will be useful,
+    but "AS-IS," 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.
+*/
+/*  Businesses and individuals that for commercial or other reasons cannot
+    comply with the terms of the GPLv2 license may obtain a commercial license
+    before incorporating Reliance Edge into proprietary software for
+    distribution in any form.  Visit http://www.datalight.com/reliance-edge for
+    more information.
+*/
+/** @file
+    @brief Implements a random number generator.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+/*  This is the global seed used by the random number generator when the caller
+    has not provided a seed to either the RedRand32() or RedRand64() functions.
+*/
+static uint64_t ullGlobalRandomNumberSeed;
+
+/*  Whether the above seed has been initialized.
+*/
+static bool fGlobalSeedInited;
+
+
+/** @brief Set the global seed used by the random number generator.
+
+    The global seed gets used when RedRand64() or RedRand32() are called with
+    a NULL seed argument.
+
+    @param ullSeed  The value to use as the global RNG seed.
+*/
+void RedRandSeed(
+    uint64_t ullSeed)
+{
+    ullGlobalRandomNumberSeed = ullSeed;
+    fGlobalSeedInited = true;
+}
+
+
+/** @brief Generate a 64-bit pseudo-random number.
+
+    The period of this random number generator is 2^64 (1.8 x 1019).  These
+    parameters are the same as the default one-stream SPRNG lcg64 generator and
+    it satisfies the requirements for a maximal period.
+
+    The tempering value is used and an AND mask and is specifically selected to
+    favor the distribution of lower bits.
+
+    @param pullSeed A pointer to the seed to use.  Set this value to NULL to
+                    use the internal global seed value.
+
+    @return A pseudo-random number in the range [0, UINT64_MAX].
+*/
+uint64_t RedRand64(
+    uint64_t       *pullSeed)
+{
+    const uint64_t  ullA = UINT64_SUFFIX(2862933555777941757);
+    const uint64_t  ullC = UINT64_SUFFIX(3037000493);
+    const uint64_t  ullT = UINT64_SUFFIX(4921441182957829599);
+    uint64_t        ullN;
+    uint64_t       *pullSeedPtr;
+    uint64_t        ullLocalSeed;
+
+    if(pullSeed != NULL)
+    {
+        ullLocalSeed = *pullSeed;
+        pullSeedPtr = pullSeed;
+    }
+    else
+    {
+        if(!fGlobalSeedInited)
+        {
+            /*  Unfortunately, the Reliance Edge OS services don't give us much
+                to work with to initialize the global seed.  There is no entropy
+                abstraction, no tick count abstraction, and the timestamp
+                abstraction uses an opaque type which is not guaranteed to be an
+                integer.  The best we can do is use the RTC.
+
+                Tests using the RNG should be supplying a seed anyway, for
+                reproducibility.
+            */
+            RedRandSeed((uint64_t)RedOsClockGetTime());
+        }
+
+        ullLocalSeed = ullGlobalRandomNumberSeed;
+        pullSeedPtr = &ullGlobalRandomNumberSeed;
+    }
+
+    ullN = (ullLocalSeed * ullA) + ullC;
+
+    *pullSeedPtr = ullN;
+
+    /*  The linear congruential generator used above produces good psuedo-random
+        64-bit number sequences, however, as with any LCG, the period of the
+        lower order bits is much shorter resulting in alternately odd/even pairs
+        in bit zero.
+
+        The result of the LGC above is tempered below with a series of XOR and
+        shift operations to produce a more acceptable equidistribution of bits
+        throughout the 64-bit range.
+    */
+    ullN ^= (ullN >> 21U) & ullT;
+    ullN ^= (ullN >> 43U) & ullT;
+    ullN ^= (ullN << 23U) & ~ullT;
+    ullN ^= (ullN << 31U) & ~ullT;
+
+    return ullN;
+}
+
+
+/** @brief Generate a 32-bit pseudo-random number.
+
+    @note   The 32-bit random number generator internally uses the 64-bit random
+            number generator, returning the low 32-bits of the pseudo-random
+            64-bit value.
+
+    @param pulSeed  A pointer to the seed to use.  Set this value to NULL to use
+                    the internal global seed value.
+
+    @return A pseudo-random number in the range [0, UINT32_MAX].
+*/
+uint32_t RedRand32(
+    uint32_t   *pulSeed)
+{
+    uint64_t    ullN;
+
+    if(pulSeed != NULL)
+    {
+        uint64_t ullLocalSeed;
+
+        ullLocalSeed = *pulSeed;
+        ullN = RedRand64(&ullLocalSeed);
+        *pulSeed = (uint32_t)ullLocalSeed;
+    }
+    else
+    {
+        ullN = RedRand64(NULL);
+    }
+
+    return (uint32_t)ullN;
+}
+