Skip to content

Commit

Permalink
Version 0.1.9
Browse files Browse the repository at this point in the history
### Added
- Documentation on the header.
- Several assertions on test_xlsx_load_sheet().
- README.md
- LICENSE

### Changed
- Some variables were renamed to reflect Excel naming.
- Error codes so they are unique.
- xlsx_read_cell() return. Now returns 1 or 0 depending on success or
failure.
- Some code style towards simpleness.
  • Loading branch information
damian-m-g committed Jan 10, 2021
1 parent a9fdb14 commit 59059f6
Show file tree
Hide file tree
Showing 18 changed files with 577 additions and 162 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.idea/
_in/
cmake-build-debug/
cmake-build-debug-mingw/
doc/
temp/
!temp/keep
PROGRAMMERNOTES.md
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.9] - 2020-01-10
### Added
- Documentation on the header.
- Several assertions on test_xlsx_load_sheet().
- README.md
- LICENSE

### Changed
- Some variables were renamed to reflect Excel naming.
- Error codes so they are unique.
- xlsx_read_cell() return. Now returns 1 or 0 depending on success or failure.
- Some code style towards simpleness.

## [0.1.8-alpha] - 2020-01-04
### Fixed
- When a cell has no value, but style set, it was triggering problems. Code has been written to take this into account.
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.9)
project(porcupine C)
project(xlsx_drone C)

set(CMAKE_C_STANDARD 11)

add_library(porcupine SHARED ext/miniz.h ext/zip.c ext/zip.h ext/sxmlc.c ext/sxmlc.h ext/sxmlsearch.c ext/sxmlsearch.h src/library.c src/library.h)
add_library(xlsx_drone SHARED ext/miniz.h ext/zip.c ext/zip.h ext/sxmlc.c ext/sxmlc.h ext/sxmlsearch.c ext/sxmlsearch.h src/xlsx_drone.c src/xlsx_drone.h)
14 changes: 14 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Copyright 2021 Damián M. G.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
132 changes: 132 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# xlsx_drone

![](https://img.shields.io/endpoint?url=https%3A//gist.githubusercontent.com/IgorJorobus/362840022499885806ad277f9d26ab99/raw/b3f375ce2433d2b9c64baa2853a2cf0d35deaed7/gcov.json)
![](https://img.shields.io/endpoint?url=https%3A//gist.githubusercontent.com/IgorJorobus/0a91b9186b72bff47cd5b93f656d69f6/raw/ea0674cd21903bbedb7bcee80cda2535307f3602/assertions.json)
![](https://img.shields.io/badge/C%20standard-C11-informational)

Fast _Microsoft Excel's_ **\*.xlsx** reader.

## Table of contents

* [Features](#features)
* [Installation](#installation)
* [Usage](#usage)
* [Essential](#essential)
* [Extended](#extended)
* [Project Status](#project-status)
* [Credits & Thanks](#credits--thanks)
* [License](#license)

## Features

* Doesn't use any external application to parse them.
* Focus on speed over functionality.
* Simple interface.
* UTF-8 support.

## Installation

Straightforward copy and paste of the _src_ and _ext_ folders into your project root folder and `#include "xlsx_drone.h"` in your source code. It's understandable that you might want to accommodate files differently, just note that _xlsx_drone.h_ calls its deppendencies with relative paths:

```c
// external libraries
#include "../ext/zip.h"
#include "../ext/sxmlc.h"
#include "../ext/sxmlsearch.h"
```

..., you might want to modify that according to your needs.

**NOTE**: You'll also find several dynamic library releases inside the _share_ folder for the current version that could be of usage.

## Usage

### Essential

```c
// open *.xlsx
xlsx_workbook_t wb;
xlsx_open("foo.xlsx", &wb);
// load sheet
xlsx_sheet_t *sheet_1 = xlsx_load_sheet(&wb, 1, NULL);
// read cell
xlsx_cell_t cell_data_holder;
xlsx_read_cell(sheet_1, 4, "B", &cell_data_holder);
// inspect result
switch(cell_data_holder.value_type) {
case XLSX_POINTER_TO_CHAR:
printf("Cell 4B has value: %s", cell_data_holder.value.pointer_to_char_value);
break;
case XLSX_INT:
printf("Cell 4B has value: %d", cell_data_holder.value.int_value);
break;
case XLSX_LONG_LONG:
printf("Cell 4B has value: %lld", cell_data_holder.value.long_long_value);
break;
case XLSX_DOUBLE:
printf("Cell 4B has value: %f", cell_data_holder.value.double_value);
break;
default:
printf("Cell 4B has no value");
}
// you can also inspect the cell category
int cell_category = cell_data_holder.style->related_category
```
...which is one of:
```c
typedef enum xlsx_cell_category {
XLSX_NUMBER, // int, long long, or double
XLSX_TEXT, // string
XLSX_DATE, // int
XLSX_TIME, // double
XLSX_DATE_TIME, // double
XLSX_UNKNOWN
} xlsx_cell_category;
```

...which is a summary of what can be set in Excel:

![](data/README.md_images/7f067bd3.png)

Note that:

* _XLSX_TIME_ category is represented as a double between 0 and 1.
* _XLSX_DATE_ category is represented as an int that stars with 1 for the first day of 1900 and raises until the last day of 9999. Values that fall out of the range are represented as text (string) although they will be pointed as _XLSX_DATE_ if the cell has that category manually fixed.
* _XLSX_DATE_TIME_ category is a combination of XLSX_TIME and XLSX_DATE.

### Extended

This section shows brief descriptions of extra features of the library.

```c
// sets on and off errors printing in stdout (default: on (1))
void xlsx_set_print_err_messages(int flag);
// inspect xlsx_errno to know what happened after some function fails
int xlsx_get_xlsx_errno(void);

// if memory is of your concern, you can unload any loaded sheet if it's of no use
void xlsx_unload_sheet(xlsx_sheet_t *sheet);
```
The rest of the public functions will return 0 or NULL if fail, and 1 or a valid pointer for success. When they return 0 or NULL, you can check `xlsx_errno` and compare it against the respective error values of the function.
**For full documentation of the API, check _src/xlsx_drone.h_.**
## Project Status
- [x] Reading capabilities for ASCII
- [x] Reading capabilities for UTF-8
- [ ] Writting capabilities (WIP)
## Credits & Thanks
* Matthieu Labas for [**sxmlc**](http://sxmlc.sourceforge.net/).
* Kuba Podgórski for [**zip**](https://github.com/kuba--/zip).
## License
#### [MIT](TODO: ...)
180 changes: 167 additions & 13 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,186 @@
desc 'compile & run the unit tests'
=begin
TASKS
=end

desc 'compile(x86) & run the unit tests'
task :ct do
build_unit_tests()
run_unit_tests()
if(build_unit_tests())
run_unit_tests()
end
end

desc 'compile unit tests'
desc 'compile(x64) & run the unit tests'
task :ct_64 do
if(build_unit_tests(_64: true))
run_unit_tests()
end
end

desc 'compile(x86) unit tests'
task :c do
build_unit_tests()
end

desc 'compile(x64) unit tests'
task :c_64 do
build_unit_tests(_64: true)
end

desc 'run the unit tests'
task :t do
run_unit_tests()
end

def build_unit_tests
desc 'clear unit tests'
task :clear_ut do
clear_unit_tests()
end

# you will execute this before every new version release
desc 'perform measures & produce badges metadata'
task :badges do
# not important if compiled with x86 or x64
if(build_unit_tests(coverage: true))
# if tests don't pass, code coverage will lie since code next to the problem isn't executed. In that case it's better to leave the last measure
if(run_unit_tests())
measure_code_coverage()
create_test_suite_badge(pass: true)
else
create_test_suite_badge(pass: false)
end
measure_test_assertions()
end
end


=begin
FUNCTIONS
=end

# @param _64 [boolean], @param coverage [boolean]
# The binary files with 0 bytes fill the purpose of showing if the exe was compiled with x86 or x64 compiler.
def build_unit_tests(_64: false, coverage: false)
command = []
command << gcc = 'gcc'
command << debug = '-ggdb' # leave empty if the target exe hasn't the purpose of debugging
if(_64)
File.delete('temp/x64') rescue nil
# locally using mingw compiler that comes packed with RubyInstaller 2.7 (version 10.2)
command << gcc = 'C:/Ruby27-x64/msys64/mingw64/bin/gcc.exe'
else
File.delete('temp/x86') rescue nil
# locally using mingw compiler downloaded from official website (version 9.2)
command << gcc = 'gcc'
end
command << debug = '-ggdb' # prepare exe for debugging purpose (gdb)
if(coverage)
command << coverage = '--coverage' # code coverage support (gcov)
end
command << standard = '-std=c11'
command << warnings = '-w' # libraries use to throw some warnings
command << preprocessing = '-D UNITY_INCLUDE_DOUBLE -D UNITY_SUPPORT_64' # double & long long support
command << directories_included_for_headers_search = '-I ext/ -I src/'
command << source_files = 'ext/zip.c ext/sxmlc.c ext/sxmlsearch.c src/library.c ext/unity.c src/library.test.c'
command << output = '-o bin/unit_tests.exe'
system(command.join(' '))
command << directories_included_for_headers_search = '-I ext/ -I src/ -I test/'
command << source_files = 'ext/zip.c ext/sxmlc.c ext/sxmlsearch.c src/xlsx_drone.c ext/unity.c test/xlsx_drone.test.c'
command << output = '-o temp/unit_tests.exe'
# output command to trigger
puts command.join(' ')
# will return true if were no problems
if(system(command.join(' ')))
print("\nCompile successful")
if(_64)
File.open('temp/x64', 'wb') {|f|}
print("(x64).\n\n")
else
File.open('temp/x86', 'wb') {|f|}
print("(x86).\n\n")
end
true
else
puts("\nCompile failed.")
false
end
end

# @return [boolean]
def run_unit_tests
system("bin/unit_tests")
end
result = system("temp/unit_tests")
puts()
result
end

def clear_unit_tests
File.delete('temp/unit_tests.exe') rescue nil
File.delete('temp/x86') rescue nil
File.delete('temp/x64') rescue nil
puts('Clear completed.')
end

# Executes gcov for the library source code according to the test suite. Prepares a JSON to be consumed by shields.io and then shown in README.md.
def measure_code_coverage
# at this point *.gcda & *.gcno files gets generated in project root dir
temp_gcov_path = 'temp/gcov.txt'
system("gcov -o . src/xlsx_drone.c > #{temp_gcov_path}")
# that generated several *.gcov files, what matters is the command output
covered = \
File.open(temp_gcov_path) do |f|
f.read.match(/executed:(\d+[.,]?\d{0,2}%)/).[](1).sub(/,/, '.') # i.e.: "32.43%"
end
# create the *.json file rdy to be consumed by shields.io
require 'json'
# inquire matching color according to the amount of code covered
color = \
case (covered.to_f)
when (80..100)
'brightgreen'
when (60..80)
'green'
when (40..60)
'yellowgreen'
else
'orange'
end
# produce the json string
json = {
'schemaVersion': 1,
'label': 'coverage',
'message': covered,
'color': color
}.to_json
# write the file
File.open('data/shields/gcov.json', 'w:utf-8:utf-8') {|f| f.write(json)}
# remove garbage
require 'fileutils'
FileUtils.remove(Dir['*.{gcda,gcno,gcov}'] << temp_gcov_path, force: true)
# provide some output
puts("Done. Code coverage: #{covered}.")
end

# Measure the amount of test assertions written and produce a JSON file rdy to be consumed by shields.io.
def measure_test_assertions
# collect the data
assertions = File.open('test/xlsx_drone.test.c') {|f| f.read.scan(/TEST_ASSERT/).size.to_s}
# produce the json string
require 'json'
json = {
'schemaVersion': 1,
'label': 'test assertions',
'message': assertions,
'color': 'informational'
}.to_json
# write the file
File.open('data/shields/assertions.json', 'w:utf-8:utf-8') {|f| f.write(json)}
# provide some output
puts("Assertions: #{assertions}.")
end

# @param pass [boolean]
def create_test_suite_badge(pass:)
# produce the json string
require 'json'
json = {
'schemaVersion': 1,
'label': 'test suite',
'message': pass ? 'pass' : 'fail',
'color': pass ? 'brightgreen' : 'red'
}.to_json
# write the file
File.open('data/shields/test_suite.json', 'w:utf-8:utf-8') {|f| f.write(json)}
end
2 changes: 1 addition & 1 deletion StartGDBServer.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Rem comiple unit_tests.exe
cmd /c rake c
Rem run the gdbserver
start gdbserver localhost:2159 bin/unit_tests.exe
start gdbserver localhost:2159 test/unit_tests.exe
Binary file added data/README.md_images/7f067bd3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 59059f6

Please sign in to comment.