├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── COPYING ├── README.md ├── RELNOTES.md ├── code-of-conduct.txt ├── doc ├── MANUAL.txt ├── lethe.1 └── todo.txt ├── etc └── gustave-dore-dante-and-the-river-of-lethe.png └── src ├── .ivk ├── BIN_DIRS ├── FORGE_TYPES ├── Forgefile.hsl ├── PROJECTS ├── forgeutils ├── install.hsl └── toolset.hsl ├── lethe.h ├── lethe_drop.c ├── lethe_drop.h ├── lethe_error.c ├── lethe_error.h ├── lethe_ldist.c ├── lethe_ldist.h ├── lethe_libc.c ├── lethe_libc.h ├── lethe_mkpath.c ├── lethe_mkpath.h ├── lethe_option.c ├── lethe_option.h ├── lethe_random.c ├── lethe_random.h ├── lethe_strglob.c ├── lethe_strglob.h ├── lethe_types.h ├── main.c └── test ├── .ivk ├── Forgefile.hsl ├── dynrand ├── .ivk ├── Forgefile.hsl └── main.c └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | lib/* 3 | src/o/* 4 | src/.Forgefile-* 5 | src/test/o/* 6 | src/test/bin/* 7 | src/test/dynrand/o/* 8 | src/test/dynrand/lib/* 9 | src/DEV_PATH 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/test/cutest"] 2 | path = src/test/cutest 3 | url = https://github.com/rafael-santiago/cutest 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## CONTRIBUTING 2 | 3 | Firstly, thank you for contributing. You should consider the following before submitting any stuff: 4 | 5 | - Be honest; 6 | - Forks always must be from (master); 7 | - New stuff should be tested; 8 | - New stuff should be detailed and documented; 9 | - Bugfixes should be tested; 10 | - Memory leaks are bad, try always to eliminate it before any pull request; 11 | - If you added some feature that has some drawback or this is not so strong against some kind of attack(s), be fair by making it clear in documentation; 12 | - Security by obscurity is silly, thanks but no; 13 | - Details are awesome in a pull request; 14 | - The first idea is providing a new feature thinking in all supported platforms; 15 | - Speed is secondary; 16 | - Some observed points in K.I.S.S. and [Suckless](https://suckless.org) are awesome; 17 | 18 | The best way of knowing what could be done is reading [doc/todo.txt](https://github.com/rafael-santiago/lethe/blob/master/doc/todo.txt). 19 | 20 | ## Code style 21 | 22 | There is no absolute truth (a.k.a unicorn) when talking about code style, anyway, this is the truth that I have 23 | been taking into consideration here. 24 | 25 | - Comments are good; 26 | - Tests are awesome (use the testing [library](https://github.com/rafael-santiago/cutest) used by the project); 27 | - If your test need a human to drive it, sorry but your test is a kind of crap; 28 | - We like C (C, not C with sugar or anything similar); 29 | - Do not use 'func()' when prototyping use 'func(void)'; 30 | - Arrays as function parameters are not welcome, use pointers; 31 | - Pointers are good. Taking into consideration that you are a fluent and experienced C programmer; 32 | - The project should be the most self-contained possible; 33 | - Git submodules are ok; 34 | - This project is GPLv2. Always include the copyright disclaimer in new code files; 35 | - Respect the credit from other people; 36 | - Give credit to other people; 37 | - Give credit to yourself; 38 | 39 | ## Code constructions 40 | 41 | Always include the statmements between ``{ ... }``. 42 | 43 | This is considered bad here: 44 | 45 | ```c 46 | if (x < y) do_stuf(); 47 | 48 | if (x < y) 49 | do_this(); 50 | else 51 | do_that(); 52 | ``` 53 | 54 | Macros are ok, however, "undef" it when it is not necessary anymore: 55 | 56 | ```c 57 | #define scoped_left_sh(x, s) ( (x) << (s) ) | ( (x) >> ( (sizeof(x) << 3) - (s) ) ) 58 | 59 | ... 60 | 61 | #undef scoped_left_sh 62 | ``` 63 | 64 | When passing a string pass its size besides assuming it as a null terminated string. 65 | 66 | When commenting some code use the following format: 67 | 68 | ```c 69 | // INFO(your name, or as people know you): This is an information. 70 | 71 | // WARN(your name, or as people know you): This is a warning. 72 | 73 | // FIXME(your name, or as people know you): I do not know how to solve it. 74 | 75 | // BUG(your name, or as people know you): I found a bug here, it should be fixed. 76 | 77 | // TODO(your name, or as people know you): To do item. 78 | 79 | // TIP(your name, or as people know you): You are giving the tips for people understand 80 | // some craziness, weird code chunk. 81 | ``` 82 | 83 | Static functions even being static must be prototyped at the beginning of the implementation file. 84 | 85 | Avoid using double quotes when including files in C implementation stuff (local includes). Headers 86 | and implemenation files should be relative to the toplevel src subdirectory. 87 | 88 | Gotos are ok if it always move forward but never ever backward. 89 | 90 | Avoid capital letters in function and variable names. 91 | 92 | C Defines: 93 | 94 | - while constants must be in upper case; 95 | - while macros must be in lower case; 96 | - while a DSL statement must be in upper case; 97 | 98 | Use ``lethe_memcmp``, ``lethe_memcpy``, ``lethe_memset`` and ``lethe_stat`` in replacement to ``memcmp``, ``memcpy``, ``memset`` and ``stat`` respectively. 99 | Those functions are defined in ``lethe_libc.h`` but if you has included ``lethe_types.h`` you indirectly has included ``lethe_libc.h`` too. 100 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafael-santiago/lethe/aa3275351d48b5c68d748fa4bdaca822cf32816d/README.md -------------------------------------------------------------------------------- /RELNOTES.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafael-santiago/lethe/aa3275351d48b5c68d748fa4bdaca822cf32816d/RELNOTES.md -------------------------------------------------------------------------------- /code-of-conduct.txt: -------------------------------------------------------------------------------- 1 | Contributor Covenant Code of Conduct 2 | 3 | Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | 25 | Examples of unacceptable behavior by participants include: 26 | 27 | 28 | * The use of sexualized language or imagery and unwelcome sexual attention or 29 | advances 30 | * Trolling, insulting/derogatory comments, and personal or political attacks 31 | * Public or private harassment 32 | * Publishing others private information, such as a physical or electronic 33 | address, without explicit permission 34 | * Other conduct which could reasonably be considered inappropriate in a 35 | professional setting 36 | 37 | 38 | Our Responsibilities 39 | 40 | Project maintainers are responsible for clarifying the standards of acceptable 41 | behavior and are expected to take appropriate and fair corrective action in 42 | response to any instances of unacceptable behavior. 43 | 44 | Project maintainers have the right and responsibility to remove, edit, or 45 | reject comments, commits, code, wiki edits, issues, and other contributions 46 | that are not aligned to this Code of Conduct, or to ban temporarily or 47 | permanently any contributor for other behaviors that they deem inappropriate, 48 | threatening, offensive, or harmful. 49 | 50 | Scope 51 | 52 | This Code of Conduct applies both within project spaces and in public spaces 53 | when an individual is representing the project or its community. Examples of 54 | representing a project or community include using an official project e-mail 55 | address, posting via an official social media account, or acting as an appointed 56 | representative at an online or offline event. Representation of a project may be 57 | further defined and clarified by project maintainers. 58 | 59 | Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported by contacting the project team at . All 63 | complaints will be reviewed and investigated and will result in a response that 64 | is deemed necessary and appropriate to the circumstances. The project team is 65 | obligated to maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted separately. 67 | 68 | Project maintainers who do not follow or enforce the Code of Conduct in good 69 | faith may face temporary or permanent repercussions as determined by other 70 | members of the project leadership. 71 | 72 | Attribution 73 | 74 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | For answers to common questions about this code of conduct, see 78 | https://www.contributor-covenant.org/faq 79 | 80 | -------------------------------------------------------------------------------- /doc/MANUAL.txt: -------------------------------------------------------------------------------- 1 | L e t h e t o o l u s e r' s m a n u a l 2 | ============================================== 3 | 4 | Let fancy still in my sense in Lethe steep; 5 | If it be thus to dream, still let me sleep! 6 | Lethe, the river of oblivion, rolls her watery labyrinth, 7 | which whoso drinks forgets both joy and grief 8 | O sister, mother, wife. 9 | Sweet Lethe is my life. 10 | I am never, never, never coming home! 11 | 12 | -- Willian Shakespeare [Twelfth Night], John Milton [Paradise Lost], Sylvia Plath [Amnesiac] 13 | 14 | Lethe is a tiny and well-simple tool for data wiping. In Greek mythology Lethe is one of the five rivers from Hades 15 | underworld. According to the myth, who touch, drink or even take a single drop from this river will experience 16 | forgetfulness. 17 | 18 | This is my implementation of a suggestion given by the worldwide known cryptographer and information security specialist 19 | Bruce Schneier in his book "Applied Cryptography", about destroying information: 20 | 21 | "Most commercial programs that claim to implement the DoD standard overwrite three times: first with all ones, then with 22 | all zeros and finally with a repeating one-zero pattern. Given my general level of paranoia, I recommend overwriting a 23 | deleted file seven times: the first time with all ones, the second time with all zeros, and five times with a 24 | cryptographically secure pseudo-random sequence. Recent developments at the National Institute of Standards and Technology 25 | with electron-tunneling microscopes suggest even that might not be enough. Honestly, if your data is sufficiently valuable, 26 | assume that it is *impossible* to erase data completely off magnetic media. Burn or shred the media; it's cheaper to buy 27 | media new than lose your secrets." 28 | 29 | -- Bruce Schneier ["Applied Cryptography" 228-229 pp.] 30 | 31 | This book was written at 90's. DoD additionally states that: 32 | 33 | "The number of times an overwrite must be accomplished depends on the storage media, sometimes on its sensitivity, and 34 | sometimes on different DoD component requirements." 35 | 36 | -- National Computer Security Center ["A Guide to Understanding Data Rememberance in Automanted Information Systems"] 37 | 38 | Here, overwrite times are configured by the user. Anyway, I think that burn-and-shred advices given by Mr. Schneier should 39 | not be discarded. 40 | 41 | WARNING: This general DoD information destruction method does not work with flash based filesystems. It probably will not 42 | work with your smartphone. Do not use this tool for doing this! Maybe in future I may extend this code to give some kind 43 | of support for flash stuff too, but by now, I won't do it. 44 | 45 | Finally, I am not reponsible for any misuse of information or code here. I am not responsible for any possible damage, 46 | data destruction or loss (partial or total) done by this software, too. Who knows your babysitter! Use it at your own 47 | risk! 48 | 49 | How can I use Lethe as a command line tool? 50 | ------------------------------------------- 51 | 52 | Lethe is a well-simple tool. It works based on commands and command options. The general idea when using lethe from your 53 | command line is: 54 | 55 | lethe [options] 56 | 57 | Until now lethe has the commands listed in Table 1. 58 | 59 | +-------------+--------------------------------------+ 60 | | Command | Utility | 61 | |-------------+--------------------------------------| 62 | | drop | Removes files and directories | 63 | | help | Offers quick help guide for commands | 64 | | man | Manual reader | 65 | | version | Displays the tool version | 66 | +-------------+--------------------------------------+ 67 | Table 1: Current commands supported. 68 | 69 | The command drop 70 | ---------------- 71 | 72 | It removes files. Its synopsis is: 73 | 74 | lethe drop [options] 75 | 76 | Supposing you want to remove the file "thanks-for-nothing.txt" and all files containing "crimson-ballroom" in their names: 77 | 78 | you@Hades:~/tmp# lethe drop thanks-for-nothing.txt *crimson-ballroom* 79 | you@Hades:~/tmp# _ 80 | 81 | 82 | By default Lethe will ask you if do you really want to delete a found file. If you prefer skipping all possible 83 | confirmations you must use --ask-me-nothing bool option. 84 | 85 | you@Hades:~/tmp# lethe drop *make-it-alright* televison-addict* \ 86 | > --ask-me-nothing 87 | you@Hades:~/tmp# _ 88 | 89 | 90 | The removing process basically consists on repeated overwrites and renames passes. By default you have five overwrite 91 | passes and ten renaming passes. In order to change those default values use the options --overwrite-passes= and/or 92 | --rename-passes=. The total of passes must be one at least or a greater value. Let's use 200 renaming and 1000 93 | overwrites passes. 94 | 95 | you@Hades:~/tmp# lethe drop * --ask-me-nothing \ 96 | > --overwrite-passes=1000 --rename-passes=200 97 | you@Hades:~/tmp# _ 98 | 99 | WARNING: The given sample command can be dangerous depending on where you are testing it. Lethe always recursively removes 100 | any found directory. I meant that it will be emptied by using an implicit "*" glob pattern. Be careful when using this 101 | tool. 102 | 103 | The renaming and overwriting stuff uses random data. By default those data is provided by the internal Lethe's randomizer. 104 | If you prefer providing your own randomizer you need to use the option --dyn-randomizer=:. 105 | 106 | Supposing you have a dynamic local library called "my-awesome-csprng.so". This library has the function "mac" 107 | well-exported and it is "your awesome csprng": 108 | 109 | you@Hades:~/tmp# lethe drop [AB]-REPORTS-*-1995.DAT \ 110 | > --dyn-randomizer=my-awesome-csprng.so:mac --ask-me-nothing 111 | you@Hades:~/tmp# _ 112 | 113 | The C-prototype for a Lethe randomizer implementation is: 114 | 115 | unsigned char randomizer(void); 116 | 117 | If during a drop process you regret about your remove choices, try to hit CTRL + c as soon as possible. It will prevent of 118 | removing more files by aborting the application. You can also interrupt lethe by sending a SIGINT or SIGTERM to its process. 119 | 120 | The command man 121 | --------------- 122 | 123 | It shows the content of this manual at your terminal screen by using your environment pager: 124 | 125 | you@Hades:~/tmp# lethe man 126 | 127 | The command help 128 | ---------------- 129 | 130 | Nothing special. It only gives you a quick command synopsis. You only need to provide as option a command as the help 131 | topic you are looking for: 132 | 133 | you@Hades:~/tmp# lethe help drop 134 | 135 | The command version 136 | ------------------- 137 | 138 | It reports the version of your Lethe binary: 139 | 140 | you@Hades:~/tmp# lethe version 141 | 142 | But you can also use --version if you prefer: 143 | 144 | you@Hades:~/tmp# lethe --version 145 | -------------------------------------------------------------------------------- /doc/lethe.1: -------------------------------------------------------------------------------- 1 | .TH lethe 1 "February 26, 2020" "version v1" "USER COMMANDS" 2 | .SH NAME 3 | lethe \- a data wiping tool. 4 | .SH SYNOPSIS 5 | .B lethe [\fIOPTIONS\fR] 6 | .SH DESCRIPTION 7 | \fILethe\fR is a tiny and well-simple tool for data wiping. In Greek mythology Lethe is one of the five rivers from Hades 8 | underworld. According to the myth, who touch, drink or even take a single drop from this river will experience 9 | forgetfulness. 10 | .PP 11 | This is my implementation of a suggestion given by the worldwide known cryptographer and information security specialist 12 | Bruce Schneier in his book "Applied Cryptography", about destroying information: 13 | .PP 14 | .RS 15 | "Most commercial programs that claim to implement the DoD standard overwrite three times: first with all ones, then with all zeros and finally with a repeating one-zero pattern. Given my general level of paranoia, I recommend overwriting a deleted file seven times: the first time with all ones, the second time with all zeros, and five times with a cryptographically secure pseudo-random sequence. Recent developments at the National Institute of Standards and Technology with electron-tunneling microscopes suggest even that might not be enough. Honestly, if your data is sufficiently valuable, assume that it is *impossible* to erase data completely off magnetic media. Burn or shred the media; it's cheaper to buy media new than lose your secrets." 16 | 17 | -- Bruce Schneier ["Applied Cryptography" 228-229 pp.] 18 | .RE 19 | .PP 20 | This book was written at 90's. DoD additionally states that: 21 | .PP 22 | .RS 23 | "The number of times an overwrite must be accomplished depends on the storage media, sometimes on its sensitivity, and sometimes on different DoD component requirements." 24 | 25 | -- National Computer Security Center ["A Guide to Understanding Data Rememberance in Automanted Information Systems"] 26 | .RE 27 | .PP 28 | Here, overwrite times are configured by the user. Anyway, I think that burn-and-shred advices given by Mr. Schneier should 29 | not be discarded. 30 | 31 | To learn more deeply about each implemented \fIlethe\fR command you should read the user's manual probably installed in: 32 | 33 | \fI/usr/local/share/lethe/doc/MANUAL\fR (or equivalent on your system). 34 | 35 | If you prefer you can run the command \fIlethe man\fR. 36 | 37 | .PP 38 | \fIWARNING\fR: This general DoD information destruction method does not work with flash based filesystems. It probably will not 39 | work with your smartphone. Do not use this tool for doing this! Maybe in future I may extend this code to give some kind 40 | of support for flash stuff too, but by now, I won't do it. 41 | .PP 42 | Finally, I am not reponsible for any misuse of information or code here. I am not responsible for any possible damage, 43 | data destruction or loss (partial or total) done by this software, too. Who knows your babysitter! Use it at your own 44 | risk! 45 | .PP 46 | .SH EXIT STATUS 47 | If the execution succeeds \fIlethe\fR returns zero, otherwise non zero values will be returned. 48 | 49 | .PP 50 | .SH 51 | SEE ALSO 52 | .TP 53 | \fI/usr/local/share/lethe/doc\fR (or equivalent on your system). You can also run \fIlethe man\fR instead. 54 | .PP 55 | .SH 56 | HOMEPAGE 57 | .TP 58 | https://github.com/rafael-santiago/lethe 59 | .SH AUTHOR 60 | Rafael Santiago 61 | -------------------------------------------------------------------------------- /doc/todo.txt: -------------------------------------------------------------------------------- 1 | x (A) Create RELNOTES.md. +Documentation 2 | x (A) Find out a way of ensuring/testing lethe_drop() without Foremost. +Tests 3 | x (A) Offer a way of indicating address to memcmp, memset and memcpy functions. +SecurityWorries,+ParanoidCare,+Feature 4 | x (B) Adding CONTRIBUTING.md 5 | x (B) Detect path to device for tests against Foremost. +Tests 6 | x (A) Add install instructions to 'README.md'. +Documentation 7 | x (A) Export 'C:\lethe\bin' path on Windows. +Installer,+Build 8 | x (A) Test --dyn-randomizer. +Tests 9 | x (A) Implement tool system tests. +Tests 10 | x (B) Test build on Solaris. Adjust anything if necessary. +Build 11 | x (B) Test build on NetBSD. Adjust anything if necessary. +Build 12 | x (B) Test build on MINIX. Adjust anything if necessary. +Build 13 | x (B) Write 'doc/MANUAL.txt'. +Documentation 14 | x (B) Write 'doc/man/lethe.1'. +Documentation 15 | x (C) Install stuff for tool. +Build 16 | x (A) Adjust tests for Windows. +Tests 17 | x (A) Windows adjustments. +Feature 18 | x (A) Test build on FreeBSD. +Build 19 | x (A) Write tool's README. +Documentation 20 | x (A) Test data oblivion without removing file. +Tests 21 | x (A) Implement tool entry point. +Feature 22 | x (B) Write some 'README'. +Documentation 23 | x (A) Finish up 'lethe_drop.c' implementation. +Feature 24 | x (A) Implement tests. +Tests 25 | x (A) Find out a way of automating some recovering process (**easily***) and integrating it in tests. +Tests 26 | -------------------------------------------------------------------------------- /etc/gustave-dore-dante-and-the-river-of-lethe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafael-santiago/lethe/aa3275351d48b5c68d748fa4bdaca822cf32816d/etc/gustave-dore-dante-and-the-river-of-lethe.png -------------------------------------------------------------------------------- /src/.ivk: -------------------------------------------------------------------------------- 1 | --forgefiles=Forgefile.hsl --Forgefile-projects=lethe-bootstrap --includes=. --obj-output-dir=o 2 | -------------------------------------------------------------------------------- /src/BIN_DIRS: -------------------------------------------------------------------------------- 1 | ../lib 2 | ../bin 3 | -------------------------------------------------------------------------------- /src/FORGE_TYPES: -------------------------------------------------------------------------------- 1 | lib 2 | tool 3 | -------------------------------------------------------------------------------- /src/Forgefile.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 by Rafael Santiago 3 | # 4 | # Use of this source code is governed by GPL-v2 license that can 5 | # be found in the COPYING file. 6 | # 7 | 8 | include forgeutils/toolset.hsl 9 | include forgeutils/install.hsl 10 | include ~/fsutil.hsl 11 | 12 | local var src type list; 13 | local var includes type list; 14 | local var cflags type list; 15 | local var libraries type list; 16 | local var ldflags type list; 17 | 18 | local var deps type string; 19 | 20 | local var curr_toolset type string; 21 | 22 | # -- lethe tool build 23 | 24 | project lethe-tool : toolset $curr_toolset : 25 | dependencies $deps : $src, $includes, $cflags, $libraries, $ldflags, "lethe"; 26 | 27 | lethe-tool.preloading() { 28 | $curr_toolset = get_app_toolset(); 29 | } 30 | 31 | lethe-tool.prologue() { 32 | setup_lists(); 33 | $deps = get_c_cpp_deps(); 34 | } 35 | 36 | lethe-tool.epilogue() { 37 | run_tests(); 38 | } 39 | 40 | # -- liblethe build 41 | 42 | project liblethe : toolset $curr_toolset : 43 | dependencies $deps : $src, $includes, $cflags, $libraries, $ldflags, "liblethe.a"; 44 | 45 | liblethe.preloading() { 46 | $curr_toolset = get_lib_toolset(); 47 | $deps = get_c_cpp_deps(); 48 | } 49 | 50 | liblethe.prologue() { 51 | setup_lists(); 52 | $deps = get_c_cpp_deps(); 53 | var s type int; 54 | $s = 0; 55 | while ($s < $src.count()) { 56 | var impfile type string; 57 | $impfile = $src.item($s); 58 | if ($impfile.match(".*(main\\.c|lethe_option\\.c|lethe_ldist\\.c)$") == 1) { 59 | var filename type string; 60 | $filename = filenamefrompath($impfile); 61 | $filename.replace(".c$", ".o"); 62 | $filename = hefesto.sys.make_path("o", $filename); 63 | hefesto.sys.rm($filename); 64 | $src.del_index($s); 65 | } 66 | $s = $s + 1; 67 | } 68 | } 69 | 70 | liblethe.epilogue() { 71 | run_tests(); 72 | } 73 | 74 | local function run_tests() : result type none { 75 | if (hefesto.sys.last_forge_result() == 0) { 76 | if (hefesto.sys.cd("test") != 1) { 77 | hefesto.sys.echo("BUILD ERROR: Test directory not found!\n"); 78 | hefesto.project.abort(1); 79 | } 80 | var ftype type list; 81 | $ftype = hefesto.sys.get_option("forge-type"); 82 | var test_options type string; 83 | $test_options = "--forge-type=" + $ftype.item(0); 84 | var quick_tests type list; 85 | $quick_tests = hefesto.sys.get_option("quick-tests"); 86 | if ($quick_tests.count() > 0) { 87 | $test_options = $test_options + " --quick-tests"; 88 | } 89 | if (hefesto.sys.run("hefesto " + $test_options) != 0) { 90 | hefesto.project.abort(1); 91 | } 92 | hefesto.sys.cd(".."); 93 | } 94 | } 95 | 96 | local function setup_lists() : result type none { 97 | $src.ls(".*\\.c$"); 98 | $includes = hefesto.sys.get_option("includes"); 99 | $cflags = hefesto.sys.get_option("cflags"); 100 | $libraries = hefesto.sys.get_option("libraries"); 101 | $ldflags = hefesto.sys.get_option("ldflags"); 102 | if (hefesto.project.name() == "lethe-tool" && hefesto.sys.os_name() == "linux") { 103 | $ldflags.add_item("-ldl"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/PROJECTS: -------------------------------------------------------------------------------- 1 | liblethe 2 | lethe-tool 3 | -------------------------------------------------------------------------------- /src/forgeutils/install.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 by Rafael Santiago 3 | # 4 | # Use of this source code is governed by GPL-v2 license that can 5 | # be found in the COPYING file. 6 | # 7 | 8 | include ~/fsutil.hsl 9 | 10 | function installer() : result type none { 11 | var option type list; 12 | 13 | $option = hefesto.sys.get_option("install"); 14 | 15 | if ($option.count() > 0) { 16 | hefesto.project.abort(do_install()); 17 | } 18 | 19 | $option = hefesto.sys.get_option("uninstall"); 20 | 21 | if ($option.count() > 0) { 22 | hefesto.project.abort(do_uninstall()); 23 | } 24 | } 25 | 26 | local function do_install() : result type int { 27 | var src type list; 28 | 29 | if (hefesto.sys.os_name() != "windows") { 30 | $src.add_item("../doc/MANUAL.txt"); 31 | $src.add_item("../doc/lethe.1"); 32 | $src.add_item("../bin/lethe"); 33 | } else { 34 | $src.add_item("../doc/MANUAL.txt"); 35 | $src.add_item("../bin/lethe.exe"); 36 | } 37 | 38 | var dest type list; 39 | 40 | if (hefesto.sys.os_name() != "windows") { 41 | $dest.add_item("/usr/local/share/lethe/doc/MANUAL.txt"); 42 | $dest.add_item("/usr/local/man/man1/lethe.1"); 43 | $dest.add_item("/usr/local/bin/lethe"); 44 | } else { 45 | $dest.add_item("C:\\lethe\\doc\\MANUAL.txt"); 46 | $dest.add_item("C:\\lethe\\bin\\lethe.exe"); 47 | } 48 | 49 | var has_error type int; 50 | 51 | $has_error = 0; 52 | 53 | var s type int; 54 | 55 | $s = 0; 56 | while ($s < $src.count() && $has_error == 0) { 57 | var destpath type string; 58 | $destpath = $dest.item($s); 59 | $destpath = pathfromfilepath($destpath); 60 | if (isdir($destpath) == 0 && mktree($destpath) == 0) { 61 | $has_error = 0; 62 | hefesto.sys.echo("ERROR: Unable to create path '" + $destpath + "'.\n"); 63 | } else { 64 | if (hefesto.sys.cp($src.item($s), $dest.item($s)) != 1) { 65 | hefesto.sys.echo("ERROR: Unable to copy '" + $dest.item($s) + "'.\n"); 66 | $has_error = 1; 67 | } 68 | } 69 | $s = $s + 1; 70 | } 71 | 72 | if ($has_error == 0) { 73 | if (hefesto.sys.os_name() == "windows") { 74 | if (export_lethe_binary() != 0) { 75 | hefesto.sys.echo("WARN: Unable to export lethe's binary path. Do it yourself.\n"); 76 | } 77 | } 78 | hefesto.sys.echo("INFO: Installed.\n"); 79 | } else { 80 | hefesto.sys.echo("ERROR: Not installed.\n"); 81 | } 82 | 83 | result $has_error; 84 | } 85 | 86 | local function do_uninstall() : result type int { 87 | var burn_list type list; 88 | 89 | if (hefesto.sys.os_name() != "windows") { 90 | $burn_list.add_item("/usr/local/share/lethe/doc/MANUAL.txt"); 91 | $burn_list.add_item("/usr/local/share/lethe/doc"); 92 | $burn_list.add_item("/usr/local/share/lethe"); 93 | $burn_list.add_item("/usr/local/man/man1/lethe.1"); 94 | $burn_list.add_item("/usr/local/bin/lethe"); 95 | } else { 96 | $burn_list.add_item("C:\\lethe\\doc\\MANUAL.txt"); 97 | $burn_list.add_item("C:\\lethe\\bin\\lethe.exe"); 98 | $burn_list.add_item("C:\\lethe\\doc"); 99 | $burn_list.add_item("C:\\lethe\\bin"); 100 | $burn_list.add_item("C:\\lethe"); 101 | } 102 | 103 | var error_nr type int; 104 | 105 | $error_nr = 0; 106 | 107 | var b type int; 108 | 109 | $b = 0; 110 | 111 | while ($b < $burn_list.count()) { 112 | var filepath type string; 113 | $filepath = $burn_list.item($b); 114 | if (isdir($filepath)) { 115 | if (hefesto.sys.rmdir($filepath) != 0) { 116 | hefesto.sys.echo("ERROR: Unable to remove '" + $filepath + "'. Try to do it by hand.\n"); 117 | $error_nr = $error_nr + 1; 118 | } 119 | } else if (hefesto.sys.rm($filepath) != 0) { 120 | hefesto.sys.echo("ERROR: Unable to remove '" + $filepath + "'. Try to do it by hand.\n"); 121 | $error_nr = $error_nr + 1; 122 | } 123 | $b = $b + 1; 124 | } 125 | 126 | if (hefesto.sys.os_name() == "windows") { 127 | if (unexport_lethe_binary() != 0) { 128 | hefesto.sys.echo("WARN: Unable to remove lethe's binary path from your PATH environment variable. " + 129 | "Do it yourself.\n"); 130 | } 131 | } 132 | 133 | var has_error type int; 134 | 135 | if ($error_nr == $burn_list.count()) { 136 | hefesto.sys.echo("\nINFO: Nothing was removed. Uninstall has failed.\n"); 137 | $has_error = 1; 138 | } else if ($error_nr == 0) { 139 | hefesto.sys.echo("INFO: Uninstalled.\n"); 140 | $has_error = 0; 141 | } else { 142 | hefesto.sys.echo("\nINFO: Uninstall was done, but some files were kept. Remove them by yourself.\n"); 143 | $has_error = 0; 144 | } 145 | 146 | result 0; 147 | } 148 | 149 | local function export_lethe_binary() : result type int { 150 | var has_error type int; 151 | $has_error = 1; 152 | 153 | var path type string; 154 | $path = hefesto.sys.env("WINREG:HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\PATH"); 155 | 156 | if ($path.len() > 0) { 157 | if ($path.match(" ;C:\\\\lethe\\\\bin;") == 0) { 158 | $path = $path + " ;C:\\lethe\\bin;"; 159 | $has_error = hefesto.sys.setenv("WINREG:HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\" + 160 | "PATH", $path); 161 | } else { 162 | $has_error = 0; 163 | } 164 | } else { 165 | hefesto.sys.echo("ERROR: Your environment PATH variable seems to be NULL.\n"); 166 | } 167 | 168 | result $has_error; 169 | } 170 | 171 | local function unexport_lethe_binary() : result type int { 172 | var has_error type int; 173 | $has_error = 0; 174 | 175 | var path type string; 176 | $path = hefesto.sys.env("WINREG:HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\PATH"); 177 | 178 | if ($path.match(" ;C:\\\\lethe\\\\bin;") == 1) { 179 | $path.replace(" ;C:\\\\lethe\\\\bin;", ""); 180 | $has_error = hefesto.sys.setenv("WINREG:HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\PATH", 181 | $path); 182 | } 183 | 184 | result $has_error; 185 | } 186 | -------------------------------------------------------------------------------- /src/forgeutils/toolset.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 by Rafael Santiago 3 | # 4 | # Use of this source code is governed by GPL-v2 license that can 5 | # be found in the COPYING file. 6 | # 7 | 8 | include ~/toolsets/gcc/gcc-lib.hsl 9 | include ~/toolsets/gcc/gcc-app.hsl 10 | include ~/toolsets/clang/clang-lib.hsl 11 | include ~/toolsets/clang/clang-app.hsl 12 | include ~/toolsets/null/null.hsl 13 | include ~/toolsets/common/utils/lang/c/dependency_scanner.hsl 14 | include ~/lsutil.hsl 15 | 16 | function get_app_toolset() : result type string { 17 | result get_toolset() + "-c-app"; 18 | } 19 | 20 | function get_lib_toolset() : result type string { 21 | result get_toolset() + "-c-lib"; 22 | } 23 | 24 | local function get_toolset() : result type string { 25 | var option type list; 26 | var return type string; 27 | 28 | $option = hefesto.sys.get_option("toolset"); 29 | 30 | if ($option.count() > 0) { 31 | $return = $option.item(0); 32 | $return.replace("-.*$", ""); 33 | result $return; 34 | } 35 | 36 | if (has_gcc()) { 37 | $return = "gcc"; 38 | } else if (has_clang()) { 39 | $return = "clang"; 40 | } else { 41 | hefesto.sys.echo("ERROR: You must have gcc or clang well installed in your system.\n" + 42 | " Install one of them and try again later.\n"); 43 | hefesto.project.abort(1); 44 | } 45 | 46 | result $return; 47 | } 48 | 49 | local function has_gcc() : result type int { 50 | result (execbkcmd("gcc --version") == 0); 51 | } 52 | 53 | local function has_clang() : result type int { 54 | result (execbkcmd("clang --version") == 0); 55 | } 56 | 57 | local function execbkcmd(cmdline type string) : result type int { 58 | if (hefesto.sys.os_name() != "windows") { 59 | $cmdline = $cmdline + " > /dev/null 2>&1"; 60 | } else { 61 | $cmdline = $cmdline + " > nul 2>&1"; 62 | } 63 | 64 | result hefesto.sys.run($cmdline); 65 | } 66 | 67 | project lethe-bootstrap : toolset "no-tool-any-set" : 0; 68 | 69 | lethe-bootstrap.prologue() { 70 | installer(); 71 | var option type list; 72 | 73 | $option = hefesto.sys.get_option("forge-type"); 74 | 75 | if ($option.count() == 0) { 76 | var ftypes type string; 77 | $ftypes = ls2str(hefesto.sys.lines_from_file("FORGE_TYPES", ".*")); 78 | $ftypes.replace("\n", ", "); 79 | $ftypes.replace(", $", "."); 80 | hefesto.sys.echo("BUILD ERROR: You need to indicate the forge type. It can be: " + $ftypes + "\n"); 81 | hefesto.project.abort(1); 82 | } 83 | 84 | var data type string; 85 | $data = $option.item(0); 86 | 87 | var forge_types type list; 88 | $forge_types = hefesto.sys.lines_from_file("FORGE_TYPES", ".*"); 89 | 90 | var projects type list; 91 | $projects = hefesto.sys.lines_from_file("PROJECTS", ".*"); 92 | 93 | var bin_dirs type list; 94 | $bin_dirs = hefesto.sys.lines_from_file("BIN_DIRS", ".*"); 95 | 96 | var i type int; 97 | $i = $forge_types.index_of($data); 98 | 99 | if ($i == -1) { 100 | $ftypes = ls2str(hefesto.sys.lines_from_file("FORGE_TYPES", ".*")); 101 | $ftypes.replace("\n", ", "); 102 | $ftypes.replace(", $", "."); 103 | hefesto.sys.echo("BUILD ERROR: Unknown forge type : '" + $data + "'.\n" + 104 | " You need to indicate a valid forge type. It can be: " + $ftypes + "\n"); 105 | hefesto.project.abort(1); 106 | } 107 | 108 | var bin_dir type string; 109 | $bin_dir = $bin_dirs.item($i); 110 | 111 | $data = $projects.item($i); 112 | hefesto.sys.forge($data, "Forgefile.hsl", hefesto.project.cmdline() + " --bin-output-dir=" + $bin_dir); 113 | } 114 | -------------------------------------------------------------------------------- /src/lethe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_H 9 | #define LETHE_LETHE_H 1 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/lethe_drop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #if defined(_WIN32) 24 | # include 25 | # include 26 | #endif 27 | 28 | int g_lethe_drop_rename_nr = 10; 29 | 30 | int g_lethe_drop_overwrite_nr = 1; 31 | 32 | static char g_lethe_allowed_fname_symbols[] = { 33 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 34 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 35 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 36 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 37 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-', '_' 38 | }; 39 | 40 | static size_t g_lethe_allowed_fname_symbols_nr = sizeof(g_lethe_allowed_fname_symbols) / 41 | sizeof(g_lethe_allowed_fname_symbols[0]); 42 | 43 | static void get_rnd_databuf(unsigned char *buf, const size_t size, lethe_randomizer get_byte); 44 | 45 | static void get_rnd_filename(char *filename, lethe_randomizer get_byte); 46 | 47 | #if defined(__unix__) 48 | static int fdoblivion(int fd, const size_t fsize, lethe_randomizer get_byte); 49 | #elif defined(_WIN32) 50 | static int fdoblivion(HANDLE fd, const size_t fsize, lethe_randomizer get_byte); 51 | #else 52 | # error Some code wanted. 53 | #endif 54 | 55 | static int lethe_remove(const char *filepath, lethe_randomizer get_byte); 56 | 57 | static int lethe_do_drop(const char *filepath, const lethe_drop_type dtype, lethe_randomizer get_byte); 58 | 59 | #if defined(_WIN32) 60 | static size_t get_blksize(const char *path); 61 | #endif 62 | 63 | int lethe_drop_pattern(const char *pattern, const lethe_drop_type dtype, ...) { 64 | DIR *dir = NULL; 65 | int has_error = 1; 66 | char cwd[4096], old_cwd[4096]; 67 | size_t cwd_size, filename_size; 68 | struct dirent *dt; 69 | char fullpath[4096], *filename; 70 | const char *p; 71 | struct stat st; 72 | lethe_randomizer get_byte = lethe_default_randomizer; 73 | va_list ap; 74 | int drop_nr = 0; 75 | int has_ldir = 0; 76 | 77 | if (pattern == NULL) { 78 | goto lethe_drop_pattern_epilogue; 79 | } 80 | 81 | if (dtype & kLetheCustomRandomizer) { 82 | va_start(ap, dtype); 83 | get_byte = va_arg(ap, lethe_randomizer); 84 | } 85 | 86 | if (lethe_stat(pattern, &st) == 0) { 87 | // INFO(Rafael): Literal file names will directly be removed. 88 | if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { 89 | if ((has_error = lethe_do_drop(pattern, dtype, get_byte)) == 0) { 90 | drop_nr = 1; 91 | } 92 | } 93 | goto lethe_drop_pattern_epilogue; 94 | } 95 | 96 | // INFO(Rafael): It is a glob pattern. Let's try to match it. 97 | 98 | lethe_set_last_filepath("."); 99 | 100 | if (getcwd(cwd, sizeof(cwd) - 1) == NULL) { 101 | lethe_set_error_code(kLetheErrorUnableToAccess); 102 | goto lethe_drop_pattern_epilogue; 103 | } 104 | 105 | cwd_size = strlen(cwd); 106 | 107 | #if defined(__unix__) 108 | has_ldir = (strstr(pattern, "/") != NULL); 109 | #elif defined(_WIN32) 110 | has_ldir = (strstr(pattern, "/") != NULL || strstr(pattern, "\\") != NULL); 111 | #else 112 | # error Some code wanted. 113 | #endif 114 | 115 | lethe_memset(fullpath, 0, sizeof(fullpath)); 116 | 117 | if (has_ldir) { 118 | // INFO(Rafael): It seems to have some literal directory indication. 119 | p = pattern + strlen(pattern) - 1; 120 | #if defined(__unix__) 121 | while (p != pattern && *p != '/') { 122 | p--; 123 | } 124 | #elif defined(_WIN32) 125 | while (p != pattern && *p != '\\' && 126 | *p != '/') { 127 | p--; 128 | } 129 | #else 130 | # error Some code wanted. 131 | #endif 132 | lethe_memcpy(fullpath, pattern, p - pattern); 133 | if (lethe_stat(fullpath, &st) != 0 || !S_ISDIR(st.st_mode)) { 134 | p = pattern; 135 | snprintf(fullpath, sizeof(fullpath) - 1, "%s", cwd); 136 | has_ldir = 0; 137 | } else { 138 | snprintf(old_cwd, sizeof(old_cwd) - 1, "%s", cwd); 139 | snprintf(cwd, sizeof(cwd) - 1, "%s", fullpath); 140 | #if defined(__unix__) 141 | p += (*p == '/'); 142 | #elif defined(_WIN32) 143 | p += (*p == '\\' || *p == '/'); 144 | #else 145 | # error Some code wanted. 146 | #endif 147 | } 148 | } else { 149 | p = pattern; 150 | snprintf(fullpath, sizeof(fullpath) - 1, "%s", cwd); 151 | } 152 | 153 | lethe_set_last_filepath(fullpath); 154 | 155 | if ((dir = opendir(fullpath)) == NULL) { 156 | lethe_set_error_code(kLetheErrorUnableToAccess); 157 | goto lethe_drop_pattern_epilogue; 158 | } 159 | 160 | has_error = 0; 161 | 162 | while (!has_error && (dt = readdir(dir)) != NULL) { 163 | filename = dt->d_name; 164 | 165 | if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { 166 | continue; 167 | } 168 | 169 | filename_size = strlen(filename); 170 | 171 | if (lethe_strglob(filename, p)) { 172 | lethe_set_last_filepath(filename); 173 | 174 | if (lethe_mkpath(fullpath, sizeof(fullpath), cwd, cwd_size, filename, filename_size) == NULL) { 175 | lethe_set_error_code(kLetheErrorUnableToAccess); 176 | has_error = 1; 177 | goto lethe_drop_pattern_epilogue; 178 | } 179 | 180 | if ((has_error = lethe_do_drop(fullpath, dtype, get_byte)) == 0) { 181 | drop_nr++; 182 | } 183 | } 184 | } 185 | 186 | lethe_drop_pattern_epilogue: 187 | 188 | if (dir != NULL) { 189 | closedir(dir); 190 | } 191 | 192 | if (!has_ldir) { 193 | chdir(cwd); 194 | } else { 195 | chdir(old_cwd); 196 | } 197 | 198 | if (dtype & kLetheCustomRandomizer) { 199 | va_end(ap); 200 | } 201 | 202 | if (drop_nr == 0) { 203 | lethe_set_error_code(kLetheErrorNothingToDrop); 204 | has_error = 1; 205 | } 206 | 207 | return has_error; 208 | } 209 | 210 | int lethe_set_rename_nr(const int value) { 211 | int has_error = 1; 212 | 213 | if (value > 0) { 214 | g_lethe_drop_rename_nr = value; 215 | has_error = 0; 216 | } 217 | 218 | return has_error; 219 | } 220 | 221 | int lethe_set_overwrite_nr(const int value) { 222 | int has_error = 1; 223 | 224 | if (value > 0) { 225 | g_lethe_drop_overwrite_nr = value; 226 | has_error = 0; 227 | } 228 | 229 | return has_error; 230 | } 231 | 232 | #if defined(_WIN32) 233 | static size_t get_blksize(const char *path) { 234 | char volume[10], ntdevpath[4096], devpath[4096], *vp; 235 | ssize_t blksize = 0; 236 | HANDLE devhandle = INVALID_HANDLE_VALUE; 237 | DISK_GEOMETRY disk_geo_ctx; 238 | DWORD dummy; 239 | 240 | if (GetVolumePathName(path, volume, sizeof(volume)) == 0) { 241 | goto get_blksize_epilogue; 242 | } 243 | 244 | if ((vp = strstr(volume, "\\")) != NULL) { 245 | *vp = 0; 246 | } 247 | 248 | if (QueryDosDevice(volume, ntdevpath, sizeof(ntdevpath)) == 0) { 249 | goto get_blksize_epilogue; 250 | } 251 | 252 | snprintf(devpath, sizeof(devpath) - 1, "\\\\.\\GLOBALROOT\\%s", ntdevpath); 253 | 254 | if ((devhandle = CreateFile(devpath, 0, 255 | FILE_SHARE_READ | FILE_SHARE_WRITE, 256 | NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { 257 | goto get_blksize_epilogue; 258 | } 259 | 260 | if (DeviceIoControl(devhandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, 261 | NULL, 0, 262 | &disk_geo_ctx, sizeof(disk_geo_ctx), &dummy, NULL)) { 263 | blksize = disk_geo_ctx.BytesPerSector; 264 | } 265 | 266 | get_blksize_epilogue: 267 | 268 | if (devhandle != INVALID_HANDLE_VALUE) { 269 | CloseHandle(devhandle); 270 | } 271 | 272 | return blksize; 273 | } 274 | #endif 275 | 276 | static int lethe_do_drop(const char *filepath, const lethe_drop_type dtype, lethe_randomizer get_byte) { 277 | int has_error = 1; 278 | struct stat st; 279 | int blkpad; 280 | int c; 281 | int o; 282 | #if defined(__unix__) 283 | int fd; 284 | #elif defined(_WIN32) 285 | size_t blksize; 286 | HANDLE fd; 287 | #endif 288 | 289 | if (filepath == NULL) { 290 | lethe_set_error_code(kLetheErrorNullFile); 291 | goto lethe_do_drop_epilogue; 292 | } 293 | 294 | if ((dtype & kLetheDataOblivion) == 0 && 295 | (dtype & kLetheFileRemove) == 0) { 296 | lethe_set_error_code(kLetheErrorNullDropType); 297 | goto lethe_do_drop_epilogue; 298 | } 299 | 300 | lethe_set_last_filepath(filepath); 301 | 302 | if (lethe_stat(filepath, &st) != 0) { 303 | lethe_set_error_code(kLetheErrorUnableToAccess); 304 | goto lethe_do_drop_epilogue; 305 | } 306 | 307 | if (get_byte == NULL) { 308 | get_byte = lethe_default_randomizer; 309 | } 310 | 311 | if (dtype & kLetheUserPrompt) { 312 | c = '?'; 313 | while (c != 'y' && c != 'n') { 314 | fprintf(stdout, "Do you really want to completely remove '%s' [y/n]: ", filepath); 315 | c = tolower(getchar()); 316 | if (c != 'y' && c != 'n') { 317 | fflush(stdout); 318 | getchar(); 319 | } 320 | } 321 | 322 | getchar(); 323 | 324 | if (c == 'n') { 325 | fprintf(stdout, "File '%s' was not removed.\n", filepath); 326 | has_error = 0; 327 | goto lethe_do_drop_epilogue; 328 | } 329 | } 330 | 331 | if ((dtype & kLetheDataOblivion) && S_ISREG(st.st_mode)) { 332 | #if defined(__unix__) 333 | if ((fd = open(filepath, O_WRONLY | O_SYNC)) == -1) { 334 | lethe_set_error_code(kLetheErrorOpenHasFailed); 335 | goto lethe_do_drop_epilogue; 336 | } 337 | #elif defined(_WIN32) 338 | if ((fd = CreateFile(filepath, 339 | GENERIC_WRITE, 340 | 0, NULL, 341 | OPEN_EXISTING, 342 | FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { 343 | lethe_set_error_code(kLetheErrorOpenHasFailed); 344 | goto lethe_do_drop_epilogue; 345 | } 346 | #else 347 | # error Some code wanted. 348 | #endif 349 | 350 | #if defined(__unix__) 351 | // INFO(Rafael): Avoid leaking the original size of the file. The file data will turn into pure gibberish, 352 | // anyway, if we can avoid leaking this kind of information, let's do it. 353 | if ((blkpad = st.st_size % st.st_blksize) > 0) { 354 | // INFO(Rafael): I have noticed that on ufs if the file size is too small, less than optimal block size, 355 | // if you force it to be equals to block size, the filesystem will discard the current inode and 356 | // write the new bloated data block to a new inode. The problem with this strategy is that the 357 | // old inode will not be really erased from disk. Forcing blkpad be equals to zero will 358 | // avoid this harmful strategy when considering privacy. 359 | if (st.st_size > st.st_blksize) { 360 | blkpad = st.st_blksize - blkpad; 361 | } else { 362 | blkpad = 0; 363 | } 364 | } 365 | #elif defined(_WIN32) 366 | if ((blksize = get_blksize(filepath)) > 0) { 367 | if ((blkpad = st.st_size % blksize) > 0) { 368 | // INFO(Rafael): I have noticed that on ufs if the file size is too small, less than optimal block size, 369 | // if you force it to be equals to block size, the filesystem will discard the current inode and 370 | // write the new bloated data block to a new inode. The problem with this strategy is that the 371 | // old inode will not be really erased from disk. Forcing blkpad be equals to zero will 372 | // avoid this harmful strategy when considering privacy. I have decided take this care on Windows too. 373 | if (st.st_size > blksize) { 374 | blkpad = blksize - blkpad; 375 | } else { 376 | blkpad = 0; 377 | } 378 | } 379 | } else { 380 | blkpad = 0; 381 | } 382 | #else 383 | # error Some code wanted. 384 | #endif 385 | 386 | o = 0; 387 | 388 | do { // WARN(Rafael): Overwrite numbers must be from at least 1 to n. No initial conditional fdoblivion done by 'do { } 389 | // while' will protect against mistakes done by a future sloppy code change. Do not replace it 390 | // with first glance obvious-simplicity of 'while' or 'for', please. 391 | has_error = fdoblivion(fd, st.st_size + blkpad, get_byte); 392 | o++; 393 | } while (o < g_lethe_drop_overwrite_nr); 394 | 395 | #if defined(__unix__) 396 | close(fd); 397 | #elif defined(_WIN32) 398 | CloseHandle(fd); 399 | #else 400 | # error Some code wanted. 401 | #endif 402 | 403 | if (has_error != 0) { 404 | lethe_set_error_code(kLetheErrorDataOblivionHasFailed); 405 | goto lethe_do_drop_epilogue; 406 | } 407 | } 408 | 409 | if (dtype & kLetheFileRemove) { 410 | if (S_ISDIR(st.st_mode)) { 411 | if ((has_error = chdir(filepath)) != 0) { 412 | goto lethe_do_drop_epilogue; 413 | } 414 | 415 | // INFO(Rafael): If a directory removing was requested then is inferred that everything within 416 | // this directory must be removed. Let's empty it. 417 | 418 | lethe_drop_pattern("*", dtype, get_byte); 419 | 420 | if ((has_error = chdir("..")) != 0) { 421 | goto lethe_do_drop_epilogue; 422 | } 423 | } 424 | 425 | has_error = lethe_remove(filepath, get_byte); 426 | } 427 | 428 | lethe_do_drop_epilogue: 429 | 430 | return has_error; 431 | } 432 | 433 | #if defined(__unix__) 434 | static int fdoblivion(int fd, const size_t fsize, lethe_randomizer get_byte) { 435 | // WARN(Rafael): This ***is not*** a silver bullet because it depends on the current filesystem (and device) in use. 436 | // What optimizations it brings and what heuristics it takes advantage to work on. 437 | // Anyway, I am following the basic idea of the DoD standard. Here we do not want to 438 | // erase every single trace of the related file. Only its content data is relevant. 439 | // Inode infos such as file size, file name and other file metadata are (at first glance) 440 | // negligible for an eavesdropper and us either. 441 | 442 | unsigned char *buf = NULL; 443 | int has_error = 1; 444 | 445 | // INFO(Rafael): Bit flipping. 446 | 447 | if ((buf = (unsigned char *) malloc(fsize)) == NULL) { 448 | goto fdoblivion_epilogue; 449 | } 450 | 451 | lethe_memset(buf, 0xFF, fsize); 452 | 453 | if (write(fd, buf, fsize) != fsize) { 454 | goto fdoblivion_epilogue; 455 | } 456 | 457 | fsync(fd); 458 | 459 | if (lseek(fd, 0, SEEK_SET) != 0) { 460 | goto fdoblivion_epilogue; 461 | } 462 | 463 | lethe_memset(buf, 0x00, fsize); 464 | 465 | if (write(fd, buf, fsize) != fsize) { 466 | goto fdoblivion_epilogue; 467 | } 468 | 469 | fsync(fd); 470 | 471 | if (lseek(fd, 0, SEEK_SET) != 0) { 472 | goto fdoblivion_epilogue; 473 | } 474 | 475 | #define fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, epilogue) {\ 476 | get_rnd_databuf(buf, fsize, get_byte);\ 477 | if (write(fd, buf, fsize) != fsize) {\ 478 | goto epilogue;\ 479 | }\ 480 | fsync(fd);\ 481 | if (lseek(fd, 0, SEEK_SET) != 0) {\ 482 | goto epilogue;\ 483 | }\ 484 | } 485 | 486 | // INFO(Rafael): This step of the implemented data wiping is based on the suggestions given by Bruce Schneier 487 | // in his book Applied Cryptography [228 pp.]. 488 | 489 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 490 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 491 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 492 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 493 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 494 | 495 | #undef fdoblivion_paranoid_reverie_step 496 | 497 | free(buf); 498 | buf = NULL; 499 | 500 | has_error = 0; 501 | 502 | fdoblivion_epilogue: 503 | 504 | if (buf != NULL) { 505 | free(buf); 506 | } 507 | 508 | if (has_error) { 509 | lethe_set_error_code(kLetheErrorDataOblivionHasFailed); 510 | } 511 | 512 | return has_error; 513 | } 514 | #elif defined(_WIN32) 515 | static int fdoblivion(HANDLE fd, const size_t fsize, lethe_randomizer get_byte) { 516 | // WARN(Rafael): This ***is not*** a silver bullet because it depends on the current filesystem (and device) in use. 517 | // What optimizations it brings and what heuristics it takes advantage to work on. 518 | // Anyway, I am following the basic idea of the DoD standard. Here we do not want to 519 | // erase every single trace of the related file. Only its content data is relevant. 520 | // Inode infos such as file size, file name and other file metadata are (at first glance) 521 | // negligible for an eavesdropper and us either. 522 | 523 | unsigned char *buf = NULL; 524 | int has_error = 1; 525 | DWORD written; 526 | 527 | // INFO(Rafael): Bit flipping. 528 | 529 | if ((buf = (unsigned char *) malloc(fsize)) == NULL) { 530 | goto fdoblivion_epilogue; 531 | } 532 | 533 | lethe_memset(buf, 0xFF, fsize); 534 | 535 | if (WriteFile(fd, buf, (DWORD)fsize, &written, NULL) == FALSE || written != fsize) { 536 | goto fdoblivion_epilogue; 537 | } 538 | 539 | FlushFileBuffers(fd); 540 | 541 | if (SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { 542 | goto fdoblivion_epilogue; 543 | } 544 | 545 | lethe_memset(buf, 0x00, fsize); 546 | 547 | if (WriteFile(fd, buf, (DWORD)fsize, &written, NULL) == FALSE || written != fsize) { 548 | goto fdoblivion_epilogue; 549 | } 550 | 551 | FlushFileBuffers(fd); 552 | 553 | if (SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { 554 | goto fdoblivion_epilogue; 555 | } 556 | 557 | #define fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, epilogue) {\ 558 | get_rnd_databuf(buf, fsize, get_byte);\ 559 | if (WriteFile(fd, buf, (DWORD)fsize, &written, NULL) == FALSE || written != fsize) {\ 560 | goto epilogue;\ 561 | }\ 562 | FlushFileBuffers(fd);\ 563 | if (SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {\ 564 | goto epilogue;\ 565 | }\ 566 | } 567 | 568 | // INFO(Rafael): This step of the implemented data wiping is based on the suggestions given by Bruce Schneier 569 | // in his book Applied Cryptography [228 pp.]. 570 | 571 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 572 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 573 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 574 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 575 | fdoblivion_paranoid_reverie_step(fd, buf, fsize, get_byte, fdoblivion_epilogue); 576 | 577 | #undef fdoblivion_paranoid_reverie_step 578 | 579 | free(buf); 580 | buf = NULL; 581 | 582 | has_error = 0; 583 | 584 | fdoblivion_epilogue: 585 | 586 | if (buf != NULL) { 587 | free(buf); 588 | } 589 | 590 | if (has_error) { 591 | lethe_set_error_code(kLetheErrorDataOblivionHasFailed); 592 | } 593 | 594 | return has_error; 595 | } 596 | #else 597 | # error Some code wanted. 598 | #endif 599 | 600 | static void get_rnd_databuf(unsigned char *buf, const size_t size, lethe_randomizer get_byte) { 601 | unsigned char *bp, *bp_end; 602 | 603 | bp = buf; 604 | bp_end = bp + size; 605 | 606 | while (bp != bp_end) { 607 | *bp = get_byte(); 608 | bp++; 609 | } 610 | } 611 | 612 | static void get_rnd_filename(char *filename, lethe_randomizer get_byte) { 613 | char *fp, *fp_end; 614 | struct stat st; 615 | unsigned char ri; 616 | 617 | fp = filename; 618 | fp_end = fp + strlen(fp); 619 | 620 | while (fp != fp_end) { 621 | do { 622 | ri = get_byte(); 623 | } while (ri >= 0xFF - (0xFF % g_lethe_allowed_fname_symbols_nr)); 624 | *fp = g_lethe_allowed_fname_symbols[(size_t)ri % g_lethe_allowed_fname_symbols_nr]; 625 | fp++; 626 | } 627 | } 628 | 629 | static int lethe_remove(const char *filepath, lethe_randomizer get_byte) { 630 | char ping_pong_paths[2][4096], *curr_fn, *last_fn, *curr_fp, *last_fp; 631 | char *fn_p[2]; 632 | int has_error = 1, do_nr; 633 | size_t filepath_size; 634 | int b; 635 | struct stat st; 636 | 637 | if (filepath == NULL) { 638 | goto lethe_remove_epilogue; 639 | } 640 | 641 | filepath_size = strlen(filepath); 642 | 643 | if (filepath_size > sizeof(ping_pong_paths[0]) - 1) { 644 | goto lethe_remove_epilogue; 645 | } 646 | 647 | snprintf(ping_pong_paths[0], sizeof(ping_pong_paths[0]) - 1, "%s", filepath); 648 | snprintf(ping_pong_paths[1], sizeof(ping_pong_paths[1]) - 1, "%s", filepath); 649 | 650 | filepath_size -= 1; 651 | 652 | #if defined(__unix__) 653 | lethe_stat(filepath, &st); 654 | if (S_ISDIR(st.st_mode)) { 655 | filepath_size = strlen(filepath) - 1; 656 | filepath_size -= (filepath[filepath_size] == '/'); 657 | } 658 | while (filepath_size > 0 && filepath[filepath_size] != '/') { 659 | filepath_size--; 660 | } 661 | filepath_size += (filepath_size != 0); 662 | #elif defined(_WIN32) 663 | lethe_stat(filepath, &st); 664 | if (S_ISDIR(st.st_mode)) { 665 | filepath_size = strlen(filepath) - 1; 666 | filepath_size -= (filepath[filepath_size] == '\\' || 667 | filepath[filepath_size] == '/'); 668 | } 669 | while (filepath_size > 0 && filepath[filepath_size] != '\\' && 670 | filepath[filepath_size] != '/') { 671 | filepath_size--; 672 | } 673 | filepath_size += (filepath_size != 0); 674 | #else 675 | # error Some code wanted. 676 | #endif 677 | 678 | fn_p[0] = &ping_pong_paths[0][filepath_size]; 679 | fn_p[1] = &ping_pong_paths[1][filepath_size]; 680 | 681 | #define get_curr_fname(curr, b) ( curr = &fn_p[b^0x1][0] ) 682 | 683 | #define get_curr_fpath(curr, b) ( curr = &ping_pong_paths[b^0x1][0] ) 684 | #define get_last_fpath(last, b) ( last = &ping_pong_paths[b ][0] ) 685 | 686 | do_nr = 0; 687 | 688 | b = 0; 689 | 690 | do { // WARN(Rafael): Rename numbers must be from at least 1 to n. No initial conditional rename done by 'do { } 691 | // while' will protect against mistakes done by a future sloppy code change. Do not replace it 692 | // with first glance obvious-simplicity of 'while' or 'for', please. 693 | get_last_fpath(last_fp, b); 694 | get_curr_fpath(curr_fp, b); 695 | 696 | get_curr_fname(curr_fn, b); 697 | 698 | do { 699 | get_rnd_filename(curr_fn, get_byte); 700 | } while (lethe_stat(curr_fp, &st) == 0); 701 | 702 | if (rename(last_fp, curr_fp) != 0) { 703 | goto lethe_remove_epilogue; 704 | } 705 | 706 | b ^= 0x1; 707 | do_nr++; 708 | } while (do_nr < g_lethe_drop_rename_nr); 709 | 710 | #undef get_curr_fname 711 | 712 | #undef get_curr_fpath 713 | #undef get_last_fpath 714 | 715 | #if defined(__unix__) 716 | has_error = remove(curr_fp); 717 | #elif defined(_WIN32) 718 | // WARN(Rafael): Due to the fact of WINAPI's choice of zero return for error cases, 719 | // let's use the unix equivalent wrappers, it will polute less the code. 720 | has_error = lethe_stat(curr_fp, &st); 721 | if (has_error) { 722 | goto lethe_remove_epilogue; 723 | } 724 | if (S_ISREG(st.st_mode)) { 725 | has_error = remove(curr_fp); 726 | } else if (S_ISDIR(st.st_mode)) { 727 | has_error = rmdir(curr_fp); 728 | } 729 | #else 730 | # error Some code wanted. 731 | #endif 732 | 733 | lethe_remove_epilogue: 734 | 735 | if (has_error) { 736 | lethe_set_error_code(kLetheErrorFileRemoveHasFailed); 737 | } 738 | 739 | lethe_memset(ping_pong_paths[0], 0, sizeof(ping_pong_paths[0])); 740 | lethe_memset(ping_pong_paths[1], 0, sizeof(ping_pong_paths[0])); 741 | 742 | return has_error; 743 | } 744 | -------------------------------------------------------------------------------- /src/lethe_drop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_DROP_H 9 | #define LETHE_LETHE_DROP_H 1 10 | 11 | #include 12 | 13 | #define lethe_drop(pattern, dtype...) lethe_drop_pattern(pattern, dtype) 14 | 15 | int lethe_drop_pattern(const char *filepath, const lethe_drop_type dtype, ...); 16 | 17 | int lethe_set_rename_nr(const int value); 18 | 19 | int lethe_set_overwrite_nr(const int value); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/lethe_error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | 12 | int g_lethe_err; 13 | 14 | char g_lethe_filepath[4096]; 15 | 16 | void lethe_set_error_code(const lethe_error_code code) { 17 | g_lethe_err = code; 18 | } 19 | 20 | void lethe_set_last_filepath(const char *filepath) { 21 | if (filepath != NULL) { 22 | snprintf(g_lethe_filepath, sizeof(g_lethe_filepath) - 1, "%s", filepath); 23 | } else { 24 | lethe_memset(g_lethe_filepath, 0, sizeof(g_lethe_filepath)); 25 | } 26 | } 27 | 28 | char *lethe_get_last_error(char *buf, const size_t buf_size) { 29 | if (buf == NULL) { 30 | goto lethe_get_last_error_epilogue; 31 | } 32 | 33 | switch (g_lethe_err) { 34 | case kLetheErrorNone: 35 | snprintf(buf, buf_size - 1, "Success."); 36 | break; 37 | 38 | case kLetheErrorNullFile: 39 | snprintf(buf, buf_size - 1, "Null file name."); 40 | break; 41 | 42 | case kLetheErrorNullDropType: 43 | snprintf(buf, buf_size - 1, "Null drop type."); 44 | break; 45 | 46 | case kLetheErrorUnableToAccess: 47 | snprintf(buf, buf_size - 1, "Unable to access file '%s'.", g_lethe_filepath); 48 | break; 49 | 50 | case kLetheErrorOpenHasFailed: 51 | snprintf(buf, buf_size - 1, "Unable to open file '%s'.", g_lethe_filepath); 52 | break; 53 | 54 | case kLetheErrorDataOblivionHasFailed: 55 | snprintf(buf, buf_size - 1, "Unable to scramble data from file '%s'.", g_lethe_filepath); 56 | break; 57 | 58 | case kLetheErrorFileRemoveHasFailed: 59 | snprintf(buf, buf_size - 1, "Unable to remove file '%s'.", g_lethe_filepath); 60 | break; 61 | 62 | case kLetheErrorNothingToDrop: 63 | snprintf(buf, buf_size - 1, "Nothing to drop."); 64 | break; 65 | 66 | default: 67 | snprintf(buf, buf_size - 1, "You have found a unicorn! Congrats!"); 68 | break; 69 | } 70 | 71 | lethe_get_last_error_epilogue: 72 | 73 | return buf; 74 | } 75 | -------------------------------------------------------------------------------- /src/lethe_error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_ERROR_H 9 | #define LETHE_LETHE_ERROR_H 1 10 | 11 | #include 12 | #include 13 | 14 | void lethe_set_error_code(const lethe_error_code code); 15 | 16 | void lethe_set_last_filepath(const char *filepath); 17 | 18 | char *lethe_get_last_error(char *buf, const size_t buf_size); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/lethe_ldist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | 12 | int levenshtein_distance(const char *yterm, const char *xterm) { 13 | int **distances = NULL; 14 | int final_distance = 0xFFFFFF; 15 | size_t yterm_size, xterm_size, y, x; 16 | int edit_op; 17 | 18 | if (yterm == NULL || xterm == NULL) { 19 | goto levenshtein_distance_epilogue; 20 | } 21 | 22 | if ((yterm_size = strlen(yterm)) == 0 || (xterm_size = strlen(xterm)) == 0) { 23 | goto levenshtein_distance_epilogue; 24 | } 25 | 26 | distances = (int **) malloc((yterm_size + 1) * sizeof(int *)); 27 | 28 | if (distances == NULL) { 29 | goto levenshtein_distance_epilogue; 30 | } 31 | 32 | for (y = 0; y <= yterm_size; y++) { 33 | distances[y] = (int *) malloc((xterm_size + 1) * sizeof(int)); 34 | if (distances[y] == NULL) { 35 | goto levenshtein_distance_epilogue; 36 | } 37 | } 38 | 39 | for (x = 0; x < xterm_size; x++) { 40 | distances[0][x] = x; 41 | } 42 | 43 | for (y = 0; y < yterm_size; y++) { 44 | distances[y][0] = y; 45 | } 46 | 47 | #define levenshtein_min(di_y, di_x, di_yx) ( ((di_x) < (di_y) && (di_x) < (di_yx)) ? (di_x) :\ 48 | ((di_y) < (di_x) && (di_y) < (di_yx)) ? (di_y) : (di_yx) ) 49 | 50 | for (y = 0; y < yterm_size; y++) { 51 | for (x = 0; x < xterm_size; x++) { 52 | edit_op = (yterm[y] == xterm[x]) ? 0 : 2; 53 | distances[y + 1][x + 1] = levenshtein_min(distances[y][x + 1] + 1, 54 | distances[y + 1][x] + 1, 55 | distances[y][x] + edit_op); 56 | } 57 | } 58 | 59 | #undef levenshtein_min 60 | 61 | final_distance = distances[yterm_size - 1][xterm_size - 1]; 62 | 63 | levenshtein_distance_epilogue: 64 | 65 | if (distances != NULL) { 66 | for (y = 0; y <= yterm_size; y++) { 67 | if (distances[y] != NULL) { 68 | free(distances[y]); 69 | } 70 | } 71 | free(distances); 72 | } 73 | 74 | return final_distance; 75 | } 76 | -------------------------------------------------------------------------------- /src/lethe_ldist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_LDIST_H 9 | #define LETHE_LETHE_LDIST_H 1 10 | 11 | int levenshtein_distance(const char *yterm, const char *xterm); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/lethe_libc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | int (*lethe_memcmp)(const void *, const void *, size_t) = memcmp; 12 | 13 | void *(*lethe_memcpy)(void *, const void *, size_t) = memcpy; 14 | 15 | void *(*lethe_memset)(void *, int, size_t) = memset; 16 | 17 | int (*lethe_stat)(const char *, struct stat *) = stat; 18 | 19 | int lethe_set_memcmp(int (*usr_memcmp)(const void *, const void *, size_t)) { 20 | int has_error = 1; 21 | if (usr_memcmp != NULL) { 22 | lethe_memcmp = usr_memcmp; 23 | has_error = 0; 24 | } 25 | return has_error; 26 | } 27 | 28 | int lethe_set_memcpy(void *(*usr_memcpy)(void *, const void *, size_t)) { 29 | int has_error = 1; 30 | if (usr_memcpy != NULL) { 31 | lethe_memcpy = usr_memcpy; 32 | has_error = 0; 33 | } 34 | return has_error; 35 | } 36 | 37 | int lethe_set_memset(void *(*usr_memset)(void *, int, size_t)) { 38 | int has_error = 1; 39 | if (usr_memset != NULL) { 40 | lethe_memset = usr_memset; 41 | has_error = 0; 42 | } 43 | return has_error; 44 | } 45 | 46 | int lethe_set_stat(int (*usr_stat)(const char *, struct stat *)) { 47 | int has_error = 1; 48 | 49 | if (usr_stat != NULL) { 50 | lethe_stat = usr_stat; 51 | has_error = 0; 52 | } 53 | 54 | return has_error; 55 | } 56 | -------------------------------------------------------------------------------- /src/lethe_libc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_LIBC_H 9 | #define LETHE_LETHE_LIBC_H 1 10 | 11 | #include 12 | #include 13 | 14 | extern int (*lethe_memcmp)(const void *, const void *, size_t); 15 | 16 | extern void *(*lethe_memcpy)(void *, const void *, size_t); 17 | 18 | extern void *(*lethe_memset)(void *, int, size_t); 19 | 20 | extern int (*lethe_stat)(const char *, struct stat *); 21 | 22 | int lethe_set_memcmp(int (*usr_memcmp)(const void *, const void *, size_t)); 23 | 24 | int lethe_set_memcpy(void *(*usr_memcpy)(void *, const void *, size_t)); 25 | 26 | int lethe_set_memset(void *(*usr_memset)(void *, int, size_t)); 27 | 28 | int lethe_set_stat(int (*usr_stat)(const char *, struct stat *)); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/lethe_mkpath.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | char *lethe_mkpath(char *buf, const size_t buf_size, const char *a, const size_t a_size, const char *b, const size_t b_size) { 12 | const char *ap, *ap_end, *bp, *bp_end; 13 | size_t m; 14 | 15 | if (buf == NULL || a == NULL || b == NULL || buf_size == 0 || (a_size + b_size + 2) > buf_size - 1) { 16 | return NULL; 17 | } 18 | 19 | lethe_memset(buf, 0, buf_size); 20 | 21 | #if defined(__unix__) 22 | ap = a; 23 | ap_end = a + a_size - (a_size > 0); 24 | 25 | while (ap_end != ap && *ap_end == '/') { 26 | ap_end--; 27 | } 28 | 29 | m = ap_end - a + (a_size > 0); 30 | 31 | if (m > 0) { 32 | lethe_memcpy(buf, a, m); 33 | } 34 | 35 | bp = b; 36 | bp_end = b + b_size; 37 | 38 | while (bp != bp_end && *bp == '/') { 39 | bp++; 40 | } 41 | 42 | *(buf + m) = (m > 0 && bp != bp_end) ? '/' : 0; 43 | 44 | if (bp != bp_end) { 45 | lethe_memcpy(buf + m + (m > 0), bp, bp_end - bp); 46 | } 47 | #elif defined(_WIN32) 48 | ap = a; 49 | ap_end = a + a_size - (a_size > 0); 50 | 51 | while (ap_end != ap && (*ap_end == '\\' || 52 | *ap_end == '/')) { 53 | ap_end--; 54 | } 55 | 56 | m = ap_end - a + (a_size > 0); 57 | 58 | if (m > 0) { 59 | lethe_memcpy(buf, a, m); 60 | } 61 | 62 | bp = b; 63 | bp_end = b + b_size; 64 | 65 | while (bp != bp_end && (*bp == '\\' || 66 | *bp == '/')) { 67 | bp++; 68 | } 69 | 70 | *(buf + m) = (m > 0 && bp != bp_end) ? '\\' : 0; 71 | 72 | if (bp != bp_end) { 73 | lethe_memcpy(buf + m + (m > 0), bp, bp_end - bp); 74 | } 75 | #else 76 | # error Some code wanted. 77 | #endif 78 | 79 | return buf; 80 | } 81 | -------------------------------------------------------------------------------- /src/lethe_mkpath.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_MKPATH_H 9 | #define LETHE_LETHE_MKPATH_H 1 10 | 11 | #include 12 | 13 | char *lethe_mkpath(char *buf, const size_t buf_size, const char *a, const size_t a_size, const char *b, const size_t b_size); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/lethe_option.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | 12 | static int g_lethe_option_argc = 0; 13 | 14 | static char **g_lethe_option_argv = NULL; 15 | 16 | static char *g_lethe_option_ucmd = NULL; 17 | 18 | char *lethe_get_ucmd(void) { 19 | return g_lethe_option_ucmd; 20 | } 21 | 22 | char *lethe_get_argv(const int a) { 23 | if (g_lethe_option_argc == 0 || g_lethe_option_argv == NULL || a >= g_lethe_option_argc) { 24 | return NULL; 25 | } 26 | return &g_lethe_option_argv[a][0]; 27 | } 28 | 29 | void lethe_option_set_argc_argv(int argc, char **argv) { 30 | g_lethe_option_ucmd = (argc > 1) ? &argv[1][0] : NULL; 31 | g_lethe_option_argc = argc - 2; 32 | if (g_lethe_option_argc > 0) { 33 | g_lethe_option_argv = &argv[2]; 34 | } else { 35 | g_lethe_option_argc = 0; 36 | g_lethe_option_argv = NULL; 37 | } 38 | } 39 | 40 | char *lethe_get_option(const char *option, char *default_value) { 41 | char *value = default_value; 42 | char temp[1024]; 43 | char **argv, **argv_end; 44 | 45 | if (g_lethe_option_argc == 0 || g_lethe_option_argv == NULL) { 46 | goto lethe_get_option_epilogue; 47 | } 48 | 49 | snprintf(temp, sizeof(temp) - 1, "--%s=", option); 50 | 51 | argv = &g_lethe_option_argv[0]; 52 | argv_end = argv + g_lethe_option_argc; 53 | 54 | while (argv != argv_end) { 55 | if (strstr(*argv, temp) == *argv) { 56 | value = *argv + strlen(temp); 57 | goto lethe_get_option_epilogue; 58 | } 59 | argv++; 60 | } 61 | 62 | lethe_get_option_epilogue: 63 | 64 | return value; 65 | } 66 | 67 | int lethe_get_bool_option(const char *option, const int default_value) { 68 | int value = default_value; 69 | char temp[1024]; 70 | char **argv, **argv_end; 71 | 72 | if (g_lethe_option_argc == 0 || g_lethe_option_argv == NULL) { 73 | goto lethe_get_bool_option_epilogue; 74 | } 75 | 76 | snprintf(temp, sizeof(temp) - 1, "--%s", option); 77 | 78 | argv = &g_lethe_option_argv[0]; 79 | argv_end = argv + g_lethe_option_argc; 80 | 81 | while (argv != argv_end) { 82 | if (strcmp(*argv, temp) == 0) { 83 | value = 1; 84 | goto lethe_get_bool_option_epilogue; 85 | } 86 | argv++; 87 | } 88 | 89 | lethe_get_bool_option_epilogue: 90 | 91 | return value; 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/lethe_option.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_OPTION_H 9 | #define LETHE_LETHE_OPTION_H 1 10 | 11 | void lethe_option_set_argc_argv(int argc, char **argv); 12 | 13 | char *lethe_get_option(const char *option, char *default_value); 14 | 15 | int lethe_get_bool_option(const char *option, const int default_value); 16 | 17 | char *lethe_get_ucmd(void); 18 | 19 | char *lethe_get_argv(const int a); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/lethe_random.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | # if defined(__unix__) 10 | # include 11 | # include 12 | # elif defined(_WIN32) 13 | # include 14 | # include 15 | #endif 16 | 17 | unsigned char lethe_default_randomizer(void) { 18 | unsigned char byte; 19 | #if defined(__unix__) 20 | int fd; 21 | 22 | fd = open("/dev/urandom", O_RDONLY); 23 | 24 | if (fd == -1) { 25 | fd = open("/dev/random", O_NONBLOCK | O_RDONLY); 26 | } 27 | 28 | if (fd != -1) { 29 | read(fd, &byte, 1); 30 | close(fd); 31 | } 32 | #elif defined(_WIN32) 33 | static HCRYPTPROV crypto_ctx = 0; 34 | 35 | if (crypto_ctx == 0 && !CryptAcquireContext(&crypto_ctx, NULL, NULL, PROV_RSA_FULL, 0)) { 36 | return byte; 37 | } 38 | 39 | CryptGenRandom(crypto_ctx, 1, &byte); 40 | #else 41 | # error Some code wanted. 42 | #endif 43 | 44 | return byte; 45 | } 46 | -------------------------------------------------------------------------------- /src/lethe_random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_RANDOM_H 9 | #define LETHE_LETHE_RANDOM_H 1 10 | 11 | unsigned char lethe_default_randomizer(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/lethe_strglob.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | int lethe_strglob(const char *str, const char *pattern) { 12 | const char *sp, *sp_end; 13 | const char *p, *p_end; 14 | int matches = 1; 15 | 16 | if (str == NULL || pattern == NULL) { 17 | return 0; 18 | } 19 | 20 | if (strcmp(pattern, "*") == 0 || *pattern == 0) { 21 | return 1; 22 | } 23 | 24 | // EMPTY-CAUSE(Rafael): Maybe avoid an excessive callstack growth on star cases, it is ugly and also sucks... 25 | 26 | sp = str; 27 | sp_end = sp + strlen(sp); 28 | p = pattern; 29 | p_end = p + strlen(p); 30 | 31 | while (matches && p != p_end && sp != sp_end) { 32 | switch (*p) { 33 | case '*': 34 | matches = (*(p + 1) == 0) && (*(sp + 1) == 0); 35 | 36 | while (!matches && sp != sp_end) { 37 | matches = lethe_strglob(sp, p + 1); 38 | sp++; 39 | } 40 | 41 | if (matches) { 42 | sp = sp_end; 43 | p = p_end; 44 | } 45 | 46 | goto lethe_strglob_epilogue; 47 | 48 | case '?': 49 | matches = (*sp != 0); 50 | break; 51 | 52 | case '[': 53 | matches = 0; 54 | p++; 55 | 56 | while (!matches && sp != sp_end && *p != ']') { 57 | matches = (*sp == *p); 58 | p++; 59 | } 60 | 61 | if (matches && *p != ']') { 62 | while (*p != ']' && p != p_end) { 63 | p++; 64 | } 65 | } 66 | break; 67 | 68 | default: 69 | matches = (*sp == *p); 70 | break; 71 | } 72 | p++; 73 | sp++; 74 | } 75 | 76 | lethe_strglob_epilogue: 77 | if (matches && sp == sp_end && p != p_end && *p == '*') { 78 | p++; 79 | } 80 | 81 | matches = (matches && (p == p_end && sp == sp_end)); 82 | 83 | return matches; 84 | } 85 | -------------------------------------------------------------------------------- /src/lethe_strglob.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * This is a free software. You can redistribute it and/or modify under 5 | * the terms of the GNU General Public License version 2. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_STRGLOB_H 9 | #define LETHE_LETHE_STRGLOB_H 1 10 | 11 | int lethe_strglob(const char *str, const char *pattern); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/lethe_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #ifndef LETHE_LETHE_TYPES_H 9 | #define LETHE_LETHE_TYPES_H 1 10 | 11 | #include 12 | 13 | typedef unsigned char (*lethe_randomizer)(void); 14 | 15 | typedef enum { 16 | kLetheDataOblivion = 0x1, 17 | kLetheFileRemove = kLetheDataOblivion << 1, 18 | kLetheCustomRandomizer = kLetheFileRemove << 1, 19 | kLetheUserPrompt = kLetheCustomRandomizer << 1 20 | }lethe_drop_type; 21 | 22 | typedef enum { 23 | kLetheErrorNone = 0, 24 | kLetheErrorNullFile, 25 | kLetheErrorNullDropType, 26 | kLetheErrorUnableToAccess, 27 | kLetheErrorOpenHasFailed, 28 | kLetheErrorDataOblivionHasFailed, 29 | kLetheErrorFileRemoveHasFailed, 30 | kLetheErrorNothingToDrop, 31 | kLetheErrorNr 32 | }lethe_error_code; 33 | 34 | extern int g_lethe_err; 35 | 36 | extern char g_lethe_filepath[4096]; 37 | 38 | extern int g_lethe_rename_nr; 39 | 40 | //extern lethe_stat g_lethe_stat; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #if defined(__unix__) 16 | # include 17 | #elif defined(_WIN32) 18 | # include 19 | #endif 20 | 21 | struct lethe_tool_commands_ctx { 22 | const char *ucmd; 23 | int (*task)(void); 24 | }; 25 | 26 | static int do_drop(void); 27 | 28 | static int do_drop_help(void); 29 | 30 | static int do_help(void); 31 | 32 | static int do_help_help(void); // INFO(Rafael): Children will love this function name... 33 | 34 | static int do_man(void); 35 | 36 | static int do_man_help(void); 37 | 38 | static int do_version(void); 39 | 40 | static int lethe_exec(char *(*get_ucmd)(void), int (*null_command)(void), 41 | struct lethe_tool_commands_ctx *commands, const size_t commands_nr); 42 | 43 | static int null_command(void); 44 | 45 | static char *get_help_topic(void); 46 | 47 | static int help_banner(void); 48 | 49 | static void sigint_watchdog(int sig_nr); 50 | 51 | static int did_you_mean(const char *ucmd, const int max_distance); 52 | 53 | static int has_more(void); 54 | 55 | static int has_less(void); 56 | 57 | static struct lethe_tool_commands_ctx g_lethe_tool_commands[] = { 58 | { "drop", do_drop }, 59 | { "help", do_help }, 60 | { "man", do_man }, 61 | { "version", do_version } 62 | }; 63 | 64 | static struct lethe_tool_commands_ctx g_lethe_tool_help_topics[] = { 65 | { "drop", do_drop_help }, 66 | { "help", do_help_help }, 67 | { "man", do_man_help } 68 | }; 69 | 70 | const char *g_lethe_tool_version = "v1"; 71 | 72 | int main(int argc, char **argv) { 73 | signal(SIGINT | SIGTERM, sigint_watchdog); 74 | lethe_option_set_argc_argv(argc, argv); 75 | return lethe_exec(lethe_get_ucmd, 76 | null_command, 77 | &g_lethe_tool_commands[0], 78 | sizeof(g_lethe_tool_commands) / sizeof(g_lethe_tool_commands[0])); 79 | } 80 | 81 | static void sigint_watchdog(int sig_nr) { 82 | // INFO(Rafael): It will be probably the panic button so let's call exit asap. 83 | fprintf(stdout, "Aborted!\n"); 84 | exit(1); 85 | } 86 | 87 | static int lethe_exec(char *(*get_ucmd)(void), int (*null_command)(void), 88 | struct lethe_tool_commands_ctx *commands, const size_t commands_nr) { 89 | char *ucmd; 90 | int has_error = 1; 91 | struct lethe_tool_commands_ctx *cmd, *cmd_end; 92 | 93 | ucmd = get_ucmd(); 94 | 95 | if (lethe_get_bool_option("version", 0) || (ucmd != NULL && strcmp(ucmd, "--version") == 0)) { 96 | // INFO(Rafael): Keeping --version people happy, in silent mode; or keeping those people in silent mode. 97 | has_error = do_version(); 98 | goto lethe_exec_epilogue; 99 | } 100 | 101 | if (ucmd == NULL) { 102 | has_error = null_command(); 103 | goto lethe_exec_epilogue; 104 | } 105 | 106 | cmd = commands; 107 | cmd_end = cmd + commands_nr; 108 | 109 | while (cmd != cmd_end && strcmp(cmd->ucmd, ucmd) != 0) { 110 | cmd++; 111 | } 112 | 113 | if (cmd != cmd_end) { 114 | has_error = cmd->task(); 115 | } else if (did_you_mean(ucmd, 2) == 0) { 116 | fprintf(stderr, "Maybe 'man' command would be more suitable for you, would not?\n"); 117 | } 118 | 119 | lethe_exec_epilogue: 120 | 121 | return has_error; 122 | } 123 | 124 | static int null_command(void) { 125 | fprintf(stderr, "Well, what are intending to do? Try to call me again by using help.\n"); 126 | return 1; 127 | } 128 | 129 | static char *get_help_topic(void) { 130 | return lethe_get_argv(0); 131 | } 132 | 133 | static int help_banner(void) { 134 | fprintf(stdout, "lethe is Copyright (C) 2020 by Rafael Santiago.\n\n" 135 | "Bug reports, feedback, etc: or " 136 | "\n" 137 | "_____\n" 138 | "usage: lethe [options]\n\n" 139 | "*** Are you looking for some quick help topic of a command? Run 'lethe help '.\n" 140 | " Are you new here? Hi there newbie! What about reading some documentation before you screw " 141 | "something up? Run 'lethe man' and\n" 142 | " welcome! ;)\n"); 143 | return 0; 144 | } 145 | 146 | static int do_drop(void) { 147 | int has_error = 1; 148 | int overwrite_nr, rename_nr; 149 | int a; 150 | char *arg; 151 | lethe_drop_type dtype; 152 | unsigned char (*randomizer)(void) = lethe_default_randomizer; 153 | char libpath[4096], *func; 154 | #if defined(__unix__) 155 | void *libdynrandom = NULL; 156 | #elif defined(_WIN32) 157 | HMODULE libdynrandom = NULL; 158 | #else 159 | # error Some code wanted. 160 | #endif 161 | 162 | if (lethe_set_overwrite_nr(atoi(lethe_get_option("overwrite-passes", "5"))) != 0) { 163 | fprintf(stderr, "ERROR: Option --overwrite-passes must be a valid number from 1 up to n.\n"); 164 | goto do_drop_epilogue; 165 | } 166 | 167 | if (lethe_set_rename_nr(atoi(lethe_get_option("rename-passes", "10"))) != 0) { 168 | fprintf(stderr, "ERROR: Option --rename-passes must be a valid number from 1 up to n.\n"); 169 | goto do_drop_epilogue; 170 | } 171 | 172 | dtype = kLetheDataOblivion | kLetheFileRemove | kLetheCustomRandomizer; 173 | 174 | if (!lethe_get_bool_option("ask-me-nothing", 0)) { 175 | dtype |= kLetheUserPrompt; 176 | } 177 | 178 | if ((arg = lethe_get_option("dyn-randomizer", NULL)) != NULL) { 179 | if ((func = strstr(arg, ":")) == NULL) { 180 | fprintf(stderr, "ERROR: Randomizer's function name not informed in --dyn-randomizer option.\n"); 181 | goto do_drop_epilogue; 182 | } 183 | 184 | lethe_memset(libpath, 0, sizeof(libpath)); 185 | lethe_memcpy(libpath, arg, func - arg); 186 | func += 1; 187 | 188 | #if defined(__unix__) 189 | if ((libdynrandom = dlopen(libpath, RTLD_LAZY)) == NULL) { 190 | fprintf(stderr, "ERROR: Unable to open '%s'.\n", libpath); 191 | goto do_drop_epilogue; 192 | } 193 | 194 | if ((randomizer = (unsigned char (*)(void)) dlsym(libdynrandom, func)) == NULL) { 195 | fprintf(stderr, "ERROR: Randomizer function '%s' not found.\n", func); 196 | goto do_drop_epilogue; 197 | } 198 | #elif defined(_WIN32) 199 | if ((libdynrandom = LoadLibrary(libpath)) == NULL) { 200 | fprintf(stderr, "ERROR: Unable to open '%s'.\n", libpath); 201 | goto do_drop_epilogue; 202 | } 203 | 204 | if ((randomizer = (unsigned char (*)(void)) GetProcAddress(libdynrandom, func)) == NULL) { 205 | fprintf(stderr, "ERROR: Randomizer function '%s' not found.\n", func); 206 | goto do_drop_epilogue; 207 | } 208 | #else 209 | # error Some code wanted. 210 | #endif 211 | 212 | } 213 | 214 | a = 0; 215 | 216 | while ((arg = lethe_get_argv(a++)) != NULL) { 217 | if (strstr(arg, "--") == arg) { 218 | continue; 219 | } 220 | if (has_error != 0) { // INFO(Rafael): At least one well-succeed drop is considered a succeed dropping process. 221 | has_error = lethe_drop(arg, dtype, randomizer); 222 | } else { 223 | lethe_drop(arg, dtype, randomizer); 224 | } 225 | } 226 | 227 | do_drop_epilogue: 228 | 229 | #if defined(__unix__) 230 | if (libdynrandom != NULL) { 231 | dlclose(libdynrandom); 232 | } 233 | #elif defined(_WIN32) 234 | if (libdynrandom != NULL) { 235 | FreeLibrary(libdynrandom); 236 | } 237 | #else 238 | # error Some code wanted. 239 | #endif 240 | 241 | if (has_error != 0) { 242 | fprintf(stderr, "No such file or directory.\n"); 243 | } 244 | 245 | return has_error; 246 | } 247 | 248 | static int do_drop_help(void) { 249 | fprintf(stdout, "use: lethe drop\n" 250 | " \n" 251 | " [--ask-me-nothing |\n" 252 | " --overwrite-passes= |\n" 253 | " --rename-passes= |\n" 254 | " --dyn-randomizer=:]\n"); 255 | return 0; 256 | } 257 | 258 | static int do_help(void) { 259 | return lethe_exec(get_help_topic, 260 | help_banner, 261 | &g_lethe_tool_help_topics[0], 262 | sizeof(g_lethe_tool_help_topics) / sizeof(g_lethe_tool_help_topics[0])); 263 | } 264 | 265 | static int do_help_help(void) { 266 | fprintf(stdout, "use: lethe help \n"); 267 | return 0; 268 | } 269 | 270 | static int do_man(void) { 271 | FILE *pager = stdout, *fp = NULL; 272 | #if defined(__unix__) 273 | const char *manpath = "/usr/local/share/lethe/doc/MANUAL.txt"; 274 | #elif defined(_WIN32) 275 | const char *manpath = "C:\\lethe\\doc\\MANUAL.txt"; 276 | #else 277 | # error Some code wanted. 278 | #endif 279 | char *content = NULL; 280 | size_t content_size; 281 | int has_error = 1; 282 | 283 | if (has_less()) { 284 | pager = popen("less", "w"); 285 | } else if (has_more()) { 286 | pager = popen("more", "w"); 287 | } 288 | 289 | if ((fp = fopen(manpath, "rb")) == NULL) { 290 | fprintf(stderr, "ERROR: Unable to find the manual.\n"); 291 | goto do_man_epilogue; 292 | } 293 | 294 | fseek(fp, 0L, SEEK_END); 295 | content_size = (size_t) ftell(fp); 296 | fseek(fp, 0L, SEEK_SET); 297 | 298 | if ((content = (char *) malloc(content_size + 1)) == NULL) { 299 | fprintf(stderr, "ERROR: Not enough memory.\n"); 300 | goto do_man_epilogue; 301 | } 302 | 303 | lethe_memset(content, 0, content_size + 1); 304 | fread(content, 1, content_size, fp); 305 | 306 | fprintf(pager, "%s", content); 307 | 308 | has_error = 0; 309 | 310 | do_man_epilogue: 311 | 312 | if (pager != NULL && pager != stdout) { 313 | pclose(pager); 314 | } 315 | 316 | if (fp != NULL) { 317 | fclose(fp); 318 | } 319 | 320 | if (content != NULL) { 321 | free(content); 322 | } 323 | 324 | return has_error; 325 | } 326 | 327 | static int do_man_help(void) { 328 | fprintf(stdout, "use: lethe man\n"); 329 | return 0; 330 | } 331 | 332 | static int do_version(void) { 333 | fprintf(stdout, "lethe-%s\n", g_lethe_tool_version); 334 | return 0; 335 | } 336 | 337 | static int did_you_mean(const char *ucmd, const int max_distance) { 338 | int distances[0xFF]; 339 | size_t d, cmd_nr; 340 | int has_some_suggestion = 0, s_nr; 341 | struct lethe_tool_commands_ctx *cmd, *cmd_end; 342 | 343 | for (d = 0; d < sizeof(distances) / sizeof(distances[0]); d++) { 344 | distances[d] = -1; 345 | } 346 | 347 | cmd = &g_lethe_tool_commands[0]; 348 | cmd_end = cmd + (cmd_nr = sizeof(g_lethe_tool_commands) / sizeof(g_lethe_tool_commands[0])); 349 | 350 | while (cmd != cmd_end) { 351 | d = cmd_nr - (cmd_end - cmd); 352 | distances[d] = levenshtein_distance(ucmd, cmd->ucmd); 353 | has_some_suggestion |= (distances[d] >= 1 && distances[d] <= max_distance); 354 | cmd++; 355 | } 356 | 357 | if (has_some_suggestion) { 358 | s_nr = 0; 359 | cmd = &g_lethe_tool_commands[0]; 360 | fprintf(stderr, "Did you mean "); 361 | while (cmd != cmd_end) { 362 | d = cmd_nr - (cmd_end - cmd); 363 | if (distances[d] >= 1 && distances[d] <= max_distance) { 364 | if (s_nr > 0) { 365 | fprintf(stderr, "%s ", ((d + 1) == cmd_nr) ? " or" : ","); 366 | } 367 | fprintf(stderr, "'%s'", cmd->ucmd); 368 | s_nr++; 369 | } 370 | cmd++; 371 | } 372 | fprintf(stderr, "?\n"); 373 | } 374 | 375 | return has_some_suggestion; 376 | } 377 | 378 | static int has_tool(const char *cmd) { 379 | char cmdline[4096]; 380 | #if defined(__unix__) 381 | snprintf(cmdline, sizeof(cmdline) - 1, "%s > /dev/null", cmd); 382 | #elif defined(_WIN32) 383 | snprintf(cmdline, sizeof(cmdline) - 1, "%s", cmd); 384 | #else 385 | # error Some code wanted. 386 | #endif 387 | return system(cmd); 388 | } 389 | 390 | static int has_more(void) { 391 | #if defined(__unix__) 392 | return (has_tool("more -V") == 0); 393 | #elif defined(_WIN32) 394 | return (has_tool("more /h ") == 0); 395 | #else 396 | # error Some code wanted. 397 | #endif 398 | } 399 | 400 | static int has_less(void) { 401 | return (has_tool("less --version") == 0); 402 | } 403 | -------------------------------------------------------------------------------- /src/test/.ivk: -------------------------------------------------------------------------------- 1 | --forgefiles=Forgefile.hsl --Forgefile-projects=lethe-tests --includes=cutest/src,../ --libraries=cutest/src/lib --ldflags=-lcutest --bin-output-dir=bin --obj-output-dir=o 2 | -------------------------------------------------------------------------------- /src/test/Forgefile.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 by Rafael Santiago 3 | # 4 | # Use of this source code is governed by GPL-v2 license that can 5 | # be found in the COPYING file. 6 | # 7 | 8 | include ../forgeutils/toolset.hsl 9 | include ~/fsutil.hsl 10 | include ~/lsutil.hsl 11 | 12 | var src type list; 13 | var includes type list; 14 | var cflags type list; 15 | var libraries type list; 16 | var ldflags type list; 17 | var appname type string; 18 | 19 | var curr_toolset type string; 20 | 21 | project lethe-tests : toolset $curr_toolset : $src, $includes, $cflags, $libraries, $ldflags, $appname; 22 | 23 | lethe-tests.preloading() { 24 | $curr_toolset = get_app_toolset(); 25 | } 26 | 27 | lethe-tests.prologue() { 28 | if (hefesto.sys.os_name() != "windows") { 29 | set_devpath(); 30 | } 31 | if (build_libcutest() != 0) { 32 | hefesto.project.abort(1); 33 | } 34 | $src.ls(".*\\.c$"); 35 | $includes = hefesto.sys.get_option("includes"); 36 | $cflags = hefesto.sys.get_option("cflags"); 37 | $libraries = hefesto.sys.get_option("libraries"); 38 | $ldflags = hefesto.sys.get_option("ldflags"); 39 | if (hefesto.sys.os_name() == "linux") { 40 | $ldflags.add_item("-lpthread"); 41 | $ldflags.add_item("-ldl"); 42 | } else if (hefesto.sys.os_name() == "freebsd" || 43 | hefesto.sys.os_name() == "netbsd" || 44 | hefesto.sys.os_name() == "openbsd") { 45 | $ldflags.add_item("-lexecinfo"); 46 | $ldflags.add_item("-lpthread"); 47 | } else if (hefesto.sys.os_name() == "minix") { 48 | $ldflags.add_item("-lexecinfo"); 49 | } else if (hefesto.sys.os_name() == "sunos") { 50 | $ldflags.add_item("-lpthread"); 51 | $cflags.add_item("-DNO_CUTEST_BACKTRACING=1"); 52 | } 53 | get_lethe_objects($ldflags); 54 | $appname = "lethe-tests"; 55 | if (hefesto.sys.os_name() == "windows") { 56 | $appname = $appname + ".exe"; 57 | } else { 58 | if (isdir("/usr/lib")) { 59 | $libraries.add_item("/usr/lib"); 60 | } 61 | if (isdir("/usr/local/lib")) { 62 | $libraries.add_item("/usr/local/lib"); 63 | } 64 | if (isdir("/usr/local/include")) { 65 | $includes.add_item("/usr/local/include"); 66 | } 67 | } 68 | var option type list; 69 | $option = hefesto.sys.get_option("forge-type"); 70 | if ($option.item(0) == "lib") { 71 | $cflags.add_item("-DLETHE_LIB=1"); 72 | } else if ($option.item(0) == "tool") { 73 | $cflags.add_item("-DLETHE_TOOL=1"); 74 | build_dynrand(); 75 | } 76 | } 77 | 78 | lethe-tests.epilogue() { 79 | if (hefesto.sys.last_forge_result() == 0) { 80 | var exit_code type int; 81 | $exit_code = hefesto.sys.run(hefesto.sys.make_path("bin", $appname) + 82 | " --cutest-leak-check=yes " + hefesto.project.cmdline()); 83 | if ($exit_code == 0) { 84 | hefesto.sys.echo("*** BUILD SUCCESS.\n"); 85 | } else { 86 | hefesto.sys.echo("~~~ BUILD FAIL.\n"); 87 | } 88 | } 89 | } 90 | 91 | local function get_lethe_objects(ldflags type list) : result type none { 92 | var old_cwd type string; 93 | $old_cwd = hefesto.sys.pwd(); 94 | 95 | if (hefesto.sys.cd("../o") != 1) { 96 | hefesto.sys.echo("BUILD ERROR: Unable to get object files. Have you built the library or tool before?\n"); 97 | hefesto.project.abort(1); 98 | } 99 | 100 | var l type int; 101 | $l = $ldflags.count() + 1; 102 | 103 | $ldflags.ls(".*\\.o$"); 104 | 105 | while ($l < $ldflags.count()) { 106 | var fpath type string; 107 | $fpath = $ldflags.item($l); 108 | if ($fpath.match(".*main\\.o$") == 1) { 109 | $ldflags.del_index($l); 110 | break; 111 | } 112 | $l = $l + 1; 113 | } 114 | 115 | if (hefesto.sys.os_name() == "windows") { 116 | $l = 0; 117 | while ($l < $ldflags.count()) { 118 | $fpath = $ldflags.item($l); 119 | if ($fpath.match(".*\\.o$") == 1) { 120 | $fpath = "\"" + $fpath + "\""; 121 | $ldflags.del_index($l); 122 | $ldflags.add_item($fpath); 123 | } else { 124 | $l = $l + 1; 125 | } 126 | } 127 | } 128 | 129 | hefesto.sys.byref($ldflags); 130 | hefesto.sys.cd($old_cwd); 131 | } 132 | 133 | local function build_libcutest() : result type int { 134 | if (isfile("cutest/src/lib/libcutest.a")) { 135 | result 0; 136 | } 137 | if (hefesto.sys.cd("cutest/src") != 1) { 138 | hefesto.sys.echo("BUILD ERROR: Cutest's submodule was not initialised.\n"); 139 | hefesto.project.abort(1); 140 | } 141 | var exit_code type int; 142 | $exit_code = hefesto.sys.run("hefesto"); 143 | hefesto.sys.cd("../../"); 144 | result $exit_code; 145 | } 146 | 147 | local function build_dynrand() : result type none { 148 | if (hefesto.sys.cd("dynrand")) { 149 | if (hefesto.sys.run("hefesto") != 0) { 150 | hefesto.project.abort(1); 151 | } 152 | hefesto.sys.cd(".."); 153 | } else { 154 | hefesto.sys.echo("BUILD ERROR: Project 'dynrand' not found.\n"); 155 | hefesto.project.abort(1); 156 | } 157 | } 158 | 159 | local function set_devpath() : result type none { 160 | var lines type list; 161 | $lines = hefesto.sys.lines_from_file("../DEV_PATH", ".*"); 162 | if ($lines.count() == 0) { 163 | $lines = hefesto.sys.lines_from_file("/etc/fstab", "^/.* / .*"); 164 | if ($lines.count() == 0) { 165 | $lines = hefesto.sys.lines_from_file("/etc/fstab", "^/.*\\t/\\t.*"); 166 | } 167 | var devpath type string; 168 | if ($lines.count() != 1) { 169 | $devpath = hefesto.sys.prompt("It was not possible to guess the device path of your '/'. Please provide this path: "); 170 | if ($devpath.len() == 0) { 171 | hefesto.sys.echo("ERROR: Unable to set your devpath. Aborting.\n"); 172 | hefesto.project.abort(1); 173 | } 174 | var devpath_confirm type string; 175 | $devpath_confirm = hefesto.sys.prompt("Confirm this path by inputing it again: "); 176 | if ($devpath != $devpath_confirm) { 177 | hefesto.sys.echo("ERROR: The paths do not match.\n"); 178 | hefesto.project.abort(1); 179 | } 180 | $devpath = $devpath + "\n"; 181 | } else { 182 | $devpath = $lines.item(0); 183 | $devpath.replace("[\\t ].*/.*$", "\n"); 184 | } 185 | var fp type file; 186 | $fp = hefesto.sys.fopen("../DEV_PATH", "w"); 187 | if ($fp == 0) { 188 | hefesto.sys.echo("ERROR: Unable to save device path to ../DEV_PATH config file.\n"); 189 | hefesto.project.abort(1); 190 | } 191 | hefesto.sys.fwrite($devpath, $devpath.len(), $fp); 192 | hefesto.sys.fclose($fp); 193 | $devpath.replace("\\n", ""); 194 | hefesto.sys.echo("======================================================================================================================\n"); 195 | hefesto.sys.echo("INFO: The lethe_drop() tests will use '" + $devpath + "' if lethe's source code tree is not stored into this\n" + 196 | " device you need to change the content of '../DEV_PATH' to the correct device path where this repository is\n" + 197 | " actually stored. Otherwise some tests will break and it will be impossible to test if Lethe is really working on\n" + 198 | " your system.\n"); 199 | hefesto.sys.echo("======================================================================================================================\n\n"); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/test/dynrand/.ivk: -------------------------------------------------------------------------------- 1 | --forgefiles=Forgefile.hsl --Forgefile-projects=dynrand --link-model=shared --obj-output-dir=o --bin-output-dir=lib 2 | -------------------------------------------------------------------------------- /src/test/dynrand/Forgefile.hsl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 by Rafael Santiago 3 | # 4 | # Use of this source code is governed by GPL-v2 license that can 5 | # be found in the COPYING file. 6 | # 7 | 8 | include ../../forgeutils/toolset.hsl 9 | 10 | local var src type list; 11 | local var includes type list; 12 | local var cflags type list; 13 | local var libraries type list; 14 | local var ldflags type list; 15 | local var libname type string; 16 | local var curr_tool type string; 17 | 18 | project dynrand : toolset $curr_tool : $src, $includes, $cflags, $libraries, $ldflags, $libname; 19 | 20 | dynrand.preloading() { 21 | $curr_tool = get_lib_toolset(); 22 | $libname = "dynrand"; 23 | if (hefesto.sys.os_name() != "windows") { 24 | $libname = $libname + ".so"; 25 | $cflags.add_item("-fPIC"); 26 | } else { 27 | $libname = $libname + ".dll"; 28 | } 29 | } 30 | 31 | dymrand.prologue() { 32 | $src.ls(".*\\.c$"); 33 | $includes = hefesto.sys.get_option("includes"); 34 | $cflags = hefesto.sys.get_option("cflags"); 35 | $libraries = hefesto.sys.get_option("libraries"); 36 | $ldflags = hefesto.sys.get_option("ldflags"); 37 | } 38 | -------------------------------------------------------------------------------- /src/test/dynrand/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | 11 | unsigned char norand(void) { 12 | static int initd = 0; 13 | 14 | if (initd == 0) { 15 | srand(time(NULL)); 16 | initd = 1; 17 | } 18 | 19 | return rand() % 256; 20 | } 21 | -------------------------------------------------------------------------------- /src/test/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 by Rafael Santiago 3 | * 4 | * Use of this source code is governed by GPL-v2 license that can 5 | * be found in the COPYING file. 6 | * 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #if defined(LETHE_TOOL) 15 | # include 16 | #endif 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | int stat_call_nr = 0; 25 | 26 | int randomizer_call_nr = 0; 27 | 28 | int memcpy_nr = 0; 29 | 30 | int memcmp_nr = 0; 31 | 32 | int memset_nr = 0; 33 | 34 | static int stat_wrapper(const char *pathname, struct stat *buf); 35 | 36 | static unsigned char randomizer_wrapper(void); 37 | 38 | static int has_foremost(void); 39 | 40 | static int has_grep(void); 41 | 42 | #if defined(__unix__) 43 | static int has_found_by_foremost(const char *signature, const size_t signature_size, const char *output_dir); 44 | 45 | static int write_foremost_config(const char *header, const size_t header_size, const char *footer, const size_t footer_size, const char *conf); 46 | #endif 47 | 48 | #if defined(__unix__) 49 | static int has_found_by_grep(const char *devpath, const char *signature); 50 | 51 | static char *get_devpath(void); 52 | #endif 53 | 54 | static char *get_random_printable_buffer(const size_t bytes_total); 55 | 56 | static int write_data_to_file(const char *filepath, const char *data, size_t data_size); 57 | 58 | static char *get_ndata_from_file(const char *filepath, const size_t data_size); 59 | 60 | static int lethe(const char *command, const char *user_choices); 61 | 62 | static int usr_memcmp(const void *b1, const void *b2, size_t len); 63 | 64 | static void *usr_memset(void *b, int c, size_t len); 65 | 66 | static void *usr_memcpy(void *dest, const void *src, size_t len); 67 | 68 | CUTE_DECLARE_TEST_CASE(lethe_tests_entry); 69 | 70 | CUTE_DECLARE_TEST_CASE(lethe_strglob_tests); 71 | CUTE_DECLARE_TEST_CASE(lethe_mkpath_tests); 72 | CUTE_DECLARE_TEST_CASE(lethe_default_randomizer_tests); 73 | CUTE_DECLARE_TEST_CASE(lethe_error_stuff_tests); 74 | #if defined(LETHE_TOOL) 75 | CUTE_DECLARE_TEST_CASE(lethe_option_stuff_tests); 76 | #endif 77 | CUTE_DECLARE_TEST_CASE(lethe_drop_tests); 78 | CUTE_DECLARE_TEST_CASE(lethe_tool_poke_tests); 79 | CUTE_DECLARE_TEST_CASE(lethe_set_memcmp_tests); 80 | CUTE_DECLARE_TEST_CASE(lethe_set_memcpy_tests); 81 | CUTE_DECLARE_TEST_CASE(lethe_set_memset_tests); 82 | 83 | CUTE_MAIN(lethe_tests_entry) 84 | 85 | CUTE_TEST_CASE(lethe_tests_entry) 86 | CUTE_RUN_TEST(lethe_set_memcmp_tests); 87 | CUTE_RUN_TEST(lethe_set_memcpy_tests); 88 | CUTE_RUN_TEST(lethe_set_memset_tests); 89 | CUTE_RUN_TEST(lethe_strglob_tests); 90 | CUTE_RUN_TEST(lethe_mkpath_tests); 91 | CUTE_RUN_TEST(lethe_default_randomizer_tests); 92 | CUTE_RUN_TEST(lethe_error_stuff_tests); 93 | #if defined(LETHE_TOOL) 94 | CUTE_RUN_TEST(lethe_option_stuff_tests); 95 | #endif 96 | CUTE_RUN_TEST(lethe_drop_tests); 97 | #if defined(LETHE_TOOL) 98 | CUTE_RUN_TEST(lethe_tool_poke_tests); 99 | #endif 100 | // INFO(Rafael): Until now lethe_memcmp is available only for future necessities, no calls to memcmp are being done here. 101 | CUTE_ASSERT(memcmp_nr == 0); 102 | CUTE_ASSERT(memcpy_nr > 0); 103 | CUTE_ASSERT(memset_nr > 0); 104 | CUTE_TEST_CASE_END 105 | 106 | CUTE_TEST_CASE(lethe_set_memcmp_tests) 107 | CUTE_ASSERT(lethe_set_memcmp(NULL) != 0); 108 | CUTE_ASSERT(lethe_set_memcmp(usr_memcmp) == 0); 109 | CUTE_TEST_CASE_END 110 | 111 | CUTE_TEST_CASE(lethe_set_memcpy_tests) 112 | CUTE_ASSERT(lethe_set_memcpy(NULL) != 0); 113 | CUTE_ASSERT(lethe_set_memcpy(usr_memcpy) == 0); 114 | CUTE_TEST_CASE_END 115 | 116 | CUTE_TEST_CASE(lethe_set_memset_tests) 117 | CUTE_ASSERT(lethe_set_memset(NULL) != 0); 118 | CUTE_ASSERT(lethe_set_memset(usr_memset) == 0); 119 | CUTE_TEST_CASE_END 120 | 121 | CUTE_TEST_CASE(lethe_tool_poke_tests) 122 | struct stat st; 123 | 124 | CUTE_ASSERT(lethe("help", NULL) == 0); 125 | CUTE_ASSERT(lethe("help drop", NULL) == 0); 126 | CUTE_ASSERT(lethe("help man", NULL) == 0); 127 | CUTE_ASSERT(lethe("help version", NULL) != 0); 128 | 129 | #if defined(__unix__) 130 | CUTE_ASSERT(mkdir("lethe-tool-poking", 0666) == 0); 131 | CUTE_ASSERT(mkdir("lethe-tool-poking/villains", 0666) == 0); 132 | #elif defined(_WIN32) 133 | CUTE_ASSERT(mkdir("lethe-tool-poking") == 0); 134 | CUTE_ASSERT(mkdir("lethe-tool-poking\\villains") == 0); 135 | #else 136 | # error Some code wanted. 137 | #endif 138 | 139 | CUTE_ASSERT(chdir("lethe-tool-poking") == 0); 140 | 141 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.txt", "It does not matter now", 22) == 0); 142 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.png", "It does not matter now", 22) == 0); 143 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.gif", "It does not matter now", 22) == 0); 144 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.jpg", "It does not matter now", 22) == 0); 145 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.dat", "It does not matter now", 22) == 0); 146 | 147 | CUTE_ASSERT(write_data_to_file("Domesticated-Animals.txt", "Today is the day...", 19) == 0); 148 | 149 | CUTE_ASSERT(write_data_to_file("villains/Fortress.txt", "Your head is like a fortress", 28) == 0); 150 | 151 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt", "Y") == 0); 152 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.png", "n") == 0); 153 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.gif", "y") == 0); 154 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.jpg", "N") == 0); 155 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.dat", "y") == 0); 156 | 157 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.txt", &st) != 0); 158 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.png", &st) == 0); 159 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.gif", &st) != 0); 160 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.jpg", &st) == 0); 161 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.dat", &st) != 0); 162 | 163 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.txt", "It does not matter now", 22) == 0); 164 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.gif", "It does not matter now", 22) == 0); 165 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.dat", "It does not matter now", 22) == 0); 166 | 167 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.* Domesticated-Animals.txt villains/* --ask-me-nothing", NULL) == 0); 168 | 169 | chdir(".."); 170 | 171 | CUTE_ASSERT(lethe("drop lethe-*-poking --ask-me-nothing", NULL) == 0); 172 | 173 | CUTE_ASSERT(stat("lethe-tool-poking", &st) != 0); 174 | 175 | // INFO(Rafael): Now the last test steps will repeated by using a dynamic randomizer. 176 | 177 | #if defined(__unix__) 178 | CUTE_ASSERT(mkdir("lethe-tool-poking", 0666) == 0); 179 | CUTE_ASSERT(mkdir("lethe-tool-poking/villains", 0666) == 0); 180 | #elif defined(_WIN32) 181 | CUTE_ASSERT(mkdir("lethe-tool-poking") == 0); 182 | CUTE_ASSERT(mkdir("lethe-tool-poking\\villains") == 0); 183 | #else 184 | # error Some code wanted. 185 | #endif 186 | 187 | CUTE_ASSERT(chdir("lethe-tool-poking") == 0); 188 | 189 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.txt", "It does not matter now", 22) == 0); 190 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.png", "It does not matter now", 22) == 0); 191 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.gif", "It does not matter now", 22) == 0); 192 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.jpg", "It does not matter now", 22) == 0); 193 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.dat", "It does not matter now", 22) == 0); 194 | 195 | CUTE_ASSERT(write_data_to_file("Domesticated-Animals.txt", "Today is the day...", 19) == 0); 196 | 197 | CUTE_ASSERT(write_data_to_file("villains/Fortress.txt", "Your head is like a fortress", 28) == 0); 198 | 199 | #if defined(__unix__) 200 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=../dynrand/lib/dynrand1.so:norand", "Y") != 0); 201 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=../dynrand/lib/dynrand.so:norandom", "Y") != 0); 202 | #elif defined(_WIN32) 203 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=../dynrand/lib/dynrand1.dll:norand", "Y") != 0); 204 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=../dynrand/lib/dynrand.dll:norandom", "Y") != 0); 205 | #else 206 | # error Some code wanted. 207 | #endif 208 | 209 | #if defined(__unix__) 210 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=../dynrand/lib/dynrand.so:norand", "Y") == 0); 211 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.png --dyn-randomizer=../dynrand/lib/dynrand.so:norand", "n") == 0); 212 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.gif --dyn-randomizer=../dynrand/lib/dynrand.so:norand", "y") == 0); 213 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.jpg --dyn-randomizer=../dynrand/lib/dynrand.so:norand", "N") == 0); 214 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.dat --dyn-randomizer=../dynrand/lib/dynrand.so:norand", "y") == 0); 215 | #elif defined(_WIN32) 216 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.txt --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", "Y") == 0); 217 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.png --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", "n") == 0); 218 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.gif --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", "y") == 0); 219 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.jpg --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", "N") == 0); 220 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.dat --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", "y") == 0); 221 | #else 222 | # error Some code wanted. 223 | #endif 224 | 225 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.txt", &st) != 0); 226 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.png", &st) == 0); 227 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.gif", &st) != 0); 228 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.jpg", &st) == 0); 229 | CUTE_ASSERT(stat("The-Way-You-Used-To-Do.dat", &st) != 0); 230 | 231 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.txt", "It does not matter now", 22) == 0); 232 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.gif", "It does not matter now", 22) == 0); 233 | CUTE_ASSERT(write_data_to_file("The-Way-You-Used-To-Do.dat", "It does not matter now", 22) == 0); 234 | 235 | #if defined(__unix__) 236 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.* Domesticated-Animals.txt villains/* --ask-me-nothing" 237 | " --dyn-randomizer=../dynrand/lib/dynrand.so:norand", NULL) == 0); 238 | #elif defined(_WIN32) 239 | CUTE_ASSERT(lethe("drop The-Way-You-Used-To-Do.* Domesticated-Animals.txt villains/* --ask-me-nothing" 240 | " --dyn-randomizer=..\\dynrand\\lib\\dynrand.dll:norand", NULL) == 0); 241 | #else 242 | # error Some code wanted. 243 | #endif 244 | 245 | chdir(".."); 246 | 247 | #if defined(__unix__) 248 | CUTE_ASSERT(lethe("drop lethe-*-poking --ask-me-nothing --dyn-randomizer=dynrand/lib/dynrand.so:norand", NULL) == 0); 249 | #elif defined(_WIN32) 250 | CUTE_ASSERT(lethe("drop lethe-*-poking --ask-me-nothing --dyn-randomizer=dynrand/lib/dynrand.dll:norand", NULL) == 0); 251 | #else 252 | # error Some code wanted. 253 | #endif 254 | 255 | CUTE_ASSERT(stat("lethe-tool-poking", &st) != 0); 256 | CUTE_TEST_CASE_END 257 | 258 | CUTE_TEST_CASE(lethe_drop_tests) 259 | char *buf, *temp; 260 | struct stat st; 261 | char cmdline[4096]; 262 | size_t buf_size; 263 | 264 | CUTE_ASSERT(lethe_set_stat(NULL) != 0); 265 | CUTE_ASSERT(lethe_set_stat(stat_wrapper) == 0); 266 | CUTE_ASSERT(lethe_set_rename_nr(0) != 0); 267 | CUTE_ASSERT(lethe_set_overwrite_nr(-1) != 0); 268 | CUTE_ASSERT(lethe_set_overwrite_nr(10203102) == 0); 269 | CUTE_ASSERT(lethe_set_overwrite_nr(1) == 0); 270 | system("rm -rf lethe-lab"); 271 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 272 | // WARN(Rafael): At this point do not remove the chdir stuff otherwise you can lose important files.!! 273 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 274 | #if defined(__unix__) 275 | CUTE_ASSERT(mkdir("lethe-lab", 0666) == 0); 276 | #elif defined(_WIN32) 277 | CUTE_ASSERT(mkdir("lethe-lab") == 0); 278 | #else 279 | # error Some code wanted. 280 | #endif 281 | CUTE_ASSERT(chdir("lethe-lab") == 0); 282 | buf = get_random_printable_buffer(10); 283 | CUTE_ASSERT(write_data_to_file("data.txt", buf, 10) == 0); 284 | CUTE_ASSERT(lethe_drop("pata.txt", kLetheDataOblivion) != 0); 285 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion) == 0); 286 | CUTE_ASSERT(stat("data.txt", &st) == 0); 287 | temp = get_ndata_from_file("data.txt", 10); 288 | CUTE_ASSERT(temp != NULL); 289 | CUTE_ASSERT(memcmp(temp, buf, 10) != 0); 290 | free(buf); 291 | free(temp); 292 | CUTE_ASSERT(remove("data.txt") == 0); 293 | buf = get_random_printable_buffer(10); 294 | CUTE_ASSERT(write_data_to_file("data.txt", buf, 10) == 0); 295 | free(buf); 296 | CUTE_ASSERT(stat("data.txt", &st) == 0); 297 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion | kLetheFileRemove) == 0); 298 | CUTE_ASSERT(stat("data.txt", &st) != 0); 299 | buf = get_random_printable_buffer(10); 300 | CUTE_ASSERT(write_data_to_file("data.txt", buf, 10) == 0); 301 | free(buf); 302 | CUTE_ASSERT(write_data_to_file("data.txt", buf, 10) == 0); 303 | CUTE_ASSERT(stat("data.txt", &st) == 0); 304 | #if defined(__unix__) 305 | CUTE_ASSERT(mkdir("sub-dir", 0666) == 0); 306 | #elif defined(_WIN32) 307 | CUTE_ASSERT(mkdir("sub-dir") == 0); 308 | #else 309 | # error Some code wanted. 310 | #endif 311 | CUTE_ASSERT(write_data_to_file("sub-dir/do-not-forget-the-joker.txt", buf, 10) == 0); 312 | #if defined(__unix__) 313 | CUTE_ASSERT(mkdir("sub-dir/empty", 0666) == 0); 314 | #elif defined(_WIN32) 315 | CUTE_ASSERT(mkdir("sub-dir/empty") == 0); 316 | #else 317 | # error Some code wanted. 318 | #endif 319 | CUTE_ASSERT(chdir("..") == 0); 320 | CUTE_ASSERT(lethe_drop("lethe-la[b]", kLetheDataOblivion | kLetheFileRemove | kLetheCustomRandomizer, 321 | randomizer_wrapper) == 0); 322 | CUTE_ASSERT(stat("lethe-lab", &st) != 0); 323 | CUTE_ASSERT(stat_call_nr > 0); 324 | CUTE_ASSERT(randomizer_call_nr > 0); 325 | 326 | if (CUTE_GET_OPTION("quick-tests") == NULL) { 327 | // INFO(Rafael): Now we will test it against foremost if possible. 328 | 329 | if (has_foremost()) { 330 | #if defined(__unix__) 331 | system("rm -rf recovery"); 332 | fprintf(stdout, "INFO: Nice, you have Foremost installed. Let's check if it can caught files removed by Lethe.\n" 333 | " Firstly, we will generate some control data and try to find it with Foremost.\n"); 334 | 335 | buf_size = 100010; 336 | 337 | buf = get_random_printable_buffer(buf_size); 338 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 339 | sleep(15); 340 | CUTE_ASSERT(system("sync data.txt") == 0); 341 | sleep(15); 342 | 343 | CUTE_ASSERT(write_foremost_config(buf, 10, buf + buf_size - 10, 10, "foremost.conf") == 0); 344 | CUTE_ASSERT(system("rm -f data.txt") == 0); 345 | fprintf(stdout, " Control data was removed. Now trying to recover it with Foremost... Hold on...\n"); 346 | 347 | // INFO(Rafael): This is the control data. Foremost must be able to recover this piece of information. 348 | snprintf(cmdline, sizeof(cmdline) - 1, "foremost -i %s -c foremost.conf -o recovery", get_devpath()); 349 | CUTE_ASSERT(system(cmdline) == 0); 350 | 351 | // INFO(Rafael): Since the search is being done over an on-line device, depending on the disk activity 352 | // the entire file will not be actually recovered. Due to it, a half file recovery will be considered 353 | // a successfuly file recovering. 354 | 355 | CUTE_ASSERT(has_found_by_foremost(buf, buf_size >> 1, "recovery") == 1); 356 | 357 | fprintf(stdout, "INFO: Nice, control data was actually found by Foremost.\n"); 358 | 359 | CUTE_ASSERT(system("rm -rf recovery") == 0); 360 | CUTE_ASSERT(remove("foremost.conf") == 0); 361 | 362 | free(buf); 363 | 364 | fprintf(stdout, " Now we will generate a random piece of data, remove it by using Lethe and try to recover\n" 365 | " it with Foremost. Wait...\n"); 366 | 367 | buf_size = 100010; 368 | buf = get_random_printable_buffer(buf_size); 369 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 370 | sleep(15); 371 | CUTE_ASSERT(system("sync data.txt") == 0); 372 | sleep(15); 373 | 374 | CUTE_ASSERT(write_foremost_config(buf, 10, buf + buf_size - 10, 10, "foremost.conf") == 0); 375 | 376 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion | kLetheFileRemove) == 0); 377 | 378 | fprintf(stdout, " Test data was removed by using Lethe. Now trying to recover it with Foremost...\n" 379 | " Hold on...\n"); 380 | 381 | // INFO(Rafael): This is the test data. Foremost must not be able to recover this piece of information. 382 | snprintf(cmdline, sizeof(cmdline) - 1, "foremost -i %s -c foremost.conf -o recovery", get_devpath()); 383 | CUTE_ASSERT(system(cmdline) == 0); 384 | 385 | CUTE_ASSERT(has_found_by_foremost(buf, buf_size >> 1, "recovery") == 0); 386 | 387 | fprintf(stdout, "INFO: Everything looks fine on your system!\n" 388 | " Foremost could not recover data removed by Lethe ;)\n"); 389 | 390 | CUTE_ASSERT(system("rm -rf recovery") == 0); 391 | CUTE_ASSERT(remove("foremost.conf") == 0); 392 | 393 | free(buf); 394 | 395 | fprintf(stdout, "\nINFO: Now let's only test data oblivion. File will stay but its content will be forgotten.\n"); 396 | 397 | buf_size = 100010; 398 | buf = get_random_printable_buffer(buf_size); 399 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 400 | sleep(15); 401 | CUTE_ASSERT(system("sync data.txt") == 0); 402 | sleep(15); 403 | 404 | fprintf(stdout, " Test data created... Now let's forget this content by using Lethe... Wait...\n"); 405 | 406 | CUTE_ASSERT(write_foremost_config(buf, 10, buf + buf_size - 10, 10, "foremost.conf") == 0); 407 | 408 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion) == 0); 409 | 410 | CUTE_ASSERT(stat("data.txt", &st) == 0); 411 | 412 | fprintf(stdout, " Done. Now trying to recover it with Foremost... Hold on...\n"); 413 | 414 | snprintf(cmdline, sizeof(cmdline) - 1, "foremost -i %s -c foremost.conf -o recovery", get_devpath()); 415 | CUTE_ASSERT(system(cmdline) == 0); 416 | 417 | CUTE_ASSERT(has_found_by_foremost(buf, buf_size, "recovery") == 0); 418 | CUTE_ASSERT(has_found_by_foremost(buf, buf_size >> 1, "recovery") == 0); 419 | 420 | fprintf(stdout, "INFO: Nice! Foremost could not recover data forgotten with Lethe ;)\n"); 421 | 422 | CUTE_ASSERT(remove("data.txt") == 0); 423 | CUTE_ASSERT(system("rm -rf recovery") == 0); 424 | CUTE_ASSERT(remove("foremost.conf") == 0); 425 | 426 | free(buf); 427 | #endif 428 | } else { 429 | if (has_grep()) { 430 | #if defined(__unix__) 431 | fprintf(stdout, "INFO: Nice, you have cat, strings and grep installed. Let's check if them can caught files removed by Lethe.\n" 432 | " Firstly, we will generate some control data and try to find it with these set of tools.\n"); 433 | 434 | buf_size = 20; 435 | 436 | buf = get_random_printable_buffer(buf_size); 437 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 438 | sleep(15); 439 | CUTE_ASSERT(system("sync data.txt") == 0); 440 | sleep(15); 441 | 442 | CUTE_ASSERT(system("rm -f data.txt") == 0); 443 | fprintf(stdout, " Control data was removed. Now trying to recover it with grep and stuff... Hold on...\n"); 444 | 445 | 446 | CUTE_ASSERT(has_found_by_grep(get_devpath(), buf) == 1); 447 | 448 | fprintf(stdout, "INFO: Nice, control data was actually found by grep.\n"); 449 | 450 | free(buf); 451 | 452 | fprintf(stdout, " Now we will generate a random piece of data, remove it by using Lethe and try to recover\n" 453 | " it with grep and stuff. Wait...\n"); 454 | 455 | buf_size = 20; 456 | buf = get_random_printable_buffer(buf_size); 457 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 458 | sleep(15); 459 | CUTE_ASSERT(system("sync data.txt") == 0); 460 | sleep(15); 461 | 462 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion | kLetheFileRemove) == 0); 463 | 464 | fprintf(stdout, " Test data was removed by using Lethe. Now trying to recover it with grep and stuff...\n" 465 | " Hold on...\n"); 466 | 467 | CUTE_ASSERT(has_found_by_grep(get_devpath(), buf) == 0); 468 | 469 | fprintf(stdout, "INFO: Everything looks fine on your system!\n" 470 | " It could not recover data removed by Lethe ;)\n"); 471 | 472 | free(buf); 473 | 474 | fprintf(stdout, "\nINFO: Now let's only test data oblivion. File will stay but its content will be forgotten.\n"); 475 | 476 | buf_size = 20; 477 | buf = get_random_printable_buffer(buf_size); 478 | CUTE_ASSERT(write_data_to_file("data.txt", buf, buf_size) == 0); 479 | sleep(15); 480 | if (system("fsync data.txt") != 0) { 481 | CUTE_ASSERT(system("sync data.txt") == 0); 482 | } 483 | sleep(15); 484 | 485 | fprintf(stdout, " Test data created... Now let's forget this content by using Lethe... Wait...\n"); 486 | 487 | CUTE_ASSERT(lethe_drop("data.txt", kLetheDataOblivion) == 0); 488 | 489 | CUTE_ASSERT(stat("data.txt", &st) == 0); 490 | 491 | fprintf(stdout, " Done. Now trying to recover it with grep and stuff... Hold on...\n"); 492 | 493 | CUTE_ASSERT(has_found_by_grep(get_devpath(), buf) == 0); 494 | 495 | fprintf(stdout, "INFO: Nice! Grep and stuff could not recover data forgotten with Lethe ;)\n"); 496 | 497 | CUTE_ASSERT(remove("data.txt") == 0); 498 | free(buf); 499 | #endif 500 | } else { 501 | #if defined(__unix__) 502 | fprintf(stdout, "WARN: Unfortunately, this test cannot really ensure if the implemented data wiping is\n" 503 | " actually working on your system. For doing it you need to install 'Foremost' data\n" 504 | " recovery tool.\n"); 505 | #else 506 | fprintf(stdout, "WARN: Unfortunately, this test cannot really ensure if the implemented data wiping is\n" 507 | " actually working on your system. Until now, Lethe needs Foremost for doing it, thus\n" 508 | " it is only available on Unix-like environments.\n"); 509 | #endif 510 | } 511 | } 512 | } else { 513 | fprintf(stdout, "WARN: Some test steps were skipped here.\n"); 514 | } 515 | CUTE_TEST_CASE_END 516 | 517 | #if defined(LETHE_TOOL) 518 | CUTE_TEST_CASE(lethe_option_stuff_tests) 519 | char *argv[] = { 520 | "./lethe", 521 | "test", 522 | "--foo=bar", 523 | "--bar=foo", 524 | "--foobar" 525 | }; 526 | int argc = 5, a; 527 | struct option_tests { 528 | int bool; 529 | char *option; 530 | char *default_s_value, *expected_s_value; 531 | int default_b_value, expected_b_value; 532 | } test_vector[] = { 533 | { 0, "foo", "foo", "bar", 0, 0 }, 534 | { 0, "bar", "bar", "foo", 0, 0 }, 535 | { 1, "foobar", NULL, NULL, 0, 1 }, 536 | { 1, "barfoo", NULL, NULL, 1, 1 }, 537 | { 0, "Zzzz", "Rooc-fiu...", "Rooc-fiu...", 0, 0 } 538 | }, *test, *test_end; 539 | char *data; 540 | 541 | CUTE_ASSERT((data = lethe_get_option("boom", "Muhauhahuahauha!!!")) != NULL); 542 | CUTE_ASSERT(strcmp(data, "Muhauhahuahauha!!!") == 0); 543 | 544 | CUTE_ASSERT(lethe_get_bool_option("boom", 0) == 0); 545 | 546 | lethe_option_set_argc_argv(0, argv); 547 | 548 | CUTE_ASSERT(lethe_get_ucmd() == NULL); 549 | CUTE_ASSERT(lethe_get_argv(0) == NULL); 550 | 551 | lethe_option_set_argc_argv(1, argv); 552 | 553 | CUTE_ASSERT(lethe_get_ucmd() == NULL); 554 | CUTE_ASSERT(lethe_get_argv(0) == NULL); 555 | 556 | lethe_option_set_argc_argv(2, argv); 557 | 558 | data = lethe_get_ucmd(); 559 | CUTE_ASSERT(data != NULL); 560 | CUTE_ASSERT(strcmp(data, "test") == 0); 561 | CUTE_ASSERT(lethe_get_argv(0) == NULL); 562 | CUTE_ASSERT(lethe_get_argv(1010101) == NULL); 563 | 564 | lethe_option_set_argc_argv(argc, argv); 565 | 566 | CUTE_ASSERT(lethe_get_bool_option("test", 0) == 0); 567 | 568 | data = lethe_get_ucmd(); 569 | CUTE_ASSERT(strcmp(data, "test") == 0); 570 | 571 | test = &test_vector[0]; 572 | test_end = test + sizeof(test_vector) / sizeof(test_vector[0]); 573 | 574 | while (test != test_end) { 575 | if (test->bool) { 576 | CUTE_ASSERT(lethe_get_bool_option(test->option, test->default_b_value) == test->expected_b_value); 577 | } else { 578 | CUTE_ASSERT((data = lethe_get_option(test->option, test->default_s_value)) != NULL); 579 | CUTE_ASSERT(strcmp(data, test->expected_s_value) == 0); 580 | } 581 | test++; 582 | } 583 | 584 | for (a = 2; a < argc; a++) { 585 | data = lethe_get_argv(a - 2); 586 | CUTE_ASSERT(data != NULL); 587 | CUTE_ASSERT(strcmp(data, argv[a]) == 0); 588 | } 589 | 590 | for (a = argc; a < 256; a++) { 591 | CUTE_ASSERT(lethe_get_argv(a) == NULL); 592 | } 593 | CUTE_TEST_CASE_END 594 | #endif 595 | 596 | CUTE_TEST_CASE(lethe_error_stuff_tests) 597 | struct lethe_error_caiau_ctx { 598 | int error_code; 599 | char *filepath; 600 | char *expected_message; 601 | } test_vector[] = { 602 | { kLetheErrorNone, "At/the/helm/of/hells/ships", "Success." }, // ;P 603 | { kLetheErrorNullFile, "", "Null file name." }, 604 | { kLetheErrorNullDropType, "", "Null drop type." }, 605 | { kLetheErrorUnableToAccess, "your/butt/butt-head/he/he/he", "Unable to access file 'your/butt/butt-head/he/he/he'." }, 606 | { kLetheErrorOpenHasFailed, "Beavis.uhhhh.'mouth'.hu-hu-hu!", "Unable to open file 'Beavis.uhhhh.'mouth'.hu-hu-hu!'." }, 607 | { kLetheErrorDataOblivionHasFailed, "Eggs.Duh", "Unable to scramble data from file 'Eggs.Duh'." }, 608 | { kLetheErrorFileRemoveHasFailed, "Caneta_Azul", "Unable to remove file 'Caneta_Azul'." }, // ROM 'cached'. 609 | { kLetheErrorNothingToDrop, "", "Nothing to drop." }, 610 | { kLetheErrorNr, "Oh! yeah! hehehehe huhhhuhu Destroy! Destroy! Destroy!!! hehehehe hhuhuhuh", 611 | "You have found a unicorn! Congrats!" } // I am a crazy person, just ignore. 612 | }, *test, *test_end; 613 | char buf[1024]; 614 | 615 | test = &test_vector[0]; 616 | test_end = test + sizeof(test_vector) / sizeof(test_vector[0]); 617 | 618 | while (test != test_end) { 619 | lethe_set_error_code(test->error_code); 620 | lethe_set_last_filepath(test->filepath); 621 | CUTE_ASSERT(lethe_get_last_error(NULL, sizeof(buf)) == NULL); 622 | CUTE_ASSERT(lethe_get_last_error(buf, sizeof(buf)) == &buf[0]); 623 | CUTE_ASSERT(strcmp(buf, test->expected_message) == 0); 624 | lethe_set_last_filepath(NULL); 625 | CUTE_ASSERT(lethe_get_last_error(buf, sizeof(buf)) == &buf[0]); // INFO(Rafael): It must not explode at our faces. 626 | test++; 627 | } 628 | CUTE_TEST_CASE_END 629 | 630 | CUTE_TEST_CASE(lethe_default_randomizer_tests) 631 | int p; 632 | // INFO(Rafael): It cannot block. If you are using a well-tested build of your OS it wont! ;) 633 | for (p = 0; p < 1000; p++) { 634 | lethe_default_randomizer(); 635 | } 636 | CUTE_TEST_CASE_END 637 | 638 | CUTE_TEST_CASE(lethe_mkpath_tests) 639 | struct mkpath_tests_ctx { 640 | const char *a; 641 | const char *b; 642 | const char *expected; 643 | } test_vector[] = { 644 | #if defined(__unix__) 645 | { "Raja", "Haje", "Raja/Haje" }, 646 | { "Raja/", "Haje", "Raja/Haje" }, 647 | { "Raja" , "/Haje", "Raja/Haje" }, 648 | { "Raja/", "/Haje", "Raja/Haje" }, 649 | { "Raja/////////", "//////////Haje","Raja/Haje"}, 650 | { "Raja////////", "Haje", "Raja/Haje"}, 651 | { "Raja", "///////////////////////////////////////////////Haje/", "Raja/Haje/" }, 652 | { "/Raja", "Haje", "/Raja/Haje" }, 653 | { "", "Haje", "Haje" }, 654 | { "Raja", "", "Raja" }, 655 | { "/", "/", "/" }, 656 | #elif defined(_WIN32) 657 | { "Raja", "Haje", "Raja\\Haje" }, 658 | { "Raja/", "Haje", "Raja\\Haje" }, 659 | { "Raja" , "/Haje", "Raja\\Haje" }, 660 | { "Raja/", "/Haje", "Raja\\Haje" }, 661 | { "Raja/////////", "//////////Haje","Raja\\Haje"}, 662 | { "Raja////////", "Haje", "Raja\\Haje"}, 663 | { "Raja", "///////////////////////////////////////////////Haje/", "Raja\\Haje/" }, 664 | { "/Raja", "Haje", "/Raja\\Haje" }, 665 | { "", "Haje", "Haje" }, 666 | { "Raja", "", "Raja" }, 667 | { "/", "/", "/" }, 668 | { "Raja\\", "Haje", "Raja\\Haje" }, 669 | { "Raja" , "/Haje", "Raja\\Haje" }, 670 | { "Raja\\", "/Haje", "Raja\\Haje" }, 671 | { "Raja\\\\\\\\\\\\\\\\\\", "\\\\\\\\\\\\\\\\\\\\Haje","Raja\\Haje"}, 672 | { "Raja\\\\\\\\\\\\\\\\", "Haje", "Raja\\Haje"}, 673 | { "Raja", "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Haje\\", "Raja\\Haje\\" }, 674 | { "C:\\Raja", "Haje", "C:\\Raja\\Haje" }, 675 | { "", "Haje", "Haje" }, 676 | { "Raja", "", "Raja" }, 677 | { "\\", "\\", "\\" }, 678 | #else 679 | # error Some code wanted. 680 | #endif 681 | { "", "", "" } 682 | }, *test, *test_end; 683 | char tiny_buf[1], buf[4096]; 684 | size_t a_size, b_size; 685 | 686 | test = &test_vector[0]; 687 | test_end = test + sizeof(test_vector) / sizeof(test_vector[0]); 688 | 689 | CUTE_ASSERT(lethe_mkpath(NULL, sizeof(buf), test->a, 4, test->b, 4) == NULL); 690 | 691 | CUTE_ASSERT(lethe_mkpath(buf, 0, test->a, 4, test->b, 4) == NULL); 692 | 693 | CUTE_ASSERT(lethe_mkpath(buf, sizeof(buf), NULL, 4, test->b, 4) == NULL); 694 | 695 | CUTE_ASSERT(lethe_mkpath(buf, sizeof(buf), test->a, 4, NULL, 4) == NULL); 696 | 697 | CUTE_ASSERT(lethe_mkpath(buf, sizeof(buf), test->a, sizeof(buf), test->b, 4) == NULL); 698 | 699 | CUTE_ASSERT(lethe_mkpath(buf, sizeof(buf), test->a, 4, test->b, sizeof(buf)) == NULL); 700 | 701 | while (test != test_end) { 702 | memset(buf, 0, sizeof(buf)); 703 | a_size = strlen(test->a); 704 | b_size = strlen(test->b); 705 | CUTE_ASSERT(lethe_mkpath(tiny_buf, sizeof(tiny_buf), test->a, a_size, test->b, b_size) == NULL); 706 | CUTE_ASSERT(lethe_mkpath(buf, sizeof(buf), test->a, a_size, test->b, b_size) == &buf[0]); 707 | CUTE_ASSERT(strcmp(buf, test->expected) == 0); 708 | test++; 709 | } 710 | CUTE_TEST_CASE_END 711 | 712 | CUTE_TEST_CASE(lethe_strglob_tests) 713 | struct strglob_tests_ctx { 714 | const char *str; 715 | const char *pattern; 716 | int result; 717 | }; 718 | struct strglob_tests_ctx tests[] = { 719 | { NULL, NULL , 0 }, 720 | { "abc", "abc" , 1 }, 721 | { "abc", "ab" , 0 }, 722 | { "abc", "a?c" , 1 }, 723 | { "abc", "ab[abdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.c]", 1 }, 724 | { "abc", "ab[abdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.?]", 0 }, 725 | { "ab*", "ab[c*]" , 1 }, 726 | { "ab*", "ab[*c]" , 1 }, 727 | { "abc", "ab*" , 1 }, 728 | { "abc", "abc*" , 1 }, 729 | { "strglob.c", "strglo*.c" , 1 }, 730 | { "parangaricutirimirruaru!!!", "*" , 1 }, 731 | { "parangaritititero", "?" , 0 }, 732 | { "parangaritititero", "?*" , 1 }, 733 | { "parangaricutirimirruaru", "paran*" , 1 }, 734 | { "parangaricutirimirruaru", "parruari" , 0 }, 735 | { "parangaricutirimirruaru", "paran*garicuti" , 0 }, 736 | { "parangaricutirimirruaru", "paran*garicutirimirruaru" , 1 }, 737 | { "parangaricutirimirruaru", "paran*ru" , 1 }, 738 | { "hell yeah!", "*yeah!" , 1 }, 739 | { ".", "*[Gg]lenda*" , 0 }, 740 | }; 741 | size_t tests_nr = sizeof(tests) / sizeof(tests[0]), t; 742 | 743 | for (t = 0; t < tests_nr; t++) { 744 | CUTE_ASSERT(lethe_strglob(tests[t].str, tests[t].pattern) == tests[t].result); 745 | } 746 | CUTE_TEST_CASE_END 747 | 748 | static int has_foremost(void) { 749 | #if defined(__unix__) 750 | return (system("foremost -V > /dev/null") == 0); 751 | #else 752 | return 0; 753 | #endif 754 | } 755 | 756 | #if defined(__unix__) 757 | static int has_found_by_foremost(const char *signature, const size_t signature_size, const char *output_dir) { 758 | DIR *dir = opendir(output_dir); 759 | int has_found = 0; 760 | struct dirent *dt; 761 | char *filename; 762 | struct stat st; 763 | FILE *fp; 764 | const char *sp, *sp_end; 765 | char *data, *d, *d_end, *ld; 766 | size_t data_size; 767 | char cwd[4096]; 768 | 769 | if (dir == NULL) { 770 | return 0; 771 | } 772 | 773 | getcwd(cwd, sizeof(cwd)); 774 | 775 | stat(output_dir, &st); 776 | 777 | if (S_ISDIR(st.st_mode) && chdir(output_dir) != 0) { 778 | goto has_found_by_foremost_epilogue; 779 | } 780 | 781 | while ((dt = readdir(dir)) != NULL && !has_found) { 782 | filename = &dt->d_name[0]; 783 | if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) { 784 | continue; 785 | } 786 | if (stat(filename, &st) != 0) { 787 | continue; 788 | } 789 | if (S_ISDIR(st.st_mode)) { 790 | has_found = has_found_by_foremost(signature, signature_size, filename); 791 | } else if (S_ISREG(st.st_mode)) { 792 | if ((fp = fopen(filename, "rb")) == NULL) { 793 | printf("ERROR: Unable to open '%s'.\n", filename); 794 | continue; 795 | } 796 | fseek(fp, 0L, SEEK_END); 797 | data_size = ftell(fp); 798 | fseek(fp, 0L, SEEK_SET); 799 | if ((data = (char *) malloc(data_size)) != NULL) { 800 | fread(data, 1, data_size, fp); 801 | d = data; 802 | d_end = d + data_size; 803 | while (!has_found && d != d_end) { 804 | sp = signature; 805 | sp_end = sp + signature_size; 806 | has_found = 1; 807 | ld = d; 808 | while (has_found && sp != sp_end && d != d_end) { 809 | has_found = (*sp == *d); 810 | sp++; 811 | d++; 812 | } 813 | d = ld + 1; 814 | } 815 | free(data); 816 | } 817 | fclose(fp); 818 | } 819 | } 820 | 821 | has_found_by_foremost_epilogue: 822 | 823 | closedir(dir); 824 | 825 | chdir(cwd); 826 | 827 | return has_found; 828 | } 829 | 830 | static int write_foremost_config(const char *header, const size_t header_size, const char *footer, const size_t footer_size, const char *conf) { 831 | FILE *fp; 832 | const char *d, *d_end; 833 | 834 | if ((fp = fopen(conf, "w")) == NULL) { 835 | return 1; 836 | } 837 | 838 | fprintf(fp, "txt\ty\t1000000\t"); 839 | 840 | d = header; 841 | d_end = d + header_size; 842 | 843 | while (d != d_end) { 844 | fprintf(fp, "\\x%.2X", *d); 845 | d++; 846 | } 847 | 848 | fprintf(fp, "\t"); 849 | 850 | d = footer; 851 | d_end = d + footer_size; 852 | 853 | while (d != d_end) { 854 | fprintf(fp, "\\x%.2X", *d); 855 | d++; 856 | } 857 | 858 | fprintf(fp, "\tASCII\n"); 859 | 860 | fclose(fp); 861 | 862 | return 0; 863 | } 864 | #endif 865 | 866 | static int stat_wrapper(const char *pathname, struct stat *buf) { 867 | stat_call_nr++; 868 | return stat(pathname, buf); 869 | } 870 | 871 | static unsigned char randomizer_wrapper(void) { 872 | randomizer_call_nr++; 873 | return lethe_default_randomizer(); 874 | } 875 | 876 | static char *get_random_printable_buffer(const size_t bytes_total) { 877 | static char bytes[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 878 | 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 879 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; 880 | const size_t bytes_nr = sizeof(bytes) / sizeof(bytes[0]); 881 | char *buf = (char *) malloc(bytes_total), *bp, *bp_end; 882 | if (buf == NULL) { 883 | return NULL; 884 | } 885 | bp = buf; 886 | bp_end = bp + bytes_total; 887 | while (bp != bp_end) { 888 | *bp = bytes[lethe_default_randomizer() % bytes_nr]; 889 | bp++; 890 | } 891 | return buf; 892 | } 893 | 894 | static int write_data_to_file(const char *filepath, const char *data, size_t data_size) { 895 | FILE *fp; 896 | if ((fp = fopen(filepath, "wb")) == NULL) { 897 | return 1; 898 | } 899 | fprintf(fp, "\n"); 900 | fwrite(data, 1, data_size, fp); 901 | fprintf(fp, "\n"); 902 | fclose(fp); 903 | return 0; 904 | } 905 | 906 | static char *get_ndata_from_file(const char *filepath, const size_t data_size) { 907 | FILE *fp; 908 | char *data; 909 | if ((fp = fopen(filepath, "rb")) == NULL) { 910 | return NULL; 911 | } 912 | if ((data = (char *)malloc(data_size)) == NULL) { 913 | return NULL; 914 | } 915 | fread(data, 1, data_size, fp); 916 | fclose(fp); 917 | return data; 918 | } 919 | 920 | static int lethe(const char *command, const char *user_choices) { 921 | FILE *fp; 922 | #if defined(__unix__) 923 | const char *binary_path = "bin/lethe"; 924 | #elif defined(_WIN32) 925 | const char *binary_path = "bin\\lethe.exe"; 926 | #else 927 | # error Some code wanted. 928 | #endif 929 | char *usr_inputs = ""; 930 | char execline[4096]; 931 | char indirections[4096]; 932 | int exit_code; 933 | struct stat st; 934 | 935 | if (user_choices != NULL) { 936 | if ((fp = fopen(".lethe_usr_inputs", "wb")) == NULL) { 937 | fprintf(stderr, "Unable to create '.lethe_usr_inputs'.\n"); 938 | return 1; 939 | } 940 | fprintf(fp, "%s", user_choices); 941 | fclose(fp); 942 | usr_inputs = " < .lethe_usr_inputs"; 943 | } 944 | 945 | memset(indirections, 0, sizeof(indirections)); 946 | 947 | #if defined(__unix__) 948 | do { 949 | strcat(indirections, "../"); 950 | snprintf(execline, sizeof(execline) - 1, "%sbin/lethe", indirections); 951 | } while (stat(execline, &st) != 0); 952 | #elif defined(_WIN32) 953 | do { 954 | strcat(indirections, "..\\"); 955 | snprintf(execline, sizeof(execline) - 1, "%sbin\\lethe.exe", indirections); 956 | } while (stat(execline, &st) != 0); 957 | #else 958 | # error Some code wanted. 959 | #endif 960 | 961 | snprintf(execline, sizeof(execline) - 1, "%s%s %s %s", indirections, binary_path, command, usr_inputs); 962 | 963 | exit_code = system(execline); 964 | 965 | remove(".lethe_usr_inputs"); 966 | 967 | return exit_code; 968 | } 969 | 970 | #if defined(__unix__) 971 | static int has_found_by_grep(const char *devpath, const char *signature) { 972 | char cmdline[4096]; 973 | snprintf(cmdline, sizeof(cmdline), "cat %s | strings | grep %s --max-count=1", devpath, signature); 974 | return (system(cmdline) == 0); 975 | } 976 | #endif 977 | 978 | static int has_grep(void) { 979 | #if defined(__unix__) 980 | return (system("cat /dev/null > /dev/null") == 0 && 981 | system("which strings > /dev/null") == 0 && 982 | system("grep --version > /dev/null") == 0); 983 | #elif defined(_WIN32) 984 | return 0; 985 | #else 986 | # error Some code wanted. 987 | #endif 988 | } 989 | 990 | #if defined(__unix__) 991 | static char *get_devpath(void) { 992 | static char devpath[4096] = "", *d; 993 | char indirections[4096] = ""; 994 | struct stat st; 995 | FILE *fp; 996 | size_t devpath_size; 997 | 998 | do { 999 | strcat(indirections, "../"); 1000 | snprintf(devpath, sizeof(devpath) - 1, "%sDEV_PATH", indirections); 1001 | } while (stat(devpath, &st) != 0); 1002 | if ((fp = fopen(devpath, "rb")) != NULL) { 1003 | fseek(fp, 0L, SEEK_END); 1004 | devpath_size = (size_t) ftell(fp); 1005 | fseek(fp, 0L, SEEK_SET); 1006 | memset(devpath, 0, sizeof(devpath)); 1007 | fread(devpath, 1, devpath_size, fp); 1008 | if ((d = strstr(devpath, "\n")) != NULL) { 1009 | *d = 0; 1010 | } 1011 | fclose(fp); 1012 | } else { 1013 | memset(devpath, 0, sizeof(devpath)); 1014 | } 1015 | return &devpath[0]; 1016 | } 1017 | #endif 1018 | 1019 | static int usr_memcmp(const void *b1, const void *b2, size_t len) { 1020 | memcmp_nr++; 1021 | return memcmp(b1, b2, len); 1022 | } 1023 | 1024 | static void *usr_memset(void *b, int c, size_t len) { 1025 | memset_nr++; 1026 | return memset(b, c, len); 1027 | } 1028 | 1029 | static void *usr_memcpy(void *dest, const void *src, size_t len) { 1030 | memcpy_nr++; 1031 | return memcpy(dest, src, len); 1032 | } 1033 | --------------------------------------------------------------------------------