├── .clang_complete ├── .gitignore ├── .travis.yml ├── Doxyfile ├── LICENSE.txt ├── Makefile ├── README.md ├── STYLE.md ├── TODO.md ├── build_pages.sh ├── doc ├── Makefile ├── api │ ├── ad.rst │ ├── al.rst │ ├── base.rst │ ├── bf.rst │ ├── cb.rst │ ├── ht.rst │ ├── index.rst │ ├── list.rst │ ├── ll.rst │ ├── log.rst │ ├── re.rst │ ├── str.rst │ ├── ut.rst │ └── util.rst ├── conf.py ├── index.rst ├── lisp │ ├── embedding.rst │ ├── index.rst │ └── types.rst └── tutorial │ ├── args.rst │ ├── arraylist.rst │ ├── charbuf.rst │ ├── errors.rst │ ├── hashtable.rst │ ├── index.rst │ ├── iterator.rst │ ├── linkedlist.rst │ ├── list.rst │ ├── logging.rst │ ├── regex.rst │ └── unittest.rst ├── inc └── libstephen │ ├── ad.h │ ├── al.h │ ├── base.h │ ├── bf.h │ ├── cb.h │ ├── ht.h │ ├── hta.h │ ├── lisp.h │ ├── list.h │ ├── ll.h │ ├── log.h │ ├── rb.h │ ├── re.h │ ├── re_internals.h │ ├── str.h │ ├── ut.h │ └── util.h ├── meson.build ├── res └── file.txt ├── src ├── args.c ├── arraylist.c ├── bitfield.c ├── charbuf.c ├── hashtable.c ├── hta.c ├── iter.c ├── linkedlist.c ├── lisp │ ├── gc.c │ ├── lex.c │ ├── types.c │ └── util.c ├── log.c ├── regex │ ├── codegen.c │ ├── instr.c │ ├── lex.c │ ├── parse.c │ ├── pike.c │ └── util.c ├── ringbuf.c ├── smbunit.c ├── str.h ├── string.c └── util.c ├── test ├── argstest.c ├── arraylisttest.c ├── bitfieldtest.c ├── charbuftest.c ├── hashtabletest.c ├── hta.c ├── itertest.c ├── linkedlisttest.c ├── listtest.c ├── logtest.c ├── main.c ├── re_codegen.c ├── re_lex.c ├── re_parse.c ├── re_pike.c ├── ringbuftest.c ├── stringtest.c └── tests.h └── util ├── lisp.c └── regex.c /.clang_complete: -------------------------------------------------------------------------------- 1 | -Isrc 2 | -Iinc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | build 3 | coverage 4 | debug 5 | release 6 | doc/_build 7 | doc/xml 8 | *~ 9 | GTAGS 10 | GRTAGS 11 | GPATH 12 | syntax: regexp 13 | (.*/)?\#[^/]*\#$ 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | language: c 5 | compiler: gcc 6 | install: 7 | - sudo apt-get update -qq 8 | - sudo apt-get install --no-install-recommends -y doxygen graphviz valgrind libedit-dev 9 | - sudo pip install sphinx sphinx_rtd_theme breathe 10 | - wget http://ftp.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz 11 | - tar xf lcov_1.11.orig.tar.gz 12 | - sudo make -C lcov-1.11/ install 13 | - rm -rf lcov-1.11 14 | - lcov --version 15 | - wget --no-check-certificate http://www.cmake.org/files/v3.5/cmake-3.5.2.tar.gz 16 | - tar xf cmake-3.5.2.tar.gz 17 | - cd cmake-3.5.2 && ./configure 18 | - sudo make install && cd .. 19 | - rm -rf cmake-3.5.2 20 | script: make test 21 | after_success: ./build_pages.sh 22 | env: 23 | global: 24 | - secure: "JS/6lbqPUbBRAUmwxDs7f67CEO1zB+BmuL9YUk/lNIlpMlsaY7rj/j5OdmR/3WY1i17g9nKSDGKyddZUr+ba8Al/dO0g7qiScj3p6a3QlSf149xgFUInK1YRkiQ1sLgu17BUwt2Bx1Ca2fipDHGHwDL01PeLpgO2UpsYNml5K5c=" 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2016, Stephen Brennan. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | * Neither the name of Stephen Brennan nor the names of his contributors may be 12 | used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL STEPHEN BRENNAN BE LIABLE FOR ANY DIRECT, 19 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: release debug test doc cov 2 | 3 | build: 4 | meson build 5 | 6 | release: build 7 | ninja -C build 8 | 9 | debug: build 10 | ninja -C build 11 | 12 | test: build 13 | ninja -C build test 14 | 15 | doc: 16 | doxygen 17 | make -C doc html 18 | 19 | cov: 20 | true # we're not doing this for now 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libstephen ![Build Status](https://travis-ci.org/brenns10/libstephen.svg?branch=master) 2 | 3 | - Author: [Stephen Brennan](http://brennan.io) 4 | - Version: Prerelease 5 | - Homepage: https://github.com/brenns10/libstephen 6 | - Tool site: http://brennan.io/libstephen 7 | - License: Revised BSD 8 | 9 | ## Description 10 | 11 | libstephen is a C library that provides a number of basic tools for C 12 | programming. I primarily started it so I could implement some data structures 13 | in C, and now it is the basis for some of my other C programming projects. Its 14 | features include: 15 | 16 | ### Data Structures 17 | 18 | - Linked list (ll) 19 | - Array list (al) 20 | - Hash table (ht) 21 | - Bit field (bf) 22 | - Character buffer (similar to a string builder in Java) 23 | 24 | ### Tools 25 | 26 | - Unit testing framework (su) 27 | - Command line argument parser 28 | - Logging framework 29 | - Regular expressions 30 | 31 | ## Build 32 | 33 | This project is built with CMake. Typical usage involves creating a build 34 | directory (`mkdir build`), entering it (`cd build`), running CMake on the source 35 | directory (`cmake ..`), and finally compiling with make (`make`). 36 | 37 | The more compact way is to run: 38 | 39 | cmake -Bbuild -H. && cmake --build build 40 | 41 | Even though I use CMake, I still have a Makefile to automate CMake tasks, so you 42 | can do `make release` and `make debug` to automatically CMake and build those 43 | configurations, and `make test` to CMake, build, and run some tests in the debug 44 | configuration. 45 | 46 | ## Documentation 47 | 48 | If you want documentation on this library, well you're in luck! One of the 49 | things I have experimented with is automatically producing and publishing 50 | up-to-date documentation. You can find documentation up-to-date with master in 51 | the [documentation section][doc] of the tool site. This includes both 52 | tutorial/commentary style documentation, as well as function-level documentation 53 | generated from the code. 54 | 55 | You can generate this documentation yourself by running the command `make doc` 56 | in the project root directory. This requires CMake, Doxygen, Python, Sphinx, and 57 | the sphinx-rtd-theme to be installed. 58 | 59 | ## Test Coverage 60 | 61 | This library uses its own unit testing framework to test itself. Coverage 62 | information is recorded by the program `gcov`, aggregated by the program `lcov`, 63 | and converted into a very nice HTML file by `genhtml`. This is very useful for 64 | detecting what code needs tests. To generate test coverage information, run the 65 | following command. at the project root: 66 | 67 | $ make cov 68 | 69 | At which point you can open the file `cov/index.html` in a browser and view the 70 | test coverage info. Alternatively, you can find the latest test coverage 71 | information on the [libstephen tool site][]. 72 | 73 | ## Plans 74 | 75 | My most up-to-date ideas are now maintained on the [GitHub Issues][] 76 | 77 | ## License 78 | 79 | Copyright (c) 2013-2016, Stephen Brennan. All rights reserved. Released under 80 | the Revised BSD license. See [LICENSE.txt][] for details. 81 | 82 | [libstephen tool site]: http://brennan.io/libstephen 83 | [doc]: http://brennan.io/libstephen/doc/ 84 | [GitHub Issues]: https://github.com/brenns10/libstephen/issues 85 | [GitHub Wiki]: https://github.com/brenns10/libstephen/wiki 86 | [LICENSE.txt]: LICENSE.txt 87 | -------------------------------------------------------------------------------- /STYLE.md: -------------------------------------------------------------------------------- 1 | # `libstephen` Code Style Guide 2 | 3 | Despite being a single developer, I haven't always been the most consistent with 4 | my code style. I'm writing this document to standardize myself on a standard 5 | set of conventions. 6 | 7 | ## Naming 8 | 9 | ### Data Structures 10 | 11 | * Data structures should be named `smb_` followed by a two to four character 12 | abbreviation of their name/purpose. Examples include: 13 | + `smb_ll` - Linked list. 14 | + `smb_iter` - Iterator. 15 | * Data structures should be declared as `typedef struct [name] { .... } [name];` 16 | so that they may be referenced with or without the `struct` keyword. 17 | * Data structure member variables should be lower case, 18 | `underscores_between_words`, and should not have any abbreviation indicating 19 | their data structure. 20 | 21 | ### Functions 22 | 23 | * Functions should be named with `underscores_between_words`. 24 | * Functions that deal with a module or data structure should be prefixed with 25 | the two to four character abbreviation for that data structure or module. 26 | * Functions that do not deal with a data structure, but instead are library 27 | level functions, should be prefixed with `smb_`. 28 | * Private functions (those local to a code file, and without a declaration in a 29 | header) need not be prefixed with anything, but should still follow the 30 | `underscores_between_words` convention. 31 | 32 | ### Variables 33 | 34 | * Local variables should also be named with `underscores_between_words`. 35 | * Global variables should not be used enough to merit a specific style. 36 | 37 | ### Constants and Macros 38 | 39 | * Constants and macros should be in `ALL_CAPS_UNDERSCORES_BETWEEN_WORDS`. 40 | * They should also generally be prefixed by `SMB_`. 41 | 42 | ### Files 43 | 44 | * All header files should go within the `libstephen/` directory. 45 | * Library level declarations go within `libstephen/base.h`. 46 | * Module level declarations should go within a file named after the module/data 47 | structure's abbreviation. 48 | * Source files should generally be named after the module that they define. 49 | However, they may use an expanded version of the module's name so as to be 50 | clearer. 51 | 52 | ## Formatting 53 | 54 | ### Operators 55 | 56 | * Binary and ternary operators should be surrounded by spaces, except when 57 | compressing them lends readability to code. 58 | * Comparison operators should be surrounded by spaces. 59 | * Assignment operators should be surrounded by spaces. 60 | * Unary operators (`-`, `++`, `--`, `*`, `&`) should be attached to their 61 | operand. This applies to the pointer operators too. In declarations, the `*` 62 | should be attached to the variable being declared. 63 | * The dereferencing operator `->` and field access operator `.` should not be 64 | surrounded by spaces. 65 | 66 | ### Braces 67 | 68 | * For function blocks, braces should start and end on a new line, and be the 69 | only characters on those lines. 70 | * For control flow structures, the opening brace should be on the same line as 71 | the initial keyword, and the closing brace should be on its own line. 72 | * When braces are on their own line, they should be at the indentation level of 73 | the containing block, not the block they define. 74 | 75 | ### Parentheses 76 | 77 | * Parentheses, in all cases, should not be separated from the text they enclose. 78 | * In function calls, the opening parenthesis should not be separated from the 79 | function name. 80 | 81 | ### Indentation 82 | 83 | * Every block should be indented two spaces from the containing block. 84 | * Documentation comments should have an indentation level of three spaces from 85 | their containing block. 86 | 87 | ### Comments 88 | 89 | * Both `//` and `/* ... */` comments are allowed. However, `//` should be 90 | preferred for most line comments that span one or two lines. 91 | * Larger, multiline comments can use `/* ... */`, but you should consider 92 | whether that comment shouldgo within a documentation comment instead of a 93 | regular old comment. 94 | * Every function (public or private) should have a documentation comment `/*** 95 | ... */` before it. It should use the Doxygen format with `@` as the keyword 96 | character. It should at least define a `@brief` for a one line explanation of 97 | the function's purpose, and in most cases should document all parameters, 98 | return values, and errors, and provide an explanation sufficient for an API 99 | user. 100 | * Public functions are required to have the above. 101 | * Files should start with a standard header (as can be seen in 102 | `src/linkedlist.c`). It should contain `@file`, `@author`, `@date`, and 103 | `@brief`. After the `@brief`, a more full explanation of the module may be 104 | provided, if necessary. 105 | * Comments may be used to delimit sections of code. They should be of the form 106 | forward slash, 79 asterisks, blank line, centered header title line, blank 107 | line, 79 asterisks, and forward slash. 108 | 109 | ### Miscellaneous 110 | 111 | * System headers should be included first, followed by a blank line, followed by 112 | libstephen headers. 113 | 114 | ## Code 115 | 116 | ### Error Handling 117 | 118 | * Error handling should be a high priority, but it should not overtake the 119 | entire code -- especially with code to handle rare and difficult to handle 120 | memory allocation errors. So, errors thrown by `malloc` will be dealt with by 121 | `smb_new()` by printing to stderr and terminating. Any call to `smb_new()` 122 | can be assumed to be successful without error checking. There will be no 123 | memory allocation error handling. 124 | * Error handling should always be achieved by a `status` parameter to a 125 | function. The status parameter should be the last parameter in the function, 126 | and it should have type `smb_status*`. 127 | * `smb_status` should be an integer, defining status codes as constants: 128 | + `SMB_SUCCESS` 129 | + `SMB_INDEX_ERROR` 130 | + `SMB_NULL_ERROR` 131 | + etc 132 | * The status variable should be checked after every function call. No 133 | exceptions. 134 | + If an error condition is impossible, then that should be documented with an 135 | assertion. 136 | * `goto`s are permissable for handling errors in code which needs to free 137 | resources. 138 | 139 | ### Assertions 140 | 141 | * Assertions from `assert.h` should be used throughout code. As with all 142 | assertions, they should only be used to indicate a *bug in the program*. That 143 | is, assert things that could only happen if there was a bug in the program. 144 | An assertion should never fire due to something like out of bounds input 145 | (unless it is an internal function where all input has already been checked). 146 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # libstephen To-Do List 2 | 3 | I already find `libstephen` to be a useful tool for my general purpose C 4 | programming. I use it extensively in my regular expression and parser project, 5 | [`cky`](https://bitbucket.org/brenns10/cky). The data structure support is an 6 | invaluable every day necessitiy for writing C code that I'm comfortable with. 7 | However, there is always room for improvement. Most definitely, my library is 8 | far from perfect, and I want to bring it as close as possible to perfection. As 9 | such, here are some of my plans for the future. 10 | 11 | ## Test Coverage 12 | 13 | Now that I've settled on a good error handling scheme, I'd like to up my test 14 | coverage significantly. 15 | 16 | ## Release 1.0 17 | 18 | This sounds silly, but lately I've done a lot of API breaking changes. I want 19 | to ensure that this doesn't happen, so my dowstream projects don't have to drown 20 | in refactoring. Therefore, I need to make any more API decisions I want 21 | quickly, and then commit to maintaining that API forever. 22 | 23 | ## Integrate Dowstream Projects 24 | 25 | CKY has a regular expression library that is mostly complete, but needs testing. 26 | Once that is complete, it should be moved into Libstephen. Possibly likewise 27 | with the grammar/parsing tools too. Also, I wrote a shell called LSH, with the 28 | intention of integrating it with Libstephen. If I get a solid syntax working 29 | with it, I can migrate that into Libstephen as well. 30 | 31 | ## Unit Test Setup and Tear Down 32 | 33 | Most unit testing libraries have setup and tear down methods. I can make 34 | group-level and test-level setup and tear down methods. 35 | 36 | ## String Functions 37 | 38 | It would be nice to have functions that work on `char *` strings, that provide 39 | functionality similar to some of Python's string handling capabilities. For 40 | instance, substrings, split, etc. 41 | -------------------------------------------------------------------------------- /build_pages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Build GitHub Pages branch automatically. 3 | ## STARTING STATE: Must be in the branch master, with no uncommitted changes. 4 | ## You've run the tests on release mode, and they run successfully! 5 | 6 | # Exit script on first error: 7 | set -e 8 | 9 | # Get the current commit 10 | COMMIT=$(git rev-parse HEAD) 11 | 12 | # Generate coverage information. 13 | make cov 14 | 15 | # Generate documentation. 16 | make doc 17 | 18 | # In a new directory, clone the gh-pages branch. 19 | cd .. 20 | git clone -b gh-pages "https://$GH_TOKEN@github.com/brenns10/libstephen.git" gh-pages 21 | cd gh-pages 22 | 23 | # Update git configuration so I can push. 24 | if [ "$1" != "dry" ]; then 25 | # Update git config. 26 | git config user.name "Travis Builder" 27 | git config user.email "nobody@example.com" 28 | fi 29 | 30 | # Copy the docs and coverage results. 31 | rm -rf doc 32 | cp -R ../libstephen/doc/_build/html doc 33 | cp -R ../libstephen/cov . 34 | 35 | # Add and commit changes. 36 | git add -A . 37 | git commit -m "[ci skip] Autodoc commit for $COMMIT." 38 | if [ "$1" != "dry" ]; then 39 | git push -q origin gh-pages 40 | fi 41 | -------------------------------------------------------------------------------- /doc/api/ad.rst: -------------------------------------------------------------------------------- 1 | Argument Parsing 2 | ================ 3 | 4 | .. doxygenfile:: libstephen/ad.h 5 | -------------------------------------------------------------------------------- /doc/api/al.rst: -------------------------------------------------------------------------------- 1 | Array List 2 | ========== 3 | 4 | .. doxygenfile:: libstephen/al.h 5 | -------------------------------------------------------------------------------- /doc/api/base.rst: -------------------------------------------------------------------------------- 1 | Libstephen Base 2 | =============== 3 | 4 | .. doxygenfile:: libstephen/base.h 5 | -------------------------------------------------------------------------------- /doc/api/bf.rst: -------------------------------------------------------------------------------- 1 | Bitfield 2 | ======== 3 | 4 | .. doxygenfile:: libstephen/bf.h 5 | -------------------------------------------------------------------------------- /doc/api/cb.rst: -------------------------------------------------------------------------------- 1 | Character Buffer 2 | ================ 3 | 4 | .. doxygenfile:: libstephen/cb.h 5 | -------------------------------------------------------------------------------- /doc/api/ht.rst: -------------------------------------------------------------------------------- 1 | Hash Table 2 | ========== 3 | 4 | .. doxygenfile:: libstephen/ht.h 5 | -------------------------------------------------------------------------------- /doc/api/index.rst: -------------------------------------------------------------------------------- 1 | Libstephen API Documentation 2 | ============================ 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | base 8 | ll 9 | al 10 | list 11 | ht 12 | bf 13 | cb 14 | ad 15 | log 16 | ut 17 | re 18 | util 19 | str 20 | -------------------------------------------------------------------------------- /doc/api/list.rst: -------------------------------------------------------------------------------- 1 | List Interface 2 | ============== 3 | 4 | .. doxygenfile:: libstephen/list.h 5 | -------------------------------------------------------------------------------- /doc/api/ll.rst: -------------------------------------------------------------------------------- 1 | Linked List 2 | =========== 3 | 4 | .. doxygenfile:: libstephen/ll.h 5 | -------------------------------------------------------------------------------- /doc/api/log.rst: -------------------------------------------------------------------------------- 1 | Logging 2 | ======= 3 | 4 | .. doxygenfile:: libstephen/log.h 5 | -------------------------------------------------------------------------------- /doc/api/re.rst: -------------------------------------------------------------------------------- 1 | Regular Expressions 2 | =================== 3 | 4 | .. doxygenfile:: libstephen/re.h 5 | -------------------------------------------------------------------------------- /doc/api/str.rst: -------------------------------------------------------------------------------- 1 | String Functions 2 | ================ 3 | 4 | .. doxygenfile:: libstephen/str.h 5 | -------------------------------------------------------------------------------- /doc/api/ut.rst: -------------------------------------------------------------------------------- 1 | Unit Testing 2 | ============ 3 | 4 | .. doxygenfile:: libstephen/ut.h 5 | -------------------------------------------------------------------------------- /doc/api/util.rst: -------------------------------------------------------------------------------- 1 | Utilities & Miscellaneous 2 | ========================= 3 | 4 | .. doxygenfile:: libstephen/util.h 5 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. libstephen documentation master file, created by 2 | sphinx-quickstart on Fri Jul 31 11:08:00 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to libstephen's documentation! 7 | ====================================== 8 | 9 | Here you will find documentation on libstephen, my personal C library. The 10 | documentation below is split into two sections. The first contains a brief 11 | tutorial for setting up a project with the library, and then commentary on each 12 | of the major modules of the library. The second section contains API reference 13 | for every header file in the project. 14 | 15 | *Please Note:* Libstephen is very much my own, personal library and creation. I 16 | use it to experiment on how I should design, implement, and document APIs. I 17 | heavily use some portions of the library (for instance, unit testing) while I 18 | rarely use some other portions (e.g. bitfields). Sometimes I add, remove, or 19 | modify interfaces without warning. And since I haven't officially given a 20 | version number beyond 0.1 for this library, there's no real guarantee for 21 | interface stability. All that is to say: if you want your project to go into 22 | production, this is not the library you're looking for. If you're just doing a 23 | personal project and you don't mind using someone else's personal project, 24 | you're in the right place! 25 | 26 | Contents: 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | 31 | tutorial/index.rst 32 | api/index.rst 33 | lisp/index.rst 34 | 35 | 36 | Indices and tables 37 | ================== 38 | 39 | * :ref:`genindex` 40 | * :ref:`search` 41 | -------------------------------------------------------------------------------- /doc/lisp/index.rst: -------------------------------------------------------------------------------- 1 | Lisp Implementation Documentation 2 | ================================= 3 | 4 | Included in libstephen (for some reason) is a somewhat functional lisp 5 | implementation. This feature is intended to provide some lightweight scripting 6 | capabilities for applications using the library, without having to link to a 7 | more complex tool like Python or Lua. 8 | 9 | For instance it may be possible for an application to write all its routines in 10 | a way that they are callable from libstephen's lisp interpreter, and then to run 11 | the main program as a small lisp script. Or an application could use the lisp 12 | implementation as a powerful and easy to use configuration language for startup. 13 | A final possibility might be enabling extensions and plugins for an application. 14 | 15 | This section is organized so that the documentation required for a library user 16 | to embed the interpreter is presented first. More advanced information about the 17 | inner workings of the interpreter is presented later. 18 | 19 | Contents: 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | 24 | embedding.rst 25 | types.rst 26 | -------------------------------------------------------------------------------- /doc/lisp/types.rst: -------------------------------------------------------------------------------- 1 | Type System 2 | =========== 3 | 4 | As previous pages have mentions, lisp objects rely on a mark and sweep garbage 5 | collection system to manage their lifetimes. This page describes how the type 6 | system is implemented, which includes the garbage collection implementation. 7 | 8 | In Java, everything is an object. In this language, everything is a 9 | ``lisp_value``. This means two things: 10 | 11 | 1. Every object contains a ``type`` pointer. 12 | 2. Every object has a variable to store its ``mark``. 13 | 3. Every object has a pointer to the object allocated after it, forming a linked 14 | list. 15 | 16 | Every type declares these using the ``LISP_VALUE_HEAD`` macro, like so: 17 | 18 | .. code:: C 19 | 20 | typedef struct { 21 | LISP_VALUE_HEAD; 22 | int x; 23 | } lisp_integer; 24 | 25 | You can cast pointers to these objects to ``lisp_value*`` and still access the 26 | type object. All objects are passed around as ``lisp_value*``. 27 | 28 | In order to allow objects to be treated differently based on their type (but in 29 | a generic way to calling code), we use the type object. 30 | 31 | A type object is just another object, with type ``lisp_type``. However, it is 32 | NOT managed by the garbage collector. It contains the string name of a type, 33 | along with pointers to implementations for the following functions: print, new, 34 | free, eval, call, and expand. Therefore, if you have ``lisp_value *object`` and 35 | you want to print it, you can do: 36 | 37 | .. code:: C 38 | 39 | object->type->print(stdout, object); 40 | 41 | Unfortunately that's very verbose. To simplify, each of the functions 42 | implemented by a type object has an associated helper function. So you can 43 | instead do: 44 | 45 | .. code:: C 46 | 47 | lisp_print(stdout, object); 48 | 49 | But there's no magic or switch statements involved here--we're simply using the 50 | type object. 51 | 52 | This means that it's not too difficult to add a type to the language! All you 53 | need to do is declare a struct for your type, implement the basic functions, and 54 | create a type object for it. Here is an example for a regex type built on 55 | libstephen's regex implementation: 56 | 57 | .. code:: C 58 | 59 | // HEADER 60 | typedef struct { 61 | LISP_VALUE_HEAD; 62 | Regex r; 63 | } lisp_regex; 64 | extern lisp_type *type_regex; 65 | 66 | // CODE 67 | static void regex_print(FILE *f, lisp_value *v); 68 | static lisp_value *regex_new(void); 69 | static void lisp_value_free(void *v); 70 | static lisp_value *regex_eval(lisp_runtime *rt, lisp_scope *s, lisp_value *re); 71 | static lisp_value *regex_call(lisp_runtime *rt, lisp_scope *s, lisp_value *c, lisp_value *a); 72 | static smb_iter regex_expand(lisp_value *v); 73 | 74 | static lisp_type *type_regex_obj = { 75 | .type=type_type, 76 | .name="regex", 77 | .print=regex_print, 78 | .new=regex_new, 79 | .free=regex_free, 80 | .call=regex_call, 81 | .expand=regex_expand, 82 | }; 83 | lisp_type *type_regex = &type_regex_obj; 84 | 85 | // function implementations 86 | 87 | Many function implementations in the type object are trivial. However, a few are 88 | rather important to get right: 89 | 90 | - The print function should not print a newline. 91 | - The new function should take no arguments. It does not need to bother to 92 | populate the ``lisp_value`` fields such as ``type`` or ``next``. Instead, 93 | these will be called by the ``lisp_new()`` wrapper function. It may be wise to 94 | write a ``lisp_new_TYPENAME(args...)`` function so users can easily create an 95 | instance of a type from some arguments. 96 | - The eval function is tricky. Many things just don't get evaluated. For 97 | instance, a regex object would probably be returned by a builtin function, but 98 | never produced as raw code to be evaluated. So its eval function should 99 | probably return an error. 100 | - Generally the call function should return an error. However it may not be the 101 | worst thing to make creative use of function call syntax. For instance, it 102 | would be interesting to have a regex type that, when called, matches on a 103 | string and returns the match object. 104 | - The most critical function to implement correctly is expand. This must return 105 | a generator, which yields pointers to other ``lisp_value`` objects contained 106 | by this object. For simple objects, this will be trivial, but for data 107 | structures it is incredibly important to get right. 108 | -------------------------------------------------------------------------------- /doc/tutorial/args.rst: -------------------------------------------------------------------------------- 1 | Argument Parsing 2 | ================ 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/ad.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | For practical programs, argument parsing is a large part of what you do. 14 | So, having a simple library to help you build normal command line 15 | interfaces can save you plenty of time. The library in 16 | ``libstephen/ad.h`` provides a basic argument parsing solution. I wrote 17 | it before I knew about the existence of well established solutions like 18 | ``getopt``. It's not a perfect solution, but it does the job for most of 19 | my use cases. 20 | 21 | Argument Types 22 | -------------- 23 | 24 | My argument processor is based on four different types of arguments: 25 | 26 | - Short flags: these are one letter flags that can be on or off. They 27 | are prefixed by a single hyphen. They can also be smooshed together. 28 | For instance, with the command ``ls -la`` has two short flags: ``l`` 29 | and ``a``. 30 | - Long flags: these are multi-letter flags that can be on or off. They 31 | have to be prefixed by a double hyphen. So, the command ``ls --all`` 32 | has a long flag: ``all``. 33 | - Parameters: both short and long flags may have parameters provided to 34 | them. For instance, ``gcc -o code.o code.c`` has ``code.o`` provided 35 | as an argument to the short flag ``o``. 36 | - Bare strings: these are neither short nor long flags. They are simply 37 | arguments provided to the program. They are typically the actual 38 | objects that the program is to operate on (input files, or input 39 | text). For instance, ``gcc -o code.o code.c`` has the bare string 40 | ``code.c``. 41 | 42 | Initialization 43 | -------------- 44 | 45 | .. code:: C 46 | 47 | void arg_data_init(smb_ad *data); 48 | smb_ad *arg_data_create(); 49 | void arg_data_destroy(smb_ad *data); 50 | void arg_data_delete(smb_ad *data); 51 | 52 | void process_args(smb_ad *data, int argc, char **argv); 53 | 54 | In order to start using the arg parsing library, you create a ``smb_ad`` 55 | object. Typically you do this on the stack in your ``main()`` function, 56 | because why would your arguments need to last any longer than that? You 57 | call ``arg_data_init()`` with a pointer to the ``smb_ad`` to initialize 58 | it. To parse your program's arguments, you call 59 | ``process_args(smb_ad *, int argc, char **argv)``. You shouldn't include 60 | the program name in ``argc`` and ``argv`` (unless you want it to be 61 | parsed as an argument). 62 | 63 | So, something like this would be the basic structure of your main 64 | function: 65 | 66 | .. code:: C 67 | 68 | int main(int argc, char *argv[]) 69 | { 70 | smb_ad args; 71 | arg_data_init(&args); 72 | process_args(&args, argc-1, argv+1); 73 | 74 | // Get arguments and flags 75 | 76 | // Do program logic 77 | 78 | arg_data_destroy(&args); 79 | return EXIT_SUCCESS; 80 | } 81 | 82 | Reading Argument Data 83 | --------------------- 84 | 85 | .. code:: C 86 | 87 | int check_flag(smb_ad *data, char flag); 88 | int check_long_flag(smb_ad *data, char *flag); 89 | int check_bare_string(smb_ad *data, char *string); 90 | char *get_flag_parameter(smb_ad *data, char flag); 91 | char *get_long_flag_parameter(smb_ad *data, char *string); 92 | 93 | Once you have processed the argument data, the ``smb_ad`` can be queried 94 | for each argument you're expecting. Typically, you'll pair short flags 95 | with long flags (e.g. ``-h`` and ``--help``). You can check for long 96 | flags with ``check_long_flag()``, and you can check for short flags with 97 | ``check_flag()``. You can get flag and long flag parameters with 98 | ``get_flag_parameter()`` and ``get_long_flag_parameter()`` respectively. 99 | You can also use ``check_bare_string()`` to check if a bare string was 100 | provided. However, that's not usually the most convenient way to access 101 | the bare strings (since you probably don't already know the bare string 102 | before it's provided). So, you can also access the linked list directly 103 | in the ``bare_strings`` field of the ``smb_ad`` struct. 104 | 105 | Here's a typical example of how you might use ``smb_ad`` to parse 106 | arguments. It continues from the outline earlier. 107 | 108 | .. code:: C 109 | 110 | if (check_flag(&args, 'h') || check_long_flag(&args, "help")) { 111 | help(); 112 | } else if (check_flag(&args, 'o') || check_long_flag(&args, "output")) { 113 | output = get_flag_parameter(&args, 'o'); 114 | if (!output) { 115 | output = get_long_flag_parameter(&args, "output"); 116 | } 117 | } 118 | 119 | // Now you can do stuff with the output file name you got! 120 | -------------------------------------------------------------------------------- /doc/tutorial/arraylist.rst: -------------------------------------------------------------------------------- 1 | Array List (smb_al) 2 | =================== 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/al.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | The array list (``smb_al``) is a dynamically expanding array of 14 | ``DATA``. Its benefits over a linked list are constant time access, with 15 | the drawback of linear time insertion. Data is more localized in array 16 | lists, but must be copied from time to time as the list expands. 17 | 18 | Operations 19 | ---------- 20 | 21 | The array list supports the four normal creation and deletion functions, 22 | which create an empty list. Here are some array list operations: 23 | 24 | .. code:: C 25 | 26 | void al_append(smb_al *list, DATA newData); 27 | void al_prepend(smb_al *list, DATA newData); 28 | DATA al_get(const smb_al *list, int index, smb_status *status); 29 | void al_remove(smb_al *list, int index, smb_status *status); 30 | void al_insert(smb_al *list, int index, DATA newData); 31 | void al_set(smb_al *list, int index, DATA newData, smb_status *status); 32 | void al_push_back(smb_al *list, DATA newData); 33 | DATA al_pop_back(smb_al *list, smb_status *status); 34 | DATA al_peek_back(smb_al *list, smb_status *status); 35 | void al_push_front(smb_al *list, DATA newData); 36 | DATA al_pop_front(smb_al *list, smb_status *status); 37 | DATA al_peek_front(smb_al *list, smb_status *status); 38 | int al_length(const smb_al *list); 39 | int al_index_of(const smb_al *list, DATA d, DATA_COMPARE comp); 40 | 41 | These operations are identical to the corresponding ones on the linked 42 | list. Appending is done in constant time, except when the array must be 43 | expanded. Insertions (including prepend) are linear time, while get/set 44 | operations are constant. 45 | 46 | These will get you a generic list: 47 | 48 | .. code:: C 49 | 50 | smb_list al_create_list(); 51 | smb_list al_cast_to_list(smb_al *list); 52 | 53 | And this will create an iterator: 54 | 55 | .. code:: C 56 | 57 | smb_iter al_get_iter(const smb_al *list); 58 | 59 | Sample Usage 60 | ------------ 61 | 62 | This is some sample code identical to the one I used to show the linked 63 | list: 64 | 65 | .. code:: C 66 | 67 | smb_al *list = al_create(); 68 | 69 | al_append(list, LLINT(0)); 70 | al_append(list, LLINT(1)); 71 | 72 | al_delete(list); 73 | 74 | Structure 75 | --------- 76 | 77 | *Here be implementation details!* 78 | 79 | Here is the structure of an ``smb_al`` struct: 80 | 81 | .. code:: C 82 | 83 | typedef struct smb_al 84 | { 85 | DATA *data; 86 | int length; 87 | int allocated; 88 | } smb_al; 89 | 90 | The ``data`` pointer points to the dynamically-allocated array of 91 | ``DATA``. ``length`` contains the number of items in the list, while 92 | ``allocated`` indicates how many ``DATA`` are currently allocated in the 93 | block pointed by ``data``. 94 | -------------------------------------------------------------------------------- /doc/tutorial/charbuf.rst: -------------------------------------------------------------------------------- 1 | Character Buffer 2 | ================ 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/cb.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | Unlike pretty much every other language under the sun, C has 14 | exceptionally few string handling functions available to you. What's 15 | worse, every other language typically hides a whole lot of complexity in 16 | its string type. Most lanugages have immutable strings, which means you 17 | can't modify them without reallocating them. While this has plenty of 18 | advantages, it's also terribly inefficient when you're doing text 19 | processing. Languages with immutable strings frequently provide a string 20 | "buffer" or "builder" abstraction, which is essentially a mutable string 21 | that has support for lots of modification actions. 22 | 23 | Since C doesn't have immutable strings, you need to implement a lot of 24 | the "complexity" that other languages provide for efficient operations 25 | on strings. This character buffer abstraction exists to remove some of 26 | the common complexity with dealing with strings in C. Namely, it does 27 | the allocation and dynamic reallocation that you would normally have to 28 | tackle when you're dealing with buffers - especially when reading input! 29 | 30 | Functions 31 | --------- 32 | 33 | Here is the struct definition and list of functions: 34 | 35 | .. code:: C 36 | 37 | typedef struct { 38 | char *buf; 39 | int capacity; 40 | int length; 41 | } cbuf; 42 | 43 | void cb_init(cbuf *obj, int capacity); 44 | cbuf *cb_create(int capacity); 45 | void cb_destroy(cbuf *obj); 46 | void cb_delete(cbuf *obj); 47 | 48 | void cb_concat(cbuf *obj, char *str); 49 | void cb_append(cbuf *obj, char next); 50 | void cb_trim(cbuf *obj); 51 | void cb_clear(cbuf *obj); 52 | void cb_printf(cbuf *obj, char *format, ...); 53 | void cb_vprintf(cbuf *obj, char *format, va_list va); 54 | 55 | The lifetime functions are self-explanatory, but here's a little 56 | commentary anyway. You really shouldn't have to use ``cb_create()`` that 57 | often. For the most part, you'll want to declare a ``cbuf`` on the stack 58 | and use ``cb_init()``. This is because most use cases for a ``cbuf`` 59 | involve building a string over the course of a single function call. I 60 | don't recommend passing around ``cbuf``\ s, treating them as strings. 61 | 62 | Also, note that calling ``cb_destroy()`` is **optional**! Generally, 63 | you'll want to keep the created string longer than you'll want to keep 64 | the ``cbuf`` around. The buffer is accessible through the ``buf`` field 65 | of the struct. Whenever you'd like, you can stop using the ``cbuf`` and 66 | start passing around the underlying buffer like a normal string. 67 | 68 | Anyway: 69 | 70 | - ``concat`` - add a string to the end. 71 | - ``append`` - add a character to the end. 72 | - ``trim`` - reallocate to just the length of the string. Useful once 73 | you've created the string and you want to stop using the ``cbuf``. 74 | - ``clear`` - clear out the whole buffer. This does nothing accept 75 | reset ``buf[0]`` to ``\0`` and reset ``length`` to ``0``. It doesn't 76 | actually "erase" the buffer. 77 | - ``printf`` - print a format string onto the buffer. ``vprintf`` does 78 | the same, but takes a ``va_list``. 79 | 80 | Wide Character Version 81 | ---------------------- 82 | 83 | In case you're unfamiliar, C has a ``wchar_t`` type, which is capable of 84 | holding any Unicode character. Some text processing tasks may require 85 | you to work with the underlying characters/code points rather than the 86 | text in some encoding. I have implemented the exact same interface, but 87 | for ``wchar_t *`` buffers. Everything is the same, except that the type 88 | is ``wcbuf``, the functions are prefixed by ``wcb_``, and the text 89 | arguments are always ``wchar_t *`` instead of ``char *``. 90 | 91 | I/O Utilities 92 | ------------- 93 | 94 | .. code:: C 95 | 96 | #include "libstephen/str.h" 97 | 98 | - `Header 99 | (master) `__ 100 | - `Implementation 101 | (master) `__ 102 | 103 | Using the character buffer, I've implemented a few simple tasks quite 104 | easily. Here are some functions you might find useful: 105 | 106 | .. code:: C 107 | 108 | char *read_file(FILE *f); 109 | wchar_t *read_filew(FILE *f); 110 | char *read_line(FILE *file); 111 | wchar_t *read_linew(FILE *file); 112 | smb_ll *split_lines(char *source); 113 | smb_ll *split_linesw(wchar_t *source); 114 | 115 | ``split_lines`` isn't actually implemented with a ``cbuf`` (you don't 116 | need one), but I'm including it because I don't have an official "String 117 | Handling" page yet. 118 | -------------------------------------------------------------------------------- /doc/tutorial/errors.rst: -------------------------------------------------------------------------------- 1 | Error Handling 2 | ============== 3 | 4 | libstephen has a standard way to deal with errors in code. Typically, 5 | errors are reported in one of two ways in C code: 6 | 7 | - A status value is returned, indicating the success of the function. 8 | - A pointer to a status variable is provided, and is set to indicate the success 9 | of the function. 10 | 11 | libstephen takes the second approach. 12 | 13 | Why a pointer to a status variable? 14 | ----------------------------------- 15 | 16 | I thought long and hard about which approach to take, and I settled on 17 | this for the following reasons: 18 | 19 | - When a function returns a value, you expect it is a "result" of the 20 | computation. Returning an error code makes it look like the error 21 | code was something the function computed. This ruins the semantics of 22 | the function signature. 23 | - Error codes on return values require reading the documentation on the 24 | function *every time you call it*. Some may use -1 as the error code, 25 | others may use 0, or NULL, or some other strategy. Plus, error return 26 | values reduce the number of possible return values for the actual 27 | result of the function (what if your function has a legitimate reason 28 | to return -1?). 29 | - If a function returns a status, it is laughably easy to just *ignore* 30 | it by not capturing the result of that expression. When it is an out 31 | parameter, you *have* to create a variable to store it in, which 32 | means no ignoring it. 33 | 34 | Values 35 | ------ 36 | 37 | The status variable type is just an ``int``, but typedef'd to 38 | ``smb_status``. There are a handful of predefined error values: 39 | 40 | - ``SMB_SUCCESS`` - no error 41 | - ``SMB_INDEX_ERROR`` - index error, for when you try to access an 42 | index that doesn't exist. 43 | - ``SMB_NOT_FOUND_ERROR`` - hash key not found in hash table 44 | - ``SMB_STOP_ITERATION`` - when an iterator stops iterating 45 | 46 | Memory allocation errors 47 | ------------------------ 48 | 49 | Memory allocation errors are not included here. It's nearly impossible 50 | to recover from an out of memory error. The code to try to handle them 51 | clutters up the library and program like none other. So, the standard 52 | practice is to just exit when memory allocation fails. 53 | 54 | In fact, libstephen provides helper functions for doing exactly this 55 | when allocating memory. Here they are: 56 | 57 | - ``type *smb_new(type, size_t amount)``: Allocates ``amount`` of memory of size 58 | ``size(type)``. Casts to the right pointer type for you. If there is an error, 59 | prints a message to standard error and terminates with an error value. EG: 60 | 61 | .. code:: C 62 | 63 | char *msg = "hi"; 64 | char *string = smb_new(char, 3); 65 | strncpy(string, msg, 3); 66 | printf("%s\n", string); //STDOUT: hi`` 67 | 68 | - ``type *smb_renew(type, type *ptr, size_t newamt)`` - Reallocates the 69 | buffer to a new size, copying if necessary. Deals with errors for 70 | you. EG: 71 | 72 | .. code:: C 73 | 74 | string = smb_renew(char, string, 4); 75 | string[2] = '!'; 76 | string[3] = '\0'; 77 | printf("%s\n", string); //STDOUT: hi!`` 78 | 79 | - ``void smb_free(void *ptr)`` - This actually doesn't do any more than 80 | ``free()`` does, but it seemed like it was necessary so that 81 | everything felt aligned. EG: 82 | 83 | .. code:: C 84 | 85 | smb_free(string); 86 | 87 | Assertions 88 | ---------- 89 | 90 | Finally, there are plenty of situations when an error should not occur, 91 | but you still have to pass and check a status variable. In those 92 | situations, ``assert()`` is your best friend. Use it only when an error 93 | should never occur. If an error could occur, you should handle the 94 | condition as part of the program logic, even if that means printing an 95 | error message and terminating the program. 96 | -------------------------------------------------------------------------------- /doc/tutorial/hashtable.rst: -------------------------------------------------------------------------------- 1 | Hash Table 2 | ========== 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/ht.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | The hash table associates keys with values. It uses a "hash" to 14 | determine the location of a key inside an array, and uses that to 15 | achieve nearly constant time lookup. A hash table needs enough memory to 16 | ensure that the table is about half empty, in order to work properly. 17 | So, the table basically trades off memory for access speed. 18 | 19 | Infrastructure 20 | -------------- 21 | 22 | Hash tables need a good deal more infrastructure than a simple list 23 | needs. A list can store values without knowing or caring about their 24 | type. On the other hand, a hash table needs to know the data type of its 25 | keys for these reasons: 26 | 27 | - Hashing! A hash function takes a key and produces a number, which can 28 | hopefully be used to index into the table. You need to know the data type in 29 | order to come up with a good hash table. 30 | - Equality testing! A hash function is bound to have collisions. So, a hash 31 | table needs to be able to check whether keys are equal, and that means knowing 32 | the type of the keys. 33 | 34 | To handle this, the hash table takes function pointers of two different 35 | types. The first is called ``HASH_FUNCTION``: 36 | 37 | .. code:: C 38 | 39 | typedef unsigned int (*HASH_FUNCTION)(DATA toHash); 40 | unsigned int my_hash(DATA toHash) { 41 | //compute hash value 42 | return hash_value; 43 | } 44 | 45 | That ``typedef`` is a bit cryptic, but it is a function pointer to a 46 | function that takes a ``DATA`` and returns an ``unsigned int``. The 47 | function below that is a hash function that implements the 48 | ``HASH_FUNCTION`` signature. The only hash functions in libstephen are 49 | ``ht_string_hash`` for strings. 50 | 51 | The other type of function pointer that is defined is ``DATA_COMPARE``: 52 | 53 | .. code:: C 54 | 55 | typedef int (*DATA_COMPARE)(DATA d1, DATA d2); 56 | int compare_int(DATA d1, DATA d2) 57 | { 58 | return d1.data_llint - d2.data_llint; 59 | } 60 | 61 | This function compares two ``DATA``. It returns 0 if they are equal. It 62 | returns ``> 0`` if ``d1 > d2``, and ``< 0`` if ``d1 < d2``. These can 63 | also be used for sorting, if I decide to implement it. Comparisons are 64 | implemented for integers, doubles, pointer values, and strings. 65 | 66 | One other function pointer is declared, in order to make it easier for 67 | users to free values from the table. If you wanted to remove a (key, 68 | value) pair from the table, where the value was a pointer to a data 69 | structure, chances are you'd need to free it first. So, you'd end up 70 | looking up the value, freeing it, then removing it from the table. 71 | Instead, I've made a function pointer type that will act on a DATA, 72 | allowing you to specify an action to happen before removing an item from 73 | the table! 74 | 75 | .. code:: C 76 | 77 | typedef void (*DATA_ACTION)(DATA d); 78 | void freer(DATA d) 79 | { 80 | free(d.data_ptr); 81 | } 82 | 83 | Operations 84 | ---------- 85 | 86 | With those function pointer types in hand, we can do a lot of fun hash 87 | table stuff. Here is a list of operations, straight from the header: 88 | 89 | .. code:: C 90 | 91 | void ht_init(smb_ht *pTable, HASH_FUNCTION hash_func, DATA_COMPARE equal); 92 | smb_ht *ht_create(HASH_FUNCTION hash_func, DATA_COMPARE equal); 93 | void ht_destroy_act(smb_ht *pTable, DATA_ACTION deleter); 94 | void ht_destroy(smb_ht *pTable); 95 | void ht_delete_act(smb_ht *pTable, DATA_ACTION deleter); 96 | void ht_delete(smb_ht *pTable); 97 | 98 | void ht_insert(smb_ht *pTable, DATA dKey, DATA dValue); 99 | void ht_remove_act(smb_ht *pTable, DATA dKey, DATA_ACTION deleter, 100 | smb_status *status); 101 | void ht_remove(smb_ht *pTable, DATA dKey, smb_status *status); 102 | DATA ht_get(smb_ht const *pTable, DATA dKey, smb_status *status); 103 | void ht_print(smb_ht const *pTable, int full_mode); 104 | 105 | I've included the ``init``/``create``/``delete``/``destroy`` functions, 106 | because the ``delete`` and ``destroy`` ones have ``_act`` variants that 107 | apply an action to every value in the table, before destroying the 108 | table. 109 | 110 | We have insertion, removal, and retrieval. There's also a printing 111 | function, which is really more of a debugging print function. It reveals 112 | some of the structure of the hash table, which is good for debugging. 113 | 114 | Sample Usage 115 | ------------ 116 | 117 | Here's an example of a using a hash table: 118 | 119 | .. code:: C 120 | 121 | smb_status status = SMB_SUCCESS; 122 | smb_ht *ht = ht_create(&ht_string_hash, &data_compare_string); 123 | 124 | char *key = "stephen"; 125 | char *value = "brennan"; 126 | ht_insert(ht, PTR(key), PTR(value)); 127 | brennan = ht_get(ht, PTR(key), &status).data_ptr; 128 | assert(status == SMB_SUCCESS); 129 | 130 | printf("%s: %s\n", key, value); 131 | //STDOUT: stephen: brennan 132 | 133 | Structure 134 | --------- 135 | 136 | The hash table structure I use could definitely be improved, and I may 137 | improve it in the future. Currently, it uses the simplest possible 138 | method for resolving collisions: chaining. Basically, each bucket is a 139 | linked list. In the future, I might change to quadratic probing. Anyhow, 140 | here is the hash table itself: 141 | 142 | .. code:: C 143 | 144 | typedef struct smb_ht 145 | { 146 | int length; 147 | int allocated; 148 | HASH_FUNCTION hash; 149 | DATA_COMPARE equal; 150 | struct smb_ht_bckt **table; 151 | } smb_ht; 152 | 153 | And here is the structure of the hash table bucket: 154 | 155 | .. code:: C 156 | 157 | typedef struct smb_ht_bckt 158 | { 159 | DATA key; 160 | DATA value; 161 | struct smb_ht_bckt *next; 162 | } smb_ht_bckt; 163 | -------------------------------------------------------------------------------- /doc/tutorial/index.rst: -------------------------------------------------------------------------------- 1 | Libstephen Tutorial 2 | =================== 3 | 4 | **Introductory Notes** 5 | 6 | Here are some basic ideas in the design of the library. 7 | 8 | - A single "generic" type, called ``DATA``. This is an 8-byte union. It can 9 | contain a very big integer, or a pointer, or a double. You can access these 10 | through the fields ``data_llint``, ``data_ptr``, and ``data_dbl`` 11 | respectively. 12 | - A simple "object oriented" approach. I'm not pursuing true object oriented 13 | programming in C, since that's painful. But I create structs, and then write 14 | functions to operate on them. 15 | - Uniform error handling, through a pointer to a status variable accepted by any 16 | function that can produce an error (memory allocation errors are not handled). 17 | - A consistent object lifetime, inspired by C++. I like the freedom of being 18 | able to allocate an object on the stack or heap. Therefore, there are four 19 | common functions for my objects: 20 | 21 | - ``*_create()`` - allocate object on the heap, initialize it. 22 | - ``*_init()`` - initialize an already allocated object. 23 | - ``*_delete()`` - free any resources held by an object, and free the object. 24 | - ``*_destroy()`` - free resources held by an object, but don't free the 25 | object. 26 | 27 | - Some useful abstractions, inspired by Python and Java: 28 | 29 | - A generic "list" data type, which is implemented by all list data 30 | structures. 31 | - A generic "iterator" for iterating over any data structure. Can also be used 32 | to make generators! 33 | 34 | **Tutorial** 35 | 36 | So, now that you know a little bit about the ideas behind my library, how do you 37 | get it up and running in your project? Well, there are quite a few ways, but 38 | the best way currently is to use a Git submodule. 39 | 40 | I'll assume you already have a Git repository with some C code. Then, to add in 41 | libstephen, you'll want to do: 42 | 43 | .. code:: bash 44 | git submodule add https://github.com/brenns10/libstephen.git libstephen 45 | 46 | This will add a ``libstephen`` directory to your project, and check out the 47 | current version of libstephen. Next, you'll want to add 48 | ``libstephen/bin/release/libstephen.a`` as a dependency for your main executable 49 | in your Makefile. You'll also want to create a rule that will build libstephen. 50 | Imagine you had an existing makefile that looked like this: 51 | 52 | .. code:: Makefile 53 | 54 | bin/release/main: $(OBJECTS) 55 | gcc -o bin/release/main $(OBJECTS) 56 | 57 | Then, you could add in libstephen to the build process as simply as this: 58 | 59 | .. code:: Makefile 60 | 61 | libstephen/bin/release/libstephen.a: 62 | make -C libstephen 63 | 64 | bin/release/main: $(OBJECTS) libstephen/bin/release/libstephen.a 65 | gcc -o bin/release/main $(OBJECTS) libstephen/bin/release/libstephen.a 66 | 67 | The only other thing is you'll want to make sure that ``libstephen/inc`` is in 68 | your include path. This is as simple as providing that path in the ``-I`` 69 | option to GCC. 70 | 71 | **Miscellaneous** 72 | 73 | After having the ``DATA`` union around for so long, I've gotten tired of having 74 | to create a ``DATA`` on the stack, assign a value to it, and then send that to 75 | my functions. So, I created a set of macros which expand to ``DATA`` literals, 76 | so you don't have to do that: 77 | 78 | .. code:: C 79 | 80 | DATA an_integer = LLINT(12); 81 | DATA a_pointer = PTR(NULL); 82 | DATA a_long_double = DBL(1.2); 83 | 84 | // sample usage 85 | smb_ll *list = ll_create(); 86 | ll_append(list, LLINT(10)); 87 | ll_append(list, LLINT(20)); 88 | ll_append(list, PTR("this is code for demonstration only!")); 89 | 90 | .. toctree:: 91 | :maxdepth: 1 92 | 93 | errors 94 | arraylist 95 | linkedlist 96 | list 97 | iterator 98 | hashtable 99 | charbuf 100 | args 101 | logging 102 | unittest 103 | regex 104 | 105 | 106 | -------------------------------------------------------------------------------- /doc/tutorial/iterator.rst: -------------------------------------------------------------------------------- 1 | Iterator 2 | ======== 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/list.h" 7 | 8 | - `Header 9 | (master) `__ 10 | 11 | The iterator struct (``smb_iter``) is my reaction to the high overhead 12 | of the list interface. The list interface allows for some powerful 13 | testing and more generic code, but it is frequently more than you really 14 | need. Even more importantly, the list interface doesn't provide linear 15 | time iteration on linked lists (iterating via ``get`` is ``O(N^2)``). 16 | 17 | So, the iterator aims to provide a read-only, iterate-once interface to 18 | getting data from a data structure. It has a drastically reduced set of 19 | features, making it much more space efficient. Additionally, iterators 20 | can be implemented for anything, not just data structures. They can be 21 | used to implement generators, and use them just like lists (this is 22 | *heavily* influenced by Python). 23 | 24 | Here are some of my future ideas for iterator/generator implementations: 25 | 26 | - Character iterators. They can be used to iterate over a string or file. They 27 | can be used to automatically convert a UTF-8 input stream to a sequence of 28 | Unicode ``wchar_t`` characters. 29 | - Mathematically inspired generators could be useful. 30 | 31 | Plus, functions operating on iterators are very generic: 32 | 33 | - Printer function would print the items of a list. 34 | - Copy functions could copy data from an iterator into a new list. 35 | 36 | Structure & Function 37 | -------------------- 38 | 39 | The iterator's structure and function are tied together. It's a bit 40 | difficult to use one without the other. 41 | 42 | .. code:: C 43 | 44 | typedef struct smb_iter { 45 | const void *ds; 46 | DATA state; 47 | int index; 48 | DATA (*next)(struct smb_iter *iter, smb_status *status); 49 | bool (*has_next)(struct smb_iter *iter); 50 | void (*destroy)(struct smb_iter *iter); 51 | void (*delete)(struct smb_iter *iter); 52 | } smb_iter; 53 | 54 | There are three data fields, which have roughly defined purposes. The 55 | ``ds`` field should hold a pointer to the data structure. The ``state`` 56 | field can hold a number or a pointer as part of the iterator's state. 57 | The ``index`` field can also hold a number, that is usually the index of 58 | the iteration (but doesn't have to be). 59 | 60 | The ``next`` and ``has_next`` functions are pretty obvious. The ``next`` 61 | function might raise a ``SMB_STOP_ITERATION`` error if you don't call 62 | ``has_next`` first. The ``destroy`` and ``delete`` functions are a bit 63 | more difficult. Since an iterator is a read-only view on a data 64 | structure, it doesn't really make sense to free the underlying data 65 | structure when you destroy an iterator. So, ``destroy`` doesn't usually 66 | do anything, and ``delete`` just ``destroy``\ s the iterator, and then 67 | frees it. The reason they do exist is so that new and exciting iterator 68 | implementations can have support for freeing up resources if necessary. 69 | 70 | Sample Usage 71 | ------------ 72 | 73 | Here is a typical use of an iterator: 74 | 75 | .. code:: C 76 | 77 | smb_iter it = // something returning an iterator to a list of numbers 78 | DATA d; 79 | 80 | while (it.has_next(&it)) { 81 | d = it.next(&it); 82 | printf("List element: %ld\n", d.data_llint); 83 | } 84 | it.destroy(&it); 85 | -------------------------------------------------------------------------------- /doc/tutorial/linkedlist.rst: -------------------------------------------------------------------------------- 1 | Linked List 2 | =========== 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/ll.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | The linked list struct (``smb_ll``) is a doubly-linked list. It is made 14 | up of a chain of nodes. Each node contains a ``DATA``, as well as links 15 | to the next and previous nodes. 16 | 17 | Operations 18 | ---------- 19 | 20 | Like all other objects, ``smb_ll`` supports the four basic 21 | creation/deletion functions. They create new, empty lists. The deletion 22 | functions delete the struct and all nodes, but they won't free any 23 | pointers contained in DATA, naturally. 24 | 25 | Here are some operations supported by ``smb_ll`` 26 | 27 | .. code:: C 28 | 29 | void ll_append(smb_ll *list, DATA newData); 30 | void ll_prepend(smb_ll *list, DATA newData); 31 | void ll_push_back(smb_ll *list, DATA newData); 32 | DATA ll_pop_back(smb_ll *list, smb_status *status); 33 | DATA ll_peek_back(smb_ll *list, smb_status *status); 34 | void ll_push_front(smb_ll *list, DATA newData); 35 | DATA ll_pop_front(smb_ll *list, smb_status *status); 36 | DATA ll_peek_front(smb_ll *list, smb_status *status); 37 | DATA ll_get(const smb_ll *list, int index, smb_status *status); 38 | void ll_remove(smb_ll *list, int index, smb_status *status); 39 | void ll_insert(smb_ll *list, int index, DATA newData); 40 | void ll_set(smb_ll *list, int index, DATA newData, smb_status *status); 41 | int ll_length(const smb_ll *list); 42 | int ll_index_of(const smb_ll *list, DATA d, DATA_COMPARE comp); 43 | 44 | This is directly from the header. The append and prepend functions are 45 | exactly what you'd expect. The push/pop/peek functions make the list act 46 | like a stack. The front ones act on the beginning of the list, and the 47 | back ones operate on the end of the list. So ``push_back`` is the same 48 | as ``append``. ``get``, ``remove``, ``insert``, and ``set`` provide your 49 | basic list access and modification functions. ``length`` tells you the 50 | length of the list (*Don't directly access the field, I don't make any 51 | guarantee that the structs will remain the same. They're implementation 52 | details!*). ``index_of`` will find the first occurrence of a value, by a 53 | linear search. 54 | 55 | You can create generic lists with the following functions: 56 | 57 | .. code:: C 58 | 59 | smb_list ll_create_list(); 60 | smb_list ll_cast_to_list(smb_ll *list); 61 | 62 | And you can get an iterator for a list with this function: 63 | 64 | .. code:: C 65 | 66 | smb_iter ll_get_iter(const smb_ll *list); 67 | 68 | Sample Usage 69 | ------------ 70 | 71 | Using the linked list is pretty simple. Here, I create a list, add some 72 | numbers to it, and then free it. 73 | 74 | .. code:: C 75 | 76 | smb_ll *list = ll_create(); 77 | 78 | ll_append(list, LLINT(0)); 79 | ll_append(list, LLINT(1)); 80 | 81 | ll_free(list); 82 | 83 | Functional Programming! 84 | ----------------------- 85 | 86 | One of the more recent additions to my linked list API is some high-level 87 | functions for lists - namely, map, filter, and fold. My implementations are all 88 | in-place for efficiency. Here they are: 89 | 90 | .. code:: C 91 | 92 | void ll_filter(smb_ll *list, bool (*test_function)(DATA)); 93 | void ll_map(smb_ll *list, DATA (*map_function)(DATA)); 94 | DATA ll_foldl(smb_ll *list, DATA start_value, DATA (*reduction)(DATA,DATA)); 95 | DATA ll_foldr(smb_ll *list, DATA start_value, DATA (*reduction)(DATA,DATA)); 96 | 97 | Check the API documentation for full details. 98 | 99 | Structure 100 | --------- 101 | 102 | The following items are implementation details, and not really necessary 103 | to use the linked list. Here is the structure of a node: 104 | 105 | .. code:: C 106 | 107 | typedef struct smb_ll_node 108 | { 109 | struct smb_ll_node *prev; 110 | struct smb_ll_node *next; 111 | DATA data; 112 | } smb_ll_node; 113 | 114 | And here is the actual linked list struct: 115 | 116 | .. code:: C 117 | 118 | typedef struct smb_ll 119 | { 120 | struct smb_ll_node *head; 121 | struct smb_ll_node *tail; 122 | int length; 123 | } smb_ll; 124 | -------------------------------------------------------------------------------- /doc/tutorial/list.rst: -------------------------------------------------------------------------------- 1 | List Interface 2 | ============== 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/list.h" 7 | 8 | - `Header 9 | (master) `__ 10 | 11 | The list interface, defined in ``libstephen/list.h``, is designed to be 12 | an implementation-agnostic interface to a list. It comes at a very high 13 | price. It is a struct containing a large set of function pointers, and a 14 | pointer to data. This means that you have a powerful, full-featured 15 | interface to the list. However, frequently using this interface is 16 | probably inefficient. 17 | 18 | Operations 19 | ---------- 20 | 21 | .. code:: C 22 | 23 | typedef struct smb_list 24 | { 25 | void *data; 26 | void (*append)(struct smb_list *l, DATA newData); 27 | void (*prepend)(struct smb_list *l, DATA newData); 28 | DATA (*get)(const struct smb_list *l, int index, smb_status *status); 29 | void (*set)(struct smb_list *l, int index, DATA newData, smb_status *status); 30 | void (*remove)(struct smb_list *l, int index, smb_status *status); 31 | void (*insert)(struct smb_list *l, int index, DATA newData); 32 | void (*delete)(struct smb_list *l); 33 | int (*length)(const struct smb_list *l); 34 | void (*push_back)(struct smb_list *l, DATA newData); 35 | DATA (*pop_back)(struct smb_list *l, smb_status *status); 36 | DATA (*peek_back)(struct smb_list *l, smb_status *status); 37 | void (*push_front)(struct smb_list *l, DATA newData); 38 | DATA (*pop_front)(struct smb_list *l, smb_status *status); 39 | DATA (*peek_front)(struct smb_list *l, smb_status *status); 40 | int (*index_of)(const struct smb_list *l, DATA d, DATA_COMPARE comp); 41 | } smb_list; 42 | 43 | These operations are most of the useful features of the array and linked 44 | lists. 45 | 46 | Sample Usage 47 | ------------ 48 | 49 | The following code is straight from one of my simpler tests for lists. 50 | It works with linked lists or array lists (but a linked list is created 51 | here). Each iteration the following code adds a number to the list, and 52 | then verifies that the list contains the expected values. 53 | 54 | .. code:: C 55 | 56 | smb_status status = SMB_SUCCESS; 57 | smb_list list = ll_create_list(); 58 | int i, j; 59 | 60 | for (i = 0; i < 200; i++) { 61 | list.append(&list, LLINT(i)); 62 | assert(list.length(&list) == i + 1); 63 | 64 | for (j = 0; j < list.length(&list); j++) { 65 | assert(list.get(&list, j, &status).data_llint == j); 66 | assert(status == SMB_SUCCESS); 67 | } 68 | } 69 | 70 | list.delete(&list); 71 | -------------------------------------------------------------------------------- /doc/tutorial/logging.rst: -------------------------------------------------------------------------------- 1 | Logging 2 | ======= 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/log.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | Logging is a very useful way to record messages about your program's 14 | operation so you can debug it later. Since C's debugging tools are 15 | rather difficult (encouraging so-called ``printf`` debugging), having a 16 | sane logging setup that can be turned on or off easily is important. 17 | Libstephen provides a very nice logging system that satisfies these 18 | requirements. It is inspired by Python's logging system, but somewhat 19 | simpler. 20 | 21 | History 22 | ------- 23 | 24 | When I first created Libstephen, I wanted a way to produce debugging 25 | printouts and disable them when I didn't need them. I came up with a 26 | macro (``SMB_DP``) that would expand to ``printf`` when ``DEBUG`` was 27 | defined, or else expand to nothing. This worked well (so long as I 28 | didn't rely on any side effects of expressions within the arguments!). 29 | However, as I started to link to and use Libstephen within other 30 | projects, I ran into issues with this system. 31 | 32 | First was that if client code linked to a debug build of Libstephen, it 33 | could get a flood of output from my own logging (that was enabled for 34 | debugging). Second, I began to need more control over logging. There was 35 | no control for enabling logging for just certain modules, or just 36 | messages of a certain priority. That is what inspired me to build a 37 | Python-like logging framework. 38 | 39 | Goals 40 | ----- 41 | 42 | I focused on the following development goals: 43 | 44 | - In the common case, the logger should be a drop-in replacement for 45 | ``printf``. It shouldn't require much extra typing or arguments. 46 | - There should be levels of "priority", from fine grained debugging 47 | output, to critical errors only. You should be able to set how much 48 | your logger shows. 49 | - Logging output should be able to be handled by multiple handlers 50 | (files). These handlers should be allowed to set their own log levels 51 | in addition to the logger. 52 | - Log messages should tell you exactly where in the code they came 53 | from. In Emacs' compilation mode, you should be able to hit enter on 54 | a log message and go directly to its source. 55 | 56 | Concepts 57 | -------- 58 | 59 | The logging system is made up of loggers (``smb_logger``), which are 60 | collections of log handlers (``smb_loghandler``), along with a log 61 | level. Log handlers have their own log level, and they have a file 62 | associated with them. The logging system also has a single "default" 63 | logger, which is used when you don't specify a logger. At startup, the 64 | default logger will report any level message to ``stderr``. 65 | 66 | The log levels are defined as macros expanding to integers: 67 | 68 | .. code:: C 69 | 70 | #define LEVEL_NOTSET 0 71 | #define LEVEL_DEBUG 10 72 | #define LEVEL_INFO 20 73 | #define LEVEL_WARNING 30 74 | #define LEVEL_ERROR 40 75 | #define LEVEL_CRITICAL 50 76 | 77 | You can log at any integer level, so these levels are just suggestions, 78 | but they come with nice macros for logging. 79 | 80 | Functions & Macros 81 | ------------------ 82 | 83 | You can create a ``smb_logger`` using the default lifetime functions 84 | (``sl_*``), which take no extra arguments. You can use the following 85 | functions to adjust loggers: 86 | 87 | .. code:: C 88 | 89 | void sl_set_level(smb_logger *l, int level); 90 | void sl_add_handler(smb_logger *l, smb_loghandler h, smb_status *status); 91 | void sl_clear_handlers(smb_logger *l); 92 | void sl_set_default_logger(smb_logger *l); 93 | 94 | Each of these functions can take ``NULL`` in order to reference the 95 | default logger. As you can see, you can also set the default logger so 96 | that code that logs to the default logger will use your own instead. If 97 | you call ``sl_set_default_logger(NULL)``, the default logger should be 98 | completely reset to defaults. 99 | 100 | Once you have your loggers (which you don't have to do immediately, 101 | since you could just use the default logger), you can use the ``LOG`` 102 | macro to begin logging: 103 | 104 | :: 105 | 106 | LOG(pointer to logger, log level, "format string", format args...) 107 | 108 | This is a macro, so in general avoid using side effects within the 109 | arguments. The current implementation will not evaluate expressions more 110 | than once, but I make no guarantee about that. 111 | 112 | If you're using the default logger, you have the following shortcuts: 113 | 114 | :: 115 | 116 | DDEBUG("format string", format args ...) 117 | DINFO("format string", format args ...) 118 | DWARNING("format string", format args ...) 119 | DERROR("format string", format args ...) 120 | DCRITICAL("format string", format args ...) 121 | 122 | Think of the ``D`` as standing for Default. If you are using any other 123 | logger, you have these shortcuts: 124 | 125 | :: 126 | 127 | LDEBUG(logger, "format string", format args ...) 128 | LINFO(logger, "format string", format args ...) 129 | LWARNING(logger, "format string", format args ...) 130 | LERROR(logger, "format string", format args ...) 131 | LCRITICAL(logger, "format string", format args ...) 132 | 133 | Think of the ``L`` as standing for Logger. You have to specify your 134 | logger. 135 | 136 | All of these macros expand to call a single function: 137 | 138 | .. code:: C 139 | 140 | void sl_log(smb_logger *l, char *file, int line, const char *function, int level, ...); 141 | 142 | You don't want to call this function yourself, since most of those 143 | arguments (file, line, function) are provided by preprocessor arguments. 144 | -------------------------------------------------------------------------------- /doc/tutorial/unittest.rst: -------------------------------------------------------------------------------- 1 | Unit Testing 2 | ============ 3 | 4 | .. code:: C 5 | 6 | #include "libstephen/ut.h" 7 | 8 | - `Header 9 | (master) `__ 10 | - `Implementation 11 | (master) `__ 12 | 13 | libstephen includes a rather spartan unit testing framework. I once 14 | nicknamed it "SmbUnit", and thus all the functions begin with ``su_``. 15 | Meanwhile, the header file is ``libstephen/ut.h``. This is a rather 16 | annoying inconsistency, and I may deal with it before releasing a 1.0. 17 | 18 | Anyway, the unit testing framework defines tests, test groups, and test 19 | assertions. There's really no escape from a good deal of boilerplate to 20 | get it to work. But I happen to use YaSnippet in Emacs, so I have 21 | defined auto-expanding snippets for creating tests and groups. 22 | 23 | History 24 | ------- 25 | 26 | One of the motivations behind the unit testing framework is to "keep it 27 | simple, stupid." I figured it would be horrible if I spent more time 28 | debugging my unit testing framework than my actual code, so I made two 29 | decisions: 30 | 31 | - The unit testing framework could not depend on any other part of 32 | libstephen! No lists! 33 | - The unit testing framework should try to minimize complex operations, 34 | like allocating and reallocating memory. 35 | 36 | Therefore, the framework has set limits for the size of a test 37 | description (20 characters), and the number of tests in a group (20 38 | tests). These are pretty much guaranteed to never go down, and they may 39 | go up. 40 | 41 | At one point, my entire library had the ability to count bytes 42 | allocated, and decrement the counter as bytes were freed. I removed this 43 | feature from the library for three reasons: 44 | 45 | - Tools like Valgrind do this without any change to the code! 46 | - The changes I needed in order to implement this feature were really 47 | limiting. Namely, I had to specify how many bytes I was freeing when 48 | I freed them! 49 | - Turning off the feature during release builds got rather tricky, 50 | especially when you started considering how client code would link 51 | the library. 52 | 53 | So, I removed the feature, which meant that tests couldn't detect memory 54 | leaks any more. But I didn't miss it too much, since it's nearly as easy 55 | to run ``valgrind bin/release/test`` as it is to run 56 | ``bin/release/test``. 57 | 58 | Operations 59 | ---------- 60 | 61 | .. code:: C 62 | 63 | smb_ut_test *su_create_test(char *description, int (*run)()); 64 | smb_ut_group *su_create_test_group(char *description); 65 | void su_add_test(smb_ut_group *group, smb_ut_test *test); 66 | int su_run_test(smb_ut_test *test); 67 | int su_run_group(smb_ut_group *group); 68 | void su_delete_test(smb_ut_test *test); 69 | void su_delete_group(smb_ut_group *group); 70 | 71 | There are no ``init`` or ``destroy`` operations, pretty much in the name 72 | of keeping it simple. The basic operation is that you create a group, 73 | create tests, add the tests to the group, and run the group (which runs 74 | each individual test). Then you delete the group (which deletes each 75 | individual test). 76 | 77 | Test functions take void, and return an int. The return value should be 78 | 0 on success, or some non-zero value on failure. This value is typically 79 | the line number of the assertion that failed. 80 | 81 | Test assertions are done with the macro ``TEST_ASSERT(condition);``. On 82 | failure, it returns the current line number. 83 | 84 | Sample Use 85 | ---------- 86 | 87 | Here is a sample from some of libstephen's unit tests: 88 | 89 | .. code:: C 90 | 91 | smb_list (*get_list)(void); 92 | 93 | int test_append() 94 | { 95 | /* ... */ 96 | TEST_ASSERT(/* test condition */); 97 | return 0; 98 | } 99 | 100 | int test_prepend() 101 | { /* ... */ } 102 | 103 | /* ... 104 | ... 105 | ... */ 106 | 107 | void run_list_tests(char *desc) 108 | { 109 | smb_ut_group *group = su_create_test_group(desc); 110 | 111 | smb_ut_test *append = su_create_test("append", test_append); 112 | su_add_test(group, append); 113 | 114 | smb_ut_test *prepend = su_create_test("prepend", test_prepend); 115 | su_add_test(group, prepend); 116 | 117 | /* ... 118 | ... 119 | ... */ 120 | 121 | su_run_group(group); 122 | su_delete_group(group); 123 | } 124 | 125 | void list_test(void) 126 | { 127 | get_list = &ll_create_list; 128 | run_list_tests("list_ll"); 129 | 130 | get_list = &al_create_list; 131 | run_list_tests("list_al"); 132 | } 133 | 134 | This is a rather advanced example that shows some cool ways to deal with 135 | lacks of test setup and teardown functions. These tests are designed to 136 | run on array lists and linked lists. There is a global ``get_list()`` 137 | function pointer that is initially set to be linked list, and then set 138 | to be array list. The test functions use this to get a UUT. 139 | 140 | Anyhow, the mean features and uses are demonstrated under the function 141 | ``run_list_tests()``. 142 | -------------------------------------------------------------------------------- /inc/libstephen/ad.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/ad.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 3 August 2014 8 | 9 | @brief Contains functions to simplify processing command line args. 10 | 11 | The args library analyzes args as generally as possible. It recognizes three 12 | different types. First is the 'flag' or 'regular flag', which is simply one 13 | character. Their order doesn't matter. They're placed on the command line 14 | after a single hyphen. 15 | 16 | Then there are long flags, which are strings that come after two hyphens. 17 | Both flags and long flags are allowed to have parameters passed with them. 18 | 19 | Finally are bare strings, which are things that aren't passed as parameters or 20 | as flags. 21 | 22 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 23 | Revised BSD License. See the LICENSE.txt file for details. 24 | 25 | *******************************************************************************/ 26 | 27 | #ifndef LIBSTEPHEN_AD_H 28 | #define LIBSTEPHEN_AD_H 29 | 30 | #include /* uint64_t */ 31 | #include "base.h" 32 | 33 | /** 34 | @brief The number of regular flags. 52 = 26 + 26. 35 | */ 36 | #define MAX_FLAGS 52 37 | 38 | /** 39 | @brief Data structure to store information on arguments passed to the program. 40 | */ 41 | typedef struct smb_ad 42 | { 43 | /** 44 | @brief Holds boolean value for whether each character flag is set. 45 | */ 46 | uint64_t flags; 47 | 48 | /** 49 | @brief Holds the parameters for each regular (character) flag. 50 | */ 51 | char *flag_strings[MAX_FLAGS]; 52 | 53 | /** 54 | @brief Holds the long flags. 55 | */ 56 | struct smb_ll *long_flags; 57 | 58 | /** 59 | @brief Holds the parameters of the long flags. 60 | */ 61 | struct smb_ll *long_flag_strings; 62 | 63 | /** 64 | @brief Holds the bare strings (strings that aren't flags or flag params). 65 | */ 66 | struct smb_ll *bare_strings; 67 | 68 | } smb_ad; 69 | 70 | /** 71 | @brief Initialize an smb_ad structure in memory that has already been 72 | allocated. 73 | @param data The pointer to the memory to allocate in. 74 | */ 75 | void arg_data_init(smb_ad *data); 76 | /** 77 | @brief Allocate and initialize a smb_ad structure for argument parsing. 78 | @returns A pointer to the structure. 79 | */ 80 | smb_ad *arg_data_create(); 81 | /** 82 | @brief Free the resources of an arg_data object, but do not free it. 83 | @param data The arg data to destroy. 84 | */ 85 | void arg_data_destroy(smb_ad *data); 86 | /** 87 | @brief Free the resources of an arg data object, and then free the object 88 | itself. 89 | @param data The arg data to delete. 90 | */ 91 | void arg_data_delete(smb_ad *data); 92 | 93 | /** 94 | @brief Analyze the arguments passed to the program. 95 | 96 | Pass in the argc and argv, but make sure to decrement and increment each 97 | respective variable so they do not include the name of the program. Unless, 98 | of course, you want the program name to be processed as an argument. Once 99 | you have called this function, you can use the querying functions to find all 100 | the arguments. 101 | 102 | @param data A pointer to an smb_ad object. 103 | @param argc The number of arguments (not including program name). 104 | @param argv The arguments themselves (not including program name). 105 | */ 106 | void process_args(smb_ad *data, int argc, char **argv); 107 | /** 108 | @brief Check whether a flag is raised. 109 | @param data The smb_ad returned by process_args(). 110 | @param flag The character flag to check. Alphabetical only. 111 | @retval 0 if the flag was not set. 112 | @retval 1 otherwise. 113 | */ 114 | int check_flag(smb_ad *data, char flag); 115 | /** 116 | @brief Check whether a long flag appeared. It must occur verbatim. 117 | @param data The smb_ad returned by process_args(). 118 | @param flag The string flag to check for. 119 | @returns the index of the flag + 1 if it is set. 120 | @retval 0 iff the flag was not set. 121 | */ 122 | int check_long_flag(smb_ad *data, char *flag); 123 | /** 124 | @brief Check whether a bare string appeared. It must occur verbatim. 125 | @param data The smb_ad returned by process_args(). 126 | @param string The string to search for. 127 | @returns the index of the flag + 1 if it is set. 128 | @retval 0 iff the flag was not set. 129 | */ 130 | int check_bare_string(smb_ad *data, char *string); 131 | /** 132 | @brief Return the string parameter associated with the flag. 133 | @param data The smb_ad returned by process_args(). 134 | @param flag The flag to find parameters of. 135 | @returns The parameter of the flag. 136 | */ 137 | char *get_flag_parameter(smb_ad *data, char flag); 138 | /** 139 | @brief Return the string parameter associated with the long string. 140 | @param data The smb_ad returned by process_args(). 141 | @param string The long flag to find parameters of. 142 | @returns The parameter of the long flag. 143 | @retval NULL if no parameter or if flag not found. 144 | */ 145 | char *get_long_flag_parameter(smb_ad *data, char *string); 146 | /** 147 | @brief Print the contents of the arg data struct to a file for debugging. 148 | @param data The struct to print. 149 | @param f The file to print to. 150 | */ 151 | void ad_print(smb_ad *data, FILE *f); 152 | 153 | #endif // LIBSTEPHEN_AD_H 154 | -------------------------------------------------------------------------------- /inc/libstephen/base.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/base.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 3 August 2014 8 | 9 | @brief Libstephen: Base Declarations. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef LIBSTEPHEN_BASE_H 17 | #define LIBSTEPHEN_BASE_H 18 | 19 | #include /* size_t */ 20 | #include /* fprintf */ 21 | #include /* bool */ 22 | 23 | /******************************************************************************* 24 | 25 | Base Data Type 26 | 27 | *******************************************************************************/ 28 | 29 | /** 30 | @brief Base data type for the data structures. 31 | 32 | Capable of containing long integers, double precision floats, or pointers. 33 | Takes up 8 bytes. 34 | */ 35 | typedef union DATA { 36 | long long int data_llint; 37 | double data_dbl; 38 | void * data_ptr; 39 | 40 | } DATA; 41 | 42 | /** 43 | @brief A function pointer that takes a DATA and performs an action on it 44 | 45 | The function could count it, call free on it, print it, etc. Useful for 46 | stuff like deleting data structures full of items (if they're pointers to 47 | dynamically allocated data, they'll need to be freed), applying an action to 48 | every item in a list (e.g. printing), and many more applications. 49 | */ 50 | typedef void (*DATA_ACTION)(DATA); 51 | 52 | /** 53 | @brief A function pointer that takes two DATA and compares them. 54 | 55 | This comparator function is used for two purposes: (1) to check for equality, 56 | and (2) to order data. If a particular type of data has no ordering, then it 57 | is sufficient for the purposes of equality testing to return 0 if equal, and 58 | 1 if not equal. However, this will fail for any function that uses the 59 | DATA_COMPARE to order DATA. Therefore, any function that takes a 60 | DATA_COMPARE should specify in its documentation whether it will use it for 61 | equality testing, or for ordering. 62 | 63 | The DATA_COMPARE function shall return 0 iff the two DATA are equal. It 64 | shall return a value less than 0 iff the first is less than the second. It 65 | shall return a value greater than zero iff the first is greater than the 66 | second. 67 | */ 68 | typedef int (*DATA_COMPARE)(DATA,DATA); 69 | 70 | int data_compare_string(DATA d1, DATA d2); 71 | int data_compare_int(DATA d1, DATA d2); 72 | int data_compare_float(DATA d1, DATA d2); 73 | int data_compare_pointer(DATA d1, DATA d2); 74 | 75 | /** 76 | @brief A function pointer that takes a DATA and prints it. 77 | 78 | This function should not print a trailing newline! There are higher level 79 | functions that will deal with that. 80 | */ 81 | typedef void (*DATA_PRINTER)(FILE*, DATA); 82 | 83 | void data_printer_string(FILE *f, DATA d); 84 | void data_printer_int(FILE *f, DATA d); 85 | void data_printer_float(FILE *f, DATA d); 86 | void data_printer_pointer(FILE *f, DATA d); 87 | 88 | 89 | /******************************************************************************* 90 | 91 | Memory Management 92 | 93 | *******************************************************************************/ 94 | 95 | void *smb___new(size_t amt); 96 | void *smb___renew(void *ptr, size_t newsize); 97 | void smb___free(void *ptr); 98 | 99 | /** 100 | @brief A nicer allocation function. 101 | 102 | This macro/function wraps malloc(). It's inspired by the g_new function in 103 | glib. It allows you to allocate memory and adjust the allocation counter in 104 | one call. Instead of specifying the size of the memory, you specify type and 105 | number of instances. 106 | 107 | @param type The type of the memory to allocate. 108 | @param n The number of instances to allocate. 109 | @returns A pointer to the allocated memory, casted to the correct type. 110 | */ 111 | #define smb_new(type, n) ((type*) smb___new((n) * sizeof(type))) 112 | 113 | /** 114 | @brief A nicer reallocation function. 115 | 116 | This macro/function wraps realloc(). It allows you reallocate memory and 117 | adjust the allocation counter accordingly. Exits on failure. 118 | 119 | @param type The type of the memory to reallocate. 120 | @param ptr The memory to reallocate. 121 | @param newamt The amount of items to allocate. 122 | @returns A pointer to the reallocated memory. 123 | */ 124 | #define smb_renew(type, ptr, newamt) \ 125 | ((type*) smb___renew(ptr, (newamt) * sizeof(type))) 126 | 127 | /** 128 | @brief A nicer free function. 129 | 130 | This macro/function wraps free(). It exits on failure and adjusts the malloc 131 | counter. 132 | @param ptr The memory you're freeing. 133 | */ 134 | #define smb_free(ptr) smb___free(ptr) 135 | 136 | /******************************************************************************* 137 | 138 | Error Handling 139 | 140 | *******************************************************************************/ 141 | 142 | /** 143 | @brief An enumeration of all possible status values for libstephen functions. 144 | */ 145 | typedef int smb_status; 146 | 147 | #define SMB_SUCCESS 0 148 | #define SMB_INDEX_ERROR 1 149 | #define SMB_NOT_FOUND_ERROR 2 150 | #define SMB_STOP_ITERATION 3 151 | #define SMB_EXTERNAL_EXCEPTION_START 100 152 | 153 | char *smb_status_string(smb_status status); 154 | 155 | #define PRINT_ERROR_LOC fprintf(stderr, "An error occurred at line %d in file "\ 156 | "%s (function %s).\n", __LINE__, __FILE__,\ 157 | __func__) 158 | 159 | /******************************************************************************* 160 | 161 | Utilities 162 | 163 | *******************************************************************************/ 164 | 165 | // For handling integers with flags. 166 | #define FLAG_CHECK(var,bit) ((var) & (bit)) 167 | #define FLAG_SET(var,bit) var |= (bit) 168 | #define FLAG_CLEAR(var,bit) var &= (~(bit)) 169 | 170 | // For creating DATA from values. 171 | #define LLINT(value) ((DATA){.data_llint=(value)}) 172 | #define PTR(value) ((DATA){.data_ptr=(value)}) 173 | #define DBL(value) ((DATA){.data_dbl=(value)}) 174 | 175 | #endif // LIBSTEPHEN_BASE_H 176 | -------------------------------------------------------------------------------- /inc/libstephen/bf.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/bf.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 3 August 2014 8 | 9 | @brief A tiny little bit of code that allows for efficient storage and 10 | access to large amounts of boolean data. 11 | 12 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 13 | Revised BSD License. See the LICENSE.txt file for details. 14 | 15 | *******************************************************************************/ 16 | 17 | #ifndef LIBSTEPHEN_BF_H 18 | #define LIBSTEPHEN_BF_H 19 | 20 | #include "base.h" 21 | 22 | /** 23 | @brief Number of bits in a char type. Shouldn't really change... 24 | */ 25 | #define BIT_PER_CHAR 8 26 | 27 | /** 28 | @brief Get the number amount of space eneded for a bitfield of the specified 29 | amount of booleans. 30 | 31 | If you want to allocate a buffer on the stack, you need this macro. 32 | 33 | @param num_bools The number of booleans. 34 | @returns The number of bytes 35 | */ 36 | #define SMB_BITFIELD_SIZE(num_bools) ((int)((num_bools) / BIT_PER_CHAR) + \ 37 | ((num_bools) % BIT_PER_CHAR == 0 ? 0 : 1)) 38 | 39 | /** 40 | @brief Initialize the memory where a bitfield is contained to all 0's. 41 | 42 | This is public so people can use the function to allocate their own bitfields 43 | on function stacks instead of via the heap. No errors are defined for this 44 | function. 45 | @param data A pointer to the bitfield. 46 | @param num_bools The size of the bitfield, in number of bools (aka bits, not 47 | bytes). 48 | */ 49 | void bf_init(unsigned char *data, int num_bools); 50 | /** 51 | @brief Allocate a bitfield capable of holding the given number of bools. 52 | 53 | This will be allocated in dynamic memory, and a pointer will be returned. 54 | The bf_init function is available for creating bitfields in arbitrary 55 | locations. 56 | @param num_bools The number of bools to fit in the bit field. 57 | @returns A pointer to the bitfield. 58 | */ 59 | unsigned char *bf_create(int num_bools); 60 | /** 61 | @brief Delete the bitfield pointed to. Only do this if you created the 62 | bitfield via bf_create(). 63 | @param data A pointer to the bitfield. 64 | @param num_bools The number of bools contained in the bitfield. 65 | */ 66 | void bf_delete(unsigned char *data, int num_bools); 67 | /** 68 | @brief Check whether the given bit is set. 69 | @param data A pointer to the bitfield. 70 | @param index The index of the bit to Check 71 | @retval 0 if the bit is not set. 72 | @retval Nonzero if the bit is set. 73 | */ 74 | int bf_check(unsigned char *data, int index); 75 | /** 76 | @brief Set a bit. 77 | @param data A pointer to the bitfield 78 | @param index The index of the bit to set. 79 | */ 80 | void bf_set(unsigned char *data, int index); 81 | /** 82 | @brief Clear a bit. 83 | @param data A pointer to the bitfield. 84 | @param index The index of the bit to clear. 85 | */ 86 | void bf_clear(unsigned char *data, int index); 87 | /** 88 | @brief Clear a bit. 89 | @param data A pointer to the bitfield. 90 | @param index The index of the bit to flip. 91 | */ 92 | void bf_flip(unsigned char *data, int index); 93 | 94 | #endif // LIBSTEPHEN_BF_H 95 | -------------------------------------------------------------------------------- /inc/libstephen/cb.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/cb.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Saturday, 23 May 2015 8 | 9 | @brief Character buffer data structure, for simpler string handling. 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef LIBSTEPHEN_CB_H 17 | #define LIBSTEPHEN_CB_H 18 | 19 | #include 20 | #include 21 | 22 | /** 23 | @brief A character buffer utility that is easier to handle than a char*. 24 | 25 | This character buffer provides an interface for string processing that allows 26 | you to be efficient, while not needing to handle nearly as much of the 27 | allocations that are necessary. It automatically expands as you add to it. 28 | */ 29 | typedef struct { 30 | /** 31 | @brief Buffer pointer. 32 | */ 33 | char *buf; 34 | /** 35 | @brief Allocated size of the buffer. 36 | */ 37 | int capacity; 38 | /** 39 | @brief Length of the string in the buffer. 40 | */ 41 | int length; 42 | } cbuf; 43 | 44 | /** 45 | @brief Initialize a brand-new character buffer. 46 | 47 | A buffer of the given capacity is initialized, and given an empty string 48 | value. 49 | @param obj The cbuf to initialize. 50 | @param capacity Initial capacity of the buffer. 51 | */ 52 | void cb_init(cbuf *obj, int capacity); 53 | /** 54 | @brief Allocate and initialize a brand-new character buffer. 55 | 56 | A buffer of the given capacity is initialized, and given an empty string 57 | value. The cbuf struct is also allocated and the pointer returned. 58 | @param capacity Initial capacity of the buffer. 59 | */ 60 | cbuf *cb_create(int capacity); 61 | /** 62 | @brief Deallocate the contents of the string buffer. 63 | @param obj The character buffer to deallocate. 64 | */ 65 | void cb_destroy(cbuf *obj); 66 | /** 67 | @brief Deallocate the contents of, and delete the string buffer. 68 | @param obj The character buffer to delete. 69 | */ 70 | void cb_delete(cbuf *obj); 71 | 72 | /** 73 | @brief Concat a string onto the end of the character buffer. 74 | @param obj The buffer to concat onto. 75 | @param str The string to concat. 76 | */ 77 | void cb_concat(cbuf *obj, char *str); 78 | /** 79 | @brief Append a character onto the end of the character buffer. 80 | @param obj The buffer to append onto. 81 | @param next The character to append. 82 | */ 83 | void cb_append(cbuf *obj, char next); 84 | /** 85 | @brief Reallocate the buffer to the exact size of the contained string. 86 | @param obj The buffer to reallocate. 87 | */ 88 | void cb_trim(cbuf *obj); 89 | /** 90 | @brief Empty the buffer of its contents. 91 | @param obj The buffer to clear. 92 | */ 93 | void cb_clear(cbuf *obj); 94 | /** 95 | @brief Format and print a string onto the end of a character buffer. 96 | @param obj The object to print onto. 97 | @param format The format string to print. 98 | @param ... The arguments to the format string. 99 | */ 100 | void cb_printf(cbuf *obj, char *format, ...); 101 | /** 102 | @brief Format and print a string onto the cbuf using a va_list. 103 | @param obj The cbuf to print into. 104 | @param format The format string to print. 105 | @param va The vararg list. 106 | */ 107 | void cb_vprintf(cbuf *obj, char *format, va_list va); 108 | 109 | 110 | /** 111 | @brief A wide character buffer. Similar to cbuf. 112 | 113 | Again, this buffer provides an interface for simpler string manipulation, by 114 | managing the buffer reallocations for you. Of course, the difference being 115 | that this one is for wide characters. 116 | */ 117 | typedef struct { 118 | /** 119 | @brief Buffer pointer. 120 | */ 121 | wchar_t *buf; 122 | /** 123 | @brief Actual capacity of the buffer. 124 | */ 125 | int capacity; 126 | /** 127 | @brief Length of the string in the buffer. 128 | */ 129 | int length; 130 | } wcbuf; 131 | 132 | /** 133 | @brief Initialize a string buffer to a given capacity. 134 | @param obj The string buffer to initialize. 135 | @param capacity The initial capacity to give it. 136 | */ 137 | void wcb_init(wcbuf *obj, int capacity); 138 | /** 139 | @brief Initialize a string buffer and allocate the struct as well. 140 | @param capacity The capacity of the string buffer to create. 141 | @return A pointer to the buffer. 142 | */ 143 | wcbuf *wcb_create(int capacity); 144 | /** 145 | @brief Destroy the wide buffer contained in the wcbuf struct. 146 | @param obj The wide buffer to destroy. 147 | */ 148 | void wcb_destroy(wcbuf *obj); 149 | /** 150 | @brief Destroy and delete the wide buffer and struct. 151 | @param obj The wide buffer struct to destroy. 152 | */ 153 | void wcb_delete(wcbuf *obj); 154 | 155 | /** 156 | @brief Concat a wide character string into the wide buffer. 157 | @param obj The wide buffer to concat onto. 158 | @param str The wide string to concat on. 159 | */ 160 | void wcb_concat(wcbuf *obj, wchar_t *str); 161 | /** 162 | @brief Append a single character onto the buffer. 163 | @param obj The wide buffer to append onto. 164 | @param next The wide character to append. 165 | */ 166 | void wcb_append(wcbuf *obj, wchar_t next); 167 | /** 168 | @brief Reallocate the buffer to the exact size of the contained string. 169 | @param obj The buffer to reallocate. 170 | */ 171 | void wcb_trim(wcbuf *obj); 172 | /** 173 | @brief Empty the buffer of its contents. 174 | @param obj The buffer to clear. 175 | */ 176 | void wcb_clear(wcbuf *obj); 177 | /** 178 | @brief Printf a wide character string onto the wide buffer. 179 | @param obj The wide buffer to printf onto. 180 | @param format Format string to print. 181 | @param ... The arguments to to put into the format string. 182 | */ 183 | void wcb_printf(wcbuf *obj, wchar_t *format, ...); 184 | /** 185 | @brief Format and print a string onto the wcbuf using a va_list. 186 | @param obj The wcbuf to print into. 187 | @param format The format string to print. 188 | @param va The vararg list. 189 | */ 190 | void wcb_vprintf(wcbuf *obj, wchar_t *format, va_list va); 191 | 192 | #endif //LIBSTEPHEN_CB_H 193 | -------------------------------------------------------------------------------- /inc/libstephen/hta.h: -------------------------------------------------------------------------------- 1 | 2 | /***************************************************************************//** 3 | 4 | @file libstephen/hta.h 5 | 6 | @author Stephen Brennan 7 | 8 | @date Created Tuesday, 20 September 2016 9 | 10 | @brief Hash Table for Any data 11 | 12 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 13 | Revised BSD License. See the LICENSE.txt file for details. 14 | 15 | *******************************************************************************/ 16 | 17 | #ifndef LIBSTEPHEN_HTA_H 18 | #define LIBSTEPHEN_HTA_H 19 | 20 | #include "base.h" 21 | 22 | #define HTA_KEY_OFFSET 1 23 | 24 | #define HTA_MARK(t, i) ((int8_t*)t->table)[i] 25 | 26 | /** 27 | @brief A hash function declaration. 28 | 29 | @param to_hash The data that will be passed to the hash function. 30 | @returns The hash value 31 | */ 32 | typedef unsigned int (*HTA_HASH)(void *to_hash); 33 | 34 | /** 35 | @brief A comparator function declaration. 36 | 37 | @param left Left side of comparison. 38 | @param right Right side of comparison. 39 | @returns < 0 if left < right, 0 if left == right, > 0 if left > right 40 | */ 41 | typedef int (*HTA_COMP)(void *left, void *right); 42 | 43 | /** 44 | @brief A print function declaration. 45 | 46 | @param f File to print to. 47 | @param obj Object to print. 48 | */ 49 | typedef void (*HTA_PRINT)(FILE *f, void *obj); 50 | 51 | /** 52 | @brief A hash table data structure. 53 | */ 54 | typedef struct smb_hta 55 | { 56 | /** 57 | @brief The number of items in the hash table. 58 | */ 59 | unsigned int length; 60 | 61 | /** 62 | @brief The number of slots allocated in the hash table. 63 | */ 64 | unsigned int allocated; 65 | 66 | /** 67 | @brief Size of keys 68 | */ 69 | unsigned int key_size; 70 | 71 | /** 72 | @brief Size of values 73 | */ 74 | unsigned int value_size; 75 | 76 | /** 77 | @brief The hash function for this hash table. 78 | */ 79 | HTA_HASH hash; 80 | 81 | /** 82 | @brief Function to use to compare equality. 83 | */ 84 | HTA_COMP equal; 85 | 86 | /** 87 | @brief Pointer to the data of the table. 88 | */ 89 | void *table; 90 | 91 | } smb_hta; 92 | 93 | /** 94 | @brief Initialize a hash table in memory already allocated. 95 | @param table A pointer to the table to initialize. 96 | @param hash_func A hash function for the table. 97 | @param equal A comparison function for DATA. 98 | @param key_size Size of keys. 99 | @param value_size Size of values. 100 | */ 101 | void hta_init(smb_hta *table, HTA_HASH hash_func, HTA_COMP equal, 102 | unsigned int key_size, unsigned int value_size); 103 | /** 104 | @brief Allocate and initialize a hash table. 105 | @param hash_func A function that takes one DATA and returns a hash value 106 | generated from it. It should be a good hash function. 107 | @param equal A comparison function for DATA. 108 | @param key_size Size of keys. 109 | @param value_size Size of values. 110 | @returns A pointer to the new hash table. 111 | */ 112 | smb_hta *hta_create(HTA_HASH hash_func, HTA_COMP equal, 113 | unsigned int key_size, unsigned int value_size); 114 | /** 115 | @brief Free any resources used by the hash table, but doesn't free the 116 | pointer. Doesn't perform any actions on the data as it is deleted. 117 | 118 | If pointers are contained within the hash table, they are not freed. Use to 119 | specify a deletion action on the hash table. 120 | @param table The table to destroy. 121 | */ 122 | void hta_destroy(smb_hta *table); 123 | /** 124 | @brief Free the hash table and its resources. No pointers contained in the 125 | table will be freed. 126 | @param table The table to free. 127 | */ 128 | void hta_delete(smb_hta *table); 129 | 130 | /** 131 | @brief Insert data into the hash table. 132 | 133 | Expands the hash table if the load factor is below a threshold. If the key 134 | already exists in the table, then the function will overwrite it with the new 135 | data provided. 136 | @param table A pointer to the hash table. 137 | @param key The key to insert. 138 | @param value The value to insert at the key. 139 | */ 140 | void hta_insert(smb_hta *table, void *key, void *value); 141 | /** 142 | @brief Remove the key, value pair stored in the hash table. 143 | 144 | This function does not call a deleter on the stored data. 145 | @param table A pointer to the hash table. 146 | @param key The key to delete. 147 | @param[out] status Status variable. 148 | @exception SMB_NOT_FOUND_ERROR If an item with the given key is not found. 149 | */ 150 | void hta_remove(smb_hta *table, void *key, smb_status *status); 151 | /** 152 | @brief Return the value associated with the key provided. 153 | @param table A pointer to the hash table. 154 | @param key The key whose value to retrieve. 155 | @param[out] status Status variable. 156 | @returns The value associated the key. 157 | @exception NOT_FOUND_ERROR 158 | */ 159 | void *hta_get(smb_hta const *table, void *key, smb_status *status); 160 | /** 161 | @brief Return true when a key is contained in the table. 162 | @param table A pointer to the hash table. 163 | @param key The key to search for. 164 | @returns Whether the key is present. 165 | */ 166 | bool hta_contains(smb_hta const *table, void *key); 167 | /** 168 | @brief Return the hash of the data, interpreting it as a string. 169 | @param data The string to hash, assuming that the value contained is a char*. 170 | @returns The hash value of the string. 171 | */ 172 | unsigned int hta_string_hash(void *data); 173 | int hta_string_comp(void *left, void *right); 174 | int hta_int_comp(void *left, void *right); 175 | /** 176 | @brief Print the entire hash table. 177 | 178 | This function is useful for diagnostics. It can show every row in the table 179 | (with full_mode) so you can see how well entries are distributed in the 180 | table. Or, it can be compact and show just the rows with data. 181 | @param table The table to print. 182 | @param full_mode Whether to print every row in the hash table. 183 | */ 184 | void hta_print(FILE *f, smb_hta const *table, HTA_PRINT key, HTA_PRINT value, 185 | int full_mode); 186 | 187 | #endif // LIBSTEPHEN_HTA_H 188 | -------------------------------------------------------------------------------- /inc/libstephen/lisp.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/lisp.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Monday, 27 June 2016 8 | 9 | @brief Lisp implementation for embedding. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_LIBSTEPHEN_LISP_H 17 | #define SMB_LIBSTEPHEN_LISP_H 18 | 19 | #include 20 | #include 21 | 22 | #include "libstephen/ht.h" 23 | #include "libstephen/rb.h" 24 | 25 | #define GC_NOMARK 'w' 26 | #define GC_QUEUED 'g' 27 | #define GC_MARKED 'b' 28 | 29 | #define LISP_VALUE_HEAD \ 30 | struct { \ 31 | struct lisp_type *type; \ 32 | struct lisp_value *next; \ 33 | char mark; \ 34 | } 35 | 36 | // Type declarations. 37 | typedef struct lisp_value { 38 | LISP_VALUE_HEAD; 39 | } lisp_value; 40 | 41 | // A lisp_runtime is NOT a lisp_value! 42 | typedef struct { 43 | lisp_value *head; 44 | lisp_value *tail; 45 | 46 | // Some special values we don't want to lose track of 47 | lisp_value *nil; 48 | 49 | smb_rb rb; 50 | } lisp_runtime; 51 | 52 | // The below ARE lisp_values! 53 | typedef struct lisp_scope { 54 | LISP_VALUE_HEAD; 55 | smb_ht scope; 56 | struct lisp_scope *up; 57 | } lisp_scope; 58 | 59 | typedef struct { 60 | LISP_VALUE_HEAD; 61 | lisp_value *left; 62 | lisp_value *right; 63 | } lisp_list; 64 | 65 | typedef struct lisp_type { 66 | LISP_VALUE_HEAD; 67 | const char *name; 68 | void (*print)(FILE *f, lisp_value *value); 69 | lisp_value * (*new)(void); 70 | void (*free)(void *value); 71 | smb_iter (*expand)(lisp_value*); 72 | lisp_value * (*eval)(lisp_runtime *rt, lisp_scope *scope, lisp_value *value); 73 | lisp_value * (*call)(lisp_runtime *rt, lisp_scope *scope, lisp_value *callable, lisp_value *arg); 74 | } lisp_type; 75 | 76 | typedef struct { 77 | LISP_VALUE_HEAD; 78 | char *sym; 79 | } lisp_symbol; 80 | 81 | typedef struct { 82 | LISP_VALUE_HEAD; 83 | char *message; 84 | } lisp_error; 85 | 86 | typedef struct { 87 | LISP_VALUE_HEAD; 88 | int x; 89 | } lisp_integer; 90 | 91 | typedef struct { 92 | LISP_VALUE_HEAD; 93 | char *s; 94 | } lisp_string; 95 | 96 | typedef lisp_value * (*lisp_builtin_func)(lisp_runtime*, lisp_scope*,lisp_value*); 97 | typedef struct { 98 | LISP_VALUE_HEAD; 99 | lisp_builtin_func call; 100 | char *name; 101 | } lisp_builtin; 102 | 103 | typedef struct { 104 | LISP_VALUE_HEAD; 105 | lisp_list *args; 106 | lisp_value *code; 107 | lisp_scope *closure; 108 | } lisp_lambda; 109 | 110 | // Interpreter stuff 111 | void lisp_init(lisp_runtime *rt); 112 | void lisp_mark(lisp_runtime *rt, lisp_value *v); 113 | void lisp_sweep(lisp_runtime *rt); 114 | void lisp_destroy(lisp_runtime *rt); 115 | 116 | // Shortcuts for type operations. 117 | void lisp_print(FILE *f, lisp_value *value); 118 | void lisp_free(lisp_value *value); 119 | lisp_value *lisp_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *value); 120 | lisp_value *lisp_call(lisp_runtime *rt, lisp_scope *scope, lisp_value *callable, 121 | lisp_value *arguments); 122 | lisp_value *lisp_new(lisp_runtime *rt, lisp_type *typ); 123 | 124 | // Shortcuts for creation of objects 125 | lisp_symbol *lisp_symbol_new(lisp_runtime *rt, char *string); 126 | lisp_error *lisp_error_new(lisp_runtime *rt, char *message); 127 | lisp_builtin *lisp_builtin_new(lisp_runtime *rt, char *name, 128 | lisp_builtin_func call); 129 | lisp_value *lisp_nil_new(lisp_runtime *rt); 130 | 131 | // Helper functions 132 | void lisp_scope_bind(lisp_scope *scope, lisp_symbol *symbol, lisp_value *value); 133 | lisp_value *lisp_scope_lookup(lisp_runtime *rt, lisp_scope *scope, 134 | lisp_symbol *symbol); 135 | void lisp_scope_add_builtin(lisp_runtime *rt, lisp_scope *scope, char *name, lisp_builtin_func call); 136 | void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope); 137 | lisp_value *lisp_eval_list(lisp_runtime *rt, lisp_scope *scope, lisp_value *list); 138 | lisp_value *lisp_parse(lisp_runtime *rt, char *input); 139 | bool lisp_get_args(lisp_list *list, char *format, ...); 140 | lisp_value *lisp_quote(lisp_runtime *rt, lisp_value *value); 141 | // List functions 142 | int lisp_list_length(lisp_list *list); 143 | bool lisp_nil_p(lisp_value *l); 144 | 145 | extern lisp_type *type_type; 146 | extern lisp_type *type_scope; 147 | extern lisp_type *type_list; 148 | extern lisp_type *type_symbol; 149 | extern lisp_type *type_error; 150 | extern lisp_type *type_integer; 151 | extern lisp_type *type_string; 152 | extern lisp_type *type_builtin; 153 | extern lisp_type *type_lambda; 154 | 155 | #endif//SMB_LIBSTEPHEN_LISP_H 156 | -------------------------------------------------------------------------------- /inc/libstephen/list.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/list.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 3 August 2014 8 | 9 | @brief List and iterator interface. 10 | 11 | This header contains the declarations for the list interface and the iterator 12 | interface. Both of these structs allow code to use linked lists or array 13 | lists without consideration for which list implementation they're using. The 14 | iterator is also the only way to efficiently iterate through the linked list. 15 | 16 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 17 | Revised BSD License. See the LICENSE.txt file for details. 18 | 19 | *******************************************************************************/ 20 | 21 | #ifndef LIBSTEPHEN_LIST_H 22 | #define LIBSTEPHEN_LIST_H 23 | 24 | #include 25 | 26 | #include "base.h" /* DATA */ 27 | 28 | /** 29 | @brief A generic list data structure. 30 | 31 | Can represent an array list or a linked list. Uses function pointers to hide 32 | the implementation. Has heavyweight memory requirements (many pointers). 33 | Function calls must be made like this: 34 | 35 | list->functionName(list, ) 36 | */ 37 | typedef struct smb_list 38 | { 39 | /** 40 | @brief A pointer to implementation-specific data. 41 | 42 | This data is used by the rest of the functions in the struct to perform all 43 | required actions. 44 | */ 45 | void *data; 46 | 47 | /** 48 | @see ll_append @see al_append 49 | */ 50 | void (*append)(struct smb_list *l, DATA newData); 51 | 52 | /** 53 | @see ll_prepend @see al_prepend 54 | */ 55 | void (*prepend)(struct smb_list *l, DATA newData); 56 | 57 | /** 58 | @see ll_get @see al_get 59 | */ 60 | DATA (*get)(const struct smb_list *l, int index, smb_status *status); 61 | 62 | /** 63 | @see ll_set @see al_set 64 | */ 65 | void (*set)(struct smb_list *l, int index, DATA newData, smb_status *status); 66 | 67 | /** 68 | @see ll_remove @see al_remove 69 | */ 70 | void (*remove)(struct smb_list *l, int index, smb_status *status); 71 | 72 | /** 73 | @see ll_insert @see al_insert 74 | */ 75 | void (*insert)(struct smb_list *l, int index, DATA newData); 76 | 77 | /** 78 | @see ll_delete @see al_delete 79 | */ 80 | void (*delete)(struct smb_list *l); 81 | 82 | /** 83 | @see ll_length @see al_length 84 | */ 85 | int (*length)(const struct smb_list *l); 86 | 87 | /** 88 | @see ll_push_back @see al_push_back 89 | */ 90 | void (*push_back)(struct smb_list *l, DATA newData); 91 | 92 | /** 93 | @see ll_pop_back @see al_pop_back 94 | */ 95 | DATA (*pop_back)(struct smb_list *l, smb_status *status); 96 | 97 | /** 98 | @see ll_peek_back @see al_peek_back 99 | */ 100 | DATA (*peek_back)(struct smb_list *l, smb_status *status); 101 | 102 | /** 103 | @see ll_push_front @see al_push_front 104 | */ 105 | void (*push_front)(struct smb_list *l, DATA newData); 106 | 107 | /** 108 | @see ll_pop_front @see al_pop_front 109 | */ 110 | DATA (*pop_front)(struct smb_list *l, smb_status *status); 111 | 112 | /** 113 | @see ll_pop_front @see al_pop_front 114 | */ 115 | DATA (*peek_front)(struct smb_list *l, smb_status *status); 116 | 117 | /** 118 | @brief Return the index of an item, or -1 if the list doesn't contain it. 119 | 120 | Performs a linear search, O(n) in the number of elements of the list. 121 | 122 | @param l A pointer to the list. 123 | @param d The data to search for. 124 | @param comp The comparator to use. If NULL, compares the bits of d. 125 | @return The index of the first occurrence of d, else -1. 126 | @exception None. 127 | */ 128 | int (*index_of)(const struct smb_list *l, DATA d, DATA_COMPARE comp); 129 | 130 | } smb_list; 131 | 132 | /** 133 | @brief A generic iterator type. 134 | 135 | This is a generic iterator type. It should be implemented for any data 136 | structure that has some semblance of sequential access. It can also be used 137 | to implement a (lazy) generator. It only provides one directional, read-only 138 | access, which makes it apply to a more general set of data structures and 139 | generators. 140 | 141 | The semantics of the iterator are as follows: An iterator points *between* 142 | any two elements in the sequential list. Its current index is the index of 143 | the element it will return next. 144 | */ 145 | typedef struct smb_iter { 146 | 147 | /** 148 | @brief The data structure this iterator refers to. 149 | 150 | This field is implementation-specific to any iterator. Behavior is 151 | undefined when it is modified. It is intended (but not required) to be 152 | used by the iterator to store a reference to the data structure. 153 | */ 154 | const void *ds; 155 | 156 | /** 157 | @brief The state of the iterator. 158 | 159 | This field is implementation-specific to any iterator. Behavior is 160 | undefined when it is modified. It is intended (but not required) to store 161 | a state variable of the iterator. 162 | */ 163 | DATA state; 164 | 165 | /** 166 | @brief The zero-based index of the iterator. 167 | 168 | This field should be automatically updated by the iterator implementation 169 | as it operates, requiring no programmer intervention. It should not be 170 | modified, but may be accessed. It stores the index of the element that 171 | will be returned next. 172 | */ 173 | int index; 174 | 175 | /** 176 | @brief Returns the next element of the iteration. 177 | @param iter The iterator being used. 178 | @return The next element of the iteration. 179 | */ 180 | DATA (*next)(struct smb_iter *iter, smb_status *status); 181 | 182 | /** 183 | @brief Returns whether the iteration has a next element. 184 | @param iter The iterator being used. 185 | @return Whether the iteration has a next element. 186 | */ 187 | bool (*has_next)(struct smb_iter *iter); 188 | 189 | /** 190 | @brief Frees any resources held by the iterator (but not the iterator). 191 | @param iter The iterator being used. 192 | @param free_src Whether to free the data structure used as the source. 193 | */ 194 | void (*destroy)(struct smb_iter *iter); 195 | 196 | /** 197 | @brief Frees any resources held by the iterator, and the iterator. 198 | @param iter The iterator being used. 199 | @param free_src Whether to free the data structure used as the source. 200 | */ 201 | void (*delete)(struct smb_iter *iter); 202 | 203 | } smb_iter; 204 | 205 | /** 206 | @brief Prints anything with an iterator. 207 | @param it The iterator. Will be invalidated after this use. 208 | @param f The file to print to. 209 | @param printer The printer for handling DATA objects. 210 | */ 211 | void iter_print(smb_iter it, FILE *f, DATA_PRINTER printer); 212 | 213 | #endif // LIBSTEPHEN_LIST_H 214 | -------------------------------------------------------------------------------- /inc/libstephen/rb.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/cb.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Monday, 8 August 2016 8 | 9 | @brief Ring buffer data structure, for nice dequeues. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef LIBSTEPHEN_RB_H 17 | #define LIBSTEPHEN_RB_H 18 | 19 | /** 20 | A ring buffer data structure. This buffer can be inserted into and removed 21 | from at either end in constant time, except for memory allocations which may 22 | have to occur to expand the buffer. However these always double the buffer 23 | size, which means the number of allocations is logarithmic with respect to 24 | the number of insertions. 25 | */ 26 | typedef struct { 27 | 28 | void *data; 29 | int dsize; 30 | 31 | int nalloc; 32 | int start; 33 | int count; 34 | 35 | } smb_rb; 36 | 37 | /** 38 | @brief Initialize a ring buffer. 39 | @param rb Pointer to a ring buffer struct. 40 | @param dsize Size of data type to store in ring buffer. 41 | @param init Initial amount of space to allocate. 42 | */ 43 | void rb_init(smb_rb *rb, int dsize, int init); 44 | /** 45 | @brief Free all resources held by the ring buffer. 46 | @param rb Pointer to the ring buffer struct. 47 | */ 48 | void rb_destroy(smb_rb *rb); 49 | /** 50 | @brief Add an item to the front of the ring buffer. May trigger expansion. 51 | @param src Area of memory to read from. 52 | */ 53 | void rb_push_front(smb_rb *rb, void *src); 54 | /** 55 | @brief Remove an item from the front of the ring buffer. 56 | @param dst Area of memory to write resulting data to. 57 | 58 | Note that behavior is unbefined if you decide to pop from an empty buffer. 59 | */ 60 | void rb_pop_front(smb_rb *rb, void *dst); 61 | /** 62 | @brief Add an item to the end of the ring buffer. May trigger expansion. 63 | @param src Area of memory to read from. 64 | */ 65 | void rb_push_back(smb_rb *rb, void *src); 66 | /** 67 | @brief Remove an item from the end of the ring buffer. 68 | @param dst Area of memory to write resulting data to. 69 | 70 | Note that behavior is undefined if you decide to pop from an empty buffer. 71 | */ 72 | void rb_pop_back(smb_rb *rb, void *dst); 73 | 74 | /** 75 | @brief Expand a ring buffer (by doubling its size). 76 | @param rb Pointer to ring buffer. 77 | 78 | Note that this is mostly an internal function, and is exposed in the header 79 | for testing purposes. No guarantee is made that its interface will stay the 80 | same, or that it will continue to exist. 81 | */ 82 | void rb_grow(smb_rb *rb); 83 | 84 | #endif //LIBSTEPHEN_RB_H 85 | -------------------------------------------------------------------------------- /inc/libstephen/re.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file regex.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Thursday, 21 January 2016 8 | 9 | @brief Declarations of data structures for regex and vm bytecode. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the Revised 12 | BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_PIKE_REGEX_H 17 | #define SMB_PIKE_REGEX_H 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | // DEFINITIONS 24 | 25 | /// @cond HIDDEN_SYMBOLS 26 | /** 27 | An instruction for the virtual machine. The details of this struct aren't 28 | important for client code, but a declaration is necessary for defining the 29 | Regex type. 30 | */ 31 | typedef struct Instr Instr; 32 | struct Instr; 33 | /// @endcond HIDDEN_SYMBOLS 34 | 35 | /** 36 | This typedef is for convenience. See the documentation for struct Regex. 37 | */ 38 | typedef struct Regex Regex; 39 | /** 40 | Represents a compiled regular expression. This consists of a pointer to the 41 | instructions, and a counter for how many instructions there are. This is not 42 | a large struct, so you shouldn't pass around pointers to it. You might as 43 | well just pass around copies of this struct. 44 | */ 45 | struct Regex { 46 | /** 47 | Number of instructions. 48 | */ 49 | size_t n; 50 | /** 51 | Pointer to instruction buffer. 52 | */ 53 | Instr *i; 54 | }; 55 | 56 | /** 57 | A convenience data structure for getting copies of captured strings. 58 | 59 | The recomp() function returns an array of start and end points for captured 60 | strings, which is technically all you need. Functionally, though, you would 61 | like a slightly more convenient way to access your captures. So, you can use 62 | the recap() to convert the string and capture list to a list of freshly 63 | allocated strings. Be sure to call recapfree() on the capture list when 64 | you're done (or manually clean up). 65 | */ 66 | typedef struct { 67 | /** 68 | The number of captured strings. 69 | */ 70 | size_t n; 71 | /** 72 | An array of length n captured strings. Each one is independently allocated, 73 | so you'll need to free them when you're done. 74 | */ 75 | char **cap; 76 | 77 | } Captures; 78 | 79 | /** 80 | A convenience data structure for getting copies of captured wide strings. 81 | This is just a wide version of Captures. 82 | */ 83 | typedef struct { 84 | /** 85 | The number of captured strings. 86 | */ 87 | size_t n; 88 | /** 89 | An array of length n captured strings. Each one is independently allocated, 90 | so you'll need to free them when you're done. 91 | */ 92 | wchar_t **cap; 93 | 94 | } WCaptures; 95 | 96 | /** 97 | Read in a program from a string. This takes the "assembly like" 98 | representation and turns it into compiled instructions. Every instruction 99 | must be on a single line, and spaces are used as delimiters. Also, labels 100 | must be on their own lines. Here is an example of code in this format: 101 | 102 | save 0 103 | L1: 104 | char a 105 | split L1 L2 106 | L2: 107 | match 108 | 109 | @param str The text representation of the program. 110 | @returns The bytecode of the program. 111 | */ 112 | Regex reread(char *str); 113 | /** 114 | Reads in a program from a file instead of a string. 115 | @param f File to read from. 116 | @returns The regex bytecode. 117 | */ 118 | Regex refread(FILE *f); 119 | /** 120 | Writes a program to the same format as the reread() functions do. 121 | @param r The regex to write. 122 | @param f The file to write to. 123 | */ 124 | void rewrite(Regex r, FILE *f); 125 | /** 126 | Free a Regex object. You must do this when you're done with it. 127 | @param r Regex to free. 128 | */ 129 | void refree(Regex r); 130 | 131 | /** 132 | Compile a regular expression! 133 | @param regex The text form of the regular expression. 134 | @returns The compiled bytecode for the regex. 135 | */ 136 | Regex recomp(const char *regex); 137 | 138 | /** 139 | Compile a wide regular expression! 140 | @param regex The text form of the regular expression. 141 | @returns The compiled bytecode for the regex. 142 | */ 143 | Regex recompw(const wchar_t *regex); 144 | 145 | /** 146 | Execute a regex on a string. 147 | @param r Compiled regular expression bytecode to execute. 148 | @param input Text to use as input. 149 | @param saved Out pointer for captured indices. 150 | @returns Length of match, or -1 if no match. 151 | */ 152 | ssize_t reexec(Regex r, const char *input, size_t **saved); 153 | /** 154 | Execute a regex on a string. 155 | @param r Compiled regular expression bytecode to execute. 156 | @param input Text to use as input. 157 | @param saved Out pointer for captured indices. 158 | @returns Length of match, or -1 if no match. 159 | */ 160 | ssize_t reexecw(Regex r, const wchar_t *input, size_t **saved); 161 | /** 162 | Return the number of saved index slots required by a regex. 163 | @param r The regular expression bytecode. 164 | @returns Number of slots. 165 | */ 166 | size_t renumsaves(Regex r); 167 | /** 168 | Convert a string and a capture list into a list of strings. 169 | 170 | This copies each capture into a newly allocated string, and returns them all 171 | in a newly allocated array of strings. These things need to be freed when 172 | you're done with them. You can either manually free each string and then the 173 | array, or you can use recapfree() to do this for you. 174 | 175 | @param s String to get strings from. 176 | @param l List of captures returned from reexec(). 177 | @param n Number of saves - use renumsaves() if you don't know. 178 | @returns A new Capture object. 179 | */ 180 | Captures recap(const char *s, const size_t *l, size_t n); 181 | /** 182 | Free a capture list from recap() 183 | 184 | Since the array and strings were all newly allocated by recap(), they need to 185 | be cleaned up. This function does the cleanup. It's nothing complicated - you 186 | can do it yourself, but it's convenient to have this to do it for you. Note 187 | that if you want to keep one of the strings from the capture list, you'll 188 | have to set its entry in the array to NULL (so free() does nothing), or else 189 | do manual cleanup. 190 | 191 | @param c Captures to free. 192 | */ 193 | void recapfree(Captures c); 194 | 195 | /** 196 | Convert a string and a capture list into a list of strings. 197 | 198 | This copies each capture into a newly allocated string, and returns them all 199 | in a newly allocated array of strings. These things need to be freed when 200 | you're done with them. You can either manually free each string and then the 201 | array, or you can use recapfree() to do this for you. 202 | 203 | @param s String to get strings from. 204 | @param l List of captures returned from reexec(). 205 | @param n Number of saves - use renumsaves() if you don't know. 206 | @returns A new Capture object. 207 | */ 208 | WCaptures recapw(const wchar_t *s, const size_t *l, size_t n); 209 | /** 210 | Free a capture list from recap() 211 | 212 | Since the array and strings were all newly allocated by recap(), they need to 213 | be cleaned up. This function does the cleanup. It's nothing complicated - you 214 | can do it yourself, but it's convenient to have this to do it for you. Note 215 | that if you want to keep one of the strings from the capture list, you'll 216 | have to set its entry in the array to NULL (so free() does nothing), or else 217 | do manual cleanup. 218 | 219 | @param c Captures to free. 220 | */ 221 | void recapwfree(WCaptures c); 222 | 223 | 224 | /** 225 | Macro for the number of elements of a statically allocated array. 226 | */ 227 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 228 | 229 | #endif // SMB_PIKE_REGEX_H 230 | -------------------------------------------------------------------------------- /inc/libstephen/re_internals.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file regparse.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Friday, 29 January 2016 8 | 9 | @brief Private lex/parse/codegen declarations. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the Revised 12 | BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_REGEX_REGPARSE_H 17 | #define SMB_REGEX_REGPARSE_H 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "re.h" 24 | 25 | enum code { 26 | Char, Match, Jump, Split, Save, Any, Range, NRange 27 | }; 28 | 29 | struct Instr { 30 | enum code code; // opcode 31 | wchar_t c; // character 32 | size_t s; // slot for "saving" a string index 33 | Instr *x, *y; // targets for jump and split 34 | size_t lastidx; // used by Pike VM for fast membership testing 35 | }; 36 | 37 | /** 38 | @brief Types of terminal symbols! 39 | */ 40 | enum TSym { 41 | CharSym, Special, Eof, LParen, RParen, LBracket, RBracket, Plus, Minus, 42 | Star, Question, Caret, Pipe, Dot 43 | }; 44 | typedef enum TSym TSym; 45 | 46 | // Lookup the name of a terminal symbol. 47 | extern char *names[]; 48 | 49 | /** 50 | @brief Types of non-terminal symbols! 51 | */ 52 | enum NTSym { 53 | TERMnt, EXPRnt, REGEXnt, CLASSnt, SUBnt 54 | }; 55 | typedef enum NTSym NTSym; 56 | 57 | // Lookup the name of a non-terminal symbol. 58 | extern char *ntnames[]; 59 | 60 | /** 61 | @brief Tokens are simply a combination of a symbol type and a character. 62 | 63 | This means that you can associate the CharSym with the particular character, 64 | and you can tell the difference between different Special's (like \b, \w, \s, 65 | etc). The character field may be useless for symbols like LParen. A token 66 | goes into a parse tree, and should contain all the information necessary for 67 | code generation from a parse tree. 68 | */ 69 | typedef struct Token Token; 70 | struct Token { 71 | TSym sym; 72 | wchar_t c; 73 | }; 74 | 75 | /** 76 | @brief Data structure that allows for simple swapping of input types. 77 | 78 | This data structure allows functions to be written to not care whether they 79 | are receiving wide character strings or "narrow" (or ascii, aka naive) 80 | strings. Which is useful. 81 | */ 82 | struct Input { 83 | const char *str; 84 | const wchar_t *wstr; 85 | }; 86 | 87 | /** 88 | @brief Read input from an existing index, regardless of string type. 89 | */ 90 | wchar_t InputIdx(struct Input in, size_t idx); 91 | 92 | /** 93 | @brief Tree data structure to store information parsed out of a regex. 94 | */ 95 | typedef struct PTree PTree; 96 | struct PTree { 97 | unsigned short nchildren; // a nonterminal symbol may have no children 98 | unsigned short production; // 0 -> terminal, anything else -> nonterminal 99 | 100 | NTSym nt; 101 | Token tok; 102 | 103 | struct PTree *children[4]; 104 | }; 105 | 106 | #define LEXER_BUFSIZE 4 107 | /** 108 | @brief Data structure containing lexer information. 109 | */ 110 | typedef struct Lexer Lexer; 111 | struct Lexer { 112 | struct Input input; 113 | size_t index; 114 | Token tok, prev; 115 | Token buf[LEXER_BUFSIZE]; 116 | size_t nbuf; 117 | }; 118 | 119 | /* Lexing */ 120 | void escape(Lexer *l); 121 | Token nextsym(Lexer *l); 122 | void unget(Token t, Lexer *l); 123 | Regex codegen(PTree *tree); 124 | 125 | /* Parsing */ 126 | PTree *TERM(Lexer *l); 127 | PTree *EXPR(Lexer *l); 128 | PTree *REGEX(Lexer *l); 129 | PTree *CLASS(Lexer *l); 130 | PTree *SUB(Lexer *l); 131 | PTree *reparse(const char *regex); 132 | PTree *reparsew(const wchar_t *winput); 133 | 134 | /* Utitlites */ 135 | void free_tree(PTree *tree); 136 | char *char_to_string(char c); 137 | 138 | #endif // SMB_REGEX_REGPARSE_H 139 | -------------------------------------------------------------------------------- /inc/libstephen/str.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/str.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Friday, 10 July 2015 8 | 9 | @brief Libstephen string functions. 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_STR_H 17 | #define SMB_STR_H 18 | 19 | #include 20 | #include 21 | 22 | #include "libstephen/ll.h" 23 | 24 | /** 25 | @brief Read a file into a string and return a pointer to it. 26 | @param f The file to read. 27 | 28 | It is the caller's responsibility to free() the returned pointer! 29 | */ 30 | char *read_file(FILE *f); 31 | /** 32 | @brief Read a file into a wide string and return a pointer to it. 33 | @param f The file to read. 34 | 35 | It is the caller's responsibility to free() the returned pointer! 36 | */ 37 | wchar_t *read_filew(FILE *f); 38 | 39 | /** 40 | @brief Read line from file into string, and return pointer to it. 41 | @param file File to read line from. 42 | 43 | It is the caller's responsibility to free() the returned pointer! 44 | */ 45 | char *read_line(FILE *file); 46 | /** 47 | @brief Read line from file into wide string, and return pointer to it. 48 | @param file File to read line from. 49 | 50 | It is the caller's responsibility to free() the returned pointer! 51 | */ 52 | wchar_t *read_linew(FILE *file); 53 | 54 | /** 55 | @brief Return a list of lines from the given string. 56 | @param source The string to split into lines. 57 | @return A linked list containing char* to each line. 58 | 59 | The string is modified! You should make a copy before doing this. Also, it 60 | is the caller's responsibility to free the returned list. 61 | */ 62 | smb_ll *split_lines(char *source); 63 | /** 64 | @brief Return a list of lines from the given wide string. 65 | @param source The string to split into lines. 66 | @return A linked list containing wchar_t* to each line. 67 | 68 | The string is modified! You should make a copy before doing this. Also, it 69 | is the caller's responsibility to free the returned list. 70 | */ 71 | smb_ll *split_linesw(wchar_t *source); 72 | 73 | #endif // SMB_STR_H 74 | -------------------------------------------------------------------------------- /inc/libstephen/util.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file libstephen/util.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Friday, 10 July 2015 8 | 9 | @brief Utility functions (mostly string). 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_UTIL_H 17 | #define SMB_UTIL_H 18 | 19 | /** 20 | @brief Read a line of wide character input from the given file. 21 | 22 | Read a line of input into a dynamically allocated buffer. The buffer is 23 | allocated by this function. It will contain the line, WITHOUT newline or EOF 24 | characters. The size of the buffer will not be the same size as the string. 25 | @param file The stream to read from, typically stdin. 26 | @param[out] status Status variable. 27 | @returns A buffer containing the line. 28 | */ 29 | wchar_t *smb_read_linew(FILE *file, smb_status *status); 30 | /** 31 | @brief Read a line of character input from the given file. 32 | 33 | Read a line of input into a dynamically allocated buffer. The buffer is 34 | allocated by this function. It will contain the line, WITHOUT newline or EOF 35 | characters. The size of the buffer will not be the same size as the string. 36 | @param file The stream to read from, typically stdin. 37 | @param[out] status Status variable. 38 | @returns A buffer containing the line. 39 | */ 40 | char *smb_read_line(FILE *file, smb_status *status); 41 | 42 | #endif // SMB_UTIL_H 43 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'libstephen', 'c', 3 | version : '0.1.0', 4 | ) 5 | 6 | sources = [ 7 | 'src/args.c', 8 | 'src/arraylist.c', 9 | 'src/bitfield.c', 10 | 'src/charbuf.c', 11 | 'src/hashtable.c', 12 | 'src/hta.c', 13 | 'src/iter.c', 14 | 'src/linkedlist.c', 15 | 'src/log.c', 16 | 'src/ringbuf.c', 17 | 'src/smbunit.c', 18 | 'src/string.c', 19 | 'src/util.c', 20 | 'src/lisp/gc.c', 21 | 'src/lisp/lex.c', 22 | 'src/lisp/types.c', 23 | 'src/lisp/util.c', 24 | 'src/regex/codegen.c', 25 | 'src/regex/instr.c', 26 | 'src/regex/lex.c', 27 | 'src/regex/parse.c', 28 | 'src/regex/pike.c', 29 | 'src/regex/util.c', 30 | ] 31 | 32 | inc = include_directories('inc') 33 | 34 | libstephen = library( 35 | 'stephen', sources, include_directories : inc, install: true 36 | ) 37 | libstephen_dep = declare_dependency( 38 | include_directories : inc, 39 | link_with : libstephen 40 | ) 41 | 42 | libedit = dependency('libedit') 43 | 44 | regex = executable('regex', 'util/regex.c', dependencies : libstephen_dep) 45 | lisp = executable( 46 | 'lisp', 'util/lisp.c', 47 | dependencies : [libstephen_dep, libedit] 48 | ) 49 | 50 | test_sources = [ 51 | 'test/argstest.c', 52 | 'test/arraylisttest.c', 53 | 'test/bitfieldtest.c', 54 | 'test/charbuftest.c', 55 | 'test/hashtabletest.c', 56 | 'test/hta.c', 57 | 'test/itertest.c', 58 | 'test/linkedlisttest.c', 59 | 'test/listtest.c', 60 | 'test/logtest.c', 61 | 'test/main.c', 62 | 'test/re_codegen.c', 63 | 'test/re_lex.c', 64 | 'test/re_parse.c', 65 | 'test/re_pike.c', 66 | 'test/ringbuftest.c', 67 | 'test/stringtest.c', 68 | ] 69 | testexe = executable('testexe', test_sources, dependencies: libstephen_dep) 70 | test('unit test', testexe) 71 | 72 | pkg = import('pkgconfig') 73 | pkg.generate(libstephen) 74 | 75 | install_headers( 76 | 'inc/libstephen/ad.h', 77 | 'inc/libstephen/al.h', 78 | 'inc/libstephen/base.h', 79 | 'inc/libstephen/bf.h', 80 | 'inc/libstephen/cb.h', 81 | 'inc/libstephen/hta.h', 82 | 'inc/libstephen/ht.h', 83 | 'inc/libstephen/lisp.h', 84 | 'inc/libstephen/list.h', 85 | 'inc/libstephen/ll.h', 86 | 'inc/libstephen/log.h', 87 | 'inc/libstephen/rb.h', 88 | 'inc/libstephen/re.h', 89 | 'inc/libstephen/re_internals.h', 90 | 'inc/libstephen/str.h', 91 | 'inc/libstephen/ut.h', 92 | 'inc/libstephen/util.h', 93 | 'inc/libstephen/base.h', 94 | subdir : 'libstephen', 95 | ) 96 | -------------------------------------------------------------------------------- /res/file.txt: -------------------------------------------------------------------------------- 1 | This is the first line of my file. 2 | This is the second line of my file. 3 | -------------------------------------------------------------------------------- /src/bitfield.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file bitfield.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Wednesday, 29 January 2014 8 | 9 | @brief Implementation of "libstephen/bf.h". 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "libstephen/base.h" 20 | #include "libstephen/bf.h" 21 | 22 | void bf_init(unsigned char *data, int num_bools) { 23 | int size = SMB_BITFIELD_SIZE(num_bools); 24 | memset(data, 0, size); 25 | } 26 | 27 | unsigned char *bf_create(int num_bools) { 28 | unsigned char *data; 29 | int size = SMB_BITFIELD_SIZE(num_bools); 30 | 31 | data = smb_new(unsigned char, size); 32 | 33 | bf_init(data, num_bools); 34 | return data; 35 | } 36 | 37 | void bf_delete(unsigned char *data, int num_bools) { 38 | (void) num_bools; // unused 39 | smb_free(data); 40 | } 41 | 42 | int bf_check(unsigned char *data, int index) { 43 | int byte_index = (int)(index / BIT_PER_CHAR); 44 | unsigned char bit_mask = 0x01 << (index % BIT_PER_CHAR); 45 | 46 | return data[byte_index] & bit_mask; 47 | } 48 | 49 | void bf_set(unsigned char *data, int index) { 50 | int byte_index = (int)(index / BIT_PER_CHAR); 51 | unsigned char bit_mask = 0x01 << (index % BIT_PER_CHAR); 52 | 53 | data[byte_index] |= bit_mask; 54 | } 55 | 56 | void bf_clear(unsigned char *data, int index) { 57 | int byte_index = (int)(index / BIT_PER_CHAR); 58 | unsigned char bit_mask = ~(0x01 << (index % BIT_PER_CHAR)); 59 | 60 | data[byte_index] &= bit_mask; 61 | } 62 | 63 | void bf_flip(unsigned char *data, int index) { 64 | int byte_index = (int)(index / BIT_PER_CHAR); 65 | unsigned char bit_mask = 0x01 << (index % BIT_PER_CHAR); 66 | 67 | unsigned char value = ~(data[byte_index] & bit_mask) & bit_mask; 68 | data[byte_index] = (data[byte_index] & ~bit_mask) | value; 69 | } 70 | -------------------------------------------------------------------------------- /src/charbuf.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file charbuf.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Saturday, 23 May 2015 8 | 9 | @brief Implementation of "libstephen/cb.h". 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "libstephen/base.h" 21 | #include "libstephen/cb.h" 22 | 23 | /******************************************************************************* 24 | 25 | cbuf Functions 26 | 27 | *******************************************************************************/ 28 | 29 | void cb_init(cbuf *obj, int capacity) 30 | { 31 | // Initialization logic 32 | obj->buf = smb_new(char, capacity); 33 | obj->buf[0] = '\0'; 34 | obj->capacity = capacity; 35 | obj->length = 0; 36 | } 37 | 38 | cbuf *cb_create(int capacity) 39 | { 40 | cbuf *obj = smb_new(cbuf, 1); 41 | cb_init(obj, capacity); 42 | return obj; 43 | } 44 | 45 | void cb_destroy(cbuf *obj) 46 | { 47 | smb_free(obj->buf); 48 | obj->buf = NULL; 49 | } 50 | 51 | void cb_delete(cbuf *obj) { 52 | cb_destroy(obj); 53 | smb_free(obj); 54 | } 55 | 56 | /** 57 | @brief Ensure that the cbuf can fit a certain amount of characters. 58 | @param obj The cbuf to expand (if necessary). 59 | @param minsize The minimum size the cbuf should be able to fit. 60 | 61 | Note that minsize should include the NUL byte as part of the character count. 62 | Therefore, to ensure that the string "four" fits in the buffer, you would 63 | want to run `cb_expand_to_fit(obj, 5)` (assuming the buffer was empty). 64 | */ 65 | static void cb_expand_to_fit(cbuf *obj, int minsize) 66 | { 67 | int newcapacity = obj->capacity; 68 | while (newcapacity < minsize) { 69 | newcapacity *= 2; 70 | } 71 | if (newcapacity != obj->capacity) { 72 | obj->buf = smb_renew(char, obj->buf, newcapacity); 73 | obj->capacity = newcapacity; 74 | } 75 | } 76 | 77 | void cb_concat(cbuf *obj, char *buf) 78 | { 79 | int length = strlen(buf); 80 | cb_expand_to_fit(obj, obj->length + length + 1); 81 | strcpy(obj->buf + obj->length, buf); 82 | obj->length += length; 83 | } 84 | 85 | void cb_append(cbuf *obj, char next) 86 | { 87 | cb_expand_to_fit(obj, obj->length + 2); // include new character + nul 88 | obj->buf[obj->length] = next; 89 | obj->length++; 90 | obj->buf[obj->length] = '\0'; 91 | } 92 | 93 | void cb_trim(cbuf *obj) 94 | { 95 | obj->buf = smb_renew(char, obj->buf, obj->length + 1); 96 | obj->capacity = obj->length + 1; 97 | } 98 | 99 | void cb_clear(cbuf *obj) 100 | { 101 | obj->buf[0] = '\0'; 102 | obj->length = 0; 103 | } 104 | 105 | void cb_vprintf(cbuf *obj, char *format, va_list va) 106 | { 107 | va_list v2; 108 | int length; 109 | va_copy(v2, va); 110 | 111 | // Find the length of the formatted string. 112 | length = vsnprintf(NULL, 0, format, va); 113 | 114 | // Make sure we have enough room for everything. 115 | cb_expand_to_fit(obj, obj->length + length + 1); 116 | 117 | // Put the formatted string into the buffer. 118 | vsnprintf(obj->buf + obj->length, length + 1, format, v2); 119 | va_end(v2); 120 | } 121 | 122 | void cb_printf(cbuf *obj, char *format, ...) 123 | { 124 | va_list va; 125 | va_start(va, format); 126 | cb_vprintf(obj, format, va); 127 | va_end(va); // Have to va_stop() it when you're done using it. 128 | } 129 | 130 | /******************************************************************************* 131 | 132 | wcbuf Functions 133 | 134 | *******************************************************************************/ 135 | 136 | void wcb_init(wcbuf *obj, int capacity) 137 | { 138 | // Initialization logic 139 | obj->buf = smb_new(wchar_t, capacity); 140 | obj->buf[0] = L'\0'; 141 | obj->capacity = capacity; 142 | obj->length = 0; 143 | } 144 | 145 | wcbuf *wcb_create(int capacity) 146 | { 147 | wcbuf *obj = smb_new(wcbuf, 1); 148 | wcb_init(obj, capacity); 149 | return obj; 150 | } 151 | 152 | void wcb_destroy(wcbuf *obj) 153 | { 154 | // Cleanup logic 155 | smb_free(obj->buf); 156 | obj->buf = NULL; 157 | } 158 | 159 | void wcb_delete(wcbuf *obj) 160 | { 161 | wcb_destroy(obj); 162 | smb_free(obj); 163 | } 164 | 165 | /** 166 | @brief Ensure that the wcbuf can fit a certain amount of characters. 167 | @param obj The wcbuf to expand (if necessary). 168 | @param minsize The minimum size the wcbuf should be able to fit. 169 | 170 | Note that minsize should include the NUL character as part of the character 171 | count. Therefore, to ensure that the string L"four" fits in the buffer, you 172 | would want to run `cb_expand_to_fit(obj, 5)` (assuming the buffer was empty). 173 | */ 174 | static void wcb_expand_to_fit(wcbuf *obj, int minsize) 175 | { 176 | int newcapacity = obj->capacity; 177 | while (newcapacity < minsize) { 178 | newcapacity *= 2; 179 | } 180 | if (newcapacity != obj->capacity) { 181 | obj->buf = smb_renew(wchar_t, obj->buf, newcapacity); 182 | obj->capacity = newcapacity; 183 | } 184 | } 185 | 186 | void wcb_concat(wcbuf *obj, wchar_t *str) 187 | { 188 | int length = wcslen(str); 189 | wcb_expand_to_fit(obj, obj->length + length + 1); 190 | wcscpy(obj->buf + obj->length, str); 191 | obj->length += length; 192 | } 193 | 194 | void wcb_append(wcbuf *obj, wchar_t next) 195 | { 196 | wcb_expand_to_fit(obj, obj->length + 2); // include new character + nul 197 | obj->buf[obj->length] = next; 198 | obj->length++; 199 | obj->buf[obj->length] = L'\0'; 200 | } 201 | 202 | void wcb_trim(wcbuf *obj) 203 | { 204 | obj->buf = smb_renew(wchar_t, obj->buf, obj->length + 1); 205 | obj->capacity = obj->length + 1; 206 | } 207 | 208 | void wcb_clear(wcbuf *obj) 209 | { 210 | obj->buf[0] = L'\0'; 211 | obj->length = 0; 212 | } 213 | 214 | void wcb_vprintf(wcbuf *obj, wchar_t *format, va_list v1) 215 | { 216 | va_list v2; 217 | char *mbformat, *mbout; 218 | size_t mbformat_len, mbout_len, wcout_len; 219 | 220 | // First, convert the wide format string to a multibyte one. 221 | mbformat_len = wcstombs(NULL, format, 0); 222 | mbformat = smb_new(char, mbformat_len + 1); 223 | wcstombs(mbformat, format, mbformat_len+1); 224 | 225 | // Then, use vsnprintf first to find out how many bytes of output to allocate. 226 | va_copy(v2, v1); 227 | mbout_len = vsnprintf(NULL, 0, mbformat, v1); 228 | 229 | // Now, actually allocate the memory and do the multibyte print. 230 | mbout = smb_new(char, mbout_len + 1); 231 | vsnprintf(mbout, mbout_len + 1, mbformat, v2); 232 | va_end(v2); 233 | 234 | // And now, get the number of wide characters this is. 235 | wcout_len = mbstowcs(NULL, mbout, 0); 236 | 237 | // Make sure we have room for everything. 238 | wcb_expand_to_fit(obj, obj->length + wcout_len + 1); 239 | 240 | // And, put the output in the buffer. 241 | mbstowcs(obj->buf + obj->length, mbout, wcout_len + 1); 242 | obj->length += wcout_len; 243 | 244 | // Free up allocated memory, and we're good to go. 245 | smb_free(mbformat); 246 | smb_free(mbout); 247 | } 248 | 249 | void wcb_printf(wcbuf *obj, wchar_t *format, ...) 250 | { 251 | va_list va; 252 | va_start(va, format); 253 | wcb_vprintf(obj, format, va); 254 | va_end(va); 255 | } 256 | -------------------------------------------------------------------------------- /src/iter.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file iter.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Monday, 4 August 2014 8 | 9 | @brief Functions that act on iterators, "libstephen/list.h". 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "libstephen/base.h" 20 | #include "libstephen/list.h" 21 | 22 | void iter_print(smb_iter it, FILE *f, DATA_PRINTER printer) 23 | { 24 | DATA d; 25 | smb_status status; 26 | fprintf(f, "smb_iter {\n"); 27 | while (it.has_next(&it)) { 28 | d = it.next(&it, &status); 29 | // used has_next 30 | assert(status == SMB_SUCCESS); 31 | printer(f, d); 32 | fprintf(f, ",\n"); 33 | } 34 | it.destroy(&it); 35 | fprintf(f, "}\n"); 36 | } 37 | -------------------------------------------------------------------------------- /src/lisp/gc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "libstephen/lisp.h" 4 | 5 | void lisp_init(lisp_runtime *rt) 6 | { 7 | rt->nil = type_list->new(); 8 | rt->nil->mark = 0; 9 | rt->nil->type = type_list; 10 | rt->nil->next = NULL; 11 | rt->head = rt->nil; 12 | rt->tail = rt->nil; 13 | rb_init(&rt->rb, sizeof(lisp_value*), 16); 14 | } 15 | 16 | void lisp_destroy(lisp_runtime *rt) 17 | { 18 | lisp_sweep(rt); 19 | rb_destroy(&rt->rb); 20 | lisp_free(rt->nil); 21 | } 22 | 23 | void lisp_mark(lisp_runtime *rt, lisp_value *v) 24 | { 25 | smb_status status = SMB_SUCCESS; 26 | rb_push_back(&rt->rb, &v); 27 | 28 | while (rt->rb.count > 0) { 29 | rb_pop_front(&rt->rb, &v); 30 | v->mark = GC_MARKED; 31 | smb_iter it = v->type->expand(v); 32 | while (it.has_next(&it)) { 33 | v = it.next(&it, &status).data_ptr; 34 | if (v->mark == GC_NOMARK) { 35 | v->mark = GC_QUEUED; 36 | rb_push_back(&rt->rb, &v); 37 | } 38 | } 39 | it.destroy(&it); 40 | } 41 | } 42 | 43 | void lisp_sweep(lisp_runtime *rt) 44 | { 45 | lisp_value *curr = rt->head; 46 | 47 | while (curr->next) { 48 | if (curr->next->mark != GC_MARKED) { 49 | lisp_value *tmp = curr->next->next; 50 | lisp_free(curr->next); 51 | curr->next = tmp; 52 | } else { 53 | curr->mark = GC_NOMARK; 54 | curr = curr->next; 55 | } 56 | } 57 | 58 | curr->mark = GC_NOMARK; 59 | rt->tail = curr; 60 | } 61 | -------------------------------------------------------------------------------- /src/lisp/lex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "libstephen/lisp.h" 5 | #include "libstephen/cb.h" 6 | 7 | typedef struct { 8 | lisp_value *result; 9 | int index; 10 | } result; 11 | 12 | result lisp_parse_value(lisp_runtime *rt, char *input, int index); 13 | 14 | result lisp_parse_integer(lisp_runtime *rt, char *input, int index) 15 | { 16 | //printf("lisp_parse_integer(%s, %d)\n", input, index); 17 | int n; 18 | lisp_integer *v = (lisp_integer*)lisp_new(rt, type_integer); 19 | sscanf(input + index, "%d%n", &v->x, &n); 20 | return (result){(lisp_value*)v, index + n}; 21 | } 22 | 23 | char lisp_escape(char escape) 24 | { 25 | switch (escape) { 26 | case 'a': 27 | return '\a'; 28 | case 'b': 29 | return '\b'; 30 | case 'f': 31 | return '\f'; 32 | case 'n': 33 | return '\n'; 34 | case 'r': 35 | return '\b'; 36 | case 't': 37 | return '\t'; 38 | case 'v': 39 | return '\v'; 40 | default: 41 | return escape; 42 | } 43 | } 44 | 45 | result lisp_parse_string(lisp_runtime *rt, char *input, int index) 46 | { 47 | int i = index + 1; 48 | cbuf cb; 49 | cb_init(&cb, 16); 50 | while (input[i] && input[i] != '"') { 51 | if (input[i] == '\\') { 52 | cb_append(&cb, lisp_escape(input[++i])); 53 | } else { 54 | cb_append(&cb, input[i]); 55 | } 56 | i++; 57 | } 58 | cb_trim(&cb); 59 | lisp_string *str = (lisp_string*)lisp_new(rt, type_string); 60 | str->s = cb.buf; 61 | return (result){(lisp_value*)str, ++i}; 62 | } 63 | 64 | result lisp_parse_list_or_sexp(lisp_runtime *rt, char *input, int index) 65 | { 66 | while (isspace(input[index])) {index++;} 67 | if (input[index] == ')') { 68 | return (result){(lisp_value*)lisp_nil_new(rt), index + 1}; 69 | } 70 | 71 | result r = lisp_parse_value(rt, input, index); 72 | index = r.index; 73 | lisp_list *rv = (lisp_list*)lisp_new(rt, type_list); 74 | rv->left = r.result; 75 | lisp_list *l = rv; 76 | 77 | while (true) { 78 | while (isspace(input[index])) { 79 | index++; 80 | } 81 | 82 | if (input[index] == '.') { 83 | index++; 84 | result r = lisp_parse_value(rt, input, index); 85 | index = r.index; 86 | l->right = r.result; 87 | return (result){(lisp_value*)rv, index}; 88 | } else if (input[index] == ')') { 89 | index++; 90 | l->right = lisp_nil_new(rt); 91 | return (result){(lisp_value*)rv, index}; 92 | } else { 93 | result r = lisp_parse_value(rt, input, index); 94 | l->right = lisp_new(rt, type_list); 95 | l = (lisp_list*)l->right; 96 | l->left = r.result; 97 | index = r.index; 98 | } 99 | } 100 | } 101 | 102 | result lisp_parse_symbol(lisp_runtime *rt, char *input, int index) 103 | { 104 | int n = 0; 105 | while (input[index + n] && !isspace(input[index + n]) && 106 | input[index + n] != ')' && input[index + n] != '.' && 107 | input[index + n] != '\'') { 108 | n++; 109 | } 110 | lisp_symbol *s = (lisp_symbol*)lisp_new(rt, type_symbol); 111 | s->sym = malloc(n + 1); 112 | strncpy(s->sym, input + index, n); 113 | s->sym[n] = '\0'; 114 | return (result){(lisp_value*)s, index + n}; 115 | } 116 | 117 | result lisp_parse_quote(lisp_runtime *rt, char *input, int index) 118 | { 119 | result r = lisp_parse_value(rt, input, index + 1); 120 | r.result = lisp_quote(rt, r.result); 121 | return r; 122 | } 123 | 124 | result lisp_parse_value(lisp_runtime *rt, char *input, int index) 125 | { 126 | while (isspace(input[index])) { 127 | index++; 128 | } 129 | 130 | if (input[index] == '"') { 131 | return lisp_parse_string(rt, input, index); 132 | } 133 | if (input[index] == '\0') { 134 | return (result){NULL, index}; 135 | } 136 | if (input[index] == ')') { 137 | return (result){lisp_nil_new(rt), index + 1}; 138 | } 139 | if (input[index] == '(') { 140 | return lisp_parse_list_or_sexp(rt, input, index + 1); 141 | } 142 | if (input[index] == '\'') { 143 | return lisp_parse_quote(rt, input, index); 144 | } 145 | if (isdigit(input[index])) { 146 | return lisp_parse_integer(rt, input, index); 147 | } 148 | return lisp_parse_symbol(rt, input, index); 149 | } 150 | 151 | lisp_value *lisp_parse(lisp_runtime *rt, char *input) 152 | { 153 | return lisp_parse_value(rt, input, 0).result; 154 | } 155 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file log.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 24 May 2015 8 | 9 | @brief Logging facilities implementation for libstephen. 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "libstephen/cb.h" 20 | #include "libstephen/log.h" 21 | 22 | /* 23 | This is the "permanent" default logger. Even when you set your own default 24 | logger, this one still hangs around, waiting for you to call 25 | sl_set_default_logger(NULL). It's declared here, but the default logger 26 | should go to standard out. Unfortunately, you can't statically declare an 27 | instance of a struct that uses the variable stdout, so this is declared 28 | without any handlers, and the first time the default logger is referenced, the 29 | default log handler is added. 30 | */ 31 | static smb_logger default_logger = { 32 | .format = SMB_DEFAULT_LOGFORMAT, 33 | .num = 0 34 | }; 35 | 36 | /* 37 | The only reason this pointer exists is so that reference_logger() knows 38 | whether or not the default logger has been set up yet. This variable remains 39 | NULL until the first time the default logger is used, at which point the 40 | default handler is added to it, and then pdefault_logger is set to 41 | &default_logger. 42 | */ 43 | static smb_logger *pdefault_logger = NULL; 44 | 45 | static char *level_names[] = {"NOTSET", "DEBUG", "INFO", 46 | "WARNING", "ERROR", "CRITICAL"}; 47 | 48 | /* 49 | This function is used by most sl_ functions to initialize their `obj` pointer. 50 | Essentially, if obj=NULL, it replaces obj with a pointer to the default 51 | logger. If the default logger hasn't been set up, it sets it up. 52 | */ 53 | static void reference_logger(smb_logger **obj) 54 | { 55 | smb_status status = SMB_SUCCESS; 56 | if (*obj == NULL) { 57 | if (pdefault_logger == NULL) { 58 | sl_add_handler(&default_logger, 59 | (smb_loghandler){.level = SMB_DEFAULT_LOGLEVEL, 60 | .dst = SMB_DEFAULT_LOGDEST}, 61 | &status); 62 | pdefault_logger = &default_logger; 63 | } 64 | *obj = pdefault_logger; 65 | } 66 | } 67 | 68 | void sl_init(smb_logger *obj) 69 | { 70 | obj->format = SMB_DEFAULT_LOGFORMAT; 71 | obj->num = 0; 72 | } 73 | 74 | smb_logger *sl_create(void) 75 | { 76 | smb_logger *obj = smb_new(smb_logger, 1); 77 | sl_init(obj); 78 | return obj; 79 | } 80 | 81 | void sl_destroy(smb_logger *obj) { 82 | (void)obj; // unused 83 | // nothing to delete 84 | } 85 | 86 | void sl_delete(smb_logger *obj) { 87 | sl_destroy(obj); 88 | smb_free(obj); 89 | } 90 | 91 | void sl_set_level(smb_logger *obj, int level) 92 | { 93 | int i; 94 | reference_logger(&obj); 95 | for (i = 0; i < obj->num; i++) { 96 | obj->handlers[i].level = level; 97 | } 98 | } 99 | 100 | void sl_add_handler(smb_logger *obj, smb_loghandler h, smb_status *status) 101 | { 102 | reference_logger(&obj); 103 | if (obj->num < SMB_MAX_LOGHANDLERS) { 104 | obj->handlers[obj->num++] = h; 105 | } else { 106 | *status = SMB_INDEX_ERROR; 107 | } 108 | } 109 | 110 | void sl_clear_handlers(smb_logger *obj) 111 | { 112 | reference_logger(&obj); 113 | obj->num = 0; 114 | } 115 | 116 | void sl_set_default_logger(smb_logger *obj) 117 | { 118 | if (obj == NULL) 119 | obj = &default_logger; 120 | pdefault_logger = obj; 121 | } 122 | 123 | /* 124 | Returns a string representing a log level. Very much not thread safe (but in 125 | reality, I don't think anything about this logger library is thread safe, so 126 | whatever ¯\_(ツ)_/¯ ). 127 | */ 128 | static char *sl_level_string(int level) { 129 | static char buf[20]; // plenty of space, to be safe. 130 | if (level % 10 == 0 && level >= LEVEL_NOTSET && level <= LEVEL_CRITICAL) { 131 | return level_names[level / 10]; 132 | } else { 133 | snprintf(buf, 20, "%d", level); 134 | return buf; 135 | } 136 | } 137 | 138 | bool sl_will_log(smb_logger *obj, int level) 139 | { 140 | for (int i = 0; i < obj->num; i++) { 141 | if (obj->handlers[i].level <= level) { 142 | return true; 143 | } 144 | } 145 | return false; 146 | } 147 | 148 | void sl_log(smb_logger *obj, char *file, int line, const char *function, int level, ...) { 149 | cbuf file_line_buf, message_buf; 150 | char *level_string, *format; 151 | va_list va; 152 | int i; 153 | 154 | reference_logger(&obj); 155 | 156 | if (!sl_will_log(obj, level)) { 157 | return; // early termination to prevent formatting if we can avoid it 158 | } 159 | 160 | cb_init(&file_line_buf, 256); 161 | cb_printf(&file_line_buf, "%s:%d", file, line); 162 | 163 | va_start(va, level); 164 | format = va_arg(va, char*); 165 | cb_init(&message_buf, 1024); 166 | cb_vprintf(&message_buf, format, va); 167 | va_end(va); 168 | 169 | level_string = sl_level_string(level); 170 | for (i = 0; i < obj->num; i++) { 171 | if (obj->handlers[i].level <= level) { 172 | fprintf(obj->handlers[i].dst, obj->format, file_line_buf.buf, function, 173 | level_string, message_buf.buf); 174 | } 175 | } 176 | cb_destroy(&file_line_buf); 177 | cb_destroy(&message_buf); 178 | } 179 | -------------------------------------------------------------------------------- /src/regex/lex.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file lex.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Friday, 29 January 2016 8 | 9 | @brief All lexer-related functions. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the Revised 12 | BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | 18 | #include "libstephen/re_internals.h" 19 | 20 | void escape(Lexer *l) 21 | { 22 | switch (InputIdx(l->input, l->index)) { 23 | case L'(': 24 | l->tok = (Token){CharSym, L'('}; 25 | break; 26 | case L')': 27 | l->tok = (Token){CharSym, L')'}; 28 | break; 29 | case L'[': 30 | l->tok = (Token){CharSym, L'['}; 31 | break; 32 | case L']': 33 | l->tok = (Token){CharSym, L']'}; 34 | break; 35 | case L'+': 36 | l->tok = (Token){CharSym, L'+'}; 37 | break; 38 | case L'-': 39 | l->tok = (Token){CharSym, L'-'}; 40 | break; 41 | case L'*': 42 | l->tok = (Token){CharSym, L'*'}; 43 | break; 44 | case L'?': 45 | l->tok = (Token){CharSym, L'?'}; 46 | break; 47 | case L'^': 48 | l->tok = (Token){CharSym, L'^'}; 49 | break; 50 | case L'n': 51 | l->tok = (Token){CharSym, L'\n'}; 52 | break; 53 | case L'.': 54 | l->tok = (Token){CharSym, L'.'}; 55 | break; 56 | case L'|': 57 | l->tok = (Token){CharSym, L'|'}; 58 | break; 59 | default: 60 | l->tok = (Token){Special, InputIdx(l->input, l->index)}; 61 | break; 62 | } 63 | } 64 | 65 | Token nextsym(Lexer *l) 66 | { 67 | if (l->tok.sym == Eof) { 68 | return l->tok; // eof never ceases to be eof! 69 | } 70 | 71 | // Handle buffered symbols first. 72 | l->prev = l->tok; 73 | if (l->nbuf > 0) { 74 | l->tok = l->buf[0]; 75 | for (size_t i = 0; i < l->nbuf - 1; i++) { 76 | l->buf[i] = l->buf[i+1]; 77 | } 78 | l->nbuf--; 79 | //printf(";; nextsym(): unbuffering {%s, '%s'}\n", names[l->tok.sym], 80 | // char_to_string(l->tok.c)); 81 | return l->tok; 82 | } 83 | 84 | switch (InputIdx(l->input, l->index)) { 85 | case L'(': 86 | l->tok = (Token){LParen, L'('}; 87 | break; 88 | case L')': 89 | l->tok = (Token){RParen, L')'}; 90 | break; 91 | case L'[': 92 | l->tok = (Token){LBracket, L'['}; 93 | break; 94 | case L']': 95 | l->tok = (Token){RBracket, L']'}; 96 | break; 97 | case L'+': 98 | l->tok = (Token){Plus, L'+'}; 99 | break; 100 | case L'-': 101 | l->tok = (Token){Minus, L'-'}; 102 | break; 103 | case L'*': 104 | l->tok = (Token){Star, L'*'}; 105 | break; 106 | case L'?': 107 | l->tok = (Token){Question, L'?'}; 108 | break; 109 | case L'^': 110 | l->tok = (Token){Caret, L'^'}; 111 | break; 112 | case L'|': 113 | l->tok = (Token){Pipe, L'|'}; 114 | break; 115 | case L'.': 116 | l->tok = (Token){Dot, L'.'}; 117 | break; 118 | case L'\\': 119 | l->index++; 120 | escape(l); 121 | break; 122 | case L'\0': 123 | l->tok = (Token){Eof, L'\0'}; 124 | break; 125 | default: 126 | l->tok = (Token){CharSym, InputIdx(l->input, l->index)}; 127 | break; 128 | } 129 | l->index++; 130 | //printf(";; nextsym(): {%s, '%s'}\n", names[l->tok.sym], char_to_string(l->tok.c)); 131 | return l->tok; 132 | } 133 | 134 | void unget(Token t, Lexer *l) 135 | { 136 | if (l->nbuf >= LEXER_BUFSIZE) { 137 | fprintf(stderr, "error: maximum lexer buffer size exceeded, dumbass.\n"); 138 | exit(1); 139 | } 140 | 141 | //printf(";; unget(): buffering {%s, '%s'}\n", names[t.sym], char_to_string(t.c)); 142 | 143 | for (int i = l->nbuf - 1; i >= 0; i--) { 144 | l->buf[i+1] = l->buf[i]; 145 | } 146 | l->buf[0] = l->tok; 147 | l->tok = t; 148 | l->nbuf++; 149 | } 150 | -------------------------------------------------------------------------------- /src/regex/pike.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file pike.c 4 | 5 | @author Stephen Brennan, based on code by Russ Cox: 6 | https://swtch.com/~rsc/regexp/regexp2.html 7 | 8 | @date Created Wednesday, 20 January 2016 9 | 10 | @brief Regex implementation using virtual machines. 11 | 12 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the Revised 13 | BSD License. See LICENSE.txt for details. 14 | 15 | Portions of this code are based on code by Russ Cox, Copyright 16 | 2007-2009. 17 | 18 | *******************************************************************************/ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "libstephen/re.h" 30 | #include "libstephen/re_internals.h" 31 | 32 | // Declarations: 33 | 34 | typedef struct thread thread; 35 | struct thread { 36 | Instr *pc; 37 | size_t *saved; 38 | }; 39 | 40 | typedef struct thread_list thread_list; 41 | struct thread_list { 42 | thread *t; 43 | size_t n; 44 | }; 45 | 46 | // Printing, for diagnostics 47 | 48 | void printthreads(thread_list *tl, Instr *prog, size_t nsave) { 49 | for (size_t i = 0; i < tl->n; i++) { 50 | printf("T%zu@pc=%lu{", i, (intptr_t) (tl->t[i].pc - prog)); 51 | for (size_t j = 0; j < nsave; j++) { 52 | printf("%lu,", tl->t[i].saved[j]); 53 | } 54 | printf("} "); 55 | } 56 | printf("\n"); 57 | } 58 | 59 | // Helper evaluation functions for instructions 60 | 61 | bool range(Instr in, wchar_t test) { 62 | if (test == L'\0') { 63 | return false; 64 | } 65 | bool result = false; 66 | char *block = (char *) in.x; 67 | 68 | // use in.s for number of ranges, in.x as char* for ranges. 69 | for (size_t i = 0; i < in.s; i++) { 70 | if (block[i*2] <= test && test <= block [i*2 + 1]) { 71 | result = true; 72 | break; // short circuit yo! 73 | } 74 | } 75 | 76 | // negate result for negative ranges 77 | if (in.code == Range) { 78 | return result; 79 | } else { 80 | return !result; 81 | } 82 | } 83 | 84 | // Pike VM functions: 85 | 86 | thread_list newthread_list(size_t n) 87 | { 88 | thread_list tl; 89 | tl.t = calloc(n, sizeof(thread)); 90 | tl.n = 0; 91 | return tl; 92 | } 93 | 94 | void addthread(thread_list *threads, Instr *pc, size_t *saved, size_t nsave, 95 | size_t sp) 96 | { 97 | //printf("addthread(): pc=%d, saved={%u, %u}, sp=%u, lastidx=%u\n", pc - extprog, 98 | // saved[0], saved[1], sp, pc->lastidx); 99 | if (pc->lastidx == sp) { 100 | // we've executed this instruction on this string index already 101 | free(saved); 102 | return; 103 | } 104 | pc->lastidx = sp; 105 | 106 | size_t *newsaved; 107 | switch (pc->code) { 108 | case Jump: 109 | addthread(threads, pc->x, saved, nsave, sp); 110 | break; 111 | case Split: 112 | newsaved = calloc(nsave, sizeof(size_t)); 113 | memcpy(newsaved, saved, nsave * sizeof(size_t)); 114 | addthread(threads, pc->x, saved, nsave, sp); 115 | addthread(threads, pc->y, newsaved, nsave, sp); 116 | break; 117 | case Save: 118 | saved[pc->s] = sp; 119 | addthread(threads, pc + 1, saved, nsave, sp); 120 | break; 121 | default: 122 | threads->t[threads->n].pc = pc; 123 | threads->t[threads->n].saved = saved; 124 | threads->n++; 125 | break; 126 | } 127 | } 128 | 129 | /** 130 | @brief "Stash" a list of captures into the "out" pointer. 131 | @param new The new list of captures encountered by the Match instruction. 132 | @param destination The out pointer where the caller wants the captures. 133 | */ 134 | void stash(size_t *new, size_t **destination) 135 | { 136 | if (!destination) { 137 | /* If the user wants to discard the captures, they'll pass NULL. This means 138 | we need to get rid of the capture list, or we'll leak the memory. */ 139 | free(new); 140 | return; 141 | } 142 | if (*destination) { 143 | /* If we have already stored a capture list, we should free that. */ 144 | free(*destination); 145 | } 146 | /* Finally, stash the pointer away. */ 147 | *destination = new; 148 | } 149 | 150 | static ssize_t reexec_internal(Regex r, const struct Input input, size_t **saved) 151 | { 152 | // Can have at most n threads, where n is the length of the program. This is 153 | // because (as it is now) the thread state is simply a program counter. 154 | thread_list curr = newthread_list(r.n); 155 | thread_list next = newthread_list(r.n); 156 | thread_list temp; 157 | size_t nsave = 0; 158 | ssize_t match = -1; 159 | 160 | // Set the out pointer to NULL so that stash() knows whether we've already 161 | // stashed away a capture list. 162 | if (saved) { 163 | *saved = NULL; 164 | } 165 | 166 | // Need to initialize lastidx to something that will never be used. 167 | for (size_t i = 0; i < r.n; i++) { 168 | r.i[i].lastidx = (size_t)-1; 169 | if (r.i[i].code == Save) { 170 | nsave++; 171 | } 172 | } 173 | 174 | // Start with a single thread and add more as we need. Note that addthread() 175 | // will execute instructions that don't consume input (i.e. epsilon closure). 176 | addthread(&curr, r.i, calloc(nsave, sizeof(size_t)), nsave, 0); 177 | 178 | size_t sp; 179 | for (sp = 0; curr.n > 0; sp++) { 180 | 181 | //printf("consider input %c\nthreads: ", input[sp]); 182 | //printthreads(&curr, r.i, nsave); 183 | 184 | // Execute each thread (this will only ever reach instructions that consume 185 | // input, since addthread() stops with those). 186 | for (size_t t = 0; t < curr.n; t++) { 187 | Instr *pc = curr.t[t].pc; 188 | 189 | switch (pc->code) { 190 | case Char: 191 | if (InputIdx(input, sp) != pc->c) { 192 | free(curr.t[t].saved); 193 | break; // fail, don't continue executing this thread 194 | } 195 | // add thread containing the next instruction to the next thread list. 196 | addthread(&next, pc+1, curr.t[t].saved, nsave, sp+1); 197 | break; 198 | case Any: 199 | if (InputIdx(input, sp) == '\0') { 200 | free(curr.t[t].saved); 201 | break; // dot can't match end of string! 202 | } 203 | // add thread containing the next instruction to the next thread list. 204 | addthread(&next, pc+1, curr.t[t].saved, nsave, sp+1); 205 | break; 206 | case Range: 207 | case NRange: 208 | if (!range(*pc, InputIdx(input, sp))) { 209 | free(curr.t[t].saved); 210 | break; 211 | } 212 | addthread(&next, pc+1, curr.t[t].saved, nsave, sp+1); 213 | break; 214 | case Match: 215 | stash(curr.t[t].saved, saved); 216 | match = sp; 217 | goto cont; 218 | default: 219 | assert(false); 220 | break; 221 | } 222 | } 223 | 224 | cont: 225 | // Swap the curr and next lists. 226 | temp = curr; 227 | curr = next; 228 | next = temp; 229 | 230 | // Reset our new next list. 231 | next.n = 0; 232 | } 233 | 234 | free(curr.t); 235 | free(next.t); 236 | return match; 237 | } 238 | 239 | ssize_t reexec(Regex r, const char *input, size_t **saved) 240 | { 241 | struct Input in = {.str=input, .wstr=NULL}; 242 | return reexec_internal(r, in, saved); 243 | } 244 | 245 | ssize_t reexecw(Regex r, const wchar_t *input, size_t **saved) 246 | { 247 | struct Input in = {.str=NULL, .wstr=input}; 248 | return reexec_internal(r, in, saved); 249 | } 250 | 251 | size_t renumsaves(Regex r) 252 | { 253 | size_t ns = 0; 254 | for (size_t i = 0; i < r.n; i++) { 255 | if (r.i[i].code == Save && r.i[i].s > ns) { 256 | ns = r.i[i].s; 257 | } 258 | } 259 | return ns + 1; 260 | } 261 | -------------------------------------------------------------------------------- /src/regex/util.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file util.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 29 May 2016 8 | 9 | @brief Regex handling utilities. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | 18 | #include "libstephen/re.h" 19 | #include "libstephen/re_internals.h" 20 | 21 | Captures recap(const char *s, const size_t *l, size_t n) 22 | { 23 | Captures c; 24 | c.n = n/2; 25 | c.cap = calloc(c.n, sizeof(char *)); 26 | for (size_t i = 0; i < n / 2; i++) { 27 | size_t start = l[i*2]; 28 | size_t end = l[i*2 + 1]; 29 | size_t length = end - start + 1; 30 | c.cap[i] = malloc(end - start + 1); 31 | strncpy(c.cap[i], s + start, length); 32 | c.cap[i][length-1] = '\0'; 33 | } 34 | return c; 35 | } 36 | 37 | void recapfree(Captures c) 38 | { 39 | for (size_t i = 0; i < c.n; i++) { 40 | free(c.cap[i]); 41 | } 42 | free(c.cap); 43 | } 44 | 45 | WCaptures recapw(const wchar_t *s, const size_t *l, size_t n) 46 | { 47 | WCaptures c; 48 | c.n = n/2; 49 | c.cap = calloc(c.n, sizeof(wchar_t *)); 50 | for (size_t i = 0; i < n / 2; i++) { 51 | size_t start = l[i*2]; 52 | size_t end = l[i*2 + 1]; 53 | size_t length = end - start + 1; 54 | c.cap[i] = malloc((end - start + 1) * sizeof(wchar_t)); 55 | wcsncpy(c.cap[i], s + start, length); 56 | c.cap[i][length-1] = '\0'; 57 | } 58 | return c; 59 | } 60 | 61 | void recapwfree(WCaptures c) 62 | { 63 | for (size_t i = 0; i < c.n; i++) { 64 | free(c.cap[i]); 65 | } 66 | free(c.cap); 67 | } 68 | 69 | wchar_t InputIdx(struct Input in, size_t idx) 70 | { 71 | if (in.str) { 72 | return (wchar_t)in.str[idx]; 73 | } else { 74 | return in.wstr[idx]; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ringbuf.c: -------------------------------------------------------------------------------- 1 | #include "libstephen/rb.h" 2 | 3 | #include 4 | #include 5 | 6 | void rb_init(smb_rb *rb, int dsize, int init) 7 | { 8 | rb->dsize = dsize; 9 | rb->nalloc = init; 10 | rb->start = 0; 11 | rb->count = 0; 12 | rb->data = calloc(dsize, init); 13 | } 14 | 15 | void rb_destroy(smb_rb *rb) 16 | { 17 | free(rb->data); 18 | } 19 | 20 | void rb_grow(smb_rb *rb) 21 | { 22 | int oldalloc = rb->nalloc; 23 | rb->nalloc *= 2; 24 | rb->data = realloc(rb->data, rb->nalloc * rb->dsize); 25 | 26 | for (int i = 0; i < rb->count; i++) { 27 | int oldindex = (rb->start + i) % oldalloc; 28 | int newindex = (rb->start + i) % rb->nalloc; 29 | if (oldindex != newindex) { 30 | memcpy(rb->data + newindex * rb->dsize, 31 | rb->data + oldindex * rb->dsize, rb->nalloc); 32 | } 33 | } 34 | } 35 | 36 | void rb_push_front(smb_rb *rb, void *src) 37 | { 38 | if (rb->count >= rb->nalloc) { 39 | rb_grow(rb); 40 | } 41 | 42 | // ensure the new start index is still positive 43 | int newstart = (rb->start + rb->nalloc - 1) % rb->nalloc; 44 | rb->start = newstart; 45 | memcpy(rb->data + rb->start * rb->dsize, src, rb->dsize); 46 | rb->count++; 47 | } 48 | 49 | void rb_pop_front(smb_rb *rb, void *dst) 50 | { 51 | int newstart = (rb->start + 1) % rb->nalloc; 52 | memcpy(dst, rb->data + rb->start * rb->dsize, rb->dsize); 53 | rb->start = newstart; 54 | rb->count--; 55 | } 56 | 57 | void rb_push_back(smb_rb *rb, void *src) 58 | { 59 | if (rb->count >= rb->nalloc) { 60 | rb_grow(rb); 61 | } 62 | 63 | int index = (rb->start + rb->count) % rb->nalloc; 64 | memcpy(rb->data + index * rb->dsize, src, rb->dsize); 65 | rb->count++; 66 | } 67 | 68 | void rb_pop_back(smb_rb *rb, void *dst) 69 | { 70 | int index = (rb->start + rb->count - 1) % rb->nalloc; 71 | memcpy(dst, rb->data + index * rb->dsize, rb->dsize); 72 | rb->count--; 73 | } 74 | -------------------------------------------------------------------------------- /src/smbunit.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file smbunit.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Saturday, 28 September 2013 8 | 9 | @brief A simple, lightweight unit test runner based on function 10 | pointers. 11 | 12 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 13 | Revised BSD License. See the LICENSE.txt file for details. 14 | 15 | *******************************************************************************/ 16 | 17 | #include /* printf */ 18 | #include /* strncpy */ 19 | 20 | #include "libstephen/base.h" 21 | #include "libstephen/ut.h" /* functions we're defining */ 22 | 23 | smb_ut_test *su_create_test(char *description, int (*run)()) 24 | { 25 | smb_ut_test *test = smb_new(smb_ut_test, 1); 26 | strncpy(test->description, description, SMB_UNIT_DESCRIPTION_SIZE - 1); 27 | test->description[SMB_UNIT_DESCRIPTION_SIZE - 1] = 0; 28 | test->run = run; 29 | return test; 30 | } 31 | 32 | smb_ut_group *su_create_test_group(char *description) 33 | { 34 | smb_ut_group *group = smb_new(smb_ut_group, 1); 35 | strncpy(group->description, description, SMB_UNIT_DESCRIPTION_SIZE - 1); 36 | group->description[SMB_UNIT_DESCRIPTION_SIZE - 1] = 0; 37 | 38 | group->num_tests = 0; 39 | return group; 40 | } 41 | 42 | void su_add_test(smb_ut_group *group, smb_ut_test *test) 43 | { 44 | if (group->num_tests < SMB_UNIT_TESTS_PER_GROUP) { 45 | group->tests[group->num_tests++] = test; 46 | } 47 | } 48 | 49 | int su_run_test(smb_ut_test *test, char *file) 50 | { 51 | int result = test->run(); 52 | 53 | if (result) { 54 | printf ("%s:%d: assertion failed in %s\n", file, result, test->description); 55 | return 1; 56 | } 57 | 58 | printf ("TEST \"%s\" passed!\n",test->description); 59 | return 0; 60 | } 61 | 62 | int su_run_group(smb_ut_group *group) 63 | { 64 | int result = 0; 65 | printf ("## GROUP \"%s\" running...\n",group->description); 66 | for (int i = 0; i < group->num_tests; i++) { 67 | result = su_run_test(group->tests[i], group->description); 68 | if (result) { 69 | printf ("## GROUP \"%s\" failed on test: %d\n\n", group->description, i); 70 | exit(result); 71 | } 72 | } 73 | printf ("## GROUP \"%s\" passed!\n\n", group->description); 74 | return 0; 75 | } 76 | 77 | void su_delete_test(smb_ut_test *test) 78 | { 79 | smb_free(test); 80 | } 81 | 82 | void su_delete_group(smb_ut_group *group) 83 | { 84 | for (int i = 0; i < group->num_tests; i++) { 85 | if (group->tests[i]) // don't delete if already deleted 86 | su_delete_test(group->tests[i]); 87 | } 88 | smb_free(group); 89 | } 90 | -------------------------------------------------------------------------------- /src/str.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file str.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 29 June 2014 8 | 9 | @brief Common functions for working with strings. 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #ifndef SMB_CKY_STR_H 17 | #define SMB_CKY_STR_H 18 | 19 | #include 20 | #include "libstephen/ll.h" 21 | 22 | /** 23 | @brief Get the value of a hexadecimal digit. 24 | @param digit The digit 25 | @return The value of the digit in hexadecimal. 26 | */ 27 | int hexit_val(wchar_t digit); 28 | /** 29 | @brief Get an escaped character from the string source. 30 | 31 | This function will advance the source pointer to after the escape sequence. 32 | It can get escapes `abfnrtv\xu`, which includes hexadecimal and unicode 33 | escapes. 34 | @brief source The source pointer 35 | @return The character that was escaped 36 | */ 37 | wchar_t get_escape(const wchar_t **source); 38 | /** 39 | @brief Place the escaped wchar in source into out. 40 | 41 | Source should be a string "\..." containing an escape sequence. It should 42 | include the backslash. This function will read the escape sequence, convert 43 | it to a wchar_t, store that in out, and return the number of characters read. 44 | @param source The string escape sequence to translate. 45 | @param len The length of the string to read. (assumed >= 1) 46 | @param out Where to store the output character. 47 | @return The number of characters read. 48 | @exception Sets *out to WEOF if it would readpast len. 49 | */ 50 | int read_escape(const wchar_t *source, int len, wchar_t *out); 51 | /** 52 | @brief Read a single character from the string, accepting escape sequences. 53 | @param source The string to read from. 54 | @param len Number of characters in the string. (assumed >= 1) 55 | @param out Place to store the resulting character. 56 | @return Number of characters read from source. 57 | @exception Sets *out to WEOF if the read would go past len. 58 | */ 59 | int read_wchar(const wchar_t *source, int len, wchar_t *out); 60 | /** 61 | @brief Return a string representation of a character, escaped if necessary. 62 | 63 | The returned string is statically allocated, but only valid until the next 64 | time fsm_print_char() is called. 65 | @param input The character to filter. 66 | @returns A statically allocated, escaped version of the character. Only 67 | valid until next call to escape_wchar(). 68 | */ 69 | wchar_t *escape_wchar(wchar_t input); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file string.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Friday, 10 July 2015 8 | 9 | @brief Libstephen string utilities! 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "libstephen/ll.h" 20 | #include "libstephen/cb.h" 21 | 22 | char *read_file(FILE *f) 23 | { 24 | cbuf cb; 25 | int c; 26 | cb_init(&cb, 1024); 27 | 28 | while ((c = fgetc(f)) != EOF) { 29 | cb_append(&cb, c); 30 | } 31 | 32 | cb_trim(&cb); 33 | return cb.buf; 34 | } 35 | 36 | wchar_t *read_filew(FILE *f) 37 | { 38 | wcbuf wcb; 39 | wint_t wc; 40 | wcb_init(&wcb, 1024); 41 | 42 | while ((wc = fgetwc(f)) != WEOF) { 43 | wcb_append(&wcb, wc); 44 | } 45 | 46 | wcb_trim(&wcb); 47 | return wcb.buf; 48 | } 49 | 50 | char *read_line(FILE *file) 51 | { 52 | cbuf cb; 53 | int c; 54 | cb_init(&cb, 256); 55 | 56 | while ((c = fgetc(file)) != EOF && c != '\n') { 57 | cb_append(&cb, c); 58 | } 59 | 60 | cb_trim(&cb); 61 | return cb.buf; 62 | } 63 | 64 | wchar_t *read_linew(FILE *file) 65 | { 66 | wcbuf wcb; 67 | wint_t wc; 68 | wcb_init(&wcb, 256); 69 | 70 | while ((wc = fgetwc(file)) != WEOF && wc != L'\n') { 71 | wcb_append(&wcb, wc); 72 | } 73 | 74 | wcb_trim(&wcb); 75 | return wcb.buf; 76 | } 77 | 78 | smb_ll *split_lines(char *source) 79 | { 80 | char *start; 81 | smb_ll *list; 82 | DATA d; 83 | 84 | /* 85 | We go through `source` looking for every newline, replace it with NUL, and 86 | add the beginnig of the line to the list. 87 | */ 88 | start = source; 89 | list = ll_create(); 90 | while (*source != '\0') { 91 | if (*source == '\n') { 92 | // Add line to list. 93 | d.data_ptr = start; 94 | ll_append(list, d); 95 | // Null-terminate the line. 96 | *source = '\0'; 97 | // Next string starts at the next character. 98 | start = source + 1; 99 | } 100 | source++; 101 | } 102 | if (start != source) { 103 | d.data_ptr = start; 104 | ll_append(list, d); 105 | } 106 | return list; 107 | } 108 | 109 | smb_ll *split_linesw(wchar_t *source) 110 | { 111 | wchar_t *start; 112 | smb_ll *list; 113 | DATA d; 114 | 115 | /* 116 | We go through `source` looking for every newline, replace it with NUL, and 117 | add the beginnig of the line to the list. 118 | */ 119 | start = source; 120 | list = ll_create(); 121 | while (*source != L'\0') { 122 | if (*source == L'\n') { 123 | // Add line to list. 124 | d.data_ptr = start; 125 | ll_append(list, d); 126 | // Null-terminate the line. 127 | *source = L'\0'; 128 | // Next string starts at the next character. 129 | start = source + 1; 130 | } 131 | source++; 132 | } 133 | if (start != source) { 134 | d.data_ptr = start; 135 | ll_append(list, d); 136 | } 137 | return list; 138 | } 139 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file util.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 1 September 2013 8 | 9 | @brief Contains definitions for the general purpose items. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include /* gettimeofday */ 17 | #include /* fprintf, fopen */ 18 | #include /* malloc, exit */ 19 | #include /* wchar_t */ 20 | #include /* bool */ 21 | #include /* strcmp */ 22 | 23 | #include "libstephen/base.h" /* SMB_* */ 24 | 25 | 26 | /** 27 | @brief Utility function for macro smb_new(). Wrapper over malloc(). 28 | 29 | Allocate a certain amount of memory. If allocation fails, EXIT with an error 30 | message. 31 | 32 | @param amt The number of bytes to allocate. 33 | @returns The pointer to the allocated memory (guaranteed). 34 | */ 35 | void *smb___new(size_t amt) 36 | { 37 | void *result = malloc(amt); 38 | if (!result) { 39 | fprintf(stderr, "smb_new: allocation error\n"); 40 | exit(1); 41 | } 42 | return result; 43 | } 44 | 45 | /** 46 | @brief Utility function for macro smb_renew(). Wrapper over realloc(). 47 | 48 | Reallocate a certain amount of memory. 49 | 50 | @param ptr The memory to reallocate. 51 | @param newsize The new size of the memory. 52 | @returns The pointer to the new block. 53 | */ 54 | void *smb___renew(void *ptr, size_t newsize) 55 | { 56 | void *result = realloc(ptr, newsize); 57 | if (!result) { 58 | fprintf(stderr, "smb_renew: allocation error\n"); 59 | exit(1); 60 | } 61 | return result; 62 | } 63 | 64 | /** 65 | @brief Utility function for macro smb_free(). Wrapper over free(). 66 | 67 | Free a pointer. 68 | 69 | @param ptr Memory to free. 70 | */ 71 | void smb___free(void *ptr) 72 | { 73 | free(ptr); 74 | } 75 | 76 | wchar_t *smb_read_linew(FILE *file, smb_status *status) 77 | { 78 | (void)status; // unused; 79 | #define SMBRL_BUFSIZE 256 80 | int bufsize = SMBRL_BUFSIZE; 81 | int position = 0; 82 | wchar_t *buffer = smb_new(wchar_t, bufsize); 83 | wint_t wc; 84 | 85 | while (true) { 86 | // Read a character 87 | wc = fgetwc(file); 88 | 89 | // If we hit EOF, replace it with a null character and return. 90 | if (wc == WEOF || wc == L'\n') { 91 | buffer[position++] = L'\0'; 92 | return buffer; 93 | } else { 94 | buffer[position++] = wc; 95 | } 96 | 97 | // If we have exceeded the buffer, reallocate. 98 | if (position >= bufsize) { 99 | buffer = smb_renew(wchar_t, buffer, bufsize+SMBRL_BUFSIZE); 100 | bufsize += SMBRL_BUFSIZE; 101 | } 102 | } 103 | } 104 | 105 | 106 | char *smb_read_line(FILE *file, smb_status *status) 107 | { 108 | (void) status; // unused 109 | int bufsize = SMBRL_BUFSIZE; 110 | int position = 0; 111 | char *buffer = smb_new(char, bufsize); 112 | int c; 113 | 114 | while (true) { 115 | // Read a character 116 | c = fgetc(file); 117 | 118 | // If we hit EOF, replace it with a null character and return. 119 | if (c == EOF || c == '\n') { 120 | buffer[position++] = '\0'; 121 | return buffer; 122 | } else { 123 | buffer[position++] = c; 124 | } 125 | 126 | // If we have exceeded the buffer, reallocate. 127 | if (position >= bufsize) { 128 | buffer = smb_renew(char, buffer, bufsize+SMBRL_BUFSIZE); 129 | bufsize += SMBRL_BUFSIZE; 130 | } 131 | } 132 | } 133 | 134 | /** 135 | @brief Print a DATA, treating it as a `char *`. 136 | @param f File to print to. 137 | @param d DATA to print. 138 | */ 139 | void data_printer_string(FILE *f, DATA d) 140 | { 141 | fprintf(f, "\"%s\"", (char *)d.data_ptr); 142 | } 143 | 144 | /** 145 | @brief Print a DATA, treating it as an int. 146 | @param f File to print to. 147 | @param d DATA to print. 148 | */ 149 | void data_printer_int(FILE *f, DATA d) 150 | { 151 | fprintf(f, "%lld", d.data_llint); 152 | } 153 | 154 | /** 155 | @brief Print a DATA, treating it as a float. 156 | @param f File to print to. 157 | @param d DATA to print. 158 | */ 159 | void data_printer_float(FILE *f, DATA d) 160 | { 161 | fprintf(f, "%f", d.data_dbl); 162 | } 163 | 164 | /** 165 | @brief Print a DATA, treating it as a pointer. 166 | @param f File to print to. 167 | @param d DATA to print. 168 | */ 169 | void data_printer_pointer(FILE *f, DATA d) 170 | { 171 | fprintf(f, "%p", d.data_ptr); 172 | } 173 | 174 | /** 175 | @brief Test whether two null terminated strings are equal. 176 | @param d1 First data. 177 | @param d2 Second data. 178 | @return An integer indicating whether the strings are equal, less than, or 179 | greater than. 180 | */ 181 | int data_compare_string(DATA d1, DATA d2) 182 | { 183 | char *s1, *s2; 184 | s1 = (char *)d1.data_ptr; 185 | s2 = (char *)d2.data_ptr; 186 | return strcmp(s1, s2); 187 | } 188 | 189 | /** 190 | @brief Test whether two ints are equal. 191 | @param d1 First int. 192 | @param d2 Second int. 193 | @return An integer indicating whether the ints are equal, less than, or 194 | greater than. 195 | */ 196 | int data_compare_int(DATA d1, DATA d2) 197 | { 198 | // Since the difference between two long long ints could be more than an int, 199 | // we need to store the difference and conditionally return. 200 | long long int diff = d1.data_llint - d2.data_llint; 201 | if (diff < 0) { 202 | return -1; 203 | } else if (diff > 0) { 204 | return 1; 205 | } else { 206 | return 0; 207 | } 208 | } 209 | 210 | /** 211 | @brief Test whether two doubles are equal. 212 | 213 | This function compares two doubles stored in DATA. However, it's NOT a smart 214 | comparison. It really just tests whether the binary representations 215 | themselves are equal. If you want a smart method for comparing floating 216 | point numbers, look elsewhere! 217 | 218 | @param d1 First double. 219 | @param d2 Second double. 220 | @return An integer indicating whether the doubles are equal, less than, or 221 | greater than. 222 | */ 223 | int data_compare_float(DATA d1, DATA d2) 224 | { 225 | double diff = d1.data_llint - d2.data_llint; 226 | if (diff < 0) { 227 | return -1; 228 | } else if (diff > 0) { 229 | return 1; 230 | } else { 231 | return 0; 232 | } 233 | } 234 | 235 | /** 236 | @brief Test whether two pointers are equal. Does not order them. 237 | @param d1 First pointer. 238 | @param d2 Second pointer. 239 | @return 0 if equal, 1 if not. 240 | */ 241 | int data_compare_pointer(DATA d1, DATA d2) 242 | { 243 | if (d1.data_ptr == d2.data_ptr) { 244 | return 0; 245 | } else { 246 | return 1; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /test/arraylisttest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file arraylisttest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Saturday, 28 September 2013 8 | 9 | @brief Test of the array list data structure. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include "libstephen/al.h" 17 | #include "libstephen/ut.h" 18 | #include "tests.h" 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // TESTS 22 | 23 | int al_test_create() 24 | { 25 | DATA d; 26 | smb_status status = SMB_SUCCESS; 27 | d.data_llint = 13; 28 | 29 | smb_al *list = al_create(); 30 | al_append(list, d); 31 | 32 | TA_INT_EQ(al_length(list), 1); 33 | 34 | TA_LLINT_EQ(al_get(list, 0, &status).data_llint, (long long int)13); 35 | TA_INT_EQ(status, SMB_SUCCESS); 36 | 37 | al_delete(list); 38 | return 0; 39 | } 40 | 41 | int al_test_create_empty() 42 | { 43 | smb_al *list = al_create(); 44 | TA_INT_EQ(al_length(list), 0); 45 | al_delete(list); 46 | return 0; 47 | } 48 | 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | // TEST LOADER AND RUNNER 52 | 53 | void array_list_test() 54 | { 55 | smb_ut_group *group = su_create_test_group("test/arraylisttest.c"); 56 | 57 | smb_ut_test *create = su_create_test("create", al_test_create); 58 | su_add_test(group, create); 59 | 60 | smb_ut_test *create_empty = su_create_test("create_empty", al_test_create_empty); 61 | su_add_test(group, create_empty); 62 | 63 | su_run_group(group); 64 | su_delete_group(group); 65 | } 66 | -------------------------------------------------------------------------------- /test/bitfieldtest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file bitfieldtest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Wednesday, 5 February 2014 8 | 9 | @brief Test for the libstephen bitfield. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include "libstephen/bf.h" 17 | #include "libstephen/ut.h" 18 | #include "tests.h" 19 | 20 | #define test_bools 80 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // TESTS 24 | 25 | int bf_test_init() 26 | { 27 | unsigned char field[SMB_BITFIELD_SIZE(test_bools)]; 28 | int i; 29 | bf_init(field, test_bools); 30 | 31 | for (i = 0; i < SMB_BITFIELD_SIZE(test_bools); i++) { 32 | TA_INT_EQ(field[i], 0); 33 | } 34 | 35 | return 0; 36 | } 37 | 38 | int bf_test_memory() 39 | { 40 | unsigned char *field; 41 | field = bf_create(test_bools); 42 | bf_delete(field, test_bools); 43 | return 0; // looking for memory leaks here 44 | } 45 | 46 | int bf_test_check() 47 | { 48 | unsigned char field[2] = {0x00, 0xFF}; 49 | int i; 50 | 51 | // don't want to init...that ruins our test variables 52 | //bf_init(field, 16); 53 | 54 | for (i = 0; i < 8; i++) { 55 | TEST_ASSERT(!bf_check(field, i)); 56 | } 57 | 58 | for (i = 8; i < 16; i++) { 59 | TEST_ASSERT(bf_check(field, i)); 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | int bf_test_set() 66 | { 67 | unsigned char field[SMB_BITFIELD_SIZE(test_bools)]; 68 | int i; 69 | 70 | bf_init(field, test_bools); 71 | for (i = 0; i < test_bools; i += 2) { 72 | TEST_ASSERT(!bf_check(field, i)); 73 | bf_set(field, i); 74 | TEST_ASSERT(bf_check(field, i)); 75 | } 76 | 77 | for (i = 0; i < test_bools; i++) { 78 | if (i % 2 == 0) {// even 79 | TEST_ASSERT(bf_check(field, i)); 80 | } else { 81 | TEST_ASSERT(!bf_check(field, i)); 82 | } 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | int bf_test_clear() 89 | { 90 | unsigned char field[SMB_BITFIELD_SIZE(test_bools)]; 91 | int i; 92 | 93 | bf_init(field, test_bools); 94 | for (i = 0; i < test_bools; i++) { 95 | bf_set(field, i); // this is already tested 96 | } 97 | 98 | for (i = 0; i < test_bools; i += 2) { 99 | TEST_ASSERT(bf_check(field, i)); 100 | bf_clear(field, i); 101 | TEST_ASSERT(!bf_check(field, i)); 102 | } 103 | 104 | for (i = 0; i < test_bools; i++) { 105 | if (i % 2 == 0) {// even 106 | TEST_ASSERT(!bf_check(field, i)); 107 | } else { 108 | TEST_ASSERT(bf_check(field, i)); 109 | } 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | int bf_test_flip() 116 | { 117 | unsigned char field[SMB_BITFIELD_SIZE(test_bools)]; 118 | int i; 119 | 120 | bf_init(field, test_bools); 121 | // Set all the even numbered fields 122 | for (i = 0; i < test_bools; i += 2) { 123 | bf_set(field, i); //tested 124 | } 125 | 126 | // Flip them all 127 | for (i = 0; i < test_bools; i++) { 128 | bf_flip(field, i); 129 | } 130 | 131 | for (i = 0; i < test_bools; i++) { 132 | if (i % 2 == 0) {// even 133 | TEST_ASSERT(!bf_check(field, i)); 134 | } else { 135 | TEST_ASSERT(bf_check(field, i)); 136 | } 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | //////////////////////////////////////////////////////////////////////////////// 143 | // TEST LOADER AND RUNNER 144 | 145 | void bit_field_test() { 146 | smb_ut_group *group = su_create_test_group("test/bitfieldtest.c"); 147 | 148 | smb_ut_test *init = su_create_test("init", bf_test_init); 149 | su_add_test(group, init); 150 | 151 | smb_ut_test *memory = su_create_test("memory", bf_test_memory); 152 | su_add_test(group, memory); 153 | 154 | smb_ut_test *check = su_create_test("check", bf_test_check); 155 | su_add_test(group, check); 156 | 157 | smb_ut_test *set = su_create_test("set", bf_test_set); 158 | su_add_test(group, set); 159 | 160 | smb_ut_test *clear = su_create_test("clear", bf_test_clear); 161 | su_add_test(group, clear); 162 | 163 | smb_ut_test *flip = su_create_test("flip", bf_test_flip); 164 | su_add_test(group, flip); 165 | 166 | su_run_group(group); 167 | su_delete_group(group); 168 | } 169 | -------------------------------------------------------------------------------- /test/itertest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file itertest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Monday, 4 August 2014 8 | 9 | @brief Libstephen: Iterator Tests 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include "libstephen/ut.h" 17 | 18 | #include "libstephen/list.h" 19 | #include "libstephen/ll.h" 20 | #include "libstephen/al.h" 21 | 22 | // Setup and tear down declarations. 23 | 24 | /** 25 | @brief Return and iterator to test. 26 | @param n Number of elements to return. 27 | 28 | The elements should be integers of 100 * index. 29 | */ 30 | smb_iter (*get_iter)(int n); 31 | /** 32 | @brief Clean up the iterator's underlying list. 33 | */ 34 | void (*cleanup)(void); 35 | void *test_data; 36 | 37 | 38 | /******************************************************************************* 39 | 40 | Tests 41 | 42 | *******************************************************************************/ 43 | 44 | /** 45 | @brief Tests that empty iterators have no values. 46 | */ 47 | int iter_test_empty() 48 | { 49 | smb_iter it = get_iter(0); 50 | TEST_ASSERT(! it.has_next(&it)); 51 | it.destroy(&it); 52 | cleanup(); 53 | return 0; 54 | } 55 | 56 | /** 57 | @brief Tests that iterators can destroy. 58 | */ 59 | int iter_test_destroy() 60 | { 61 | smb_iter it = get_iter(10); 62 | it.destroy(&it); 63 | cleanup(); 64 | return 0; 65 | } 66 | 67 | /** 68 | @brief Tests that iterators can free. 69 | */ 70 | int iter_test_delete() 71 | { 72 | smb_iter *it = smb_new(smb_iter, 1); 73 | *it = get_iter(10); 74 | it->delete(it); 75 | cleanup(); 76 | return 0; 77 | } 78 | 79 | /** 80 | @brief Tests that iterators have the correct amounts of values. 81 | */ 82 | int iter_test_count() 83 | { 84 | smb_status status = SMB_SUCCESS; 85 | #define MAX_TEST_COUNT 1000 86 | for (int i = 0; i < MAX_TEST_COUNT; i++) { 87 | smb_iter it = get_iter(i); 88 | int n = 0; 89 | while (it.has_next(&it)) { 90 | it.next(&it, &status); 91 | TA_INT_EQ(status, SMB_SUCCESS); 92 | n++; 93 | } 94 | it.next(&it, &status); 95 | TA_INT_EQ(status, SMB_STOP_ITERATION); 96 | it.destroy(&it); 97 | cleanup(); 98 | TA_INT_EQ(i, n); 99 | } 100 | return 0; 101 | } 102 | 103 | /** 104 | @brief Tests that iterators return correct values in the correct order. 105 | */ 106 | int iter_test_values() 107 | { 108 | #define TEST_COUNT 1000 109 | smb_status status; 110 | smb_iter it = get_iter(TEST_COUNT); 111 | DATA d; 112 | int i = 0; 113 | while (it.has_next(&it)) { 114 | d = it.next(&it, &status); 115 | TA_INT_EQ(status, SMB_SUCCESS); 116 | TA_LLINT_EQ(d.data_llint, (long long)100 * i); 117 | i++; 118 | } 119 | it.destroy(&it); 120 | cleanup(); 121 | return 0; 122 | } 123 | 124 | 125 | /******************************************************************************* 126 | 127 | Setup/Tear Down 128 | 129 | *******************************************************************************/ 130 | 131 | /** 132 | @brief Return an iterator for an array list. 133 | @param size Number of elements to add. 134 | @return An iterator. 135 | */ 136 | smb_iter get_al_iter(int size) 137 | { 138 | test_data = al_create(); 139 | 140 | for (int i = 0; i < size; i++) { 141 | DATA d = { .data_llint = 100 * i }; 142 | al_append(test_data, d); 143 | } 144 | 145 | return al_get_iter(test_data); 146 | } 147 | 148 | /** 149 | @brief Clean up array list. 150 | */ 151 | void al_cleanup(void) 152 | { 153 | al_delete(test_data); 154 | } 155 | 156 | /** 157 | @brief Return a linked list iterator. 158 | @param Number of elements in the list. 159 | @return An iterator. 160 | */ 161 | smb_iter get_ll_iter(int size) 162 | { 163 | test_data = ll_create(); 164 | 165 | for (int i = 0; i < size; i++) { 166 | DATA d = { .data_llint = 100 * i }; 167 | ll_append(test_data, d); 168 | } 169 | 170 | return ll_get_iter(test_data); 171 | } 172 | 173 | /** 174 | @brief Clean up linked list. 175 | */ 176 | void ll_cleanup(void) 177 | { 178 | ll_delete(test_data); 179 | } 180 | 181 | /******************************************************************************* 182 | 183 | Test Running 184 | 185 | *******************************************************************************/ 186 | 187 | /** 188 | @brief Run the tests in this file with a specified description. 189 | 190 | This allows you to set the setup and tear down functions to different values 191 | and run the tests again with a different name. 192 | 193 | @param desc The description for this run through. 194 | */ 195 | void run_tests(char *desc) 196 | { 197 | smb_ut_group *group = su_create_test_group(desc); 198 | 199 | smb_ut_test *empty = su_create_test("empty", iter_test_empty); 200 | su_add_test(group, empty); 201 | 202 | smb_ut_test *destroy = su_create_test("destroy", iter_test_destroy); 203 | su_add_test(group, destroy); 204 | 205 | smb_ut_test *delete = su_create_test("delete", iter_test_delete); 206 | su_add_test(group, delete); 207 | 208 | smb_ut_test *count = su_create_test("count", iter_test_count); 209 | su_add_test(group, count); 210 | 211 | smb_ut_test *values = su_create_test("values", iter_test_values); 212 | su_add_test(group, values); 213 | 214 | su_run_group(group); 215 | su_delete_group(group); 216 | } 217 | 218 | /** 219 | @brief Run the tests on iterators. 220 | */ 221 | void iter_test(void) 222 | { 223 | get_iter = &get_al_iter; 224 | cleanup = &al_cleanup; 225 | run_tests("al: test/itertest.c"); 226 | 227 | get_iter = &get_ll_iter; 228 | cleanup = &ll_cleanup; 229 | run_tests("ll: test/itertest.c"); 230 | } 231 | -------------------------------------------------------------------------------- /test/logtest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file logtest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 24 May 2015 8 | 9 | @brief Tests for logging (can't use smbunit though) 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | 18 | #include "libstephen/log.h" 19 | #include "libstephen/ut.h" 20 | 21 | int test_levels(void) 22 | { 23 | smb_status status = SMB_SUCCESS; 24 | sl_clear_handlers(NULL); 25 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_DEBUG, .dst=stdout}, &status); 26 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_INFO, .dst=stdout}, &status); 27 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_WARNING, .dst=stdout}, &status); 28 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_ERROR, .dst=stdout}, &status); 29 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_CRITICAL, .dst=stdout}, &status); 30 | LOG(NULL, LEVEL_NOTSET, "this appears 0 times"); 31 | DDEBUG("this appears 1 time"); 32 | DINFO("this appears 2 times"); 33 | DWARNING("this appears 3 times"); 34 | DERROR("this appears 4 times"); 35 | DCRITICAL("this appears 5 times"); 36 | sl_set_level(NULL, LEVEL_DEBUG); 37 | DDEBUG("this appears 5 times"); 38 | LOG(NULL, LEVEL_DEBUG + 1, "this also appears 5 times, with level 11"); 39 | return 0; 40 | } 41 | 42 | int test_override(void) 43 | { 44 | smb_status status = SMB_SUCCESS; 45 | smb_logger *logger = sl_create(); 46 | sl_clear_handlers(NULL); 47 | sl_add_handler(logger, (smb_loghandler){.level=LEVEL_INFO, .dst=stdout}, &status); 48 | DDEBUG("you shouldn't see this"); 49 | DINFO("you shouldn't see this"); 50 | sl_set_default_logger(logger); 51 | DDEBUG("you shouldn't see this"); 52 | DINFO("you should see this"); 53 | sl_set_default_logger(NULL); 54 | DDEBUG("you shouldn't see this"); 55 | DINFO("you shouldn't see this"); 56 | sl_delete(logger); 57 | return 0; 58 | } 59 | 60 | int test_too_many_levels(void) 61 | { 62 | smb_status status = SMB_SUCCESS; 63 | sl_clear_handlers(NULL); 64 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_DEBUG, .dst=stdout}, &status); 65 | TA_INT_EQ(status, SMB_SUCCESS); 66 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_INFO, .dst=stdout}, &status); 67 | TA_INT_EQ(status, SMB_SUCCESS); 68 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_WARNING, .dst=stdout}, &status); 69 | TA_INT_EQ(status, SMB_SUCCESS); 70 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_ERROR, .dst=stdout}, &status); 71 | TA_INT_EQ(status, SMB_SUCCESS); 72 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_CRITICAL, .dst=stdout}, &status); 73 | TA_INT_EQ(status, SMB_SUCCESS); 74 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_DEBUG, .dst=stdout}, &status); 75 | TA_INT_EQ(status, SMB_SUCCESS); 76 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_INFO, .dst=stdout}, &status); 77 | TA_INT_EQ(status, SMB_SUCCESS); 78 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_WARNING, .dst=stdout}, &status); 79 | TA_INT_EQ(status, SMB_SUCCESS); 80 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_ERROR, .dst=stdout}, &status); 81 | TA_INT_EQ(status, SMB_SUCCESS); 82 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_CRITICAL, .dst=stdout}, &status); 83 | TA_INT_EQ(status, SMB_SUCCESS); 84 | sl_add_handler(NULL, (smb_loghandler){.level=LEVEL_CRITICAL, .dst=stdout}, &status); 85 | TA_INT_EQ(status, SMB_INDEX_ERROR); 86 | return 0; 87 | } 88 | 89 | void log_test(void) 90 | { 91 | smb_ut_group *group = su_create_test_group("test/logtest.c"); 92 | 93 | smb_ut_test *levels = su_create_test("levels", test_levels); 94 | su_add_test(group, levels); 95 | 96 | smb_ut_test *override = su_create_test("override", test_override); 97 | su_add_test(group, override); 98 | 99 | smb_ut_test *too_many_levels = su_create_test("too_many_levels", test_too_many_levels); 100 | su_add_test(group, too_many_levels); 101 | 102 | printf("ALERT: Some logging tests are manual!\n"); 103 | su_run_group(group); 104 | su_delete_group(group); 105 | } 106 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file main.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Thursday, 12 September 2013 8 | 9 | @brief Run tests on the libstephen library. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | 18 | #include "libstephen/base.h" 19 | #include "libstephen/log.h" 20 | #include "tests.h" 21 | 22 | /** 23 | Main test function 24 | */ 25 | int main(int argc, char ** argv) 26 | { 27 | (void) argc; // unused 28 | (void) argv; // unused 29 | sl_set_level(NULL, LEVEL_INFO); 30 | linked_list_test(); 31 | array_list_test(); 32 | hash_table_test(); 33 | hta_test(); 34 | bit_field_test(); 35 | iter_test(); 36 | list_test(); 37 | args_test(); 38 | charbuf_test(); 39 | string_test(); 40 | parse_test(); 41 | lex_test(); 42 | codegen_test(); 43 | pike_test(); 44 | log_test(); 45 | ringbuf_test(); 46 | // return args_test_main(argc, argv); 47 | } 48 | -------------------------------------------------------------------------------- /test/ringbuftest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file ringbuftest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Saturday, 23 May 2015 8 | 9 | @brief Tests for the ring buffer. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | #include 16 | 17 | #include "libstephen/ut.h" 18 | #include "libstephen/rb.h" 19 | 20 | void print_ringbuf(smb_rb *rb) 21 | { 22 | printf("dsize:%d, nalloc:%d, start:%d, count:%d\n", 23 | rb->dsize, rb->nalloc, rb->start, rb->count); 24 | for (int i = 0; i < rb->count; i++) { 25 | int index, v; 26 | index = (rb->start + i) % rb->nalloc; 27 | memcpy(&v, rb->data + index * rb->dsize, rb->dsize); 28 | printf("%d: %c\n", index, v); 29 | } 30 | } 31 | 32 | int test_push_front(void) 33 | { 34 | smb_rb rb; 35 | rb_init(&rb, sizeof(int), 4); 36 | rb.start = 1; 37 | 38 | int v = 'a'; 39 | rb_push_front(&rb, &v); 40 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 41 | TA_INT_EQ(rb.count, 1); 42 | TA_INT_EQ(rb.start, 0); 43 | 44 | v = 'b'; 45 | rb_push_front(&rb, &v); 46 | TA_INT_EQ(((int*)rb.data)[3], 'b'); 47 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 48 | TA_INT_EQ(rb.count, 2); 49 | TA_INT_EQ(rb.start, 3); 50 | 51 | v = 'c'; 52 | rb_push_front(&rb, &v); 53 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 54 | TA_INT_EQ(((int*)rb.data)[3], 'b'); 55 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 56 | TA_INT_EQ(rb.count, 3); 57 | TA_INT_EQ(rb.start, 2); 58 | 59 | v = 'd'; 60 | rb_push_front(&rb, &v); 61 | TA_INT_EQ(((int*)rb.data)[1], 'd'); 62 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 63 | TA_INT_EQ(((int*)rb.data)[3], 'b'); 64 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 65 | TA_INT_EQ(rb.count, 4); 66 | TA_INT_EQ(rb.start, 1); 67 | 68 | v = 'e'; 69 | rb_push_front(&rb, &v); 70 | TA_INT_EQ(((int*)rb.data)[0], 'e'); 71 | TA_INT_EQ(((int*)rb.data)[1], 'd'); 72 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 73 | TA_INT_EQ(((int*)rb.data)[3], 'b'); 74 | TA_INT_EQ(((int*)rb.data)[4], 'a'); 75 | TA_INT_EQ(rb.count, 5); 76 | TA_INT_EQ(rb.start, 0); 77 | 78 | rb_destroy(&rb); 79 | return 0; 80 | } 81 | 82 | int test_pop_front(void) 83 | { 84 | smb_rb rb; 85 | rb_init(&rb, sizeof(int), 4); 86 | rb.start = 1; 87 | 88 | int v = 'a'; 89 | rb_push_front(&rb, &v); // a 90 | v = 'b'; 91 | rb_push_back(&rb, &v); // ab 92 | v = 'c'; 93 | rb_push_front(&rb, &v); // cab 94 | v = 'd'; 95 | rb_push_back(&rb, &v); // cabd 96 | v = 'e'; 97 | rb_push_front(&rb, &v); // ecabd 98 | 99 | TA_INT_EQ(rb.count, 5); 100 | rb_pop_front(&rb, &v); 101 | TA_INT_EQ(v, 'e'); 102 | rb_pop_front(&rb, &v); 103 | TA_INT_EQ(v, 'c'); 104 | rb_pop_front(&rb, &v); 105 | TA_INT_EQ(v, 'a'); 106 | rb_pop_front(&rb, &v); 107 | TA_INT_EQ(v, 'b'); 108 | rb_pop_front(&rb, &v); 109 | TA_INT_EQ(v, 'd'); 110 | rb_destroy(&rb); 111 | return 0; 112 | } 113 | 114 | int test_push_back(void) 115 | { 116 | smb_rb rb; 117 | rb_init(&rb, sizeof(int), 4); 118 | 119 | int v = 'a'; 120 | rb_push_back(&rb, &v); 121 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 122 | TA_INT_EQ(rb.count, 1); 123 | 124 | v = 'b'; 125 | rb_push_back(&rb, &v); 126 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 127 | TA_INT_EQ(((int*)rb.data)[1], 'b'); 128 | TA_INT_EQ(rb.count, 2); 129 | 130 | v = 'c'; 131 | rb_push_back(&rb, &v); 132 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 133 | TA_INT_EQ(((int*)rb.data)[1], 'b'); 134 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 135 | TA_INT_EQ(rb.count, 3); 136 | 137 | v = 'd'; 138 | rb_push_back(&rb, &v); 139 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 140 | TA_INT_EQ(((int*)rb.data)[1], 'b'); 141 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 142 | TA_INT_EQ(((int*)rb.data)[3], 'd'); 143 | TA_INT_EQ(rb.count, 4); 144 | 145 | v = 'e'; 146 | rb_push_back(&rb, &v); 147 | TA_INT_EQ(((int*)rb.data)[0], 'a'); 148 | TA_INT_EQ(((int*)rb.data)[1], 'b'); 149 | TA_INT_EQ(((int*)rb.data)[2], 'c'); 150 | TA_INT_EQ(((int*)rb.data)[3], 'd'); 151 | TA_INT_EQ(((int*)rb.data)[4], 'e'); 152 | TA_INT_EQ(rb.count, 5); 153 | TA_INT_EQ(rb.nalloc, 8); 154 | 155 | rb_destroy(&rb); 156 | return 0; 157 | } 158 | 159 | int test_pop_back(void) 160 | { 161 | smb_rb rb; 162 | rb_init(&rb, sizeof(int), 4); 163 | rb.start = 1; 164 | 165 | int v = 'a'; 166 | rb_push_front(&rb, &v); // a 167 | v = 'b'; 168 | rb_push_back(&rb, &v); // ab 169 | v = 'c'; 170 | rb_push_front(&rb, &v); // cab 171 | v = 'd'; 172 | rb_push_back(&rb, &v); // cabd 173 | v = 'e'; 174 | rb_push_front(&rb, &v); // ecabd 175 | //print_ringbuf(&rb); 176 | 177 | TA_INT_EQ(rb.count, 5); 178 | rb_pop_back(&rb, &v); 179 | TA_INT_EQ(v, 'd'); 180 | rb_pop_back(&rb, &v); 181 | TA_INT_EQ(v, 'b'); 182 | rb_pop_back(&rb, &v); 183 | TA_INT_EQ(v, 'a'); 184 | rb_pop_back(&rb, &v); 185 | TA_INT_EQ(v, 'c'); 186 | rb_pop_back(&rb, &v); 187 | TA_INT_EQ(v, 'e'); 188 | rb_destroy(&rb); 189 | return 0; 190 | } 191 | 192 | void ringbuf_test(void) 193 | { 194 | smb_ut_group *group = su_create_test_group("test/ringbuftest.c"); 195 | 196 | smb_ut_test *push_front = su_create_test("push_front", test_push_front); 197 | su_add_test(group, push_front); 198 | 199 | smb_ut_test *pop_front = su_create_test("pop_front", test_pop_front); 200 | su_add_test(group, pop_front); 201 | 202 | smb_ut_test *push_back = su_create_test("push_back", test_push_back); 203 | su_add_test(group, push_back); 204 | 205 | smb_ut_test *pop_back = su_create_test("pop_back", test_pop_back); 206 | su_add_test(group, pop_back); 207 | 208 | su_run_group(group); 209 | su_delete_group(group); 210 | } 211 | -------------------------------------------------------------------------------- /test/stringtest.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file stringtest.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Sunday, 12 July 2015 8 | 9 | @brief Tests for string functions. 10 | 11 | @copyright Copyright (c) 2015-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See LICENSE.txt for details. 13 | 14 | *******************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "libstephen/ut.h" 22 | #include "libstephen/str.h" 23 | 24 | /* 25 | int test_read_file(void) 26 | { 27 | FILE *f = fopen("res/file.txt", "r"); 28 | char *expected = 29 | "This is the first line of my file.\n" 30 | "This is the second line of my file.\n"; 31 | char *str = read_file(f); 32 | 33 | TA_STR_EQ(expected, str) 34 | smb_free(str); 35 | fclose(f); 36 | return 0; 37 | } 38 | 39 | int test_read_filew(void) 40 | { 41 | FILE *f = fopen("res/file.txt", "r"); 42 | wchar_t *expected = 43 | L"This is the first line of my file.\n" 44 | L"This is the second line of my file.\n"; 45 | wchar_t *str = read_filew(f); 46 | 47 | TA_WSTR_EQ(expected, str); 48 | smb_free(str); 49 | fclose(f); 50 | return 0; 51 | } 52 | 53 | int test_read_line(void) 54 | { 55 | FILE *f = fopen("res/file.txt", "r"); 56 | int i = 0; 57 | char *lines[] = { 58 | "This is the first line of my file.", 59 | "This is the second line of my file.", 60 | "" 61 | }; 62 | char *line; 63 | 64 | while (!feof(f)) { 65 | line = read_line(f); 66 | TA_STR_EQ(lines[i], line) 67 | smb_free(line); 68 | i++; 69 | } 70 | fclose(f); 71 | return 0; 72 | } 73 | 74 | int test_read_linew(void) 75 | { 76 | FILE *f = fopen("res/file.txt", "r"); 77 | int i = 0; 78 | wchar_t *lines[] = { 79 | L"This is the first line of my file.", 80 | L"This is the second line of my file.", 81 | L"" 82 | }; 83 | wchar_t *line; 84 | 85 | while (!feof(f)) { 86 | line = read_linew(f); 87 | TA_WSTR_EQ(lines[i], line); 88 | smb_free(line); 89 | i++; 90 | } 91 | fclose(f); 92 | return 0; 93 | }*/ 94 | 95 | int test_split_lines(void) 96 | { 97 | char multiline_string[] = "line1\nline2\nline3\n"; 98 | char *lines[] = {"line1", "line2", "line3"}; 99 | char *line; 100 | smb_ll *linelist = split_lines(multiline_string); 101 | smb_iter it = ll_get_iter(linelist); 102 | smb_status st = SMB_SUCCESS; 103 | while (it.has_next(&it)) { 104 | line = it.next(&it, &st).data_ptr; 105 | TA_STR_EQ(lines[it.index-1], line) 106 | assert(st == SMB_SUCCESS); 107 | } 108 | ll_delete(linelist); 109 | return 0; 110 | } 111 | 112 | int test_split_linesw(void) 113 | { 114 | wchar_t multiline_string[] = L"line1\nline2\nline3\n"; 115 | wchar_t *lines[] = {L"line1", L"line2", L"line3"}; 116 | wchar_t *line; 117 | smb_ll *linelist = split_linesw(multiline_string); 118 | smb_iter it = ll_get_iter(linelist); 119 | smb_status st = SMB_SUCCESS; 120 | while (it.has_next(&it)) { 121 | line = it.next(&it, &st).data_ptr; 122 | TA_WSTR_EQ(lines[it.index-1], line); 123 | assert(st == SMB_SUCCESS); 124 | } 125 | ll_delete(linelist); 126 | return 0; 127 | } 128 | 129 | int test_split_lines_nonewline(void) 130 | { 131 | char multiline_string[] = "line1\nline2\nline3"; 132 | char *lines[] = {"line1", "line2", "line3"}; 133 | char *line; 134 | smb_ll *linelist = split_lines(multiline_string); 135 | smb_iter it = ll_get_iter(linelist); 136 | smb_status st = SMB_SUCCESS; 137 | while (it.has_next(&it)) { 138 | line = it.next(&it, &st).data_ptr; 139 | TA_STR_EQ(lines[it.index-1], line) 140 | assert(st == SMB_SUCCESS); 141 | } 142 | ll_delete(linelist); 143 | return 0; 144 | } 145 | 146 | int test_split_linesw_nonewline(void) 147 | { 148 | wchar_t multiline_string[] = L"line1\nline2\nline3"; 149 | wchar_t *lines[] = {L"line1", L"line2", L"line3"}; 150 | wchar_t *line; 151 | smb_ll *linelist = split_linesw(multiline_string); 152 | smb_iter it = ll_get_iter(linelist); 153 | smb_status st = SMB_SUCCESS; 154 | while (it.has_next(&it)) { 155 | line = it.next(&it, &st).data_ptr; 156 | TA_WSTR_EQ(lines[it.index-1], line); 157 | assert(st == SMB_SUCCESS); 158 | } 159 | ll_delete(linelist); 160 | return 0; 161 | } 162 | 163 | void string_test(void) 164 | { 165 | smb_ut_group *group = su_create_test_group("test/stringtest.c"); 166 | 167 | /* 168 | smb_ut_test *read_file = su_create_test("read_file", test_read_file); 169 | su_add_test(group, read_file); 170 | 171 | smb_ut_test *read_filew = su_create_test("read_filew", test_read_filew); 172 | su_add_test(group, read_filew); 173 | 174 | smb_ut_test *read_line = su_create_test("read_line", test_read_line); 175 | su_add_test(group, read_line); 176 | 177 | smb_ut_test *read_linew = su_create_test("read_linew", test_read_linew); 178 | su_add_test(group, read_linew); 179 | */ 180 | 181 | smb_ut_test *split_lines = su_create_test("split_lines", test_split_lines); 182 | su_add_test(group, split_lines); 183 | 184 | smb_ut_test *split_linesw = su_create_test("split_linesw", test_split_linesw); 185 | su_add_test(group, split_linesw); 186 | 187 | smb_ut_test *split_lines_nonewline = su_create_test("split_lines_nonewline", test_split_lines_nonewline); 188 | su_add_test(group, split_lines_nonewline); 189 | 190 | smb_ut_test *split_linesw_nonewline = su_create_test("split_linesw_nonewline", test_split_linesw_nonewline); 191 | su_add_test(group, split_linesw_nonewline); 192 | 193 | su_run_group(group); 194 | su_delete_group(group); 195 | } 196 | -------------------------------------------------------------------------------- /test/tests.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file tests.h 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Thursday, 12 September 2013 8 | 9 | @brief Header for all the tests. 10 | 11 | @copyright Copyright (c) 2013-2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | /** 17 | Run the linked list tests 18 | */ 19 | void linked_list_test(); 20 | 21 | /** 22 | Run the array list tests 23 | */ 24 | void array_list_test(); 25 | 26 | /** 27 | Run the hash table tests 28 | */ 29 | void hash_table_test(); 30 | 31 | /** 32 | Run the hash table any data tests 33 | */ 34 | void hta_test(); 35 | 36 | /** 37 | Run the bit field tests 38 | */ 39 | void bit_field_test(); 40 | 41 | /** 42 | Run the iter tests 43 | */ 44 | void iter_test(void); 45 | 46 | /** 47 | Run the list tests 48 | */ 49 | void list_test(void); 50 | 51 | /** 52 | Run the args tests 53 | */ 54 | void args_test(void); 55 | 56 | /** 57 | Run the cbuf test. 58 | */ 59 | void charbuf_test(void); 60 | 61 | /** 62 | Run the log test (this one is visual, no unit tests). 63 | */ 64 | void log_test(void); 65 | 66 | /** 67 | Run the string test. 68 | */ 69 | void string_test(void); 70 | 71 | /** 72 | Regex tests. 73 | */ 74 | void parse_test(void); 75 | void lex_test(void); 76 | void codegen_test(void); 77 | void pike_test(void); 78 | void ringbuf_test(void); 79 | 80 | 81 | /** 82 | Output statistics of main function args. 83 | */ 84 | int args_test_main(int argc, char **argv); 85 | -------------------------------------------------------------------------------- /util/lisp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "libstephen/lisp.h" 5 | 6 | int main(int argc, char **argv) 7 | { 8 | lisp_runtime rt; 9 | lisp_init(&rt); 10 | lisp_scope *scope = (lisp_scope*)lisp_new(&rt, type_scope); 11 | lisp_scope_populate_builtins(&rt, scope); 12 | 13 | while (true) { 14 | char *input = readline("> "); 15 | if (input == NULL) { 16 | break; 17 | } 18 | lisp_value *value = lisp_parse(&rt, input); 19 | add_history(input); 20 | free(input); 21 | lisp_value *result = lisp_eval(&rt, scope, value); 22 | lisp_print(stdout, result); 23 | fprintf(stdout, "\n"); 24 | lisp_mark(&rt, (lisp_value*)scope); 25 | lisp_sweep(&rt); 26 | } 27 | 28 | lisp_destroy(&rt); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /util/regex.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************//** 2 | 3 | @file regex.c 4 | 5 | @author Stephen Brennan 6 | 7 | @date Created Thursday, 12 September 2013 8 | 9 | @brief Utility for exercising an arbitrary regex. 10 | 11 | @copyright Copyright (c) 2016, Stephen Brennan. Released under the 12 | Revised BSD License. See the LICENSE.txt file for details. 13 | 14 | *******************************************************************************/ 15 | 16 | 17 | #include 18 | #include 19 | 20 | #include "libstephen/re.h" 21 | 22 | 23 | int main(int argc, char **argv) 24 | { 25 | if (argc < 3) { 26 | fprintf(stderr, "too few arguments\n"); 27 | fprintf(stderr, "usage: %s REGEXP string1 [string2 [...]]\n", argv[0]); 28 | exit(EXIT_FAILURE); 29 | } 30 | 31 | // Try to open first arg as file. 32 | Regex code; 33 | FILE *in = fopen(argv[1], "r"); 34 | 35 | if (in == NULL) { 36 | // If it doesn't open, it's a regex we should compile. 37 | printf(";; Regex: \"%s\"\n\n", argv[1]); 38 | code = recomp(argv[1]); 39 | printf(";; BEGIN GENERATED CODE:\n"); 40 | } else { 41 | // Otherwise, open it and read the code from it. 42 | code = refread(in); 43 | printf(";; BEGIN READ CODE:\n"); 44 | } 45 | rewrite(code, stdout); 46 | 47 | int ns = renumsaves(code); 48 | printf(";; BEGIN TEST RUNS:\n"); 49 | 50 | for (int i = 2; i < argc; i++) { 51 | size_t *saves = NULL; 52 | ssize_t match = reexec(code, argv[i], &saves); 53 | if (match != -1) { 54 | // It matches, report the captured groups. 55 | printf(";; \"%s\": match(%zd) ", argv[i], match); 56 | for (ssize_t j = 0; j < ns; j += 2) { 57 | printf("(%zd, %zd) ", saves[j], saves[j+1]); 58 | } 59 | printf("\n"); 60 | } else { 61 | // Otherwise, report no match. 62 | printf(";; \"%s\": no match\n", argv[i]); 63 | } 64 | } 65 | 66 | refree(code); 67 | } 68 | --------------------------------------------------------------------------------