Initial commit

This commit is contained in:
Ivan Grokhotkov 2015-05-13 19:17:41 +03:00
commit 18bb3c49be
37 changed files with 12179 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
make_spiffs
make_spiffs.exe

70
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

718
spiffs/spiffs_nucleus.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 = "&lt;";
std::string gt = "&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 = "&lt;";
std::string gt = "&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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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