mirror of
https://github.com/igrr/mkspiffs.git
synced 2025-05-08 19:40:40 +08:00
Initial commit
This commit is contained in:
commit
18bb3c49be
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.o
|
||||
make_spiffs
|
||||
make_spiffs.exe
|
70
Makefile
Normal file
70
Makefile
Normal file
@ -0,0 +1,70 @@
|
||||
CFLAGS ?= -std=gnu99 -Os -Wall
|
||||
CXXFLAGS ?= -std=c++11 -Os -Wall
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
TARGET_OS := WINDOWS
|
||||
DIST_SUFFIX := windows
|
||||
ARCHIVE_CMD := 7z a
|
||||
ARCHIVE_EXTENSION := zip
|
||||
TARGET := make_spiffs.exe
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
TARGET_OS := LINUX
|
||||
UNAME_P := $(shell uname -p)
|
||||
ifeq ($(UNAME_P),x86_64)
|
||||
DIST_SUFFIX := linux64
|
||||
endif
|
||||
ifneq ($(filter %86,$(UNAME_P)),)
|
||||
DIST_SUFFIX := linux32
|
||||
endif
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
TARGET_OS := OSX
|
||||
DIST_SUFFIX := osx
|
||||
endif
|
||||
ARCHIVE_CMD := tar czf
|
||||
ARCHIVE_EXTENSION := tar.gz
|
||||
TARGET := make_spiffs
|
||||
endif
|
||||
|
||||
VERSION ?= $(shell git describe --always)
|
||||
|
||||
OBJ := main.o \
|
||||
spiffs/spiffs_cache.o \
|
||||
spiffs/spiffs_check.o \
|
||||
spiffs/spiffs_gc.o \
|
||||
spiffs/spiffs_hydrogen.o \
|
||||
spiffs/spiffs_nucleus.o \
|
||||
|
||||
INCLUDES := -Itclap -Ispiffs -I.
|
||||
|
||||
CFLAGS += $(TARGET_CFLAGS)
|
||||
CXXFLAGS += $(TARGET_CXXFLAGS)
|
||||
|
||||
CPPFLAGS += $(INCLUDES) -D$(TARGET_OS) -DVERSION=\"$(VERSION)\"
|
||||
|
||||
DIST_NAME := make_spiffs-$(VERSION)-$(DIST_SUFFIX)
|
||||
DIST_DIR := $(DIST_NAME)
|
||||
DIST_ARCHIVE := $(DIST_NAME).$(ARCHIVE_EXTENSION)
|
||||
|
||||
.PHONY: all clean dist
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
dist: checkdirs $(TARGET) $(DIST_DIR)
|
||||
cp $(TARGET) $(DIST_DIR)/
|
||||
$(ARCHIVE_CMD) $(DIST_ARCHIVE) $(DIST_DIR)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
g++ $^ -o $@
|
||||
strip $(TARGET)
|
||||
|
||||
|
||||
$(DIST_DIR):
|
||||
@mkdir -p $@
|
||||
|
||||
clean:
|
||||
@rm *.o
|
||||
@rm spiffs/*.o
|
||||
@rm -f $(TARGET)
|
290
main.cpp
Normal file
290
main.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
//
|
||||
// main.cpp
|
||||
// make_spiffs
|
||||
//
|
||||
// Created by Ivan Grokhotkov on 13/05/15.
|
||||
// Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
//
|
||||
#define TCLAP_SETBASE_ZERO 1
|
||||
|
||||
#include <iostream>
|
||||
#include "spiffs/spiffs.h"
|
||||
#include <vector>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include "tclap/CmdLine.h"
|
||||
#include "tclap/UnlabeledValueArg.h"
|
||||
|
||||
#define LOG_PAGE_SIZE 256
|
||||
|
||||
static std::vector<uint8_t> s_flashmem;
|
||||
static std::string s_dirName;
|
||||
static std::string s_imageName;
|
||||
static int s_imageSize;
|
||||
|
||||
enum Action { ACTION_NONE, ACTION_PACK, ACTION_LIST };
|
||||
static Action s_action = ACTION_NONE;
|
||||
|
||||
static spiffs _filesystemStorageHandle;
|
||||
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
|
||||
static u8_t spiffs_fds[32*4];
|
||||
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4];
|
||||
|
||||
|
||||
static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst)
|
||||
{
|
||||
memcpy(dst, &s_flashmem[0] + addr, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src)
|
||||
{
|
||||
memcpy(&s_flashmem[0] + addr, src, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
static s32_t api_spiffs_erase(u32_t addr, u32_t size)
|
||||
{
|
||||
memset(&s_flashmem[0] + addr, 0xff, size);
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
spiffs_config spiffs_get_storage_config()
|
||||
{
|
||||
spiffs_config cfg = {0};
|
||||
|
||||
cfg.phys_addr = 0x0000;
|
||||
cfg.phys_size = (u32_t) s_flashmem.size();
|
||||
|
||||
const int flash_sector_size = 4096;
|
||||
const int log_page_size = 256;
|
||||
|
||||
cfg.phys_erase_block = flash_sector_size; // according to datasheet
|
||||
cfg.log_block_size = flash_sector_size * 2; // Important to make large
|
||||
cfg.log_page_size = log_page_size; // as we said
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void check_callback(spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2)
|
||||
{
|
||||
}
|
||||
|
||||
void spiffs_mount(bool init)
|
||||
{
|
||||
spiffs_config cfg = spiffs_get_storage_config();
|
||||
|
||||
// debugf("fs.start:%x, size:%d Kb\n", cfg.phys_addr, cfg.phys_size / 1024);
|
||||
|
||||
cfg.hal_read_f = api_spiffs_read;
|
||||
cfg.hal_write_f = api_spiffs_write;
|
||||
cfg.hal_erase_f = api_spiffs_erase;
|
||||
|
||||
int res = SPIFFS_mount(&_filesystemStorageHandle,
|
||||
&cfg,
|
||||
spiffs_work_buf,
|
||||
spiffs_fds,
|
||||
sizeof(spiffs_fds),
|
||||
spiffs_cache,
|
||||
sizeof(spiffs_cache),
|
||||
NULL);
|
||||
|
||||
// debugf("mount res: %d\n", res);
|
||||
if (init) {
|
||||
spiffs_file fd = SPIFFS_open(&_filesystemStorageHandle, "tmp", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
|
||||
uint8_t tmp = 0xff;
|
||||
SPIFFS_write(&_filesystemStorageHandle, fd, &tmp, 1);
|
||||
SPIFFS_fremove(&_filesystemStorageHandle, fd);
|
||||
SPIFFS_close(&_filesystemStorageHandle, fd);
|
||||
} else {
|
||||
_filesystemStorageHandle.check_cb_f = &check_callback;
|
||||
//SPIFFS_check(&_filesystemStorageHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void spiffs_unmount()
|
||||
{
|
||||
uint32_t total, used;
|
||||
SPIFFS_info(&_filesystemStorageHandle, &total, &used);
|
||||
SPIFFS_vis(&_filesystemStorageHandle);
|
||||
std::cerr << "total: " << total << ", used: " << used << std::endl;
|
||||
SPIFFS_unmount(&_filesystemStorageHandle);
|
||||
}
|
||||
|
||||
int add_file(char* name, const char* path) {
|
||||
const size_t write_size = 512;
|
||||
uint8_t write_block[write_size];
|
||||
|
||||
FILE* src = fopen(path, "rb");
|
||||
if (!src) {
|
||||
std::cerr << "error: failed to open " << path << " for reading" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
spiffs_file dst = SPIFFS_open(&_filesystemStorageHandle, name, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
|
||||
|
||||
fseek(src, 0, SEEK_END);
|
||||
size_t size = ftell(src);
|
||||
fseek(src, 0, SEEK_SET);
|
||||
|
||||
size_t left = size;
|
||||
while (left > 0)
|
||||
{
|
||||
size_t will_write = std::min(left, write_size);
|
||||
if (will_write != fread(write_block, 1, will_write, src)) {
|
||||
std::cerr << "error: failed to read from file";
|
||||
fclose(src);
|
||||
SPIFFS_close(&_filesystemStorageHandle, dst);
|
||||
return 1;
|
||||
}
|
||||
SPIFFS_write(&_filesystemStorageHandle, dst, write_block, write_size);
|
||||
left -= will_write;
|
||||
}
|
||||
|
||||
SPIFFS_close(&_filesystemStorageHandle, dst);
|
||||
fclose(src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_files(const char* dirname)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
if ((dir = opendir (dirname)) != NULL) {
|
||||
while ((ent = readdir (dir)) != NULL) {
|
||||
if (ent->d_name[0] == '.')
|
||||
continue;
|
||||
std::string fullpath = dirname;
|
||||
fullpath += '/';
|
||||
fullpath += ent->d_name;
|
||||
struct stat path_stat;
|
||||
stat (fullpath.c_str(), &path_stat);
|
||||
if (!S_ISREG(path_stat.st_mode)) {
|
||||
std::cerr << "skipping " << ent->d_name << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cerr << "adding " << ent->d_name << std::endl;
|
||||
if (add_file(ent->d_name, fullpath.c_str()) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir (dir);
|
||||
} else {
|
||||
std::cerr << "warning: can't read source directory" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void listFiles() {
|
||||
spiffs_DIR dir;
|
||||
spiffs_dirent ent;
|
||||
|
||||
spiffs_DIR* res = SPIFFS_opendir(&_filesystemStorageHandle, 0, &dir);
|
||||
spiffs_dirent* it;
|
||||
while (true) {
|
||||
it = SPIFFS_readdir(&dir, &ent);
|
||||
if (!it)
|
||||
break;
|
||||
|
||||
std::cout << it->size << '\t' << it->name << std::endl;
|
||||
}
|
||||
SPIFFS_closedir(&dir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int actionPack() {
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
FILE* fdres = fopen(s_imageName.c_str(), "wb");
|
||||
if (!fdres) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
spiffs_mount(true);
|
||||
add_files(s_dirName.c_str());
|
||||
spiffs_unmount();
|
||||
|
||||
|
||||
fwrite(&s_flashmem[0], 4, s_flashmem.size()/4, fdres);
|
||||
|
||||
fclose(fdres);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int actionList() {
|
||||
s_flashmem.resize(s_imageSize, 0xff);
|
||||
|
||||
FILE* fdsrc = fopen(s_imageName.c_str(), "rb");
|
||||
if (!fdsrc) {
|
||||
std::cerr << "error: failed to open image file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
fread(&s_flashmem[0], 4, s_flashmem.size()/4, fdsrc);
|
||||
fclose(fdsrc);
|
||||
|
||||
spiffs_mount(false);
|
||||
listFiles();
|
||||
spiffs_unmount();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int processArgs(int argc, const char** argv) {
|
||||
|
||||
TCLAP::CmdLine cmd("", ' ', "0.1");
|
||||
TCLAP::ValueArg<std::string> packArg( "c", "create", "create spiffs image from a directory", true, "", "pack_dir");
|
||||
TCLAP::SwitchArg listArg( "l", "list", "list spiffs image to a directory", false);
|
||||
TCLAP::UnlabeledValueArg<std::string> outNameArg( "image_file", "spiffs image file", true, "", "image_file" );
|
||||
TCLAP::ValueArg<int> sizeArg( "s", "fs_size", "fs image size, in bytes", false, 0xb000, "number" );
|
||||
|
||||
cmd.add( sizeArg );
|
||||
cmd.xorAdd( packArg, listArg );
|
||||
cmd.add( outNameArg );
|
||||
cmd.parse( argc, argv );
|
||||
|
||||
if (packArg.isSet()) {
|
||||
s_dirName = packArg.getValue();
|
||||
s_action = ACTION_PACK;
|
||||
}
|
||||
else if (listArg.isSet()) {
|
||||
s_action = ACTION_LIST;
|
||||
}
|
||||
|
||||
s_imageName = outNameArg.getValue();
|
||||
s_imageSize = sizeArg.getValue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
try {
|
||||
processArgs(argc, argv);
|
||||
}
|
||||
catch(...) {
|
||||
std::cerr << "Invalid arguments" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
switch (s_action) {
|
||||
case ACTION_PACK: return actionPack();
|
||||
case ACTION_LIST: return actionList();
|
||||
default: ;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
20
spiffs/LICENSE
Normal file
20
spiffs/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976<at>gmail.com)
|
||||
|
||||
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.
|
506
spiffs/spiffs.h
Normal file
506
spiffs/spiffs.h
Normal file
@ -0,0 +1,506 @@
|
||||
/*
|
||||
* spiffs.h
|
||||
*
|
||||
* Created on: May 26, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef SPIFFS_H_
|
||||
#define SPIFFS_H_
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "spiffs_config.h"
|
||||
|
||||
#define SPIFFS_OK 0
|
||||
#define SPIFFS_ERR_NOT_MOUNTED -10000
|
||||
#define SPIFFS_ERR_FULL -10001
|
||||
#define SPIFFS_ERR_NOT_FOUND -10002
|
||||
#define SPIFFS_ERR_END_OF_OBJECT -10003
|
||||
#define SPIFFS_ERR_DELETED -10004
|
||||
#define SPIFFS_ERR_NOT_FINALIZED -10005
|
||||
#define SPIFFS_ERR_NOT_INDEX -10006
|
||||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
|
||||
#define SPIFFS_ERR_FILE_CLOSED -10008
|
||||
#define SPIFFS_ERR_FILE_DELETED -10009
|
||||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
|
||||
#define SPIFFS_ERR_IS_INDEX -10011
|
||||
#define SPIFFS_ERR_IS_FREE -10012
|
||||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
|
||||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
|
||||
#define SPIFFS_ERR_INDEX_REF_FREE -10015
|
||||
#define SPIFFS_ERR_INDEX_REF_LU -10016
|
||||
#define SPIFFS_ERR_INDEX_REF_INVALID -10017
|
||||
#define SPIFFS_ERR_INDEX_FREE -10018
|
||||
#define SPIFFS_ERR_INDEX_LU -10019
|
||||
#define SPIFFS_ERR_INDEX_INVALID -10020
|
||||
#define SPIFFS_ERR_NOT_WRITABLE -10021
|
||||
#define SPIFFS_ERR_NOT_READABLE -10022
|
||||
#define SPIFFS_ERR_CONFLICTING_NAME -10023
|
||||
#define SPIFFS_ERR_NOT_CONFIGURED -10024
|
||||
|
||||
#define SPIFFS_ERR_NOT_A_FS -10025
|
||||
#define SPIFFS_ERR_MOUNTED -10026
|
||||
#define SPIFFS_ERR_ERASE_FAIL -10027
|
||||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
|
||||
|
||||
|
||||
#define SPIFFS_ERR_INTERNAL -10050
|
||||
|
||||
#define SPIFFS_ERR_TEST -10100
|
||||
|
||||
|
||||
// spiffs file descriptor index type. must be signed
|
||||
typedef s16_t spiffs_file;
|
||||
// spiffs file descriptor flags
|
||||
typedef u16_t spiffs_flags;
|
||||
// spiffs file mode
|
||||
typedef u16_t spiffs_mode;
|
||||
// object type
|
||||
typedef u8_t spiffs_obj_type;
|
||||
|
||||
/* spi read call function type */
|
||||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
||||
/* spi write call function type */
|
||||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
|
||||
/* spi erase call function type */
|
||||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
||||
|
||||
/* file system check callback report operation */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_LOOKUP = 0,
|
||||
SPIFFS_CHECK_INDEX,
|
||||
SPIFFS_CHECK_PAGE
|
||||
} spiffs_check_type;
|
||||
|
||||
/* file system check callback report type */
|
||||
typedef enum {
|
||||
SPIFFS_CHECK_PROGRESS = 0,
|
||||
SPIFFS_CHECK_ERROR,
|
||||
SPIFFS_CHECK_FIX_INDEX,
|
||||
SPIFFS_CHECK_FIX_LOOKUP,
|
||||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
|
||||
SPIFFS_CHECK_DELETE_PAGE,
|
||||
SPIFFS_CHECK_DELETE_BAD_FILE,
|
||||
} spiffs_check_report;
|
||||
|
||||
/* file system check callback function */
|
||||
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
|
||||
u32_t arg1, u32_t arg2);
|
||||
|
||||
#ifndef SPIFFS_DBG
|
||||
#define SPIFFS_DBG(...) \
|
||||
print(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_GC_DBG
|
||||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CACHE_DBG
|
||||
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef SPIFFS_CHECK_DBG
|
||||
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/* Any write to the filehandle is appended to end of the file */
|
||||
#define SPIFFS_APPEND (1<<0)
|
||||
/* If the opened file exists, it will be truncated to zero length before opened */
|
||||
#define SPIFFS_TRUNC (1<<1)
|
||||
/* If the opened file does not exist, it will be created before opened */
|
||||
#define SPIFFS_CREAT (1<<2)
|
||||
/* The opened file may only be read */
|
||||
#define SPIFFS_RDONLY (1<<3)
|
||||
/* The opened file may only be writted */
|
||||
#define SPIFFS_WRONLY (1<<4)
|
||||
/* The opened file may be both read and writted */
|
||||
#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
|
||||
/* Any writes to the filehandle will never be cached */
|
||||
#define SPIFFS_DIRECT (1<<5)
|
||||
|
||||
#define SPIFFS_SEEK_SET (0)
|
||||
#define SPIFFS_SEEK_CUR (1)
|
||||
#define SPIFFS_SEEK_END (2)
|
||||
|
||||
#define SPIFFS_TYPE_FILE (1)
|
||||
#define SPIFFS_TYPE_DIR (2)
|
||||
#define SPIFFS_TYPE_HARD_LINK (3)
|
||||
#define SPIFFS_TYPE_SOFT_LINK (4)
|
||||
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#endif
|
||||
|
||||
// phys structs
|
||||
|
||||
// spiffs spi configuration struct
|
||||
typedef struct {
|
||||
// physical read function
|
||||
spiffs_read hal_read_f;
|
||||
// physical write function
|
||||
spiffs_write hal_write_f;
|
||||
// physical erase function
|
||||
spiffs_erase hal_erase_f;
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
// physical size of the spi flash
|
||||
u32_t phys_size;
|
||||
// physical offset in spi flash used for spiffs,
|
||||
// must be on block boundary
|
||||
u32_t phys_addr;
|
||||
// physical size when erasing a block
|
||||
u32_t phys_erase_block;
|
||||
|
||||
// logical size of a block, must be on physical
|
||||
// block size boundary and must never be less than
|
||||
// a physical block
|
||||
u32_t log_block_size;
|
||||
// logical size of a page, must be at least
|
||||
// log_block_size / 8
|
||||
u32_t log_page_size;
|
||||
#endif
|
||||
} spiffs_config;
|
||||
|
||||
typedef struct {
|
||||
// file system configuration
|
||||
spiffs_config cfg;
|
||||
// number of logical blocks
|
||||
u32_t block_count;
|
||||
|
||||
// cursor for free blocks, block index
|
||||
spiffs_block_ix free_cursor_block_ix;
|
||||
// cursor for free blocks, entry index
|
||||
int free_cursor_obj_lu_entry;
|
||||
// cursor when searching, block index
|
||||
spiffs_block_ix cursor_block_ix;
|
||||
// cursor when searching, entry index
|
||||
int cursor_obj_lu_entry;
|
||||
|
||||
// primary work buffer, size of a logical page
|
||||
u8_t *lu_work;
|
||||
// secondary work buffer, size of a logical page
|
||||
u8_t *work;
|
||||
// file descriptor memory area
|
||||
u8_t *fd_space;
|
||||
// available file descriptors
|
||||
u32_t fd_count;
|
||||
|
||||
// last error
|
||||
s32_t err_code;
|
||||
|
||||
// current number of free blocks
|
||||
u32_t free_blocks;
|
||||
// current number of busy pages
|
||||
u32_t stats_p_allocated;
|
||||
// current number of deleted pages
|
||||
u32_t stats_p_deleted;
|
||||
// flag indicating that garbage collector is cleaning
|
||||
u8_t cleaning;
|
||||
// max erase count amongst all blocks
|
||||
spiffs_obj_id max_erase_count;
|
||||
|
||||
#if SPIFFS_GC_STATS
|
||||
u32_t stats_gc_runs;
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
// cache memory
|
||||
void *cache;
|
||||
// cache size
|
||||
u32_t cache_size;
|
||||
#if SPIFFS_CACHE_STATS
|
||||
u32_t cache_hits;
|
||||
u32_t cache_misses;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// check callback function
|
||||
spiffs_check_callback check_cb_f;
|
||||
|
||||
// mounted flag
|
||||
u8_t mounted;
|
||||
// config magic
|
||||
u32_t config_magic;
|
||||
} spiffs;
|
||||
|
||||
/* spiffs file status struct */
|
||||
typedef struct {
|
||||
spiffs_obj_id obj_id;
|
||||
u32_t size;
|
||||
spiffs_obj_type type;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
} spiffs_stat;
|
||||
|
||||
struct spiffs_dirent {
|
||||
spiffs_obj_id obj_id;
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
spiffs_obj_type type;
|
||||
u32_t size;
|
||||
spiffs_page_ix pix;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
spiffs *fs;
|
||||
spiffs_block_ix block;
|
||||
int entry;
|
||||
} spiffs_DIR;
|
||||
|
||||
// functions
|
||||
|
||||
/**
|
||||
* Initializes the file system dynamic parameters and mounts the filesystem.
|
||||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
||||
* if the flash does not contain a recognizable file system.
|
||||
* In this case, SPIFFS_format must be called prior to remounting.
|
||||
* @param fs the file system struct
|
||||
* @param config the physical and logical configuration of the file system
|
||||
* @param work a memory work buffer comprising 2*config->log_page_size
|
||||
* bytes used throughout all file system operations
|
||||
* @param fd_space memory for file descriptors
|
||||
* @param fd_space_size memory size of file descriptors
|
||||
* @param cache memory for cache, may be null
|
||||
* @param cache_size memory size of cache
|
||||
* @param check_cb_f callback function for reporting during consistency checks
|
||||
*/
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f);
|
||||
|
||||
/**
|
||||
* Unmounts the file system. All file handles will be flushed of any
|
||||
* cached writes and closed.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_unmount(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Creates a new file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Opens/creates a file.
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the new file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
|
||||
/**
|
||||
* Opens a file by given dir entry.
|
||||
* Optimization purposes, when traversing a file system with SPIFFS_readdir
|
||||
* a normal SPIFFS_open would need to traverse the filesystem again to find
|
||||
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||
* @param fs the file system struct
|
||||
* @param path the dir entry to the file
|
||||
* @param flags the flags for the open command, can be combinations of
|
||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
||||
* SPIFFS_CREAT will have no effect in this case.
|
||||
* @param mode ignored, for posix compliance
|
||||
*/
|
||||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode);
|
||||
|
||||
/**
|
||||
* Reads from given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf where to put read data
|
||||
* @param len how much to read
|
||||
* @returns number of bytes read, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Writes to given filehandle.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param buf the data to write
|
||||
* @param len how much to write
|
||||
* @returns number of bytes written, or -1 if error
|
||||
*/
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
/**
|
||||
* Moves the read/write file offset
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle
|
||||
* @param offs how much/where to move the offset
|
||||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
||||
* if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
|
||||
* if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offset
|
||||
*/
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
|
||||
|
||||
/**
|
||||
* Removes a file by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_remove(spiffs *fs, char *path);
|
||||
|
||||
/**
|
||||
* Removes a file by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to remove
|
||||
*/
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Gets file status by path
|
||||
* @param fs the file system struct
|
||||
* @param path the path of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Gets file status by filehandle
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to stat
|
||||
* @param s the stat struct to populate
|
||||
*/
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
|
||||
|
||||
/**
|
||||
* Flushes all pending write operations from cache for given file
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to flush
|
||||
*/
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Closes a filehandle. If there are pending write operations, these are finalized before closing.
|
||||
* @param fs the file system struct
|
||||
* @param fh the filehandle of the file to close
|
||||
*/
|
||||
void SPIFFS_close(spiffs *fs, spiffs_file fh);
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
* @param fs the file system struct
|
||||
* @param old path of file to rename
|
||||
* @param newPath new path of file
|
||||
*/
|
||||
s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath);
|
||||
|
||||
/**
|
||||
* Returns last error of last file operation.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_errno(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Clears last error.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
void SPIFFS_clearerr(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Opens a directory stream corresponding to the given name.
|
||||
* The stream is positioned at the first entry in the directory.
|
||||
* On hydrogen builds the name argument is ignored as hydrogen builds always correspond
|
||||
* to a flat file structure - no directories.
|
||||
* @param fs the file system struct
|
||||
* @param name the name of the directory
|
||||
* @param d pointer the directory stream to be populated
|
||||
*/
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Closes a directory stream
|
||||
* @param d the directory stream to close
|
||||
*/
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d);
|
||||
|
||||
/**
|
||||
* Reads a directory into given spifs_dirent struct.
|
||||
* @param d pointer to the directory stream
|
||||
* @param e the dirent struct to be populated
|
||||
* @returns null if error or end of stream, else given dirent is returned
|
||||
*/
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
|
||||
|
||||
/**
|
||||
* Runs a consistency check on given filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_check(spiffs *fs);
|
||||
|
||||
|
||||
/**
|
||||
* Returns number of total bytes available and number of used bytes.
|
||||
* This is an estimation, and depends on if there a many files with little
|
||||
* data or few files with much data.
|
||||
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
|
||||
* run. This indicates a power loss in midst of things. In worst case
|
||||
* (repeated powerlosses in mending or gc) you might have to delete some files.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param total total number of bytes in filesystem
|
||||
* @param used used number of bytes in filesystem
|
||||
*/
|
||||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
|
||||
|
||||
/**
|
||||
* Formats the entire file system. All data will be lost.
|
||||
* The filesystem must not be mounted when calling this.
|
||||
*
|
||||
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
|
||||
* MUST be called prior to formatting in order to configure the filesystem.
|
||||
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
|
||||
* SPIFFS_format.
|
||||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
* SPIFFS_unmount first.
|
||||
*/
|
||||
s32_t SPIFFS_format(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
*/
|
||||
u8_t SPIFFS_mounted(spiffs *fs);
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
/**
|
||||
* Prints out a visualization of the filesystem.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_vis(spiffs *fs);
|
||||
#endif
|
||||
|
||||
#if SPIFFS_BUFFER_HELP
|
||||
/**
|
||||
* Returns number of bytes needed for the filedescriptor buffer given
|
||||
* amount of file descriptors.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
/**
|
||||
* Returns number of bytes needed for the cache buffer given
|
||||
* amount of cache pages.
|
||||
*/
|
||||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SPIFFS_CHACHE
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPIFFS_H_ */
|
303
spiffs/spiffs_cache.c
Normal file
303
spiffs/spiffs_cache.c
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* spiffs_cache.c
|
||||
*
|
||||
* Created on: Jun 23, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
// returns cached page for give page index, or null if no such cached page
|
||||
static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
cp->pix == pix ) {
|
||||
SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix);
|
||||
cp->last_access = cache->last_access;
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// frees cached page
|
||||
static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
|
||||
if (cache->cpage_use_map & (1<<ix)) {
|
||||
if (write_back &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
|
||||
res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
|
||||
}
|
||||
|
||||
cp->flags = 0;
|
||||
cache->cpage_use_map &= ~(1 << ix);
|
||||
|
||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id);
|
||||
} else {
|
||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// removes the oldest accessed cached page
|
||||
static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
|
||||
// at least one free cpage
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
// all busy, scan thru all to find the cpage which has oldest access
|
||||
int i;
|
||||
int cand_ix = -1;
|
||||
u32_t oldest_val = 0;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->last_access - cp->last_access) > oldest_val &&
|
||||
(cp->flags & flag_mask) == flags) {
|
||||
oldest_val = cache->last_access - cp->last_access;
|
||||
cand_ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (cand_ix >= 0) {
|
||||
res = spiffs_cache_page_free(fs, cand_ix, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// allocates a new cached page and returns it, or null if all cache pages are busy
|
||||
static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
if (cache->cpage_use_map == 0xffffffff) {
|
||||
// out of cache memory
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
if ((cache->cpage_use_map & (1<<i)) == 0) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
cache->cpage_use_map |= (1<<i);
|
||||
cp->last_access = cache->last_access;
|
||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i);
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
// out of cache entries
|
||||
return 0;
|
||||
}
|
||||
|
||||
// drops the cache page for give page index
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
if (cp) {
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
// reads from spi flash or the cache
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst) {
|
||||
(void)fh;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
|
||||
cache->last_access++;
|
||||
if (cp) {
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_hits++;
|
||||
#endif
|
||||
cp->last_access = cache->last_access;
|
||||
} else {
|
||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
|
||||
// for second layer lookup functions, we do not cache in order to prevent shredding
|
||||
return fs->cfg.hal_read_f(
|
||||
addr ,
|
||||
len,
|
||||
dst);
|
||||
}
|
||||
#if SPIFFS_CACHE_STATS
|
||||
fs->cache_misses++;
|
||||
#endif
|
||||
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp) {
|
||||
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
||||
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
}
|
||||
|
||||
s32_t res2 = fs->cfg.hal_read_f(
|
||||
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||
spiffs_get_cache_page(fs, cache, cp->ix));
|
||||
if (res2 != SPIFFS_OK) {
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
|
||||
return res;
|
||||
}
|
||||
|
||||
// writes to spi flash and/or the cache
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src) {
|
||||
(void)fh;
|
||||
spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
|
||||
|
||||
if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
|
||||
// have a cache page
|
||||
// copy in data to cache page
|
||||
|
||||
if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
|
||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
||||
// page is being deleted, wipe from cache - unless it is a lookup page
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
return fs->cfg.hal_write_f(addr, len, src);
|
||||
}
|
||||
|
||||
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
|
||||
memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
|
||||
|
||||
cache->last_access++;
|
||||
cp->last_access = cache->last_access;
|
||||
|
||||
if (cp->flags && SPIFFS_CACHE_FLAG_WRTHRU) {
|
||||
// page is being updated, no write-cache, just pass thru
|
||||
return fs->cfg.hal_write_f(addr, len, src);
|
||||
} else {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
} else {
|
||||
// no cache page, no write cache - just write thru
|
||||
return fs->cfg.hal_write_f(addr, len, src);
|
||||
}
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
// returns the cache page that this fd refers, or null if no cache page
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
|
||||
if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
|
||||
// all cpages free, no cpage cannot be assigned to obj_id
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < cache->cpage_count; i++) {
|
||||
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||
if ((cache->cpage_use_map & (1<<i)) &&
|
||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
|
||||
cp->obj_id == fd->obj_id) {
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocates a new cache page and refers this to given fd - flushes an old cache
|
||||
// page if all cache is busy
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
// before this function is called, it is ensured that there is no already existing
|
||||
// cache page with same object id
|
||||
spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||
spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
|
||||
if (cp == 0) {
|
||||
// could not get cache page
|
||||
return 0;
|
||||
}
|
||||
|
||||
cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
|
||||
cp->obj_id = fd->obj_id;
|
||||
fd->cache_page = cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
// unrefers all fds that this cache page refers to and releases the cache page
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
if (cp == 0) return;
|
||||
u32_t i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) {
|
||||
cur_fd->cache_page = 0;
|
||||
}
|
||||
}
|
||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
||||
|
||||
cp->obj_id = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// initializes the cache
|
||||
void spiffs_cache_init(spiffs *fs) {
|
||||
if (fs->cache == 0) return;
|
||||
u32_t sz = fs->cache_size;
|
||||
u32_t cache_mask = 0;
|
||||
int i;
|
||||
int cache_entries =
|
||||
(sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
if (cache_entries <= 0) return;
|
||||
|
||||
for (i = 0; i < cache_entries; i++) {
|
||||
cache_mask <<= 1;
|
||||
cache_mask |= 1;
|
||||
}
|
||||
|
||||
spiffs_cache cache;
|
||||
memset(&cache, 0, sizeof(spiffs_cache));
|
||||
cache.cpage_count = cache_entries;
|
||||
cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
|
||||
|
||||
cache.cpage_use_map = 0xffffffff;
|
||||
cache.cpage_use_mask = cache_mask;
|
||||
memcpy(fs->cache, &cache, sizeof(spiffs_cache));
|
||||
|
||||
spiffs_cache *c = spiffs_get_cache(fs);
|
||||
|
||||
memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
|
||||
|
||||
c->cpage_use_map &= ~(c->cpage_use_mask);
|
||||
for (i = 0; i < cache.cpage_count; i++) {
|
||||
spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPIFFS_CACHE
|
973
spiffs/spiffs_check.c
Normal file
973
spiffs/spiffs_check.c
Normal file
@ -0,0 +1,973 @@
|
||||
/*
|
||||
* spiffs_check.c
|
||||
*
|
||||
* Contains functionality for checking file system consistency
|
||||
* and mending problems.
|
||||
* Three levels of consistency checks are implemented:
|
||||
*
|
||||
* Look up consistency
|
||||
* Checks if indices in lookup pages are coherent with page headers
|
||||
* Object index consistency
|
||||
* Checks if there are any orphaned object indices (missing object index headers).
|
||||
* If an object index is found but not its header, the object index is deleted.
|
||||
* This is critical for the following page consistency check.
|
||||
* Page consistency
|
||||
* Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed
|
||||
*
|
||||
*
|
||||
* Created on: Jul 7, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
//---------------------------------------
|
||||
// Look up consistency
|
||||
|
||||
// searches in the object indices and returns the referenced page index given
|
||||
// the object id and the data span index
|
||||
// destroys fs->lu_work
|
||||
static s32_t spiffs_object_get_data_page_index_reference(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix data_spix,
|
||||
spiffs_page_ix *pix,
|
||||
spiffs_page_ix *objix_pix) {
|
||||
s32_t res;
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
|
||||
// find obj index for obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// load obj index entry
|
||||
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
|
||||
if (objix_spix == 0) {
|
||||
// get referenced page from object index header
|
||||
addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
|
||||
} else {
|
||||
// get referenced page from object index
|
||||
addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
|
||||
}
|
||||
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// copies page contents to a new page
|
||||
static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) {
|
||||
s32_t res;
|
||||
res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0,
|
||||
SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// rewrites the object index for given object id and replaces the
|
||||
// data page index to a new page index
|
||||
static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) {
|
||||
s32_t res;
|
||||
spiffs_block_ix bix;
|
||||
int entry;
|
||||
spiffs_page_ix free_pix;
|
||||
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
|
||||
|
||||
// find free entry
|
||||
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
if (objix_spix == 0) {
|
||||
// calc index in index header
|
||||
entry = data_spix;
|
||||
} else {
|
||||
// calc entry in index
|
||||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
|
||||
}
|
||||
// load index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
// be ultra safe, double check header against provided data
|
||||
if (objix_p_hdr->obj_id != obj_id) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_OBJ_ID_MISM;
|
||||
}
|
||||
if (objix_p_hdr->span_ix != objix_spix) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_SPIX_MISM;
|
||||
}
|
||||
if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX |
|
||||
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) !=
|
||||
(SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) {
|
||||
spiffs_page_delete(fs, free_pix);
|
||||
return SPIFFS_ERR_CHECK_FLAGS_BAD;
|
||||
}
|
||||
|
||||
// rewrite in mem
|
||||
if (objix_spix == 0) {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
|
||||
} else {
|
||||
((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
|
||||
}
|
||||
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
|
||||
sizeof(spiffs_obj_id),
|
||||
(u8_t *)&obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_page_delete(fs, objix_pix);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// deletes an object just by marking object index header as deleted
|
||||
static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
s32_t res;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t),
|
||||
(u8_t *)&flags);
|
||||
return res;
|
||||
}
|
||||
|
||||
// validates the given look up entry
|
||||
static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
|
||||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
|
||||
(void)cur_block;
|
||||
(void)cur_entry;
|
||||
u8_t delete_page = 0;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix ref_pix;
|
||||
// check validity, take actions
|
||||
if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) ||
|
||||
((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) {
|
||||
// look up entry deleted / free but used in page header
|
||||
SPIFFS_CHECK_DBG("LU: pix %04x deleted/free in lu but not on page\n", cur_pix);
|
||||
*reload_lu = 1;
|
||||
delete_page = 1;
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// header says data page
|
||||
// data page can be removed if not referenced by some object index
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix == cur_pix) {
|
||||
// data page referenced by object index but deleted in lu
|
||||
// copy page to new place and re-write the object index to new place
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: %04x rewritten to %04x, affected objix_pix %04x\n", cur_pix, new_pix, objix_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
} else {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// header says index page
|
||||
// index page can be removed if other index with same obj_id and spanix is found
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no such index page found, check for a data page amongst page headers
|
||||
// lu cannot be trusted
|
||||
res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0);
|
||||
if (res == SPIFFS_OK) { // ignore other errors
|
||||
// got a data page also, assume lu corruption only, rewrite to new page
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
}
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
|
||||
// look up entry used
|
||||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix %04x differ in obj_id lu:%04x ph:%04x\n", cur_pix, lu_obj_id, p_hdr->obj_id);
|
||||
delete_page = 1;
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
|
||||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
|
||||
(p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) {
|
||||
// page deleted or not finalized, just remove it
|
||||
} else {
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
// if data page, check for reference to this page
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if found, rewrite page with object id, update index, and delete current
|
||||
if (ref_pix == cur_pix) {
|
||||
spiffs_page_ix new_pix;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
|
||||
res = spiffs_page_delete(fs, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||
*reload_lu = 1;
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else if index, check for other pages with both obj_id's and spanix
|
||||
spiffs_page_ix objix_pix_lu, objix_pix_ph;
|
||||
// see if other object index page exists for lookup obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// if both obj_id's found, just delete current
|
||||
if (objix_pix_ph == 0 || objix_pix_lu == 0) {
|
||||
// otherwise try finding first corresponding data pages
|
||||
spiffs_page_ix data_pix_lu, data_pix_ph;
|
||||
// see if other data page exists for look up obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_lu = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other data page exists for page header obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_ph = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
spiffs_page_ix new_pix;
|
||||
if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) ||
|
||||
(objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) {
|
||||
// got a data page for page header obj id
|
||||
// rewrite as obj_id_ph
|
||||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
|
||||
(objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) {
|
||||
// got a data page for look up obj id
|
||||
// rewrite as obj_id_lu
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
} else {
|
||||
// cannot safely do anything
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) ||
|
||||
((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) {
|
||||
SPIFFS_CHECK_DBG("LU: %04x lu/page index marking differ\n", cur_pix);
|
||||
spiffs_page_ix data_pix, objix_pix_d;
|
||||
// see if other data page exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// see if other object index exists for given obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
objix_pix_d = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
delete_page = 1;
|
||||
// if other data page exists and object index exists, just delete page
|
||||
if (data_pix && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n");
|
||||
} else
|
||||
// if only data page exists, make this page index
|
||||
if (data_pix && objix_pix_d == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
|
||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else
|
||||
// if only index exists, make data page
|
||||
if (data_pix == 0 && objix_pix_d) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
|
||||
spiffs_page_header new_ph;
|
||||
spiffs_page_ix new_pix;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = p_hdr->span_ix;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// if nothing exists, we cannot safely make a decision - delete
|
||||
}
|
||||
}
|
||||
else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
|
||||
SPIFFS_CHECK_DBG("LU: pix %04x busy in lu but deleted on page\n", cur_pix);
|
||||
delete_page = 1;
|
||||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
|
||||
SPIFFS_CHECK_DBG("LU: pix %04x busy but not final\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
*reload_lu = 1;
|
||||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (ref_pix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n");
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// page referenced by object index but not final
|
||||
// just finalize
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
|
||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
|
||||
sizeof(u8_t), (u8_t*)&flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
|
||||
u32_t user_data, void *user_p) {
|
||||
(void)user_data;
|
||||
(void)user_p;
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
int reload_lu = 0;
|
||||
|
||||
res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Scans all object look up. For each entry, corresponding page header is checked for validity.
|
||||
// If an object index header page is found, this is also checked
|
||||
s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
|
||||
(void)check_all_objects;
|
||||
s32_t res = SPIFFS_OK;
|
||||
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (res != SPIFFS_OK) {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Page consistency
|
||||
|
||||
// Scans all pages (except lu pages), reserves 4 bits in working memory for each page
|
||||
// bit 0: 0 == FREE|DELETED, 1 == USED
|
||||
// bit 1: 0 == UNREFERENCED, 1 == REFERENCED
|
||||
// bit 2: 0 == NOT_INDEX, 1 == INDEX
|
||||
// bit 3: unused
|
||||
// A consistent file system will have only pages being
|
||||
// * x000 free, unreferenced, not index
|
||||
// * x011 used, referenced only once, not index
|
||||
// * x101 used, unreferenced, index
|
||||
// The working memory might not fit all pages so several scans might be needed
|
||||
static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
||||
const u32_t bits = 4;
|
||||
const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits;
|
||||
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix pix_offset = 0;
|
||||
|
||||
// for each range of pages fitting into work memory
|
||||
while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
|
||||
// set this flag to abort all checks and rescan the page range
|
||||
u8_t restart = 0;
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
spiffs_block_ix cur_block = 0;
|
||||
// build consistency bitmap for id range traversing all blocks
|
||||
while (!restart && cur_block < fs->block_count) {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
|
||||
(pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) +
|
||||
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
|
||||
0);
|
||||
|
||||
// traverse each page except for lookup pages
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
|
||||
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
|
||||
// read header
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan);
|
||||
const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits);
|
||||
const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits;
|
||||
|
||||
if (within_range &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) {
|
||||
// used
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0));
|
||||
}
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) &&
|
||||
(p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) {
|
||||
// found non-deleted index
|
||||
if (within_range) {
|
||||
fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2));
|
||||
}
|
||||
|
||||
// load non-deleted index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// traverse index for referenced pages
|
||||
spiffs_page_ix *object_page_index;
|
||||
spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
|
||||
|
||||
int entries;
|
||||
int i;
|
||||
spiffs_span_ix data_spix_offset;
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// object header page index
|
||||
entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
|
||||
data_spix_offset = 0;
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
|
||||
} else {
|
||||
// object page index
|
||||
entries = SPIFFS_OBJ_IX_LEN(fs);
|
||||
data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1);
|
||||
object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix));
|
||||
}
|
||||
|
||||
// for all entries in index
|
||||
for (i = 0; !restart && i < entries; i++) {
|
||||
spiffs_page_ix rpix = object_page_index[i];
|
||||
u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan;
|
||||
|
||||
if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs))
|
||||
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
|
||||
|
||||
// bad reference
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x bad pix / LU referenced from page %04x\n",
|
||||
rpix, cur_pix);
|
||||
// check for data page elsewhere
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, 0, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// if not, allocate free page
|
||||
spiffs_page_header new_ph;
|
||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
||||
new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
new_ph.span_ix = data_spix_offset + i;
|
||||
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ %04x\n", data_pix);
|
||||
}
|
||||
// remap index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix %04x\n", cur_pix);
|
||||
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
|
||||
// delete file
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
} else {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
|
||||
} else if (rpix_within_range) {
|
||||
|
||||
// valid reference
|
||||
// read referenced page header
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// cross reference page header check
|
||||
if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) ||
|
||||
rp_hdr.span_ix != data_spix_offset + i ||
|
||||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x has inconsistent page header ix id/span:%04x/%04x, ref id/span:%04x/%04x flags:%02x\n",
|
||||
rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i,
|
||||
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
|
||||
// try finding correct page
|
||||
spiffs_page_ix data_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
|
||||
data_spix_offset + i, rpix, &data_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
data_pix = 0;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (data_pix == 0) {
|
||||
// not found, this index is badly borked
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
break;
|
||||
} else {
|
||||
// found it, so rewrite index
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix %04x, rewrite ix pix %04x id %04x\n",
|
||||
data_pix, cur_pix, p_hdr.obj_id);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// mark rpix as referenced
|
||||
const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
|
||||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
|
||||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x multiple referenced from page %04x\n",
|
||||
rpix, cur_pix);
|
||||
// Here, we should have fixed all broken references - getting this means there
|
||||
// must be multiple files with same object id. Only solution is to delete
|
||||
// the object which is referring to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n",
|
||||
p_hdr.obj_id, cur_pix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// extra precaution, delete this page also
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
}
|
||||
fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1));
|
||||
}
|
||||
}
|
||||
} // for all index entries
|
||||
} // found index
|
||||
|
||||
// next page
|
||||
cur_pix++;
|
||||
}
|
||||
// next block
|
||||
cur_block++;
|
||||
}
|
||||
// check consistency bitmap
|
||||
if (!restart) {
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix rpix;
|
||||
|
||||
u32_t byte_ix;
|
||||
u8_t bit_ix;
|
||||
for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) {
|
||||
for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) {
|
||||
u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7;
|
||||
spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix;
|
||||
|
||||
// 000 ok - free, unreferenced, not index
|
||||
|
||||
if (bitmask == 0x1) {
|
||||
|
||||
// 001
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x USED, UNREFERENCED, not index\n", cur_pix);
|
||||
|
||||
u8_t rewrite_ix_to_this = 0;
|
||||
u8_t delete_page = 0;
|
||||
// check corresponding object index entry
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix,
|
||||
&rpix, &objix_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) {
|
||||
// pointing to a bad page altogether, rewrite index to this
|
||||
rewrite_ix_to_this = 1;
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is bad: %04x, rewrite to this %04x\n", rpix, cur_pix);
|
||||
} else {
|
||||
// pointing to something else, check what
|
||||
spiffs_page_header rp_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) &&
|
||||
((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) ==
|
||||
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
|
||||
// pointing to something else valid, just delete this page then
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: %04x, delete this %04x\n", rpix, cur_pix);
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// pointing to something weird, update index to point to this page instead
|
||||
if (rpix != cur_pix) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref is weird: %04x %s%s%s%s, rewrite this %04x\n", rpix,
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
|
||||
(rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "",
|
||||
cur_pix);
|
||||
rewrite_ix_to_this = 1;
|
||||
} else {
|
||||
// should not happen, destined for fubar
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete %04x\n", cur_pix);
|
||||
delete_page = 1;
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
if (rewrite_ix_to_this) {
|
||||
// if pointing to invalid page, redirect index to this page
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id %04x data spix %04x to point to this pix: %04x\n",
|
||||
p_hdr.obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
|
||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||
// index bad also, cannot mend this file
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||
} else {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
restart = 1;
|
||||
continue;
|
||||
} else if (delete_page) {
|
||||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
if (bitmask == 0x2) {
|
||||
|
||||
// 010
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, not index\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
|
||||
// 011 ok - busy, referenced, not index
|
||||
|
||||
if (bitmask == 0x4) {
|
||||
|
||||
// 100
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x FREE, unreferenced, INDEX\n", cur_pix);
|
||||
|
||||
// this should never happen, major fubar
|
||||
}
|
||||
|
||||
// 101 ok - busy, unreferenced, index
|
||||
|
||||
if (bitmask == 0x6) {
|
||||
|
||||
// 110
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
if (bitmask == 0x7) {
|
||||
|
||||
// 111
|
||||
SPIFFS_CHECK_DBG("PA: pix %04x USED, REFERENCED, INDEX\n", cur_pix);
|
||||
|
||||
// no op, this should be taken care of when checking valid references
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// next page range
|
||||
if (!restart) {
|
||||
pix_offset += pages_per_scan;
|
||||
}
|
||||
} // while page range not reached end
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks consistency amongst all pages and fixes irregularities
|
||||
s32_t spiffs_page_consistency_check(spiffs *fs) {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
s32_t res = spiffs_page_consistency_check_i(fs);
|
||||
if (res != SPIFFS_OK) {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Object index consistency
|
||||
|
||||
// searches for given object id in temporary object id index,
|
||||
// returns the index or -1
|
||||
static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
|
||||
u32_t i;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) {
|
||||
if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
|
||||
int cur_entry, u32_t user_data, void *user_p) {
|
||||
(void)user_data;
|
||||
s32_t res_c = SPIFFS_VIS_COUNTINUE;
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t *log_ix = (u32_t *)user_p;
|
||||
spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
|
||||
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
|
||||
(cur_block * 256)/fs->block_count, 0);
|
||||
|
||||
if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (p_hdr.span_ix == 0 &&
|
||||
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET)) {
|
||||
SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
|
||||
return res_c;
|
||||
}
|
||||
|
||||
if (p_hdr.span_ix == 0) {
|
||||
// objix header page, register objid as reachable
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
if (r == -1) {
|
||||
// not registered, do it
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
}
|
||||
} else { // span index
|
||||
// objix page, see if header can be found
|
||||
int r = spiffs_object_index_search(fs, obj_id);
|
||||
u8_t delete = 0;
|
||||
if (r == -1) {
|
||||
// not in temporary index, try finding it
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix);
|
||||
res_c = SPIFFS_VIS_COUNTINUE_RELOAD;
|
||||
if (res == SPIFFS_OK) {
|
||||
// found, register as reachable
|
||||
obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// not found, register as unreachable
|
||||
delete = 1;
|
||||
obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||
} else {
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
(*log_ix)++;
|
||||
if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
|
||||
*log_ix = 0;
|
||||
}
|
||||
} else {
|
||||
// in temporary index, check reachable flag
|
||||
if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// registered as unreachable
|
||||
delete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete) {
|
||||
SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n",
|
||||
cur_pix, obj_id, p_hdr.span_ix);
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
} // span index
|
||||
} // valid object index id
|
||||
|
||||
return res_c;
|
||||
}
|
||||
|
||||
// Removes orphaned and partially deleted index pages.
|
||||
// Scans for index pages. When an index page is found, corresponding index header is searched for.
|
||||
// If no such page exists, the index page cannot be reached as no index header exists and must be
|
||||
// deleted.
|
||||
s32_t spiffs_object_index_consistency_check(spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
// impl note:
|
||||
// fs->work is used for a temporary object index memory, listing found object ids and
|
||||
// indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
|
||||
// In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
|
||||
// a reachable/unreachable object id.
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
u32_t obj_id_log_ix = 0;
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix,
|
||||
0, 0);
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
if (res != SPIFFS_OK) {
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
|
||||
}
|
||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
|
||||
return res;
|
||||
}
|
||||
|
232
spiffs/spiffs_config.h
Normal file
232
spiffs/spiffs_config.h
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* spiffs_config.h
|
||||
*
|
||||
* Created on: Jul 3, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#ifndef SPIFFS_CONFIG_H_
|
||||
#define SPIFFS_CONFIG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define IRAM_ATTR
|
||||
#define STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
|
||||
#define STORE_ATTR __attribute__((aligned(4)))
|
||||
|
||||
#define c_memcpy memcpy
|
||||
#define c_printf printf
|
||||
#define c_memset memset
|
||||
|
||||
typedef int16_t file_t;
|
||||
typedef int32_t s32_t;
|
||||
typedef uint32_t u32_t;
|
||||
typedef int16_t s16_t;
|
||||
typedef uint16_t u16_t;
|
||||
typedef int8_t s8_t;
|
||||
typedef uint8_t u8_t;
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0 /* set file offset to offset */
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1 /* set file offset to current plus offset */
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2 /* set file offset to EOF plus offset */
|
||||
#endif
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
// compile time switches
|
||||
#define debugf(fmt, ...) //os_printf(fmt"\r\n", ##__VA_ARGS__)
|
||||
#define SYSTEM_ERROR(fmt, ...) //os_printf("ERROR: " fmt "\r\n", ##__VA_ARGS__)
|
||||
|
||||
// Set generic spiffs debug output call.
|
||||
#ifndef SPIFFS_DGB
|
||||
#define SPIFFS_DBG(...) //os_printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for garbage collecting.
|
||||
#ifndef SPIFFS_GC_DGB
|
||||
#define SPIFFS_GC_DBG(...) //os_printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for caching.
|
||||
#ifndef SPIFFS_CACHE_DGB
|
||||
#define SPIFFS_CACHE_DBG(...) //os_printf(__VA_ARGS__)
|
||||
#endif
|
||||
// Set spiffs debug output call for system consistency checks.
|
||||
#ifndef SPIFFS_CHECK_DGB
|
||||
#define SPIFFS_CHECK_DBG(...) //os_printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// Enable/disable API functions to determine exact number of bytes
|
||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||
// this can be disabled to reduce flash.
|
||||
#ifndef SPIFFS_BUFFER_HELP
|
||||
#define SPIFFS_BUFFER_HELP 0
|
||||
#endif
|
||||
|
||||
// Enables/disable memory read caching of nucleus file system operations.
|
||||
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||
#ifndef SPIFFS_CACHE
|
||||
#define SPIFFS_CACHE 1
|
||||
#endif
|
||||
#if SPIFFS_CACHE
|
||||
// Enables memory write caching for file descriptors in hydrogen
|
||||
#ifndef SPIFFS_CACHE_WR
|
||||
#define SPIFFS_CACHE_WR 1
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||
#ifndef SPIFFS_CACHE_STATS
|
||||
#define SPIFFS_CACHE_STATS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Always check header of each accessed page to ensure consistent state.
|
||||
// If enabled it will increase number of reads, will increase flash.
|
||||
#ifndef SPIFFS_PAGE_CHECK
|
||||
#define SPIFFS_PAGE_CHECK 1
|
||||
#endif
|
||||
|
||||
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||
#ifndef SPIFFS_GC_MAX_RUNS
|
||||
#define SPIFFS_GC_MAX_RUNS 3
|
||||
#endif
|
||||
|
||||
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||
#ifndef SPIFFS_GC_STATS
|
||||
#define SPIFFS_GC_STATS 0
|
||||
#endif
|
||||
|
||||
// Garbage collecting examines all pages in a block which and sums up
|
||||
// to a block score. Deleted pages normally gives positive score and
|
||||
// used pages normally gives a negative score (as these must be moved).
|
||||
// To have a fair wear-leveling, the erase age is also included in score,
|
||||
// whose factor normally is the most positive.
|
||||
// The larger the score, the more likely it is that the block will
|
||||
// picked for garbage collection.
|
||||
|
||||
// Garbage collecting heuristics - weight used for deleted pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_DELET
|
||||
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for used pages.
|
||||
#ifndef SPIFFS_GC_HEUR_W_USED
|
||||
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||
#endif
|
||||
// Garbage collecting heuristics - weight used for time between
|
||||
// last erased and erase of this block.
|
||||
#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE
|
||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||
#endif
|
||||
|
||||
// Object name maximum length.
|
||||
#ifndef SPIFFS_OBJ_NAME_LEN
|
||||
#define SPIFFS_OBJ_NAME_LEN (32)
|
||||
#endif
|
||||
|
||||
// Size of buffer allocated on stack used when copying data.
|
||||
// Lower value generates more read/writes. No meaning having it bigger
|
||||
// than logical page size.
|
||||
#ifndef SPIFFS_COPY_BUFFER_STACK
|
||||
#define SPIFFS_COPY_BUFFER_STACK (64)
|
||||
#endif
|
||||
|
||||
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||
// These should be defined on a multithreaded system
|
||||
|
||||
// define this to entering a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_LOCK
|
||||
#define SPIFFS_LOCK(fs)
|
||||
#endif
|
||||
// define this to exiting a mutex if you're running on a multithreaded system
|
||||
#ifndef SPIFFS_UNLOCK
|
||||
#define SPIFFS_UNLOCK(fs)
|
||||
#endif
|
||||
|
||||
|
||||
// Enable if only one spiffs instance with constant configuration will exist
|
||||
// on the target. This will reduce calculations, flash and memory accesses.
|
||||
// Parts of configuration must be defined below instead of at time of mount.
|
||||
#ifndef SPIFFS_SINGLETON
|
||||
#define SPIFFS_SINGLETON 0
|
||||
#endif
|
||||
|
||||
#if SPIFFS_SINGLETON
|
||||
// Instead of giving parameters in config struct, singleton build must
|
||||
// give parameters in defines below.
|
||||
#ifndef SPIFFS_CFG_PHYS_SZ
|
||||
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_PHYS_ADDR
|
||||
#define SPIFFS_CFG_PHYS_ADDR(ignore) (0)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_PAGE_SZ
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256)
|
||||
#endif
|
||||
#ifndef SPIFFS_CFG_LOG_BLOCK_SZ
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set SPFIFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||
// in the api. This function will visualize all filesystem using given printf
|
||||
// function.
|
||||
#ifndef SPIFFS_TEST_VISUALISATION
|
||||
#define SPIFFS_TEST_VISUALISATION 1
|
||||
#endif
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
#ifndef spiffs_printf
|
||||
#define spiffs_printf(...) c_printf(__VA_ARGS__)
|
||||
#endif
|
||||
// spiffs_printf argument for a free page
|
||||
#ifndef SPIFFS_TEST_VIS_FREE_STR
|
||||
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||
#endif
|
||||
// spiffs_printf argument for a deleted page
|
||||
#ifndef SPIFFS_TEST_VIS_DELE_STR
|
||||
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||
#endif
|
||||
// spiffs_printf argument for an index page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_INDX_STR
|
||||
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||
#endif
|
||||
// spiffs_printf argument for a data page for given object id
|
||||
#ifndef SPIFFS_TEST_VIS_DATA_STR
|
||||
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Types depending on configuration such as the amount of flash bytes
|
||||
// given to spiffs file system in total (spiffs_file_system_size),
|
||||
// the logical block size (log_block_size), and the logical page size
|
||||
// (log_page_size)
|
||||
|
||||
// Block index type. Make sure the size of this type can hold
|
||||
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||
typedef u16_t spiffs_block_ix;
|
||||
// Page index type. Make sure the size of this type can hold
|
||||
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||
typedef u16_t spiffs_page_ix;
|
||||
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||
// size of this type can hold the highest object id on a full system,
|
||||
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||
typedef u16_t spiffs_obj_id;
|
||||
// Object span index type. Make sure the size of this type can
|
||||
// hold the largest possible span index on the system -
|
||||
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||
typedef u16_t spiffs_span_ix;
|
||||
|
||||
#endif /* SPIFFS_CONFIG_H_ */
|
559
spiffs/spiffs_gc.c
Normal file
559
spiffs/spiffs_gc.c
Normal file
@ -0,0 +1,559 @@
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
// Erases a logical block and updates the erase counter.
|
||||
// If cache is enabled, all pages that might be cached in this block
|
||||
// is dropped.
|
||||
static s32_t spiffs_gc_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res;
|
||||
|
||||
SPIFFS_GC_DBG("gc: erase block %i\n", bix);
|
||||
res = spiffs_erase_block(fs, bix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
u32_t i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
// Searches for blocks where all entries are deleted - if one is found,
|
||||
// the block is erased. Compared to the non-quick gc, the quick one ensures
|
||||
// that no updates are needed on existing objects on pages that are erased.
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_quick: running\n", cur_block);
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// find fully deleted blocks
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
} else {
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
// found a fully deleted block
|
||||
fs->stats_p_deleted -= deleted_pages_in_block;
|
||||
res = spiffs_gc_erase_block(fs, cur_block);
|
||||
return res;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks if garbaga collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
s32_t res;
|
||||
s32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 3 &&
|
||||
(s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||
// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
// return SPIFFS_ERR_FULL;
|
||||
// }
|
||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
|
||||
SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
||||
return SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
do {
|
||||
SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
spiffs_block_ix cand;
|
||||
s32_t prev_free_pages = free_pages;
|
||||
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
|
||||
}
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
cand = cands[0];
|
||||
fs->cleaning = 1;
|
||||
//printf("gcing: cleaning block %i\n", cand);
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
fs->cleaning = 0;
|
||||
if (res < 0) {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
|
||||
} else {
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_page_stats(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_block(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
|
||||
if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
|
||||
// abort early to reduce wear, at least tried once
|
||||
SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
(s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
res = SPIFFS_ERR_FULL;
|
||||
}
|
||||
|
||||
SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
|
||||
fs->stats_p_allocated + fs->stats_p_deleted,
|
||||
fs->free_blocks, free_pages, tries, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Updates page statistics for a block that is about to be erased
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
u32_t dele = 0;
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
dele++;
|
||||
} else {
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Finds block candidates to erase
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count,
|
||||
char fs_crammed) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
// divide up work area into block indices and scores
|
||||
// todo alignment?
|
||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
||||
|
||||
*block_candidates = cand_blocks;
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page &&
|
||||
cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
|
||||
// read erase count
|
||||
spiffs_obj_id erase_count;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
||||
SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
|
||||
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
spiffs_obj_id erase_age;
|
||||
if (fs->max_erase_count > erase_count) {
|
||||
erase_age = fs->max_erase_count - erase_count;
|
||||
} else {
|
||||
erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
|
||||
}
|
||||
|
||||
s32_t score =
|
||||
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
|
||||
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
|
||||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
||||
int cand_ix = 0;
|
||||
SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
}
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
}
|
||||
cand_ix++;
|
||||
}
|
||||
(*candidate_count)++;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
MOVE_OBJ_IX,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
spiffs_page_ix cur_objix_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
// scan object lookup again for remaining object index pages, move to new page in other block
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_gc gc;
|
||||
spiffs_page_ix cur_pix = 0;
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
|
||||
SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
|
||||
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
// move free cursor to next block, cannot use free pages from the block we want to clean
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0;
|
||||
|
||||
// scan through lookup pages
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
gc.cur_obj_id = obj_id;
|
||||
scan = 0;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA:
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
|
||||
} else {
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
} else {
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scan = 0;
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
if (res != SPIFFS_OK) break;
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix objix_pix;
|
||||
gc.stored_scan_entry_index = cur_entry;
|
||||
cur_entry = 0;
|
||||
gc.state = MOVE_OBJ_DATA;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
|
||||
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
// store modified objix (hdr) page
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index;
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// store object index header page
|
||||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// store object index page
|
||||
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
|
||||
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_IX:
|
||||
gc.state = FINISHED;
|
||||
break;
|
||||
default:
|
||||
cur_entry = 0;
|
||||
break;
|
||||
}
|
||||
SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
|
||||
} // while state != FINISHED
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
933
spiffs/spiffs_hydrogen.c
Normal file
933
spiffs/spiffs_hydrogen.c
Normal file
@ -0,0 +1,933 @@
|
||||
/*
|
||||
* spiffs_hydrogen.c
|
||||
*
|
||||
* Created on: Jun 16, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh);
|
||||
|
||||
#if SPIFFS_BUFFER_HELP
|
||||
u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) {
|
||||
return num_descs * sizeof(spiffs_fd);
|
||||
}
|
||||
#if SPIFFS_CACHE
|
||||
u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) {
|
||||
return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
u8_t SPIFFS_mounted(spiffs *fs) {
|
||||
return SPIFFS_CHECK_MOUNT(fs);
|
||||
}
|
||||
|
||||
s32_t SPIFFS_format(spiffs *fs) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
if (SPIFFS_CHECK_MOUNT(fs)) {
|
||||
fs->err_code = SPIFFS_ERR_MOUNTED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32_t res;
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_block_ix bix = 0;
|
||||
while (bix < fs->block_count) {
|
||||
fs->max_erase_count = 0;
|
||||
res = spiffs_erase_block(fs, bix);
|
||||
if (res != SPIFFS_OK) {
|
||||
res = SPIFFS_ERR_ERASE_FAIL;
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
bix++;
|
||||
}
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size,
|
||||
spiffs_check_callback check_cb_f) {
|
||||
SPIFFS_LOCK(fs);
|
||||
memset(fs, 0, sizeof(spiffs));
|
||||
memcpy(&fs->cfg, config, sizeof(spiffs_config));
|
||||
fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
fs->work = &work[0];
|
||||
fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
|
||||
memset(fd_space, 0, fd_space_size);
|
||||
// align fd_space pointer to pointer size byte boundary, below is safe
|
||||
u8_t ptr_size = sizeof(void*);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
fd_space += (ptr_size-addr_lsb);
|
||||
fd_space_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
fs->fd_space = fd_space;
|
||||
fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
|
||||
|
||||
// align cache pointer to 4 byte boundary, below is safe
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
addr_lsb = ((u8_t)cache) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
u8_t *cache_8 = (u8_t *)cache;
|
||||
cache_8 += (ptr_size-addr_lsb);
|
||||
cache = cache_8;
|
||||
cache_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
if (cache_size & (ptr_size-1)) {
|
||||
cache_size -= (cache_size & (ptr_size-1));
|
||||
}
|
||||
#if SPIFFS_CACHE
|
||||
fs->cache = cache;
|
||||
fs->cache_size = cache_size;
|
||||
spiffs_cache_init(fs);
|
||||
#endif
|
||||
|
||||
s32_t res;
|
||||
|
||||
#if SPIFFS_USE_MAGIC
|
||||
res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE;
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
#endif
|
||||
|
||||
fs->config_magic = SPIFFS_CONFIG_MAGIC;
|
||||
|
||||
res = spiffs_obj_lu_scan(fs);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
|
||||
SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
|
||||
SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header));
|
||||
SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
|
||||
SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs));
|
||||
SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count);
|
||||
SPIFFS_DBG("free blocks: %i\n", fs->free_blocks);
|
||||
|
||||
fs->check_cb_f = check_cb_f;
|
||||
|
||||
fs->mounted = 1;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPIFFS_unmount(spiffs *fs) {
|
||||
if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return;
|
||||
SPIFFS_LOCK(fs);
|
||||
u32_t i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0) {
|
||||
#if SPIFFS_CACHE
|
||||
(void)spiffs_fflush_cache(fs, cur_fd->file_nbr);
|
||||
#endif
|
||||
spiffs_fd_return(fs, cur_fd->file_nbr);
|
||||
}
|
||||
}
|
||||
fs->mounted = 0;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
}
|
||||
|
||||
s32_t SPIFFS_errno(spiffs *fs) {
|
||||
return fs->err_code;
|
||||
}
|
||||
|
||||
void SPIFFS_clearerr(spiffs *fs) {
|
||||
fs->err_code = SPIFFS_OK;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) {
|
||||
(void)mode;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
spiffs_obj_id obj_id;
|
||||
s32_t res;
|
||||
|
||||
res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (u8_t *)path);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
res = spiffs_object_create(fs, obj_id, (u8_t *)path, SPIFFS_TYPE_FILE, 0);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode) {
|
||||
(void)mode;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
spiffs_page_ix pix;
|
||||
|
||||
s32_t res = spiffs_fd_find_new(fs, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix);
|
||||
if ((flags & SPIFFS_CREAT) == 0) {
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
if ((flags & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) {
|
||||
spiffs_obj_id obj_id;
|
||||
// no need to enter conflicting name here, already looked for it above
|
||||
res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, &pix);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
flags &= ~SPIFFS_TRUNC;
|
||||
} else {
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
res = spiffs_object_open_by_page(fs, pix, fd, flags, mode);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
if (flags & SPIFFS_TRUNC) {
|
||||
res = spiffs_object_truncate(fd, 0, 0);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
fd->fdoffset = 0;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return fd->file_nbr;
|
||||
}
|
||||
|
||||
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
|
||||
s32_t res = spiffs_fd_find_new(fs, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
if (flags & SPIFFS_TRUNC) {
|
||||
res = spiffs_object_truncate(fd, 0, 0);
|
||||
if (res < SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
fd->fdoffset = 0;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return fd->file_nbr;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
s32_t res;
|
||||
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
if ((fd->flags & SPIFFS_RDONLY) == 0) {
|
||||
res = SPIFFS_ERR_NOT_READABLE;
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_fflush_cache(fs, fh);
|
||||
#endif
|
||||
|
||||
if (fd->fdoffset + len >= fd->size) {
|
||||
// reading beyond file size
|
||||
s32_t avail = fd->size - fd->fdoffset;
|
||||
if (avail <= 0) {
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT);
|
||||
}
|
||||
res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf);
|
||||
if (res == SPIFFS_ERR_END_OF_OBJECT) {
|
||||
fd->fdoffset += avail;
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return avail;
|
||||
} else {
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
len = avail;
|
||||
}
|
||||
} else {
|
||||
// reading within file size
|
||||
res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
fd->fdoffset += len;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) {
|
||||
(void)fs;
|
||||
s32_t res = SPIFFS_OK;
|
||||
s32_t remaining = len;
|
||||
if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) {
|
||||
s32_t m_len = MIN((s32_t)(fd->size - offset), len);
|
||||
res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
remaining -= m_len;
|
||||
u8_t *buf_8 = (u8_t *)buf;
|
||||
buf_8 += m_len;
|
||||
buf = buf_8;
|
||||
offset += m_len;
|
||||
}
|
||||
if (remaining > 0) {
|
||||
res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
s32_t res;
|
||||
u32_t offset;
|
||||
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
if ((fd->flags & SPIFFS_WRONLY) == 0) {
|
||||
res = SPIFFS_ERR_NOT_WRITABLE;
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
offset = fd->fdoffset;
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
if (fd->cache_page == 0) {
|
||||
// see if object id is associated with cache already
|
||||
fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
|
||||
}
|
||||
#endif
|
||||
if (fd->flags & SPIFFS_APPEND) {
|
||||
if (fd->size == SPIFFS_UNDEFINED_LEN) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = fd->size;
|
||||
}
|
||||
#if SPIFFS_CACHE_WR
|
||||
if (fd->cache_page) {
|
||||
offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
if ((fd->flags & SPIFFS_DIRECT) == 0) {
|
||||
if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
|
||||
// small write, try to cache it
|
||||
u8_t alloc_cpage = 1;
|
||||
if (fd->cache_page) {
|
||||
// have a cached page for this fd already, check cache page boundaries
|
||||
if (offset < fd->cache_page->offset || // writing before cache
|
||||
offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache
|
||||
offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
|
||||
{
|
||||
// boundary violation, write back cache first and allocate new
|
||||
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n",
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
|
||||
fd->cache_page->offset, fd->cache_page->size);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
} else {
|
||||
// writing within cache
|
||||
alloc_cpage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc_cpage) {
|
||||
fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd);
|
||||
if (fd->cache_page) {
|
||||
fd->cache_page->offset = offset;
|
||||
fd->cache_page->size = 0;
|
||||
SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n",
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (fd->cache_page) {
|
||||
u32_t offset_in_cpage = offset - fd->cache_page->offset;
|
||||
SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n",
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id,
|
||||
offset, offset_in_cpage, len);
|
||||
spiffs_cache *cache = spiffs_get_cache(fs);
|
||||
u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix);
|
||||
memcpy(&cpage_data[offset_in_cpage], buf, len);
|
||||
fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len);
|
||||
fd->fdoffset += len;
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return len;
|
||||
} else {
|
||||
res = spiffs_hydro_write(fs, fd, buf, offset, len);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
fd->fdoffset += len;
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
// big write, no need to cache it - but first check if there is a cached write already
|
||||
if (fd->cache_page) {
|
||||
// write back cache first
|
||||
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n",
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
|
||||
fd->cache_page->offset, fd->cache_page->size);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
res = spiffs_hydro_write(fs, fd, buf, offset, len);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res = spiffs_hydro_write(fs, fd, buf, offset, len);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
fd->fdoffset += len;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
s32_t res;
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_fflush_cache(fs, fh);
|
||||
#endif
|
||||
|
||||
switch (whence) {
|
||||
case SPIFFS_SEEK_CUR:
|
||||
offs = fd->fdoffset+offs;
|
||||
break;
|
||||
case SPIFFS_SEEK_END:
|
||||
offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offs > (s32_t)fd->size) {
|
||||
res = SPIFFS_ERR_END_OF_OBJECT;
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
if (fd->cursor_objix_spix != objix_spix) {
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_span(
|
||||
fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
fd->cursor_objix_spix = objix_spix;
|
||||
fd->cursor_objix_pix = pix;
|
||||
}
|
||||
fd->fdoffset = offs;
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_remove(spiffs *fs, char *path) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
spiffs_page_ix pix;
|
||||
s32_t res;
|
||||
|
||||
res = spiffs_fd_find_new(fs, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_find_object_index_header_by_name(fs, (u8_t *)path, &pix);
|
||||
if (res != SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_open_by_page(fs, pix, fd, 0,0);
|
||||
if (res != SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_truncate(fd, 0, 1);
|
||||
if (res != SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
s32_t res;
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
if ((fd->flags & SPIFFS_WRONLY) == 0) {
|
||||
res = SPIFFS_ERR_NOT_WRITABLE;
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
}
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
#endif
|
||||
|
||||
res = spiffs_object_truncate(fd, 0, 1);
|
||||
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) {
|
||||
spiffs_page_object_ix_header objix_hdr;
|
||||
spiffs_obj_id obj_id;
|
||||
s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh,
|
||||
SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
|
||||
u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) +
|
||||
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id);
|
||||
res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh,
|
||||
obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
|
||||
s->obj_id = obj_id;
|
||||
s->type = objix_hdr.type;
|
||||
s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
|
||||
strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
s32_t res;
|
||||
spiffs_page_ix pix;
|
||||
|
||||
res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_stat_pix(fs, pix, 0, s);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
s32_t res;
|
||||
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_fflush_cache(fs, fh);
|
||||
#endif
|
||||
|
||||
res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks if there are any cached writes for the object id associated with
|
||||
// given filehandle. If so, these writes are flushed.
|
||||
static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
#if SPIFFS_CACHE_WR
|
||||
|
||||
spiffs_fd *fd;
|
||||
res = spiffs_fd_get(fs, fh, &fd);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
|
||||
if ((fd->flags & SPIFFS_DIRECT) == 0) {
|
||||
if (fd->cache_page == 0) {
|
||||
// see if object id is associated with cache already
|
||||
fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
|
||||
}
|
||||
if (fd->cache_page) {
|
||||
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n",
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
|
||||
fd->cache_page->offset, fd->cache_page->size);
|
||||
if (res < SPIFFS_OK) {
|
||||
fs->err_code = res;
|
||||
}
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
s32_t res = SPIFFS_OK;
|
||||
#if SPIFFS_CACHE_WR
|
||||
SPIFFS_LOCK(fs);
|
||||
res = spiffs_fflush_cache(fs, fh);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs,res);
|
||||
SPIFFS_UNLOCK(fs);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void SPIFFS_close(spiffs *fs, spiffs_file fh) {
|
||||
if (!SPIFFS_CHECK_CFG((fs))) {
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SPIFFS_CHECK_MOUNT(fs)) {
|
||||
fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return;
|
||||
}
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
spiffs_fflush_cache(fs, fh);
|
||||
#endif
|
||||
spiffs_fd_return(fs, fh);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
}
|
||||
|
||||
s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_page_ix pix_old, pix_dummy;
|
||||
spiffs_fd *fd;
|
||||
|
||||
s32_t res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)old, &pix_old);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)new, &pix_dummy);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
res = SPIFFS_OK;
|
||||
} else if (res == SPIFFS_OK) {
|
||||
res = SPIFFS_ERR_CONFLICTING_NAME;
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_fd_find_new(fs, &fd);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0);
|
||||
if (res != SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new,
|
||||
0, &pix_dummy);
|
||||
|
||||
if (res != SPIFFS_OK) {
|
||||
spiffs_fd_return(fs, fd->file_nbr);
|
||||
}
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) {
|
||||
(void)name;
|
||||
|
||||
if (!SPIFFS_CHECK_CFG((fs))) {
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!SPIFFS_CHECK_MOUNT(fs)) {
|
||||
fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
d->fs = fs;
|
||||
d->block = 0;
|
||||
d->entry = 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
static s32_t spiffs_read_dir_v(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_block_ix bix,
|
||||
int ix_entry,
|
||||
u32_t user_data,
|
||||
void *user_p) {
|
||||
(void)user_data;
|
||||
s32_t res;
|
||||
spiffs_page_object_ix_header objix_hdr;
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED ||
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) {
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr);
|
||||
if (res != SPIFFS_OK) return res;
|
||||
if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) &&
|
||||
objix_hdr.p_hdr.span_ix == 0 &&
|
||||
(objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
|
||||
struct spiffs_dirent *e = (struct spiffs_dirent *)user_p;
|
||||
e->obj_id = obj_id;
|
||||
strcpy((char *)e->name, (char *)objix_hdr.name);
|
||||
e->type = objix_hdr.type;
|
||||
e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
|
||||
e->pix = pix;
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
|
||||
if (!SPIFFS_CHECK_MOUNT(d->fs)) {
|
||||
d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return 0;
|
||||
}
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_block_ix bix;
|
||||
int entry;
|
||||
s32_t res;
|
||||
struct spiffs_dirent *ret = 0;
|
||||
|
||||
res = spiffs_obj_lu_find_entry_visitor(d->fs,
|
||||
d->block,
|
||||
d->entry,
|
||||
SPIFFS_VIS_NO_WRAP,
|
||||
0,
|
||||
spiffs_read_dir_v,
|
||||
0,
|
||||
e,
|
||||
&bix,
|
||||
&entry);
|
||||
if (res == SPIFFS_OK) {
|
||||
d->block = bix;
|
||||
d->entry = entry + 1;
|
||||
ret = e;
|
||||
} else {
|
||||
d->fs->err_code = res;
|
||||
}
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d) {
|
||||
SPIFFS_API_CHECK_CFG(d->fs);
|
||||
SPIFFS_API_CHECK_MOUNT(d->fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_check(spiffs *fs) {
|
||||
s32_t res;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
res = spiffs_lookup_consistency_check(fs, 0);
|
||||
|
||||
res = spiffs_object_index_consistency_check(fs);
|
||||
|
||||
res = spiffs_page_consistency_check(fs);
|
||||
|
||||
res = spiffs_obj_lu_scan(fs);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
|
||||
u32_t blocks = fs->block_count;
|
||||
u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs);
|
||||
u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page
|
||||
|
||||
if (total) {
|
||||
*total = total_data_pages * data_page_size;
|
||||
}
|
||||
|
||||
if (used) {
|
||||
*used = fs->stats_p_allocated * data_page_size;
|
||||
}
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
s32_t SPIFFS_vis(spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_block_ix bix = 0;
|
||||
|
||||
while (bix < fs->block_count) {
|
||||
// check each object lookup page
|
||||
int obj_lookup_page = 0;
|
||||
int cur_entry = 0;
|
||||
|
||||
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (cur_entry == 0) {
|
||||
spiffs_printf("%4i ", bix);
|
||||
} else if ((cur_entry & 0x3f) == 0) {
|
||||
spiffs_printf(" ");
|
||||
}
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
spiffs_printf(SPIFFS_TEST_VIS_FREE_STR);
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
|
||||
spiffs_printf(SPIFFS_TEST_VIS_DELE_STR);
|
||||
} else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){
|
||||
spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id));
|
||||
} else {
|
||||
spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id));
|
||||
}
|
||||
cur_entry++;
|
||||
if ((cur_entry & 0x3f) == 0) {
|
||||
spiffs_printf("\n");
|
||||
}
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
spiffs_obj_id erase_count;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
||||
SPIFFS_ERASE_COUNT_PADDR(fs, bix),
|
||||
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (erase_count != (spiffs_obj_id)-1) {
|
||||
spiffs_printf("\tera_cnt: %i\n", erase_count);
|
||||
} else {
|
||||
spiffs_printf("\tera_cnt: N/A\n");
|
||||
}
|
||||
|
||||
bix++;
|
||||
} // per block
|
||||
|
||||
spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count);
|
||||
spiffs_printf("last_errno: %i\n", fs->err_code);
|
||||
spiffs_printf("blocks: %i\n", fs->block_count);
|
||||
spiffs_printf("free_blocks: %i\n", fs->free_blocks);
|
||||
spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated);
|
||||
spiffs_printf("page_delet: %i\n", fs->stats_p_deleted);
|
||||
u32_t total, used;
|
||||
SPIFFS_info(fs, &total, &used);
|
||||
spiffs_printf("used: %i of %i\n", used, total);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return res;
|
||||
}
|
||||
#endif
|
1896
spiffs/spiffs_nucleus.c
Normal file
1896
spiffs/spiffs_nucleus.c
Normal file
File diff suppressed because it is too large
Load Diff
718
spiffs/spiffs_nucleus.h
Normal file
718
spiffs/spiffs_nucleus.h
Normal file
@ -0,0 +1,718 @@
|
||||
/*
|
||||
* spiffs_nucleus.h
|
||||
*
|
||||
* Created on: Jun 15, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
/* SPIFFS layout
|
||||
*
|
||||
* spiffs is designed for following spi flash characteristics:
|
||||
* - only big areas of data (blocks) can be erased
|
||||
* - erasing resets all bits in a block to ones
|
||||
* - writing pulls ones to zeroes
|
||||
* - zeroes cannot be pulled to ones, without erase
|
||||
* - wear leveling
|
||||
*
|
||||
* spiffs is also meant to be run on embedded, memory constraint devices.
|
||||
*
|
||||
* Entire area is divided in blocks. Entire area is also divided in pages.
|
||||
* Each block contains same number of pages. A page cannot be erased, but a
|
||||
* block can be erased.
|
||||
*
|
||||
* Entire area must be block_size * x
|
||||
* page_size must be block_size / (2^y) where y > 2
|
||||
*
|
||||
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
|
||||
*
|
||||
* BLOCK 0 PAGE 0 object lookup 1
|
||||
* PAGE 1 object lookup 2
|
||||
* ...
|
||||
* PAGE n-1 object lookup n
|
||||
* PAGE n object data 1
|
||||
* PAGE n+1 object data 2
|
||||
* ...
|
||||
* PAGE n+m-1 object data m
|
||||
*
|
||||
* BLOCK 1 PAGE n+m object lookup 1
|
||||
* PAGE n+m+1 object lookup 2
|
||||
* ...
|
||||
* PAGE 2n+m-1 object lookup n
|
||||
* PAGE 2n+m object data 1
|
||||
* PAGE 2n+m object data 2
|
||||
* ...
|
||||
* PAGE 2n+2m-1 object data m
|
||||
* ...
|
||||
*
|
||||
* n is number of object lookup pages, which is number of pages needed to index all pages
|
||||
* in a block by object id
|
||||
* : block_size / page_size * sizeof(obj_id) / page_size
|
||||
* m is number data pages, which is number of pages in block minus number of lookup pages
|
||||
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
|
||||
* thus, n+m is total number of pages in a block
|
||||
* : block_size / page_size
|
||||
*
|
||||
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
|
||||
*
|
||||
* Object lookup pages contain object id entries. Each entry represent the corresponding
|
||||
* data page.
|
||||
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
|
||||
* An object id being 0x0000 represents a deleted page.
|
||||
*
|
||||
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
|
||||
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
|
||||
* page 2 : data : data for object id 0008
|
||||
* page 3 : data : data for object id 0001
|
||||
* page 4 : data : data for object id 0aaa
|
||||
* ...
|
||||
*
|
||||
*
|
||||
* Object data pages can be either object index pages or object content.
|
||||
* All object data pages contains a data page header, containing object id and span index.
|
||||
* The span index denotes the object page ordering amongst data pages with same object id.
|
||||
* This applies to both object index pages (when index spans more than one page of entries),
|
||||
* and object data pages.
|
||||
* An object index page contains page entries pointing to object content page. The entry index
|
||||
* in a object index page correlates to the span index in the actual object data page.
|
||||
* The first object index page (span index 0) is called object index header page, and also
|
||||
* contains object flags (directory/file), size, object name etc.
|
||||
*
|
||||
* ex:
|
||||
* BLOCK 1
|
||||
* PAGE 256: objectl lookup page 1
|
||||
* [*123] [ 123] [ 123] [ 123]
|
||||
* [ 123] [*123] [ 123] [ 123]
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 257: objectl lookup page 2
|
||||
* [free] [free] [free] [free] ...
|
||||
* PAGE 258: object index page (header)
|
||||
* obj.id:0123 span.ix:0000 flags:INDEX
|
||||
* size:1600 name:ex.txt type:file
|
||||
* [259] [260] [261] [262]
|
||||
* PAGE 259: object data page
|
||||
* obj.id:0123 span.ix:0000 flags:DATA
|
||||
* PAGE 260: object data page
|
||||
* obj.id:0123 span.ix:0001 flags:DATA
|
||||
* PAGE 261: object data page
|
||||
* obj.id:0123 span.ix:0002 flags:DATA
|
||||
* PAGE 262: object data page
|
||||
* obj.id:0123 span.ix:0003 flags:DATA
|
||||
* PAGE 263: object index page
|
||||
* obj.id:0123 span.ix:0001 flags:INDEX
|
||||
* [264] [265] [fre] [fre]
|
||||
* [fre] [fre] [fre] [fre]
|
||||
* PAGE 264: object data page
|
||||
* obj.id:0123 span.ix:0004 flags:DATA
|
||||
* PAGE 265: object data page
|
||||
* obj.id:0123 span.ix:0005 flags:DATA
|
||||
*
|
||||
*/
|
||||
#ifndef SPIFFS_NUCLEUS_H_
|
||||
#define SPIFFS_NUCLEUS_H_
|
||||
|
||||
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
|
||||
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
|
||||
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
|
||||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
|
||||
|
||||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
|
||||
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
|
||||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
|
||||
|
||||
#define SPIFFS_EV_IX_UPD 0
|
||||
#define SPIFFS_EV_IX_NEW 1
|
||||
#define SPIFFS_EV_IX_DEL 2
|
||||
|
||||
#define SPIFFS_OBJ_ID_IX_FLAG (1<<(8*sizeof(spiffs_obj_id)-1))
|
||||
|
||||
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
|
||||
|
||||
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
|
||||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
|
||||
|
||||
#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
|
||||
|
||||
#define SPIFFS_CONFIG_MAGIC (0x20090315)
|
||||
|
||||
#if SPIFFS_SINGLETON == 0
|
||||
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
|
||||
((fs)->cfg.log_page_size)
|
||||
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
|
||||
((fs)->cfg.log_block_size)
|
||||
#define SPIFFS_CFG_PHYS_SZ(fs) \
|
||||
((fs)->cfg.phys_size)
|
||||
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
|
||||
((fs)->cfg.phys_erase_block)
|
||||
#define SPIFFS_CFG_PHYS_ADDR(fs) \
|
||||
((fs)->cfg.phys_addr)
|
||||
#endif
|
||||
|
||||
// total number of pages
|
||||
#define SPIFFS_MAX_PAGES(fs) \
|
||||
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// total number of pages per block, including object lookup pages
|
||||
#define SPIFFS_PAGES_PER_BLOCK(fs) \
|
||||
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// number of object lookup pages per block
|
||||
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
|
||||
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
|
||||
// checks if page index belongs to object lookup
|
||||
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
|
||||
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// number of object lookup entries in all object lookup pages
|
||||
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
|
||||
// converts a block to physical address
|
||||
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
|
||||
// converts a object lookup entry to page index
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
|
||||
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
|
||||
// converts a object lookup entry to physical address of corresponding page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
|
||||
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a page to physical address
|
||||
#define SPIFFS_PAGE_TO_PADDR(fs, page) \
|
||||
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// converts a physical address to page
|
||||
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// gives index in page for a physical address
|
||||
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
|
||||
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
|
||||
// returns containing block for given page
|
||||
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
|
||||
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// returns starting page for block
|
||||
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
|
||||
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
|
||||
// converts page to entry in object lookup page
|
||||
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
|
||||
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
|
||||
// returns data size in a data page
|
||||
#define SPIFFS_DATA_PAGE_SIZE(fs) \
|
||||
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
|
||||
// returns physical address for block's erase count,
|
||||
// always in the physical last entry of the last object lookup page
|
||||
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
|
||||
// returns physical address for block's magic,
|
||||
// always in the physical second last entry of the last object lookup page
|
||||
#define SPIFFS_MAGIC_PADDR(fs, bix) \
|
||||
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
|
||||
// checks if there is any room for magic in the object luts
|
||||
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
|
||||
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
|
||||
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
|
||||
|
||||
// define helpers object
|
||||
|
||||
// entries in an object header page index
|
||||
#define SPIFFS_OBJ_HDR_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix))
|
||||
// entries in an object page index
|
||||
#define SPIFFS_OBJ_IX_LEN(fs) \
|
||||
((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix))
|
||||
// object index entry for given data span index
|
||||
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
|
||||
// object index span index number for given data span index or entry
|
||||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
|
||||
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
|
||||
|
||||
|
||||
#define SPIFFS_OP_T_OBJ_LU (0<<0)
|
||||
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
|
||||
#define SPIFFS_OP_T_OBJ_IX (2<<0)
|
||||
#define SPIFFS_OP_T_OBJ_DA (3<<0)
|
||||
#define SPIFFS_OP_C_DELE (0<<2)
|
||||
#define SPIFFS_OP_C_UPDT (1<<2)
|
||||
#define SPIFFS_OP_C_MOVS (2<<2)
|
||||
#define SPIFFS_OP_C_MOVD (3<<2)
|
||||
#define SPIFFS_OP_C_FLSH (4<<2)
|
||||
#define SPIFFS_OP_C_READ (5<<2)
|
||||
#define SPIFFS_OP_C_WRTHRU (6<<2)
|
||||
|
||||
#define SPIFFS_OP_TYPE_MASK (3<<0)
|
||||
#define SPIFFS_OP_COM_MASK (7<<2)
|
||||
|
||||
|
||||
// if 0, this page is written to, else clean
|
||||
#define SPIFFS_PH_FLAG_USED (1<<0)
|
||||
// if 0, writing is finalized, else under modification
|
||||
#define SPIFFS_PH_FLAG_FINAL (1<<1)
|
||||
// if 0, this is an index page, else a data page
|
||||
#define SPIFFS_PH_FLAG_INDEX (1<<2)
|
||||
// if 0, page is deleted, else valid
|
||||
#define SPIFFS_PH_FLAG_DELET (1<<7)
|
||||
// if 0, this index header is being deleted
|
||||
#define SPIFFS_PH_FLAG_IXDELE (1<<6)
|
||||
|
||||
|
||||
#define SPIFFS_CHECK_MOUNT(fs) \
|
||||
((fs)->mounted != 0)
|
||||
|
||||
#define SPIFFS_CHECK_CFG(fs) \
|
||||
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
|
||||
|
||||
#define SPIFFS_CHECK_RES(res) \
|
||||
do { \
|
||||
if ((res) < SPIFFS_OK) return (res); \
|
||||
} while (0);
|
||||
|
||||
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
||||
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_CFG(fs) \
|
||||
if (!SPIFFS_CHECK_CFG((fs))) { \
|
||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->err_code = (res); \
|
||||
SPIFFS_UNLOCK(fs); \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
|
||||
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
|
||||
|
||||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
||||
|
||||
|
||||
// check id
|
||||
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||
// report argument object id to visitor - else object lookup id is reported
|
||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||
// stop searching at end of all look up pages
|
||||
#define SPIFFS_VIS_NO_WRAP (1<<2)
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
|
||||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
|
||||
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
|
||||
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
|
||||
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
|
||||
#define SPIFFS_CACHE_FLAG_DATA (1<<4)
|
||||
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
|
||||
|
||||
#define SPIFFS_CACHE_PAGE_SIZE(fs) \
|
||||
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
|
||||
|
||||
#define spiffs_get_cache(fs) \
|
||||
((spiffs_cache *)((fs)->cache))
|
||||
|
||||
#define spiffs_get_cache_page_hdr(fs, c, ix) \
|
||||
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
|
||||
|
||||
#define spiffs_get_cache_page(fs, c, ix) \
|
||||
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
|
||||
|
||||
// cache page struct
|
||||
typedef struct {
|
||||
// cache flags
|
||||
u8_t flags;
|
||||
// cache page index
|
||||
u8_t ix;
|
||||
// last access of this cache page
|
||||
u32_t last_access;
|
||||
union {
|
||||
// type read cache
|
||||
struct {
|
||||
// read cache page index
|
||||
spiffs_page_ix pix;
|
||||
};
|
||||
#if SPIFFS_CACHE_WR
|
||||
// type write cache
|
||||
struct {
|
||||
// write cache
|
||||
spiffs_obj_id obj_id;
|
||||
// offset in cache page
|
||||
u32_t offset;
|
||||
// size of cache page
|
||||
u16_t size;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
} spiffs_cache_page;
|
||||
|
||||
// cache struct
|
||||
typedef struct {
|
||||
u8_t cpage_count;
|
||||
u32_t last_access;
|
||||
u32_t cpage_use_map;
|
||||
u32_t cpage_use_mask;
|
||||
u8_t *cpages;
|
||||
} spiffs_cache;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// spiffs nucleus file descriptor
|
||||
typedef struct {
|
||||
// the filesystem of this descriptor
|
||||
spiffs *fs;
|
||||
// number of file descriptor - if 0, the file descriptor is closed
|
||||
spiffs_file file_nbr;
|
||||
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
|
||||
spiffs_obj_id obj_id;
|
||||
// size of the file
|
||||
u32_t size;
|
||||
// cached object index header page index
|
||||
spiffs_page_ix objix_hdr_pix;
|
||||
// cached offset object index page index
|
||||
spiffs_page_ix cursor_objix_pix;
|
||||
// cached offset object index span index
|
||||
spiffs_span_ix cursor_objix_spix;
|
||||
// current absolute offset
|
||||
u32_t offset;
|
||||
// current file descriptor offset
|
||||
u32_t fdoffset;
|
||||
// fd flags
|
||||
spiffs_flags flags;
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *cache_page;
|
||||
#endif
|
||||
} spiffs_fd;
|
||||
|
||||
|
||||
// object structs
|
||||
|
||||
// page header, part of each page except object lookup pages
|
||||
// NB: this is always aligned when the data page is an object index,
|
||||
// as in this case struct spiffs_page_object_ix is used
|
||||
typedef struct __attribute(( packed )) {
|
||||
// object id
|
||||
spiffs_obj_id obj_id;
|
||||
// object span index
|
||||
spiffs_span_ix span_ix;
|
||||
// flags
|
||||
u8_t flags;
|
||||
} spiffs_page_header;
|
||||
|
||||
// object index header page header
|
||||
typedef struct __attribute(( packed ))
|
||||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
||||
#endif
|
||||
{
|
||||
// common page header
|
||||
spiffs_page_header p_hdr;
|
||||
// alignment
|
||||
u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)];
|
||||
// size of object
|
||||
u32_t size;
|
||||
// type of object
|
||||
spiffs_obj_type type;
|
||||
// name of object
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||
} spiffs_page_object_ix_header;
|
||||
|
||||
// object index page header
|
||||
typedef struct __attribute(( packed )) {
|
||||
spiffs_page_header p_hdr;
|
||||
u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)];
|
||||
} spiffs_page_object_ix;
|
||||
|
||||
// callback func for object lookup visitor
|
||||
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
|
||||
u32_t user_data, void *user_p);
|
||||
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
|
||||
#else
|
||||
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
|
||||
spiffs_phys_rd((fs), (addr), (len), (dst))
|
||||
#define _spiffs_wr(fs, op, fh, addr, len, src) \
|
||||
spiffs_phys_wr((fs), (addr), (len), (src))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_phys_rd(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_phys_wr(
|
||||
spiffs *fs,
|
||||
#if SPIFFS_CACHE
|
||||
u8_t op,
|
||||
spiffs_file fh,
|
||||
#endif
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src);
|
||||
|
||||
s32_t spiffs_phys_cpy(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u32_t dst,
|
||||
u32_t src,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_phys_count_free_blocks(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
u8_t flags,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_visitor_f v,
|
||||
u32_t user_data,
|
||||
void *user_p,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_erase_block(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_obj_lu_scan(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free_obj_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id *obj_id,
|
||||
u8_t *conflicting_name);
|
||||
|
||||
s32_t spiffs_obj_lu_find_free(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix starting_block,
|
||||
int starting_lu_entry,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_block_ix *block_ix,
|
||||
int *lu_entry);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix exclusion_pix,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_page_allocate_data(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *ph,
|
||||
u8_t *data,
|
||||
u32_t len,
|
||||
u32_t page_offs,
|
||||
u8_t finalize,
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
s32_t spiffs_page_move(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u8_t *page_data,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_header *page_hdr,
|
||||
spiffs_page_ix src_pix,
|
||||
spiffs_page_ix *dst_pix);
|
||||
|
||||
s32_t spiffs_page_delete(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_object_create(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||
spiffs_obj_type type,
|
||||
spiffs_page_ix *objix_hdr_pix);
|
||||
|
||||
s32_t spiffs_object_update_index_hdr(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_page_ix objix_hdr_pix,
|
||||
u8_t *new_objix_hdr_data,
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||
u32_t size,
|
||||
spiffs_page_ix *new_pix);
|
||||
|
||||
void spiffs_cb_object_event(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd,
|
||||
int ev,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix spix,
|
||||
spiffs_page_ix new_pix,
|
||||
u32_t new_size);
|
||||
|
||||
s32_t spiffs_object_open_by_id(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_open_by_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix,
|
||||
spiffs_fd *f,
|
||||
spiffs_flags flags,
|
||||
spiffs_mode mode);
|
||||
|
||||
s32_t spiffs_object_append(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_modify(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u8_t *data,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_object_read(
|
||||
spiffs_fd *fd,
|
||||
u32_t offset,
|
||||
u32_t len,
|
||||
u8_t *dst);
|
||||
|
||||
s32_t spiffs_object_truncate(
|
||||
spiffs_fd *fd,
|
||||
u32_t new_len,
|
||||
u8_t remove_object);
|
||||
|
||||
s32_t spiffs_object_find_object_index_header_by_name(
|
||||
spiffs *fs,
|
||||
u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||
spiffs_page_ix *pix);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len);
|
||||
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidate,
|
||||
int *candidate_count,
|
||||
char fs_crammed);
|
||||
|
||||
s32_t spiffs_gc_clean(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs);
|
||||
|
||||
// ---------------
|
||||
|
||||
s32_t spiffs_fd_find_new(
|
||||
spiffs *fs,
|
||||
spiffs_fd **fd);
|
||||
|
||||
s32_t spiffs_fd_return(
|
||||
spiffs *fs,
|
||||
spiffs_file f);
|
||||
|
||||
s32_t spiffs_fd_get(
|
||||
spiffs *fs,
|
||||
spiffs_file f,
|
||||
spiffs_fd **fd);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
void spiffs_cache_init(
|
||||
spiffs *fs);
|
||||
|
||||
void spiffs_cache_drop_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
|
||||
void spiffs_cache_fd_release(
|
||||
spiffs *fs,
|
||||
spiffs_cache_page *cp);
|
||||
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
s32_t spiffs_lookup_consistency_check(
|
||||
spiffs *fs,
|
||||
u8_t check_all_objects);
|
||||
|
||||
s32_t spiffs_page_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
s32_t spiffs_object_index_consistency_check(
|
||||
spiffs *fs);
|
||||
|
||||
#endif /* SPIFFS_NUCLEUS_H_ */
|
692
tclap/Arg.h
Normal file
692
tclap/Arg.h
Normal file
@ -0,0 +1,692 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: Arg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_ARGUMENT_H
|
||||
#define TCLAP_ARGUMENT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#else
|
||||
#define HAVE_SSTREAM
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(HAVE_SSTREAM)
|
||||
#include <sstream>
|
||||
typedef std::istringstream istringstream;
|
||||
#elif defined(HAVE_STRSTREAM)
|
||||
#include <strstream>
|
||||
typedef std::istrstream istringstream;
|
||||
#else
|
||||
#error "Need a stringstream (sstream or strstream) to compile!"
|
||||
#endif
|
||||
|
||||
#include <tclap/ArgException.h>
|
||||
#include <tclap/Visitor.h>
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/ArgTraits.h>
|
||||
#include <tclap/StandardTraits.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A virtual base class that defines the essential data for all arguments.
|
||||
* This class, or one of its existing children, must be subclassed to do
|
||||
* anything.
|
||||
*/
|
||||
class Arg
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
Arg(const Arg& rhs);
|
||||
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
Arg& operator=(const Arg& rhs);
|
||||
|
||||
/**
|
||||
* Indicates whether the rest of the arguments should be ignored.
|
||||
*/
|
||||
static bool& ignoreRestRef() { static bool ign = false; return ign; }
|
||||
|
||||
/**
|
||||
* The delimiter that separates an argument flag/name from the
|
||||
* value.
|
||||
*/
|
||||
static char& delimiterRef() { static char delim = ' '; return delim; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The single char flag used to identify the argument.
|
||||
* This value (preceded by a dash {-}), can be used to identify
|
||||
* an argument on the command line. The _flag can be blank,
|
||||
* in fact this is how unlabeled args work. Unlabeled args must
|
||||
* override appropriate functions to get correct handling. Note
|
||||
* that the _flag does NOT include the dash as part of the flag.
|
||||
*/
|
||||
std::string _flag;
|
||||
|
||||
/**
|
||||
* A single work namd indentifying the argument.
|
||||
* This value (preceded by two dashed {--}) can also be used
|
||||
* to identify an argument on the command line. Note that the
|
||||
* _name does NOT include the two dashes as part of the _name. The
|
||||
* _name cannot be blank.
|
||||
*/
|
||||
std::string _name;
|
||||
|
||||
/**
|
||||
* Description of the argument.
|
||||
*/
|
||||
std::string _description;
|
||||
|
||||
/**
|
||||
* Indicating whether the argument is required.
|
||||
*/
|
||||
bool _required;
|
||||
|
||||
/**
|
||||
* Label to be used in usage description. Normally set to
|
||||
* "required", but can be changed when necessary.
|
||||
*/
|
||||
std::string _requireLabel;
|
||||
|
||||
/**
|
||||
* Indicates whether a value is required for the argument.
|
||||
* Note that the value may be required but the argument/value
|
||||
* combination may not be, as specified by _required.
|
||||
*/
|
||||
bool _valueRequired;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument has been set.
|
||||
* Indicates that a value on the command line has matched the
|
||||
* name/flag of this argument and the values have been set accordingly.
|
||||
*/
|
||||
bool _alreadySet;
|
||||
|
||||
/**
|
||||
* A pointer to a vistitor object.
|
||||
* The visitor allows special handling to occur as soon as the
|
||||
* argument is matched. This defaults to NULL and should not
|
||||
* be used unless absolutely necessary.
|
||||
*/
|
||||
Visitor* _visitor;
|
||||
|
||||
/**
|
||||
* Whether this argument can be ignored, if desired.
|
||||
*/
|
||||
bool _ignoreable;
|
||||
|
||||
/**
|
||||
* Indicates that the arg was set as part of an XOR and not on the
|
||||
* command line.
|
||||
*/
|
||||
bool _xorSet;
|
||||
|
||||
bool _acceptsMultipleValues;
|
||||
|
||||
/**
|
||||
* Performs the special handling described by the Vistitor.
|
||||
*/
|
||||
void _checkWithVisitor() const;
|
||||
|
||||
/**
|
||||
* Primary constructor. YOU (yes you) should NEVER construct an Arg
|
||||
* directly, this is a base class that is extended by various children
|
||||
* that are meant to be used. Use SwitchArg, ValueArg, MultiArg,
|
||||
* UnlabeledValueArg, or UnlabeledMultiArg instead.
|
||||
*
|
||||
* \param flag - The flag identifying the argument.
|
||||
* \param name - The name identifying the argument.
|
||||
* \param desc - The description of the argument, used in the usage.
|
||||
* \param req - Whether the argument is required.
|
||||
* \param valreq - Whether the a value is required for the argument.
|
||||
* \param v - The visitor checked by the argument. Defaults to NULL.
|
||||
*/
|
||||
Arg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
bool valreq,
|
||||
Visitor* v = NULL );
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Arg();
|
||||
|
||||
/**
|
||||
* Adds this to the specified list of Args.
|
||||
* \param argList - The list to add this to.
|
||||
*/
|
||||
virtual void addToList( std::list<Arg*>& argList ) const;
|
||||
|
||||
/**
|
||||
* Begin ignoring arguments since the "--" argument was specified.
|
||||
*/
|
||||
static void beginIgnoring() { ignoreRestRef() = true; }
|
||||
|
||||
/**
|
||||
* Whether to ignore the rest.
|
||||
*/
|
||||
static bool ignoreRest() { return ignoreRestRef(); }
|
||||
|
||||
/**
|
||||
* The delimiter that separates an argument flag/name from the
|
||||
* value.
|
||||
*/
|
||||
static char delimiter() { return delimiterRef(); }
|
||||
|
||||
/**
|
||||
* The char used as a place holder when SwitchArgs are combined.
|
||||
* Currently set to the bell char (ASCII 7).
|
||||
*/
|
||||
static char blankChar() { return (char)7; }
|
||||
|
||||
/**
|
||||
* The char that indicates the beginning of a flag. Defaults to '-', but
|
||||
* clients can define TCLAP_FLAGSTARTCHAR to override.
|
||||
*/
|
||||
#ifndef TCLAP_FLAGSTARTCHAR
|
||||
#define TCLAP_FLAGSTARTCHAR '-'
|
||||
#endif
|
||||
static char flagStartChar() { return TCLAP_FLAGSTARTCHAR; }
|
||||
|
||||
/**
|
||||
* The sting that indicates the beginning of a flag. Defaults to "-", but
|
||||
* clients can define TCLAP_FLAGSTARTSTRING to override. Should be the same
|
||||
* as TCLAP_FLAGSTARTCHAR.
|
||||
*/
|
||||
#ifndef TCLAP_FLAGSTARTSTRING
|
||||
#define TCLAP_FLAGSTARTSTRING "-"
|
||||
#endif
|
||||
static const std::string flagStartString() { return TCLAP_FLAGSTARTSTRING; }
|
||||
|
||||
/**
|
||||
* The sting that indicates the beginning of a name. Defaults to "--", but
|
||||
* clients can define TCLAP_NAMESTARTSTRING to override.
|
||||
*/
|
||||
#ifndef TCLAP_NAMESTARTSTRING
|
||||
#define TCLAP_NAMESTARTSTRING "--"
|
||||
#endif
|
||||
static const std::string nameStartString() { return TCLAP_NAMESTARTSTRING; }
|
||||
|
||||
/**
|
||||
* The name used to identify the ignore rest argument.
|
||||
*/
|
||||
static const std::string ignoreNameString() { return "ignore_rest"; }
|
||||
|
||||
/**
|
||||
* Sets the delimiter for all arguments.
|
||||
* \param c - The character that delimits flags/names from values.
|
||||
*/
|
||||
static void setDelimiter( char c ) { delimiterRef() = c; }
|
||||
|
||||
/**
|
||||
* Pure virtual method meant to handle the parsing and value assignment
|
||||
* of the string on the command line.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. What is
|
||||
* passed in from main.
|
||||
*/
|
||||
virtual bool processArg(int *i, std::vector<std::string>& args) = 0;
|
||||
|
||||
/**
|
||||
* Operator ==.
|
||||
* Equality operator. Must be virtual to handle unlabeled args.
|
||||
* \param a - The Arg to be compared to this.
|
||||
*/
|
||||
virtual bool operator==(const Arg& a) const;
|
||||
|
||||
/**
|
||||
* Returns the argument flag.
|
||||
*/
|
||||
const std::string& getFlag() const;
|
||||
|
||||
/**
|
||||
* Returns the argument name.
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* Returns the argument description.
|
||||
*/
|
||||
std::string getDescription() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument is required.
|
||||
*/
|
||||
virtual bool isRequired() const;
|
||||
|
||||
/**
|
||||
* Sets _required to true. This is used by the XorHandler.
|
||||
* You really have no reason to ever use it.
|
||||
*/
|
||||
void forceRequired();
|
||||
|
||||
/**
|
||||
* Sets the _alreadySet value to true. This is used by the XorHandler.
|
||||
* You really have no reason to ever use it.
|
||||
*/
|
||||
void xorSet();
|
||||
|
||||
/**
|
||||
* Indicates whether a value must be specified for argument.
|
||||
*/
|
||||
bool isValueRequired() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument has already been set. Only true
|
||||
* if the arg has been matched on the command line.
|
||||
*/
|
||||
bool isSet() const;
|
||||
|
||||
/**
|
||||
* Indicates whether the argument can be ignored, if desired.
|
||||
*/
|
||||
bool isIgnoreable() const;
|
||||
|
||||
/**
|
||||
* A method that tests whether a string matches this argument.
|
||||
* This is generally called by the processArg() method. This
|
||||
* method could be re-implemented by a child to change how
|
||||
* arguments are specified on the command line.
|
||||
* \param s - The string to be compared to the flag/name to determine
|
||||
* whether the arg matches.
|
||||
*/
|
||||
virtual bool argMatches( const std::string& s ) const;
|
||||
|
||||
/**
|
||||
* Returns a simple string representation of the argument.
|
||||
* Primarily for debugging.
|
||||
*/
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Returns a short ID for the usage.
|
||||
* \param valueId - The value used in the id.
|
||||
*/
|
||||
virtual std::string shortID( const std::string& valueId = "val" ) const;
|
||||
|
||||
/**
|
||||
* Returns a long ID for the usage.
|
||||
* \param valueId - The value used in the id.
|
||||
*/
|
||||
virtual std::string longID( const std::string& valueId = "val" ) const;
|
||||
|
||||
/**
|
||||
* Trims a value off of the flag.
|
||||
* \param flag - The string from which the flag and value will be
|
||||
* trimmed. Contains the flag once the value has been trimmed.
|
||||
* \param value - Where the value trimmed from the string will
|
||||
* be stored.
|
||||
*/
|
||||
virtual void trimFlag( std::string& flag, std::string& value ) const;
|
||||
|
||||
/**
|
||||
* Checks whether a given string has blank chars, indicating that
|
||||
* it is a combined SwitchArg. If so, return true, otherwise return
|
||||
* false.
|
||||
* \param s - string to be checked.
|
||||
*/
|
||||
bool _hasBlanks( const std::string& s ) const;
|
||||
|
||||
/**
|
||||
* Sets the requireLabel. Used by XorHandler. You shouldn't ever
|
||||
* use this.
|
||||
* \param s - Set the requireLabel to this value.
|
||||
*/
|
||||
void setRequireLabel( const std::string& s );
|
||||
|
||||
/**
|
||||
* Used for MultiArgs and XorHandler to determine whether args
|
||||
* can still be set.
|
||||
*/
|
||||
virtual bool allowMore();
|
||||
|
||||
/**
|
||||
* Use by output classes to determine whether an Arg accepts
|
||||
* multiple values.
|
||||
*/
|
||||
virtual bool acceptsMultipleValues();
|
||||
|
||||
/**
|
||||
* Clears the Arg object and allows it to be reused by new
|
||||
* command lines.
|
||||
*/
|
||||
virtual void reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Typedef of an Arg list iterator.
|
||||
*/
|
||||
typedef std::list<Arg*>::iterator ArgListIterator;
|
||||
|
||||
/**
|
||||
* Typedef of an Arg vector iterator.
|
||||
*/
|
||||
typedef std::vector<Arg*>::iterator ArgVectorIterator;
|
||||
|
||||
/**
|
||||
* Typedef of a Visitor list iterator.
|
||||
*/
|
||||
typedef std::list<Visitor*>::iterator VisitorListIterator;
|
||||
|
||||
/*
|
||||
* Extract a value of type T from it's string representation contained
|
||||
* in strVal. The ValueLike parameter used to select the correct
|
||||
* specialization of ExtractValue depending on the value traits of T.
|
||||
* ValueLike traits use operator>> to assign the value from strVal.
|
||||
*/
|
||||
template<typename T> void
|
||||
ExtractValue(T &destVal, const std::string& strVal, ValueLike vl)
|
||||
{
|
||||
static_cast<void>(vl); // Avoid warning about unused vl
|
||||
std::istringstream is(strVal);
|
||||
|
||||
int valuesRead = 0;
|
||||
while ( is.good() ) {
|
||||
if ( is.peek() != EOF )
|
||||
#ifdef TCLAP_SETBASE_ZERO
|
||||
is >> std::setbase(0) >> destVal;
|
||||
#else
|
||||
is >> destVal;
|
||||
#endif
|
||||
else
|
||||
break;
|
||||
|
||||
valuesRead++;
|
||||
}
|
||||
|
||||
if ( is.fail() )
|
||||
throw( ArgParseException("Couldn't read argument value "
|
||||
"from string '" + strVal + "'"));
|
||||
|
||||
|
||||
if ( valuesRead > 1 )
|
||||
throw( ArgParseException("More than one valid value parsed from "
|
||||
"string '" + strVal + "'"));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a value of type T from it's string representation contained
|
||||
* in strVal. The ValueLike parameter used to select the correct
|
||||
* specialization of ExtractValue depending on the value traits of T.
|
||||
* StringLike uses assignment (operator=) to assign from strVal.
|
||||
*/
|
||||
template<typename T> void
|
||||
ExtractValue(T &destVal, const std::string& strVal, StringLike sl)
|
||||
{
|
||||
static_cast<void>(sl); // Avoid warning about unused sl
|
||||
SetString(destVal, strVal);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN Arg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline Arg::Arg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
bool valreq,
|
||||
Visitor* v) :
|
||||
_flag(flag),
|
||||
_name(name),
|
||||
_description(desc),
|
||||
_required(req),
|
||||
_requireLabel("required"),
|
||||
_valueRequired(valreq),
|
||||
_alreadySet(false),
|
||||
_visitor( v ),
|
||||
_ignoreable(true),
|
||||
_xorSet(false),
|
||||
_acceptsMultipleValues(false)
|
||||
{
|
||||
if ( _flag.length() > 1 )
|
||||
throw(SpecificationException(
|
||||
"Argument flag can only be one character long", toString() ) );
|
||||
|
||||
if ( _name != ignoreNameString() &&
|
||||
( _flag == Arg::flagStartString() ||
|
||||
_flag == Arg::nameStartString() ||
|
||||
_flag == " " ) )
|
||||
throw(SpecificationException("Argument flag cannot be either '" +
|
||||
Arg::flagStartString() + "' or '" +
|
||||
Arg::nameStartString() + "' or a space.",
|
||||
toString() ) );
|
||||
|
||||
if ( ( _name.substr( 0, Arg::flagStartString().length() ) == Arg::flagStartString() ) ||
|
||||
( _name.substr( 0, Arg::nameStartString().length() ) == Arg::nameStartString() ) ||
|
||||
( _name.find( " ", 0 ) != std::string::npos ) )
|
||||
throw(SpecificationException("Argument name begin with either '" +
|
||||
Arg::flagStartString() + "' or '" +
|
||||
Arg::nameStartString() + "' or space.",
|
||||
toString() ) );
|
||||
|
||||
}
|
||||
|
||||
inline Arg::~Arg() { }
|
||||
|
||||
inline std::string Arg::shortID( const std::string& valueId ) const
|
||||
{
|
||||
std::string id = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
id = Arg::flagStartString() + _flag;
|
||||
else
|
||||
id = Arg::nameStartString() + _name;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
if ( !_required )
|
||||
id = "[" + id + "]";
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline std::string Arg::longID( const std::string& valueId ) const
|
||||
{
|
||||
std::string id = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
{
|
||||
id += Arg::flagStartString() + _flag;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
id += ", ";
|
||||
}
|
||||
|
||||
id += Arg::nameStartString() + _name;
|
||||
|
||||
if ( _valueRequired )
|
||||
id += std::string( 1, Arg::delimiter() ) + "<" + valueId + ">";
|
||||
|
||||
return id;
|
||||
|
||||
}
|
||||
|
||||
inline bool Arg::operator==(const Arg& a) const
|
||||
{
|
||||
if ( ( _flag != "" && _flag == a._flag ) || _name == a._name)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string Arg::getDescription() const
|
||||
{
|
||||
std::string desc = "";
|
||||
if ( _required )
|
||||
desc = "(" + _requireLabel + ") ";
|
||||
|
||||
// if ( _valueRequired )
|
||||
// desc += "(value required) ";
|
||||
|
||||
desc += _description;
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline const std::string& Arg::getFlag() const { return _flag; }
|
||||
|
||||
inline const std::string& Arg::getName() const { return _name; }
|
||||
|
||||
inline bool Arg::isRequired() const { return _required; }
|
||||
|
||||
inline bool Arg::isValueRequired() const { return _valueRequired; }
|
||||
|
||||
inline bool Arg::isSet() const
|
||||
{
|
||||
if ( _alreadySet && !_xorSet )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Arg::isIgnoreable() const { return _ignoreable; }
|
||||
|
||||
inline void Arg::setRequireLabel( const std::string& s)
|
||||
{
|
||||
_requireLabel = s;
|
||||
}
|
||||
|
||||
inline bool Arg::argMatches( const std::string& argFlag ) const
|
||||
{
|
||||
if ( ( argFlag == Arg::flagStartString() + _flag && _flag != "" ) ||
|
||||
argFlag == Arg::nameStartString() + _name )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string Arg::toString() const
|
||||
{
|
||||
std::string s = "";
|
||||
|
||||
if ( _flag != "" )
|
||||
s += Arg::flagStartString() + _flag + " ";
|
||||
|
||||
s += "(" + Arg::nameStartString() + _name + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
inline void Arg::_checkWithVisitor() const
|
||||
{
|
||||
if ( _visitor != NULL )
|
||||
_visitor->visit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of trimFlag.
|
||||
*/
|
||||
inline void Arg::trimFlag(std::string& flag, std::string& value) const
|
||||
{
|
||||
int stop = 0;
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < flag.length(); i++ )
|
||||
if ( flag[i] == Arg::delimiter() )
|
||||
{
|
||||
stop = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( stop > 1 )
|
||||
{
|
||||
value = flag.substr(stop+1);
|
||||
flag = flag.substr(0,stop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of _hasBlanks.
|
||||
*/
|
||||
inline bool Arg::_hasBlanks( const std::string& s ) const
|
||||
{
|
||||
for ( int i = 1; static_cast<unsigned int>(i) < s.length(); i++ )
|
||||
if ( s[i] == Arg::blankChar() )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void Arg::forceRequired()
|
||||
{
|
||||
_required = true;
|
||||
}
|
||||
|
||||
inline void Arg::xorSet()
|
||||
{
|
||||
_alreadySet = true;
|
||||
_xorSet = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden by Args that need to added to the end of the list.
|
||||
*/
|
||||
inline void Arg::addToList( std::list<Arg*>& argList ) const
|
||||
{
|
||||
argList.push_front( const_cast<Arg*>(this) );
|
||||
}
|
||||
|
||||
inline bool Arg::allowMore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Arg::acceptsMultipleValues()
|
||||
{
|
||||
return _acceptsMultipleValues;
|
||||
}
|
||||
|
||||
inline void Arg::reset()
|
||||
{
|
||||
_xorSet = false;
|
||||
_alreadySet = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//END Arg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
200
tclap/ArgException.h
Normal file
200
tclap/ArgException.h
Normal file
@ -0,0 +1,200 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ArgException.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_ARG_EXCEPTION_H
|
||||
#define TCLAP_ARG_EXCEPTION_H
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A simple class that defines and argument exception. Should be caught
|
||||
* whenever a CmdLine is created and parsed.
|
||||
*/
|
||||
class ArgException : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source.
|
||||
* \param td - Text describing the type of ArgException it is.
|
||||
* of the exception.
|
||||
*/
|
||||
ArgException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined",
|
||||
const std::string& td = "Generic ArgException")
|
||||
: std::exception(),
|
||||
_errorText(text),
|
||||
_argId( id ),
|
||||
_typeDescription(td)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~ArgException() throw() { }
|
||||
|
||||
/**
|
||||
* Returns the error text.
|
||||
*/
|
||||
std::string error() const { return ( _errorText ); }
|
||||
|
||||
/**
|
||||
* Returns the argument id.
|
||||
*/
|
||||
std::string argId() const
|
||||
{
|
||||
if ( _argId == "undefined" )
|
||||
return " ";
|
||||
else
|
||||
return ( "Argument: " + _argId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arg id and error text.
|
||||
*/
|
||||
const char* what() const throw()
|
||||
{
|
||||
static std::string ex;
|
||||
ex = _argId + " -- " + _errorText;
|
||||
return ex.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the exception. Used to explain and distinguish
|
||||
* between different child exceptions.
|
||||
*/
|
||||
std::string typeDescription() const
|
||||
{
|
||||
return _typeDescription;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* The text of the exception message.
|
||||
*/
|
||||
std::string _errorText;
|
||||
|
||||
/**
|
||||
* The argument related to this exception.
|
||||
*/
|
||||
std::string _argId;
|
||||
|
||||
/**
|
||||
* Describes the type of the exception. Used to distinguish
|
||||
* between different child exceptions.
|
||||
*/
|
||||
std::string _typeDescription;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from within the child Arg classes when it fails to properly
|
||||
* parse the argument it has been passed.
|
||||
*/
|
||||
class ArgParseException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
ArgParseException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string( "Exception found while parsing " ) +
|
||||
std::string( "the value the Arg has been passed." ))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from CmdLine when the arguments on the command line are not
|
||||
* properly specified, e.g. too many arguments, required argument missing, etc.
|
||||
*/
|
||||
class CmdLineParseException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
CmdLineParseException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string( "Exception found when the values ") +
|
||||
std::string( "on the command line do not meet ") +
|
||||
std::string( "the requirements of the defined ") +
|
||||
std::string( "Args." ))
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Thrown from Arg and CmdLine when an Arg is improperly specified, e.g.
|
||||
* same flag as another Arg, same name, etc.
|
||||
*/
|
||||
class SpecificationException : public ArgException
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param text - The text of the exception.
|
||||
* \param id - The text identifying the argument source
|
||||
* of the exception.
|
||||
*/
|
||||
SpecificationException( const std::string& text = "undefined exception",
|
||||
const std::string& id = "undefined" )
|
||||
: ArgException( text,
|
||||
id,
|
||||
std::string("Exception found when an Arg object ")+
|
||||
std::string("is improperly defined by the ") +
|
||||
std::string("developer." ))
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
class ExitException {
|
||||
public:
|
||||
ExitException(int estat) : _estat(estat) {}
|
||||
|
||||
int getExitStatus() const { return _estat; }
|
||||
|
||||
private:
|
||||
int _estat;
|
||||
};
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
||||
|
87
tclap/ArgTraits.h
Normal file
87
tclap/ArgTraits.h
Normal file
@ -0,0 +1,87 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ArgTraits.h
|
||||
*
|
||||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// This is an internal tclap file, you should probably not have to
|
||||
// include this directly
|
||||
|
||||
#ifndef TCLAP_ARGTRAITS_H
|
||||
#define TCLAP_ARGTRAITS_H
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
// We use two empty structs to get compile type specialization
|
||||
// function to work
|
||||
|
||||
/**
|
||||
* A value like argument value type is a value that can be set using
|
||||
* operator>>. This is the default value type.
|
||||
*/
|
||||
struct ValueLike {
|
||||
typedef ValueLike ValueCategory;
|
||||
virtual ~ValueLike() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A string like argument value type is a value that can be set using
|
||||
* operator=(string). Usefull if the value type contains spaces which
|
||||
* will be broken up into individual tokens by operator>>.
|
||||
*/
|
||||
struct StringLike {
|
||||
virtual ~StringLike() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class can inherit from this object to make it have string like
|
||||
* traits. This is a compile time thing and does not add any overhead
|
||||
* to the inherenting class.
|
||||
*/
|
||||
struct StringLikeTrait {
|
||||
typedef StringLike ValueCategory;
|
||||
virtual ~StringLikeTrait() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class can inherit from this object to make it have value like
|
||||
* traits. This is a compile time thing and does not add any overhead
|
||||
* to the inherenting class.
|
||||
*/
|
||||
struct ValueLikeTrait {
|
||||
typedef ValueLike ValueCategory;
|
||||
virtual ~ValueLikeTrait() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Arg traits are used to get compile type specialization when parsing
|
||||
* argument values. Using an ArgTraits you can specify the way that
|
||||
* values gets assigned to any particular type during parsing. The two
|
||||
* supported types are StringLike and ValueLike.
|
||||
*/
|
||||
template<typename T>
|
||||
struct ArgTraits {
|
||||
typedef typename T::ValueCategory ValueCategory;
|
||||
virtual ~ArgTraits() {}
|
||||
//typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
25
tclap/COPYING
Normal file
25
tclap/COPYING
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
Copyright (c) 2003 Michael E. Smoot
|
||||
|
||||
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.
|
||||
|
||||
|
633
tclap/CmdLine.h
Normal file
633
tclap/CmdLine.h
Normal file
@ -0,0 +1,633 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLine.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CMDLINE_H
|
||||
#define TCLAP_CMDLINE_H
|
||||
|
||||
#include <tclap/SwitchArg.h>
|
||||
#include <tclap/MultiSwitchArg.h>
|
||||
#include <tclap/UnlabeledValueArg.h>
|
||||
#include <tclap/UnlabeledMultiArg.h>
|
||||
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/HelpVisitor.h>
|
||||
#include <tclap/VersionVisitor.h>
|
||||
#include <tclap/IgnoreRestVisitor.h>
|
||||
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/StdOutput.h>
|
||||
|
||||
#include <tclap/Constraint.h>
|
||||
#include <tclap/ValuesConstraint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h> // Needed for exit(), which isn't defined in some envs.
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
template<typename T> void DelPtr(T ptr)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
template<typename C> void ClearContainer(C &c)
|
||||
{
|
||||
typedef typename C::value_type value_type;
|
||||
std::for_each(c.begin(), c.end(), DelPtr<value_type>);
|
||||
c.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The base class that manages the command line definition and passes
|
||||
* along the parsing to the appropriate Arg classes.
|
||||
*/
|
||||
class CmdLine : public CmdLineInterface
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of arguments that will be tested against the
|
||||
* command line.
|
||||
*/
|
||||
std::list<Arg*> _argList;
|
||||
|
||||
/**
|
||||
* The name of the program. Set to argv[0].
|
||||
*/
|
||||
std::string _progName;
|
||||
|
||||
/**
|
||||
* A message used to describe the program. Used in the usage output.
|
||||
*/
|
||||
std::string _message;
|
||||
|
||||
/**
|
||||
* The version to be displayed with the --version switch.
|
||||
*/
|
||||
std::string _version;
|
||||
|
||||
/**
|
||||
* The number of arguments that are required to be present on
|
||||
* the command line. This is set dynamically, based on the
|
||||
* Args added to the CmdLine object.
|
||||
*/
|
||||
int _numRequired;
|
||||
|
||||
/**
|
||||
* The character that is used to separate the argument flag/name
|
||||
* from the value. Defaults to ' ' (space).
|
||||
*/
|
||||
char _delimiter;
|
||||
|
||||
/**
|
||||
* The handler that manages xoring lists of args.
|
||||
*/
|
||||
XorHandler _xorHandler;
|
||||
|
||||
/**
|
||||
* A list of Args to be explicitly deleted when the destructor
|
||||
* is called. At the moment, this only includes the three default
|
||||
* Args.
|
||||
*/
|
||||
std::list<Arg*> _argDeleteOnExitList;
|
||||
|
||||
/**
|
||||
* A list of Visitors to be explicitly deleted when the destructor
|
||||
* is called. At the moment, these are the Vistors created for the
|
||||
* default Args.
|
||||
*/
|
||||
std::list<Visitor*> _visitorDeleteOnExitList;
|
||||
|
||||
/**
|
||||
* Object that handles all output for the CmdLine.
|
||||
*/
|
||||
CmdLineOutput* _output;
|
||||
|
||||
/**
|
||||
* Should CmdLine handle parsing exceptions internally?
|
||||
*/
|
||||
bool _handleExceptions;
|
||||
|
||||
/**
|
||||
* Throws an exception listing the missing args.
|
||||
*/
|
||||
void missingArgsException();
|
||||
|
||||
/**
|
||||
* Checks whether a name/flag string matches entirely matches
|
||||
* the Arg::blankChar. Used when multiple switches are combined
|
||||
* into a single argument.
|
||||
* \param s - The message to be used in the usage.
|
||||
*/
|
||||
bool _emptyCombined(const std::string& s);
|
||||
|
||||
/**
|
||||
* Perform a delete ptr; operation on ptr when this object is deleted.
|
||||
*/
|
||||
void deleteOnExit(Arg* ptr);
|
||||
|
||||
/**
|
||||
* Perform a delete ptr; operation on ptr when this object is deleted.
|
||||
*/
|
||||
void deleteOnExit(Visitor* ptr);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
CmdLine(const CmdLine& rhs);
|
||||
CmdLine& operator=(const CmdLine& rhs);
|
||||
|
||||
/**
|
||||
* Encapsulates the code common to the constructors
|
||||
* (which is all of it).
|
||||
*/
|
||||
void _constructor();
|
||||
|
||||
|
||||
/**
|
||||
* Is set to true when a user sets the output object. We use this so
|
||||
* that we don't delete objects that are created outside of this lib.
|
||||
*/
|
||||
bool _userSetOutput;
|
||||
|
||||
/**
|
||||
* Whether or not to automatically create help and version switches.
|
||||
*/
|
||||
bool _helpAndVersion;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Command line constructor. Defines how the arguments will be
|
||||
* parsed.
|
||||
* \param message - The message to be used in the usage
|
||||
* output.
|
||||
* \param delimiter - The character that is used to separate
|
||||
* the argument flag/name from the value. Defaults to ' ' (space).
|
||||
* \param version - The version number to be used in the
|
||||
* --version switch.
|
||||
* \param helpAndVersion - Whether or not to create the Help and
|
||||
* Version switches. Defaults to true.
|
||||
*/
|
||||
CmdLine(const std::string& message,
|
||||
const char delimiter = ' ',
|
||||
const std::string& version = "none",
|
||||
bool helpAndVersion = true);
|
||||
|
||||
/**
|
||||
* Deletes any resources allocated by a CmdLine object.
|
||||
*/
|
||||
virtual ~CmdLine();
|
||||
|
||||
/**
|
||||
* Adds an argument to the list of arguments to be parsed.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
void add( Arg& a );
|
||||
|
||||
/**
|
||||
* An alternative add. Functionally identical.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
void add( Arg* a );
|
||||
|
||||
/**
|
||||
* Add two Args that will be xor'd. If this method is used, add does
|
||||
* not need to be called.
|
||||
* \param a - Argument to be added and xor'd.
|
||||
* \param b - Argument to be added and xor'd.
|
||||
*/
|
||||
void xorAdd( Arg& a, Arg& b );
|
||||
|
||||
/**
|
||||
* Add a list of Args that will be xor'd. If this method is used,
|
||||
* add does not need to be called.
|
||||
* \param xors - List of Args to be added and xor'd.
|
||||
*/
|
||||
void xorAdd( std::vector<Arg*>& xors );
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param argc - Number of arguments.
|
||||
* \param argv - Array of arguments.
|
||||
*/
|
||||
void parse(int argc, const char * const * argv);
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param args - A vector of strings representing the args.
|
||||
* args[0] is still the program name.
|
||||
*/
|
||||
void parse(std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
CmdLineOutput* getOutput();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setOutput(CmdLineOutput* co);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getVersion();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getProgramName();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::list<Arg*>& getArgList();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
XorHandler& getXorHandler();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
char getDelimiter();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string& getMessage();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool hasHelpAndVersion();
|
||||
|
||||
/**
|
||||
* Disables or enables CmdLine's internal parsing exception handling.
|
||||
*
|
||||
* @param state Should CmdLine handle parsing exceptions internally?
|
||||
*/
|
||||
void setExceptionHandling(const bool state);
|
||||
|
||||
/**
|
||||
* Returns the current state of the internal exception handling.
|
||||
*
|
||||
* @retval true Parsing exceptions are handled internally.
|
||||
* @retval false Parsing exceptions are propagated to the caller.
|
||||
*/
|
||||
bool getExceptionHandling() const;
|
||||
|
||||
/**
|
||||
* Allows the CmdLine object to be reused.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//Begin CmdLine.cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline CmdLine::CmdLine(const std::string& m,
|
||||
char delim,
|
||||
const std::string& v,
|
||||
bool help )
|
||||
:
|
||||
_argList(std::list<Arg*>()),
|
||||
_progName("not_set_yet"),
|
||||
_message(m),
|
||||
_version(v),
|
||||
_numRequired(0),
|
||||
_delimiter(delim),
|
||||
_xorHandler(XorHandler()),
|
||||
_argDeleteOnExitList(std::list<Arg*>()),
|
||||
_visitorDeleteOnExitList(std::list<Visitor*>()),
|
||||
_output(0),
|
||||
_handleExceptions(true),
|
||||
_userSetOutput(false),
|
||||
_helpAndVersion(help)
|
||||
{
|
||||
_constructor();
|
||||
}
|
||||
|
||||
inline CmdLine::~CmdLine()
|
||||
{
|
||||
ClearContainer(_argDeleteOnExitList);
|
||||
ClearContainer(_visitorDeleteOnExitList);
|
||||
|
||||
if ( !_userSetOutput ) {
|
||||
delete _output;
|
||||
_output = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CmdLine::_constructor()
|
||||
{
|
||||
_output = new StdOutput;
|
||||
|
||||
Arg::setDelimiter( _delimiter );
|
||||
|
||||
Visitor* v;
|
||||
|
||||
if ( _helpAndVersion )
|
||||
{
|
||||
v = new HelpVisitor( this, &_output );
|
||||
SwitchArg* help = new SwitchArg("h","help",
|
||||
"Displays usage information and exits.",
|
||||
false, v);
|
||||
add( help );
|
||||
deleteOnExit(help);
|
||||
deleteOnExit(v);
|
||||
|
||||
v = new VersionVisitor( this, &_output );
|
||||
SwitchArg* vers = new SwitchArg("","version",
|
||||
"Displays version information and exits.",
|
||||
false, v);
|
||||
add( vers );
|
||||
deleteOnExit(vers);
|
||||
deleteOnExit(v);
|
||||
}
|
||||
|
||||
v = new IgnoreRestVisitor();
|
||||
SwitchArg* ignore = new SwitchArg(Arg::flagStartString(),
|
||||
Arg::ignoreNameString(),
|
||||
"Ignores the rest of the labeled arguments following this flag.",
|
||||
false, v);
|
||||
add( ignore );
|
||||
deleteOnExit(ignore);
|
||||
deleteOnExit(v);
|
||||
}
|
||||
|
||||
inline void CmdLine::xorAdd( std::vector<Arg*>& ors )
|
||||
{
|
||||
_xorHandler.add( ors );
|
||||
|
||||
for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++)
|
||||
{
|
||||
(*it)->forceRequired();
|
||||
(*it)->setRequireLabel( "OR required" );
|
||||
add( *it );
|
||||
}
|
||||
}
|
||||
|
||||
inline void CmdLine::xorAdd( Arg& a, Arg& b )
|
||||
{
|
||||
std::vector<Arg*> ors;
|
||||
ors.push_back( &a );
|
||||
ors.push_back( &b );
|
||||
xorAdd( ors );
|
||||
}
|
||||
|
||||
inline void CmdLine::add( Arg& a )
|
||||
{
|
||||
add( &a );
|
||||
}
|
||||
|
||||
inline void CmdLine::add( Arg* a )
|
||||
{
|
||||
for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
|
||||
if ( *a == *(*it) )
|
||||
throw( SpecificationException(
|
||||
"Argument with same flag/name already exists!",
|
||||
a->longID() ) );
|
||||
|
||||
a->addToList( _argList );
|
||||
|
||||
if ( a->isRequired() )
|
||||
_numRequired++;
|
||||
}
|
||||
|
||||
|
||||
inline void CmdLine::parse(int argc, const char * const * argv)
|
||||
{
|
||||
// this step is necessary so that we have easy access to
|
||||
// mutable strings.
|
||||
std::vector<std::string> args;
|
||||
for (int i = 0; i < argc; i++)
|
||||
args.push_back(argv[i]);
|
||||
|
||||
parse(args);
|
||||
}
|
||||
|
||||
inline void CmdLine::parse(std::vector<std::string>& args)
|
||||
{
|
||||
bool shouldExit = false;
|
||||
int estat = 0;
|
||||
|
||||
try {
|
||||
_progName = args.front();
|
||||
args.erase(args.begin());
|
||||
|
||||
int requiredCount = 0;
|
||||
|
||||
for (int i = 0; static_cast<unsigned int>(i) < args.size(); i++)
|
||||
{
|
||||
bool matched = false;
|
||||
for (ArgListIterator it = _argList.begin();
|
||||
it != _argList.end(); it++) {
|
||||
if ( (*it)->processArg( &i, args ) )
|
||||
{
|
||||
requiredCount += _xorHandler.check( *it );
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if the argument is an empty combined
|
||||
// switch and if so, then we've actually matched it
|
||||
if ( !matched && _emptyCombined( args[i] ) )
|
||||
matched = true;
|
||||
|
||||
if ( !matched && !Arg::ignoreRest() )
|
||||
throw(CmdLineParseException("Couldn't find match "
|
||||
"for argument",
|
||||
args[i]));
|
||||
}
|
||||
|
||||
if ( requiredCount < _numRequired )
|
||||
missingArgsException();
|
||||
|
||||
if ( requiredCount > _numRequired )
|
||||
throw(CmdLineParseException("Too many arguments!"));
|
||||
|
||||
} catch ( ArgException& e ) {
|
||||
// If we're not handling the exceptions, rethrow.
|
||||
if ( !_handleExceptions) {
|
||||
throw;
|
||||
}
|
||||
|
||||
try {
|
||||
_output->failure(*this,e);
|
||||
} catch ( ExitException &ee ) {
|
||||
estat = ee.getExitStatus();
|
||||
shouldExit = true;
|
||||
}
|
||||
} catch (ExitException &ee) {
|
||||
// If we're not handling the exceptions, rethrow.
|
||||
if ( !_handleExceptions) {
|
||||
throw;
|
||||
}
|
||||
|
||||
estat = ee.getExitStatus();
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
if (shouldExit)
|
||||
exit(estat);
|
||||
}
|
||||
|
||||
inline bool CmdLine::_emptyCombined(const std::string& s)
|
||||
{
|
||||
if ( s.length() > 0 && s[0] != Arg::flagStartChar() )
|
||||
return false;
|
||||
|
||||
for ( int i = 1; static_cast<unsigned int>(i) < s.length(); i++ )
|
||||
if ( s[i] != Arg::blankChar() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void CmdLine::missingArgsException()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
std::string missingArgList;
|
||||
for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++)
|
||||
{
|
||||
if ( (*it)->isRequired() && !(*it)->isSet() )
|
||||
{
|
||||
missingArgList += (*it)->getName();
|
||||
missingArgList += ", ";
|
||||
count++;
|
||||
}
|
||||
}
|
||||
missingArgList = missingArgList.substr(0,missingArgList.length()-2);
|
||||
|
||||
std::string msg;
|
||||
if ( count > 1 )
|
||||
msg = "Required arguments missing: ";
|
||||
else
|
||||
msg = "Required argument missing: ";
|
||||
|
||||
msg += missingArgList;
|
||||
|
||||
throw(CmdLineParseException(msg));
|
||||
}
|
||||
|
||||
inline void CmdLine::deleteOnExit(Arg* ptr)
|
||||
{
|
||||
_argDeleteOnExitList.push_back(ptr);
|
||||
}
|
||||
|
||||
inline void CmdLine::deleteOnExit(Visitor* ptr)
|
||||
{
|
||||
_visitorDeleteOnExitList.push_back(ptr);
|
||||
}
|
||||
|
||||
inline CmdLineOutput* CmdLine::getOutput()
|
||||
{
|
||||
return _output;
|
||||
}
|
||||
|
||||
inline void CmdLine::setOutput(CmdLineOutput* co)
|
||||
{
|
||||
if ( !_userSetOutput )
|
||||
delete _output;
|
||||
_userSetOutput = true;
|
||||
_output = co;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getVersion()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getProgramName()
|
||||
{
|
||||
return _progName;
|
||||
}
|
||||
|
||||
inline std::list<Arg*>& CmdLine::getArgList()
|
||||
{
|
||||
return _argList;
|
||||
}
|
||||
|
||||
inline XorHandler& CmdLine::getXorHandler()
|
||||
{
|
||||
return _xorHandler;
|
||||
}
|
||||
|
||||
inline char CmdLine::getDelimiter()
|
||||
{
|
||||
return _delimiter;
|
||||
}
|
||||
|
||||
inline std::string& CmdLine::getMessage()
|
||||
{
|
||||
return _message;
|
||||
}
|
||||
|
||||
inline bool CmdLine::hasHelpAndVersion()
|
||||
{
|
||||
return _helpAndVersion;
|
||||
}
|
||||
|
||||
inline void CmdLine::setExceptionHandling(const bool state)
|
||||
{
|
||||
_handleExceptions = state;
|
||||
}
|
||||
|
||||
inline bool CmdLine::getExceptionHandling() const
|
||||
{
|
||||
return _handleExceptions;
|
||||
}
|
||||
|
||||
inline void CmdLine::reset()
|
||||
{
|
||||
for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
|
||||
(*it)->reset();
|
||||
|
||||
_progName.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//End CmdLine.cpp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
150
tclap/CmdLineInterface.h
Normal file
150
tclap/CmdLineInterface.h
Normal file
@ -0,0 +1,150 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLineInterface.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_COMMANDLINE_INTERFACE_H
|
||||
#define TCLAP_COMMANDLINE_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class Arg;
|
||||
class CmdLineOutput;
|
||||
class XorHandler;
|
||||
|
||||
/**
|
||||
* The base class that manages the command line definition and passes
|
||||
* along the parsing to the appropriate Arg classes.
|
||||
*/
|
||||
class CmdLineInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~CmdLineInterface() {}
|
||||
|
||||
/**
|
||||
* Adds an argument to the list of arguments to be parsed.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
virtual void add( Arg& a )=0;
|
||||
|
||||
/**
|
||||
* An alternative add. Functionally identical.
|
||||
* \param a - Argument to be added.
|
||||
*/
|
||||
virtual void add( Arg* a )=0;
|
||||
|
||||
/**
|
||||
* Add two Args that will be xor'd.
|
||||
* If this method is used, add does
|
||||
* not need to be called.
|
||||
* \param a - Argument to be added and xor'd.
|
||||
* \param b - Argument to be added and xor'd.
|
||||
*/
|
||||
virtual void xorAdd( Arg& a, Arg& b )=0;
|
||||
|
||||
/**
|
||||
* Add a list of Args that will be xor'd. If this method is used,
|
||||
* add does not need to be called.
|
||||
* \param xors - List of Args to be added and xor'd.
|
||||
*/
|
||||
virtual void xorAdd( std::vector<Arg*>& xors )=0;
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param argc - Number of arguments.
|
||||
* \param argv - Array of arguments.
|
||||
*/
|
||||
virtual void parse(int argc, const char * const * argv)=0;
|
||||
|
||||
/**
|
||||
* Parses the command line.
|
||||
* \param args - A vector of strings representing the args.
|
||||
* args[0] is still the program name.
|
||||
*/
|
||||
void parse(std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns the CmdLineOutput object.
|
||||
*/
|
||||
virtual CmdLineOutput* getOutput()=0;
|
||||
|
||||
/**
|
||||
* \param co - CmdLineOutput object that we want to use instead.
|
||||
*/
|
||||
virtual void setOutput(CmdLineOutput* co)=0;
|
||||
|
||||
/**
|
||||
* Returns the version string.
|
||||
*/
|
||||
virtual std::string& getVersion()=0;
|
||||
|
||||
/**
|
||||
* Returns the program name string.
|
||||
*/
|
||||
virtual std::string& getProgramName()=0;
|
||||
|
||||
/**
|
||||
* Returns the argList.
|
||||
*/
|
||||
virtual std::list<Arg*>& getArgList()=0;
|
||||
|
||||
/**
|
||||
* Returns the XorHandler.
|
||||
*/
|
||||
virtual XorHandler& getXorHandler()=0;
|
||||
|
||||
/**
|
||||
* Returns the delimiter string.
|
||||
*/
|
||||
virtual char getDelimiter()=0;
|
||||
|
||||
/**
|
||||
* Returns the message string.
|
||||
*/
|
||||
virtual std::string& getMessage()=0;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the help and version switches were created
|
||||
* automatically.
|
||||
*/
|
||||
virtual bool hasHelpAndVersion()=0;
|
||||
|
||||
/**
|
||||
* Resets the instance as if it had just been constructed so that the
|
||||
* instance can be reused.
|
||||
*/
|
||||
virtual void reset()=0;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
#endif
|
74
tclap/CmdLineOutput.h
Normal file
74
tclap/CmdLineOutput.h
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: CmdLineOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CMDLINEOUTPUT_H
|
||||
#define TCLAP_CMDLINEOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class CmdLineInterface;
|
||||
class ArgException;
|
||||
|
||||
/**
|
||||
* The interface that any output object must implement.
|
||||
*/
|
||||
class CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Virtual destructor.
|
||||
*/
|
||||
virtual ~CmdLineOutput() {}
|
||||
|
||||
/**
|
||||
* Generates some sort of output for the USAGE.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c)=0;
|
||||
|
||||
/**
|
||||
* Generates some sort of output for the version.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c)=0;
|
||||
|
||||
/**
|
||||
* Generates some sort of output for a failure.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure( CmdLineInterface& c,
|
||||
ArgException& e )=0;
|
||||
|
||||
};
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
68
tclap/Constraint.h
Normal file
68
tclap/Constraint.h
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: Constraint.h
|
||||
*
|
||||
* Copyright (c) 2005, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_CONSTRAINT_H
|
||||
#define TCLAP_CONSTRAINT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* The interface that defines the interaction between the Arg and Constraint.
|
||||
*/
|
||||
template<class T>
|
||||
class Constraint
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns a description of the Constraint.
|
||||
*/
|
||||
virtual std::string description() const =0;
|
||||
|
||||
/**
|
||||
* Returns the short ID for the Constraint.
|
||||
*/
|
||||
virtual std::string shortID() const =0;
|
||||
|
||||
/**
|
||||
* The method used to verify that the value parsed from the command
|
||||
* line meets the constraint.
|
||||
* \param value - The value that will be checked.
|
||||
*/
|
||||
virtual bool check(const T& value) const =0;
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Silences warnings about Constraint being a base class with virtual
|
||||
* functions but without a virtual destructor.
|
||||
*/
|
||||
virtual ~Constraint() { ; }
|
||||
};
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
299
tclap/DocBookOutput.h
Normal file
299
tclap/DocBookOutput.h
Normal file
@ -0,0 +1,299 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: DocBookOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_DOCBOOKOUTPUT_H
|
||||
#define TCLAP_DOCBOOKOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A class that generates DocBook output for usage() method for the
|
||||
* given CmdLine and its Args.
|
||||
*/
|
||||
class DocBookOutput : public CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Prints the usage to stdout. Can be overridden to
|
||||
* produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints the version to stdout. Can be overridden
|
||||
* to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints (to stderr) an error message, short usage
|
||||
* Can be overridden to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure(CmdLineInterface& c,
|
||||
ArgException& e );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Substitutes the char r for string x in string s.
|
||||
* \param s - The string to operate on.
|
||||
* \param r - The char to replace.
|
||||
* \param x - What to replace r with.
|
||||
*/
|
||||
void substituteSpecialChars( std::string& s, char r, std::string& x );
|
||||
void removeChar( std::string& s, char r);
|
||||
void basename( std::string& s );
|
||||
|
||||
void printShortArg(Arg* it);
|
||||
void printLongArg(Arg* it);
|
||||
|
||||
char theDelimiter;
|
||||
};
|
||||
|
||||
|
||||
inline void DocBookOutput::version(CmdLineInterface& _cmd)
|
||||
{
|
||||
std::cout << _cmd.getVersion() << std::endl;
|
||||
}
|
||||
|
||||
inline void DocBookOutput::usage(CmdLineInterface& _cmd )
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string progName = _cmd.getProgramName();
|
||||
std::string xversion = _cmd.getVersion();
|
||||
theDelimiter = _cmd.getDelimiter();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
basename(progName);
|
||||
|
||||
std::cout << "<?xml version='1.0'?>" << std::endl;
|
||||
std::cout << "<!DOCTYPE refentry PUBLIC \"-//OASIS//DTD DocBook XML V4.2//EN\"" << std::endl;
|
||||
std::cout << "\t\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\">" << std::endl << std::endl;
|
||||
|
||||
std::cout << "<refentry>" << std::endl;
|
||||
|
||||
std::cout << "<refmeta>" << std::endl;
|
||||
std::cout << "<refentrytitle>" << progName << "</refentrytitle>" << std::endl;
|
||||
std::cout << "<manvolnum>1</manvolnum>" << std::endl;
|
||||
std::cout << "</refmeta>" << std::endl;
|
||||
|
||||
std::cout << "<refnamediv>" << std::endl;
|
||||
std::cout << "<refname>" << progName << "</refname>" << std::endl;
|
||||
std::cout << "<refpurpose>" << _cmd.getMessage() << "</refpurpose>" << std::endl;
|
||||
std::cout << "</refnamediv>" << std::endl;
|
||||
|
||||
std::cout << "<refsynopsisdiv>" << std::endl;
|
||||
std::cout << "<cmdsynopsis>" << std::endl;
|
||||
|
||||
std::cout << "<command>" << progName << "</command>" << std::endl;
|
||||
|
||||
// xor
|
||||
for ( int i = 0; (unsigned int)i < xorList.size(); i++ )
|
||||
{
|
||||
std::cout << "<group choice='req'>" << std::endl;
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end(); it++ )
|
||||
printShortArg((*it));
|
||||
|
||||
std::cout << "</group>" << std::endl;
|
||||
}
|
||||
|
||||
// rest of args
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
printShortArg((*it));
|
||||
|
||||
std::cout << "</cmdsynopsis>" << std::endl;
|
||||
std::cout << "</refsynopsisdiv>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Description</title>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << _cmd.getMessage() << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Options</title>" << std::endl;
|
||||
|
||||
std::cout << "<variablelist>" << std::endl;
|
||||
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
printLongArg((*it));
|
||||
|
||||
std::cout << "</variablelist>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "<refsect1>" << std::endl;
|
||||
std::cout << "<title>Version</title>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << xversion << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</refsect1>" << std::endl;
|
||||
|
||||
std::cout << "</refentry>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void DocBookOutput::failure( CmdLineInterface& _cmd,
|
||||
ArgException& e )
|
||||
{
|
||||
static_cast<void>(_cmd); // unused
|
||||
std::cout << e.what() << std::endl;
|
||||
throw ExitException(1);
|
||||
}
|
||||
|
||||
inline void DocBookOutput::substituteSpecialChars( std::string& s,
|
||||
char r,
|
||||
std::string& x )
|
||||
{
|
||||
size_t p;
|
||||
while ( (p = s.find_first_of(r)) != std::string::npos )
|
||||
{
|
||||
s.erase(p,1);
|
||||
s.insert(p,x);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::removeChar( std::string& s, char r)
|
||||
{
|
||||
size_t p;
|
||||
while ( (p = s.find_first_of(r)) != std::string::npos )
|
||||
{
|
||||
s.erase(p,1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::basename( std::string& s )
|
||||
{
|
||||
size_t p = s.find_last_of('/');
|
||||
if ( p != std::string::npos )
|
||||
{
|
||||
s.erase(0, p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void DocBookOutput::printShortArg(Arg* a)
|
||||
{
|
||||
std::string lt = "<";
|
||||
std::string gt = ">";
|
||||
|
||||
std::string id = a->shortID();
|
||||
substituteSpecialChars(id,'<',lt);
|
||||
substituteSpecialChars(id,'>',gt);
|
||||
removeChar(id,'[');
|
||||
removeChar(id,']');
|
||||
|
||||
std::string choice = "opt";
|
||||
if ( a->isRequired() )
|
||||
choice = "plain";
|
||||
|
||||
std::cout << "<arg choice='" << choice << '\'';
|
||||
if ( a->acceptsMultipleValues() )
|
||||
std::cout << " rep='repeat'";
|
||||
|
||||
|
||||
std::cout << '>';
|
||||
if ( !a->getFlag().empty() )
|
||||
std::cout << a->flagStartChar() << a->getFlag();
|
||||
else
|
||||
std::cout << a->nameStartString() << a->getName();
|
||||
if ( a->isValueRequired() )
|
||||
{
|
||||
std::string arg = a->shortID();
|
||||
removeChar(arg,'[');
|
||||
removeChar(arg,']');
|
||||
removeChar(arg,'<');
|
||||
removeChar(arg,'>');
|
||||
arg.erase(0, arg.find_last_of(theDelimiter) + 1);
|
||||
std::cout << theDelimiter;
|
||||
std::cout << "<replaceable>" << arg << "</replaceable>";
|
||||
}
|
||||
std::cout << "</arg>" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void DocBookOutput::printLongArg(Arg* a)
|
||||
{
|
||||
std::string lt = "<";
|
||||
std::string gt = ">";
|
||||
|
||||
std::string desc = a->getDescription();
|
||||
substituteSpecialChars(desc,'<',lt);
|
||||
substituteSpecialChars(desc,'>',gt);
|
||||
|
||||
std::cout << "<varlistentry>" << std::endl;
|
||||
|
||||
if ( !a->getFlag().empty() )
|
||||
{
|
||||
std::cout << "<term>" << std::endl;
|
||||
std::cout << "<option>";
|
||||
std::cout << a->flagStartChar() << a->getFlag();
|
||||
std::cout << "</option>" << std::endl;
|
||||
std::cout << "</term>" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "<term>" << std::endl;
|
||||
std::cout << "<option>";
|
||||
std::cout << a->nameStartString() << a->getName();
|
||||
if ( a->isValueRequired() )
|
||||
{
|
||||
std::string arg = a->shortID();
|
||||
removeChar(arg,'[');
|
||||
removeChar(arg,']');
|
||||
removeChar(arg,'<');
|
||||
removeChar(arg,'>');
|
||||
arg.erase(0, arg.find_last_of(theDelimiter) + 1);
|
||||
std::cout << theDelimiter;
|
||||
std::cout << "<replaceable>" << arg << "</replaceable>";
|
||||
}
|
||||
std::cout << "</option>" << std::endl;
|
||||
std::cout << "</term>" << std::endl;
|
||||
|
||||
std::cout << "<listitem>" << std::endl;
|
||||
std::cout << "<para>" << std::endl;
|
||||
std::cout << desc << std::endl;
|
||||
std::cout << "</para>" << std::endl;
|
||||
std::cout << "</listitem>" << std::endl;
|
||||
|
||||
std::cout << "</varlistentry>" << std::endl;
|
||||
}
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
76
tclap/HelpVisitor.h
Normal file
76
tclap/HelpVisitor.h
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: HelpVisitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_HELP_VISITOR_H
|
||||
#define TCLAP_HELP_VISITOR_H
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/Visitor.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Visitor object that calls the usage method of the given CmdLineOutput
|
||||
* object for the specified CmdLine object.
|
||||
*/
|
||||
class HelpVisitor: public Visitor
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying.
|
||||
*/
|
||||
HelpVisitor(const HelpVisitor& rhs);
|
||||
HelpVisitor& operator=(const HelpVisitor& rhs);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The CmdLine the output will be generated for.
|
||||
*/
|
||||
CmdLineInterface* _cmd;
|
||||
|
||||
/**
|
||||
* The output object.
|
||||
*/
|
||||
CmdLineOutput** _out;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param cmd - The CmdLine the output will be generated for.
|
||||
* \param out - The type of output.
|
||||
*/
|
||||
HelpVisitor(CmdLineInterface* cmd, CmdLineOutput** out)
|
||||
: Visitor(), _cmd( cmd ), _out( out ) { }
|
||||
|
||||
/**
|
||||
* Calls the usage method of the CmdLineOutput for the
|
||||
* specified CmdLine.
|
||||
*/
|
||||
void visit() { (*_out)->usage(*_cmd); throw ExitException(0); }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
52
tclap/IgnoreRestVisitor.h
Normal file
52
tclap/IgnoreRestVisitor.h
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: IgnoreRestVisitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_IGNORE_REST_VISITOR_H
|
||||
#define TCLAP_IGNORE_REST_VISITOR_H
|
||||
|
||||
#include <tclap/Visitor.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Vistor that tells the CmdLine to begin ignoring arguments after
|
||||
* this one is parsed.
|
||||
*/
|
||||
class IgnoreRestVisitor: public Visitor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
IgnoreRestVisitor() : Visitor() {}
|
||||
|
||||
/**
|
||||
* Sets Arg::_ignoreRest.
|
||||
*/
|
||||
void visit() { Arg::beginIgnoring(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
433
tclap/MultiArg.h
Normal file
433
tclap/MultiArg.h
Normal file
@ -0,0 +1,433 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: MultiArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_MULTIPLE_ARGUMENT_H
|
||||
#define TCLAP_MULTIPLE_ARGUMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/Arg.h>
|
||||
#include <tclap/Constraint.h>
|
||||
|
||||
namespace TCLAP {
|
||||
/**
|
||||
* An argument that allows multiple values of type T to be specified. Very
|
||||
* similar to a ValueArg, except a vector of values will be returned
|
||||
* instead of just one.
|
||||
*/
|
||||
template<class T>
|
||||
class MultiArg : public Arg
|
||||
{
|
||||
public:
|
||||
typedef std::vector<T> container_type;
|
||||
typedef typename container_type::iterator iterator;
|
||||
typedef typename container_type::const_iterator const_iterator;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of values parsed from the CmdLine.
|
||||
*/
|
||||
std::vector<T> _values;
|
||||
|
||||
/**
|
||||
* The description of type T to be used in the usage.
|
||||
*/
|
||||
std::string _typeDesc;
|
||||
|
||||
/**
|
||||
* A list of constraint on this Arg.
|
||||
*/
|
||||
Constraint<T>* _constraint;
|
||||
|
||||
/**
|
||||
* Extracts the value from the string.
|
||||
* Attempts to parse string as type T, if this fails an exception
|
||||
* is thrown.
|
||||
* \param val - The string to be read.
|
||||
*/
|
||||
void _extractValue( const std::string& val );
|
||||
|
||||
/**
|
||||
* Used by XorHandler to decide whether to keep parsing for this arg.
|
||||
*/
|
||||
bool _allowMore;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v = NULL);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately. It knows the difference
|
||||
* between labeled and unlabeled.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns a vector of type T containing the values parsed from
|
||||
* the command line.
|
||||
*/
|
||||
const std::vector<T>& getValue();
|
||||
|
||||
/**
|
||||
* Returns an iterator over the values parsed from the command
|
||||
* line.
|
||||
*/
|
||||
const_iterator begin() const { return _values.begin(); }
|
||||
|
||||
/**
|
||||
* Returns the end of the values parsed from the command
|
||||
* line.
|
||||
*/
|
||||
const_iterator end() const { return _values.end(); }
|
||||
|
||||
/**
|
||||
* Returns the a short id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string shortID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Returns the a long id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string longID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Once we've matched the first value, then the arg is no longer
|
||||
* required.
|
||||
*/
|
||||
virtual bool isRequired() const;
|
||||
|
||||
virtual bool allowMore();
|
||||
|
||||
virtual void reset();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying
|
||||
*/
|
||||
MultiArg<T>(const MultiArg<T>& rhs);
|
||||
MultiArg<T>& operator=(const MultiArg<T>& rhs);
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v) :
|
||||
Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL ),
|
||||
_allowMore(false)
|
||||
{
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL ),
|
||||
_allowMore(false)
|
||||
{
|
||||
parser.add( this );
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint ),
|
||||
_allowMore(false)
|
||||
{
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MultiArg<T>::MultiArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg( flag, name, desc, req, true, v ),
|
||||
_values(std::vector<T>()),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint ),
|
||||
_allowMore(false)
|
||||
{
|
||||
parser.add( this );
|
||||
_acceptsMultipleValues = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const std::vector<T>& MultiArg<T>::getValue() { return _values; }
|
||||
|
||||
template<class T>
|
||||
bool MultiArg<T>::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
if ( _hasBlanks( args[*i] ) )
|
||||
return false;
|
||||
|
||||
std::string flag = args[*i];
|
||||
std::string value = "";
|
||||
|
||||
trimFlag( flag, value );
|
||||
|
||||
if ( argMatches( flag ) )
|
||||
{
|
||||
if ( Arg::delimiter() != ' ' && value == "" )
|
||||
throw( ArgParseException(
|
||||
"Couldn't find delimiter for this argument!",
|
||||
toString() ) );
|
||||
|
||||
// always take the first one, regardless of start string
|
||||
if ( value == "" )
|
||||
{
|
||||
(*i)++;
|
||||
if ( static_cast<unsigned int>(*i) < args.size() )
|
||||
_extractValue( args[*i] );
|
||||
else
|
||||
throw( ArgParseException("Missing a value for this argument!",
|
||||
toString() ) );
|
||||
}
|
||||
else
|
||||
_extractValue( value );
|
||||
|
||||
/*
|
||||
// continuing taking the args until we hit one with a start string
|
||||
while ( (unsigned int)(*i)+1 < args.size() &&
|
||||
args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 &&
|
||||
args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 )
|
||||
_extractValue( args[++(*i)] );
|
||||
*/
|
||||
|
||||
_alreadySet = true;
|
||||
_checkWithVisitor();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
std::string MultiArg<T>::shortID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::shortID(_typeDesc) + " ... ";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
std::string MultiArg<T>::longID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::longID(_typeDesc) + " (accepted multiple times)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Once we've matched the first value, then the arg is no longer
|
||||
* required.
|
||||
*/
|
||||
template<class T>
|
||||
bool MultiArg<T>::isRequired() const
|
||||
{
|
||||
if ( _required )
|
||||
{
|
||||
if ( _values.size() > 1 )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MultiArg<T>::_extractValue( const std::string& val )
|
||||
{
|
||||
try {
|
||||
T tmp;
|
||||
ExtractValue(tmp, val, typename ArgTraits<T>::ValueCategory());
|
||||
_values.push_back(tmp);
|
||||
} catch( ArgParseException &e) {
|
||||
throw ArgParseException(e.error(), toString());
|
||||
}
|
||||
|
||||
if ( _constraint != NULL )
|
||||
if ( ! _constraint->check( _values.back() ) )
|
||||
throw( CmdLineParseException( "Value '" + val +
|
||||
"' does not meet constraint: " +
|
||||
_constraint->description(),
|
||||
toString() ) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool MultiArg<T>::allowMore()
|
||||
{
|
||||
bool am = _allowMore;
|
||||
_allowMore = true;
|
||||
return am;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MultiArg<T>::reset()
|
||||
{
|
||||
Arg::reset();
|
||||
_values.clear();
|
||||
}
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
216
tclap/MultiSwitchArg.h
Normal file
216
tclap/MultiSwitchArg.h
Normal file
@ -0,0 +1,216 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: MultiSwitchArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* Copyright (c) 2005, Michael E. Smoot, Daniel Aarno, Erik Zeek.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_MULTI_SWITCH_ARG_H
|
||||
#define TCLAP_MULTI_SWITCH_ARG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/SwitchArg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A multiple switch argument. If the switch is set on the command line, then
|
||||
* the getValue method will return the number of times the switch appears.
|
||||
*/
|
||||
class MultiSwitchArg : public SwitchArg
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The value of the switch.
|
||||
*/
|
||||
int _value;
|
||||
|
||||
/**
|
||||
* Used to support the reset() method so that ValueArg can be
|
||||
* reset to their constructed value.
|
||||
*/
|
||||
int _default;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* MultiSwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param init - Optional. The initial/default value of this Arg.
|
||||
* Defaults to 0.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
int init = 0,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* MultiSwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param init - Optional. The initial/default value of this Arg.
|
||||
* Defaults to 0.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
int init = 0,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the SwitchArg version of this method to set the
|
||||
* _value of the argument appropriately.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed
|
||||
* in from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns int, the number of times the switch has been set.
|
||||
*/
|
||||
int getValue();
|
||||
|
||||
/**
|
||||
* Returns the shortID for this Arg.
|
||||
*/
|
||||
std::string shortID(const std::string& val) const;
|
||||
|
||||
/**
|
||||
* Returns the longID for this Arg.
|
||||
*/
|
||||
std::string longID(const std::string& val) const;
|
||||
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN MultiSwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
inline MultiSwitchArg::MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
int init,
|
||||
Visitor* v )
|
||||
: SwitchArg(flag, name, desc, false, v),
|
||||
_value( init ),
|
||||
_default( init )
|
||||
{ }
|
||||
|
||||
inline MultiSwitchArg::MultiSwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
int init,
|
||||
Visitor* v )
|
||||
: SwitchArg(flag, name, desc, false, v),
|
||||
_value( init ),
|
||||
_default( init )
|
||||
{
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
inline int MultiSwitchArg::getValue() { return _value; }
|
||||
|
||||
inline bool MultiSwitchArg::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
if ( argMatches( args[*i] ))
|
||||
{
|
||||
// so the isSet() method will work
|
||||
_alreadySet = true;
|
||||
|
||||
// Matched argument: increment value.
|
||||
++_value;
|
||||
|
||||
_checkWithVisitor();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( combinedSwitchesMatch( args[*i] ) )
|
||||
{
|
||||
// so the isSet() method will work
|
||||
_alreadySet = true;
|
||||
|
||||
// Matched argument: increment value.
|
||||
++_value;
|
||||
|
||||
// Check for more in argument and increment value.
|
||||
while ( combinedSwitchesMatch( args[*i] ) )
|
||||
++_value;
|
||||
|
||||
_checkWithVisitor();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
MultiSwitchArg::shortID(const std::string& val) const
|
||||
{
|
||||
return Arg::shortID(val) + " ... ";
|
||||
}
|
||||
|
||||
inline std::string
|
||||
MultiSwitchArg::longID(const std::string& val) const
|
||||
{
|
||||
return Arg::longID(val) + " (accepted multiple times)";
|
||||
}
|
||||
|
||||
inline void
|
||||
MultiSwitchArg::reset()
|
||||
{
|
||||
MultiSwitchArg::_value = MultiSwitchArg::_default;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//END MultiSwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
62
tclap/OptionalUnlabeledTracker.h
Normal file
62
tclap/OptionalUnlabeledTracker.h
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: OptionalUnlabeledTracker.h
|
||||
*
|
||||
* Copyright (c) 2005, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_OPTIONAL_UNLABELED_TRACKER_H
|
||||
#define TCLAP_OPTIONAL_UNLABELED_TRACKER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
class OptionalUnlabeledTracker
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
static void check( bool req, const std::string& argName );
|
||||
|
||||
static void gotOptional() { alreadyOptionalRef() = true; }
|
||||
|
||||
static bool& alreadyOptional() { return alreadyOptionalRef(); }
|
||||
|
||||
private:
|
||||
|
||||
static bool& alreadyOptionalRef() { static bool ct = false; return ct; }
|
||||
};
|
||||
|
||||
|
||||
inline void OptionalUnlabeledTracker::check( bool req, const std::string& argName )
|
||||
{
|
||||
if ( OptionalUnlabeledTracker::alreadyOptional() )
|
||||
throw( SpecificationException(
|
||||
"You can't specify ANY Unlabeled Arg following an optional Unlabeled Arg",
|
||||
argName ) );
|
||||
|
||||
if ( !req )
|
||||
OptionalUnlabeledTracker::gotOptional();
|
||||
}
|
||||
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
208
tclap/StandardTraits.h
Normal file
208
tclap/StandardTraits.h
Normal file
@ -0,0 +1,208 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: StandardTraits.h
|
||||
*
|
||||
* Copyright (c) 2007, Daniel Aarno, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// This is an internal tclap file, you should probably not have to
|
||||
// include this directly
|
||||
|
||||
#ifndef TCLAP_STANDARD_TRAITS_H
|
||||
#define TCLAP_STANDARD_TRAITS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // To check for long long
|
||||
#endif
|
||||
|
||||
// If Microsoft has already typedef'd wchar_t as an unsigned
|
||||
// short, then compiles will break because it's as if we're
|
||||
// creating ArgTraits twice for unsigned short. Thus...
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _NATIVE_WCHAR_T_DEFINED
|
||||
#define TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
// ======================================================================
|
||||
// Integer types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* ints have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<int> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* shorts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<short> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* chars have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<char> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
#ifdef HAVE_LONG_LONG
|
||||
/**
|
||||
* long longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<long long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
// Unsigned integer types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* unsigned longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned ints have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned int> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned shorts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned short> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* unsigned chars have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned char> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
// Microsoft implements size_t awkwardly.
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
/**
|
||||
* size_ts have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<size_t> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_LONG_LONG
|
||||
/**
|
||||
* unsigned long longs have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<unsigned long long> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
// Float types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* floats have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<float> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
/**
|
||||
* doubles have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<double> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// Other types
|
||||
// ======================================================================
|
||||
|
||||
/**
|
||||
* bools have value-like semantics.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<bool> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* wchar_ts have value-like semantics.
|
||||
*/
|
||||
#ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS
|
||||
template<>
|
||||
struct ArgTraits<wchar_t> {
|
||||
typedef ValueLike ValueCategory;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Strings have string like argument traits.
|
||||
*/
|
||||
template<>
|
||||
struct ArgTraits<std::string> {
|
||||
typedef StringLike ValueCategory;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void SetString(T &dst, const std::string &src)
|
||||
{
|
||||
dst = src;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
298
tclap/StdOutput.h
Normal file
298
tclap/StdOutput.h
Normal file
@ -0,0 +1,298 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: StdOutput.h
|
||||
*
|
||||
* Copyright (c) 2004, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_STDCMDLINEOUTPUT_H
|
||||
#define TCLAP_STDCMDLINEOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A class that isolates any output from the CmdLine object so that it
|
||||
* may be easily modified.
|
||||
*/
|
||||
class StdOutput : public CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Prints the usage to stdout. Can be overridden to
|
||||
* produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints the version to stdout. Can be overridden
|
||||
* to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints (to stderr) an error message, short usage
|
||||
* Can be overridden to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure(CmdLineInterface& c,
|
||||
ArgException& e );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Writes a brief usage message with short args.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param os - The stream to write the message to.
|
||||
*/
|
||||
void _shortUsage( CmdLineInterface& c, std::ostream& os ) const;
|
||||
|
||||
/**
|
||||
* Writes a longer usage message with long and short args,
|
||||
* provides descriptions and prints message.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param os - The stream to write the message to.
|
||||
*/
|
||||
void _longUsage( CmdLineInterface& c, std::ostream& os ) const;
|
||||
|
||||
/**
|
||||
* This function inserts line breaks and indents long strings
|
||||
* according the params input. It will only break lines at spaces,
|
||||
* commas and pipes.
|
||||
* \param os - The stream to be printed to.
|
||||
* \param s - The string to be printed.
|
||||
* \param maxWidth - The maxWidth allowed for the output line.
|
||||
* \param indentSpaces - The number of spaces to indent the first line.
|
||||
* \param secondLineOffset - The number of spaces to indent the second
|
||||
* and all subsequent lines in addition to indentSpaces.
|
||||
*/
|
||||
void spacePrint( std::ostream& os,
|
||||
const std::string& s,
|
||||
int maxWidth,
|
||||
int indentSpaces,
|
||||
int secondLineOffset ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline void StdOutput::version(CmdLineInterface& _cmd)
|
||||
{
|
||||
std::string progName = _cmd.getProgramName();
|
||||
std::string xversion = _cmd.getVersion();
|
||||
|
||||
std::cout << std::endl << progName << " version: "
|
||||
<< xversion << std::endl << std::endl;
|
||||
}
|
||||
|
||||
inline void StdOutput::usage(CmdLineInterface& _cmd )
|
||||
{
|
||||
std::cout << std::endl << "USAGE: " << std::endl << std::endl;
|
||||
|
||||
_shortUsage( _cmd, std::cout );
|
||||
|
||||
std::cout << std::endl << std::endl << "Where: " << std::endl << std::endl;
|
||||
|
||||
_longUsage( _cmd, std::cout );
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
}
|
||||
|
||||
inline void StdOutput::failure( CmdLineInterface& _cmd,
|
||||
ArgException& e )
|
||||
{
|
||||
std::string progName = _cmd.getProgramName();
|
||||
|
||||
std::cerr << "PARSE ERROR: " << e.argId() << std::endl
|
||||
<< " " << e.error() << std::endl << std::endl;
|
||||
|
||||
if ( _cmd.hasHelpAndVersion() )
|
||||
{
|
||||
std::cerr << "Brief USAGE: " << std::endl;
|
||||
|
||||
_shortUsage( _cmd, std::cerr );
|
||||
|
||||
std::cerr << std::endl << "For complete USAGE and HELP type: "
|
||||
<< std::endl << " " << progName << " --help"
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
else
|
||||
usage(_cmd);
|
||||
|
||||
throw ExitException(1);
|
||||
}
|
||||
|
||||
inline void
|
||||
StdOutput::_shortUsage( CmdLineInterface& _cmd,
|
||||
std::ostream& os ) const
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string progName = _cmd.getProgramName();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
|
||||
std::string s = progName + " ";
|
||||
|
||||
// first the xor
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
|
||||
{
|
||||
s += " {";
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end(); it++ )
|
||||
s += (*it)->shortID() + "|";
|
||||
|
||||
s[s.length()-1] = '}';
|
||||
}
|
||||
|
||||
// then the rest
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
s += " " + (*it)->shortID();
|
||||
|
||||
// if the program name is too long, then adjust the second line offset
|
||||
int secondLineOffset = static_cast<int>(progName.length()) + 2;
|
||||
if ( secondLineOffset > 75/2 )
|
||||
secondLineOffset = static_cast<int>(75/2);
|
||||
|
||||
spacePrint( os, s, 75, 3, secondLineOffset );
|
||||
}
|
||||
|
||||
inline void
|
||||
StdOutput::_longUsage( CmdLineInterface& _cmd,
|
||||
std::ostream& os ) const
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string message = _cmd.getMessage();
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
|
||||
// first the xor
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
|
||||
{
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end();
|
||||
it++ )
|
||||
{
|
||||
spacePrint( os, (*it)->longID(), 75, 3, 3 );
|
||||
spacePrint( os, (*it)->getDescription(), 75, 5, 0 );
|
||||
|
||||
if ( it+1 != xorList[i].end() )
|
||||
spacePrint(os, "-- OR --", 75, 9, 0);
|
||||
}
|
||||
os << std::endl << std::endl;
|
||||
}
|
||||
|
||||
// then the rest
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
if ( !xorHandler.contains( (*it) ) )
|
||||
{
|
||||
spacePrint( os, (*it)->longID(), 75, 3, 3 );
|
||||
spacePrint( os, (*it)->getDescription(), 75, 5, 0 );
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
os << std::endl;
|
||||
|
||||
spacePrint( os, message, 75, 3, 0 );
|
||||
}
|
||||
|
||||
inline void StdOutput::spacePrint( std::ostream& os,
|
||||
const std::string& s,
|
||||
int maxWidth,
|
||||
int indentSpaces,
|
||||
int secondLineOffset ) const
|
||||
{
|
||||
int len = static_cast<int>(s.length());
|
||||
|
||||
if ( (len + indentSpaces > maxWidth) && maxWidth > 0 )
|
||||
{
|
||||
int allowedLen = maxWidth - indentSpaces;
|
||||
int start = 0;
|
||||
while ( start < len )
|
||||
{
|
||||
// find the substring length
|
||||
// int stringLen = std::min<int>( len - start, allowedLen );
|
||||
// doing it this way to support a VisualC++ 2005 bug
|
||||
using namespace std;
|
||||
int stringLen = min<int>( len - start, allowedLen );
|
||||
|
||||
// trim the length so it doesn't end in middle of a word
|
||||
if ( stringLen == allowedLen )
|
||||
while ( stringLen >= 0 &&
|
||||
s[stringLen+start] != ' ' &&
|
||||
s[stringLen+start] != ',' &&
|
||||
s[stringLen+start] != '|' )
|
||||
stringLen--;
|
||||
|
||||
// ok, the word is longer than the line, so just split
|
||||
// wherever the line ends
|
||||
if ( stringLen <= 0 )
|
||||
stringLen = allowedLen;
|
||||
|
||||
// check for newlines
|
||||
for ( int i = 0; i < stringLen; i++ )
|
||||
if ( s[start+i] == '\n' )
|
||||
stringLen = i+1;
|
||||
|
||||
// print the indent
|
||||
for ( int i = 0; i < indentSpaces; i++ )
|
||||
os << " ";
|
||||
|
||||
if ( start == 0 )
|
||||
{
|
||||
// handle second line offsets
|
||||
indentSpaces += secondLineOffset;
|
||||
|
||||
// adjust allowed len
|
||||
allowedLen -= secondLineOffset;
|
||||
}
|
||||
|
||||
os << s.substr(start,stringLen) << std::endl;
|
||||
|
||||
// so we don't start a line with a space
|
||||
while ( s[stringLen+start] == ' ' && start < len )
|
||||
start++;
|
||||
|
||||
start += stringLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < indentSpaces; i++ )
|
||||
os << " ";
|
||||
os << s << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
266
tclap/SwitchArg.h
Normal file
266
tclap/SwitchArg.h
Normal file
@ -0,0 +1,266 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: SwitchArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_SWITCH_ARG_H
|
||||
#define TCLAP_SWITCH_ARG_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A simple switch argument. If the switch is set on the command line, then
|
||||
* the getValue method will return the opposite of the default value for the
|
||||
* switch.
|
||||
*/
|
||||
class SwitchArg : public Arg
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The value of the switch.
|
||||
*/
|
||||
bool _value;
|
||||
|
||||
/**
|
||||
* Used to support the reset() method so that ValueArg can be
|
||||
* reset to their constructed value.
|
||||
*/
|
||||
bool _default;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* SwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param def - The default value for this Switch.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
SwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool def = false,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* SwitchArg constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param def - The default value for this Switch.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
SwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
bool def = false,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed
|
||||
* in from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Checks a string to see if any of the chars in the string
|
||||
* match the flag for this Switch.
|
||||
*/
|
||||
bool combinedSwitchesMatch(std::string& combined);
|
||||
|
||||
/**
|
||||
* Returns bool, whether or not the switch has been set.
|
||||
*/
|
||||
bool getValue();
|
||||
|
||||
virtual void reset();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Checks to see if we've found the last match in
|
||||
* a combined string.
|
||||
*/
|
||||
bool lastCombined(std::string& combined);
|
||||
|
||||
/**
|
||||
* Does the common processing of processArg.
|
||||
*/
|
||||
void commonProcessing();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN SwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
inline SwitchArg::SwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool default_val,
|
||||
Visitor* v )
|
||||
: Arg(flag, name, desc, false, false, v),
|
||||
_value( default_val ),
|
||||
_default( default_val )
|
||||
{ }
|
||||
|
||||
inline SwitchArg::SwitchArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
CmdLineInterface& parser,
|
||||
bool default_val,
|
||||
Visitor* v )
|
||||
: Arg(flag, name, desc, false, false, v),
|
||||
_value( default_val ),
|
||||
_default(default_val)
|
||||
{
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
inline bool SwitchArg::getValue() { return _value; }
|
||||
|
||||
inline bool SwitchArg::lastCombined(std::string& combinedSwitches )
|
||||
{
|
||||
for ( unsigned int i = 1; i < combinedSwitches.length(); i++ )
|
||||
if ( combinedSwitches[i] != Arg::blankChar() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SwitchArg::combinedSwitchesMatch(std::string& combinedSwitches )
|
||||
{
|
||||
// make sure this is actually a combined switch
|
||||
if ( combinedSwitches.length() > 0 &&
|
||||
combinedSwitches[0] != Arg::flagStartString()[0] )
|
||||
return false;
|
||||
|
||||
// make sure it isn't a long name
|
||||
if ( combinedSwitches.substr( 0, Arg::nameStartString().length() ) ==
|
||||
Arg::nameStartString() )
|
||||
return false;
|
||||
|
||||
// make sure the delimiter isn't in the string
|
||||
if ( combinedSwitches.find_first_of( Arg::delimiter() ) != std::string::npos )
|
||||
return false;
|
||||
|
||||
// ok, we're not specifying a ValueArg, so we know that we have
|
||||
// a combined switch list.
|
||||
for ( unsigned int i = 1; i < combinedSwitches.length(); i++ )
|
||||
if ( _flag.length() > 0 &&
|
||||
combinedSwitches[i] == _flag[0] &&
|
||||
_flag[0] != Arg::flagStartString()[0] )
|
||||
{
|
||||
// update the combined switches so this one is no longer present
|
||||
// this is necessary so that no unlabeled args are matched
|
||||
// later in the processing.
|
||||
//combinedSwitches.erase(i,1);
|
||||
combinedSwitches[i] = Arg::blankChar();
|
||||
return true;
|
||||
}
|
||||
|
||||
// none of the switches passed in the list match.
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void SwitchArg::commonProcessing()
|
||||
{
|
||||
if ( _xorSet )
|
||||
throw(CmdLineParseException(
|
||||
"Mutually exclusive argument already set!", toString()));
|
||||
|
||||
if ( _alreadySet )
|
||||
throw(CmdLineParseException("Argument already set!", toString()));
|
||||
|
||||
_alreadySet = true;
|
||||
|
||||
if ( _value == true )
|
||||
_value = false;
|
||||
else
|
||||
_value = true;
|
||||
|
||||
_checkWithVisitor();
|
||||
}
|
||||
|
||||
inline bool SwitchArg::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
// if the whole string matches the flag or name string
|
||||
if ( argMatches( args[*i] ) )
|
||||
{
|
||||
commonProcessing();
|
||||
|
||||
return true;
|
||||
}
|
||||
// if a substring matches the flag as part of a combination
|
||||
else if ( combinedSwitchesMatch( args[*i] ) )
|
||||
{
|
||||
// check again to ensure we don't misinterpret
|
||||
// this as a MultiSwitchArg
|
||||
if ( combinedSwitchesMatch( args[*i] ) )
|
||||
throw(CmdLineParseException("Argument already set!",
|
||||
toString()));
|
||||
|
||||
commonProcessing();
|
||||
|
||||
// We only want to return true if we've found the last combined
|
||||
// match in the string, otherwise we return true so that other
|
||||
// switches in the combination will have a chance to match.
|
||||
return lastCombined( args[*i] );
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void SwitchArg::reset()
|
||||
{
|
||||
Arg::reset();
|
||||
_value = _default;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//End SwitchArg.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
301
tclap/UnlabeledMultiArg.h
Normal file
301
tclap/UnlabeledMultiArg.h
Normal file
@ -0,0 +1,301 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: UnlabeledMultiArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H
|
||||
#define TCLAP_MULTIPLE_UNLABELED_ARGUMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/MultiArg.h>
|
||||
#include <tclap/OptionalUnlabeledTracker.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* Just like a MultiArg, except that the arguments are unlabeled. Basically,
|
||||
* this Arg will slurp up everything that hasn't been matched to another
|
||||
* Arg.
|
||||
*/
|
||||
template<class T>
|
||||
class UnlabeledMultiArg : public MultiArg<T>
|
||||
{
|
||||
|
||||
// If compiler has two stage name lookup (as gcc >= 3.4 does)
|
||||
// this is requried to prevent undef. symbols
|
||||
using MultiArg<T>::_ignoreable;
|
||||
using MultiArg<T>::_hasBlanks;
|
||||
using MultiArg<T>::_extractValue;
|
||||
using MultiArg<T>::_typeDesc;
|
||||
using MultiArg<T>::_name;
|
||||
using MultiArg<T>::_description;
|
||||
using MultiArg<T>::_alreadySet;
|
||||
using MultiArg<T>::toString;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param name - The name of the Arg. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param ignoreable - Whether or not this argument can be ignored
|
||||
* using the "--" flag.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
UnlabeledMultiArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
/**
|
||||
* Constructor.
|
||||
* \param name - The name of the Arg. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param ignoreable - Whether or not this argument can be ignored
|
||||
* using the "--" flag.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
UnlabeledMultiArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param name - The name of the Arg. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param ignoreable - Whether or not this argument can be ignored
|
||||
* using the "--" flag.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
UnlabeledMultiArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param name - The name of the Arg. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param ignoreable - Whether or not this argument can be ignored
|
||||
* using the "--" flag.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
UnlabeledMultiArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately. It knows the difference
|
||||
* between labeled and unlabeled.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns the a short id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string shortID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Returns the a long id string. Used in the usage.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string longID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Opertor ==.
|
||||
* \param a - The Arg to be compared to this.
|
||||
*/
|
||||
virtual bool operator==(const Arg& a) const;
|
||||
|
||||
/**
|
||||
* Pushes this to back of list rather than front.
|
||||
* \param argList - The list this should be added to.
|
||||
*/
|
||||
virtual void addToList( std::list<Arg*>& argList ) const;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: MultiArg<T>("", name, desc, req, typeDesc, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(true, toString());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: MultiArg<T>("", name, desc, req, typeDesc, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(true, toString());
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: MultiArg<T>("", name, desc, req, constraint, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(true, toString());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
UnlabeledMultiArg<T>::UnlabeledMultiArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: MultiArg<T>("", name, desc, req, constraint, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(true, toString());
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
bool UnlabeledMultiArg<T>::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
|
||||
if ( _hasBlanks( args[*i] ) )
|
||||
return false;
|
||||
|
||||
// never ignore an unlabeled multi arg
|
||||
|
||||
|
||||
// always take the first value, regardless of the start string
|
||||
_extractValue( args[(*i)] );
|
||||
|
||||
/*
|
||||
// continue taking args until we hit the end or a start string
|
||||
while ( (unsigned int)(*i)+1 < args.size() &&
|
||||
args[(*i)+1].find_first_of( Arg::flagStartString() ) != 0 &&
|
||||
args[(*i)+1].find_first_of( Arg::nameStartString() ) != 0 )
|
||||
_extractValue( args[++(*i)] );
|
||||
*/
|
||||
|
||||
_alreadySet = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string UnlabeledMultiArg<T>::shortID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return std::string("<") + _typeDesc + "> ...";
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string UnlabeledMultiArg<T>::longID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return std::string("<") + _typeDesc + "> (accepted multiple times)";
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool UnlabeledMultiArg<T>::operator==(const Arg& a) const
|
||||
{
|
||||
if ( _name == a.getName() || _description == a.getDescription() )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void UnlabeledMultiArg<T>::addToList( std::list<Arg*>& argList ) const
|
||||
{
|
||||
argList.push_back( const_cast<Arg*>(static_cast<const Arg* const>(this)) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
340
tclap/UnlabeledValueArg.h
Normal file
340
tclap/UnlabeledValueArg.h
Normal file
@ -0,0 +1,340 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: UnlabeledValueArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_UNLABELED_VALUE_ARGUMENT_H
|
||||
#define TCLAP_UNLABELED_VALUE_ARGUMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/ValueArg.h>
|
||||
#include <tclap/OptionalUnlabeledTracker.h>
|
||||
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* The basic unlabeled argument that parses a value.
|
||||
* This is a template class, which means the type T defines the type
|
||||
* that a given object will attempt to parse when an UnlabeledValueArg
|
||||
* is reached in the list of args that the CmdLine iterates over.
|
||||
*/
|
||||
template<class T>
|
||||
class UnlabeledValueArg : public ValueArg<T>
|
||||
{
|
||||
|
||||
// If compiler has two stage name lookup (as gcc >= 3.4 does)
|
||||
// this is requried to prevent undef. symbols
|
||||
using ValueArg<T>::_ignoreable;
|
||||
using ValueArg<T>::_hasBlanks;
|
||||
using ValueArg<T>::_extractValue;
|
||||
using ValueArg<T>::_typeDesc;
|
||||
using ValueArg<T>::_name;
|
||||
using ValueArg<T>::_description;
|
||||
using ValueArg<T>::_alreadySet;
|
||||
using ValueArg<T>::toString;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* UnlabeledValueArg constructor.
|
||||
* \param name - A one word name for the argument. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param ignoreable - Allows you to specify that this argument can be
|
||||
* ignored if the '--' flag is set. This defaults to false (cannot
|
||||
* be ignored) and should generally stay that way unless you have
|
||||
* some special need for certain arguments to be ignored.
|
||||
* \param v - Optional Vistor. You should leave this blank unless
|
||||
* you have a very good reason.
|
||||
*/
|
||||
UnlabeledValueArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
const std::string& typeDesc,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL);
|
||||
|
||||
/**
|
||||
* UnlabeledValueArg constructor.
|
||||
* \param name - A one word name for the argument. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param ignoreable - Allows you to specify that this argument can be
|
||||
* ignored if the '--' flag is set. This defaults to false (cannot
|
||||
* be ignored) and should generally stay that way unless you have
|
||||
* some special need for certain arguments to be ignored.
|
||||
* \param v - Optional Vistor. You should leave this blank unless
|
||||
* you have a very good reason.
|
||||
*/
|
||||
UnlabeledValueArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* UnlabeledValueArg constructor.
|
||||
* \param name - A one word name for the argument. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param ignoreable - Allows you to specify that this argument can be
|
||||
* ignored if the '--' flag is set. This defaults to false (cannot
|
||||
* be ignored) and should generally stay that way unless you have
|
||||
* some special need for certain arguments to be ignored.
|
||||
* \param v - Optional Vistor. You should leave this blank unless
|
||||
* you have a very good reason.
|
||||
*/
|
||||
UnlabeledValueArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
Constraint<T>* constraint,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL );
|
||||
|
||||
|
||||
/**
|
||||
* UnlabeledValueArg constructor.
|
||||
* \param name - A one word name for the argument. Note that this is used for
|
||||
* identification, not as a long flag.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param ignoreable - Allows you to specify that this argument can be
|
||||
* ignored if the '--' flag is set. This defaults to false (cannot
|
||||
* be ignored) and should generally stay that way unless you have
|
||||
* some special need for certain arguments to be ignored.
|
||||
* \param v - Optional Vistor. You should leave this blank unless
|
||||
* you have a very good reason.
|
||||
*/
|
||||
UnlabeledValueArg( const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable = false,
|
||||
Visitor* v = NULL);
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately. Handling specific to
|
||||
* unlabled arguments.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings.
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Overrides shortID for specific behavior.
|
||||
*/
|
||||
virtual std::string shortID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Overrides longID for specific behavior.
|
||||
*/
|
||||
virtual std::string longID(const std::string& val="val") const;
|
||||
|
||||
/**
|
||||
* Overrides operator== for specific behavior.
|
||||
*/
|
||||
virtual bool operator==(const Arg& a ) const;
|
||||
|
||||
/**
|
||||
* Instead of pushing to the front of list, push to the back.
|
||||
* \param argList - The list to add this to.
|
||||
*/
|
||||
virtual void addToList( std::list<Arg*>& argList ) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor implemenation.
|
||||
*/
|
||||
template<class T>
|
||||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
const std::string& typeDesc,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: ValueArg<T>("", name, desc, req, val, typeDesc, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
|
||||
OptionalUnlabeledTracker::check(req, toString());
|
||||
|
||||
}
|
||||
|
||||
template<class T>
|
||||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: ValueArg<T>("", name, desc, req, val, typeDesc, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(req, toString());
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor implemenation.
|
||||
*/
|
||||
template<class T>
|
||||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
Constraint<T>* constraint,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: ValueArg<T>("", name, desc, req, val, constraint, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(req, toString());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
UnlabeledValueArg<T>::UnlabeledValueArg(const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
bool ignoreable,
|
||||
Visitor* v)
|
||||
: ValueArg<T>("", name, desc, req, val, constraint, v)
|
||||
{
|
||||
_ignoreable = ignoreable;
|
||||
OptionalUnlabeledTracker::check(req, toString());
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of processArg().
|
||||
*/
|
||||
template<class T>
|
||||
bool UnlabeledValueArg<T>::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
|
||||
if ( _alreadySet )
|
||||
return false;
|
||||
|
||||
if ( _hasBlanks( args[*i] ) )
|
||||
return false;
|
||||
|
||||
// never ignore an unlabeled arg
|
||||
|
||||
_extractValue( args[*i] );
|
||||
_alreadySet = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding shortID for specific output.
|
||||
*/
|
||||
template<class T>
|
||||
std::string UnlabeledValueArg<T>::shortID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return std::string("<") + _typeDesc + ">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding longID for specific output.
|
||||
*/
|
||||
template<class T>
|
||||
std::string UnlabeledValueArg<T>::longID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
|
||||
// Ideally we would like to be able to use RTTI to return the name
|
||||
// of the type required for this argument. However, g++ at least,
|
||||
// doesn't appear to return terribly useful "names" of the types.
|
||||
return std::string("<") + _typeDesc + ">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding operator== for specific behavior.
|
||||
*/
|
||||
template<class T>
|
||||
bool UnlabeledValueArg<T>::operator==(const Arg& a ) const
|
||||
{
|
||||
if ( _name == a.getName() || _description == a.getDescription() )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void UnlabeledValueArg<T>::addToList( std::list<Arg*>& argList ) const
|
||||
{
|
||||
argList.push_back( const_cast<Arg*>(static_cast<const Arg* const>(this)) );
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
425
tclap/ValueArg.h
Normal file
425
tclap/ValueArg.h
Normal file
@ -0,0 +1,425 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ValueArg.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_VALUE_ARGUMENT_H
|
||||
#define TCLAP_VALUE_ARGUMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <tclap/Arg.h>
|
||||
#include <tclap/Constraint.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* The basic labeled argument that parses a value.
|
||||
* This is a template class, which means the type T defines the type
|
||||
* that a given object will attempt to parse when the flag/name is matched
|
||||
* on the command line. While there is nothing stopping you from creating
|
||||
* an unflagged ValueArg, it is unwise and would cause significant problems.
|
||||
* Instead use an UnlabeledValueArg.
|
||||
*/
|
||||
template<class T>
|
||||
class ValueArg : public Arg
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The value parsed from the command line.
|
||||
* Can be of any type, as long as the >> operator for the type
|
||||
* is defined.
|
||||
*/
|
||||
T _value;
|
||||
|
||||
/**
|
||||
* Used to support the reset() method so that ValueArg can be
|
||||
* reset to their constructed value.
|
||||
*/
|
||||
T _default;
|
||||
|
||||
/**
|
||||
* A human readable description of the type to be parsed.
|
||||
* This is a hack, plain and simple. Ideally we would use RTTI to
|
||||
* return the name of type T, but until there is some sort of
|
||||
* consistent support for human readable names, we are left to our
|
||||
* own devices.
|
||||
*/
|
||||
std::string _typeDesc;
|
||||
|
||||
/**
|
||||
* A Constraint this Arg must conform to.
|
||||
*/
|
||||
Constraint<T>* _constraint;
|
||||
|
||||
/**
|
||||
* Extracts the value from the string.
|
||||
* Attempts to parse string as type T, if this fails an exception
|
||||
* is thrown.
|
||||
* \param val - value to be parsed.
|
||||
*/
|
||||
void _extractValue( const std::string& val );
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Labeled ValueArg constructor.
|
||||
* You could conceivably call this constructor with a blank flag,
|
||||
* but that would make you a bad person. It would also cause
|
||||
* an exception to be thrown. If you want an unlabeled argument,
|
||||
* use the other constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
ValueArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v = NULL);
|
||||
|
||||
|
||||
/**
|
||||
* Labeled ValueArg constructor.
|
||||
* You could conceivably call this constructor with a blank flag,
|
||||
* but that would make you a bad person. It would also cause
|
||||
* an exception to be thrown. If you want an unlabeled argument,
|
||||
* use the other constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param typeDesc - A short, human readable description of the
|
||||
* type that this object expects. This is used in the generation
|
||||
* of the USAGE statement. The goal is to be helpful to the end user
|
||||
* of the program.
|
||||
* \param parser - A CmdLine parser object to add this Arg to
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
ValueArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Labeled ValueArg constructor.
|
||||
* You could conceivably call this constructor with a blank flag,
|
||||
* but that would make you a bad person. It would also cause
|
||||
* an exception to be thrown. If you want an unlabeled argument,
|
||||
* use the other constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param parser - A CmdLine parser object to add this Arg to.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
ValueArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Labeled ValueArg constructor.
|
||||
* You could conceivably call this constructor with a blank flag,
|
||||
* but that would make you a bad person. It would also cause
|
||||
* an exception to be thrown. If you want an unlabeled argument,
|
||||
* use the other constructor.
|
||||
* \param flag - The one character flag that identifies this
|
||||
* argument on the command line.
|
||||
* \param name - A one word name for the argument. Can be
|
||||
* used as a long flag on the command line.
|
||||
* \param desc - A description of what the argument is for or
|
||||
* does.
|
||||
* \param req - Whether the argument is required on the command
|
||||
* line.
|
||||
* \param value - The default value assigned to this argument if it
|
||||
* is not present on the command line.
|
||||
* \param constraint - A pointer to a Constraint object used
|
||||
* to constrain this Arg.
|
||||
* \param v - An optional visitor. You probably should not
|
||||
* use this unless you have a very good reason.
|
||||
*/
|
||||
ValueArg( const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T value,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v = NULL );
|
||||
|
||||
/**
|
||||
* Handles the processing of the argument.
|
||||
* This re-implements the Arg version of this method to set the
|
||||
* _value of the argument appropriately. It knows the difference
|
||||
* between labeled and unlabeled.
|
||||
* \param i - Pointer the the current argument in the list.
|
||||
* \param args - Mutable list of strings. Passed
|
||||
* in from main().
|
||||
*/
|
||||
virtual bool processArg(int* i, std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Returns the value of the argument.
|
||||
*/
|
||||
T& getValue() ;
|
||||
|
||||
/**
|
||||
* Specialization of shortID.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string shortID(const std::string& val = "val") const;
|
||||
|
||||
/**
|
||||
* Specialization of longID.
|
||||
* \param val - value to be used.
|
||||
*/
|
||||
virtual std::string longID(const std::string& val = "val") const;
|
||||
|
||||
virtual void reset() ;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying
|
||||
*/
|
||||
ValueArg<T>(const ValueArg<T>& rhs);
|
||||
ValueArg<T>& operator=(const ValueArg<T>& rhs);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor implementation.
|
||||
*/
|
||||
template<class T>
|
||||
ValueArg<T>::ValueArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
const std::string& typeDesc,
|
||||
Visitor* v)
|
||||
: Arg(flag, name, desc, req, true, v),
|
||||
_value( val ),
|
||||
_default( val ),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL )
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
ValueArg<T>::ValueArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
const std::string& typeDesc,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg(flag, name, desc, req, true, v),
|
||||
_value( val ),
|
||||
_default( val ),
|
||||
_typeDesc( typeDesc ),
|
||||
_constraint( NULL )
|
||||
{
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
ValueArg<T>::ValueArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
Constraint<T>* constraint,
|
||||
Visitor* v)
|
||||
: Arg(flag, name, desc, req, true, v),
|
||||
_value( val ),
|
||||
_default( val ),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint )
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
ValueArg<T>::ValueArg(const std::string& flag,
|
||||
const std::string& name,
|
||||
const std::string& desc,
|
||||
bool req,
|
||||
T val,
|
||||
Constraint<T>* constraint,
|
||||
CmdLineInterface& parser,
|
||||
Visitor* v)
|
||||
: Arg(flag, name, desc, req, true, v),
|
||||
_value( val ),
|
||||
_default( val ),
|
||||
_typeDesc( constraint->shortID() ),
|
||||
_constraint( constraint )
|
||||
{
|
||||
parser.add( this );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of getValue().
|
||||
*/
|
||||
template<class T>
|
||||
T& ValueArg<T>::getValue() { return _value; }
|
||||
|
||||
/**
|
||||
* Implementation of processArg().
|
||||
*/
|
||||
template<class T>
|
||||
bool ValueArg<T>::processArg(int *i, std::vector<std::string>& args)
|
||||
{
|
||||
if ( _ignoreable && Arg::ignoreRest() )
|
||||
return false;
|
||||
|
||||
if ( _hasBlanks( args[*i] ) )
|
||||
return false;
|
||||
|
||||
std::string flag = args[*i];
|
||||
|
||||
std::string value = "";
|
||||
trimFlag( flag, value );
|
||||
|
||||
if ( argMatches( flag ) )
|
||||
{
|
||||
if ( _alreadySet )
|
||||
{
|
||||
if ( _xorSet )
|
||||
throw( CmdLineParseException(
|
||||
"Mutually exclusive argument already set!",
|
||||
toString()) );
|
||||
else
|
||||
throw( CmdLineParseException("Argument already set!",
|
||||
toString()) );
|
||||
}
|
||||
|
||||
if ( Arg::delimiter() != ' ' && value == "" )
|
||||
throw( ArgParseException(
|
||||
"Couldn't find delimiter for this argument!",
|
||||
toString() ) );
|
||||
|
||||
if ( value == "" )
|
||||
{
|
||||
(*i)++;
|
||||
if ( static_cast<unsigned int>(*i) < args.size() )
|
||||
_extractValue( args[*i] );
|
||||
else
|
||||
throw( ArgParseException("Missing a value for this argument!",
|
||||
toString() ) );
|
||||
}
|
||||
else
|
||||
_extractValue( value );
|
||||
|
||||
_alreadySet = true;
|
||||
_checkWithVisitor();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of shortID.
|
||||
*/
|
||||
template<class T>
|
||||
std::string ValueArg<T>::shortID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::shortID( _typeDesc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of longID.
|
||||
*/
|
||||
template<class T>
|
||||
std::string ValueArg<T>::longID(const std::string& val) const
|
||||
{
|
||||
static_cast<void>(val); // Ignore input, don't warn
|
||||
return Arg::longID( _typeDesc );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ValueArg<T>::_extractValue( const std::string& val )
|
||||
{
|
||||
try {
|
||||
ExtractValue(_value, val, typename ArgTraits<T>::ValueCategory());
|
||||
} catch( ArgParseException &e) {
|
||||
throw ArgParseException(e.error(), toString());
|
||||
}
|
||||
|
||||
if ( _constraint != NULL )
|
||||
if ( ! _constraint->check( _value ) )
|
||||
throw( CmdLineParseException( "Value '" + val +
|
||||
+ "' does not meet constraint: "
|
||||
+ _constraint->description(),
|
||||
toString() ) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ValueArg<T>::reset()
|
||||
{
|
||||
Arg::reset();
|
||||
_value = _default;
|
||||
}
|
||||
|
||||
} // namespace TCLAP
|
||||
|
||||
#endif
|
148
tclap/ValuesConstraint.h
Normal file
148
tclap/ValuesConstraint.h
Normal file
@ -0,0 +1,148 @@
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ValuesConstraint.h
|
||||
*
|
||||
* Copyright (c) 2005, Michael E. Smoot
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_VALUESCONSTRAINT_H
|
||||
#define TCLAP_VALUESCONSTRAINT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <tclap/Constraint.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#else
|
||||
#define HAVE_SSTREAM
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SSTREAM)
|
||||
#include <sstream>
|
||||
#elif defined(HAVE_STRSTREAM)
|
||||
#include <strstream>
|
||||
#else
|
||||
#error "Need a stringstream (sstream or strstream) to compile!"
|
||||
#endif
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Constraint that constrains the Arg to only those values specified
|
||||
* in the constraint.
|
||||
*/
|
||||
template<class T>
|
||||
class ValuesConstraint : public Constraint<T>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param allowed - vector of allowed values.
|
||||
*/
|
||||
ValuesConstraint(std::vector<T>& allowed);
|
||||
|
||||
/**
|
||||
* Virtual destructor.
|
||||
*/
|
||||
virtual ~ValuesConstraint() {}
|
||||
|
||||
/**
|
||||
* Returns a description of the Constraint.
|
||||
*/
|
||||
virtual std::string description() const;
|
||||
|
||||
/**
|
||||
* Returns the short ID for the Constraint.
|
||||
*/
|
||||
virtual std::string shortID() const;
|
||||
|
||||
/**
|
||||
* The method used to verify that the value parsed from the command
|
||||
* line meets the constraint.
|
||||
* \param value - The value that will be checked.
|
||||
*/
|
||||
virtual bool check(const T& value) const;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of valid values.
|
||||
*/
|
||||
std::vector<T> _allowed;
|
||||
|
||||
/**
|
||||
* The string used to describe the allowed values of this constraint.
|
||||
*/
|
||||
std::string _typeDesc;
|
||||
|
||||
};
|
||||
|
||||
template<class T>
|
||||
ValuesConstraint<T>::ValuesConstraint(std::vector<T>& allowed)
|
||||
: _allowed(allowed),
|
||||
_typeDesc("")
|
||||
{
|
||||
for ( unsigned int i = 0; i < _allowed.size(); i++ )
|
||||
{
|
||||
|
||||
#if defined(HAVE_SSTREAM)
|
||||
std::ostringstream os;
|
||||
#elif defined(HAVE_STRSTREAM)
|
||||
std::ostrstream os;
|
||||
#else
|
||||
#error "Need a stringstream (sstream or strstream) to compile!"
|
||||
#endif
|
||||
|
||||
os << _allowed[i];
|
||||
|
||||
std::string temp( os.str() );
|
||||
|
||||
if ( i > 0 )
|
||||
_typeDesc += "|";
|
||||
_typeDesc += temp;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool ValuesConstraint<T>::check( const T& val ) const
|
||||
{
|
||||
if ( std::find(_allowed.begin(),_allowed.end(),val) == _allowed.end() )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string ValuesConstraint<T>::shortID() const
|
||||
{
|
||||
return _typeDesc;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string ValuesConstraint<T>::description() const
|
||||
{
|
||||
return _typeDesc;
|
||||
}
|
||||
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
||||
|
81
tclap/VersionVisitor.h
Normal file
81
tclap/VersionVisitor.h
Normal file
@ -0,0 +1,81 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: VersionVisitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_VERSION_VISITOR_H
|
||||
#define TCLAP_VERSION_VISITOR_H
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/Visitor.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A Vistor that will call the version method of the given CmdLineOutput
|
||||
* for the specified CmdLine object and then exit.
|
||||
*/
|
||||
class VersionVisitor: public Visitor
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Prevent accidental copying
|
||||
*/
|
||||
VersionVisitor(const VersionVisitor& rhs);
|
||||
VersionVisitor& operator=(const VersionVisitor& rhs);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The CmdLine of interest.
|
||||
*/
|
||||
CmdLineInterface* _cmd;
|
||||
|
||||
/**
|
||||
* The output object.
|
||||
*/
|
||||
CmdLineOutput** _out;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param cmd - The CmdLine the output is generated for.
|
||||
* \param out - The type of output.
|
||||
*/
|
||||
VersionVisitor( CmdLineInterface* cmd, CmdLineOutput** out )
|
||||
: Visitor(), _cmd( cmd ), _out( out ) { }
|
||||
|
||||
/**
|
||||
* Calls the version method of the output object using the
|
||||
* specified CmdLine.
|
||||
*/
|
||||
void visit() {
|
||||
(*_out)->version(*_cmd);
|
||||
throw ExitException(0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
53
tclap/Visitor.h
Normal file
53
tclap/Visitor.h
Normal file
@ -0,0 +1,53 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: Visitor.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef TCLAP_VISITOR_H
|
||||
#define TCLAP_VISITOR_H
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A base class that defines the interface for visitors.
|
||||
*/
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor. Does nothing.
|
||||
*/
|
||||
Visitor() { }
|
||||
|
||||
/**
|
||||
* Destructor. Does nothing.
|
||||
*/
|
||||
virtual ~Visitor() { }
|
||||
|
||||
/**
|
||||
* Does nothing. Should be overridden by child.
|
||||
*/
|
||||
virtual void visit() { }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
166
tclap/XorHandler.h
Normal file
166
tclap/XorHandler.h
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: XorHandler.h
|
||||
*
|
||||
* Copyright (c) 2003, Michael E. Smoot .
|
||||
* Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_XORHANDLER_H
|
||||
#define TCLAP_XORHANDLER_H
|
||||
|
||||
#include <tclap/Arg.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* This class handles lists of Arg's that are to be XOR'd on the command
|
||||
* line. This is used by CmdLine and you shouldn't ever use it.
|
||||
*/
|
||||
class XorHandler
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The list of of lists of Arg's to be or'd together.
|
||||
*/
|
||||
std::vector< std::vector<Arg*> > _orList;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor. Does nothing.
|
||||
*/
|
||||
XorHandler( ) : _orList(std::vector< std::vector<Arg*> >()) {}
|
||||
|
||||
/**
|
||||
* Add a list of Arg*'s that will be orred together.
|
||||
* \param ors - list of Arg* that will be xor'd.
|
||||
*/
|
||||
void add( std::vector<Arg*>& ors );
|
||||
|
||||
/**
|
||||
* Checks whether the specified Arg is in one of the xor lists and
|
||||
* if it does match one, returns the size of the xor list that the
|
||||
* Arg matched. If the Arg matches, then it also sets the rest of
|
||||
* the Arg's in the list. You shouldn't use this.
|
||||
* \param a - The Arg to be checked.
|
||||
*/
|
||||
int check( const Arg* a );
|
||||
|
||||
/**
|
||||
* Returns the XOR specific short usage.
|
||||
*/
|
||||
std::string shortUsage();
|
||||
|
||||
/**
|
||||
* Prints the XOR specific long usage.
|
||||
* \param os - Stream to print to.
|
||||
*/
|
||||
void printLongUsage(std::ostream& os);
|
||||
|
||||
/**
|
||||
* Simply checks whether the Arg is contained in one of the arg
|
||||
* lists.
|
||||
* \param a - The Arg to be checked.
|
||||
*/
|
||||
bool contains( const Arg* a );
|
||||
|
||||
std::vector< std::vector<Arg*> >& getXorList();
|
||||
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//BEGIN XOR.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
inline void XorHandler::add( std::vector<Arg*>& ors )
|
||||
{
|
||||
_orList.push_back( ors );
|
||||
}
|
||||
|
||||
inline int XorHandler::check( const Arg* a )
|
||||
{
|
||||
// iterate over each XOR list
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < _orList.size(); i++ )
|
||||
{
|
||||
// if the XOR list contains the arg..
|
||||
ArgVectorIterator ait = std::find( _orList[i].begin(),
|
||||
_orList[i].end(), a );
|
||||
if ( ait != _orList[i].end() )
|
||||
{
|
||||
// first check to see if a mutually exclusive switch
|
||||
// has not already been set
|
||||
for ( ArgVectorIterator it = _orList[i].begin();
|
||||
it != _orList[i].end();
|
||||
it++ )
|
||||
if ( a != (*it) && (*it)->isSet() )
|
||||
throw(CmdLineParseException(
|
||||
"Mutually exclusive argument already set!",
|
||||
(*it)->toString()));
|
||||
|
||||
// go through and set each arg that is not a
|
||||
for ( ArgVectorIterator it = _orList[i].begin();
|
||||
it != _orList[i].end();
|
||||
it++ )
|
||||
if ( a != (*it) )
|
||||
(*it)->xorSet();
|
||||
|
||||
// return the number of required args that have now been set
|
||||
if ( (*ait)->allowMore() )
|
||||
return 0;
|
||||
else
|
||||
return static_cast<int>(_orList[i].size());
|
||||
}
|
||||
}
|
||||
|
||||
if ( a->isRequired() )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool XorHandler::contains( const Arg* a )
|
||||
{
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < _orList.size(); i++ )
|
||||
for ( ArgVectorIterator it = _orList[i].begin();
|
||||
it != _orList[i].end();
|
||||
it++ )
|
||||
if ( a == (*it) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::vector< std::vector<Arg*> >& XorHandler::getXorList()
|
||||
{
|
||||
return _orList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//END XOR.cpp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace TCLAP
|
||||
|
||||
#endif
|
323
tclap/ZshCompletionOutput.h
Normal file
323
tclap/ZshCompletionOutput.h
Normal file
@ -0,0 +1,323 @@
|
||||
// -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* file: ZshCompletionOutput.h
|
||||
*
|
||||
* Copyright (c) 2006, Oliver Kiddle
|
||||
* All rights reverved.
|
||||
*
|
||||
* See the file COPYING in the top directory of this distribution for
|
||||
* more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TCLAP_ZSHCOMPLETIONOUTPUT_H
|
||||
#define TCLAP_ZSHCOMPLETIONOUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <tclap/CmdLineInterface.h>
|
||||
#include <tclap/CmdLineOutput.h>
|
||||
#include <tclap/XorHandler.h>
|
||||
#include <tclap/Arg.h>
|
||||
|
||||
namespace TCLAP {
|
||||
|
||||
/**
|
||||
* A class that generates a Zsh completion function as output from the usage()
|
||||
* method for the given CmdLine and its Args.
|
||||
*/
|
||||
class ZshCompletionOutput : public CmdLineOutput
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ZshCompletionOutput();
|
||||
|
||||
/**
|
||||
* Prints the usage to stdout. Can be overridden to
|
||||
* produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void usage(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints the version to stdout. Can be overridden
|
||||
* to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
*/
|
||||
virtual void version(CmdLineInterface& c);
|
||||
|
||||
/**
|
||||
* Prints (to stderr) an error message, short usage
|
||||
* Can be overridden to produce alternative behavior.
|
||||
* \param c - The CmdLine object the output is generated for.
|
||||
* \param e - The ArgException that caused the failure.
|
||||
*/
|
||||
virtual void failure(CmdLineInterface& c,
|
||||
ArgException& e );
|
||||
|
||||
protected:
|
||||
|
||||
void basename( std::string& s );
|
||||
void quoteSpecialChars( std::string& s );
|
||||
|
||||
std::string getMutexList( CmdLineInterface& _cmd, Arg* a );
|
||||
void printOption( Arg* it, std::string mutex );
|
||||
void printArg( Arg* it );
|
||||
|
||||
std::map<std::string, std::string> common;
|
||||
char theDelimiter;
|
||||
};
|
||||
|
||||
ZshCompletionOutput::ZshCompletionOutput()
|
||||
: common(std::map<std::string, std::string>()),
|
||||
theDelimiter('=')
|
||||
{
|
||||
common["host"] = "_hosts";
|
||||
common["hostname"] = "_hosts";
|
||||
common["file"] = "_files";
|
||||
common["filename"] = "_files";
|
||||
common["user"] = "_users";
|
||||
common["username"] = "_users";
|
||||
common["directory"] = "_directories";
|
||||
common["path"] = "_directories";
|
||||
common["url"] = "_urls";
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::version(CmdLineInterface& _cmd)
|
||||
{
|
||||
std::cout << _cmd.getVersion() << std::endl;
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::usage(CmdLineInterface& _cmd )
|
||||
{
|
||||
std::list<Arg*> argList = _cmd.getArgList();
|
||||
std::string progName = _cmd.getProgramName();
|
||||
std::string xversion = _cmd.getVersion();
|
||||
theDelimiter = _cmd.getDelimiter();
|
||||
basename(progName);
|
||||
|
||||
std::cout << "#compdef " << progName << std::endl << std::endl <<
|
||||
"# " << progName << " version " << _cmd.getVersion() << std::endl << std::endl <<
|
||||
"_arguments -s -S";
|
||||
|
||||
for (ArgListIterator it = argList.begin(); it != argList.end(); it++)
|
||||
{
|
||||
if ( (*it)->shortID().at(0) == '<' )
|
||||
printArg((*it));
|
||||
else if ( (*it)->getFlag() != "-" )
|
||||
printOption((*it), getMutexList(_cmd, *it));
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::failure( CmdLineInterface& _cmd,
|
||||
ArgException& e )
|
||||
{
|
||||
static_cast<void>(_cmd); // unused
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::quoteSpecialChars( std::string& s )
|
||||
{
|
||||
size_t idx = s.find_last_of(':');
|
||||
while ( idx != std::string::npos )
|
||||
{
|
||||
s.insert(idx, 1, '\\');
|
||||
idx = s.find_last_of(':', idx);
|
||||
}
|
||||
idx = s.find_last_of('\'');
|
||||
while ( idx != std::string::npos )
|
||||
{
|
||||
s.insert(idx, "'\\'");
|
||||
if (idx == 0)
|
||||
idx = std::string::npos;
|
||||
else
|
||||
idx = s.find_last_of('\'', --idx);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::basename( std::string& s )
|
||||
{
|
||||
size_t p = s.find_last_of('/');
|
||||
if ( p != std::string::npos )
|
||||
{
|
||||
s.erase(0, p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::printArg(Arg* a)
|
||||
{
|
||||
static int count = 1;
|
||||
|
||||
std::cout << " \\" << std::endl << " '";
|
||||
if ( a->acceptsMultipleValues() )
|
||||
std::cout << '*';
|
||||
else
|
||||
std::cout << count++;
|
||||
std::cout << ':';
|
||||
if ( !a->isRequired() )
|
||||
std::cout << ':';
|
||||
|
||||
std::cout << a->getName() << ':';
|
||||
std::map<std::string, std::string>::iterator compArg = common.find(a->getName());
|
||||
if ( compArg != common.end() )
|
||||
{
|
||||
std::cout << compArg->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "_guard \"^-*\" " << a->getName();
|
||||
}
|
||||
std::cout << '\'';
|
||||
}
|
||||
|
||||
inline void ZshCompletionOutput::printOption(Arg* a, std::string mutex)
|
||||
{
|
||||
std::string flag = a->flagStartChar() + a->getFlag();
|
||||
std::string name = a->nameStartString() + a->getName();
|
||||
std::string desc = a->getDescription();
|
||||
|
||||
// remove full stop and capitalisation from description as
|
||||
// this is the convention for zsh function
|
||||
if (!desc.compare(0, 12, "(required) "))
|
||||
{
|
||||
desc.erase(0, 12);
|
||||
}
|
||||
if (!desc.compare(0, 15, "(OR required) "))
|
||||
{
|
||||
desc.erase(0, 15);
|
||||
}
|
||||
size_t len = desc.length();
|
||||
if (len && desc.at(--len) == '.')
|
||||
{
|
||||
desc.erase(len);
|
||||
}
|
||||
if (len)
|
||||
{
|
||||
desc.replace(0, 1, 1, tolower(desc.at(0)));
|
||||
}
|
||||
|
||||
std::cout << " \\" << std::endl << " '" << mutex;
|
||||
|
||||
if ( a->getFlag().empty() )
|
||||
{
|
||||
std::cout << name;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "'{" << flag << ',' << name << "}'";
|
||||
}
|
||||
if ( theDelimiter == '=' && a->isValueRequired() )
|
||||
std::cout << "=-";
|
||||
quoteSpecialChars(desc);
|
||||
std::cout << '[' << desc << ']';
|
||||
|
||||
if ( a->isValueRequired() )
|
||||
{
|
||||
std::string arg = a->shortID();
|
||||
arg.erase(0, arg.find_last_of(theDelimiter) + 1);
|
||||
if ( arg.at(arg.length()-1) == ']' )
|
||||
arg.erase(arg.length()-1);
|
||||
if ( arg.at(arg.length()-1) == ']' )
|
||||
{
|
||||
arg.erase(arg.length()-1);
|
||||
}
|
||||
if ( arg.at(0) == '<' )
|
||||
{
|
||||
arg.erase(arg.length()-1);
|
||||
arg.erase(0, 1);
|
||||
}
|
||||
size_t p = arg.find('|');
|
||||
if ( p != std::string::npos )
|
||||
{
|
||||
do
|
||||
{
|
||||
arg.replace(p, 1, 1, ' ');
|
||||
}
|
||||
while ( (p = arg.find_first_of('|', p)) != std::string::npos );
|
||||
quoteSpecialChars(arg);
|
||||
std::cout << ": :(" << arg << ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << ':' << arg;
|
||||
std::map<std::string, std::string>::iterator compArg = common.find(arg);
|
||||
if ( compArg != common.end() )
|
||||
{
|
||||
std::cout << ':' << compArg->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << '\'';
|
||||
}
|
||||
|
||||
inline std::string ZshCompletionOutput::getMutexList( CmdLineInterface& _cmd, Arg* a)
|
||||
{
|
||||
XorHandler xorHandler = _cmd.getXorHandler();
|
||||
std::vector< std::vector<Arg*> > xorList = xorHandler.getXorList();
|
||||
|
||||
if (a->getName() == "help" || a->getName() == "version")
|
||||
{
|
||||
return "(-)";
|
||||
}
|
||||
|
||||
std::ostringstream list;
|
||||
if ( a->acceptsMultipleValues() )
|
||||
{
|
||||
list << '*';
|
||||
}
|
||||
|
||||
for ( int i = 0; static_cast<unsigned int>(i) < xorList.size(); i++ )
|
||||
{
|
||||
for ( ArgVectorIterator it = xorList[i].begin();
|
||||
it != xorList[i].end();
|
||||
it++)
|
||||
if ( a == (*it) )
|
||||
{
|
||||
list << '(';
|
||||
for ( ArgVectorIterator iu = xorList[i].begin();
|
||||
iu != xorList[i].end();
|
||||
iu++ )
|
||||
{
|
||||
bool notCur = (*iu) != a;
|
||||
bool hasFlag = !(*iu)->getFlag().empty();
|
||||
if ( iu != xorList[i].begin() && (notCur || hasFlag) )
|
||||
list << ' ';
|
||||
if (hasFlag)
|
||||
list << (*iu)->flagStartChar() << (*iu)->getFlag() << ' ';
|
||||
if ( notCur || hasFlag )
|
||||
list << (*iu)->nameStartString() << (*iu)->getName();
|
||||
}
|
||||
list << ')';
|
||||
return list.str();
|
||||
}
|
||||
}
|
||||
|
||||
// wasn't found in xor list
|
||||
if (!a->getFlag().empty()) {
|
||||
list << "(" << a->flagStartChar() << a->getFlag() << ' ' <<
|
||||
a->nameStartString() << a->getName() << ')';
|
||||
}
|
||||
|
||||
return list.str();
|
||||
}
|
||||
|
||||
} //namespace TCLAP
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user