├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING ├── CONTRIBUTING_NEW_KEYWORD ├── LICENSE ├── README.md ├── bindings └── pypyfsqlf.py ├── cli ├── CMakeLists.txt ├── cli.c ├── cli.h └── main.c ├── cmake ├── toolchain-linux-x86-64-gcc.cmake ├── toolchain-linux-x86-gcc.cmake └── toolchain-windows-x86-mingw.cmake ├── gui ├── CMakeLists.txt ├── basic_notepad.cpp ├── basic_notepad.hpp ├── dnd_target.cpp ├── dnd_target.hpp ├── fsqlf_right.xpm ├── wx_fsqlf.cpp └── wx_fsqlf.hpp ├── include └── lib_fsqlf.h ├── lib_fsqlf ├── CMakeLists.txt ├── conf_file │ ├── conf_file_create.c │ └── conf_file_read.c ├── formatter │ ├── fsqlf.lex │ ├── lex_wrapper.c │ ├── print_keywords.c │ ├── print_keywords.h │ ├── tokque.c │ └── tokque.h ├── kw │ ├── is_word.c │ ├── is_word.h │ ├── kw.c │ ├── kwmap.c │ └── kwmap_defaults.def └── lex │ ├── token.c │ ├── token.h │ └── token_test.c ├── makefile ├── tests ├── CMakeLists.txt ├── cases │ ├── bigquery_expected.sql │ ├── bigquery_input.sql │ ├── case_expected.sql │ ├── case_input.sql │ ├── comment_marker_expected.sql │ ├── comment_marker_input.sql │ ├── create_table_expected.sql │ ├── create_table_input.sql │ ├── delete_expected.sql │ ├── delete_input.sql │ ├── group_order_expected.sql │ ├── group_order_input.sql │ ├── insert_expected.sql │ ├── insert_input.sql │ ├── many_semicolons_expected.sql │ ├── many_semicolons_input.sql │ ├── update_expected.sql │ ├── update_input.sql │ ├── using_expected.sql │ └── using_input.sql ├── format_files_test.c ├── registered_testcases.h └── tools │ ├── file_compare.c │ └── file_compare.h └── utils ├── map ├── uthash.h └── uthash_test.c ├── queue ├── queue.c ├── queue.h └── queue_test.c ├── stack ├── stack.c ├── stack.h └── stack_test.c ├── string ├── read_int.c ├── read_int.h └── test_read_int.c └── text_to_header └── text_to_header.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | build* 3 | 4 | 5 | #editor stuff 6 | *~ 7 | *.swp 8 | *.*project 9 | *.*workspace 10 | 11 | 12 | #archive for uploading 13 | *.zip 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(fsqlf) 3 | 4 | 5 | # Put all compiled outputs to single directory. 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 8 | 9 | 10 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 11 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") 12 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") 13 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc") 14 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") 15 | endif () 16 | 17 | 18 | add_subdirectory(lib_fsqlf) 19 | add_subdirectory(cli) 20 | add_subdirectory(gui) 21 | add_subdirectory(tests) 22 | 23 | 24 | enable_testing() 25 | add_test(NAME test_fsqlf_cli COMMAND format_files_test) 26 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Folder layout: 2 | 3 | ./buildtools - things specific for building (except for makefile) 4 | ./cli - code specific to command line interface 5 | ./gui - code specific to graphical user interface 6 | ./include - header of fsqlf library 7 | ./lib_fsqlf - code of fsqlf library 8 | ./tests - things specific to testing (tools & test-cases) 9 | ./utils - generic code of considerable size. External code files also go here. 10 | 11 | 12 | Shortnames used: 13 | 14 | 'kw' - keyword with all its settings 15 | 'kwmap' - collection of all 'kw's 16 | 'nl', 'tab', 'sp' - new line, tabulation/indentation, space character. 17 | 18 | 19 | Code style: 20 | 21 | Dialect: C99. 22 | Target platforms: windows and linux (more is better; but no promises). 23 | Indentation: 4 spaces (no tabs). 24 | Line limit: 79 characters (soft). 25 | Exported public symbols: prefix "fsqlf_". 26 | Exported private symbols: prefix "FSQLF_" (static functions don't need these). 27 | Also, names of similar things, should share same prefix. (mimic namespacing) 28 | Avoid typedef'ing structs. (only if need to hide or separate from struct) 29 | Follow K&R style. 30 | External code files should go under utils, and follow their own conventions. 31 | 32 | 33 | Code style illustration: 34 | ---- 35 | #include // puts() 36 | 37 | 38 | char * FSQLF_first() 39 | { 40 | // For things that have names (functions, structs, enums), 41 | // opening curly brace goes on new line. 42 | if (1 == 1) { 43 | // Opening bracket for if/for/while/switch go on same line. 44 | return NULL; 45 | } 46 | } 47 | 48 | 49 | // Two lines between adjacent functions. 50 | // (same for any other significant top level blocks) 51 | // If there is need to split arguments on separate lines, 52 | // closing parenthesis goes on new line. 53 | static void second( 54 | struct long_configuration *cfg, 55 | char *out_buffer 56 | ) 57 | { 58 | puts(FSQLF_first()); 59 | puts( 60 | "NULL" 61 | ); 62 | } 63 | ---- 64 | 65 | -------------------------------------------------------------------------------- /CONTRIBUTING_NEW_KEYWORD: -------------------------------------------------------------------------------- 1 | Short instruction how to add simple keyword formatting: 2 | 3 | 1. Register new keyword formatting configuration 4 | in "lib_fsqlf/kw/kwmap_defaults.def". 5 | 6 | 2. Add regexp and tie it keyword formatting configuration 7 | in "lib_fsqlf/formatter/fsqlf.lex". 8 | 9 | 3. To help ensure that it won't accidentally break later, add testcase 10 | in "tests/cases/" 11 | and "tests/registered_testcases.h". 12 | 13 | 4. Ensure that nothing else got broken. 14 | Execute "make test". (works only on linux) 15 | 16 | 17 | Note: this instruction works only for simplest cases of formatting, 18 | for more complex case one will need to invest time in analyzing/reading code. 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Free SQL Formatter 2 | ==== 3 | 4 | This is a tool that adds linebreaks and indentation to SQL source code. 5 | 6 | Licensed under LGPL. Full license is available in file 'LICENSE', which you should have received with the program 7 | 8 | 9 | How it works 10 | ---- 11 | fsqlf simply goes through provided input text, and tries to recognise a predefined set of keywords. If a keyword is recognized it adds spacing around it using settings in the configuration file (and to some level settings from CLI/GUI). 12 | 13 | 14 | Table of content: 15 | ---- 16 | 17 | 1. Current state of development 18 | 2. Compilation 19 | 1. Requirements 20 | 2. Compilation process 21 | 3. TODO lists 22 | 4. Usage 23 | 1. Usage commandline 24 | 2. Usage graphical user interface 25 | 5. Project site 26 | - APPENDIX A - wxWidgets installation 27 | 28 | 29 | 30 | 1. Current state of development 31 | ---- 32 | My use case is formatting generated SQL code - make something readable out of one long line of SQL code 33 | 34 | Current list of implemented capabilities: 35 | 36 | - indent subqueries 37 | - new line in cases where it seems appropriate (items in SELECT list. Keywords FROM, JOIN, ON, WHERE, AND, OR, DELETE, INSERT, UPDATE, SET) 38 | - capitalisation of recognised keywords (not all - I like some keywords lowercase, but this should be configurable eventually) 39 | - gui 40 | - configurations read from file 41 | 42 | (see TODO list what is planned for future) 43 | 44 | 45 | 46 | 2.1 Compilation - Requirements 47 | ---- 48 | 49 | The following list actually should be sufficient. Look into 'makefile' for compilation instructions 50 | 51 | - flex # http://flex.sourceforge.net/ 52 | - C compiler # 53 | - wxWidgets # needed for Graphical User Interface (GUI) 54 | - C++ compiler # for compiling GUI 55 | 56 | 57 | Stuff that I got installed: 58 | 59 | - flex # http://flex.sourceforge.net/ 60 | - gcc 61 | - wxWidgets # needed for Graphical User Interface (GUI). If you want to build Windows executables on Linux machine see 'APPENDIX.1 - wxWidgets installation' 62 | - g++ # for compiling GUI 63 | - make # This makes all compiling automated 64 | - mingw32 # cross compiler which is used to produce Windows executables under Linux 65 | - zip # used only for creating zip archive with source code and executable files 66 | - git # used for version control and creating zip archive with source code and executable files 67 | 68 | 69 | 2.2 Compilation process 70 | ---- 71 | General instructions 72 | 73 | - Use 'flex' on core/fsqlf.lex 74 | - Compile its output with C compiler and name the executable 'fsqlf.exe' if you on Windows and 'fsqlf' in other cases (the name is needed for gui to be able to call it) 75 | - Compile 'gui/wx_fsqlf.cpp' with C++ compiler 76 | - Put both executables 'fsqlf' and 'wx_fsqlf' into one directory. 77 | 78 | If you use Linux and have installed the same programs as I have (see section 2.1), then you can use the make file 79 | 80 | # make all # Compile source for LINUX and WINDOWS. Excutables can be found in 'bin/' directory: 'fsqlf', 'fsqlf.exe', 'gui_wx_basic', 'gui_wx_basic.exe' 81 | # make test # Compile and run program on test file 'test.sql' and print output to console 82 | # make zip # Create zip archive with executables and source code binaries (for publishing) 83 | # make clean # Remove all files created with other make commands 84 | 85 | 86 | 3. TODO list: 87 | ---- 88 | 89 | - parenthesis in restrictions (e.g. AND (a=1 OR b=1) ) 90 | - improve GUI 91 | - make 3 configuration modes: 92 | 1. none-use-file-config 93 | 2. detailed-as-in-file 94 | 3. select-templates-of-configurations 95 | - improve analytical function formatting 96 | - alignment of comments 97 | - in equality condition align all comparison signs ('=' , '<>', etc.) 98 | 99 | 100 | 4.1 Usage from command line 101 | ---- 102 | Windows 103 | 104 | # bin/fsqlf.exe # read from stdin, write to stdout 105 | # bin/fsqlf.exe input_file # read from file, write to stdout 106 | # bin/fsqlf.exe input_file output_file # use files for reading and writing 107 | # bin/fsqlf.exe --help # info on more options 108 | 109 | Usage in Linux is equivalent, just without the extension '.exe' 110 | Formatting configuration can be changed in 'formatting.conf' 111 | 112 | 113 | 4.2 Usage of graphical user interface 114 | ---- 115 | Go to directory 'bin/' (this is needed so 'wx_fsqlf.exe' would be started in the same folder where 'fsqlf.exe' is located) 116 | In Windows execute 'wx_fsqlf.exe' 117 | In Linux execute 'wx_fsqlf' 118 | 119 | When it starts, just fill your code into the formatter window and press 'Format' 120 | Formating configuration can be changed in 'formatting.conf' 121 | 122 | 123 | 124 | 5. Project site 125 | ---- 126 | Most recent source code can be found at sourceforge. 127 | Project site address http://sourceforge.net/projects/fsqlf/ 128 | Source can also be forked at github https://github.com/dnsmkl/fsqlf 129 | 130 | 131 | 132 | * * * 133 | 134 | 135 | 6. APPENDIX A - wxWidgets installation 136 | ==== 137 | 138 | I use Debian, so I describe what I did. Your system probably is different, but maybe this will at least give you an idea in which direction to go. 139 | 140 | Install for normal compilation 141 | ---- 142 | Install WxWidgets 143 | (http://wiki.wxwidgets.org/Installing_and_configuring_under_Ubuntu) 144 | 145 | #!/bin/bash 146 | # apt-get install libwxbase3.0-dev libwxgtk3.0-dev 147 | # cd /usr/include 148 | # ls | grep wx 149 | # ln -sv wx-3.0/wx wx 150 | 151 | Install for cross-compiling windows binaries on linux 152 | ---- 153 | Install mingw (cross) compiler 154 | 155 | #!/bin/bash 156 | # apt-get install mingw-w64 157 | 158 | Download wxWidgets source from http://www.wxwidgets.org/ 159 | (some forums mention that sources provided by ubuntu/debian are not good, 160 | after experiment it appeared that `apt-get source libwxbase3.0-dev`, 161 | doesn't contain several (external?) source files used by makefile e.g. png.c) 162 | 163 | Build wxWidgets with mingw 164 | 165 | #!/bin/bash 166 | # ./configure --prefix=/usr/i686-w64-mingw32 --host=i686-w64-mingw32 --build=`./config.guess` --enable-unicode --disable-shared 167 | # make 168 | # make install 169 | # cd /usr/i686-w64-mingw32/include 170 | # ln -sv wx-3.0/wx wx 171 | 172 | References 173 | ---- 174 | This was done by following instructions found in http://wiki.wxwidgets.org/Cross-Compiling_Under_Linux 175 | with a little adjustment by after reading http://forums.codeblocks.org/index.php?topic=7988.msg%msg_id% 176 | and http://old.nabble.com/mingwm10.dll-ts8920679.html 177 | 178 | -------------------------------------------------------------------------------- /bindings/pypyfsqlf.py: -------------------------------------------------------------------------------- 1 | """Python bindings for libfsqlf.""" 2 | 3 | 4 | from ctypes import CDLL, POINTER, c_char_p, c_int, c_void_p, byref 5 | 6 | 7 | class Fsqlf(object): 8 | """Ctypes wrapper around libfsqlf. 9 | 10 | Example: 11 | 12 | >>> with Fsqlf("~/fsqlf/builds/linux-x86/bin/libfsqlf.so") as fsqlf: 13 | >>> print(fsqlf.format(" select 4 from t where x in (select 1);")) 14 | SELECT 15 | 4 16 | FROM t 17 | WHERE x IN 18 | ( 19 | SELECT 20 | 1 21 | ) 22 | ; 23 | """ 24 | 25 | def __init__(self, libpath=None): 26 | """Initialize formatter. 27 | 28 | Note: Library allocates memory during `__init__`, 29 | so corresponding `cleanup` call is needed to release the memory. 30 | (Or use `with` syntax to make cleanup automatically) 31 | 32 | """ 33 | # Specify ctypes stuff. 34 | self._lib = CDLL(libpath) 35 | self._lib.fsqlf_kwmap_init.argtypes = [POINTER(c_void_p)] 36 | self._lib.fsqlf_kwmap_destroy.argtypes = [c_void_p] 37 | self._lib.fsqlf_format_bytes.argtypes = [c_void_p, c_char_p, c_int, POINTER(c_char_p)] 38 | 39 | # Init the keyword configuration. 40 | self._kwmap = c_void_p() 41 | self._lib.fsqlf_kwmap_init(byref(self._kwmap)) 42 | 43 | def format(self, sql_query): 44 | """Format sql query.""" 45 | result = c_char_p() 46 | self._lib.fsqlf_format_bytes(self._kwmap, c_char_p(sql_query), len(sql_query), byref(result)); 47 | return result.value 48 | 49 | def cleanup(self): 50 | """Free-up memory allocated during initialization. 51 | 52 | Formatter is not usable after cleanup. 53 | """ 54 | self._lib.fsqlf_kwmap_destroy(self._kwmap) 55 | 56 | def __enter__(self): 57 | return self 58 | 59 | def __exit__(self, type, value, tb): 60 | self.cleanup() 61 | 62 | 63 | def format(sql_query, libpath): 64 | """Format sql query. 65 | 66 | Convenience function that initializes and destroys formatter on each call. 67 | """ 68 | with Fsqlf(libpath) as fsqlf: 69 | print(fsqlf.format(sql_query)) 70 | -------------------------------------------------------------------------------- /cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fsqlf_cli) 2 | # Sql formatter on command line. 3 | 4 | 5 | set(fsqlf_sources 6 | cli.c 7 | main.c 8 | ../utils/string/read_int.c) 9 | 10 | 11 | add_executable(fsqlf ${fsqlf_sources}) 12 | set_target_properties(fsqlf PROPERTIES C_STANDARD 99) 13 | target_include_directories(fsqlf PRIVATE ${CMAKE_SOURCE_DIR}/include) 14 | target_link_libraries(fsqlf libfsqlf_static) 15 | 16 | 17 | add_custom_target(formatting.conf ALL 18 | COMMAND $ --create-config-file ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/formatting.conf 19 | WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 20 | DEPENDS fsqlf) 21 | 22 | 23 | install(TARGETS fsqlf DESTINATION bin) 24 | install(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/formatting.conf 25 | DESTINATION share/fsqlf 26 | RENAME formatting.conf.example) 27 | -------------------------------------------------------------------------------- /cli/cli.c: -------------------------------------------------------------------------------- 1 | #include // isdigit 2 | #include // exit 3 | #include // fprintf, stderr 4 | #include 5 | #include "../utils/string/read_int.h" // FSQLF_read_int 6 | #include "cli.h" 7 | 8 | 9 | 10 | 11 | #define FAIL_WITH_ERROR( CODE , MESSAGE , ... ) \ 12 | do { \ 13 | fprintf(stderr, "\n" MESSAGE "\n", __VA_ARGS__ ); \ 14 | exit( CODE ); \ 15 | } while (0) 16 | 17 | 18 | #define PRINT_OPTION_INFO( OPTION_TEXT , OPTION_DESCRIPTION ) \ 19 | fprintf(stderr, " " OPTION_TEXT "\n " OPTION_DESCRIPTION "\n") 20 | 21 | 22 | #define ARGV_MATCH(INDEX, TEXT) (strcmp(argv[INDEX], TEXT) == 0) 23 | 24 | 25 | static void usage_info(int argc, char **argv) 26 | { 27 | fprintf(stderr, "usage:\n" ); 28 | PRINT_OPTION_INFO( "fsqlf [] [] [options]", 29 | "Read from and write formatted output to (use std I/O if missing)\n" 30 | " If there are overlaping options set, then the last one (overlapping setting) wins.\n" 31 | " e.g. If config file is set 2 times, then from 1st file use only configs that don't exist in the 2nd file."); 32 | PRINT_OPTION_INFO( "fsqlf --create-config-file ", "Create config file. (file is overwritten if already exists)"); 33 | fprintf(stderr, "options:\n"); 34 | PRINT_OPTION_INFO( "-i " , "Use as input"); 35 | PRINT_OPTION_INFO( "-o " , "Use as output"); 36 | PRINT_OPTION_INFO( "--config-file " , "Read configuration from "); 37 | PRINT_OPTION_INFO( "--select-comma-newline (after|before|none)" , "New lines for each item in SELECT clause"); 38 | PRINT_OPTION_INFO( "--select-newline-after " , "Put new lines right after SELECT keyword"); 39 | PRINT_OPTION_INFO( "--newline-or-before " , "Put new lines before OR keyword"); 40 | PRINT_OPTION_INFO( "--newline-or-after " , "Put new lines before OR keyword"); 41 | PRINT_OPTION_INFO( "--newline-and-before " , "Put new lines before AND keyword"); 42 | PRINT_OPTION_INFO( "--newline-and-after " , "Put new lines before AND keyword"); 43 | PRINT_OPTION_INFO( "--newline-major-sections " , "Put new lines before major sections (FROM, JOIN, WHERE)"); 44 | PRINT_OPTION_INFO( "--keyword-case (upper|lower|initcap|none)" , "Convert all keywords to UPPER, lower, or Initcap case, or not to convert case at all"); 45 | PRINT_OPTION_INFO( "--keyword-text (original|default)" , "Use original or programs default text for the keyword, when there are several alternatives"); 46 | PRINT_OPTION_INFO( "--help, -h" , "Show this help."); 47 | } 48 | 49 | 50 | // Get argument and convert it to integer 51 | static int get_int_arg(int i, int argc, char **argv) 52 | { 53 | int r; 54 | if (!FSQLF_read_int(argv[i], 1000, &r)) { 55 | FAIL_WITH_ERROR(1, "Missing or invalid value for option : %s", argv[i-1]); 56 | } 57 | return r; 58 | } 59 | 60 | 61 | void read_cli_options(struct fsqlf_kw_conf *kwall, int argc, char **argv, 62 | FILE **fin, FILE **fout) 63 | { 64 | int i; 65 | if (argc == 1) return; // use stdin and stdout 66 | 67 | if (argc == 2 && strcmp(argv[1], "--create-config-file") == 0) { 68 | FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[1]); 69 | } 70 | if (argc == 3 && strcmp(argv[1], "--create-config-file") == 0) { 71 | if (fsqlf_kwmap_conffile_create(argv[2]) != FSQLF_OK) { 72 | fprintf(stderr, "Problem occurred during creation of config file '%s'.\n", argv[2]); 73 | exit(1); 74 | } else { 75 | fprintf(stderr, "Configuration was written to file '%s'.\n", argv[2]); 76 | exit(0); 77 | } 78 | } 79 | 80 | for (i = 1; i < argc; i++) { 81 | if (argv[i][0] != '-') { 82 | if ((*fin) == stdin) { 83 | //try to openinig INPUT file 84 | (*fin) = fopen(argv[1], "r"); 85 | if (!(*fin)) { 86 | FAIL_WITH_ERROR(1, "Error opening input file: %s", argv[i]); 87 | } 88 | } 89 | else if ((*fout) == stdout) { //try to openinig OUTPUT file (only if INPUT file is set) 90 | (*fout) = fopen(argv[2], "w+"); 91 | if (!(*fout)) FAIL_WITH_ERROR(1, "Error opening output file: %s", argv[i]); 92 | } 93 | } else if (ARGV_MATCH(i, "-i")) { 94 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 95 | (*fin) = fopen(argv[i], "r"); 96 | if (!(*fin)) FAIL_WITH_ERROR(1, "Error opening input file: %s", argv[i]); 97 | } else if (ARGV_MATCH(i, "-o")) { 98 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 99 | (*fout) = fopen(argv[i], "w+"); 100 | if (!(*fout)) FAIL_WITH_ERROR(1, "Error opening output file: %s", argv[i]); 101 | } else if (ARGV_MATCH(i, "--config-file")) { 102 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 103 | if (fsqlf_kwmap_conffile_read(kwall, argv[i]) == FSQLF_FAIL) { 104 | FAIL_WITH_ERROR(1, "Error reading configuration file: %s", argv[i]); 105 | } 106 | } else if (ARGV_MATCH(i, "--select-comma-newline")) { 107 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 108 | if (strcmp(argv[i], "after") == 0) { 109 | fsqlf_kw_get(kwall, "kw_comma")->before.new_line = 0; 110 | fsqlf_kw_get(kwall, "kw_comma")->after.new_line = 1; 111 | } else if (strcmp(argv[i], "before") == 0) { 112 | fsqlf_kw_get(kwall, "kw_comma")->before.new_line = 1; 113 | fsqlf_kw_get(kwall, "kw_comma")->after.new_line = 0; 114 | } else if (strcmp(argv[i], "none") == 0) { 115 | fsqlf_kw_get(kwall, "kw_comma")->before.new_line = 0; 116 | fsqlf_kw_get(kwall, "kw_comma")->after.new_line = 0; 117 | } 118 | } else if (ARGV_MATCH(i, "--keyword-case")) { 119 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 120 | if (strcmp(argv[i], "none") == 0) { 121 | fsqlf_kwmap_set_case(kwall, FSQLF_KWCASE_ORIGINAL); 122 | } else if (strcmp(argv[i], "upper") == 0) { 123 | fsqlf_kwmap_set_case(kwall, FSQLF_KWCASE_UPPER); 124 | } else if (strcmp(argv[i], "lower") == 0) { 125 | fsqlf_kwmap_set_case(kwall, FSQLF_KWCASE_LOWER); 126 | } else if (strcmp(argv[i], "initcap") == 0) { 127 | fsqlf_kwmap_set_case(kwall, FSQLF_KWCASE_INITCAP); 128 | } 129 | } else if (ARGV_MATCH(i, "--keyword-text")) { 130 | if (++i >= argc) FAIL_WITH_ERROR(1, "Missing value for option : %s", argv[i-1]); 131 | if (strcmp(argv[i], "original") == 0) { 132 | fsqlf_kwmap_set_spelling(kwall, FSQLF_KWSPELLING_USE_ORIGINAL); 133 | } else if (strcmp(argv[i], "default") == 0) { 134 | fsqlf_kwmap_set_spelling(kwall, FSQLF_KWSPELLING_USE_HARDCODED_DEFAULT); 135 | } 136 | } else if (ARGV_MATCH(i, "--select-newline-after")) { 137 | fsqlf_kw_get(kwall, "kw_select")->after.new_line = get_int_arg(++i, argc, argv); 138 | } else if (ARGV_MATCH(i, "--newline-or-before")) { 139 | fsqlf_kw_get(kwall, "kw_or")->before.new_line = get_int_arg(++i, argc, argv); 140 | } else if (ARGV_MATCH(i, "--newline-or-after")) { 141 | fsqlf_kw_get(kwall, "kw_or")->after.new_line = get_int_arg(++i, argc, argv); 142 | } else if (ARGV_MATCH(i, "--newline-and-before")) { 143 | fsqlf_kw_get(kwall, "kw_and")->before.new_line = get_int_arg(++i, argc, argv); 144 | } else if (ARGV_MATCH(i, "--newline-and-after")) { 145 | fsqlf_kw_get(kwall, "kw_and")->after.new_line = get_int_arg(++i, argc, argv); 146 | } else if (ARGV_MATCH(i, "--newline-major-sections")) { 147 | int new_line_count = get_int_arg(++i, argc, argv); 148 | fsqlf_kwmap_set_major_clause_nl(kwall, new_line_count); 149 | } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { 150 | usage_info(argc, argv); 151 | exit(0); 152 | } else FAIL_WITH_ERROR(1, "Option `%s' is not recognised or used incorrectly.\nTry `%s --help' for more information\n", argv[i], argv[0]); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /cli/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_H 2 | #define CLI_H 3 | 4 | 5 | #include // fprintf, FILE 6 | #include 7 | 8 | 9 | void read_cli_options(struct fsqlf_kw_conf *kwall, int argc, char **argv, 10 | FILE **fin, FILE **fout); 11 | 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /cli/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cli.h" // read_cli_options 3 | 4 | 5 | int main(int argc, char **argv) 6 | { 7 | // Initialise with STD I/O (later can be changed by command line options). 8 | FILE *fin, *fout; 9 | fin = stdin; 10 | fout = stdout; 11 | fsqlf_kwmap_t kwmap; 12 | 13 | fsqlf_kwmap_init(&kwmap); // Init default configs. 14 | fsqlf_kwmap_conffile_read_default(kwmap); // Read configs from file. 15 | read_cli_options(kwmap, argc, argv, &fin, &fout); // Read configs from command line. 16 | 17 | fsqlf_format_file(kwmap, fin, fout); 18 | 19 | fsqlf_kwmap_destroy(kwmap); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /cmake/toolchain-linux-x86-64-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_VERSION 1) 3 | set(CMAKE_SYSTEM_PROCESSOR "x86-64") 4 | 5 | 6 | set(CMAKE_C_COMPILER gcc) 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64" CACHE STRING "c flags") 8 | set(CMAKE_CXX_COMPILER g++) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64" CACHE STRING "c++ flags") 10 | 11 | 12 | set(CMAKE_FIND_ROOT_PATH /usr/) 13 | 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 18 | -------------------------------------------------------------------------------- /cmake/toolchain-linux-x86-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_VERSION 1) 3 | set(CMAKE_SYSTEM_PROCESSOR "x86") 4 | 5 | 6 | set(CMAKE_C_COMPILER gcc) 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") 8 | set(CMAKE_CXX_COMPILER g++) 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") 10 | 11 | 12 | set(CMAKE_FIND_ROOT_PATH /usr/) 13 | 14 | 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 18 | -------------------------------------------------------------------------------- /cmake/toolchain-windows-x86-mingw.cmake: -------------------------------------------------------------------------------- 1 | # This script is needed, to cross-compile from linux to windows. 2 | # (compile on linux computer, but produce windows executables) 3 | 4 | 5 | # Name of the target operating system. 6 | SET(CMAKE_SYSTEM_NAME Windows) 7 | 8 | 9 | # Compilers for C and C++. 10 | SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 11 | SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 12 | SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres) 13 | 14 | 15 | # Here is the target environment located. 16 | SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 17 | 18 | 19 | # Adjust where FIND_XXX() commands search: 20 | # - headers and libraries in target environment. 21 | # - programs in host environment. 22 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 25 | 26 | 27 | # references: 28 | # http://www.vtk.org/Wiki/CMake_Cross_Compiling 29 | 30 | 31 | # cmake 2.8.0 has bug regarding cross-compiling. 32 | # file: /usr/share/cmake-2.8/Modules/FindwxWidgets.cmake 33 | # It was fixed on 2014-08-22: 34 | # https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=33286235048495ceafb636d549d9a4e8891967ae 35 | # -if(WIN32 AND NOT CYGWIN AND NOT MSYS) 36 | # +if(WIN32 AND NOT CYGWIN AND NOT MSYS AND NOT CMAKE_CROSSCOMPILING) 37 | # set(wxWidgets_FIND_STYLE "win32") 38 | # else() 39 | # - if(UNIX OR MSYS) 40 | # - set(wxWidgets_FIND_STYLE "unix") 41 | # - endif() 42 | # + set(wxWidgets_FIND_STYLE "unix") 43 | # endif() 44 | # Also see: http://opencpn.org/ocpn/node/306 45 | -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fsqlf_gui) 2 | # Sql formatter GUI. 3 | 4 | 5 | # Ordering is important for mingw: "adv" must go before "core". 6 | find_package(wxWidgets REQUIRED adv core base aui) 7 | include(${wxWidgets_USE_FILE}) 8 | set(wxWidgets_EXCLUDE_COMMON_LIBRARIES TRUE) 9 | 10 | 11 | # Remove -mthreads, so there would be no need for mingwm10.dll. 12 | list(REMOVE_ITEM wxWidgets_LIBRARIES -mthreads) 13 | string(REGEX REPLACE "-mthreads" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | 15 | 16 | set(wx_fsqlf_sources 17 | basic_notepad.cpp 18 | dnd_target.cpp 19 | wx_fsqlf.cpp 20 | license_text.h) 21 | set(wx_fsqlf_includes 22 | PRIVATE ${CMAKE_SOURCE_DIR}/include 23 | PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 24 | add_executable(wx_fsqlf ${wx_fsqlf_sources}) 25 | set_target_properties(fsqlf PROPERTIES CXX_STANDARD 11) 26 | target_include_directories(wx_fsqlf ${wx_fsqlf_includes}) 27 | target_link_libraries(wx_fsqlf ${wxWidgets_LIBRARIES} libfsqlf_static) 28 | 29 | 30 | # "About" window: version string from git. 31 | set(git_cmd "git") 32 | set(git_arg1 "describe") 33 | set(git_arg2 "master") 34 | execute_process(COMMAND ${git_cmd} ${git_arg1} ${git_arg2} 35 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 36 | OUTPUT_VARIABLE git_describe1) 37 | string(STRIP ${git_describe1} git_describe2) 38 | message(STATUS "git describe master: ${git_describe2} (not portable)") 39 | add_definitions(-DVERSION="${git_describe2}") 40 | 41 | 42 | # "About" window: license text. 43 | add_executable(text_to_header EXCLUDE_FROM_ALL 44 | ${CMAKE_SOURCE_DIR}/utils/text_to_header/text_to_header.c) 45 | add_custom_command(OUTPUT license_text.h 46 | COMMAND 47 | $ 48 | ${CMAKE_SOURCE_DIR}/LICENSE license_text.h 49 | LICENSE_TEXT 50 | DEPENDS text_to_header 51 | COMMENT "Generating: license_text.h") 52 | 53 | 54 | install(TARGETS wx_fsqlf DESTINATION bin) 55 | -------------------------------------------------------------------------------- /gui/basic_notepad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "basic_notepad.hpp" 4 | 5 | #include "fsqlf_right.xpm" 6 | #include "license_text.h" 7 | 8 | 9 | enum EventIds 10 | { 11 | idSave = wxID_HIGHEST, 12 | idOpen, 13 | idExit, 14 | idAbout, 15 | idCut, 16 | idCopy, 17 | idPaste, 18 | idSelectAll, 19 | idMaxLen 20 | }; 21 | 22 | 23 | BEGIN_EVENT_TABLE(BasicNotepad, wxFrame) 24 | EVT_MENU(idSave, BasicNotepad::onSave) 25 | EVT_MENU(idOpen, BasicNotepad::onOpen) 26 | EVT_MENU(idExit, BasicNotepad::onExit) 27 | 28 | EVT_MENU(idCut , BasicNotepad::onCut) 29 | EVT_MENU(idCopy , BasicNotepad::onCopy) 30 | EVT_MENU(idPaste , BasicNotepad::onPaste) 31 | EVT_MENU(idSelectAll , BasicNotepad::onSelectAll) 32 | 33 | EVT_TEXT_MAXLEN(idMaxLen, BasicNotepad::onMaxLen) 34 | 35 | 36 | EVT_MENU(idAbout, BasicNotepad::onAbout) 37 | END_EVENT_TABLE() 38 | 39 | 40 | BasicNotepad::BasicNotepad(const wxString title): 41 | wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(650,500)), 42 | text_area(new wxTextCtrl(this, wxID_ANY, _(""), wxDefaultPosition, 43 | wxDefaultSize, wxTE_PROCESS_ENTER | wxTE_MULTILINE)), 44 | dnd_target(new DndTarget(this->text_area)), 45 | file_menu(new wxMenu()), 46 | edit_menu(new wxMenu()), 47 | help_menu(new wxMenu()) 48 | { 49 | SetIcon(wxIcon(fsqlf_right)); 50 | this->create_menubar(); 51 | } 52 | 53 | 54 | void BasicNotepad::create_textarea(wxSizer* parent_sizer) 55 | { 56 | text_area->SetFont(wxFont(10, wxTELETYPE, wxNORMAL, wxNORMAL)); 57 | parent_sizer->Add(this->text_area,1,wxEXPAND,0); 58 | } 59 | 60 | 61 | void BasicNotepad::create_menubar() 62 | { 63 | wxMenuBar* menu; 64 | 65 | menu = new wxMenuBar(); 66 | 67 | menu->Append(file_menu, _("&File")); 68 | menu->Append(edit_menu, _("&Edit")); 69 | menu->Append(help_menu, _("&Help")); 70 | 71 | file_menu->Append(idSave, _("&Save File\tCtrl-S")); 72 | file_menu->Append(idOpen, _("&Open File\tCtrl-O")); 73 | file_menu->AppendSeparator(); 74 | file_menu->Append(idExit, _("E&xit\tAlt-F4")); 75 | 76 | edit_menu->Append(idCut , _("Cu&t\tCtrl-X")); 77 | edit_menu->Append(idCopy , _("&Copy\tCtrl-C")); 78 | edit_menu->Append(idPaste , _("&Paste\tCtrl-V")); 79 | edit_menu->AppendSeparator(); 80 | edit_menu->Append(idSelectAll, _("Select &All\tCtrl-A")); 81 | 82 | help_menu->Append(idAbout, _("&About...\tAlt-F1")); 83 | 84 | this->SetMenuBar(menu); 85 | } 86 | 87 | 88 | void BasicNotepad::onSave( wxCommandEvent &event) 89 | { 90 | wxFileDialog *saveDialog = new wxFileDialog(this, _("Save File~"), _(""), _(""), _("SQL (*.sql)|*.sql|All (*.*)|*.*"), wxFD_SAVE); 91 | if (wxID_OK == saveDialog->ShowModal()) this->text_area->SaveFile(saveDialog->GetPath()); 92 | } 93 | 94 | 95 | void BasicNotepad::onOpen( wxCommandEvent &event) 96 | { 97 | wxFileDialog *openDialog = new wxFileDialog(this, _("Open File~"), _(""), _(""), _("SQL (*.sql)|*.sql|All (*.*)|*.*"), wxFD_OPEN); 98 | if (wxID_OK == openDialog->ShowModal()) this->text_area->LoadFile(openDialog->GetPath()); 99 | } 100 | 101 | 102 | void BasicNotepad::onExit(wxCommandEvent &event) 103 | { 104 | //this->Destroy(); 105 | } 106 | 107 | 108 | void BasicNotepad::onCopy(wxCommandEvent &event) 109 | { 110 | this->text_area->Copy(); 111 | } 112 | 113 | 114 | void BasicNotepad::onCut(wxCommandEvent &event) 115 | { 116 | this->text_area->Cut(); 117 | } 118 | 119 | 120 | void BasicNotepad::onPaste(wxCommandEvent &event) 121 | { 122 | this->text_area->Paste(); 123 | } 124 | 125 | 126 | void BasicNotepad::onSelectAll(wxCommandEvent &event) 127 | { 128 | this->text_area->SetSelection(-1, -1); 129 | } 130 | 131 | 132 | void BasicNotepad::onAbout(wxCommandEvent &event) 133 | { 134 | wxAboutDialogInfo info; 135 | info.SetName(_("Free SQL Formatter")); 136 | info.SetVersion(_(VERSION)); 137 | info.SetDescription(_T("Free SQL Formatter beautifies SQL code. It is particularly useful in case one has to deal with machine generated SQL code")); 138 | info.SetCopyright(_T("(C) 2011-2016 Danas Mikelinskas ")); 139 | info.SetLicence(wxString(LICENSE_TEXT, wxConvUTF8, LICENSE_TEXT_LEN)); 140 | wxAboutBox(info); 141 | } 142 | 143 | 144 | void BasicNotepad::onMaxLen(wxCommandEvent &event) 145 | { 146 | new wxMessageDialog(this, _("Maximum Limit Reached"), _(""), wxOK|wxCENTRE ); 147 | } 148 | -------------------------------------------------------------------------------- /gui/basic_notepad.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BASIC_NOTEPAD_HPP 2 | #define BASIC_NOTEPAD_HPP 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "dnd_target.hpp" 11 | 12 | 13 | class BasicNotepad: public wxFrame 14 | { 15 | protected: 16 | wxTextCtrl* text_area; 17 | DndTarget* dnd_target; 18 | protected: 19 | wxMenu *file_menu, *edit_menu, *help_menu; 20 | 21 | public: 22 | BasicNotepad(const wxString title); 23 | void create_textarea(wxSizer* parent); 24 | 25 | private: 26 | void create_menubar(); 27 | 28 | void onSave(wxCommandEvent &event); 29 | void onOpen(wxCommandEvent &event); 30 | void onExit(wxCommandEvent &event); 31 | void onCopy(wxCommandEvent &event); 32 | void onCut(wxCommandEvent &event); 33 | void onPaste(wxCommandEvent &event); 34 | void onSelectAll(wxCommandEvent &event); 35 | void onAbout(wxCommandEvent &event); 36 | void onMaxLen(wxCommandEvent &event); 37 | 38 | DECLARE_EVENT_TABLE() 39 | }; 40 | 41 | 42 | //endif BASIC_NOTEPAD_HPP 43 | #endif 44 | -------------------------------------------------------------------------------- /gui/dnd_target.cpp: -------------------------------------------------------------------------------- 1 | #include "dnd_target.hpp" 2 | 3 | 4 | bool DndTarget::OnDropFiles(wxCoord x, wxCoord y, 5 | const wxArrayString &filenames) 6 | { 7 | this->text_area->LoadFile(filenames[0]); 8 | } 9 | 10 | 11 | DndTarget::DndTarget(wxTextCtrl* text_area) 12 | { 13 | this->text_area = text_area; 14 | this->text_area->SetDropTarget(this); 15 | } 16 | -------------------------------------------------------------------------------- /gui/dnd_target.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DND_TARGET_HPP 2 | #define DND_TARGET_HPP 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | // Drag and drop target 10 | // Note: 11 | // Notepad it self can not ingerit from wxFileDropTarget 12 | // because this causes segfault at exit 13 | // (See also: http://stackoverflow.com/a/7096992/788634) 14 | class DndTarget: public wxFileDropTarget 15 | { 16 | private: 17 | wxTextCtrl* text_area; 18 | bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames); 19 | 20 | public: 21 | DndTarget(wxTextCtrl* text_area); 22 | }; 23 | 24 | 25 | //endif DND_TARGET_HPP 26 | #endif 27 | -------------------------------------------------------------------------------- /gui/fsqlf_right.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char *fsqlf_right_xpm[] = { 3 | /* columns rows colors chars-per-pixel */ 4 | "48 48 6 1", 5 | " c #050000", 6 | ". c #006609", 7 | "X c #170050", 8 | "o c #FEFBFF", 9 | "O c #FFFFFF", 10 | "+ c None", 11 | /* pixels */ 12 | "++++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++++", 13 | "++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++", 14 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 15 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 16 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 17 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 18 | "OXXXXXXXXOOOXXXXXXOOOOXXXXXOOOXXXXXXOOOXXXXXXXXO", 19 | "OXXXXXXXXOXXXXXXXXOOXXXXXXXXXOXXXXXXOOOXXXXXXXXO", 20 | "OXXXXXXXXOXXXXXXXXOOXXXXXXXXXOOXXXXXOOOXXXXXXXXO", 21 | "OXXXXXXXXOXXXXXXXXOXXXXXXXXXXXOXXXXXOOOXXXXXXXXO", 22 | "OXXXXXOOOOXXXXXXOOOXXXXXOXXXXXOXXXXXOOOXXXXXOOOO", 23 | "OXXXXXOOOOXXXXXXOOOXXXXOOOXXXXOXXXXXOOOXXXXXOOOO", 24 | "OXXXXXOOOOOXXXXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXOOOO", 25 | "OXXXXXXXXOOOOXXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXXXXO", 26 | "OXXXXXXXXOOOOOXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXXXXO", 27 | "OXXXXXOOOOOOOOXXXOOOXXXXOXXXXOOXXXXXOOOXXXXXOOOO", 28 | "OXXXXXOOOOOXXXXXXOOOOXXXXXXXOOOXXXXXXXOXXXXXOOOO", 29 | "OXXXXXOOOOOXXXXXOOOOOOXXXXXOOOOXXXXXXXOXXXXXOOOO", 30 | "OOOOOOOOOOOOOOOOOOOOOOOOOXXXXOOOOOOOOOOOOOOOOOOO", 31 | "OOOOOOOOOOOOOOOOOOOOOOOOOXXXOOOOOOOOOOOOOOOOOOOO", 32 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 33 | "OOOOOOOOOOOOOOOOOOOOOOOO O", 34 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 35 | "OOOOOOOOOOOOOOOOOOOOOOOO O O OOOOOOOOOOOOO O", 36 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 37 | "OOOOOOOOOOOOOOOOOOOOOOOO O O OOOOOOOOOOO O", 38 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 39 | "OOOOOOOOOOOOOOOOOOOOOOOO OO O O O OOOOOO O", 40 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 41 | "OOOOOOOOOOOOOOOOOOOOOOOO O O oO O OOOOO O", 42 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 43 | "OOOOOOOOOOOOOOOOOOOOOOOO O O O O OOOOOO O", 44 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 45 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOooOOOOOOOOOOOOOO O", 46 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO.O......O.....OO O", 47 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 48 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO....OO........OO O", 49 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 50 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO.......OO...O.OO O", 51 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 52 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 53 | "OOOOOOOOOOOOOOOOOOOOOOOO O", 54 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 55 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 56 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 57 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 58 | "++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++", 59 | "++++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++++" 60 | }; 61 | 62 | /* XPM */ 63 | static const char *fsqlf_right[] = { 64 | /* columns rows colors chars-per-pixel */ 65 | "48 48 6 1", 66 | " c #050000", 67 | ". c #006609", 68 | "X c #170050", 69 | "o c #FEFBFF", 70 | "O c #FFFFFF", 71 | "+ c None", 72 | /* pixels */ 73 | "++++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++++", 74 | "++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++", 75 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 76 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 77 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 78 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 79 | "OXXXXXXXXOOOXXXXXXOOOOXXXXXOOOXXXXXXOOOXXXXXXXXO", 80 | "OXXXXXXXXOXXXXXXXXOOXXXXXXXXXOXXXXXXOOOXXXXXXXXO", 81 | "OXXXXXXXXOXXXXXXXXOOXXXXXXXXXOOXXXXXOOOXXXXXXXXO", 82 | "OXXXXXXXXOXXXXXXXXOXXXXXXXXXXXOXXXXXOOOXXXXXXXXO", 83 | "OXXXXXOOOOXXXXXXOOOXXXXXOXXXXXOXXXXXOOOXXXXXOOOO", 84 | "OXXXXXOOOOXXXXXXOOOXXXXOOOXXXXOXXXXXOOOXXXXXOOOO", 85 | "OXXXXXOOOOOXXXXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXOOOO", 86 | "OXXXXXXXXOOOOXXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXXXXO", 87 | "OXXXXXXXXOOOOOXXXOOXXXXOOOXXXXOXXXXXOOOXXXXXXXXO", 88 | "OXXXXXOOOOOOOOXXXOOOXXXXOXXXXOOXXXXXOOOXXXXXOOOO", 89 | "OXXXXXOOOOOXXXXXXOOOOXXXXXXXOOOXXXXXXXOXXXXXOOOO", 90 | "OXXXXXOOOOOXXXXXOOOOOOXXXXXOOOOXXXXXXXOXXXXXOOOO", 91 | "OOOOOOOOOOOOOOOOOOOOOOOOOXXXXOOOOOOOOOOOOOOOOOOO", 92 | "OOOOOOOOOOOOOOOOOOOOOOOOOXXXOOOOOOOOOOOOOOOOOOOO", 93 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 94 | "OOOOOOOOOOOOOOOOOOOOOOOO O", 95 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 96 | "OOOOOOOOOOOOOOOOOOOOOOOO O O OOOOOOOOOOOOO O", 97 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 98 | "OOOOOOOOOOOOOOOOOOOOOOOO O O OOOOOOOOOOO O", 99 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 100 | "OOOOOOOOOOOOOOOOOOOOOOOO OO O O O OOOOOO O", 101 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 102 | "OOOOOOOOOOOOOOOOOOOOOOOO O O oO O OOOOO O", 103 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 104 | "OOOOOOOOOOOOOOOOOOOOOOOO O O O O OOOOOO O", 105 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 106 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOooOOOOOOOOOOOOOO O", 107 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO.O......O.....OO O", 108 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 109 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO....OO........OO O", 110 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 111 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOO.......OO...O.OO O", 112 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 113 | "OOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOO O", 114 | "OOOOOOOOOOOOOOOOOOOOOOOO O", 115 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 116 | "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 117 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 118 | "+OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO+", 119 | "++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++", 120 | "++++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO++++" 121 | }; 122 | -------------------------------------------------------------------------------- /gui/wx_fsqlf.cpp: -------------------------------------------------------------------------------- 1 | #include "wx_fsqlf.hpp" 2 | 3 | 4 | extern "C" { 5 | #include "lib_fsqlf.h" 6 | } 7 | 8 | 9 | // define version if it was not passed as an argument in compilation command 10 | #ifndef VERSION 11 | #define VERSION "custom-version" 12 | #endif 13 | 14 | 15 | enum EventIds 16 | { 17 | idFormat = wxID_HIGHEST, 18 | idUnformat, 19 | idUseConfigNlOther 20 | }; 21 | 22 | 23 | BEGIN_EVENT_TABLE(FsqlfGui, BasicNotepad) 24 | EVT_BUTTON(idFormat, FsqlfGui::onFormat) 25 | EVT_BUTTON(idUnformat, FsqlfGui::onUnformat) 26 | 27 | EVT_MENU(idFormat , FsqlfGui::onFormat) 28 | EVT_MENU(idUnformat , FsqlfGui::onUnformat) 29 | 30 | EVT_CHECKBOX(idUseConfigNlOther, FsqlfGui::onUseConfigNlOther) 31 | END_EVENT_TABLE() 32 | 33 | 34 | #define TITLE "wx Free SQL Formatter" 35 | 36 | 37 | FsqlfGui::FsqlfGui(): BasicNotepad(_(TITLE)) 38 | { 39 | wxNotebook* nb = new wxNotebook(this, wxID_ANY); 40 | wxBoxSizer* sizerh = new wxBoxSizer(wxHORIZONTAL); 41 | wxBoxSizer* left_sizer = new wxBoxSizer(wxVERTICAL); 42 | 43 | this->SetSizer(sizerh); 44 | sizerh->Add(left_sizer,0,0,0); 45 | 46 | FsqlfGui::create_buttons(left_sizer); 47 | FsqlfGui::create_textarea(sizerh); 48 | 49 | left_sizer->Add(nb,0,0,0); 50 | FsqlfGui::create_options(nb); 51 | 52 | this->edit_menu->AppendSeparator(); 53 | this->edit_menu->Append(idFormat, _("&Format\tCtrl-F")); 54 | this->edit_menu->Append(idUnformat, _("&Unformat\tCtrl-U")); 55 | } 56 | 57 | 58 | void FsqlfGui::create_buttons(wxSizer* parent_sizer) 59 | { 60 | wxWindow* window = parent_sizer->GetContainingWindow(); 61 | parent_sizer->Add(new wxButton(window, idFormat, _("Format")), 0, 0, 0); 62 | parent_sizer->Add(new wxButton(window, idUnformat, _("Unformat")), 0, 0, 0); 63 | } 64 | 65 | 66 | void FsqlfGui::create_options(wxNotebook* nb) 67 | { 68 | wxPanel* parent_panel = new wxPanel(nb); 69 | wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); 70 | parent_panel->SetSizer(sizer); 71 | nb->AddPage(parent_panel, _("Options")); 72 | 73 | create_options_nl_comma(sizer); 74 | create_options_nl_keywords(sizer); 75 | create_options_nl_major_sections(sizer); 76 | create_options_text(sizer); 77 | } 78 | 79 | 80 | void add_newcheckbox(wxCheckBox* &result_checkbox, 81 | wxSizer* parent_sizer, 82 | wxString title, 83 | bool default_val, 84 | bool enabled = true, 85 | int id = -1) 86 | { 87 | result_checkbox = new wxCheckBox(parent_sizer->GetContainingWindow(), id, title); 88 | result_checkbox->SetValue(default_val); 89 | if (!enabled) result_checkbox->Disable(); 90 | parent_sizer->Add(result_checkbox, 0, 0, 0); 91 | } 92 | 93 | 94 | void FsqlfGui::create_options_nl_comma(wxSizer* parent_sizer) 95 | { 96 | // Radio buttons - new lines in SELECT clause 97 | #define NUM_COMMA_NL_CHOICES (4) 98 | wxString choices[NUM_COMMA_NL_CHOICES]; 99 | choices[0] = _("Use Config File"); 100 | choices[1] = _("Before"); 101 | choices[2] = _("After"); 102 | choices[3] = _("None"); 103 | 104 | sel_comma_nl = new wxRadioBox(parent_sizer->GetContainingWindow(), wxID_ANY 105 | , _("New line:[comma]"), wxDefaultPosition, wxDefaultSize 106 | , NUM_COMMA_NL_CHOICES, choices, 1, wxRA_SPECIFY_COLS); 107 | sel_comma_nl->SetSelection(0); 108 | parent_sizer->Add(sel_comma_nl,0,0,0); 109 | } 110 | 111 | 112 | void FsqlfGui::create_options_nl_keywords(wxSizer* parent_sizer) 113 | { 114 | // Check boxes for : OR , AND , SELECT 115 | wxStaticBox * box = new wxStaticBox(parent_sizer->GetContainingWindow(), 116 | wxID_ANY, _("New line:[other]")); 117 | wxStaticBoxSizer* sizer = new wxStaticBoxSizer(box, wxVERTICAL); 118 | parent_sizer->Add(sizer,0,0,0); 119 | 120 | add_newcheckbox(nl_use_config , sizer, _("Use config") , true , true, 121 | idUseConfigNlOther); 122 | add_newcheckbox(nl_after_select, sizer, _("[select] after"), true , false); 123 | add_newcheckbox(nl_before_or , sizer, _("[or] before") , false, false); 124 | add_newcheckbox(nl_after_or , sizer, _("[or] after") , false, false); 125 | add_newcheckbox(nl_before_and , sizer, _("[and] before") , true , false); 126 | add_newcheckbox(nl_after_and , sizer, _("[and] after") , false, false); 127 | } 128 | 129 | 130 | void FsqlfGui::create_options_nl_major_sections(wxSizer* parent_sizer) 131 | { 132 | #define NUM_CHOICES (3) 133 | wxString choices[NUM_CHOICES]; 134 | choices[0] = _("Use Config File"); 135 | choices[1] = _("1 New Line"); 136 | choices[2] = _("2 New Lines"); 137 | nl_major_sections = new wxRadioBox(parent_sizer->GetContainingWindow(), 138 | wxID_ANY, _("Major sections"), wxDefaultPosition, wxDefaultSize, 139 | NUM_CHOICES, choices, 1, wxRA_SPECIFY_COLS); 140 | nl_major_sections->SetSelection(0); 141 | parent_sizer->Add(nl_major_sections,0,0,0); 142 | } 143 | 144 | 145 | void FsqlfGui::create_options_text(wxSizer* parent_sizer) 146 | { 147 | add_newcheckbox(use_original_text, parent_sizer, _("Use original keyword text"), false); 148 | 149 | // CASE settings 150 | wxString choices[4]; 151 | choices[0] = _("None (aBc)"); 152 | choices[1] = _("Upper (ABC)"); 153 | choices[2] = _("Lower (abc)"); 154 | choices[3] = _("Init (Abc)"); 155 | case_all_kw = new wxRadioBox(parent_sizer->GetContainingWindow(), wxID_ANY, 156 | _("Keyword case"), wxDefaultPosition, wxDefaultSize, 157 | 4, choices, 1, wxRA_SPECIFY_COLS); 158 | case_all_kw->SetSelection(1); 159 | parent_sizer->Add(case_all_kw,0,0,0); 160 | } 161 | 162 | 163 | void FsqlfGui::onFormat(wxCommandEvent &event) 164 | { 165 | this->original_text = this->text_area->GetValue(); 166 | 167 | // Init kwmap and set it according to gui-controls. 168 | fsqlf_kwmap_t kwmap; 169 | fsqlf_kwmap_init(&kwmap); 170 | fsqlf_kwmap_conffile_read_default(kwmap); 171 | switch (this->sel_comma_nl->GetSelection()) { 172 | case 1: 173 | fsqlf_kw_get(kwmap, "kw_comma")->before.new_line = 1; 174 | fsqlf_kw_get(kwmap, "kw_comma")->after.new_line = 0; 175 | break; 176 | case 2: 177 | fsqlf_kw_get(kwmap, "kw_comma")->before.new_line = 0; 178 | fsqlf_kw_get(kwmap, "kw_comma")->after.new_line = 1; 179 | break; 180 | case 3: 181 | fsqlf_kw_get(kwmap, "kw_comma")->before.new_line = 0; 182 | fsqlf_kw_get(kwmap, "kw_comma")->after.new_line = 0; 183 | break; 184 | } 185 | 186 | if (this->nl_use_config->GetValue() == 0) { 187 | if (this->nl_after_select->GetValue()) { 188 | fsqlf_kw_get(kwmap, "kw_select")->after.new_line = 1; 189 | } else { 190 | fsqlf_kw_get(kwmap, "kw_select")->after.new_line = 0; 191 | } 192 | 193 | if (this->nl_before_or->GetValue()) { 194 | fsqlf_kw_get(kwmap, "kw_or")->before.new_line = 1; 195 | } else { 196 | fsqlf_kw_get(kwmap, "kw_or")->before.new_line = 0; 197 | } 198 | 199 | if (this->nl_after_or->GetValue()) { 200 | fsqlf_kw_get(kwmap, "kw_or")->after.new_line = 1; 201 | } else { 202 | fsqlf_kw_get(kwmap, "kw_or")->after.new_line = 0; 203 | } 204 | 205 | if (this->nl_before_and->GetValue()) { 206 | fsqlf_kw_get(kwmap, "kw_and")->before.new_line = 1; 207 | } else { 208 | fsqlf_kw_get(kwmap, "kw_and")->before.new_line = 0; 209 | } 210 | 211 | if (this->nl_after_and->GetValue()) { 212 | fsqlf_kw_get(kwmap, "kw_and")->after.new_line = 1; 213 | } else { 214 | fsqlf_kw_get(kwmap, "kw_and")->after.new_line = 0; 215 | } 216 | } 217 | 218 | switch (this->nl_major_sections->GetSelection()) { 219 | case 0: break; // Use config file. 220 | case 1: fsqlf_kwmap_set_major_clause_nl(kwmap, 1); break; 221 | case 2: fsqlf_kwmap_set_major_clause_nl(kwmap, 2); break; 222 | } 223 | 224 | if (this->use_original_text->GetValue()) { 225 | fsqlf_kwmap_set_spelling(kwmap, FSQLF_KWSPELLING_USE_ORIGINAL); 226 | } else { 227 | fsqlf_kwmap_set_spelling(kwmap, FSQLF_KWSPELLING_USE_HARDCODED_DEFAULT); 228 | } 229 | 230 | switch (this->case_all_kw->GetSelection()) { 231 | case 0: fsqlf_kwmap_set_case(kwmap, FSQLF_KWCASE_ORIGINAL); break; 232 | case 1: fsqlf_kwmap_set_case(kwmap, FSQLF_KWCASE_UPPER); break; 233 | case 2: fsqlf_kwmap_set_case(kwmap, FSQLF_KWCASE_LOWER); break; 234 | case 3: fsqlf_kwmap_set_case(kwmap, FSQLF_KWCASE_INITCAP); break; 235 | } 236 | 237 | // Actual formatting. 238 | wxCharBuffer buffer = this->original_text.ToUTF8(); 239 | const char *input = buffer.data(); 240 | char *output; 241 | fsqlf_format_bytes(kwmap, input, strlen(input), &output); 242 | this->text_area->SetValue(wxString(output, wxConvUTF8)); 243 | 244 | // Cleanup of kwmap. 245 | free(output); 246 | fsqlf_kwmap_destroy(kwmap); 247 | } 248 | 249 | 250 | void FsqlfGui::onUnformat(wxCommandEvent &event) 251 | { 252 | if (this->original_text.IsEmpty()) return; // prevent deletion of everything 253 | this->text_area->Clear(); 254 | this->text_area->SetValue(this->original_text); 255 | } 256 | 257 | 258 | void FsqlfGui::onUseConfigNlOther(wxCommandEvent &event) 259 | { 260 | if (event.IsChecked()) { 261 | this->nl_after_select->Disable(); 262 | this->nl_before_or->Disable(); 263 | this->nl_after_or->Disable(); 264 | this->nl_before_and->Disable(); 265 | this->nl_after_and->Disable(); 266 | } else { 267 | this->nl_after_select->Enable(); 268 | this->nl_before_or->Enable(); 269 | this->nl_after_or->Enable(); 270 | this->nl_before_and->Enable(); 271 | this->nl_after_and->Enable(); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /gui/wx_fsqlf.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WX_FSQLF_HPP 2 | #define WX_FSQLF_HPP 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "basic_notepad.hpp" 15 | 16 | 17 | class FsqlfGui: public BasicNotepad 18 | { 19 | public: 20 | FsqlfGui(); 21 | 22 | private: 23 | wxString original_text; 24 | 25 | wxRadioBox *sel_comma_nl, *case_all_kw, *nl_major_sections; 26 | wxCheckBox* nl_after_select; 27 | wxCheckBox *nl_use_config; 28 | wxCheckBox *nl_before_or, *nl_after_or; 29 | wxCheckBox *nl_before_and, *nl_after_and; 30 | wxCheckBox* use_original_text; 31 | 32 | void create_buttons(wxSizer* parent_sizer); 33 | void create_options(wxNotebook*); 34 | void create_options_nl_comma(wxSizer*); 35 | void create_options_nl_keywords(wxSizer*); 36 | void create_options_nl_major_sections(wxSizer*); 37 | void create_options_text(wxSizer* sizer); 38 | 39 | void onUnformat(wxCommandEvent &event); 40 | void onFormat(wxCommandEvent &event); 41 | void onUseConfigNlOther(wxCommandEvent &event); 42 | 43 | DECLARE_EVENT_TABLE() 44 | }; 45 | 46 | 47 | class MainApp : public wxApp 48 | { 49 | public: 50 | virtual bool OnInit() { 51 | FsqlfGui *main = new FsqlfGui(); 52 | main->Show(true); 53 | 54 | return true; 55 | } 56 | }; 57 | 58 | 59 | IMPLEMENT_APP(MainApp) 60 | 61 | 62 | //endif WX_FSQLF_HPP 63 | #endif 64 | -------------------------------------------------------------------------------- /include/lib_fsqlf.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FSQLF_H 2 | #define LIB_FSQLF_H 3 | 4 | 5 | #include // FILE 6 | #include "../utils/map/uthash.h" // UT_hash_handle, HASH_ADD_KEYPTR, HASH_FIND_STR 7 | 8 | 9 | // Return value (in case function returns status code). 10 | // At the moment only OK and FAIL, 11 | // but later might add some more granular codes. 12 | enum fsqlf_status 13 | { 14 | FSQLF_OK = 0, 15 | FSQLF_FAIL = 1 16 | }; 17 | 18 | 19 | enum fsqlf_kwcase 20 | { 21 | // Use whatever was in the input. 22 | FSQLF_KWCASE_ORIGINAL = 0, 23 | 24 | // Example: "select". 25 | FSQLF_KWCASE_LOWER = 1, 26 | 27 | // Example: "SELECT". 28 | FSQLF_KWCASE_UPPER = 2, 29 | 30 | // Example: "Select". 31 | FSQLF_KWCASE_INITCAP = 3 32 | }; 33 | 34 | 35 | // Keyword text variation to be used. 36 | // (e.g. "left outer join" vs "left join") 37 | enum fsqlf_kwspelling 38 | { 39 | // Use whatever was in the input. 40 | FSQLF_KWSPELLING_USE_ORIGINAL = 0, 41 | 42 | // Convert some keywords to fsqlf defaults. 43 | // Defaults are hard-coded in "kw_defaults.def". 44 | // Current defaults prefer: 45 | // - full words (e.g. convert "sel" to "select") 46 | // - lower word count (e.g. convert "left outer join" to "left join") 47 | FSQLF_KWSPELLING_USE_HARDCODED_DEFAULT = 1, 48 | }; 49 | 50 | 51 | struct fsqlf_spacing 52 | { 53 | int global_indent_change; 54 | int new_line; 55 | int indent; 56 | int space; 57 | }; 58 | 59 | 60 | struct FSQLF_out_buffer 61 | { 62 | char *buffer; 63 | size_t len_alloc; // allocated length 64 | size_t len_used; 65 | }; 66 | 67 | 68 | struct fsqlf_kw_conf 69 | { 70 | struct fsqlf_spacing before; 71 | struct fsqlf_spacing after; 72 | 73 | enum fsqlf_kwspelling print_original_text; 74 | enum fsqlf_kwcase print_case; 75 | unsigned short int is_word; // two adjacent words MUST be separated by some spacing 76 | char *text; 77 | 78 | const char *name; 79 | UT_hash_handle hh; // makes this structure hashable 80 | }; 81 | 82 | 83 | // Typedef to be used when map of all 'kw's is needed. 84 | typedef struct fsqlf_kw_conf * fsqlf_kwmap_t; 85 | 86 | 87 | #ifdef STATIC_LIBFSQLF 88 | # define FSQLF_API 89 | #elif defined(_WIN32) 90 | # ifdef BUILDING_LIBFSQLF 91 | # define FSQLF_API __declspec(dllexport) 92 | # else 93 | # define FSQLF_API __declspec(dllimport) 94 | # endif 95 | #else 96 | # define FSQLF_API 97 | #endif 98 | 99 | 100 | // Create and retrieve new struct fsqlf_kw_conf and add it to global map. 101 | void FSQLF_kw_create(fsqlf_kwmap_t *kwmap, const char *name); 102 | FSQLF_API struct fsqlf_kw_conf * fsqlf_kw_get(fsqlf_kwmap_t kwmap, const char *name); 103 | 104 | // Init and destroy kwmap 105 | FSQLF_API void fsqlf_kwmap_init(fsqlf_kwmap_t *kwmap); 106 | FSQLF_API void fsqlf_kwmap_destroy(fsqlf_kwmap_t kwmap); 107 | 108 | // Set case for keyword text. 109 | FSQLF_API void fsqlf_kwmap_set_case(fsqlf_kwmap_t kwmap, enum fsqlf_kwcase keyword_case); 110 | 111 | // Set used variation of keyword text. (e.g. "left outer join" vs "left join") 112 | FSQLF_API void fsqlf_kwmap_set_spelling(fsqlf_kwmap_t kwmap, enum fsqlf_kwspelling kw_text_to_use); 113 | 114 | // Set new line count before major clauses (from, join, where) 115 | FSQLF_API void fsqlf_kwmap_set_major_clause_nl(fsqlf_kwmap_t kwmap, int nl_count); 116 | 117 | 118 | // Create & read formatting configuration file. 119 | #ifndef FSQLF_CONFFILE_NAME 120 | #define FSQLF_CONFFILE_NAME "formatting.conf" 121 | #endif 122 | 123 | 124 | #ifndef FSQLF_CONFFILE_LINELENGTH 125 | #define FSQLF_CONFFILE_LINELENGTH (100) 126 | #endif 127 | 128 | 129 | FSQLF_API enum fsqlf_status fsqlf_kwmap_conffile_create(char *config_file_name); 130 | FSQLF_API enum fsqlf_status fsqlf_kwmap_conffile_read(fsqlf_kwmap_t kwmap, const char *file); 131 | // "read_default" looks in working directory or "~/.fslqf" for formatting.conf. 132 | FSQLF_API enum fsqlf_status fsqlf_kwmap_conffile_read_default(fsqlf_kwmap_t kwmap); 133 | 134 | 135 | // Actual formatting 136 | FSQLF_API void fsqlf_format_file(fsqlf_kwmap_t kwmap, FILE *fin, FILE *fout); 137 | FSQLF_API void fsqlf_format_bytes(fsqlf_kwmap_t kwmap, 138 | const char *bytes_in, int len, char **bytes_out 139 | ); 140 | 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /lib_fsqlf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fsqlf_lib) 2 | # Formatter as library - shared and static. 3 | 4 | 5 | find_package(FLEX) 6 | FLEX_TARGET(lexyy formatter/fsqlf.lex ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c 7 | COMPILE_FLAGS --header-file=${CMAKE_CURRENT_BINARY_DIR}/lex.yy.h) 8 | 9 | 10 | set(libfsqlf_sources 11 | ${FLEX_lexyy_OUTPUTS} 12 | ../utils/queue/queue.c 13 | ../utils/stack/stack.c 14 | ../utils/string/read_int.c 15 | conf_file/conf_file_create.c 16 | conf_file/conf_file_read.c 17 | formatter/lex_wrapper.c 18 | formatter/print_keywords.c 19 | formatter/tokque.c 20 | kw/is_word.c 21 | kw/kw.c 22 | kw/kwmap.c 23 | lex/token.c) 24 | set(libfsqlf_includes 25 | PRIVATE ${CMAKE_SOURCE_DIR}/include 26 | PRIVATE ${CMAKE_CURRENT_BINARY_DIR} # lex.yy.h 27 | PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/formatter) # tokque.h 28 | 29 | 30 | add_library(libfsqlf SHARED ${libfsqlf_sources}) 31 | set_target_properties(libfsqlf PROPERTIES C_STANDARD 99) 32 | set_target_properties(libfsqlf PROPERTIES LIBRARY_OUTPUT_NAME fsqlf) 33 | target_compile_definitions(libfsqlf PRIVATE BUILDING_LIBFSQLF) 34 | target_include_directories(libfsqlf ${libfsqlf_includes}) 35 | 36 | 37 | add_library(libfsqlf_static STATIC ${libfsqlf_sources}) 38 | set_target_properties(libfsqlf_static PROPERTIES C_STANDARD 99) 39 | set_target_properties(libfsqlf_static PROPERTIES LIBRARY_OUTPUT_NAME fsqlf_static) 40 | target_compile_definitions(libfsqlf_static PUBLIC STATIC_LIBFSQLF) 41 | target_include_directories(libfsqlf_static ${libfsqlf_includes}) 42 | 43 | 44 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include) 45 | install(TARGETS libfsqlf 46 | ARCHIVE DESTINATION lib 47 | LIBRARY DESTINATION lib 48 | RUNTIME DESTINATION bin) 49 | install(TARGETS libfsqlf_static 50 | ARCHIVE DESTINATION lib 51 | LIBRARY DESTINATION lib 52 | RUNTIME DESTINATION bin) 53 | -------------------------------------------------------------------------------- /lib_fsqlf/conf_file/conf_file_create.c: -------------------------------------------------------------------------------- 1 | #include // FILE, fprintf, fputs, fclose 2 | #include 3 | 4 | 5 | // Create formatting configuration file with default content. 6 | // TODO: write from kwmap instead of stuff from "../kw/kwmap_defaults.def" 7 | enum fsqlf_status fsqlf_kwmap_conffile_create(char *config_file_name) 8 | { 9 | FILE *config_file; 10 | config_file = fopen(config_file_name, "w"); 11 | 12 | if (!config_file) { 13 | fprintf(stderr, "Failed to create '%s' file!\n", config_file_name); 14 | return FSQLF_FAIL; 15 | } 16 | 17 | fputs("# This file contains formatting (spacing) settings,\n", config_file); 18 | fputs("# which can be used to override the default formatting style of FSQLF.\n", config_file); 19 | fputs("#\n", config_file); 20 | fputs("# Lines that are ignored (in this file):\n", config_file); 21 | fputs("# - Lines starting with '#' are treated as comments, thus ignored.\n", config_file); 22 | fputs("# - Last line is ignored (blank line or comment at the end of file is needed).\n", config_file); 23 | fputs("# - Empty lines are ignored (they fall into invalid line category).\n", config_file); 24 | fputs("# - Invalid config lines are ignored (see below for details).\n", config_file); 25 | fputs("#\n", config_file); 26 | fputs("# Valid config lines satisfy following criteria:\n", config_file); 27 | fputs("# - Contains `setting name` followed by 6 integer values.\n", config_file); 28 | fputs("# - Line begins with `setting name` (no leading spacing).\n", config_file); 29 | fputs("# - Values are separated by spaces (not tabs, not commas, not anything else).\n", config_file); 30 | fputs("# - `Setting name` should match one of predefined FSQLF setting names.\n", config_file); 31 | fprintf(config_file, "# - Line limit is %d characters (longer lines are ignored).\n", FSQLF_CONFFILE_LINELENGTH); 32 | fputs("# (if some criteria is violated, then line is invalid - thus ignored).\n", config_file); 33 | fputs("#\n", config_file); 34 | fputs("# If there are couple of valid lines with same setting_name,\n" , config_file); 35 | fputs("# then only the last one has effect.\n" , config_file); 36 | fputs("\n", config_file); 37 | fputs("\n", config_file); 38 | 39 | fputs("# space_after ------------------------------------------------------------+\n",config_file); 40 | fputs("# tab_after -------------------------------------------------------+ |\n",config_file); 41 | fputs("# new_line_after -------------------------------------------+ | |\n",config_file); 42 | fputs("# global_indent_change_after-------------------------+ | | |\n",config_file); 43 | fputs("# | | | |\n",config_file); 44 | fputs("# space_before -------------------------------+ | | | |\n",config_file); 45 | fputs("# tab_before --------------------------+ | | | | |\n",config_file); 46 | fputs("# new_line_before --------------+ | | | | | |\n",config_file); 47 | fputs("# global_indent_ | | | | | | |\n",config_file); 48 | fputs("# _change_before --------+ | | | | | | |\n",config_file); 49 | fputs("# | | | | | | | |\n",config_file); 50 | fputs("# setting_name | | | | | | | |\n",config_file); 51 | 52 | // Define macro to print one line containing config of single keyword 53 | // and run it (via #include) for each keyword. 54 | #define XMACRO(NAME, gib, nlb, tb, sb, gia, nla, ta, sa, ... ) \ 55 | fprintf(config_file, "%-24s %s %6s %6s %6s %6s %6s %6s %6s\n", \ 56 | #NAME, #gib, #nlb, #tb, #sb, #gia, #nla, #ta, #sa); 57 | #include "../kw/kwmap_defaults.def" 58 | #undef XMACRO 59 | 60 | fputs("\n\n", config_file); 61 | fputs("# Some explanations regarding names (shortenings) used above:\n", config_file); 62 | fputs("# - left_p = left parenthesis\n", config_file); 63 | fputs("# - right_p = right parenthesis\n", config_file); 64 | fputs("# - ins = insert\n", config_file); 65 | fputs("# - sub = subquery\n", config_file); 66 | fputs("# - grpby = group by\n", config_file); 67 | fputs("# - ordby = order by\n", config_file); 68 | fputs("# - fsqlf_kw_get = keyword (though for some reason it's used also for punctuation character..)\n", config_file); 69 | fputs("\n\n", config_file); 70 | 71 | if (fclose(config_file) == 0) { 72 | return FSQLF_OK; 73 | } else { 74 | fprintf(stderr, "Failed to close '%s' file!\n", config_file_name); 75 | return FSQLF_FAIL; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib_fsqlf/conf_file/conf_file_read.c: -------------------------------------------------------------------------------- 1 | #include // fopen, fclose 2 | #include // strcmp, strchr, strncat, strncpy, strlen 3 | #include // assert 4 | #include // stat 5 | #include 6 | #include "../../utils/string/read_int.h" 7 | 8 | 9 | static int file_exists(const char *filename) 10 | { 11 | struct stat buffer; 12 | return stat(filename, &buffer) == 0; 13 | } 14 | 15 | 16 | static void set_from_array(struct fsqlf_kw_conf *kwall, const char *name, const int *values) 17 | { 18 | struct fsqlf_kw_conf *k; 19 | k = fsqlf_kw_get(kwall, name); 20 | if (k != NULL) { 21 | k->before.global_indent_change = values[0]; 22 | k->before.new_line = values[1]; 23 | k->before.indent = values[2]; 24 | k->before.space = values[3]; 25 | k->after.global_indent_change = values[4]; 26 | k->after.new_line = values[5]; 27 | k->after.indent = values[6]; 28 | k->after.space = values[7]; 29 | } 30 | } 31 | 32 | 33 | // Read specified config file. 34 | enum fsqlf_status fsqlf_kwmap_conffile_read(struct fsqlf_kw_conf *kwall, const char *file) 35 | { 36 | FILE *config_file = fopen(file, "r"); 37 | if (!config_file) { 38 | return FSQLF_FAIL; 39 | } 40 | 41 | char line[FSQLF_CONFFILE_LINELENGTH+1]; 42 | char setting_name[FSQLF_CONFFILE_LINELENGTH+1]; 43 | 44 | while (fgets(line, FSQLF_CONFFILE_LINELENGTH, config_file)) { 45 | // Lines starting with '#' are comments: skip them. 46 | // If line doesn't fit into buffer, it is invalid: skip it. 47 | if (line[0] == '#' || !strchr(line, '\n')) { 48 | do { 49 | // Skip chunks until new line is found 50 | if (!fgets(line, FSQLF_CONFFILE_LINELENGTH, config_file)) break; 51 | } while (!strchr(line, '\n')); 52 | // Skip that also, as it is continuation 53 | // of same line that we want to skip 54 | continue; 55 | } 56 | 57 | size_t llen = strlen(line); 58 | 59 | // Read setting name. 60 | char *space_ptr = strchr(line, ' '); 61 | if (!space_ptr) continue; 62 | space_ptr[0] = '\0'; 63 | strncpy(setting_name, line, FSQLF_CONFFILE_LINELENGTH); 64 | 65 | // Read numeric setting values. 66 | // nl before, tab before, space before, nl after, tab after, space after 67 | char *pos = space_ptr + 1; // Skip \0 char. 68 | llen -= pos - line; 69 | const int VALUE_COUNT = 8; 70 | int setting_values[VALUE_COUNT]; 71 | size_t cnt = FSQLF_read_int_array(pos, llen, VALUE_COUNT, setting_values); 72 | if (cnt == 0) { 73 | continue; 74 | } 75 | set_from_array(kwall, setting_name, setting_values); 76 | } 77 | 78 | fclose(config_file); 79 | return FSQLF_OK; 80 | } 81 | 82 | 83 | static char * get_path_to_user_folder_conf() 84 | { 85 | // Get path to conf file in user (home) directory. 86 | 87 | // ..get all the ingredients. 88 | #ifndef _WIN32 89 | const char *home_dir = getenv("HOME"); 90 | #else 91 | const char *home_dir = getenv("USERPROFILE"); 92 | #endif 93 | if (home_dir == NULL) return NULL; 94 | const char *fsqlf_sub = "/.fsqlf/"; 95 | const char *conf_file = FSQLF_CONFFILE_NAME; 96 | 97 | // ..get complete path to conf file. 98 | size_t full_len = strlen(home_dir) + strlen(fsqlf_sub) 99 | + strlen(conf_file) + 1; 100 | char * full_path = malloc(full_len); 101 | if (full_path == NULL) return NULL; 102 | strncpy(full_path, home_dir, full_len); 103 | strncat(full_path, fsqlf_sub, full_len - strlen(full_path)); 104 | strncat(full_path, conf_file, full_len - strlen(full_path)); 105 | return full_path; 106 | } 107 | 108 | 109 | // Read configuration file from default conf file. 110 | // (working directory and "fsqlf/" under user dir) 111 | enum fsqlf_status fsqlf_kwmap_conffile_read_default(struct fsqlf_kw_conf *kwall) 112 | { 113 | // First try file in working directory. 114 | if (file_exists(FSQLF_CONFFILE_NAME)) { 115 | return fsqlf_kwmap_conffile_read(kwall, FSQLF_CONFFILE_NAME); 116 | } 117 | 118 | char *full_path = get_path_to_user_folder_conf(); 119 | if (full_path == NULL) return FSQLF_FAIL; 120 | 121 | // Read the file. 122 | enum fsqlf_status ret_code = fsqlf_kwmap_conffile_read(kwall, full_path); 123 | 124 | // Cleanup. 125 | free(full_path); 126 | 127 | return ret_code; 128 | } 129 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/fsqlf.lex: -------------------------------------------------------------------------------- 1 | /* 2 | Thanks to http://vsbabu.org/software/lsqlb.html 3 | Vattekkat Satheesh Babu, on Dec 30, 1998 4 | Helped to learn about flex a bit 5 | */ 6 | 7 | %top{ 8 | // This code goes at the "top" of the generated file. 9 | // Also it gets into generated header file. 10 | #include // fprintf, stdin, stdout 11 | #include "tokque.h" // FSQLF_tokque_putthrough 12 | #include "../../utils/stack/stack.h" // struct FSQLF_stack 13 | 14 | 15 | typedef struct 16 | { 17 | int left; 18 | int right; 19 | } pair; 20 | 21 | 22 | struct fsqlf_formatter_state 23 | { 24 | // Global indent. (e.g. for subqueries and case expressions) 25 | int currindent; 26 | 27 | // Count of left and right paranthesis. 28 | int left_p; 29 | int right_p; 30 | 31 | // Lex state (start condition) stack. 32 | struct FSQLF_stack lexstate_stack; 33 | 34 | // Pairs of paranthesis stored at each start of subquery. 35 | struct FSQLF_stack sub_openings; 36 | 37 | // Configuration of all keywords. 38 | struct fsqlf_kw_conf *kwall; 39 | 40 | // Queue used for processing tokens. 41 | struct FSQLF_queue tqueue; 42 | 43 | // Output buffer. 44 | struct FSQLF_out_buffer bout; 45 | }; 46 | 47 | } 48 | 49 | 50 | %{ 51 | // This does not get into generated header file. 52 | 53 | #define BEGIN_STATE(NEWSTATE) BEGIN(NEWSTATE); 54 | 55 | #define PUSH_STATE(NEWSTATE) \ 56 | do { \ 57 | FSQLF_stack_push(&yyextra->lexstate_stack, &(int){YY_START}); \ 58 | BEGIN_STATE(NEWSTATE); \ 59 | } while (0) 60 | 61 | #define POP_STATE() \ 62 | do { \ 63 | BEGIN_STATE(*(int*)FSQLF_stack_peek(&yyextra->lexstate_stack)); \ 64 | FSQLF_stack_pop(&yyextra->lexstate_stack); \ 65 | } while (0) 66 | 67 | // Use KW with ability to change state. 68 | #define TUSE_W_STATES(TKW) \ 69 | do { \ 70 | struct FSQLF_state_change sc = FSQLF_tokque_putthrough( \ 71 | &yyextra->tqueue, \ 72 | yyout, &yyextra->bout, \ 73 | &yyextra->currindent, yytext, yyleng, TKW, YY_START \ 74 | ); \ 75 | if (sc.state_change_action == FSQLF_SCA_BEGIN) { \ 76 | BEGIN(sc.new_state); \ 77 | } \ 78 | } while (0) 79 | 80 | // Use KW without ability to change state. 81 | #define TUSE_SIMPLE(TKW) \ 82 | do { \ 83 | FSQLF_tokque_putthrough( \ 84 | &yyextra->tqueue, \ 85 | yyout, &yyextra->bout, \ 86 | &yyextra->currindent, yytext, yyleng, TKW, YY_START \ 87 | ); \ 88 | } while (0) 89 | 90 | %} 91 | 92 | 93 | DIGIT [0-9] 94 | NUMBER {DIGIT}+([.]{DIGIT}+)?([eE][+-]?{DIGIT}+)? 95 | ID [A-Za-z_][A-Za-z0-9_]* 96 | SPACE [ \t\n] 97 | DBOBJECT ({ID}[.]){0,2}{ID} 98 | 99 | 100 | LEFTP [(] 101 | RIGHTP [)] 102 | 103 | UNION (?i:union) 104 | UNION_ALL (?i:union{SPACE}+all) 105 | INTERSECT (?i:intersect) 106 | EXCEPT (?i:except) 107 | MINUS (?i:minus) 108 | 109 | TABLE_OPT (?i:global|volatile|set|multiset|temporary) 110 | CREATE (?i:create) 111 | DROP (?i:drop) 112 | TABLE (?i:table) 113 | VIEW (?i:view) 114 | IFEXISTS (?i:if{SPACE}+exists) 115 | SELECT (?i:select|sel) 116 | AS (?i:as) 117 | FROM (?i:from) 118 | USING (?i:using) 119 | 120 | IJOIN (?i:(inner{SPACE}+)?JOIN) 121 | LJOIN (?i:left{SPACE}+(OUTER{SPACE}+)?JOIN) 122 | RJOIN (?i:right{SPACE}+(OUTER{SPACE}+)?JOIN) 123 | FJOIN (?i:full{SPACE}+(OUTER{SPACE}+)?JOIN) 124 | CJOIN (?i:cross{SPACE}+JOIN{SPACE}+) 125 | ANYJOIN ({LJOIN}|{RJOIN}|{FJOIN}|{CJOIN}) 126 | 127 | ON (?i:on) 128 | WHERE (?i:where) 129 | SAMPLE (?i:sample) 130 | AND (?i:and) 131 | OR (?i:or) 132 | NOT (?i:not) 133 | EXISTS (?i:exists) 134 | IN (?i:in) 135 | LIKE (?i:like) 136 | 137 | GROUPBY (?i:group{SPACE}+by) 138 | ORDERBY (?i:order{SPACE}+by) 139 | HAVING (?i:having) 140 | QUALIFY (?i:qualify) 141 | 142 | COMMA [,] 143 | COMP_EQ (=) 144 | COMP_NE (<>) 145 | COMP_LE (<=) 146 | COMP_GE (>=) 147 | COMP_LT (<) 148 | COMP_GT (>) 149 | 150 | COMMENT_ONE_LINE [-]{2,}[^\n]*[\n]? 151 | COMMENT_ML [/][*]([^*]|[*]+[^*/])*[*]+[/] 152 | 153 | STRING ([xX]?['][^'']*['])+ 154 | SEMICOLON ; 155 | OP_PLUS (\+) 156 | OP_MINUS (-) 157 | OP_MULT (\*) 158 | OP_DIV (\/) 159 | OP_CONCAT (\|\|) 160 | 161 | 162 | INSERTINTO (?i:(ins|insert){SPACE}+into) 163 | UPDATE (?i:upd|update) 164 | SET (?i:set) 165 | DELETEFROM (?i:(del|delete){SPACE}+from) 166 | DELETE (?i:(del|delete)) 167 | 168 | CASE (?i:case) 169 | WHEN (?i:when) 170 | THEN (?i:then) 171 | ELSE (?i:else) 172 | END (?i:end) 173 | 174 | 175 | %option reentrant 176 | %option extra-type="struct fsqlf_formatter_state *" 177 | %option noyywrap 178 | %option nounput 179 | %option noinput 180 | /* always-interactive - removes usage of fileno, isatty (not C99 compliant). 181 | * See details at: 182 | * http://flex.sourceforge.net/manual/Why-do-flex-scanners-call-fileno-if-it-is-not-ANSI-compatible_003f.html 183 | */ 184 | %option always-interactive 185 | 186 | %s stSELECT stFROM stWHERE stON stEXISTS stLEFTP stJOIN stIN stCOMMA stINLIST stFROM_LEFTP stP_SUB stORDERBY stGROUPBY stINSERT stINSCOLLIST stUPDATE stSET stDELETE stIN_CONSTLIST stCREATE stTAB_COL_LIST 187 | 188 | %% 189 | 190 | {DELETEFROM} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_deletefrom")); } 191 | {DELETE} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_deletefrom")); } 192 | {UPDATE} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_update")); } 193 | {SET} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_set")); } 194 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_set")); } 195 | 196 | {DROP} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_drop")); } 197 | {IFEXISTS} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_ifexists")); } 198 | {USING} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_using")); } 199 | {TABLE} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_table")); } 200 | {VIEW} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_view")); } 201 | 202 | /* SET operations */ 203 | {UNION} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_union")); } 204 | {UNION_ALL} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_union_all")); } 205 | {MINUS} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_minus")); } 206 | {INTERSECT} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_intersect")); } 207 | {EXCEPT} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_except")); } 208 | 209 | /* comparison and arithmetic operators */ 210 | {IN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_in")); } 211 | {LIKE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_like")); }; 212 | {COMP_EQ} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_eq")); }; 213 | {COMP_NE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_ne")); }; 214 | {COMP_LE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_le")); }; 215 | {COMP_GE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_ge")); }; 216 | {COMP_LT} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_lt")); }; 217 | {COMP_GT} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comp_gt")); }; 218 | {NOT} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_not")); }; 219 | {EXISTS} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_exists")); }; 220 | {OP_PLUS} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_op_plus")); }; 221 | {OP_MINUS} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_op_minus")); }; 222 | {OP_MULT} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_op_mult")); }; 223 | {OP_DIV} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_op_div")); }; 224 | {OP_CONCAT} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_op_concat")); }; 225 | 226 | /* SELECT ... FROM */ 227 | {SELECT} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_select")); }; 228 | {COMMA} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_comma")); }; 229 | 230 | {LEFTP} { PUSH_STATE(stLEFTP); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_left_p")); yyextra->left_p++; }; 231 | {LEFTP} { PUSH_STATE(stLEFTP); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_left_p")); yyextra->left_p++; }; 232 | {COMMA} { TUSE_SIMPLE(NULL); }; 233 | {ORDERBY} { TUSE_SIMPLE(NULL); }; 234 | {FROM} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_from_2")); }; 235 | {RIGHTP} { POP_STATE(); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p")); yyextra->right_p++; }; 236 | {FROM} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_from")); }; 237 | {AS} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_as")); }; 238 | 239 | /* FROM ... JOIN ... ON ... WHERE ...*/ 240 | {IJOIN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_inner_join")); }; 241 | {LJOIN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_left_join")); }; 242 | {RJOIN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_right_join")); }; 243 | {FJOIN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_full_join")); }; 244 | {CJOIN} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_cross_join")); }; 245 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_join")); }; 246 | {ON} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_on")); }; 247 | {WHERE} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_where")); }; 248 | {AND} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_and")); }; 249 | {OR} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_or")); }; 250 | 251 | /* GROUPBY .. ORDER BY ... HAVING .. QUALIFY */ 252 | {GROUPBY} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_groupby")); } 253 | {ORDERBY} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_orderby")); } 254 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_ordby")); }; 255 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_grpby")); }; 256 | {HAVING} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_having")); } 257 | {QUALIFY} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_qualify")); } 258 | 259 | {INSERTINTO} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_insertinto")); } 260 | {LEFTP} { PUSH_STATE(stINSCOLLIST); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_left_p_ins")); }; 261 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_ins")); } 262 | {RIGHTP} { POP_STATE(); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p_ins")); }; 263 | 264 | {CREATE} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_create")); } 265 | {LEFTP} { PUSH_STATE(stTAB_COL_LIST); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_left_p_create")); }; 266 | {COMMA} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_comma_create")); } 267 | {RIGHTP} { POP_STATE(); TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p_create")); }; 268 | 269 | /* possible subselect */ 270 | {LEFTP} { 271 | BEGIN_STATE(*(int*)FSQLF_stack_peek(&yyextra->lexstate_stack)); 272 | TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_left_p")); 273 | yyextra->left_p++; 274 | PUSH_STATE(stP_SUB); 275 | }; 276 | {LEFTP} { PUSH_STATE(stP_SUB); }; 277 | {SELECT} { 278 | BEGIN_STATE(stSELECT); 279 | FSQLF_tokque_putthrough(&yyextra->tqueue, yyout, &yyextra->bout, &yyextra->currindent, "(", 1, fsqlf_kw_get(yyextra->kwall, "kw_left_p_sub"), YY_START); 280 | FSQLF_stack_push(&yyextra->sub_openings, &(pair){yyextra->left_p, yyextra->right_p}); // begin sub 281 | yyextra->currindent++; // begin sub 282 | TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_select")); 283 | }; 284 | {NUMBER}|{STRING}|{DBOBJECT} { 285 | if (*(int*)FSQLF_stack_peek(&yyextra->lexstate_stack) == stFROM 286 | || *(int*)FSQLF_stack_peek(&yyextra->lexstate_stack) == stJOIN) { 287 | BEGIN_STATE(*(int*)FSQLF_stack_peek(&yyextra->lexstate_stack)); 288 | FSQLF_tokque_putthrough(&yyextra->tqueue, yyout, &yyextra->bout, &yyextra->currindent, "(", 1, fsqlf_kw_get(yyextra->kwall, "kw_left_p"), YY_START); 289 | yyextra->left_p++; 290 | TUSE_SIMPLE(NULL); 291 | } else { 292 | BEGIN_STATE(stIN_CONSTLIST); 293 | FSQLF_tokque_putthrough(&yyextra->tqueue, yyout, &yyextra->bout, &yyextra->currindent, "(", 1, fsqlf_kw_get(yyextra->kwall, "kw_left_p"), YY_START); 294 | yyextra->left_p++; 295 | TUSE_SIMPLE(NULL); 296 | } 297 | }; 298 | {COMMENT_ML} { TUSE_SIMPLE(NULL); }; 299 | {COMMENT_ONE_LINE} { TUSE_SIMPLE(NULL); }; 300 | {SPACE} { }; 301 | {RIGHTP} { 302 | FSQLF_tokque_putthrough(&yyextra->tqueue, yyout, &yyextra->bout, &yyextra->currindent, "(", 1, fsqlf_kw_get(yyextra->kwall, "kw_left_p"), YY_START); 303 | yyextra->left_p++; 304 | POP_STATE(); 305 | TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p")); 306 | yyextra->right_p++; 307 | }; 308 | . { 309 | BEGIN_STATE(*(int*)FSQLF_stack_peek(&yyextra->lexstate_stack)); 310 | FSQLF_tokque_putthrough(&yyextra->tqueue, yyout, &yyextra->bout, &yyextra->currindent, "(", 1, fsqlf_kw_get(yyextra->kwall, "kw_left_p"), YY_START); 311 | yyextra->left_p++; 312 | TUSE_SIMPLE(NULL); 313 | }; 314 | 315 | {RIGHTP} { 316 | POP_STATE(); 317 | if (!FSQLF_stack_empty(&yyextra->sub_openings) && 318 | yyextra->left_p 319 | - (*(pair*)FSQLF_stack_peek(&yyextra->sub_openings)).left 320 | == 321 | (yyextra->right_p+1) 322 | - (*(pair*)FSQLF_stack_peek(&yyextra->sub_openings)).right 323 | - 1) { 324 | FSQLF_stack_pop(&yyextra->sub_openings); // end sub 325 | yyextra->currindent--; // end sub 326 | TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p_sub")); 327 | } else { 328 | TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_right_p")); 329 | yyextra->right_p++; 330 | } 331 | 332 | }; 333 | 334 | {CASE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_case")); } 335 | {WHEN} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_when")); } 336 | {THEN} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_then")); } 337 | {ELSE} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_else")); } 338 | {END} { TUSE_SIMPLE(fsqlf_kw_get(yyextra->kwall, "kw_end")); } 339 | 340 | {COMMENT_ML} { TUSE_SIMPLE(NULL); } 341 | {COMMENT_ONE_LINE} { TUSE_SIMPLE(NULL);}; 342 | 343 | {STRING} { TUSE_SIMPLE(NULL); } 344 | {SPACE}+ { /* discard spaces */; } 345 | {DBOBJECT} { TUSE_SIMPLE(NULL); } 346 | {NUMBER} { TUSE_SIMPLE(NULL); } 347 | {SEMICOLON} { TUSE_W_STATES(fsqlf_kw_get(yyextra->kwall, "kw_semicolon")); } 348 | <*>. { TUSE_SIMPLE(NULL); } 349 | 350 | 351 | <> { 352 | FSQLF_tokque_finish_out(&yyextra->tqueue, yyout, &yyextra->bout); 353 | fprintf(yyout, "\n"); 354 | return 0; 355 | } 356 | 357 | %% 358 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/lex_wrapper.c: -------------------------------------------------------------------------------- 1 | #include "lex.yy.h" 2 | #include 3 | 4 | 5 | void fsqlf_format_bytes(fsqlf_kwmap_t kwmap, 6 | const char *bytes_in, int len, char **bytes_out 7 | ) 8 | { 9 | struct fsqlf_formatter_state f_state; 10 | FSQLF_stack_init(&f_state.lexstate_stack, sizeof(int)); 11 | FSQLF_stack_init(&f_state.sub_openings, sizeof(pair)); 12 | FSQLF_tokque_init(&f_state.tqueue); 13 | f_state.currindent = 0; 14 | f_state.left_p = 0; 15 | f_state.right_p = 0; 16 | f_state.kwall = kwmap; 17 | f_state.bout.len_used = 0; 18 | f_state.bout.len_alloc = len * 1.5; 19 | f_state.bout.buffer = malloc(f_state.bout.len_alloc); 20 | 21 | yyscan_t scanner; 22 | yylex_init(&scanner); 23 | yy_scan_bytes(bytes_in, len, scanner); 24 | yyset_extra(&f_state, scanner); 25 | 26 | yylex(scanner); 27 | *bytes_out = f_state.bout.buffer; 28 | 29 | yylex_destroy(scanner); 30 | } 31 | 32 | 33 | void fsqlf_format_file(struct fsqlf_kw_conf *kwall, FILE *fin, FILE *fout) 34 | { 35 | struct fsqlf_formatter_state f_state; 36 | FSQLF_stack_init(&f_state.lexstate_stack, sizeof(int)); 37 | FSQLF_stack_init(&f_state.sub_openings, sizeof(pair)); 38 | FSQLF_tokque_init(&f_state.tqueue); 39 | f_state.currindent = 0; 40 | f_state.left_p = 0; 41 | f_state.right_p = 0; 42 | f_state.kwall = kwall; 43 | f_state.bout = (struct FSQLF_out_buffer){NULL, 0, 0}; 44 | 45 | yyscan_t scanner; 46 | yylex_init(&scanner); 47 | yyset_in(fin, scanner); 48 | yyset_out(fout, scanner); 49 | yyset_extra(&f_state, scanner); 50 | 51 | yylex(scanner); 52 | 53 | yylex_destroy(scanner); 54 | } 55 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/print_keywords.c: -------------------------------------------------------------------------------- 1 | #include // strlen 2 | #include // tolower, toupper 3 | #include // fprintf, fputs 4 | #include // assert 5 | #include "print_keywords.h" 6 | 7 | 8 | // Return max argument. 9 | // Strange name is chosen just to make it unique, 10 | // so it would not clash with some compiler/platform specific macros. 11 | static int max_2args_(int a, int b) 12 | { 13 | return a > b ? a : b; 14 | } 15 | 16 | 17 | // based on 3rd (bool) arg return 18 | // either max(1st, 2nd) 19 | // or 2nd arg 20 | static int max_or_current(int prev_count, int curr_count, char use_only_curr_ind) 21 | { 22 | if (use_only_curr_ind) { 23 | return curr_count; 24 | } 25 | else{ 26 | return max_2args_(prev_count, curr_count); 27 | } 28 | } 29 | 30 | 31 | static struct fsqlf_spacing calculate_spacing_pure( 32 | struct fsqlf_spacing afterspacing_of_prev, 33 | unsigned short int isword_of_prev, 34 | struct fsqlf_spacing beforespacing_of_current, 35 | unsigned short int isword_of_current, 36 | int global_indent_level) 37 | { 38 | /* Combine spacings of adjacent words (previous and current). 39 | Combinations are done in two ways: 40 | - take MAX 41 | - use only current words settings 42 | (one more option could be to 43 | first print previous word's 'after' spacing, 44 | then print current word's 'before' spacing, but this could/would 45 | result in trailing spaces on a line) 46 | Decision, which of them to use, is made so: 47 | There are 3 types of spacings. 48 | They are ranked this way: space-1, indentation-2, new line-3. 49 | If current word has some non-zero spacing setting, 50 | then for spacings of lower ranks use conf only from current word. 51 | Rationale for such logic: 52 | If word has in its settings new-line=1 and indent/spaces=0, 53 | then there is expectation that keyword will be 54 | at the start of the line, no matter what previous fsqlf_kw_get setting 55 | for indentation after it was. 56 | (except for global indent level, because of subselect) 57 | */ 58 | struct fsqlf_spacing r; // result to be built 59 | 60 | 61 | r.new_line = max_2args_(afterspacing_of_prev.new_line, beforespacing_of_current.new_line); 62 | 63 | r.indent = max_or_current( 64 | afterspacing_of_prev.indent 65 | , beforespacing_of_current.indent 66 | , beforespacing_of_current.new_line>1); 67 | r.indent += r.new_line ? global_indent_level : 0; 68 | 69 | r.space = max_or_current( 70 | afterspacing_of_prev.space 71 | , beforespacing_of_current.space 72 | , beforespacing_of_current.new_line || beforespacing_of_current.indent); 73 | 74 | // adjacent words MUST have some spacing 75 | if (!r.new_line && !r.indent && !r.space && 76 | isword_of_prev && isword_of_current) { 77 | r.space = 1; 78 | } 79 | 80 | return r; 81 | } 82 | 83 | 84 | static char *str_to_case(const char *s_text, enum fsqlf_kwcase s_case) 85 | { 86 | size_t len = strlen(s_text); 87 | char *formatted_result = malloc(len+1); 88 | assert(formatted_result); 89 | 90 | switch (s_case) { 91 | case FSQLF_KWCASE_LOWER: 92 | for (int i = 0; ibuffer) { 174 | fprintf(fout, "%s", spacing_txt); 175 | fprintf(fout, "%s", text); 176 | } else { 177 | size_t len_spacing = strlen(spacing_txt); 178 | size_t len_text = strlen(text); 179 | if (bout->len_used + len_spacing + len_text + 1 > bout->len_alloc) { 180 | size_t len_realloc = bout->len_alloc * 1.5; 181 | bout->buffer = realloc(bout->buffer, len_realloc); 182 | assert(bout->buffer); 183 | bout->len_alloc = len_realloc; 184 | } 185 | strncpy(bout->buffer + bout->len_used, spacing_txt, len_spacing); 186 | bout->len_used += len_spacing; 187 | strncpy(bout->buffer + bout->len_used, text, len_text); 188 | bout->len_used += len_text; 189 | bout->buffer[bout->len_used] = '\0'; 190 | } 191 | free(spacing_txt); 192 | } 193 | 194 | 195 | void FSQLF_print( 196 | FILE *fout, 197 | struct FSQLF_out_buffer *bout, 198 | size_t indent, 199 | const char *yytext, 200 | const struct fsqlf_kw_conf *kw, 201 | const struct fsqlf_kw_conf *prev_kw 202 | ) 203 | { 204 | assert(kw); 205 | const char *text_nocase = choose_kw_text(*kw, yytext); 206 | char *text = str_to_case(text_nocase, kw->print_case); 207 | 208 | struct fsqlf_spacing spacing = calculate_spacing_pure( 209 | prev_kw->after, 210 | prev_kw->is_word, 211 | kw->before, 212 | kw->is_word, 213 | indent); 214 | print_output(fout, bout, spacing, text); 215 | 216 | free(text); 217 | } 218 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/print_keywords.h: -------------------------------------------------------------------------------- 1 | #ifndef print_keywords_h 2 | #define print_keywords_h 3 | 4 | 5 | #include // struct fsqlf_kw_conf, fsqlf_kw_get 6 | 7 | 8 | void FSQLF_print( 9 | FILE *fout, 10 | struct FSQLF_out_buffer *bout, 11 | size_t indent, 12 | const char *text, 13 | const struct fsqlf_kw_conf *kw, 14 | const struct fsqlf_kw_conf *prev_kw 15 | ); 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/tokque.c: -------------------------------------------------------------------------------- 1 | #include // fprintf, fputs 2 | #include // assert 3 | #include // isalnum 4 | #include "../kw/is_word.h" 5 | #include "../lex/token.h" // struct FSQLF_token, FSQLF_clear_token, FSQLF_set_token 6 | #include "print_keywords.h" // FSQLF_print 7 | #include "tokque.h" 8 | #define YY_HEADER_EXPORT_START_CONDITIONS 9 | #include "lex.yy.h" // start conditions (states) 10 | 11 | 12 | static const size_t LOOK_BEHIND_COUNT = 1; 13 | 14 | 15 | void FSQLF_tokque_init(struct FSQLF_queue *tq) 16 | { 17 | FSQLF_queue_init(tq, sizeof(struct FSQLF_token)); 18 | } 19 | 20 | 21 | static struct fsqlf_kw_conf * dummy_kw_for_txt(char *txt, size_t length) 22 | { 23 | struct fsqlf_kw_conf *kw = calloc(1, sizeof(struct fsqlf_kw_conf)); 24 | 25 | // Adjustment necessary for single line comments, 26 | // for keeping indentation and new lines right. 27 | int pos_last_char = length - 1; 28 | if (txt[pos_last_char] == '\n') { 29 | // 'Move' ending new line from 'txtdup' to 'kw' 30 | txt[pos_last_char] = '\0'; 31 | kw->after.new_line = 1; 32 | } 33 | 34 | // Word-vs-operator check. 35 | // Ensure that two adjacent words have spacing inbetween. 36 | kw->is_word = FSQLF_is_word(txt, length); 37 | return kw; 38 | } 39 | 40 | 41 | static void tokque_put_kw( 42 | struct FSQLF_queue *tq, 43 | int *currindent, 44 | char *text, 45 | size_t len, 46 | struct fsqlf_kw_conf *kw0 47 | ) 48 | { 49 | // Note: de-allocation will be done based on token-class. 50 | enum FSQLF_token_class tcls = 51 | kw0 ? FSQLF_TOKEN_CLASS_KW : FSQLF_TOKEN_CLASS_TXT; 52 | struct fsqlf_kw_conf *kw = 53 | kw0 ? kw0 : dummy_kw_for_txt(text, len); 54 | 55 | struct FSQLF_token *tok = 56 | (struct FSQLF_token *) FSQLF_queue_alloc_back(tq); 57 | *currindent += kw->before.global_indent_change; 58 | FSQLF_set_token(tok, tcls, text, len, kw, (*currindent)); 59 | *currindent += kw->after.global_indent_change; 60 | } 61 | 62 | 63 | static int tokque_print_one(struct FSQLF_queue *tq, FILE *fout, 64 | struct FSQLF_out_buffer *bout) 65 | { 66 | if (FSQLF_queue_empty(tq)) { 67 | return 0; // queue was empty, so printing was not possible 68 | } else if (tq->length == 1) { 69 | struct FSQLF_token *tok; 70 | tok = (struct FSQLF_token *) FSQLF_queue_peek_n(tq, 0); 71 | struct fsqlf_kw_conf prev = {{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0}; 72 | FSQLF_print(fout, bout, tok->indent, tok->text, tok->kw_setting, &prev); 73 | return 1; // success - printing was made 74 | } else { 75 | assert(tq->length >= 2); 76 | struct FSQLF_token *tok, *prev; 77 | prev = (struct FSQLF_token *) FSQLF_queue_peek_n(tq, 0); 78 | tok = (struct FSQLF_token *) FSQLF_queue_peek_n(tq, 1); 79 | FSQLF_print(fout, bout, tok->indent, tok->text, tok->kw_setting, 80 | prev->kw_setting); 81 | if (prev->token_class == FSQLF_TOKEN_CLASS_TXT) { 82 | free(prev->kw_setting); 83 | } 84 | FSQLF_queue_drop_head(tq); 85 | FSQLF_clear_token(tok); 86 | return 1; // success - printing was made 87 | } 88 | } 89 | 90 | 91 | static struct FSQLF_state_change decide_new_state( 92 | int cur_state, 93 | const struct fsqlf_kw_conf *s 94 | ) 95 | { 96 | if (s) { 97 | if (strcmp(s->name, "kw_deletefrom") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stDELETE}; 98 | else if (strcmp(s->name, "kw_insertinto") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stINSERT}; 99 | else if (strcmp(s->name, "kw_update") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stUPDATE}; 100 | else if (strcmp(s->name, "kw_create") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stCREATE}; 101 | else if (strcmp(s->name, "kw_drop") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 102 | else if (strcmp(s->name, "kw_ifexists") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 103 | else if (strcmp(s->name, "kw_view") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 104 | else if (strcmp(s->name, "kw_union") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 105 | else if (strcmp(s->name, "kw_union_all") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 106 | else if (strcmp(s->name, "kw_minus") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 107 | else if (strcmp(s->name, "kw_intersect") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 108 | else if (strcmp(s->name, "kw_except") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 109 | else if (strcmp(s->name, "kw_semicolon") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, INITIAL}; 110 | else if (strcmp(s->name, "kw_groupby") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stGROUPBY}; 111 | else if (strcmp(s->name, "kw_orderby") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stORDERBY}; 112 | else if (strcmp(s->name, "kw_having") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stWHERE}; 113 | else if (strcmp(s->name, "kw_qualify") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stWHERE}; 114 | else if (strcmp(s->name, "kw_set") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stSET}; 115 | else if (strcmp(s->name, "kw_select") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stSELECT}; 116 | else if (strcmp(s->name, "kw_comma") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stCOMMA}; 117 | else if (strcmp(s->name, "kw_inner_join") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stJOIN}; 118 | else if (strcmp(s->name, "kw_left_join") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stJOIN}; 119 | else if (strcmp(s->name, "kw_right_join") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stJOIN}; 120 | else if (strcmp(s->name, "kw_full_join") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stJOIN}; 121 | else if (strcmp(s->name, "kw_cross_join") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stJOIN}; 122 | else if (strcmp(s->name, "kw_on") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stON}; 123 | else if (strcmp(s->name, "kw_where") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stWHERE}; 124 | else if (strcmp(s->name, "kw_from") == 0) return (struct FSQLF_state_change) {FSQLF_SCA_BEGIN, stFROM}; 125 | } 126 | 127 | return (struct FSQLF_state_change) {0, 0}; 128 | } 129 | 130 | 131 | // This routine has goal to do 4 things: 132 | // 1. Place token on the queue. 133 | // 2. Accumulate tokens till enough info is available for recognition. 134 | // 3. Perform recognition. 135 | // 4. Output fully recognized tokens. 136 | /// 137 | // At the moment only 1st and 4th parts are done. 138 | // TODO: implement 2nd and 3rd 139 | struct FSQLF_state_change FSQLF_tokque_putthrough( 140 | struct FSQLF_queue *tq, 141 | FILE *fout, 142 | struct FSQLF_out_buffer *bout, 143 | int *currindent, 144 | char *text, 145 | size_t len, 146 | struct fsqlf_kw_conf *kw0, 147 | int cur_state 148 | ) 149 | { 150 | // Place on queue. 151 | tokque_put_kw(tq, currindent, text, len, kw0); 152 | 153 | // Retrieve from queue and print. 154 | tokque_print_one(tq, fout, bout); 155 | 156 | // Send command for state change 157 | return decide_new_state(cur_state, kw0); 158 | } 159 | 160 | 161 | void FSQLF_tokque_finish_out(struct FSQLF_queue *tq, FILE *fout, struct FSQLF_out_buffer *bout) 162 | { 163 | // Print out all queue 164 | while (tq->length > LOOK_BEHIND_COUNT) { 165 | tokque_print_one(tq, fout, bout); 166 | } 167 | // Cleanup 168 | FSQLF_queue_clear(tq); 169 | } 170 | -------------------------------------------------------------------------------- /lib_fsqlf/formatter/tokque.h: -------------------------------------------------------------------------------- 1 | #ifndef tokque_h 2 | #define tokque_h 3 | 4 | 5 | // struct fsqlf_kw_conf, fsqlf_kw_get, struct FSQLF_prev_kw 6 | #include 7 | #include "../../utils/queue/queue.h" 8 | 9 | 10 | struct FSQLF_state_change 11 | { 12 | int state_change_action; 13 | int new_state; // start conditions of flex lexer generator 14 | }; 15 | 16 | 17 | // SCA - state change action 18 | enum FSQLF_state_change_action 19 | { 20 | FSQLF_SCA_NONE, 21 | FSQLF_SCA_BEGIN, 22 | FSQLF_SCA_PUSH, 23 | FSQLF_SCA_POP 24 | }; 25 | 26 | 27 | void FSQLF_tokque_init(struct FSQLF_queue *tq); 28 | 29 | 30 | 31 | struct FSQLF_state_change FSQLF_tokque_putthrough( 32 | struct FSQLF_queue *tq, 33 | FILE *fout, 34 | struct FSQLF_out_buffer *bout, 35 | int *currindent, 36 | char *text, 37 | size_t len, 38 | struct fsqlf_kw_conf *s, 39 | int cur_state 40 | ); 41 | 42 | 43 | void FSQLF_tokque_finish_out(struct FSQLF_queue *tq, FILE *fout, struct FSQLF_out_buffer *bout); 44 | 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /lib_fsqlf/kw/is_word.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | // "word" as opposed to "operator"/"separator"/"delimiter". 6 | // "SELECT" is a word, "<=" is not a word. 7 | // It is necessary to separate "SELECT" and "column1" with space. 8 | // 9 | // Side-note: 10 | // "<" and "=" also need space preserved between them. 11 | // (in case such malformed SQL is formatted) 12 | int FSQLF_is_word(char* text, size_t length) { 13 | if (length <= 2) { 14 | for (size_t i = 0; i < length; ++i) { 15 | if (!isalnum(text[i])) { 16 | return 0; 17 | } 18 | } 19 | } 20 | 21 | return 1; 22 | } 23 | -------------------------------------------------------------------------------- /lib_fsqlf/kw/is_word.h: -------------------------------------------------------------------------------- 1 | #ifndef IS_WORD_H 2 | #define IS_WORD_H 3 | 4 | 5 | int FSQLF_is_word(char* txt, size_t length); 6 | 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /lib_fsqlf/kw/kw.c: -------------------------------------------------------------------------------- 1 | #include // malloc 2 | #include // assert 3 | #include // struct fsqlf_kw_conf 4 | 5 | 6 | // Functions that retrieve single element of kw-map. 7 | 8 | 9 | void FSQLF_kw_create(fsqlf_kwmap_t *kwmap, const char *name) 10 | { 11 | struct fsqlf_kw_conf *tmp; 12 | tmp = (struct fsqlf_kw_conf*) calloc(1, sizeof(struct fsqlf_kw_conf)); 13 | assert(tmp); 14 | tmp->name = name; 15 | HASH_ADD_KEYPTR(hh, *kwmap, tmp->name, strlen(tmp->name), tmp); 16 | } 17 | 18 | 19 | struct fsqlf_kw_conf *fsqlf_kw_get(fsqlf_kwmap_t kwmap, const char *name) 20 | { 21 | struct fsqlf_kw_conf *match; 22 | HASH_FIND_STR(kwmap, name, match); 23 | return match; 24 | } 25 | -------------------------------------------------------------------------------- /lib_fsqlf/kw/kwmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "is_word.h" 3 | 4 | 5 | // Functions that operate on kw-map as a whole. 6 | // (as opposed to manipulating single element of the map) 7 | 8 | 9 | void fsqlf_kwmap_set_case(fsqlf_kwmap_t kwmap, enum fsqlf_kwcase keyword_case) 10 | { 11 | #define XMACRO( NAME , ... ) \ 12 | fsqlf_kw_get(kwmap, #NAME)->print_case = keyword_case; 13 | #include "kwmap_defaults.def" 14 | #undef XMACRO 15 | } 16 | 17 | 18 | // Set used variation of keyword text. (e.g. "left outer join" vs "left join") 19 | void fsqlf_kwmap_set_spelling(fsqlf_kwmap_t kwmap, enum fsqlf_kwspelling kw_text_to_use) 20 | { 21 | #define XMACRO( NAME , ... ) \ 22 | fsqlf_kw_get(kwmap, #NAME)->print_original_text = kw_text_to_use; 23 | #include "kwmap_defaults.def" 24 | #undef XMACRO 25 | } 26 | 27 | 28 | // Init all keyword settings to defaults. 29 | void fsqlf_kwmap_init(fsqlf_kwmap_t *kwmap) 30 | { 31 | *kwmap = NULL; 32 | #define XMACRO(NAME, gib, nlb, tb, sb, gia, nla, ta, sa, TEXT) \ 33 | do { \ 34 | size_t length = strlen(TEXT); \ 35 | FSQLF_kw_create(kwmap, #NAME); \ 36 | fsqlf_kw_get(*kwmap, #NAME)->before.global_indent_change = gib; \ 37 | fsqlf_kw_get(*kwmap, #NAME)->before.new_line = nlb; \ 38 | fsqlf_kw_get(*kwmap, #NAME)->before.indent = tb; \ 39 | fsqlf_kw_get(*kwmap, #NAME)->before.space = sb; \ 40 | fsqlf_kw_get(*kwmap, #NAME)->after.global_indent_change = gia; \ 41 | fsqlf_kw_get(*kwmap, #NAME)->after.new_line = nla; \ 42 | fsqlf_kw_get(*kwmap, #NAME)->after.indent = ta; \ 43 | fsqlf_kw_get(*kwmap, #NAME)->after.space = sa; \ 44 | fsqlf_kw_get(*kwmap, #NAME)->print_original_text = \ 45 | FSQLF_KWSPELLING_USE_HARDCODED_DEFAULT; \ 46 | fsqlf_kw_get(*kwmap, #NAME)->print_case = FSQLF_KWCASE_UPPER; \ 47 | fsqlf_kw_get(*kwmap, #NAME)->text = TEXT; \ 48 | fsqlf_kw_get(*kwmap, #NAME)->is_word = \ 49 | FSQLF_is_word(TEXT, length); \ 50 | } while (0); 51 | #include "kwmap_defaults.def" 52 | #undef XMACRO 53 | } 54 | 55 | 56 | void fsqlf_kwmap_destroy(fsqlf_kwmap_t kwmap) 57 | { 58 | struct fsqlf_kw_conf *current_kw, *tmp; 59 | HASH_ITER(hh, kwmap, current_kw, tmp) { 60 | HASH_DEL(kwmap, current_kw); 61 | free(current_kw); 62 | } 63 | } 64 | 65 | 66 | void fsqlf_kwmap_set_major_clause_nl(fsqlf_kwmap_t kwmap, int nl_count) 67 | { 68 | fsqlf_kw_get(kwmap, "kw_from")->before.new_line = nl_count; 69 | fsqlf_kw_get(kwmap, "kw_where")->before.new_line = nl_count; 70 | fsqlf_kw_get(kwmap, "kw_inner_join")->before.new_line = nl_count; 71 | fsqlf_kw_get(kwmap, "kw_left_join")->before.new_line = nl_count; 72 | fsqlf_kw_get(kwmap, "kw_right_join")->before.new_line = nl_count; 73 | fsqlf_kw_get(kwmap, "kw_full_join")->before.new_line = nl_count; 74 | fsqlf_kw_get(kwmap, "kw_cross_join")->before.new_line = nl_count; 75 | } 76 | -------------------------------------------------------------------------------- /lib_fsqlf/kw/kwmap_defaults.def: -------------------------------------------------------------------------------- 1 | // This file contains compile time (default) configuration of fsqlf 2 | 3 | // 4 | // text --------------------------------------+ 5 | // after: spaces ----------------------------------+ | 6 | // after: indentation -------------------------------+ | | 7 | // after: new line ----------------------------+ | | | 8 | // after: global indent change -----------------+ | | | | 9 | // | | | | | 10 | // before: spaces ---------------------+ | | | | | 11 | // before: indentation ------------------+ | | | | | | 12 | // before: new line ---------------+ | | | | | | | 13 | // before: global indent change ---+ | | | | | | | | 14 | // | | | | | | | | | 15 | // name | | | | | | | | | 16 | // 17 | 18 | XMACRO( kw_like , 0, 0, 0, 1, 0, 0, 0, 1, "LIKE" ) 19 | XMACRO( kw_op_plus , 0, 0, 0, 1, 0, 0, 0, 1, "+" ) 20 | XMACRO( kw_op_minus , 0, 0, 0, 1, 0, 0, 0, 1, "-" ) 21 | XMACRO( kw_op_mult , 0, 0, 0, 1, 0, 0, 0, 1, "*" ) 22 | XMACRO( kw_op_div , 0, 0, 0, 1, 0, 0, 0, 1, "/" ) 23 | XMACRO( kw_op_concat , 0, 0, 0, 1, 0, 0, 0, 1, "||" ) 24 | XMACRO( kw_not , 0, 0, 0, 1, 0, 0, 0, 1, "NOT" ) 25 | XMACRO( kw_comp_eq , 0, 0, 0, 1, 0, 0, 0, 1, "=" ) 26 | XMACRO( kw_comp_ne , 0, 0, 0, 1, 0, 0, 0, 1, "<>" ) 27 | XMACRO( kw_comp_le , 0, 0, 0, 1, 0, 0, 0, 1, "<=" ) 28 | XMACRO( kw_comp_ge , 0, 0, 0, 1, 0, 0, 0, 1, ">=" ) 29 | XMACRO( kw_comp_lt , 0, 0, 0, 1, 0, 0, 0, 1, "<" ) 30 | XMACRO( kw_comp_gt , 0, 0, 0, 1, 0, 0, 0, 1, ">" ) 31 | XMACRO( kw_comma , 0, 1, 0, 0, 0, 0, 0, 1, "," ) 32 | XMACRO( kw_select , 0, 1, 0, 0, 0, 1, 0, 2, "SELECT" ) 33 | XMACRO( kw_inner_join , 0, 1, 0, 0, 0, 0, 0, 1, "JOIN" ) 34 | XMACRO( kw_left_join , 0, 1, 0, 0, 0, 0, 0, 1, "LEFT JOIN" ) 35 | XMACRO( kw_right_join , 0, 1, 0, 0, 0, 0, 0, 1, "RIGHT JOIN" ) 36 | XMACRO( kw_full_join , 0, 1, 0, 0, 0, 0, 0, 1, "FULL JOIN" ) 37 | XMACRO( kw_cross_join , 0, 1, 0, 0, 0, 0, 0, 1, "CROSS JOIN" ) 38 | XMACRO( kw_from , 0, 1, 0, 0, 0, 0, 0, 1, "FROM" ) 39 | XMACRO( kw_on , 0, 1, 0, 1, 0, 0, 0, 1, "ON" ) 40 | XMACRO( kw_where , 0, 1, 0, 0, 0, 0, 0, 1, "WHERE" ) 41 | XMACRO( kw_and , 0, 1, 0, 0, 0, 0, 0, 1, "AND" ) 42 | XMACRO( kw_or , 0, 1, 0, 0, 0, 0, 0, 1, "OR" ) 43 | XMACRO( kw_exists , 0, 0, 0, 0, 0, 0, 0, 1, "exists" ) 44 | XMACRO( kw_in , 0, 0, 0, 0, 0, 0, 0, 1, "in" ) 45 | XMACRO( kw_from_2 , 0, 0, 0, 1, 0, 0, 0, 1, "from" ) 46 | XMACRO( kw_as , 0, 0, 0, 1, 0, 0, 0, 1, "as" ) 47 | XMACRO( kw_left_p , 0, 0, 0, 0, 0, 0, 0, 0, "(" ) 48 | XMACRO( kw_right_p , 0, 0, 0, 0, 0, 0, 0, 0, ")" ) 49 | XMACRO( kw_left_p_sub , 0, 1, 0, 0, 0, 0, 0, 0, "(" ) 50 | XMACRO( kw_right_p_sub , 0, 1, 0, 0, 0, 0, 0, 1, ")" ) 51 | XMACRO( kw_create , 0, 2, 0, 0, 0, 0, 0, 0, "CREATE" ) 52 | XMACRO( kw_left_p_create , 0, 1, 0, 0, 0, 1, 0, 0, "(" ) 53 | XMACRO( kw_right_p_create , 0, 1, 0, 0, 0, 1, 0, 0, ")" ) 54 | XMACRO( kw_comma_create , 0, 1, 0, 0, 0, 0, 0, 1, "," ) 55 | XMACRO( kw_drop , 0, 2, 0, 0, 0, 0, 0, 0, "DROP" ) 56 | XMACRO( kw_table , 0, 0, 0, 1, 0, 0, 0, 0, "TABLE" ) 57 | XMACRO( kw_ifexists , 0, 0, 0, 0, 0, 0, 0, 0, "IF EXISTS" ) 58 | XMACRO( kw_view , 0, 0, 0, 0, 0, 0, 0, 0, "VIEW" ) 59 | XMACRO( kw_union , 0, 2, 1, 0, 0, 1, 0, 0, "UNION" ) 60 | XMACRO( kw_minus , 0, 2, 1, 0, 0, 1, 0, 0, "MINUS" ) 61 | XMACRO( kw_union_all , 0, 2, 1, 0, 0, 1, 0, 0, "UNION ALL" ) 62 | XMACRO( kw_intersect , 0, 2, 1, 0, 0, 1, 0, 0, "INTERSECT" ) 63 | XMACRO( kw_except , 0, 2, 1, 0, 0, 1, 0, 0, "EXCEPT" ) 64 | XMACRO( kw_groupby , 0, 1, 0, 0, 0, 0, 0, 0, "GROUP BY" ) 65 | XMACRO( kw_orderby , 0, 1, 0, 0, 0, 0, 0, 0, "ORDER BY" ) 66 | XMACRO( kw_semicolon , 0, 1, 0, 0, 0, 5, 0, 0, ";" ) 67 | XMACRO( kw_having , 0, 1, 0, 0, 0, 0, 0, 0, "HAVING" ) 68 | XMACRO( kw_qualify , 0, 1, 0, 0, 0, 0, 0, 0, "QUALIFY" ) 69 | XMACRO( kw_insertinto , 0, 1, 0, 0, 0, 0, 0, 0, "INSERT INTO" ) 70 | XMACRO( kw_left_p_ins , 0, 1, 0, 0, 0, 1, 0, 2, "(" ) 71 | XMACRO( kw_right_p_ins , 0, 1, 0, 0, 0, 0, 0, 0, ")" ) 72 | XMACRO( kw_comma_ins , 0, 1, 0, 0, 0, 0, 0, 1, "," ) 73 | XMACRO( kw_comma_grpby , 0, 0, 0, 0, 0, 0, 0, 1, "," ) 74 | XMACRO( kw_comma_ordby , 0, 0, 0, 0, 0, 0, 0, 1, "," ) 75 | XMACRO( kw_comma_join , 0, 1, 0, 0, 0, 0, 0, 1, "," ) 76 | XMACRO( kw_update , 0, 1, 0, 0, 0, 0, 0, 1, "UPDATE" ) 77 | XMACRO( kw_set , 0, 1, 0, 0, 0, 1, 0, 2, "SET" ) 78 | XMACRO( kw_comma_set , 0, 1, 0, 0, 0, 0, 0, 1, "," ) 79 | XMACRO( kw_deletefrom , 0, 1, 0, 0, 0, 0, 0, 0, "DELETE FROM" ) 80 | XMACRO( kw_case , 0, 0, 0, 0, 1, 0, 0, 0, "CASE" ) 81 | XMACRO( kw_end ,-1, 1, 0, 0, 0, 0, 0, 0, "END" ) 82 | XMACRO( kw_when , 0, 1, 0, 0, 0, 0, 0, 0, "WHEN" ) 83 | XMACRO( kw_then , 0, 1, 0, 0, 0, 0, 0, 0, "THEN" ) 84 | XMACRO( kw_else , 0, 1, 0, 0, 0, 0, 0, 0, "ELSE" ) 85 | XMACRO( kw_using , 0, 0, 0, 0, 0, 1, 0, 0, "USING" ) 86 | -------------------------------------------------------------------------------- /lib_fsqlf/lex/token.c: -------------------------------------------------------------------------------- 1 | #include // malloc 2 | #include // assert 3 | #include // strncpy 4 | #include "token.h" 5 | 6 | 7 | struct FSQLF_token *FSQLF_make_token( 8 | const enum FSQLF_token_class token_class, 9 | const char *text, 10 | const int leng, 11 | struct fsqlf_kw_conf *kw_setting, 12 | const size_t indent) 13 | { 14 | struct FSQLF_token *tok; 15 | tok = malloc(sizeof(struct FSQLF_token)); 16 | assert(tok); 17 | FSQLF_set_token(tok, token_class, text, leng, kw_setting, indent); 18 | return tok; 19 | } 20 | 21 | 22 | void FSQLF_delete_token(struct FSQLF_token **tok) 23 | { 24 | FSQLF_clear_token(*tok); 25 | free((*tok)); 26 | (*tok) = NULL; 27 | } 28 | 29 | 30 | void FSQLF_set_token(struct FSQLF_token * tok, 31 | const enum FSQLF_token_class token_class, 32 | const char *text, 33 | const int leng, 34 | struct fsqlf_kw_conf *kw_conf, 35 | const size_t indent) 36 | { 37 | assert(tok); 38 | tok->token_class = token_class; 39 | 40 | tok->text = malloc(leng+1); 41 | assert(tok->text); 42 | strncpy(tok->text, text, leng); 43 | tok->text[leng] = '\0'; 44 | tok->leng = leng; 45 | 46 | tok->kw_setting = kw_conf; 47 | tok->indent = indent; 48 | } 49 | 50 | 51 | void FSQLF_clear_token(struct FSQLF_token *tok) 52 | { 53 | free(tok->text); 54 | tok->text = NULL; 55 | } 56 | -------------------------------------------------------------------------------- /lib_fsqlf/lex/token.h: -------------------------------------------------------------------------------- 1 | #ifndef token_h 2 | #define token_h 3 | 4 | 5 | #include // kw_conf, struct FSQLF_prev_kw prev 6 | 7 | 8 | // Currently token.h is not used, but there is a plan for it. 9 | 10 | // GOAL 11 | // is make it possible to specify syntax at runtime. 12 | // This could be split into some milestones: 13 | // A. Keywords that require only trivial spacing (before, after). 14 | // B. Keywords that affect spacing of other keywords (global indent) 15 | // C. things like string definition 16 | // (motivation for this: c-like-escaped-quote - '\'' is valid 17 | // string literal in mysql, while unfinished string in some others) 18 | 19 | // OBSTACLE 20 | // at the moment keyword recognition is done completely 21 | // by flex-generated lexer and it requires to know all definitions 22 | // at lexer generation time (this means even before compilation). 23 | 24 | // PLAN 25 | // to achieve it: 26 | // - Reduce responsibility of flex lexer to recognizing only 'token classes': 27 | // comments, spacing, word, punctuation, string, parenthesis. 28 | // - Further recognition of word tokens (and possibly punctuation), 29 | // will be done outside of flex-generated lexer (lets say in quasiparser). 30 | // - Quasiparser will: 31 | // 1. Accept the token with it's class 32 | // 2. Place it on the queue 33 | // 3. Perform recognition 34 | // (if lookahead is needed, it hold processing 35 | // until enough lookahead available) 36 | // 4. Remove recognized tokens from the queue and pass them to printing 37 | // routines. 38 | 39 | // Additional notes: 40 | // - no additional external dependencies are allowed 41 | // (it is needed to use fsqlf at work and can not add external deps) 42 | // - using flex generated lexer possibly gave quite good performance, 43 | // it would be strange to expect that performance will stay as is 44 | // after these changes. 45 | // Though goal is to preserve usablity of fsqlf (too slow == unusable). 46 | 47 | 48 | 49 | enum FSQLF_token_class 50 | { 51 | FSQLF_TOKEN_CLASS_TXT = 0, // Non-keyword text 52 | FSQLF_TOKEN_CLASS_KW = 1, // Keyword 53 | FSQLF_TOKEN_CLASS_SP = 2, // Spacing 54 | FSQLF_TOKEN_CLASS_CMT = 3 // Comment 55 | }; 56 | 57 | 58 | struct FSQLF_token 59 | { 60 | enum FSQLF_token_class token_class; 61 | char *text; 62 | int leng; 63 | struct fsqlf_kw_conf *kw_setting; 64 | size_t indent; 65 | }; 66 | 67 | 68 | struct FSQLF_token *FSQLF_make_token( 69 | const enum FSQLF_token_class token_class, 70 | const char *text, 71 | const int leng, 72 | struct fsqlf_kw_conf *kw_setting, 73 | const size_t indent); 74 | 75 | 76 | void FSQLF_delete_token(struct FSQLF_token **tok); 77 | 78 | 79 | void FSQLF_set_token(struct FSQLF_token * tok, 80 | const enum FSQLF_token_class token_class, 81 | const char *text, 82 | const int leng, 83 | struct fsqlf_kw_conf *kw_conf, 84 | const size_t indent); 85 | 86 | 87 | void FSQLF_clear_token(struct FSQLF_token *tok); 88 | 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /lib_fsqlf/lex/token_test.c: -------------------------------------------------------------------------------- 1 | #include // memcmp 2 | #include // assert 3 | #include "token.h" 4 | 5 | 6 | void token_test() 7 | { 8 | struct token *t = FSQLF_make_token(FSQLF_TOKEN_CLASS_CMT, "blabla", 6, NULL); 9 | 10 | assert(t->token_class == FSQLF_TOKEN_CLASS_CMT); 11 | assert(t->leng == 6); 12 | assert(memcmp(t->text, "blabla", t->leng) == 0); 13 | assert(t->kw_setting == NULL); 14 | 15 | FSQLF_delete_token(&t); 16 | assert(!t); 17 | } 18 | 19 | 20 | int main() 21 | { 22 | token_test(); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PRJNAME=fsqlf 2 | 3 | CFLAGS+=-std=c99 4 | CFLAGS+=-Wall 5 | CFLAGS+=-pedantic-errors 6 | CFLAGS+=-g 7 | CFLAGS+=-Iinclude 8 | 9 | CXXFLAGS+=-DVERSION=\"$(VERSION)\" 10 | CXXFLAGS+=-Iinclude 11 | 12 | ifdef WIN 13 | BLD=builds/make-win-x86-64 14 | OS_TARGET=windows 15 | EXEC_CLI=$(BLD)/fsqlf.exe 16 | EXEC_GUI=$(BLD)/wx_fsqlf.exe 17 | UTIL_TXT2H=$(BLD)/text_to_header.exe 18 | CFLAGS+=-DBUILDING_LIBFSQLF 19 | # i686 20 | # https://myonlineusb.wordpress.com/2011/06/08/what-is-the-difference-between-i386-i486-i586-i686-i786/ 21 | # CC=i586-mingw32msvc-gcc 22 | # CXX=i586-mingw32msvc-g++ 23 | # CXXFLAGS+= `/usr/i586-mingw32msvc/bin/wx-config --cxxflags | sed 's/-mthreads//'` 24 | # LDFLAGS+= `/usr/i586-mingw32msvc/bin/wx-config --libs | sed 's/-mthreads//'` 25 | CC=i686-w64-mingw32-gcc 26 | CXX=i686-w64-mingw32-g++ 27 | CXXFLAGS+= `/usr/i686-w64-mingw32/bin/wx-config --cxxflags | sed 's/-mthreads//'` 28 | LDFLAGS+=-static-libgcc -static-libstdc++ 29 | LDFLAGS+= `/usr/i686-w64-mingw32/bin/wx-config --libs | sed 's/-mthreads//'` 30 | # Option "-mthreads" needs to be removed, so mingwm10.dll would not be needed 31 | # (http://old.nabble.com/mingwm10.dll-ts8920679.html) 32 | else 33 | BLD=builds/make-linux 34 | OS_TARGET=linux 35 | PREFIX=/usr/local 36 | EXEC_CLI=$(BLD)/fsqlf 37 | EXEC_GUI=$(BLD)/wx_fsqlf 38 | UTIL_TXT2H=$(BLD)/text_to_header 39 | CC=gcc 40 | CXX=g++ 41 | CXXFLAGS+= `wx-config --cxxflags` 42 | LDFLAGS+= `wx-config --libs` 43 | ifneq (Darwin, ${_system_type}) 44 | CFLAGS+=-m32 45 | else 46 | CFLAGS+=-m64 47 | endif 48 | endif 49 | 50 | ifeq (Darwin, ${_system_type}) 51 | LIBNAME=$(BLD)/libfsqlf.dylib 52 | LIBFLAGS=-dynamiclib 53 | else 54 | ifdef WIN 55 | LIBNAME=$(BLD)/libfsqlf.dll 56 | LIBNAME2=$(BLD)/libfsqlf.lib 57 | LIBFLAGS=-shared -Wl,--out-implib,$(LIBNAME2) 58 | else 59 | LIBNAME=$(BLD)/libfsqlf.so 60 | LIBFLAGS=-shared 61 | endif 62 | endif 63 | 64 | 65 | 66 | .PHONY: all clean zip test install uninstall 67 | 68 | 69 | 70 | all: $(EXEC_CLI) $(EXEC_GUI) 71 | 72 | 73 | 74 | # 75 | # BUILD LIB 76 | # 77 | LCOBJ += $(BLD)/lib_fsqlf/conf_file/conf_file_create.o 78 | LCOBJ += $(BLD)/lib_fsqlf/conf_file/conf_file_read.o 79 | LCOBJ += $(BLD)/lib_fsqlf/formatter/lex_wrapper.o 80 | LCOBJ += $(BLD)/lib_fsqlf/formatter/print_keywords.o 81 | LCOBJ += $(BLD)/lib_fsqlf/formatter/tokque.o 82 | LCOBJ += $(BLD)/lib_fsqlf/kw/kw.o 83 | LCOBJ += $(BLD)/lib_fsqlf/kw/kwmap.o 84 | LCOBJ += $(BLD)/lib_fsqlf/kw/is_word.o 85 | LCOBJ += $(BLD)/lib_fsqlf/lex/token.o 86 | LCOBJ += $(BLD)/utils/queue/queue.o 87 | LCOBJ += $(BLD)/utils/stack/stack.o 88 | LCOBJ += $(BLD)/utils/string/read_int.o 89 | BLDDIRS += $(dir $(LCOBJ)) 90 | 91 | $(LCOBJ): $(BLD)/%.o: ./%.c | $(BLDDIRS) 92 | $(CC) -o $@ -c $< $(CFLAGS) -I$(BLD) -Ilib_fsqlf/formatter 93 | $(BLD)/lex.yy.o: $(BLD)/lex.yy.c 94 | $(CC) -o $@ -c $< $(CFLAGS) -Ilib_fsqlf/formatter 95 | 96 | $(filter lib_fsqlf/%,$(LCOBJ)): $(BLDP)%.o: ./%.c include/lib_fsqlf.h 97 | 98 | $(LIBNAME): $(LCOBJ) $(BLD)/lex.yy.o 99 | $(CC) -o $@ $^ $(CFLAGS) $(LIBFLAGS) 100 | 101 | $(BLD)/lib_fsqlf/conf_file/conf_file_read.o: utils/string/read_int.h 102 | $(BLD)/lib_fsqlf/formatter/lex_wrapper.o: $(BLD)/lex.yy.h 103 | $(BLD)/lex.yy.h: $(BLD)/lex.yy.c 104 | $(BLD)/lex.yy.c: lib_fsqlf/formatter/fsqlf.lex lib_fsqlf/formatter/print_keywords.h 105 | # flex options (e.g. `-o`) has to be before input file 106 | flex -o $@ --header-file=$(BLD)/lex.yy.h $< 107 | 108 | 109 | 110 | # 111 | # BUILD CLI 112 | # 113 | COBJ += $(BLD)/cli/main.o 114 | COBJ += $(BLD)/cli/cli.o 115 | BLDDIRS += $(dir $(COBJ)) 116 | 117 | $(COBJ): $(BLD)/%.o: ./%.c include/lib_fsqlf.h | $(BLDDIRS) 118 | $(CC) -o $@ -c $< $(CFLAGS) 119 | 120 | INTUTIL = $(BLD)/utils/string/read_int.o 121 | $(EXEC_CLI): $(COBJ) $(INTUTIL) $(LIBNAME) 122 | $(CC) -o $@ $(CFLAGS) $(COBJ) $(INTUTIL) -L$(BLD) -lfsqlf -Wl,-rpath,. 123 | 124 | 125 | 126 | # 127 | # BUILD GUI 128 | # 129 | CXXOBJ += $(BLD)/gui/wx_fsqlf.o 130 | CXXOBJ += $(BLD)/gui/basic_notepad.o 131 | CXXOBJ += $(BLD)/gui/dnd_target.o 132 | BLDDIRS += $(dir $(CXXOBJ)) 133 | 134 | $(CXXOBJ): $(BLD)/%.o: ./%.cpp ./%.hpp | $(BLDDIRS) 135 | $(CXX) -o $@ -c $< $(CXXFLAGS) -I$(BLD) 136 | 137 | $(EXEC_GUI): $(CXXOBJ) $(LIBNAME) 138 | $(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) -L$(BLD) -lfsqlf -Wl,-rpath,. 139 | 140 | $(BLD)/gui/wx_fsqlf.o: gui/basic_notepad.hpp 141 | $(BLD)/gui/basic_notepad.o: gui/dnd_target.hpp $(BLD)/license_text.h 142 | 143 | $(UTIL_TXT2H): utils/text_to_header/text_to_header.c 144 | $(CC) -o $@ $< $(CFLAGS) 145 | $(BLD)/license_text.h: LICENSE $(UTIL_TXT2H) 146 | $(UTIL_TXT2H) $< $@ LICENSE_TEXT 147 | 148 | 149 | 150 | # 151 | # TESTING 152 | # 153 | # Simple regression testing - testing against gold (pre-saved correct output) 154 | # Given certain input to `fsqlf`, actual output (lead) is compared 155 | # against to it's predefined expected output (gold). 156 | # TF stands for "test file". 157 | TSTOBJ += $(BLD)/tests/tools/file_compare.o 158 | TSTOBJ += $(BLD)/tests/format_files_test.o 159 | BLDDIRS += $(dir $(TSTOBJ)) 160 | BLDDIRS += $(BLD)/tests/cases 161 | 162 | test: $(EXEC_CLI) $(BLD)/tests/format_files_test 163 | $(BLD)/tests/format_files_test 164 | 165 | $(TSTOBJ): $(BLD)/%.o: ./%.c | $(BLDDIRS) 166 | $(CC) -o $@ -c $< $(CFLAGS) \ 167 | -D PATH_FSQLF_CLI=\"$(EXEC_CLI)\" \ 168 | -D PATH_FSQLF_LIB=\"$(BLD)\" \ 169 | -D PATH_TC_STATICSQL=\"tests/cases/\" \ 170 | -D PATH_TC_GENERATED=\"$(BLD)/tests/cases/\" 171 | 172 | $(BLD)/tests/format_files_test: $(TSTOBJ) 173 | $(CC) -o $@ $^ $(CFLAGS) 174 | 175 | 176 | 177 | # 178 | # OUT OF SOURCE BUILD FOLDERS 179 | # 180 | $(sort $(BLDDIRS)): 181 | mkdir -p $@ 182 | 183 | 184 | 185 | # 186 | # CLEANUP 187 | # 188 | clean: 189 | rm -f -R builds/ 190 | 191 | 192 | 193 | # 194 | # BUILD ARCHIVE (source and binaries for publishing) 195 | # 196 | $(BLD)/formatting.conf: lib_fsqlf/kw/kwmap_defaults.def $(EXEC_CLI) 197 | LD_LIBRARY_PATH=$(BLD) ./$(EXEC_CLI) --create-config-file $(BLD)/formatting.conf 198 | 199 | VERSION:=$(shell git describe master) 200 | PKGAREA:=builds/packaging 201 | ZIP_NAME:=$(PKGAREA)/$(PRJNAME).$(VERSION).zip 202 | 203 | zip: tmp_folder 204 | rm -f $(ZIP_NAME) 205 | git archive master -o $(ZIP_NAME) --format=zip --prefix='$(PRJNAME)/source/' 206 | cd $(PKGAREA) && zip -r ../../$(ZIP_NAME) $(PRJNAME) 207 | 208 | tmp_folder: LICENSE README.md 209 | make prep_bin 210 | make prep_bin WIN=1 211 | cp -t $(PKGAREA)/$(PRJNAME) $^ 212 | 213 | prep_bin: $(EXEC_CLI) $(EXEC_GUI) $(LIBNAME) $(BLD)/formatting.conf 214 | rm -Rf $(PKGAREA)/$(PRJNAME)/$(OS_TARGET) 215 | mkdir -p $(PKGAREA)/$(PRJNAME)/$(OS_TARGET) 216 | cp -t $(PKGAREA)/$(PRJNAME)/$(OS_TARGET) $^ $(LIBNAME2) 217 | 218 | 219 | 220 | # 221 | # INSTALLATION 222 | # 223 | ifeq ($(OS_TARGET),linux) 224 | 225 | install: $(EXEC_CLI) $(EXEC_GUI) $(BLD)/formatting.conf 226 | install -d $(PREFIX)/bin 227 | install $(EXEC_CLI) $(EXEC_GUI) $(PREFIX)/bin 228 | install -d $(PREFIX)/share/fsqlf 229 | install -m 644 $(BLD)/formatting.conf $(PREFIX)/share/fsqlf/formatting.conf.example 230 | install -d $(PREFIX)/lib 231 | install $(LIBNAME) $(PREFIX)/lib 232 | 233 | uninstall: 234 | ifdef EXEC_CLI 235 | rm -vf $(PREFIX)/bin/$(EXEC_CLI) 236 | endif 237 | ifdef EXEC_GUI 238 | rm -vf $(PREFIX)/bin/$(EXEC_GUI) 239 | endif 240 | ifdef LIBNAME 241 | rm -vf $(PREFIX)/lib/$(LIBNAME) 242 | endif 243 | rm -vf $(PREFIX)/share/fsqlf/formatting.conf.example 244 | rm -vfd $(PREFIX)/share/fsqlf 245 | endif 246 | 247 | 248 | 249 | # 250 | # CMake 251 | # 252 | CMREL:=-DCMAKE_BUILD_TYPE=RELEASE 253 | CMTLINUX32:=-DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain-linux-x86-gcc.cmake 254 | CMTLINUX64:=-DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain-linux-x86-64-gcc.cmake 255 | CMTWIN32:=-DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain-windows-x86-mingw.cmake 256 | 257 | cmake-linux-x86: 258 | mkdir -p builds/cmake-linux-x86 259 | cd builds/cmake-linux-x86 && cmake $(CMREL) $(CMTLINUX32) ../.. && make && make test 260 | 261 | cmake-linux-x86-64: 262 | mkdir -p builds/cmake-linux-x86-64 263 | cd builds/cmake-linux-x86-64 && cmake $(CMREL) $(CMTLINUX64) ../.. && make && make test 264 | 265 | cmake-win-x86: 266 | mkdir -p builds/cmake-win-x86 267 | cd builds/cmake-win-x86 && cmake $(CMREL) $(CMTWIN32) ../.. && make && make test 268 | 269 | ninja-linux-x86: 270 | mkdir -p builds/ninja-linux-x86 271 | cd builds/ninja-linux-x86 && cmake $(CMREL) $(CMTLINUX32) -G Ninja ../.. && ninja 272 | 273 | 274 | 275 | # makefile reference 276 | # $@ - target 277 | # $+ - all prerequisites 278 | # $^ - all prerequisites, but list each name only once 279 | # $< - first prerequisite 280 | # $? - all prerequisites newer than target 281 | # $| - order only prerequisites 282 | # 283 | # See also: 284 | # http://www.gnu.org/software/make/manual/make.html#Automatic-Variables 285 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(test_fsqlf_cli) 2 | # Test fsqlf cli. 3 | 4 | 5 | add_executable(format_files_test format_files_test.c tools/file_compare.c) 6 | 7 | 8 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cases) 9 | # get_property(PATH_FSQLF_CLI TARGET fsqlf PROPERTY LOCATION) 10 | 11 | add_definitions( 12 | -DPATH_TC_STATICSQL="${CMAKE_CURRENT_SOURCE_DIR}/cases/" 13 | -DPATH_TC_GENERATED="${CMAKE_CURRENT_BINARY_DIR}/cases/" 14 | -DPATH_FSQLF_CLI="$") 15 | # target_compile_definitions() requires newer version of cmake. 16 | -------------------------------------------------------------------------------- /tests/cases/bigquery_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 3 | asbaba AS g 4 | , cast(bfbfb AS date) + 1 as x 5 | , 2 - 1 as b1 6 | , 2 * 1 as c1 7 | , 2 / 1 as d1 8 | , "str" || "cat"as concatted /*/*/ 9 | , min(a)over(partition by x,c order by r rows between 1 preceeding and 1 preceeding) 10 | , 'dfgdf/*gdfgd*/fg''dfgdf/*gdfgd*/fg''dfgdf/*gdfgd*/fg' as c /*a*a*a*/ 11 | , cast(19.2 AS decimal(18,2))/******/ 12 | , ' */ ' /**/ 13 | , cast(19.2 AS decimal(18,2))/* sdsd *****---*/ 14 | , asextract(day FROM t)as _toto -- comment case select from 15 | FROM (bobo as gogo 16 | LEFT JOIN 17 | ( 18 | SELECT 19 | 1 20 | FROM 21 | ( 22 | SELECT 23 | 1 24 | FROM T 25 | 26 | UNION 27 | SELECT 28 | 2 29 | 30 | INTERSECT 31 | SELECT 32 | 2 33 | 34 | EXCEPT 35 | SELECT 36 | 2 37 | ) T 38 | ) baba 39 | ON 1 IN (1,2,3) 40 | OR x <> 1 41 | AND 1 = 1 42 | AND 1 = 1 43 | AND "a" LIKE "b") 44 | LEFT JOIN (baba 45 | CROSS JOIN gaga) 46 | ON baba.g = T.z 47 | WHERE toto 48 | AND EXISTS 49 | ( 50 | SELECT 51 | 1 52 | FROM t 53 | WHERE b = g 54 | AND NOT EXISTS 55 | ( 56 | SELECT 57 | 1 58 | FROM T 59 | ) 60 | ) 61 | AND 1 = 1 62 | AND 1 < 1 63 | AND 1 > 1 64 | AND 1 >= 1 65 | AND 1 <= 1 66 | AND 1 <> 1 67 | AND a IN 68 | ( 69 | SELECT 70 | 1 71 | FROM hsd 72 | ) 73 | AND b IN (19,2,3) 74 | ; 75 | 76 | 77 | 78 | 79 | -- 80 | -------------------------------------------------------------------------------- /tests/cases/bigquery_input.sql: -------------------------------------------------------------------------------- 1 | select asbaba as g 2 | , cast( bfbfb as date) +1 as x 3 | , 2-1 as b1 4 | , 2*1 as c1 5 | , 2/1 as d1 6 | , "str"||"cat" as concatted /*/*/ 7 | , min(a) over (partition by x,c order by r rows between 1 preceeding and 1 preceeding) 8 | ,'dfgdf/*gdfgd*/fg''dfgdf/*gdfgd*/fg''dfgdf/*gdfgd*/fg' as c /*a*a*a*/ 9 | , cast(19.2 as decimal(18,2)) /******/ 10 | , ' */ ' /**/ 11 | , cast( 19.2 as decimal( 18 , 2 ) ) 12 | /* sdsd *****---*/ 13 | 14 | ,asextract(day from t) as _toto -- comment case select from 15 | from (bobo as gogo 16 | left join ( SELECT 1 FROM ( SELECT 1 FROM T UNION SELECT 2 intersect SELECT 2 except SELECT 2 ) T ) baba on 1 in ( 1 , 2 , 3) OR x<>1 and 1=1 and 1=1 AND "a" LIKE "b") left outer join (baba cross join gaga) on baba.g=T.z 17 | where toto and exists (select 1 from t where b=g and not exists ( SELECT 1 FROM T ) ) AND 1=1 18 | AND 1<1 AND 1>1 AND 1>=1 AND 1<=1 AND 1<>1 19 | and a in (select 1 from hsd) and b in ( 19 , 2 , 3) ; 20 | 21 | -- -------------------------------------------------------------------------------- /tests/cases/case_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 3 | CASE 4 | WHEN CASE X 5 | WHEN 1 6 | THEN 'lv2:1' 7 | ELSE 'lv2:not1' 8 | END = 1 and x = x 9 | THEN 'LV1' 10 | ELSE 'still-LV1' 11 | END 12 | -------------------------------------------------------------------------------- /tests/cases/case_input.sql: -------------------------------------------------------------------------------- 1 | select 2 | case when case X when 1 then 'lv2:1' else 'lv2:not1' end = 1 and x = x 3 | then 'LV1' 4 | else 'still-LV1' 5 | end 6 | -------------------------------------------------------------------------------- /tests/cases/comment_marker_expected.sql: -------------------------------------------------------------------------------- 1 | /*********/ 2 | -------------------------------------------------------------------------------- /tests/cases/comment_marker_input.sql: -------------------------------------------------------------------------------- 1 | /*********/ -------------------------------------------------------------------------------- /tests/cases/create_table_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | CREATE TABLE schema_name.table_name 4 | ( 5 | id int 6 | , col2 varchar(30) 7 | , col3 non standart keywords char(1) 8 | ) 9 | ; 10 | 11 | 12 | 13 | 14 | CREATE volatile multiset TABLE schema_name.table_name 15 | ( 16 | id int 17 | , col2 varchar(30) 18 | , col3 non standart keywords char(1) 19 | ) 20 | ; 21 | -------------------------------------------------------------------------------- /tests/cases/create_table_input.sql: -------------------------------------------------------------------------------- 1 | create table schema_name.table_name(id int, col2 varchar(30), col3 non standart keywords char(1)); 2 | 3 | 4 | create 5 | volatile multiset table schema_name.table_name(id int, col2 varchar(30), col3 non standart keywords char(1)); 6 | -------------------------------------------------------------------------------- /tests/cases/delete_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | DELETE FROM t1 3 | WHERE c1 = 2 4 | ; 5 | 6 | 7 | 8 | 9 | DELETE FROM t1 10 | WHERE c1 = 2 11 | ; 12 | -------------------------------------------------------------------------------- /tests/cases/delete_input.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM t1 WHERE c1=2; 2 | DELETE from t1 WHERE c1=2; -------------------------------------------------------------------------------- /tests/cases/group_order_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 3 | 1 4 | , 2 5 | FROM x 6 | GROUP BY 1, 2, 3, 4 7 | ORDER BY 1, 2 8 | ; 9 | -------------------------------------------------------------------------------- /tests/cases/group_order_input.sql: -------------------------------------------------------------------------------- 1 | sel 1,2 from x group by 1,2,3,4 ORDER BY 1,2; 2 | -------------------------------------------------------------------------------- /tests/cases/insert_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | INSERT INTO SCH1.TAB1 3 | ( 4 | col1 5 | , col2 6 | , col3 7 | ) 8 | SELECT 9 | colA 10 | , colB 11 | , colC 12 | FROM SCH2.TAB2 13 | ; 14 | -------------------------------------------------------------------------------- /tests/cases/insert_input.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO SCH1.TAB1 (col1,col2,col3) SELECT colA,colB,colC FROM SCH2.TAB2; 2 | -------------------------------------------------------------------------------- /tests/cases/many_semicolons_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 3 | a 4 | , b 5 | , c 6 | ; 7 | 8 | 9 | 10 | 11 | SELECT 12 | a 13 | , b 14 | , c 15 | ; 16 | 17 | 18 | 19 | 20 | SELECT 21 | a 22 | , b 23 | , c 24 | ; 25 | 26 | 27 | 28 | 29 | SELECT 30 | a 31 | , b 32 | , c 33 | ; 34 | -------------------------------------------------------------------------------- /tests/cases/many_semicolons_input.sql: -------------------------------------------------------------------------------- 1 | SELECT a,b,c; SELECT a,b,c; SELECT a,b,c; SELECT a,b,c; -------------------------------------------------------------------------------- /tests/cases/update_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | UPDATE s1.t1 3 | SET 4 | c1 = x 5 | WHERE 1 = 1 6 | ; 7 | 8 | 9 | 10 | 11 | UPDATE at1 12 | FROM t1 at1 13 | , t2 at2 14 | SET 15 | c1 = x 16 | , c2 = y 17 | WHERE at1.id = at2.id 18 | ; 19 | -------------------------------------------------------------------------------- /tests/cases/update_input.sql: -------------------------------------------------------------------------------- 1 | UPDATE s1.t1 2 | SET c1=x 3 | WHERE 1=1 4 | ; 5 | 6 | 7 | UPDATE at1 8 | FROM t1 at1 ,t2 at2 9 | SET c1=x, c2=y 10 | WHERE at1.id=at2.id 11 | ; 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/cases/using_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 3 | * 4 | FROM table1 5 | JOIN table2 USING 6 | (nicekey) 7 | -------------------------------------------------------------------------------- /tests/cases/using_input.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM table1 JOIN table2 USING(nicekey) -------------------------------------------------------------------------------- /tests/format_files_test.c: -------------------------------------------------------------------------------- 1 | #include // NULL 2 | #include // puts() 3 | #include // assert() 4 | #include // strlen() 5 | #include "registered_testcases.h" 6 | #include "tools/file_compare.h" 7 | 8 | 9 | #ifndef PATH_FSQLF_CLI 10 | #define PATH_FSQLF_CLI "../fsqlf" 11 | #endif 12 | 13 | 14 | #ifndef PATH_FSQLF_LIB 15 | #define PATH_FSQLF_LIB ".." 16 | #endif 17 | 18 | 19 | #define VERBOSE (0) 20 | 21 | 22 | void alloc_join_4(char **dest, 23 | const char *src1, size_t len1, 24 | const char *src2, size_t len2, 25 | const char *src3, size_t len3, 26 | const char *src4, size_t len4 27 | ) 28 | { 29 | size_t rlen = len1 + len2 + len3 + len4 + 3; 30 | *dest = malloc(rlen+1); 31 | assert(*dest); 32 | char *desttmp = *dest; 33 | 34 | memcpy(desttmp, src1, len1); 35 | desttmp += len1; 36 | memcpy(desttmp, " ", 1); 37 | desttmp += 1; 38 | memcpy(desttmp, src2, len2); 39 | desttmp += len2; 40 | memcpy(desttmp, " ", 1); 41 | desttmp += 1; 42 | memcpy(desttmp, src3, len3); 43 | desttmp += len3; 44 | memcpy(desttmp, " ", 1); 45 | desttmp += 1; 46 | memcpy(desttmp, src4, len4); 47 | desttmp += len4; 48 | (*desttmp) = '\0'; 49 | assert((*dest)[rlen] == '\0'); 50 | } 51 | 52 | 53 | // Iterate over all test-cases. 54 | // See if actual formatting output matches expected. 55 | int main(int argc, char **argv) 56 | { 57 | int return_code = 0; // success unless proven otherwise 58 | 59 | for (int i = 0; i < TC_COUNT; i++) { 60 | char *cmd; 61 | const char *fsqlf = PATH_FSQLF_CLI; 62 | const char *lib_path = "LD_LIBRARY_PATH=" PATH_FSQLF_LIB; 63 | alloc_join_4(&cmd, 64 | lib_path, strlen(lib_path), 65 | fsqlf, strlen(fsqlf), 66 | tcs[i][TC_INPUT], strlen(tcs[i][TC_INPUT]), 67 | tcs[i][TC_ACTUAL], strlen(tcs[i][TC_ACTUAL]) 68 | ); 69 | 70 | if (VERBOSE) puts(cmd); 71 | if (system(cmd)) goto CASE_FCOMP_ERROR; 72 | 73 | enum fcomp_result m = file_compare( 74 | tcs[i][TC_EXPECTED], 75 | tcs[i][TC_ACTUAL] 76 | ); 77 | switch (m) { 78 | case FCOMP_MATCH: 79 | printf("file_compare: MATCH (%s)\n", tcs[i][TC_ACTUAL]); 80 | return_code = 0; 81 | break; 82 | case FCOMP_DIFFER: 83 | printf("file_compare: DIFFER (%s)\n", tcs[i][TC_ACTUAL]); 84 | return_code = 1; // failure 85 | break; 86 | case FCOMP_ERROR: 87 | CASE_FCOMP_ERROR: 88 | printf("file_compare: ERROR (%s)\n", tcs[i][TC_ACTUAL]); 89 | return_code = 2; // failure 90 | break; 91 | default: 92 | assert(0); 93 | return 77; // remove gcc warning 94 | } 95 | free(cmd); 96 | } 97 | 98 | return return_code; 99 | } 100 | -------------------------------------------------------------------------------- /tests/registered_testcases.h: -------------------------------------------------------------------------------- 1 | #ifndef REGISTERED_TESTCASES_H 2 | #define REGISTERED_TESTCASES_H 3 | 4 | 5 | #ifndef PATH_TC_STATICSQL 6 | #define PATH_TC_STATICSQL "cases/" 7 | #endif 8 | 9 | 10 | #ifndef PATH_TC_GENERATED 11 | #define PATH_TC_GENERATED "cases/" 12 | #endif 13 | 14 | 15 | enum tc_position 16 | { 17 | TC_INPUT = 0, 18 | TC_ACTUAL = 1, 19 | TC_EXPECTED = 2 20 | }; 21 | 22 | 23 | #define TC_COUNT (10) 24 | const char *tcs[TC_COUNT][3] = { 25 | { 26 | PATH_TC_STATICSQL "bigquery_input.sql", 27 | PATH_TC_GENERATED "bigquery_actual.sql", 28 | PATH_TC_STATICSQL "bigquery_expected.sql" 29 | }, 30 | { 31 | PATH_TC_STATICSQL "case_input.sql", 32 | PATH_TC_GENERATED "case_actual.sql", 33 | PATH_TC_STATICSQL "case_expected.sql" 34 | }, 35 | { 36 | PATH_TC_STATICSQL "create_table_input.sql", 37 | PATH_TC_GENERATED "create_table_actual.sql", 38 | PATH_TC_STATICSQL "create_table_expected.sql" 39 | }, 40 | { 41 | PATH_TC_STATICSQL "comment_marker_input.sql", 42 | PATH_TC_GENERATED "comment_marker_actual.sql", 43 | PATH_TC_STATICSQL "comment_marker_expected.sql" 44 | }, 45 | { 46 | PATH_TC_STATICSQL "delete_input.sql", 47 | PATH_TC_GENERATED "delete_actual.sql", 48 | PATH_TC_STATICSQL "delete_expected.sql" 49 | }, 50 | { 51 | PATH_TC_STATICSQL "group_order_input.sql", 52 | PATH_TC_GENERATED "group_order_actual.sql", 53 | PATH_TC_STATICSQL "group_order_expected.sql" 54 | }, 55 | { 56 | PATH_TC_STATICSQL "insert_input.sql", 57 | PATH_TC_GENERATED "insert_actual.sql", 58 | PATH_TC_STATICSQL "insert_expected.sql" 59 | }, 60 | { 61 | PATH_TC_STATICSQL "many_semicolons_input.sql", 62 | PATH_TC_GENERATED "many_semicolons_actual.sql", 63 | PATH_TC_STATICSQL "many_semicolons_expected.sql" 64 | }, 65 | { 66 | PATH_TC_STATICSQL "update_input.sql", 67 | PATH_TC_GENERATED "update_actual.sql", 68 | PATH_TC_STATICSQL "update_expected.sql" 69 | }, 70 | { 71 | PATH_TC_STATICSQL "using_input.sql", 72 | PATH_TC_GENERATED "using_actual.sql", 73 | PATH_TC_STATICSQL "using_expected.sql" 74 | } 75 | }; 76 | 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /tests/tools/file_compare.c: -------------------------------------------------------------------------------- 1 | #include // fopen(), getc(), EOF 2 | #include // assert() 3 | #include "file_compare.h" 4 | 5 | 6 | // Char-bychar comparison of two files. 7 | enum fcomp_result file_compare(const char *path_left, const char *path_right) 8 | { 9 | FILE *left = fopen(path_left, "r"); 10 | FILE *right = fopen(path_right, "r"); 11 | if (left == NULL) { 12 | perror("file_compare() - error opening file on the left. Error"); 13 | return FCOMP_ERROR; 14 | } 15 | if (right == NULL) { 16 | perror("file_compare() - error opening file on the right. Error"); 17 | return FCOMP_ERROR; 18 | } 19 | 20 | int c_l, c_r; 21 | do { 22 | c_l = getc(left); 23 | c_r = getc(right); 24 | if (c_l != c_r) return FCOMP_DIFFER; 25 | } while (c_l != EOF && c_r != EOF); 26 | return FCOMP_MATCH; 27 | } 28 | 29 | 30 | // Example: 31 | // -- 32 | // int main(int argc, char** argv) 33 | // { 34 | // enum fcomp_result m = file_compare("file_compare.c", "ffff.c"); 35 | // switch (m) { 36 | // case FCOMP_MATCH: 37 | // puts("file_compare: MATCH"); 38 | // break; 39 | // case FCOMP_DIFFER: 40 | // puts("file_compare: DIFFER"); 41 | // break; 42 | // case FCOMP_ERROR: 43 | // puts("file_compare: ERROR"); 44 | // break; 45 | // default: 46 | // assert(0); 47 | // } 48 | // } 49 | // -- 50 | -------------------------------------------------------------------------------- /tests/tools/file_compare.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_COMPARE_H 2 | #define FILE_COMPARE_H 3 | 4 | 5 | enum fcomp_result 6 | { 7 | FCOMP_MATCH = 0, 8 | FCOMP_DIFFER = 1, 9 | FCOMP_ERROR = 2 10 | }; 11 | 12 | 13 | // Char-bychar comparison of two files. 14 | enum fcomp_result file_compare(const char *path_left, const char *path_right); 15 | 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /utils/map/uthash_test.c: -------------------------------------------------------------------------------- 1 | // Test case/usecase/demo/tryout relevant for use in fsqlf 2 | /// 3 | // Code taken (a bit modified) from 4 | // http://troydhanson.github.io/uthash/userguide.html#_string_keys 5 | 6 | 7 | 8 | #include // strcpy 9 | #include // malloc 10 | #include // printf 11 | #include "uthash.h" 12 | 13 | 14 | struct User 15 | { 16 | const char *name; // key 17 | size_t name_len; 18 | int id; 19 | UT_hash_handle hh; // makes this structure hashable 20 | }; 21 | 22 | 23 | int main(int argc, char *argv[]) 24 | { 25 | // Head of hashmap that will map user-name to user-struct. 26 | struct User *map_users = NULL; 27 | 28 | 29 | // Fill the hashmap with values. 30 | const char *names[] = { "joe", "bobbby", "betty", NULL }; 31 | const char **name_ptr; 32 | int i=0; 33 | for (name_ptr = names; *name_ptr != NULL; name_ptr++) { 34 | // .. create new user struct. 35 | struct User *newu; 36 | newu = (struct User*) malloc(sizeof(struct User)); 37 | newu->name = *name_ptr; 38 | newu->id = i++; 39 | newu->name_len = strlen(newu->name); 40 | // .. add new user to the map. 41 | HASH_ADD_KEYPTR(hh, map_users, newu->name, newu->name_len, newu); 42 | } 43 | 44 | 45 | // Retrieve a value from map. 46 | struct User *match; 47 | HASH_FIND_STR(map_users, "betty", match); 48 | if (match) { 49 | printf("%s's id is %d\n", match->name, match->id); 50 | printf("%s's username length is %d\n", match->name, match->name_len); 51 | } 52 | 53 | 54 | // Iterate and remove. 55 | // alternative would be just: HASH_CLEAR(hh, users); 56 | struct User *user_iter; 57 | // seems `tmp` is needed by uthash internals 58 | // to provide deletion-safe iteration. 59 | struct User *tmp; 60 | HASH_ITER(hh, map_users, user_iter, tmp) { 61 | HASH_DEL(map_users, user_iter); 62 | free(user_iter); 63 | } 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /utils/queue/queue.c: -------------------------------------------------------------------------------- 1 | // Queue implementation. 2 | /// 3 | // Macros for configuration: 4 | // FSQLF_QUEUE_INIT_CAPACITY - initial capacity (optional; defaults to 16) 5 | /// 6 | // Defined names for queue usage: 7 | // (other names should not be used) 8 | // struct FSQLF_queue - queue struct 9 | // struct FSQLF_queue.length - number of items in the queue (size_t). 10 | // void FSQLF_queue_init(struct FSQLF_queue*) - initialize queue. 11 | // void FSQLF_queue_clear(struct FSQLF_queue*) - free resources, make queue unusable. 12 | // void *FSQLF_queue_alloc_back(struct FSQLF_queue * const q) - Allocate item onto the back. 13 | // void FSQLF_queue_push_back(struct FSQLF_queue*, void*) - Add item onto the back. 14 | // void FSQLF_queue_drop_head(struct FSQLF_queue*) - Remove item from head. 15 | // void *FSQLF_queue_peek_n(struct FSQLF_queue*, size_t) - Return certain element. 16 | // int FSQLF_queue_empty(struct FSQLF_queue*) - Check if queue is empty. 17 | 18 | 19 | #include // malloc, realloc 20 | #include // memcpy 21 | #include // assert 22 | #include "queue.h" 23 | 24 | 25 | static size_t queue_array_pos(const size_t, const size_t, const size_t); 26 | static void queue_increase_capacity(struct FSQLF_queue * const); 27 | 28 | 29 | void FSQLF_queue_init(struct FSQLF_queue * const q, size_t isize) 30 | { 31 | q->length = 0; 32 | q->start = 0; 33 | q->capacity = FSQLF_QUEUE_INIT_CAPACITY; 34 | q->isize = isize; 35 | q->items = (char*) malloc(q->isize * q->capacity); 36 | assert(q->items != NULL); 37 | } 38 | 39 | 40 | void FSQLF_queue_clear(struct FSQLF_queue * const q) 41 | { 42 | q->length = 0; 43 | q->start = 0; 44 | q->capacity = 0; 45 | free(q->items); 46 | } 47 | 48 | 49 | void *FSQLF_queue_alloc_back(struct FSQLF_queue * const q) 50 | { 51 | if (q->length == q->capacity) { 52 | queue_increase_capacity(q); 53 | } 54 | assert(q->length < q->capacity); 55 | size_t arr_pos = queue_array_pos(q->length, q->start, q->capacity); 56 | q->length++; 57 | return q->items + arr_pos * q->isize; 58 | } 59 | 60 | 61 | void FSQLF_queue_push_back(struct FSQLF_queue * const q, const void * const item) 62 | { 63 | char *place_for_new_item = (char*) FSQLF_queue_alloc_back(q); 64 | memcpy(place_for_new_item, item, q->isize); 65 | } 66 | 67 | 68 | void FSQLF_queue_drop_head(struct FSQLF_queue * const q) 69 | { 70 | assert(q->length > 0); 71 | q->start++; 72 | if (q->start == q->capacity) q->start = 0; // Wrap past the end. 73 | q->length--; 74 | } 75 | 76 | 77 | void *FSQLF_queue_peek_n(const struct FSQLF_queue * const q, const size_t n) 78 | { 79 | assert(n < q->length); 80 | assert(q->length <= q->capacity); 81 | size_t arr_pos = queue_array_pos(n, q->start, q->capacity); 82 | return q->items + arr_pos * q->isize; 83 | } 84 | 85 | 86 | int FSQLF_queue_empty(const struct FSQLF_queue * const q) 87 | { 88 | return q->length == 0; 89 | } 90 | 91 | 92 | // Helper function for converting queue position to internal array position. 93 | // Queue is implemented in array, where queue can start not at zero 94 | // and elements can wrap past the end. 95 | // (without such wrapping capabilities 96 | // it would be needed to constantly re-order elements) 97 | /// 98 | // Lets imagine internal array of capacity 5 99 | // and queue that starts at element 2 in internal array: 100 | // 0 1 2 3 4 <- internal array positions 101 | // 3 4 0 1 2 <- queue elements 102 | static size_t queue_array_pos(const size_t que_n, 103 | const size_t que_start, 104 | const size_t arr_capacity) 105 | { 106 | if (que_n < 0) assert(0); 107 | if (que_n >= arr_capacity) assert(0); 108 | if (arr_capacity == 0) assert(0); 109 | 110 | size_t size_till_end = arr_capacity-que_start; 111 | size_t r = que_n < size_till_end ? 112 | que_n + que_start : // If fits into remaining array. 113 | que_n - size_till_end; // If element wrapps past the end. 114 | 115 | assert(r >= 0); 116 | assert(r < arr_capacity); 117 | 118 | return r; 119 | } 120 | 121 | 122 | // Helper function for increasing capacity of internal array. 123 | static void queue_increase_capacity(struct FSQLF_queue * const q) 124 | { 125 | size_t old_cap = q->capacity; 126 | q->capacity *= 2; 127 | q->items = (char*) realloc(q->items, q->isize * q->capacity); 128 | assert(q->items != NULL); 129 | // Copy elements that had to wrap past end, 130 | // to new space available at the end. 131 | size_t i, old_pos, new_pos; 132 | for (i = 0; i < (q->length); i++) { 133 | old_pos = queue_array_pos(i, q->start, old_cap); 134 | new_pos = queue_array_pos(i, q->start, q->capacity); 135 | memcpy(q->items + new_pos * q->isize, 136 | q->items + old_pos * q->isize, q->isize); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /utils/queue/queue.h: -------------------------------------------------------------------------------- 1 | // Queue implementation. 2 | /// 3 | // Macros for configuration: 4 | // FSQLF_QUEUE_INIT_CAPACITY - initial capacity (optional; defaults to 16) 5 | /// 6 | // Defined names for queue usage: 7 | // (other names should not be used) 8 | // struct FSQLF_queue - queue struct 9 | // struct FSQLF_queue.length - number of items in the queue (size_t). 10 | // void FSQLF_queue_init(struct FSQLF_queue*) - initialize queue. 11 | // void FSQLF_queue_clear(struct FSQLF_queue*) - free resources, make queue unusable. 12 | // void *FSQLF_queue_alloc_back(struct FSQLF_queue * const q) - Allocate item onto the back. 13 | // void FSQLF_queue_push_back(struct FSQLF_queue*, void*) - Add item onto the back. 14 | // void FSQLF_queue_drop_head(struct FSQLF_queue*) - Remove item from head. 15 | // void *FSQLF_queue_peek_n(struct FSQLF_queue*, size_t) - Return certain element. 16 | // int FSQLF_queue_empty(struct FSQLF_queue*) - Check if queue is empty. 17 | 18 | 19 | #ifndef QUEUE_H 20 | #define QUEUE_H 21 | 22 | 23 | #include 24 | 25 | 26 | #ifndef FSQLF_QUEUE_INIT_CAPACITY 27 | #define FSQLF_QUEUE_INIT_CAPACITY (16) 28 | #endif 29 | 30 | 31 | struct FSQLF_queue 32 | { 33 | // Internal array for item storage. 34 | // (see queue_array_pos() for info about positions used for queue items) 35 | char *items; 36 | 37 | // Position of first queue item in internal array. 38 | size_t start; 39 | 40 | // Size of the queue - number of elements currently in the queue. 41 | size_t length; 42 | 43 | // Size of internal array - max number of elements possible without realloc. 44 | size_t capacity; 45 | 46 | // Size of individual item. 47 | size_t isize; 48 | }; 49 | 50 | 51 | void FSQLF_queue_init(struct FSQLF_queue * const q, size_t isize); 52 | void FSQLF_queue_clear(struct FSQLF_queue * const q); 53 | void *FSQLF_queue_alloc_back(struct FSQLF_queue * const q); 54 | void FSQLF_queue_push_back(struct FSQLF_queue * const q, const void * const item); 55 | void FSQLF_queue_drop_head(struct FSQLF_queue * const q); 56 | void *FSQLF_queue_peek_n(const struct FSQLF_queue * const q, const size_t n); 57 | int FSQLF_queue_empty(const struct FSQLF_queue * const q); 58 | 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /utils/queue/queue_test.c: -------------------------------------------------------------------------------- 1 | #define QUEUE_ITEM_T int 2 | #include 3 | #define FSQLF_QUEUE_INIT_CAPACITY (1) 4 | #include "queue.h" 5 | #include "queue.c" 6 | 7 | 8 | 9 | void test_queue() 10 | { 11 | QUEUE_ITEM_T item1 = 11; 12 | QUEUE_ITEM_T item2 = 22; 13 | QUEUE_ITEM_T item3 = 33; 14 | QUEUE_ITEM_T item4 = 44; 15 | 16 | QUEUE_ITEM_T tmp; 17 | 18 | struct FSQLF_queue tq; 19 | 20 | // At first 21 | FSQLF_queue_init(&tq, sizeof(int)); 22 | // ..queue should be empty. 23 | assert(FSQLF_queue_empty(&tq)); 24 | 25 | // After adding one item 26 | FSQLF_queue_push_back(&tq, &item1); 27 | // .. queue should become non-empty 28 | // .. and length should be 1. 29 | assert(!FSQLF_queue_empty(&tq)); 30 | assert((tq.length) == 1); 31 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 11); 32 | 33 | // Push_back some more items 34 | FSQLF_queue_push_back(&tq, &item2); 35 | FSQLF_queue_push_back(&tq, &item3); 36 | FSQLF_queue_push_back(&tq, &item4); 37 | // .. and do the checks again. 38 | assert(!FSQLF_queue_empty(&tq)); 39 | assert(tq.length==4); 40 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 11); 41 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 22); 42 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 33); 43 | assert(*(int*)FSQLF_queue_peek_n(&tq, 3) == 44); 44 | 45 | // Now drop_head and push_back couple of times repeatedly. 46 | // (to check if wrapping works correctly) 47 | FSQLF_queue_drop_head(&tq); 48 | assert(tq.length==3); 49 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 22); 50 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 33); 51 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 44); 52 | 53 | FSQLF_queue_push_back(&tq, &item3); 54 | assert(tq.length==4); 55 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 22); 56 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 33); 57 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 44); 58 | assert(*(int*)FSQLF_queue_peek_n(&tq, 3) == 33); 59 | 60 | FSQLF_queue_drop_head(&tq); 61 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 33); 62 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 44); 63 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 33); 64 | 65 | FSQLF_queue_push_back(&tq, &item2); 66 | assert(tq.length==4); 67 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 33); 68 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 44); 69 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 33); 70 | assert(*(int*)FSQLF_queue_peek_n(&tq, 3) == 22); 71 | 72 | FSQLF_queue_drop_head(&tq); 73 | assert(tq.length==3); 74 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 44); 75 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 33); 76 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 22); 77 | 78 | FSQLF_queue_push_back(&tq, &item1); 79 | assert(tq.length==4); 80 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 44); 81 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 33); 82 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 22); 83 | assert(*(int*)FSQLF_queue_peek_n(&tq, 3) == 11); 84 | 85 | FSQLF_queue_push_back(&tq, &item2); 86 | FSQLF_queue_push_back(&tq, &item1); 87 | FSQLF_queue_push_back(&tq, &item3); 88 | FSQLF_queue_push_back(&tq, &item4); 89 | 90 | assert(tq.length==8); 91 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 44); 92 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 33); 93 | assert(*(int*)FSQLF_queue_peek_n(&tq, 2) == 22); 94 | assert(*(int*)FSQLF_queue_peek_n(&tq, 3) == 11); 95 | assert(*(int*)FSQLF_queue_peek_n(&tq, 4) == 22); 96 | assert(*(int*)FSQLF_queue_peek_n(&tq, 5) == 11); 97 | assert(*(int*)FSQLF_queue_peek_n(&tq, 6) == 33); 98 | assert(*(int*)FSQLF_queue_peek_n(&tq, 7) == 44); 99 | 100 | // Now dropp till it's empty. 101 | FSQLF_queue_drop_head(&tq); assert(tq.length == 7); 102 | FSQLF_queue_drop_head(&tq); assert(tq.length == 6); 103 | FSQLF_queue_drop_head(&tq); assert(tq.length == 5); 104 | FSQLF_queue_drop_head(&tq); assert(tq.length == 4); 105 | FSQLF_queue_drop_head(&tq); assert(tq.length == 3); 106 | FSQLF_queue_drop_head(&tq); assert(tq.length == 2); 107 | FSQLF_queue_drop_head(&tq); assert(tq.length == 1); 108 | FSQLF_queue_drop_head(&tq); assert(tq.length == 0); 109 | assert(FSQLF_queue_empty(&tq)); 110 | } 111 | 112 | 113 | void test_queue_internals() 114 | { 115 | struct FSQLF_queue tq; 116 | FSQLF_queue_init(&tq, sizeof(int)); 117 | 118 | FSQLF_queue_push_back(&tq, &(int){0}); 119 | FSQLF_queue_push_back(&tq, &(int){1}); 120 | 121 | assert(tq.start == 0); 122 | assert(tq.length == 2); 123 | assert(tq.capacity == 2); 124 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 0); 125 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 1); 126 | 127 | FSQLF_queue_drop_head(&tq); 128 | FSQLF_queue_push_back(&tq, &(int){2}); 129 | 130 | assert(tq.start == 1); 131 | assert(tq.length == 2); 132 | assert(tq.capacity == 2); 133 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 1); 134 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 2); 135 | 136 | FSQLF_queue_drop_head(&tq); 137 | FSQLF_queue_push_back(&tq, &(int){3}); 138 | 139 | assert(tq.start == 0); 140 | assert(tq.length == 2); 141 | assert(tq.capacity == 2); 142 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 2); 143 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 3); 144 | 145 | FSQLF_queue_drop_head(&tq); 146 | FSQLF_queue_push_back(&tq, &(int){4}); 147 | 148 | assert(tq.start == 1); 149 | assert(tq.length == 2); 150 | assert(tq.capacity == 2); 151 | assert(*(int*)FSQLF_queue_peek_n(&tq, 0) == 3); 152 | assert(*(int*)FSQLF_queue_peek_n(&tq, 1) == 4); 153 | } 154 | 155 | 156 | void test_queue_array_pos() 157 | { 158 | size_t i; 159 | for (i = 0; i < 10; i++) assert(queue_array_pos(i, 0, 10) == i); 160 | 161 | // Lets imagine internal array of capacity 5 162 | // and queue that starts at element 2 in internal array: 163 | // 0 1 2 3 4 <- internal array positions 164 | // 3 4 0 1 2 <- queue elements 165 | assert(queue_array_pos(0, 2, 5) == 2); 166 | assert(queue_array_pos(1, 2, 5) == 3); 167 | assert(queue_array_pos(2, 2, 5) == 4); 168 | assert(queue_array_pos(3, 2, 5) == 0); 169 | assert(queue_array_pos(4, 2, 5) == 1); 170 | } 171 | 172 | 173 | int main() 174 | { 175 | test_queue_array_pos(); 176 | test_queue_internals(); 177 | test_queue(); 178 | return 0; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /utils/stack/stack.c: -------------------------------------------------------------------------------- 1 | // Generic stack (with lots of void*) 2 | 3 | 4 | #include 5 | #include // malloc, realloc 6 | #include // memcpy 7 | #include "stack.h" 8 | 9 | 10 | 11 | #define STATE_STACK_SIZE (100) 12 | 13 | 14 | void FSQLF_stack_init(struct FSQLF_stack *stk, size_t isize) 15 | { 16 | stk->length = 0; 17 | stk->isize = isize; 18 | stk->capacity = 100; 19 | stk->items = malloc(stk->isize * stk->capacity); 20 | assert(stk->items); 21 | } 22 | 23 | 24 | void FSQLF_stack_push(struct FSQLF_stack *stk, const void *newitem) 25 | { 26 | assert(stk->length >= 0 && stk->length < STATE_STACK_SIZE); 27 | memcpy(stk->items + stk->length * stk->isize, newitem, stk->isize); 28 | stk->length++; 29 | } 30 | 31 | 32 | void *FSQLF_stack_pop(struct FSQLF_stack *stk) 33 | { 34 | stk->length--; 35 | assert(stk->length >= 0 && stk->length < stk->capacity); 36 | return stk->items + stk->length * stk->isize; 37 | } 38 | 39 | 40 | void *FSQLF_stack_peek(const struct FSQLF_stack *stk) 41 | { 42 | assert(stk->length > 0 && stk->length < STATE_STACK_SIZE); 43 | return stk->items + (stk->length-1) * stk->isize; 44 | } 45 | 46 | 47 | int FSQLF_stack_empty(const struct FSQLF_stack *stk) 48 | { 49 | return stk->length == 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /utils/stack/stack.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_H 2 | #define STACK_H 3 | 4 | 5 | struct FSQLF_stack 6 | { 7 | // last item => items[length-1] 8 | // first item => items[0] 9 | // stack empty => length=0 10 | size_t length; 11 | size_t isize; 12 | size_t capacity; 13 | char *items; 14 | }; 15 | 16 | 17 | void FSQLF_stack_init(struct FSQLF_stack *stk, size_t isize); 18 | void FSQLF_stack_push(struct FSQLF_stack *stk, const void *newitem); 19 | void *FSQLF_stack_pop(struct FSQLF_stack *stk); 20 | void *FSQLF_stack_peek(const struct FSQLF_stack *stk); 21 | int FSQLF_stack_empty(const struct FSQLF_stack *stk); 22 | 23 | 24 | #endif 25 | // gcc -c stack.c -o stack.o && gcc -c stack_test_new.c -o stack_test_new.o && gcc stack_test_new.o stack.o && ./a.out 26 | -------------------------------------------------------------------------------- /utils/stack/stack_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stack.h" 4 | 5 | 6 | void print_stack_struct(const struct FSQLF_stack *stk) 7 | { 8 | int i; 9 | printf("Dump: %d %d", stk->length, stk->isize); 10 | for (i = 0; i < stk->length * stk->isize; ++i) { 11 | if (i % stk->isize == 0) printf("\n"); 12 | printf("%d ", ((char*)stk->items)[i]); 13 | } 14 | puts("\n"); 15 | fflush(stdout); 16 | } 17 | 18 | 19 | void stack_testing_suite() 20 | { 21 | puts("stack_testing_suite:start"); 22 | 23 | struct FSQLF_stack t; // t=test 24 | FSQLF_stack_init(&t, sizeof(int)); 25 | assert(FSQLF_stack_empty(&t)); 26 | 27 | FSQLF_stack_push(&t, &(int){1}); 28 | assert(!FSQLF_stack_empty(&t)); 29 | assert(*(int*) FSQLF_stack_peek(&t) == 1); 30 | FSQLF_stack_push(&t, &(int){3}); 31 | assert(*(int*) FSQLF_stack_peek(&t) == 3); 32 | FSQLF_stack_push(&t, &(int){9}); 33 | assert(*(int*) FSQLF_stack_peek(&t) == 9); 34 | FSQLF_stack_push(&t, &(int){41}); 35 | assert(*(int*) FSQLF_stack_peek(&t) == 41); 36 | 37 | assert(*(int*) FSQLF_stack_pop(&t) == 41); 38 | assert(*(int*) FSQLF_stack_peek(&t) == 9); 39 | assert(*(int*) FSQLF_stack_pop(&t) == 9); 40 | assert(*(int*) FSQLF_stack_peek(&t) == 3); 41 | assert(*(int*) FSQLF_stack_pop(&t) == 3); 42 | 43 | FSQLF_stack_push(&t, &(int){123}); 44 | assert(*(int*) FSQLF_stack_peek(&t) == 123); 45 | 46 | assert(*(int*) FSQLF_stack_pop(&t) == 123); 47 | assert(*(int*) FSQLF_stack_peek(&t) == 1); 48 | assert(*(int*) FSQLF_stack_pop(&t) == 1); 49 | 50 | assert(FSQLF_stack_empty(&t)); 51 | 52 | puts("stack_testing_suite:success\n"); 53 | } 54 | 55 | 56 | void main() 57 | { 58 | stack_testing_suite(); 59 | } 60 | -------------------------------------------------------------------------------- /utils/string/read_int.c: -------------------------------------------------------------------------------- 1 | #include // NULL 2 | #include // isdigit 3 | #include // INT_MAX 4 | #include // assert 5 | #include "read_int.h" 6 | 7 | 8 | // Parse positive integer. Skip leading spaces. 9 | // Only leading spaces and adjacent digits are read. 10 | // @text - text from which we want to extract integer 11 | // @lim - limit characters of text length 12 | // @result - output param for parsed integer 13 | // (if value in string is too large to fit into int, then INT_MAX is set) 14 | // return value - number of chars read (on error 0 is returned) 15 | // 16 | // Rationale for existance. 17 | // Q. Why not just use strtol? 18 | // A. strtol returns 0 on failure to parse. 19 | // This makes it impossible to distinguish between parsed 0 and failure. 20 | size_t FSQLF_read_int(const char *text, size_t lim, int *result) 21 | { 22 | assert(text != NULL); 23 | assert(result != NULL); 24 | 25 | size_t chars_read = 0; 26 | size_t digits_read = 0; 27 | int tmp_res = 0; 28 | 29 | char no_digit_yet = 1; // bool - no digits found yet? 30 | for (size_t i = 0; i < lim; ++i) { 31 | char c = text[i]; 32 | no_digit_yet = no_digit_yet && !isdigit(c); // Mark found digit. 33 | 34 | if (no_digit_yet) { 35 | if (c == ' ') { 36 | ++chars_read; 37 | continue; // Skip leading spaces. 38 | } else { 39 | return 0; // 0 read. 40 | } 41 | } else { 42 | if (isdigit(c)) { 43 | // Read the digits number 44 | int digit = c - '0'; 45 | assert(digit < 10); 46 | assert(digit >= 0); 47 | 48 | if (tmp_res <= INT_MAX / 10) { 49 | tmp_res *= 10; 50 | } else { 51 | tmp_res = INT_MAX; 52 | } 53 | 54 | if (tmp_res < INT_MAX - digit) { 55 | tmp_res += digit; 56 | } else { 57 | tmp_res = INT_MAX; 58 | } 59 | 60 | ++chars_read; 61 | ++digits_read; 62 | } else { 63 | break; // Number has ended. 64 | } 65 | } 66 | } 67 | 68 | if (digits_read > 0) { 69 | *result = tmp_res; 70 | return chars_read; 71 | } else { 72 | return 0; 73 | } 74 | } 75 | 76 | 77 | size_t FSQLF_read_int_array(const char *text, size_t lim, size_t n, int *result) 78 | { 79 | size_t total_cnt = 0; // Cumulative char count. 80 | for (int i = 0; i < n; ++i) { 81 | size_t cnt = FSQLF_read_int(text + total_cnt, lim - total_cnt, result + i); 82 | if (cnt > 0) { 83 | total_cnt += cnt; 84 | } else { 85 | return 0; 86 | } 87 | } 88 | 89 | return total_cnt; 90 | } 91 | -------------------------------------------------------------------------------- /utils/string/read_int.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_INT_H 2 | #define READ_INT_H 3 | 4 | 5 | #include // size_t 6 | 7 | 8 | size_t FSQLF_read_int(const char *text, size_t lim, int *result); 9 | size_t FSQLF_read_int_array(const char *text, size_t lim, size_t n, int *result); 10 | 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /utils/string/test_read_int.c: -------------------------------------------------------------------------------- 1 | #include // assert 2 | #include // INT_MAX 3 | #include "read_int.h" 4 | 5 | 6 | int main() 7 | { 8 | int r; 9 | int cnt = 1; 10 | 11 | // Invalid number: starting with leading space. 12 | r = 7789; 13 | cnt = FSQLF_read_int(" z15", 6, &r); 14 | assert(cnt == 0); 15 | assert(r == 7789); // unchanged 16 | 17 | // Invalid number: empty string 18 | r = 7789; 19 | cnt = FSQLF_read_int("", 6, &r); 20 | assert(cnt == 0); 21 | assert(r == 7789); // unchanged 22 | 23 | // Invalid number: only spaces 24 | r = 7789; 25 | cnt = FSQLF_read_int(" ", 6, &r); 26 | assert(cnt == 0); 27 | assert(r == 7789); // unchanged 28 | 29 | // Valid number: limted by @lim 30 | r = 7789; 31 | cnt = FSQLF_read_int("1234", 2, &r); 32 | assert(cnt == 2); 33 | assert(r == 12); 34 | 35 | // Valid number: limted by @lim 36 | r = 7789; 37 | cnt = FSQLF_read_int(" 1234", 2, &r); 38 | assert(cnt == 2); 39 | assert(r == 1); 40 | 41 | // Number too big to fit into int 42 | r = 7789; 43 | cnt = FSQLF_read_int("9234567890123456789012345678901234567890 1", 1000, &r); 44 | assert(cnt == 40); 45 | assert(r == INT_MAX); 46 | 47 | 48 | // Demo: incrementaly read numbers from string. 49 | const char *txt = "12 345 6789"; 50 | 51 | cnt = FSQLF_read_int(txt, 6, &r); 52 | assert(cnt == 2); 53 | assert(r == 12); 54 | txt += cnt; // txt = " 345 6789" 55 | 56 | cnt = FSQLF_read_int(txt, 6, &r); 57 | assert(cnt == 4); // 1 space + 3 digits 58 | assert(r == 345); 59 | txt += cnt; // txt = " 6789" 60 | 61 | cnt = FSQLF_read_int(txt, 6, &r); 62 | assert(cnt == 6); // 2 space + 4 digits 63 | assert(r == 6789); 64 | txt += cnt; // txt = "" 65 | 66 | cnt = FSQLF_read_int(txt, 6, &r); 67 | assert(cnt == 0); // 2 space + 4 digits 68 | assert(r == 6789); 69 | 70 | 71 | // Tests for FSQLF_read_int_array 72 | int rs[10]; 73 | 74 | // Valid input 75 | cnt = FSQLF_read_int_array("1 2 3", 5, 3, rs); 76 | assert(cnt == 5); 77 | assert(rs[0] == 1); 78 | assert(rs[1] == 2); 79 | assert(rs[2] == 3); 80 | 81 | // One of numbers is invalid 82 | cnt = FSQLF_read_int_array("1 a 3", 5, 3, rs); 83 | assert(cnt == 0); 84 | 85 | // Less values exist in string then requested 86 | cnt = FSQLF_read_int_array("1 2 ", 5, 3, rs); 87 | assert(cnt == 0); 88 | 89 | // String contains more value then requested 90 | cnt = FSQLF_read_int_array("1 2 3 4", 20, 3, rs); 91 | assert(cnt == 5); 92 | 93 | // String contains enough values, but limit is set too low 94 | cnt = FSQLF_read_int_array("1 2 3 4", 3, 3, rs); 95 | assert(cnt == 0); 96 | } 97 | 98 | // gcc read_int.c -std=c99 -c -o read_int.o && gcc test_read_int.c -std=c99 -c -o test_read_int.o && gcc *.o && ./a.out 99 | -------------------------------------------------------------------------------- /utils/text_to_header/text_to_header.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void print_usage_info(const char *progname) 6 | { 7 | printf("Usage:\n"); 8 | printf(" %s \n", progname); 9 | } 10 | 11 | 12 | void exit_error_invalid_arguments(int argc, const char *progname) 13 | { 14 | printf("Error: Invalid number of arguments.\n" 15 | " Number of arguments supplied: %d.\n" 16 | " Number of arguments expected: 3.\n", argc-1); 17 | print_usage_info(progname); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | 22 | void exit_error_input_file_opening(const char* fname, const char *progname) 23 | { 24 | printf("Error: Failed to open input file '%s'.\n", fname); 25 | print_usage_info(progname); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | 30 | void exit_error_output_file_opening(const char* fname, const char *progname) 31 | { 32 | printf("Error: Failed to open output file '%s'.\n", fname); 33 | print_usage_info(progname); 34 | exit(EXIT_FAILURE); 35 | } 36 | 37 | 38 | int main(int argc, char **argv) 39 | { 40 | if (argc != 4) exit_error_invalid_arguments(argc, argv[0]); 41 | FILE *input = fopen(argv[1], "r"); 42 | if (!input) exit_error_input_file_opening(argv[1], argv[0]); 43 | FILE *output = fopen(argv[2], "w"); 44 | if (!output) exit_error_output_file_opening(argv[2], argv[0]); 45 | 46 | fprintf(output, "#ifndef %s_H\n", argv[3]); 47 | fprintf(output, "#define %s_H\n\n\n", argv[3]); 48 | fprintf(output, "const char *%s =\n", argv[3]); 49 | 50 | int counter = 0; 51 | int ch; 52 | while ((ch = fgetc(input)) != EOF) { 53 | ++counter; 54 | if (counter == 1) fprintf(output, " \""); 55 | else if (counter % 10 == 1) fprintf(output, "\"\n \""); 56 | fprintf(output, "\\x%02x", ch); 57 | } 58 | fprintf(output, "\";\n\n\n"); 59 | fprintf(output, "const size_t %s_LEN = %d;\n\n\n", argv[3], counter); 60 | fprintf(output, "#endif // %s_H\n", argv[3]); 61 | 62 | 63 | fclose(input); 64 | fclose(output); 65 | return EXIT_SUCCESS; 66 | } 67 | --------------------------------------------------------------------------------