├── src ├── util.h ├── hook.h ├── format.h ├── tracer.h ├── options.h ├── util.c ├── shelltrace.c ├── hook.c ├── options.c ├── tracer.c ├── format.c ├── bstrlib.h └── bstrlib.c ├── .gitignore ├── CMakeLists.txt ├── .ycm_extra_conf.py └── LICENSE /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef ST_UTIL_H 2 | #define ST_UTIL_H 3 | 4 | #define MIN(a, b) ((a) < (b)? (a) : (b)) 5 | #define MAX(a, b) ((a) > (b)? (a) : (b)) 6 | 7 | int readfile(char* filename, char** buf); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/hook.h: -------------------------------------------------------------------------------- 1 | #ifndef ST_HOOK_H 2 | #define ST_HOOK_H 3 | 4 | #include 5 | #include "tracer.h" 6 | 7 | void st_hook_code(uc_engine *uc, uint64_t address, uint32_t size, 8 | struct st_tracer *tracer); 9 | 10 | void st_hook_sys(uc_engine *uc, uint32_t intno, struct st_tracer *tracer); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | #ifndef ST_FORMATTER_H 2 | #define ST_FORMATTER_H 3 | 4 | #include 5 | #include "tracer.h" 6 | #include "bstrlib.h" 7 | 8 | struct st_syscall_args { 9 | uint64_t syscall; 10 | uint64_t args[5]; 11 | }; 12 | 13 | bstring st_format_syscall(struct st_syscall_args *syscall_args, 14 | struct st_tracer *tracer); 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | build 35 | *.pyc 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(CMAKEDEMO) 3 | 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-function") 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") 8 | 9 | add_executable(shelltrace src/shelltrace.c src/options.c src/tracer.c src/util.c src/hook.c src/format.c src/bstrlib.c) 10 | target_link_libraries(shelltrace argp unicorn capstone) 11 | 12 | -------------------------------------------------------------------------------- /src/tracer.h: -------------------------------------------------------------------------------- 1 | #ifndef ST_TRACER_H 2 | #define ST_TRACER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "options.h" 8 | 9 | typedef struct st_tracer { 10 | struct st_options* options; 11 | uc_engine* uc; 12 | int last_uc_err; 13 | uc_hook hooks[2]; 14 | csh cs; 15 | } st_tracer_t; 16 | 17 | int st_tracer_init(struct st_tracer** tracer, struct st_options* options); 18 | 19 | int st_tracer_run(struct st_tracer* tracer); 20 | 21 | void st_tracer_destroy(struct st_tracer* tracer); 22 | 23 | #endif // ST_TRACER_H 24 | -------------------------------------------------------------------------------- /src/options.h: -------------------------------------------------------------------------------- 1 | #ifndef ST_ARGP_H 2 | #define ST_ARGP_H 3 | 4 | #include 5 | #include 6 | 7 | #include "stdint.h" 8 | 9 | typedef struct st_options { 10 | int trace_code; 11 | int trace_syscall; 12 | 13 | char* shellcode; 14 | int shellcode_size; 15 | 16 | uintptr_t shellcode_addr; 17 | uc_arch arch; 18 | uc_mode mode; 19 | 20 | uintptr_t stack_addr; 21 | size_t stack_size; 22 | } st_options_t; 23 | 24 | void st_options_init(struct st_options** options); 25 | 26 | int st_options_parse( 27 | struct st_options* options, 28 | int argc, 29 | char** argv); 30 | 31 | void st_options_destroy(struct st_options* options); 32 | 33 | #endif // ST_ARGP_H 34 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | 8 | int readfile(char* filename, char** buf) { 9 | int fd = open(filename, O_RDONLY); 10 | if(fd < 0) { 11 | return -1; 12 | } 13 | 14 | *buf = 0; 15 | int res = 0; 16 | 17 | struct stat sb; 18 | if(fstat(fd, &sb)) { 19 | res = -1; 20 | goto CLEANUP; 21 | } 22 | 23 | *buf = malloc(sb.st_size); 24 | 25 | int nread = 0; 26 | int n = 0; 27 | 28 | do { 29 | n = read(fd, *buf, sb.st_size); 30 | if(n < 0) { 31 | res = -1; 32 | goto CLEANUP; 33 | } 34 | nread += n; 35 | } while(n != 0 && nread < sb.st_size); 36 | 37 | res = nread; 38 | 39 | CLEANUP: 40 | close(fd); 41 | return res; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/shelltrace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "options.h" 5 | #include "tracer.h" 6 | 7 | int main(int argc, char* argv[]) { 8 | struct st_options* options; 9 | struct st_tracer* tracer; 10 | 11 | st_options_init(&options); 12 | st_options_parse(options, argc, argv); 13 | 14 | if(st_tracer_init(&tracer, options)) { 15 | if (tracer) { 16 | fprintf(stderr, "Error: Initialization failed: %d\n", tracer->last_uc_err); 17 | } else { 18 | fprintf(stderr, "Error: Cannot allocate tracer\n"); 19 | } 20 | } 21 | 22 | if(st_tracer_run(tracer)) { 23 | fprintf(stderr, "Error: Running shellcore failed: %d\n", tracer->last_uc_err); 24 | } 25 | 26 | st_tracer_destroy(tracer); 27 | st_options_destroy(options); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/hook.c: -------------------------------------------------------------------------------- 1 | #include "hook.h" 2 | #include "util.h" 3 | #include "format.h" 4 | 5 | #define CODE_BUF 16 6 | 7 | void st_hook_code(uc_engine *uc, uint64_t address, uint32_t size, 8 | struct st_tracer *tracer) { 9 | uint8_t code_buf[CODE_BUF]; 10 | int code_size = MIN(size, CODE_BUF); 11 | uc_mem_read(uc, address, code_buf, code_size); 12 | 13 | cs_insn *insn; 14 | int insn_count = cs_disasm(tracer->cs, code_buf, code_size, address, 1, &insn); 15 | if(insn_count) { 16 | printf("0x%llx: %s %s\n", insn->address, insn->mnemonic, insn->op_str); 17 | } else { 18 | printf("0x%llx: \n", address); 19 | } 20 | } 21 | 22 | void st_hook_sys(uc_engine *uc, uint32_t intno, struct st_tracer *tracer) { 23 | if(intno != 0x80) { 24 | return; 25 | } 26 | 27 | struct st_syscall_args syscall_args; 28 | memset(&syscall_args, 0, sizeof(syscall_args)); 29 | 30 | uc_reg_read(uc, UC_X86_REG_EAX, &syscall_args.syscall); 31 | 32 | uc_reg_read(uc, UC_X86_REG_EBX, &syscall_args.args[0]); 33 | uc_reg_read(uc, UC_X86_REG_ECX, &syscall_args.args[1]); 34 | uc_reg_read(uc, UC_X86_REG_EDX, &syscall_args.args[2]); 35 | uc_reg_read(uc, UC_X86_REG_ESI, &syscall_args.args[3]); 36 | uc_reg_read(uc, UC_X86_REG_EDI, &syscall_args.args[4]); 37 | 38 | switch(syscall_args.syscall) { 39 | case 0x1: 40 | uc_emu_stop(uc); 41 | break; 42 | case 0xb: 43 | uc_emu_stop(uc); 44 | break; 45 | } 46 | 47 | bstring formatted_syscall = st_format_syscall(&syscall_args, tracer); 48 | printf("syscall: %s\n", bdata(formatted_syscall)); 49 | bdestroy(formatted_syscall); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/options.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "options.h" 6 | #include "util.h" 7 | 8 | #define BUF_SIZE 1024 9 | 10 | const char *argp_program_version = 11 | "shelltrace 0.1"; 12 | const char *argp_program_bug_address = 13 | "isra17 "; 14 | 15 | static char doc[] = "Shelltrace -- Utility to trace shellcode execution"; 16 | 17 | static char args_doc[] = ""; 18 | 19 | static struct argp_option options[] = { 20 | {"trace-code", 'c', 0, 0, "Trace instructions"}, 21 | {"trace-sys", 's', 0, 0, "Trace syscalls"}, 22 | {"shellcode", 'f', "FILE", 0, "File with shellcode"}, 23 | {0} 24 | }; 25 | 26 | static error_t parse_opt (int key, char *arg, struct argp_state *state) 27 | { 28 | struct st_options* options = state->input; 29 | switch(key){ 30 | case 'c': 31 | options->trace_code = 1; 32 | break; 33 | case 's': 34 | options->trace_syscall = 1; 35 | break; 36 | case 'f': 37 | { 38 | options->shellcode_size = readfile( 39 | arg, 40 | &options->shellcode); 41 | 42 | if(options->shellcode_size < 0) { 43 | perror("read_file_content(shellcode)"); 44 | } 45 | } 46 | break; 47 | } 48 | return 0; 49 | } 50 | 51 | static struct argp st_argp = {options, parse_opt, args_doc, doc}; 52 | 53 | void st_options_init(struct st_options** poptions) { 54 | struct st_options* options; 55 | options = malloc(sizeof(struct st_options)); 56 | options->trace_code = 0; 57 | options->trace_syscall = 1; 58 | 59 | options->shellcode_size = 0; 60 | options->shellcode = 0; 61 | options->shellcode_addr = 0x1000000; 62 | 63 | options->arch = UC_ARCH_X86; 64 | options->mode = UC_MODE_32; 65 | 66 | options->stack_addr = 0x60000000; 67 | options->stack_size = 0x100000; 68 | 69 | *poptions = options; 70 | } 71 | 72 | int st_options_parse( 73 | struct st_options* options, 74 | int argc, 75 | char** argv) 76 | { 77 | return argp_parse(&st_argp, argc, argv, 0, 0, options); 78 | } 79 | 80 | void st_options_destroy(struct st_options* options) { 81 | if(options->shellcode) { 82 | free(options->shellcode); 83 | } 84 | 85 | free(options); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/tracer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tracer.h" 5 | #include "hook.h" 6 | 7 | #define ALIGN_ADDR(addr) ((addr) & ~0xFFF) 8 | #define ALIGN_SIZE(addr, size) ((((addr) & 0xFFF) + (size) + 0xFFF) & ~0xFFF) 9 | 10 | int st_tracer_init(struct st_tracer** ptracer, struct st_options* options) { 11 | struct st_tracer* tracer = malloc(sizeof(struct st_tracer)); 12 | *ptracer = tracer; 13 | 14 | if(!tracer) { 15 | return -1; 16 | } 17 | 18 | tracer->options = options; 19 | for(size_t i=0; i < 2; i++) { 20 | tracer->hooks[i] = 0; 21 | } 22 | 23 | // Create uc_engine 24 | tracer->last_uc_err = uc_open(options->arch, options->mode, &tracer->uc); 25 | if(tracer->last_uc_err) return -1; 26 | 27 | // Allocate stack 28 | int stack_mem_start = tracer->options->stack_addr - tracer->options->stack_size; 29 | tracer->last_uc_err = uc_mem_map( 30 | tracer->uc, 31 | ALIGN_ADDR(stack_mem_start), 32 | ALIGN_SIZE(stack_mem_start, tracer->options->stack_size + 0x100), 33 | UC_PROT_READ | UC_PROT_WRITE); 34 | if(tracer->last_uc_err) return -1; 35 | 36 | uc_reg_write(tracer->uc, UC_X86_REG_ESP, &tracer->options->stack_addr); 37 | 38 | // Allocate shellcode 39 | tracer->last_uc_err = uc_mem_map( 40 | tracer->uc, 41 | ALIGN_ADDR(tracer->options->shellcode_addr), 42 | ALIGN_SIZE(tracer->options->shellcode_addr, tracer->options->shellcode_size), 43 | UC_PROT_ALL); 44 | if(tracer->last_uc_err) return -1; 45 | 46 | tracer->last_uc_err = uc_mem_write( 47 | tracer->uc, 48 | tracer->options->shellcode_addr, 49 | tracer->options->shellcode, 50 | tracer->options->shellcode_size); 51 | if(tracer->last_uc_err) return -1; 52 | 53 | // Add hooks 54 | if(tracer->options->trace_code) { 55 | uc_hook_add( 56 | tracer->uc, 57 | &tracer->hooks[0], 58 | UC_HOOK_CODE, 59 | st_hook_code, 60 | tracer, 61 | 1, 0); 62 | } 63 | 64 | if(tracer->options->trace_syscall) { 65 | uc_hook_add( 66 | tracer->uc, 67 | &tracer->hooks[1], 68 | UC_HOOK_INTR, 69 | st_hook_sys, 70 | tracer, 71 | 1, 0); 72 | } 73 | 74 | // Init capstone 75 | // TODO: Translate uc arch to cs 76 | if (cs_open(CS_ARCH_X86, CS_MODE_32, &tracer->cs) != CS_ERR_OK) { 77 | // TODO: expose the error somewhere in `tracer` 78 | return -1; 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | int st_tracer_run(struct st_tracer* tracer) { 85 | tracer->last_uc_err = uc_emu_start( 86 | tracer->uc, 87 | tracer->options->shellcode_addr, 88 | tracer->options->shellcode_addr + tracer->options->shellcode_size + 1, 89 | 0, 0); 90 | if(tracer->last_uc_err) return -1; 91 | 92 | return 0; 93 | } 94 | 95 | void st_tracer_destroy(struct st_tracer* tracer) { 96 | cs_close(&tracer->cs); 97 | uc_close(tracer->uc); 98 | free(tracer); 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/format.c: -------------------------------------------------------------------------------- 1 | #include "format.h" 2 | 3 | static bstring strfmt(uint64_t arg, struct st_tracer *tracer); 4 | static bstring astrfmt(uint64_t arg, struct st_tracer *tracer); 5 | static bstring intfmt(uint64_t arg, struct st_tracer *tracer); 6 | 7 | typedef bstring (*arg_formatter)(uint64_t, struct st_tracer *); 8 | 9 | struct syscall_format { 10 | int syscall; 11 | char *name; 12 | arg_formatter arg_formatters[6]; 13 | }; 14 | 15 | struct syscall_format syscall_formats[] = { 16 | {1, "exit", {intfmt, 0}}, 17 | {2, "fork", {intfmt, 0}}, 18 | {3, "read", {intfmt, 0}}, 19 | {4, "write", {intfmt, 0}}, 20 | {5, "open", {intfmt, 0}}, 21 | {6, "close", {intfmt, 0}}, 22 | {7, "waitpid", {intfmt, 0}}, 23 | {8, "creat", {intfmt, 0}}, 24 | {9, "link", {intfmt, 0}}, 25 | {10, "unlink", {intfmt, 0}}, 26 | {11, "execve", {strfmt, astrfmt, astrfmt, 0}}, 27 | {63, "dup2", {intfmt, intfmt, 0}}, 28 | {102, "socketcall", {intfmt, intfmt, 0}}, 29 | }; 30 | 31 | struct syscall_format default_format = { 32 | -1, "", {intfmt, intfmt, intfmt, intfmt, intfmt, 0}}; 33 | 34 | static struct syscall_format* find_syscall_formatter(int syscall) { 35 | int imin=0; 36 | int imax = sizeof(syscall_formats)/sizeof(syscall_formats[0]); 37 | 38 | while(imin <= imax) { 39 | int imid = (imin+imax)/2; 40 | if(syscall_formats[imid].syscall == syscall) { 41 | return &syscall_formats[imid]; 42 | } else if(syscall_formats[imid].syscall < syscall) { 43 | imin = imid + 1; 44 | } else { 45 | imax = imid - 1; 46 | } 47 | } 48 | 49 | return &default_format; 50 | } 51 | 52 | bstring st_format_syscall(struct st_syscall_args *syscall_args, 53 | struct st_tracer *tracer) { 54 | bstring formatted_syscall; 55 | struct syscall_format *formatter = 56 | find_syscall_formatter(syscall_args->syscall); 57 | 58 | formatted_syscall = bformat(formatter->name, syscall_args->syscall); 59 | 60 | bconchar(formatted_syscall, '('); 61 | arg_formatter* arg_formatter_it = formatter->arg_formatters; 62 | uint64_t *arg_it = syscall_args->args; 63 | while (*arg_formatter_it) { 64 | bstring arg_str = (*arg_formatter_it)(*arg_it, tracer); 65 | bconcat(formatted_syscall, arg_str); 66 | bdestroy(arg_str); 67 | 68 | arg_formatter_it++; 69 | arg_it++; 70 | if (*arg_formatter_it) { 71 | bcatcstr(formatted_syscall, ", "); 72 | } 73 | } 74 | bconchar(formatted_syscall, ')'); 75 | 76 | return formatted_syscall; 77 | } 78 | 79 | bstring strfmt(uint64_t arg, struct st_tracer *tracer) { 80 | char mem_buf[256]; 81 | uc_err err = uc_mem_read(tracer->uc, arg, mem_buf, sizeof(mem_buf)); 82 | if(mem_buf[sizeof(mem_buf)-1]) { 83 | memcpy(mem_buf + sizeof(mem_buf) - 4, "...", 4); 84 | } 85 | 86 | if(err) { 87 | return bformat("0x%llx => "); 88 | } else { 89 | return bformat("0x%llx => \"%s\"", arg, mem_buf); 90 | } 91 | } 92 | 93 | bstring astrfmt(uint64_t arg, struct st_tracer *tracer) { 94 | if(!arg) { 95 | return intfmt(arg, tracer); 96 | } 97 | 98 | bstring formatted_array = bformat("0x%llx => [", arg); 99 | uint64_t item = 0; 100 | 101 | // TODO: assumes 32 bits, make it arch/mode independant 102 | uc_mem_read(tracer->uc, arg, &item, 4); 103 | while(item) { 104 | bstring formatted_item = strfmt(item, tracer); 105 | bconcat(formatted_array, formatted_item); 106 | bdestroy(formatted_item); 107 | 108 | arg += 4; 109 | uc_mem_read(tracer->uc, arg, &item, 4); 110 | 111 | if(item) { 112 | bcatcstr(formatted_array, ", "); 113 | } 114 | } 115 | bconchar(formatted_array, ']'); 116 | 117 | return formatted_array; 118 | } 119 | 120 | bstring intfmt(uint64_t arg, struct st_tracer *tracer) { 121 | bstring formatted_arg = bformat("0x%llx", arg); 122 | return formatted_arg; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import ycm_core 3 | 4 | # These are the compilation flags that will be used in case there's no 5 | # compilation database set (by default, one is not set). 6 | # CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. 7 | flags = [ 8 | '-Wall', 9 | '-Wextra', 10 | '-Werror', 11 | '-Wc++98-compat', 12 | '-Wno-long-long', 13 | '-Wno-variadic-macros', 14 | '-Wno-missing-field-initializers', 15 | '-fexceptions', 16 | '-DNDEBUG', 17 | # For a C project, you would set this to something like 'c99' instead of 18 | '-std=c99', 19 | # For a C project, you would set this to 'c' instead of 'c++'. 20 | '-x', 'c', 21 | '-I.', 22 | ] 23 | 24 | 25 | # Set this to the absolute path to the folder (NOT the file!) containing the 26 | # compile_commands.json file to use that instead of 'flags'. See here for 27 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 28 | # 29 | # You can get CMake to generate this file for you by adding: 30 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 31 | # to your CMakeLists.txt file. 32 | # 33 | # Most projects will NOT need to set this to anything; you can just change the 34 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 35 | compilation_database_folder = '' 36 | 37 | if os.path.exists( compilation_database_folder ): 38 | database = ycm_core.CompilationDatabase( compilation_database_folder ) 39 | else: 40 | database = None 41 | 42 | SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] 43 | 44 | def DirectoryOfThisScript(): 45 | return os.path.dirname( os.path.abspath( __file__ ) ) 46 | 47 | 48 | def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): 49 | if not working_directory: 50 | return list( flags ) 51 | new_flags = [] 52 | make_next_absolute = False 53 | path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] 54 | for flag in flags: 55 | new_flag = flag 56 | 57 | if make_next_absolute: 58 | make_next_absolute = False 59 | if not flag.startswith( '/' ): 60 | new_flag = os.path.join( working_directory, flag ) 61 | 62 | for path_flag in path_flags: 63 | if flag == path_flag: 64 | make_next_absolute = True 65 | break 66 | 67 | if flag.startswith( path_flag ): 68 | path = flag[ len( path_flag ): ] 69 | new_flag = path_flag + os.path.join( working_directory, path ) 70 | break 71 | 72 | if new_flag: 73 | new_flags.append( new_flag ) 74 | return new_flags 75 | 76 | 77 | def IsHeaderFile( filename ): 78 | extension = os.path.splitext( filename )[ 1 ] 79 | return extension in [ '.h', '.hxx', '.hpp', '.hh' ] 80 | 81 | 82 | def GetCompilationInfoForFile( filename ): 83 | # The compilation_commands.json file generated by CMake does not have entries 84 | # for header files. So we do our best by asking the db for flags for a 85 | # corresponding source file, if any. If one exists, the flags for that file 86 | # should be good enough. 87 | if IsHeaderFile( filename ): 88 | basename = os.path.splitext( filename )[ 0 ] 89 | for extension in SOURCE_EXTENSIONS: 90 | replacement_file = basename + extension 91 | if os.path.exists( replacement_file ): 92 | compilation_info = database.GetCompilationInfoForFile( 93 | replacement_file ) 94 | if compilation_info.compiler_flags_: 95 | return compilation_info 96 | return None 97 | return database.GetCompilationInfoForFile( filename ) 98 | 99 | 100 | def FlagsForFile( filename, **kwargs ): 101 | if database: 102 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 103 | # python list, but a "list-like" StringVec object 104 | compilation_info = GetCompilationInfoForFile( filename ) 105 | if not compilation_info: 106 | return None 107 | 108 | final_flags = MakeRelativePathsInFlagsAbsolute( 109 | compilation_info.compiler_flags_, 110 | compilation_info.compiler_working_dir_ ) 111 | 112 | # NOTE: This is just for YouCompleteMe; it's highly likely that your project 113 | # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR 114 | # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. 115 | try: 116 | final_flags.remove( '-stdlib=libc++' ) 117 | except ValueError: 118 | pass 119 | else: 120 | relative_to = DirectoryOfThisScript() 121 | final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) 122 | 123 | return { 124 | 'flags': final_flags, 125 | 'do_cache': True 126 | } 127 | -------------------------------------------------------------------------------- /src/bstrlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This source file is part of the bstring string library. This code was 3 | * written by Paul Hsieh in 2002-2015, and is covered by the BSD open source 4 | * license and the GPL. Refer to the accompanying documentation for details 5 | * on usage and license. 6 | */ 7 | 8 | /* 9 | * bstrlib.h 10 | * 11 | * This file is the interface for the core bstring functions. 12 | */ 13 | 14 | #ifndef BSTRLIB_INCLUDE 15 | #define BSTRLIB_INCLUDE 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP) 27 | # if defined (__TURBOC__) && !defined (__BORLANDC__) 28 | # define BSTRLIB_NOVSNP 29 | # endif 30 | #endif 31 | 32 | #define BSTR_ERR (-1) 33 | #define BSTR_OK (0) 34 | #define BSTR_BS_BUFF_LENGTH_GET (0) 35 | 36 | typedef struct tagbstring * bstring; 37 | typedef const struct tagbstring * const_bstring; 38 | 39 | /* Copy functions */ 40 | #define cstr2bstr bfromcstr 41 | extern bstring bfromcstr (const char * str); 42 | extern bstring bfromcstralloc (int mlen, const char * str); 43 | extern bstring bfromcstrrangealloc (int minl, int maxl, const char* str); 44 | extern bstring blk2bstr (const void * blk, int len); 45 | extern char * bstr2cstr (const_bstring s, char z); 46 | extern int bcstrfree (char * s); 47 | extern bstring bstrcpy (const_bstring b1); 48 | extern int bassign (bstring a, const_bstring b); 49 | extern int bassignmidstr (bstring a, const_bstring b, int left, int len); 50 | extern int bassigncstr (bstring a, const char * str); 51 | extern int bassignblk (bstring a, const void * s, int len); 52 | 53 | /* Destroy function */ 54 | extern int bdestroy (bstring b); 55 | 56 | /* Space allocation hinting functions */ 57 | extern int balloc (bstring s, int len); 58 | extern int ballocmin (bstring b, int len); 59 | 60 | /* Substring extraction */ 61 | extern bstring bmidstr (const_bstring b, int left, int len); 62 | 63 | /* Various standard manipulations */ 64 | extern int bconcat (bstring b0, const_bstring b1); 65 | extern int bconchar (bstring b0, char c); 66 | extern int bcatcstr (bstring b, const char * s); 67 | extern int bcatblk (bstring b, const void * s, int len); 68 | extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill); 69 | extern int binsertch (bstring s1, int pos, int len, unsigned char fill); 70 | extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill); 71 | extern int bdelete (bstring s1, int pos, int len); 72 | extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill); 73 | extern int btrunc (bstring b, int n); 74 | 75 | /* Scan/search functions */ 76 | extern int bstricmp (const_bstring b0, const_bstring b1); 77 | extern int bstrnicmp (const_bstring b0, const_bstring b1, int n); 78 | extern int biseqcaseless (const_bstring b0, const_bstring b1); 79 | extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len); 80 | extern int biseq (const_bstring b0, const_bstring b1); 81 | extern int bisstemeqblk (const_bstring b0, const void * blk, int len); 82 | extern int biseqcstr (const_bstring b, const char * s); 83 | extern int biseqcstrcaseless (const_bstring b, const char * s); 84 | extern int bstrcmp (const_bstring b0, const_bstring b1); 85 | extern int bstrncmp (const_bstring b0, const_bstring b1, int n); 86 | extern int binstr (const_bstring s1, int pos, const_bstring s2); 87 | extern int binstrr (const_bstring s1, int pos, const_bstring s2); 88 | extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2); 89 | extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2); 90 | extern int bstrchrp (const_bstring b, int c, int pos); 91 | extern int bstrrchrp (const_bstring b, int c, int pos); 92 | #define bstrchr(b,c) bstrchrp ((b), (c), 0) 93 | #define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1) 94 | extern int binchr (const_bstring b0, int pos, const_bstring b1); 95 | extern int binchrr (const_bstring b0, int pos, const_bstring b1); 96 | extern int bninchr (const_bstring b0, int pos, const_bstring b1); 97 | extern int bninchrr (const_bstring b0, int pos, const_bstring b1); 98 | extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos); 99 | extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos); 100 | 101 | /* List of string container functions */ 102 | struct bstrList { 103 | int qty, mlen; 104 | bstring * entry; 105 | }; 106 | extern struct bstrList * bstrListCreate (void); 107 | extern int bstrListDestroy (struct bstrList * sl); 108 | extern int bstrListAlloc (struct bstrList * sl, int msz); 109 | extern int bstrListAllocMin (struct bstrList * sl, int msz); 110 | 111 | /* String split and join functions */ 112 | extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar); 113 | extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr); 114 | extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr); 115 | extern bstring bjoin (const struct bstrList * bl, const_bstring sep); 116 | extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos, 117 | int (* cb) (void * parm, int ofs, int len), void * parm); 118 | extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos, 119 | int (* cb) (void * parm, int ofs, int len), void * parm); 120 | extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, 121 | int (* cb) (void * parm, int ofs, int len), void * parm); 122 | 123 | /* Miscellaneous functions */ 124 | extern int bpattern (bstring b, int len); 125 | extern int btoupper (bstring b); 126 | extern int btolower (bstring b); 127 | extern int bltrimws (bstring b); 128 | extern int brtrimws (bstring b); 129 | extern int btrimws (bstring b); 130 | 131 | #if !defined (BSTRLIB_NOVSNP) 132 | extern bstring bformat (const char * fmt, ...); 133 | extern int bformata (bstring b, const char * fmt, ...); 134 | extern int bassignformat (bstring b, const char * fmt, ...); 135 | extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist); 136 | 137 | #define bvformata(ret, b, fmt, lastarg) { \ 138 | bstring bstrtmp_b = (b); \ 139 | const char * bstrtmp_fmt = (fmt); \ 140 | int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \ 141 | for (;;) { \ 142 | va_list bstrtmp_arglist; \ 143 | va_start (bstrtmp_arglist, lastarg); \ 144 | bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \ 145 | va_end (bstrtmp_arglist); \ 146 | if (bstrtmp_r >= 0) { /* Everything went ok */ \ 147 | bstrtmp_r = BSTR_OK; \ 148 | break; \ 149 | } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \ 150 | bstrtmp_r = BSTR_ERR; \ 151 | break; \ 152 | } \ 153 | bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \ 154 | } \ 155 | ret = bstrtmp_r; \ 156 | } 157 | 158 | #endif 159 | 160 | typedef int (*bNgetc) (void *parm); 161 | typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm); 162 | 163 | /* Input functions */ 164 | extern bstring bgets (bNgetc getcPtr, void * parm, char terminator); 165 | extern bstring bread (bNread readPtr, void * parm); 166 | extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator); 167 | extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator); 168 | extern int breada (bstring b, bNread readPtr, void * parm); 169 | 170 | /* Stream functions */ 171 | extern struct bStream * bsopen (bNread readPtr, void * parm); 172 | extern void * bsclose (struct bStream * s); 173 | extern int bsbufflength (struct bStream * s, int sz); 174 | extern int bsreadln (bstring b, struct bStream * s, char terminator); 175 | extern int bsreadlns (bstring r, struct bStream * s, const_bstring term); 176 | extern int bsread (bstring b, struct bStream * s, int n); 177 | extern int bsreadlna (bstring b, struct bStream * s, char terminator); 178 | extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term); 179 | extern int bsreada (bstring b, struct bStream * s, int n); 180 | extern int bsunread (struct bStream * s, const_bstring b); 181 | extern int bspeek (bstring r, const struct bStream * s); 182 | extern int bssplitscb (struct bStream * s, const_bstring splitStr, 183 | int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); 184 | extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, 185 | int (* cb) (void * parm, int ofs, const_bstring entry), void * parm); 186 | extern int bseof (const struct bStream * s); 187 | 188 | struct tagbstring { 189 | int mlen; 190 | int slen; 191 | unsigned char * data; 192 | }; 193 | 194 | /* Accessor macros */ 195 | #define blengthe(b, e) (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen)) 196 | #define blength(b) (blengthe ((b), 0)) 197 | #define bdataofse(b, o, e) (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o)) 198 | #define bdataofs(b, o) (bdataofse ((b), (o), (void *)0)) 199 | #define bdatae(b, e) (bdataofse (b, 0, e)) 200 | #define bdata(b) (bdataofs (b, 0)) 201 | #define bchare(b, p, e) ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e)) 202 | #define bchar(b, p) bchare ((b), (p), '\0') 203 | 204 | /* Static constant string initialization macro */ 205 | #define bsStaticMlen(q,m) {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")} 206 | #if defined(_MSC_VER) 207 | # define bsStatic(q) bsStaticMlen(q,-32) 208 | #endif 209 | #ifndef bsStatic 210 | # define bsStatic(q) bsStaticMlen(q,-__LINE__) 211 | #endif 212 | 213 | /* Static constant block parameter pair */ 214 | #define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1) 215 | 216 | #define bcatStatic(b,s) ((bcatblk)((b), bsStaticBlkParms(s))) 217 | #define bfromStatic(s) ((blk2bstr)(bsStaticBlkParms(s))) 218 | #define bassignStatic(b,s) ((bassignblk)((b), bsStaticBlkParms(s))) 219 | #define bisstemeqcaselessStatic(b,s) ((bisstemeqcaselessblk)((b), bsStaticBlkParms(s))) 220 | 221 | /* Reference building macros */ 222 | #define cstr2tbstr btfromcstr 223 | #define btfromcstr(t,s) { \ 224 | (t).data = (unsigned char *) (s); \ 225 | (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \ 226 | (t).mlen = -1; \ 227 | } 228 | #define blk2tbstr(t,s,l) { \ 229 | (t).data = (unsigned char *) (s); \ 230 | (t).slen = l; \ 231 | (t).mlen = -1; \ 232 | } 233 | #define btfromblk(t,s,l) blk2tbstr(t,s,l) 234 | #define bmid2tbstr(t,b,p,l) { \ 235 | const_bstring bstrtmp_s = (b); \ 236 | if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) { \ 237 | int bstrtmp_left = (p); \ 238 | int bstrtmp_len = (l); \ 239 | if (bstrtmp_left < 0) { \ 240 | bstrtmp_len += bstrtmp_left; \ 241 | bstrtmp_left = 0; \ 242 | } \ 243 | if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left) \ 244 | bstrtmp_len = bstrtmp_s->slen - bstrtmp_left; \ 245 | if (bstrtmp_len <= 0) { \ 246 | (t).data = (unsigned char *)""; \ 247 | (t).slen = 0; \ 248 | } else { \ 249 | (t).data = bstrtmp_s->data + bstrtmp_left; \ 250 | (t).slen = bstrtmp_len; \ 251 | } \ 252 | } else { \ 253 | (t).data = (unsigned char *)""; \ 254 | (t).slen = 0; \ 255 | } \ 256 | (t).mlen = -__LINE__; \ 257 | } 258 | #define btfromblkltrimws(t,s,l) { \ 259 | int bstrtmp_idx = 0, bstrtmp_len = (l); \ 260 | unsigned char * bstrtmp_s = (s); \ 261 | if (bstrtmp_s && bstrtmp_len >= 0) { \ 262 | for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) { \ 263 | if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ 264 | } \ 265 | } \ 266 | (t).data = bstrtmp_s + bstrtmp_idx; \ 267 | (t).slen = bstrtmp_len - bstrtmp_idx; \ 268 | (t).mlen = -__LINE__; \ 269 | } 270 | #define btfromblkrtrimws(t,s,l) { \ 271 | int bstrtmp_len = (l) - 1; \ 272 | unsigned char * bstrtmp_s = (s); \ 273 | if (bstrtmp_s && bstrtmp_len >= 0) { \ 274 | for (; bstrtmp_len >= 0; bstrtmp_len--) { \ 275 | if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ 276 | } \ 277 | } \ 278 | (t).data = bstrtmp_s; \ 279 | (t).slen = bstrtmp_len + 1; \ 280 | (t).mlen = -__LINE__; \ 281 | } 282 | #define btfromblktrimws(t,s,l) { \ 283 | int bstrtmp_idx = 0, bstrtmp_len = (l) - 1; \ 284 | unsigned char * bstrtmp_s = (s); \ 285 | if (bstrtmp_s && bstrtmp_len >= 0) { \ 286 | for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) { \ 287 | if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \ 288 | } \ 289 | for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) { \ 290 | if (!isspace (bstrtmp_s[bstrtmp_len])) break; \ 291 | } \ 292 | } \ 293 | (t).data = bstrtmp_s + bstrtmp_idx; \ 294 | (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \ 295 | (t).mlen = -__LINE__; \ 296 | } 297 | 298 | /* Write protection macros */ 299 | #define bwriteprotect(t) { if ((t).mlen >= 0) (t).mlen = -1; } 300 | #define bwriteallow(t) { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); } 301 | #define biswriteprotected(t) ((t).mlen <= 0) 302 | 303 | #ifdef __cplusplus 304 | } 305 | #endif 306 | 307 | #endif 308 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /src/bstrlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This source file is part of the bstring string library. This code was 3 | * written by Paul Hsieh in 2002-2015, and is covered by the BSD open source 4 | * license and the GPL. Refer to the accompanying documentation for details 5 | * on usage and license. 6 | */ 7 | 8 | /* 9 | * bstrlib.c 10 | * 11 | * This file is the core module for implementing the bstring functions. 12 | */ 13 | 14 | #if defined (_MSC_VER) 15 | # define _CRT_SECURE_NO_WARNINGS 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "bstrlib.h" 25 | 26 | /* Optionally include a mechanism for debugging memory */ 27 | 28 | #if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG) 29 | #include "memdbg.h" 30 | #endif 31 | 32 | #ifndef bstr__alloc 33 | #define bstr__alloc(x) malloc (x) 34 | #endif 35 | 36 | #ifndef bstr__free 37 | #define bstr__free(p) free (p) 38 | #endif 39 | 40 | #ifndef bstr__realloc 41 | #define bstr__realloc(p,x) realloc ((p), (x)) 42 | #endif 43 | 44 | #ifndef bstr__memcpy 45 | #define bstr__memcpy(d,s,l) memcpy ((d), (s), (l)) 46 | #endif 47 | 48 | #ifndef bstr__memmove 49 | #define bstr__memmove(d,s,l) memmove ((d), (s), (l)) 50 | #endif 51 | 52 | #ifndef bstr__memset 53 | #define bstr__memset(d,c,l) memset ((d), (c), (l)) 54 | #endif 55 | 56 | #ifndef bstr__memcmp 57 | #define bstr__memcmp(d,c,l) memcmp ((d), (c), (l)) 58 | #endif 59 | 60 | #ifndef bstr__memchr 61 | #define bstr__memchr(s,c,l) memchr ((s), (c), (l)) 62 | #endif 63 | 64 | /* Just a length safe wrapper for memmove. */ 65 | 66 | #define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); } 67 | 68 | /* Compute the snapped size for a given requested size. By snapping to powers 69 | of 2 like this, repeated reallocations are avoided. */ 70 | static int snapUpSize (int i) { 71 | if (i < 8) { 72 | i = 8; 73 | } else { 74 | unsigned int j; 75 | j = (unsigned int) i; 76 | 77 | j |= (j >> 1); 78 | j |= (j >> 2); 79 | j |= (j >> 4); 80 | j |= (j >> 8); /* Ok, since int >= 16 bits */ 81 | #if (UINT_MAX != 0xffff) 82 | j |= (j >> 16); /* For 32 bit int systems */ 83 | #if (UINT_MAX > 0xffffffffUL) 84 | j |= (j >> 32); /* For 64 bit int systems */ 85 | #endif 86 | #endif 87 | /* Least power of two greater than i */ 88 | j++; 89 | if ((int) j >= i) i = (int) j; 90 | } 91 | return i; 92 | } 93 | 94 | /* int balloc (bstring b, int len) 95 | * 96 | * Increase the size of the memory backing the bstring b to at least len. 97 | */ 98 | int balloc (bstring b, int olen) { 99 | int len; 100 | if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || 101 | b->mlen < b->slen || olen <= 0) { 102 | return BSTR_ERR; 103 | } 104 | 105 | if (olen >= b->mlen) { 106 | unsigned char * x; 107 | 108 | if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK; 109 | 110 | /* Assume probability of a non-moving realloc is 0.125 */ 111 | if (7 * b->mlen < 8 * b->slen) { 112 | 113 | /* If slen is close to mlen in size then use realloc to reduce 114 | the memory defragmentation */ 115 | 116 | reallocStrategy:; 117 | 118 | x = (unsigned char *) bstr__realloc (b->data, (size_t) len); 119 | if (x == NULL) { 120 | 121 | /* Since we failed, try allocating the tighest possible 122 | allocation */ 123 | 124 | if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) { 125 | return BSTR_ERR; 126 | } 127 | } 128 | } else { 129 | 130 | /* If slen is not close to mlen then avoid the penalty of copying 131 | the extra bytes that are allocated, but not considered part of 132 | the string */ 133 | 134 | if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) { 135 | 136 | /* Perhaps there is no available memory for the two 137 | allocations to be in memory at once */ 138 | 139 | goto reallocStrategy; 140 | 141 | } else { 142 | if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen); 143 | bstr__free (b->data); 144 | } 145 | } 146 | b->data = x; 147 | b->mlen = len; 148 | b->data[b->slen] = (unsigned char) '\0'; 149 | 150 | #if defined (BSTRLIB_TEST_CANARY) 151 | if (len > b->slen + 1) { 152 | memchr (b->data + b->slen + 1, 'X', len - (b->slen + 1)); 153 | } 154 | #endif 155 | } 156 | 157 | return BSTR_OK; 158 | } 159 | 160 | /* int ballocmin (bstring b, int len) 161 | * 162 | * Set the size of the memory backing the bstring b to len or b->slen+1, 163 | * whichever is larger. Note that repeated use of this function can degrade 164 | * performance. 165 | */ 166 | int ballocmin (bstring b, int len) { 167 | unsigned char * s; 168 | 169 | if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || 170 | b->mlen < b->slen || len <= 0) { 171 | return BSTR_ERR; 172 | } 173 | 174 | if (len < b->slen + 1) len = b->slen + 1; 175 | 176 | if (len != b->mlen) { 177 | s = (unsigned char *) bstr__realloc (b->data, (size_t) len); 178 | if (NULL == s) return BSTR_ERR; 179 | s[b->slen] = (unsigned char) '\0'; 180 | b->data = s; 181 | b->mlen = len; 182 | } 183 | 184 | return BSTR_OK; 185 | } 186 | 187 | /* bstring bfromcstr (const char * str) 188 | * 189 | * Create a bstring which contains the contents of the '\0' terminated char * 190 | * buffer str. 191 | */ 192 | bstring bfromcstr (const char * str) { 193 | bstring b; 194 | int i; 195 | size_t j; 196 | 197 | if (str == NULL) return NULL; 198 | j = (strlen) (str); 199 | i = snapUpSize ((int) (j + (2 - (j != 0)))); 200 | if (i <= (int) j) return NULL; 201 | 202 | b = (bstring) bstr__alloc (sizeof (struct tagbstring)); 203 | if (NULL == b) return NULL; 204 | b->slen = (int) j; 205 | if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { 206 | bstr__free (b); 207 | return NULL; 208 | } 209 | 210 | bstr__memcpy (b->data, str, j+1); 211 | return b; 212 | } 213 | 214 | /* bstring bfromcstrrangealloc (int minl, int maxl, const char* str) 215 | * 216 | * Create a bstring which contains the contents of the '\0' terminated 217 | * char* buffer str. The memory buffer backing the string is at least 218 | * minl characters in length, but an attempt is made to allocate up to 219 | * maxl characters. 220 | */ 221 | bstring bfromcstrrangealloc (int minl, int maxl, const char* str) { 222 | bstring b; 223 | int i; 224 | size_t j; 225 | 226 | /* Bad parameters? */ 227 | if (str == NULL) return NULL; 228 | if (maxl < minl || minl < 0) return NULL; 229 | 230 | /* Adjust lengths */ 231 | j = (strlen) (str); 232 | if ((size_t) minl < (j+1)) minl = (int) (j+1); 233 | if (maxl < minl) maxl = minl; 234 | i = maxl; 235 | 236 | b = (bstring) bstr__alloc (sizeof (struct tagbstring)); 237 | if (b == NULL) return NULL; 238 | b->slen = (int) j; 239 | 240 | while (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) { 241 | int k = (i >> 1) + (minl >> 1); 242 | if (i == k || i < minl) { 243 | bstr__free (b); 244 | return NULL; 245 | } 246 | i = k; 247 | } 248 | 249 | bstr__memcpy (b->data, str, j+1); 250 | return b; 251 | } 252 | 253 | /* bstring bfromcstralloc (int mlen, const char * str) 254 | * 255 | * Create a bstring which contains the contents of the '\0' terminated 256 | * char* buffer str. The memory buffer backing the string is at least 257 | * mlen characters in length. 258 | */ 259 | bstring bfromcstralloc (int mlen, const char * str) { 260 | return bfromcstrrangealloc (mlen, mlen, str); 261 | } 262 | 263 | /* bstring blk2bstr (const void * blk, int len) 264 | * 265 | * Create a bstring which contains the content of the block blk of length 266 | * len. 267 | */ 268 | bstring blk2bstr (const void * blk, int len) { 269 | bstring b; 270 | int i; 271 | 272 | if (blk == NULL || len < 0) return NULL; 273 | b = (bstring) bstr__alloc (sizeof (struct tagbstring)); 274 | if (b == NULL) return NULL; 275 | b->slen = len; 276 | 277 | i = len + (2 - (len != 0)); 278 | i = snapUpSize (i); 279 | 280 | b->mlen = i; 281 | 282 | b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen); 283 | if (b->data == NULL) { 284 | bstr__free (b); 285 | return NULL; 286 | } 287 | 288 | if (len > 0) bstr__memcpy (b->data, blk, (size_t) len); 289 | b->data[len] = (unsigned char) '\0'; 290 | 291 | return b; 292 | } 293 | 294 | /* char * bstr2cstr (const_bstring s, char z) 295 | * 296 | * Create a '\0' terminated char * buffer which is equal to the contents of 297 | * the bstring s, except that any contained '\0' characters are converted 298 | * to the character in z. This returned value should be freed with a 299 | * bcstrfree () call, by the calling application. 300 | */ 301 | char * bstr2cstr (const_bstring b, char z) { 302 | int i, l; 303 | char * r; 304 | 305 | if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; 306 | l = b->slen; 307 | r = (char *) bstr__alloc ((size_t) (l + 1)); 308 | if (r == NULL) return r; 309 | 310 | for (i=0; i < l; i ++) { 311 | r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i])); 312 | } 313 | 314 | r[l] = (unsigned char) '\0'; 315 | 316 | return r; 317 | } 318 | 319 | /* int bcstrfree (char * s) 320 | * 321 | * Frees a C-string generated by bstr2cstr (). This is normally unnecessary 322 | * since it just wraps a call to bstr__free (), however, if bstr__alloc () 323 | * and bstr__free () have been redefined as a macros within the bstrlib 324 | * module (via defining them in memdbg.h after defining 325 | * BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std 326 | * library functions, then this allows a correct way of freeing the memory 327 | * that allows higher level code to be independent from these macro 328 | * redefinitions. 329 | */ 330 | int bcstrfree (char * s) { 331 | if (s) { 332 | bstr__free (s); 333 | return BSTR_OK; 334 | } 335 | return BSTR_ERR; 336 | } 337 | 338 | /* int bconcat (bstring b0, const_bstring b1) 339 | * 340 | * Concatenate the bstring b1 to the bstring b0. 341 | */ 342 | int bconcat (bstring b0, const_bstring b1) { 343 | int len, d; 344 | bstring aux = (bstring) b1; 345 | 346 | if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR; 347 | 348 | d = b0->slen; 349 | len = b1->slen; 350 | if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR; 351 | 352 | if (b0->mlen <= d + len + 1) { 353 | ptrdiff_t pd = b1->data - b0->data; 354 | if (0 <= pd && pd < b0->mlen) { 355 | if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; 356 | } 357 | if (balloc (b0, d + len + 1) != BSTR_OK) { 358 | if (aux != b1) bdestroy (aux); 359 | return BSTR_ERR; 360 | } 361 | } 362 | 363 | bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len); 364 | b0->data[d + len] = (unsigned char) '\0'; 365 | b0->slen = d + len; 366 | if (aux != b1) bdestroy (aux); 367 | return BSTR_OK; 368 | } 369 | 370 | /* int bconchar (bstring b, char c) 371 | * 372 | * Concatenate the single character c to the bstring b. 373 | */ 374 | int bconchar (bstring b, char c) { 375 | int d; 376 | 377 | if (b == NULL) return BSTR_ERR; 378 | d = b->slen; 379 | if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; 380 | b->data[d] = (unsigned char) c; 381 | b->data[d + 1] = (unsigned char) '\0'; 382 | b->slen++; 383 | return BSTR_OK; 384 | } 385 | 386 | /* int bcatcstr (bstring b, const char * s) 387 | * 388 | * Concatenate a char * string to a bstring. 389 | */ 390 | int bcatcstr (bstring b, const char * s) { 391 | char * d; 392 | int i, l; 393 | 394 | if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen 395 | || b->mlen <= 0 || s == NULL) return BSTR_ERR; 396 | 397 | /* Optimistically concatenate directly */ 398 | l = b->mlen - b->slen; 399 | d = (char *) &b->data[b->slen]; 400 | for (i=0; i < l; i++) { 401 | if ((*d++ = *s++) == '\0') { 402 | b->slen += i; 403 | return BSTR_OK; 404 | } 405 | } 406 | b->slen += i; 407 | 408 | /* Need to explicitely resize and concatenate tail */ 409 | return bcatblk (b, (const void *) s, (int) strlen (s)); 410 | } 411 | 412 | /* int bcatblk (bstring b, const void * s, int len) 413 | * 414 | * Concatenate a fixed length buffer to a bstring. 415 | */ 416 | int bcatblk (bstring b, const void * s, int len) { 417 | int nl; 418 | 419 | if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen 420 | || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR; 421 | 422 | if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */ 423 | if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR; 424 | 425 | bBlockCopy (&b->data[b->slen], s, (size_t) len); 426 | b->slen = nl; 427 | b->data[nl] = (unsigned char) '\0'; 428 | return BSTR_OK; 429 | } 430 | 431 | /* bstring bstrcpy (const_bstring b) 432 | * 433 | * Create a copy of the bstring b. 434 | */ 435 | bstring bstrcpy (const_bstring b) { 436 | bstring b0; 437 | int i,j; 438 | 439 | /* Attempted to copy an invalid string? */ 440 | if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; 441 | 442 | b0 = (bstring) bstr__alloc (sizeof (struct tagbstring)); 443 | if (b0 == NULL) { 444 | /* Unable to allocate memory for string header */ 445 | return NULL; 446 | } 447 | 448 | i = b->slen; 449 | j = snapUpSize (i + 1); 450 | 451 | b0->data = (unsigned char *) bstr__alloc (j); 452 | if (b0->data == NULL) { 453 | j = i + 1; 454 | b0->data = (unsigned char *) bstr__alloc (j); 455 | if (b0->data == NULL) { 456 | /* Unable to allocate memory for string data */ 457 | bstr__free (b0); 458 | return NULL; 459 | } 460 | } 461 | 462 | b0->mlen = j; 463 | b0->slen = i; 464 | 465 | if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i); 466 | b0->data[b0->slen] = (unsigned char) '\0'; 467 | 468 | return b0; 469 | } 470 | 471 | /* int bassign (bstring a, const_bstring b) 472 | * 473 | * Overwrite the string a with the contents of string b. 474 | */ 475 | int bassign (bstring a, const_bstring b) { 476 | if (b == NULL || b->data == NULL || b->slen < 0) 477 | return BSTR_ERR; 478 | if (b->slen != 0) { 479 | if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR; 480 | bstr__memmove (a->data, b->data, b->slen); 481 | } else { 482 | if (a == NULL || a->data == NULL || a->mlen < a->slen || 483 | a->slen < 0 || a->mlen == 0) 484 | return BSTR_ERR; 485 | } 486 | a->data[b->slen] = (unsigned char) '\0'; 487 | a->slen = b->slen; 488 | return BSTR_OK; 489 | } 490 | 491 | /* int bassignmidstr (bstring a, const_bstring b, int left, int len) 492 | * 493 | * Overwrite the string a with the middle of contents of string b 494 | * starting from position left and running for a length len. left and 495 | * len are clamped to the ends of b as with the function bmidstr. 496 | */ 497 | int bassignmidstr (bstring a, const_bstring b, int left, int len) { 498 | if (b == NULL || b->data == NULL || b->slen < 0) 499 | return BSTR_ERR; 500 | 501 | if (left < 0) { 502 | len += left; 503 | left = 0; 504 | } 505 | 506 | if (len > b->slen - left) len = b->slen - left; 507 | 508 | if (a == NULL || a->data == NULL || a->mlen < a->slen || 509 | a->slen < 0 || a->mlen == 0) 510 | return BSTR_ERR; 511 | 512 | if (len > 0) { 513 | if (balloc (a, len) != BSTR_OK) return BSTR_ERR; 514 | bstr__memmove (a->data, b->data + left, len); 515 | a->slen = len; 516 | } else { 517 | a->slen = 0; 518 | } 519 | a->data[a->slen] = (unsigned char) '\0'; 520 | return BSTR_OK; 521 | } 522 | 523 | /* int bassigncstr (bstring a, const char * str) 524 | * 525 | * Overwrite the string a with the contents of char * string str. Note that 526 | * the bstring a must be a well defined and writable bstring. If an error 527 | * occurs BSTR_ERR is returned however a may be partially overwritten. 528 | */ 529 | int bassigncstr (bstring a, const char * str) { 530 | int i; 531 | size_t len; 532 | if (a == NULL || a->data == NULL || a->mlen < a->slen || 533 | a->slen < 0 || a->mlen == 0 || NULL == str) 534 | return BSTR_ERR; 535 | 536 | for (i=0; i < a->mlen; i++) { 537 | if ('\0' == (a->data[i] = str[i])) { 538 | a->slen = i; 539 | return BSTR_OK; 540 | } 541 | } 542 | 543 | a->slen = i; 544 | len = strlen (str + i); 545 | if (len > INT_MAX || i + len + 1 > INT_MAX || 546 | 0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR; 547 | bBlockCopy (a->data + i, str + i, (size_t) len + 1); 548 | a->slen += (int) len; 549 | return BSTR_OK; 550 | } 551 | 552 | /* int bassignblk (bstring a, const void * s, int len) 553 | * 554 | * Overwrite the string a with the contents of the block (s, len). Note that 555 | * the bstring a must be a well defined and writable bstring. If an error 556 | * occurs BSTR_ERR is returned and a is not overwritten. 557 | */ 558 | int bassignblk (bstring a, const void * s, int len) { 559 | if (a == NULL || a->data == NULL || a->mlen < a->slen || 560 | a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) 561 | return BSTR_ERR; 562 | if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR; 563 | bBlockCopy (a->data, s, (size_t) len); 564 | a->data[len] = (unsigned char) '\0'; 565 | a->slen = len; 566 | return BSTR_OK; 567 | } 568 | 569 | /* int btrunc (bstring b, int n) 570 | * 571 | * Truncate the bstring to at most n characters. 572 | */ 573 | int btrunc (bstring b, int n) { 574 | if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen || 575 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 576 | if (b->slen > n) { 577 | b->slen = n; 578 | b->data[n] = (unsigned char) '\0'; 579 | } 580 | return BSTR_OK; 581 | } 582 | 583 | #define upcase(c) (toupper ((unsigned char) c)) 584 | #define downcase(c) (tolower ((unsigned char) c)) 585 | #define wspace(c) (isspace ((unsigned char) c)) 586 | 587 | /* int btoupper (bstring b) 588 | * 589 | * Convert contents of bstring to upper case. 590 | */ 591 | int btoupper (bstring b) { 592 | int i, len; 593 | if (b == NULL || b->data == NULL || b->mlen < b->slen || 594 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 595 | for (i=0, len = b->slen; i < len; i++) { 596 | b->data[i] = (unsigned char) upcase (b->data[i]); 597 | } 598 | return BSTR_OK; 599 | } 600 | 601 | /* int btolower (bstring b) 602 | * 603 | * Convert contents of bstring to lower case. 604 | */ 605 | int btolower (bstring b) { 606 | int i, len; 607 | if (b == NULL || b->data == NULL || b->mlen < b->slen || 608 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 609 | for (i=0, len = b->slen; i < len; i++) { 610 | b->data[i] = (unsigned char) downcase (b->data[i]); 611 | } 612 | return BSTR_OK; 613 | } 614 | 615 | /* int bstricmp (const_bstring b0, const_bstring b1) 616 | * 617 | * Compare two strings without differentiating between case. The return 618 | * value is the difference of the values of the characters where the two 619 | * strings first differ after lower case transformation, otherwise 0 is 620 | * returned indicating that the strings are equal. If the lengths are 621 | * different, then a difference from 0 is given, but if the first extra 622 | * character is '\0', then it is taken to be the value UCHAR_MAX+1. 623 | */ 624 | int bstricmp (const_bstring b0, const_bstring b1) { 625 | int i, v, n; 626 | 627 | if (bdata (b0) == NULL || b0->slen < 0 || 628 | bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN; 629 | if ((n = b0->slen) > b1->slen) n = b1->slen; 630 | else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK; 631 | 632 | for (i = 0; i < n; i ++) { 633 | v = (char) downcase (b0->data[i]) 634 | - (char) downcase (b1->data[i]); 635 | if (0 != v) return v; 636 | } 637 | 638 | if (b0->slen > n) { 639 | v = (char) downcase (b0->data[n]); 640 | if (v) return v; 641 | return UCHAR_MAX + 1; 642 | } 643 | if (b1->slen > n) { 644 | v = - (char) downcase (b1->data[n]); 645 | if (v) return v; 646 | return - (int) (UCHAR_MAX + 1); 647 | } 648 | return BSTR_OK; 649 | } 650 | 651 | /* int bstrnicmp (const_bstring b0, const_bstring b1, int n) 652 | * 653 | * Compare two strings without differentiating between case for at most n 654 | * characters. If the position where the two strings first differ is 655 | * before the nth position, the return value is the difference of the values 656 | * of the characters, otherwise 0 is returned. If the lengths are different 657 | * and less than n characters, then a difference from 0 is given, but if the 658 | * first extra character is '\0', then it is taken to be the value 659 | * UCHAR_MAX+1. 660 | */ 661 | int bstrnicmp (const_bstring b0, const_bstring b1, int n) { 662 | int i, v, m; 663 | 664 | if (bdata (b0) == NULL || b0->slen < 0 || 665 | bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN; 666 | m = n; 667 | if (m > b0->slen) m = b0->slen; 668 | if (m > b1->slen) m = b1->slen; 669 | 670 | if (b0->data != b1->data) { 671 | for (i = 0; i < m; i ++) { 672 | v = (char) downcase (b0->data[i]); 673 | v -= (char) downcase (b1->data[i]); 674 | if (v != 0) return b0->data[i] - b1->data[i]; 675 | } 676 | } 677 | 678 | if (n == m || b0->slen == b1->slen) return BSTR_OK; 679 | 680 | if (b0->slen > m) { 681 | v = (char) downcase (b0->data[m]); 682 | if (v) return v; 683 | return UCHAR_MAX + 1; 684 | } 685 | 686 | v = - (char) downcase (b1->data[m]); 687 | if (v) return v; 688 | return - (int) (UCHAR_MAX + 1); 689 | } 690 | 691 | /* int biseqcaseless (const_bstring b0, const_bstring b1) 692 | * 693 | * Compare two strings for equality without differentiating between case. 694 | * If the strings differ other than in case, 0 is returned, if the strings 695 | * are the same, 1 is returned, if there is an error, -1 is returned. If 696 | * the length of the strings are different, this function is O(1). '\0' 697 | * termination characters are not treated in any special way. 698 | */ 699 | int biseqcaseless (const_bstring b0, const_bstring b1) { 700 | int i, n; 701 | 702 | if (bdata (b0) == NULL || b0->slen < 0 || 703 | bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR; 704 | if (b0->slen != b1->slen) return BSTR_OK; 705 | if (b0->data == b1->data || b0->slen == 0) return 1; 706 | for (i=0, n=b0->slen; i < n; i++) { 707 | if (b0->data[i] != b1->data[i]) { 708 | unsigned char c = (unsigned char) downcase (b0->data[i]); 709 | if (c != (unsigned char) downcase (b1->data[i])) return 0; 710 | } 711 | } 712 | return 1; 713 | } 714 | 715 | /* int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) 716 | * 717 | * Compare beginning of string b0 with a block of memory of length len 718 | * without differentiating between case for equality. If the beginning of b0 719 | * differs from the memory block other than in case (or if b0 is too short), 720 | * 0 is returned, if the strings are the same, 1 is returned, if there is an 721 | * error, -1 is returned. '\0' characters are not treated in any special 722 | * way. 723 | */ 724 | int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) { 725 | int i; 726 | 727 | if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) 728 | return BSTR_ERR; 729 | if (b0->slen < len) return BSTR_OK; 730 | if (b0->data == (const unsigned char *) blk || len == 0) return 1; 731 | 732 | for (i = 0; i < len; i ++) { 733 | if (b0->data[i] != ((const unsigned char *) blk)[i]) { 734 | if (downcase (b0->data[i]) != 735 | downcase (((const unsigned char *) blk)[i])) return 0; 736 | } 737 | } 738 | return 1; 739 | } 740 | 741 | /* 742 | * int bltrimws (bstring b) 743 | * 744 | * Delete whitespace contiguous from the left end of the string. 745 | */ 746 | int bltrimws (bstring b) { 747 | int i, len; 748 | 749 | if (b == NULL || b->data == NULL || b->mlen < b->slen || 750 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 751 | 752 | for (len = b->slen, i = 0; i < len; i++) { 753 | if (!wspace (b->data[i])) { 754 | return bdelete (b, 0, i); 755 | } 756 | } 757 | 758 | b->data[0] = (unsigned char) '\0'; 759 | b->slen = 0; 760 | return BSTR_OK; 761 | } 762 | 763 | /* 764 | * int brtrimws (bstring b) 765 | * 766 | * Delete whitespace contiguous from the right end of the string. 767 | */ 768 | int brtrimws (bstring b) { 769 | int i; 770 | 771 | if (b == NULL || b->data == NULL || b->mlen < b->slen || 772 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 773 | 774 | for (i = b->slen - 1; i >= 0; i--) { 775 | if (!wspace (b->data[i])) { 776 | if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; 777 | b->slen = i + 1; 778 | return BSTR_OK; 779 | } 780 | } 781 | 782 | b->data[0] = (unsigned char) '\0'; 783 | b->slen = 0; 784 | return BSTR_OK; 785 | } 786 | 787 | /* 788 | * int btrimws (bstring b) 789 | * 790 | * Delete whitespace contiguous from both ends of the string. 791 | */ 792 | int btrimws (bstring b) { 793 | int i, j; 794 | 795 | if (b == NULL || b->data == NULL || b->mlen < b->slen || 796 | b->slen < 0 || b->mlen <= 0) return BSTR_ERR; 797 | 798 | for (i = b->slen - 1; i >= 0; i--) { 799 | if (!wspace (b->data[i])) { 800 | if (b->mlen > i) b->data[i+1] = (unsigned char) '\0'; 801 | b->slen = i + 1; 802 | for (j = 0; wspace (b->data[j]); j++) {} 803 | return bdelete (b, 0, j); 804 | } 805 | } 806 | 807 | b->data[0] = (unsigned char) '\0'; 808 | b->slen = 0; 809 | return BSTR_OK; 810 | } 811 | 812 | /* int biseq (const_bstring b0, const_bstring b1) 813 | * 814 | * Compare the string b0 and b1. If the strings differ, 0 is returned, if 815 | * the strings are the same, 1 is returned, if there is an error, -1 is 816 | * returned. If the length of the strings are different, this function is 817 | * O(1). '\0' termination characters are not treated in any special way. 818 | */ 819 | int biseq (const_bstring b0, const_bstring b1) { 820 | if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || 821 | b0->slen < 0 || b1->slen < 0) return BSTR_ERR; 822 | if (b0->slen != b1->slen) return BSTR_OK; 823 | if (b0->data == b1->data || b0->slen == 0) return 1; 824 | return !bstr__memcmp (b0->data, b1->data, b0->slen); 825 | } 826 | 827 | /* int bisstemeqblk (const_bstring b0, const void * blk, int len) 828 | * 829 | * Compare beginning of string b0 with a block of memory of length len for 830 | * equality. If the beginning of b0 differs from the memory block (or if b0 831 | * is too short), 0 is returned, if the strings are the same, 1 is returned, 832 | * if there is an error, -1 is returned. '\0' characters are not treated in 833 | * any special way. 834 | */ 835 | int bisstemeqblk (const_bstring b0, const void * blk, int len) { 836 | int i; 837 | 838 | if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0) 839 | return BSTR_ERR; 840 | if (b0->slen < len) return BSTR_OK; 841 | if (b0->data == (const unsigned char *) blk || len == 0) return 1; 842 | 843 | for (i = 0; i < len; i ++) { 844 | if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK; 845 | } 846 | return 1; 847 | } 848 | 849 | /* int biseqcstr (const_bstring b, const char *s) 850 | * 851 | * Compare the bstring b and char * string s. The C string s must be '\0' 852 | * terminated at exactly the length of the bstring b, and the contents 853 | * between the two must be identical with the bstring b with no '\0' 854 | * characters for the two contents to be considered equal. This is 855 | * equivalent to the condition that their current contents will be always be 856 | * equal when comparing them in the same format after converting one or the 857 | * other. If the strings are equal 1 is returned, if they are unequal 0 is 858 | * returned and if there is a detectable error BSTR_ERR is returned. 859 | */ 860 | int biseqcstr (const_bstring b, const char * s) { 861 | int i; 862 | if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; 863 | for (i=0; i < b->slen; i++) { 864 | if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK; 865 | } 866 | return s[i] == '\0'; 867 | } 868 | 869 | /* int biseqcstrcaseless (const_bstring b, const char *s) 870 | * 871 | * Compare the bstring b and char * string s. The C string s must be '\0' 872 | * terminated at exactly the length of the bstring b, and the contents 873 | * between the two must be identical except for case with the bstring b with 874 | * no '\0' characters for the two contents to be considered equal. This is 875 | * equivalent to the condition that their current contents will be always be 876 | * equal ignoring case when comparing them in the same format after 877 | * converting one or the other. If the strings are equal, except for case, 878 | * 1 is returned, if they are unequal regardless of case 0 is returned and 879 | * if there is a detectable error BSTR_ERR is returned. 880 | */ 881 | int biseqcstrcaseless (const_bstring b, const char * s) { 882 | int i; 883 | if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR; 884 | for (i=0; i < b->slen; i++) { 885 | if (s[i] == '\0' || 886 | (b->data[i] != (unsigned char) s[i] && 887 | downcase (b->data[i]) != (unsigned char) downcase (s[i]))) 888 | return BSTR_OK; 889 | } 890 | return s[i] == '\0'; 891 | } 892 | 893 | /* int bstrcmp (const_bstring b0, const_bstring b1) 894 | * 895 | * Compare the string b0 and b1. If there is an error, SHRT_MIN is returned, 896 | * otherwise a value less than or greater than zero, indicating that the 897 | * string pointed to by b0 is lexicographically less than or greater than 898 | * the string pointed to by b1 is returned. If the the string lengths are 899 | * unequal but the characters up until the length of the shorter are equal 900 | * then a value less than, or greater than zero, indicating that the string 901 | * pointed to by b0 is shorter or longer than the string pointed to by b1 is 902 | * returned. 0 is returned if and only if the two strings are the same. If 903 | * the length of the strings are different, this function is O(n). Like its 904 | * standard C library counter part strcmp, the comparison does not proceed 905 | * past any '\0' termination characters encountered. 906 | */ 907 | int bstrcmp (const_bstring b0, const_bstring b1) { 908 | int i, v, n; 909 | 910 | if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || 911 | b0->slen < 0 || b1->slen < 0) return SHRT_MIN; 912 | n = b0->slen; if (n > b1->slen) n = b1->slen; 913 | if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0)) 914 | return BSTR_OK; 915 | 916 | for (i = 0; i < n; i ++) { 917 | v = ((char) b0->data[i]) - ((char) b1->data[i]); 918 | if (v != 0) return v; 919 | if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; 920 | } 921 | 922 | if (b0->slen > n) return 1; 923 | if (b1->slen > n) return -1; 924 | return BSTR_OK; 925 | } 926 | 927 | /* int bstrncmp (const_bstring b0, const_bstring b1, int n) 928 | * 929 | * Compare the string b0 and b1 for at most n characters. If there is an 930 | * error, SHRT_MIN is returned, otherwise a value is returned as if b0 and 931 | * b1 were first truncated to at most n characters then bstrcmp was called 932 | * with these new strings are paremeters. If the length of the strings are 933 | * different, this function is O(n). Like its standard C library counter 934 | * part strcmp, the comparison does not proceed past any '\0' termination 935 | * characters encountered. 936 | */ 937 | int bstrncmp (const_bstring b0, const_bstring b1, int n) { 938 | int i, v, m; 939 | 940 | if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL || 941 | b0->slen < 0 || b1->slen < 0) return SHRT_MIN; 942 | m = n; 943 | if (m > b0->slen) m = b0->slen; 944 | if (m > b1->slen) m = b1->slen; 945 | 946 | if (b0->data != b1->data) { 947 | for (i = 0; i < m; i ++) { 948 | v = ((char) b0->data[i]) - ((char) b1->data[i]); 949 | if (v != 0) return v; 950 | if (b0->data[i] == (unsigned char) '\0') return BSTR_OK; 951 | } 952 | } 953 | 954 | if (n == m || b0->slen == b1->slen) return BSTR_OK; 955 | 956 | if (b0->slen > m) return 1; 957 | return -1; 958 | } 959 | 960 | /* bstring bmidstr (const_bstring b, int left, int len) 961 | * 962 | * Create a bstring which is the substring of b starting from position left 963 | * and running for a length len (clamped by the end of the bstring b.) If 964 | * b is detectably invalid, then NULL is returned. The section described 965 | * by (left, len) is clamped to the boundaries of b. 966 | */ 967 | bstring bmidstr (const_bstring b, int left, int len) { 968 | 969 | if (b == NULL || b->slen < 0 || b->data == NULL) return NULL; 970 | 971 | if (left < 0) { 972 | len += left; 973 | left = 0; 974 | } 975 | 976 | if (len > b->slen - left) len = b->slen - left; 977 | 978 | if (len <= 0) return bfromcstr (""); 979 | return blk2bstr (b->data + left, len); 980 | } 981 | 982 | /* int bdelete (bstring b, int pos, int len) 983 | * 984 | * Removes characters from pos to pos+len-1 inclusive and shifts the tail of 985 | * the bstring starting from pos+len to pos. len must be positive for this 986 | * call to have any effect. The section of the string described by (pos, 987 | * len) is clamped to boundaries of the bstring b. 988 | */ 989 | int bdelete (bstring b, int pos, int len) { 990 | /* Clamp to left side of bstring */ 991 | if (pos < 0) { 992 | len += pos; 993 | pos = 0; 994 | } 995 | 996 | if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || 997 | b->mlen < b->slen || b->mlen <= 0) 998 | return BSTR_ERR; 999 | if (len > 0 && pos < b->slen) { 1000 | if (pos + len >= b->slen) { 1001 | b->slen = pos; 1002 | } else { 1003 | bBlockCopy ((char *) (b->data + pos), 1004 | (char *) (b->data + pos + len), 1005 | b->slen - (pos+len)); 1006 | b->slen -= len; 1007 | } 1008 | b->data[b->slen] = (unsigned char) '\0'; 1009 | } 1010 | return BSTR_OK; 1011 | } 1012 | 1013 | /* int bdestroy (bstring b) 1014 | * 1015 | * Free up the bstring. Note that if b is detectably invalid or not writable 1016 | * then no action is performed and BSTR_ERR is returned. Like a freed memory 1017 | * allocation, dereferences, writes or any other action on b after it has 1018 | * been bdestroyed is undefined. 1019 | */ 1020 | int bdestroy (bstring b) { 1021 | if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen || 1022 | b->data == NULL) 1023 | return BSTR_ERR; 1024 | 1025 | bstr__free (b->data); 1026 | 1027 | /* In case there is any stale usage, there is one more chance to 1028 | notice this error. */ 1029 | 1030 | b->slen = -1; 1031 | b->mlen = -__LINE__; 1032 | b->data = NULL; 1033 | 1034 | bstr__free (b); 1035 | return BSTR_OK; 1036 | } 1037 | 1038 | /* int binstr (const_bstring b1, int pos, const_bstring b2) 1039 | * 1040 | * Search for the bstring b2 in b1 starting from position pos, and searching 1041 | * forward. If it is found then return with the first position where it is 1042 | * found, otherwise return BSTR_ERR. Note that this is just a brute force 1043 | * string searcher that does not attempt clever things like the Boyer-Moore 1044 | * search algorithm. Because of this there are many degenerate cases where 1045 | * this can take much longer than it needs to. 1046 | */ 1047 | int binstr (const_bstring b1, int pos, const_bstring b2) { 1048 | int j, ii, ll, lf; 1049 | unsigned char * d0; 1050 | unsigned char c0; 1051 | register unsigned char * d1; 1052 | register unsigned char c1; 1053 | register int i; 1054 | 1055 | if (b1 == NULL || b1->data == NULL || b1->slen < 0 || 1056 | b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; 1057 | if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; 1058 | if (b1->slen < pos || pos < 0) return BSTR_ERR; 1059 | if (b2->slen == 0) return pos; 1060 | 1061 | /* No space to find such a string? */ 1062 | if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR; 1063 | 1064 | /* An obvious alias case */ 1065 | if (b1->data == b2->data && pos == 0) return 0; 1066 | 1067 | i = pos; 1068 | 1069 | d0 = b2->data; 1070 | d1 = b1->data; 1071 | ll = b2->slen; 1072 | 1073 | /* Peel off the b2->slen == 1 case */ 1074 | c0 = d0[0]; 1075 | if (1 == ll) { 1076 | for (;i < lf; i++) if (c0 == d1[i]) return i; 1077 | return BSTR_ERR; 1078 | } 1079 | 1080 | c1 = c0; 1081 | j = 0; 1082 | lf = b1->slen - 1; 1083 | 1084 | ii = -1; 1085 | if (i < lf) do { 1086 | /* Unrolled current character test */ 1087 | if (c1 != d1[i]) { 1088 | if (c1 != d1[1+i]) { 1089 | i += 2; 1090 | continue; 1091 | } 1092 | i++; 1093 | } 1094 | 1095 | /* Take note if this is the start of a potential match */ 1096 | if (0 == j) ii = i; 1097 | 1098 | /* Shift the test character down by one */ 1099 | j++; 1100 | i++; 1101 | 1102 | /* If this isn't past the last character continue */ 1103 | if (j < ll) { 1104 | c1 = d0[j]; 1105 | continue; 1106 | } 1107 | 1108 | N0:; 1109 | 1110 | /* If no characters mismatched, then we matched */ 1111 | if (i == ii+j) return ii; 1112 | 1113 | /* Shift back to the beginning */ 1114 | i -= j; 1115 | j = 0; 1116 | c1 = c0; 1117 | } while (i < lf); 1118 | 1119 | /* Deal with last case if unrolling caused a misalignment */ 1120 | if (i == lf && ll == j+1 && c1 == d1[i]) goto N0; 1121 | 1122 | return BSTR_ERR; 1123 | } 1124 | 1125 | /* int binstrr (const_bstring b1, int pos, const_bstring b2) 1126 | * 1127 | * Search for the bstring b2 in b1 starting from position pos, and searching 1128 | * backward. If it is found then return with the first position where it is 1129 | * found, otherwise return BSTR_ERR. Note that this is just a brute force 1130 | * string searcher that does not attempt clever things like the Boyer-Moore 1131 | * search algorithm. Because of this there are many degenerate cases where 1132 | * this can take much longer than it needs to. 1133 | */ 1134 | int binstrr (const_bstring b1, int pos, const_bstring b2) { 1135 | int j, i, l; 1136 | unsigned char * d0, * d1; 1137 | 1138 | if (b1 == NULL || b1->data == NULL || b1->slen < 0 || 1139 | b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; 1140 | if (b1->slen == pos && b2->slen == 0) return pos; 1141 | if (b1->slen < pos || pos < 0) return BSTR_ERR; 1142 | if (b2->slen == 0) return pos; 1143 | 1144 | /* Obvious alias case */ 1145 | if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0; 1146 | 1147 | i = pos; 1148 | if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; 1149 | 1150 | /* If no space to find such a string then snap back */ 1151 | if (l + 1 <= i) i = l; 1152 | j = 0; 1153 | 1154 | d0 = b2->data; 1155 | d1 = b1->data; 1156 | l = b2->slen; 1157 | 1158 | for (;;) { 1159 | if (d0[j] == d1[i + j]) { 1160 | j ++; 1161 | if (j >= l) return i; 1162 | } else { 1163 | i --; 1164 | if (i < 0) break; 1165 | j=0; 1166 | } 1167 | } 1168 | 1169 | return BSTR_ERR; 1170 | } 1171 | 1172 | /* int binstrcaseless (const_bstring b1, int pos, const_bstring b2) 1173 | * 1174 | * Search for the bstring b2 in b1 starting from position pos, and searching 1175 | * forward but without regard to case. If it is found then return with the 1176 | * first position where it is found, otherwise return BSTR_ERR. Note that 1177 | * this is just a brute force string searcher that does not attempt clever 1178 | * things like the Boyer-Moore search algorithm. Because of this there are 1179 | * many degenerate cases where this can take much longer than it needs to. 1180 | */ 1181 | int binstrcaseless (const_bstring b1, int pos, const_bstring b2) { 1182 | int j, i, l, ll; 1183 | unsigned char * d0, * d1; 1184 | 1185 | if (b1 == NULL || b1->data == NULL || b1->slen < 0 || 1186 | b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; 1187 | if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR; 1188 | if (b1->slen < pos || pos < 0) return BSTR_ERR; 1189 | if (b2->slen == 0) return pos; 1190 | 1191 | l = b1->slen - b2->slen + 1; 1192 | 1193 | /* No space to find such a string? */ 1194 | if (l <= pos) return BSTR_ERR; 1195 | 1196 | /* An obvious alias case */ 1197 | if (b1->data == b2->data && pos == 0) return BSTR_OK; 1198 | 1199 | i = pos; 1200 | j = 0; 1201 | 1202 | d0 = b2->data; 1203 | d1 = b1->data; 1204 | ll = b2->slen; 1205 | 1206 | for (;;) { 1207 | if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { 1208 | j ++; 1209 | if (j >= ll) return i; 1210 | } else { 1211 | i ++; 1212 | if (i >= l) break; 1213 | j=0; 1214 | } 1215 | } 1216 | 1217 | return BSTR_ERR; 1218 | } 1219 | 1220 | /* int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) 1221 | * 1222 | * Search for the bstring b2 in b1 starting from position pos, and searching 1223 | * backward but without regard to case. If it is found then return with the 1224 | * first position where it is found, otherwise return BSTR_ERR. Note that 1225 | * this is just a brute force string searcher that does not attempt clever 1226 | * things like the Boyer-Moore search algorithm. Because of this there are 1227 | * many degenerate cases where this can take much longer than it needs to. 1228 | */ 1229 | int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) { 1230 | int j, i, l; 1231 | unsigned char * d0, * d1; 1232 | 1233 | if (b1 == NULL || b1->data == NULL || b1->slen < 0 || 1234 | b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR; 1235 | if (b1->slen == pos && b2->slen == 0) return pos; 1236 | if (b1->slen < pos || pos < 0) return BSTR_ERR; 1237 | if (b2->slen == 0) return pos; 1238 | 1239 | /* Obvious alias case */ 1240 | if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK; 1241 | 1242 | i = pos; 1243 | if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR; 1244 | 1245 | /* If no space to find such a string then snap back */ 1246 | if (l + 1 <= i) i = l; 1247 | j = 0; 1248 | 1249 | d0 = b2->data; 1250 | d1 = b1->data; 1251 | l = b2->slen; 1252 | 1253 | for (;;) { 1254 | if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) { 1255 | j ++; 1256 | if (j >= l) return i; 1257 | } else { 1258 | i --; 1259 | if (i < 0) break; 1260 | j=0; 1261 | } 1262 | } 1263 | 1264 | return BSTR_ERR; 1265 | } 1266 | 1267 | 1268 | /* int bstrchrp (const_bstring b, int c, int pos) 1269 | * 1270 | * Search for the character c in b forwards from the position pos 1271 | * (inclusive). 1272 | */ 1273 | int bstrchrp (const_bstring b, int c, int pos) { 1274 | unsigned char * p; 1275 | 1276 | if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; 1277 | p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos)); 1278 | if (p) return (int) (p - b->data); 1279 | return BSTR_ERR; 1280 | } 1281 | 1282 | /* int bstrrchrp (const_bstring b, int c, int pos) 1283 | * 1284 | * Search for the character c in b backwards from the position pos in string 1285 | * (inclusive). 1286 | */ 1287 | int bstrrchrp (const_bstring b, int c, int pos) { 1288 | int i; 1289 | 1290 | if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR; 1291 | for (i=pos; i >= 0; i--) { 1292 | if (b->data[i] == (unsigned char) c) return i; 1293 | } 1294 | return BSTR_ERR; 1295 | } 1296 | 1297 | #if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF) 1298 | #define LONG_LOG_BITS_QTY (3) 1299 | #define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY) 1300 | #define LONG_TYPE unsigned char 1301 | 1302 | #define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY) 1303 | struct charField { LONG_TYPE content[CFCLEN]; }; 1304 | #define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1)))) 1305 | #define setInCharField(cf,idx) { \ 1306 | unsigned int c = (unsigned int) (idx); \ 1307 | (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \ 1308 | } 1309 | 1310 | #else 1311 | 1312 | #define CFCLEN (1 << CHAR_BIT) 1313 | struct charField { unsigned char content[CFCLEN]; }; 1314 | #define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)]) 1315 | #define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0 1316 | 1317 | #endif 1318 | 1319 | /* Convert a bstring to charField */ 1320 | static int buildCharField (struct charField * cf, const_bstring b) { 1321 | int i; 1322 | if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR; 1323 | memset ((void *) cf->content, 0, sizeof (struct charField)); 1324 | for (i=0; i < b->slen; i++) { 1325 | setInCharField (cf, b->data[i]); 1326 | } 1327 | return BSTR_OK; 1328 | } 1329 | 1330 | static void invertCharField (struct charField * cf) { 1331 | int i; 1332 | for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i]; 1333 | } 1334 | 1335 | /* Inner engine for binchr */ 1336 | static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) { 1337 | int i; 1338 | for (i=pos; i < len; i++) { 1339 | unsigned char c = (unsigned char) data[i]; 1340 | if (testInCharField (cf, c)) return i; 1341 | } 1342 | return BSTR_ERR; 1343 | } 1344 | 1345 | /* int binchr (const_bstring b0, int pos, const_bstring b1); 1346 | * 1347 | * Search for the first position in b0 starting from pos or after, in which 1348 | * one of the characters in b1 is found and return it. If such a position 1349 | * does not exist in b0, then BSTR_ERR is returned. 1350 | */ 1351 | int binchr (const_bstring b0, int pos, const_bstring b1) { 1352 | struct charField chrs; 1353 | if (pos < 0 || b0 == NULL || b0->data == NULL || 1354 | b0->slen <= pos) return BSTR_ERR; 1355 | if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos); 1356 | if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; 1357 | return binchrCF (b0->data, b0->slen, pos, &chrs); 1358 | } 1359 | 1360 | /* Inner engine for binchrr */ 1361 | static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) { 1362 | int i; 1363 | for (i=pos; i >= 0; i--) { 1364 | unsigned int c = (unsigned int) data[i]; 1365 | if (testInCharField (cf, c)) return i; 1366 | } 1367 | return BSTR_ERR; 1368 | } 1369 | 1370 | /* int binchrr (const_bstring b0, int pos, const_bstring b1); 1371 | * 1372 | * Search for the last position in b0 no greater than pos, in which one of 1373 | * the characters in b1 is found and return it. If such a position does not 1374 | * exist in b0, then BSTR_ERR is returned. 1375 | */ 1376 | int binchrr (const_bstring b0, int pos, const_bstring b1) { 1377 | struct charField chrs; 1378 | if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL || 1379 | b0->slen < pos) return BSTR_ERR; 1380 | if (pos == b0->slen) pos--; 1381 | if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos); 1382 | if (0 > buildCharField (&chrs, b1)) return BSTR_ERR; 1383 | return binchrrCF (b0->data, pos, &chrs); 1384 | } 1385 | 1386 | /* int bninchr (const_bstring b0, int pos, const_bstring b1); 1387 | * 1388 | * Search for the first position in b0 starting from pos or after, in which 1389 | * none of the characters in b1 is found and return it. If such a position 1390 | * does not exist in b0, then BSTR_ERR is returned. 1391 | */ 1392 | int bninchr (const_bstring b0, int pos, const_bstring b1) { 1393 | struct charField chrs; 1394 | if (pos < 0 || b0 == NULL || b0->data == NULL || 1395 | b0->slen <= pos) return BSTR_ERR; 1396 | if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; 1397 | invertCharField (&chrs); 1398 | return binchrCF (b0->data, b0->slen, pos, &chrs); 1399 | } 1400 | 1401 | /* int bninchrr (const_bstring b0, int pos, const_bstring b1); 1402 | * 1403 | * Search for the last position in b0 no greater than pos, in which none of 1404 | * the characters in b1 is found and return it. If such a position does not 1405 | * exist in b0, then BSTR_ERR is returned. 1406 | */ 1407 | int bninchrr (const_bstring b0, int pos, const_bstring b1) { 1408 | struct charField chrs; 1409 | if (pos < 0 || b0 == NULL || b0->data == NULL || 1410 | b0->slen < pos) return BSTR_ERR; 1411 | if (pos == b0->slen) pos--; 1412 | if (buildCharField (&chrs, b1) < 0) return BSTR_ERR; 1413 | invertCharField (&chrs); 1414 | return binchrrCF (b0->data, pos, &chrs); 1415 | } 1416 | 1417 | /* int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill) 1418 | * 1419 | * Overwrite the string b0 starting at position pos with the string b1. If 1420 | * the position pos is past the end of b0, then the character "fill" is 1421 | * appended as necessary to make up the gap between the end of b0 and pos. 1422 | * If b1 is NULL, it behaves as if it were a 0-length string. 1423 | */ 1424 | int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) { 1425 | int d, newlen; 1426 | ptrdiff_t pd; 1427 | bstring aux = (bstring) b1; 1428 | 1429 | if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || 1430 | b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR; 1431 | if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR; 1432 | 1433 | d = pos; 1434 | 1435 | /* Aliasing case */ 1436 | if (NULL != aux) { 1437 | if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) { 1438 | if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR; 1439 | } 1440 | d += aux->slen; 1441 | } 1442 | 1443 | /* Increase memory size if necessary */ 1444 | if (balloc (b0, d + 1) != BSTR_OK) { 1445 | if (aux != b1) bdestroy (aux); 1446 | return BSTR_ERR; 1447 | } 1448 | 1449 | newlen = b0->slen; 1450 | 1451 | /* Fill in "fill" character as necessary */ 1452 | if (pos > newlen) { 1453 | bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen)); 1454 | newlen = pos; 1455 | } 1456 | 1457 | /* Copy b1 to position pos in b0. */ 1458 | if (aux != NULL) { 1459 | bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen); 1460 | if (aux != b1) bdestroy (aux); 1461 | } 1462 | 1463 | /* Indicate the potentially increased size of b0 */ 1464 | if (d > newlen) newlen = d; 1465 | 1466 | b0->slen = newlen; 1467 | b0->data[newlen] = (unsigned char) '\0'; 1468 | 1469 | return BSTR_OK; 1470 | } 1471 | 1472 | /* int binsert (bstring b1, int pos, bstring b2, unsigned char fill) 1473 | * 1474 | * Inserts the string b2 into b1 at position pos. If the position pos is 1475 | * past the end of b1, then the character "fill" is appended as necessary to 1476 | * make up the gap between the end of b1 and pos. Unlike bsetstr, binsert 1477 | * does not allow b2 to be NULL. 1478 | */ 1479 | int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) { 1480 | int d, l; 1481 | ptrdiff_t pd; 1482 | bstring aux = (bstring) b2; 1483 | 1484 | if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || 1485 | b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR; 1486 | 1487 | /* Aliasing case */ 1488 | if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) { 1489 | if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; 1490 | } 1491 | 1492 | /* Compute the two possible end pointers */ 1493 | d = b1->slen + aux->slen; 1494 | l = pos + aux->slen; 1495 | if ((d|l) < 0) return BSTR_ERR; 1496 | 1497 | if (l > d) { 1498 | /* Inserting past the end of the string */ 1499 | if (balloc (b1, l + 1) != BSTR_OK) { 1500 | if (aux != b2) bdestroy (aux); 1501 | return BSTR_ERR; 1502 | } 1503 | bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen)); 1504 | b1->slen = l; 1505 | } else { 1506 | /* Inserting in the middle of the string */ 1507 | if (balloc (b1, d + 1) != BSTR_OK) { 1508 | if (aux != b2) bdestroy (aux); 1509 | return BSTR_ERR; 1510 | } 1511 | bBlockCopy (b1->data + l, b1->data + pos, d - l); 1512 | b1->slen = d; 1513 | } 1514 | bBlockCopy (b1->data + pos, aux->data, aux->slen); 1515 | b1->data[b1->slen] = (unsigned char) '\0'; 1516 | if (aux != b2) bdestroy (aux); 1517 | return BSTR_OK; 1518 | } 1519 | 1520 | /* int breplace (bstring b1, int pos, int len, bstring b2, 1521 | * unsigned char fill) 1522 | * 1523 | * Replace a section of a string from pos for a length len with the string b2. 1524 | * fill is used is pos > b1->slen. 1525 | */ 1526 | int breplace (bstring b1, int pos, int len, const_bstring b2, 1527 | unsigned char fill) { 1528 | int pl, ret; 1529 | ptrdiff_t pd; 1530 | bstring aux = (bstring) b2; 1531 | 1532 | if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || 1533 | b2 == NULL || b1->data == NULL || b2->data == NULL || 1534 | b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen || 1535 | b1->mlen <= 0) return BSTR_ERR; 1536 | 1537 | /* Straddles the end? */ 1538 | if (pl >= b1->slen) { 1539 | if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret; 1540 | if (pos + b2->slen < b1->slen) { 1541 | b1->slen = pos + b2->slen; 1542 | b1->data[b1->slen] = (unsigned char) '\0'; 1543 | } 1544 | return ret; 1545 | } 1546 | 1547 | /* Aliasing case */ 1548 | if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) { 1549 | if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR; 1550 | } 1551 | 1552 | if (aux->slen > len) { 1553 | if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) { 1554 | if (aux != b2) bdestroy (aux); 1555 | return BSTR_ERR; 1556 | } 1557 | } 1558 | 1559 | if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len)); 1560 | bstr__memcpy (b1->data + pos, aux->data, aux->slen); 1561 | b1->slen += aux->slen - len; 1562 | b1->data[b1->slen] = (unsigned char) '\0'; 1563 | if (aux != b2) bdestroy (aux); 1564 | return BSTR_OK; 1565 | } 1566 | 1567 | /* 1568 | * findreplaceengine is used to implement bfindreplace and 1569 | * bfindreplacecaseless. It works by breaking the three cases of 1570 | * expansion, reduction and replacement, and solving each of these 1571 | * in the most efficient way possible. 1572 | */ 1573 | 1574 | typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2); 1575 | 1576 | #define INITIAL_STATIC_FIND_INDEX_COUNT 32 1577 | 1578 | static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) { 1579 | int i, ret, slen, mlen, delta, acc; 1580 | int * d; 1581 | int static_d[INITIAL_STATIC_FIND_INDEX_COUNT+1]; /* This +1 is unnecessary, but it shuts up LINT. */ 1582 | ptrdiff_t pd; 1583 | bstring auxf = (bstring) find; 1584 | bstring auxr = (bstring) repl; 1585 | 1586 | if (b == NULL || b->data == NULL || find == NULL || 1587 | find->data == NULL || repl == NULL || repl->data == NULL || 1588 | pos < 0 || find->slen <= 0 || b->mlen <= 0 || b->slen > b->mlen || 1589 | b->slen < 0 || repl->slen < 0) return BSTR_ERR; 1590 | if (pos > b->slen - find->slen) return BSTR_OK; 1591 | 1592 | /* Alias with find string */ 1593 | pd = (ptrdiff_t) (find->data - b->data); 1594 | if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) { 1595 | if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR; 1596 | } 1597 | 1598 | /* Alias with repl string */ 1599 | pd = (ptrdiff_t) (repl->data - b->data); 1600 | if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) { 1601 | if (NULL == (auxr = bstrcpy (repl))) { 1602 | if (auxf != find) bdestroy (auxf); 1603 | return BSTR_ERR; 1604 | } 1605 | } 1606 | 1607 | delta = auxf->slen - auxr->slen; 1608 | 1609 | /* in-place replacement since find and replace strings are of equal 1610 | length */ 1611 | if (delta == 0) { 1612 | while ((pos = instr (b, pos, auxf)) >= 0) { 1613 | bstr__memcpy (b->data + pos, auxr->data, auxr->slen); 1614 | pos += auxf->slen; 1615 | } 1616 | if (auxf != find) bdestroy (auxf); 1617 | if (auxr != repl) bdestroy (auxr); 1618 | return BSTR_OK; 1619 | } 1620 | 1621 | /* shrinking replacement since auxf->slen > auxr->slen */ 1622 | if (delta > 0) { 1623 | acc = 0; 1624 | 1625 | while ((i = instr (b, pos, auxf)) >= 0) { 1626 | if (acc && i > pos) 1627 | bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); 1628 | if (auxr->slen) 1629 | bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen); 1630 | acc += delta; 1631 | pos = i + auxf->slen; 1632 | } 1633 | 1634 | if (acc) { 1635 | i = b->slen; 1636 | if (i > pos) 1637 | bstr__memmove (b->data + pos - acc, b->data + pos, i - pos); 1638 | b->slen -= acc; 1639 | b->data[b->slen] = (unsigned char) '\0'; 1640 | } 1641 | 1642 | if (auxf != find) bdestroy (auxf); 1643 | if (auxr != repl) bdestroy (auxr); 1644 | return BSTR_OK; 1645 | } 1646 | 1647 | /* expanding replacement since find->slen < repl->slen. Its a lot 1648 | more complicated. This works by first finding all the matches and 1649 | storing them to a growable array, then doing at most one resize of 1650 | the destination bstring and then performing the direct memory transfers 1651 | of the string segment pieces to form the final result. The growable 1652 | array of matches uses a deferred doubling reallocing strategy. What 1653 | this means is that it starts as a reasonably fixed sized auto array in 1654 | the hopes that many if not most cases will never need to grow this 1655 | array. But it switches as soon as the bounds of the array will be 1656 | exceeded. An extra find result is always appended to this array that 1657 | corresponds to the end of the destination string, so slen is checked 1658 | against mlen - 1 rather than mlen before resizing. 1659 | */ 1660 | 1661 | mlen = INITIAL_STATIC_FIND_INDEX_COUNT; 1662 | d = (int *) static_d; /* Avoid malloc for trivial/initial cases */ 1663 | acc = slen = 0; 1664 | 1665 | while ((pos = instr (b, pos, auxf)) >= 0) { 1666 | if (slen >= mlen - 1) { 1667 | int *t; 1668 | int sl; 1669 | mlen += mlen; 1670 | sl = sizeof (int *) * mlen; 1671 | if (static_d == d) d = NULL; /* static_d cannot be realloced */ 1672 | if (mlen <= 0 || sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) { 1673 | ret = BSTR_ERR; 1674 | goto done; 1675 | } 1676 | if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d)); 1677 | d = t; 1678 | } 1679 | d[slen] = pos; 1680 | slen++; 1681 | acc -= delta; 1682 | pos += auxf->slen; 1683 | if (pos < 0 || acc < 0) { 1684 | ret = BSTR_ERR; 1685 | goto done; 1686 | } 1687 | } 1688 | 1689 | /* slen <= INITIAL_STATIC_INDEX_COUNT-1 or mlen-1 here. */ 1690 | d[slen] = b->slen; 1691 | 1692 | if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) { 1693 | b->slen += acc; 1694 | for (i = slen-1; i >= 0; i--) { 1695 | int s, l; 1696 | s = d[i] + auxf->slen; 1697 | l = d[i+1] - s; /* d[slen] may be accessed here. */ 1698 | if (l) { 1699 | bstr__memmove (b->data + s + acc, b->data + s, l); 1700 | } 1701 | if (auxr->slen) { 1702 | bstr__memmove (b->data + s + acc - auxr->slen, 1703 | auxr->data, auxr->slen); 1704 | } 1705 | acc += delta; 1706 | } 1707 | b->data[b->slen] = (unsigned char) '\0'; 1708 | } 1709 | 1710 | done:; 1711 | if (static_d != d) bstr__free (d); 1712 | if (auxf != find) bdestroy (auxf); 1713 | if (auxr != repl) bdestroy (auxr); 1714 | return ret; 1715 | } 1716 | 1717 | /* int bfindreplace (bstring b, const_bstring find, const_bstring repl, 1718 | * int pos) 1719 | * 1720 | * Replace all occurrences of a find string with a replace string after a 1721 | * given point in a bstring. 1722 | */ 1723 | int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) { 1724 | return findreplaceengine (b, find, repl, pos, binstr); 1725 | } 1726 | 1727 | /* int bfindreplacecaseless (bstring b, const_bstring find, 1728 | * const_bstring repl, int pos) 1729 | * 1730 | * Replace all occurrences of a find string, ignoring case, with a replace 1731 | * string after a given point in a bstring. 1732 | */ 1733 | int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) { 1734 | return findreplaceengine (b, find, repl, pos, binstrcaseless); 1735 | } 1736 | 1737 | /* int binsertch (bstring b, int pos, int len, unsigned char fill) 1738 | * 1739 | * Inserts the character fill repeatedly into b at position pos for a 1740 | * length len. If the position pos is past the end of b, then the 1741 | * character "fill" is appended as necessary to make up the gap between the 1742 | * end of b and the position pos + len. 1743 | */ 1744 | int binsertch (bstring b, int pos, int len, unsigned char fill) { 1745 | int d, l, i; 1746 | 1747 | if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen || 1748 | b->mlen <= 0 || len < 0) return BSTR_ERR; 1749 | 1750 | /* Compute the two possible end pointers */ 1751 | d = b->slen + len; 1752 | l = pos + len; 1753 | if ((d|l) < 0) return BSTR_ERR; 1754 | 1755 | if (l > d) { 1756 | /* Inserting past the end of the string */ 1757 | if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR; 1758 | pos = b->slen; 1759 | b->slen = l; 1760 | } else { 1761 | /* Inserting in the middle of the string */ 1762 | if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR; 1763 | for (i = d - 1; i >= l; i--) { 1764 | b->data[i] = b->data[i - len]; 1765 | } 1766 | b->slen = d; 1767 | } 1768 | 1769 | for (i=pos; i < l; i++) b->data[i] = fill; 1770 | b->data[b->slen] = (unsigned char) '\0'; 1771 | return BSTR_OK; 1772 | } 1773 | 1774 | /* int bpattern (bstring b, int len) 1775 | * 1776 | * Replicate the bstring, b in place, end to end repeatedly until it 1777 | * surpasses len characters, then chop the result to exactly len characters. 1778 | * This function operates in-place. The function will return with BSTR_ERR 1779 | * if b is NULL or of length 0, otherwise BSTR_OK is returned. 1780 | */ 1781 | int bpattern (bstring b, int len) { 1782 | int i, d; 1783 | 1784 | d = blength (b); 1785 | if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR; 1786 | if (len > 0) { 1787 | if (d == 1) return bsetstr (b, len, NULL, b->data[0]); 1788 | for (i = d; i < len; i++) b->data[i] = b->data[i - d]; 1789 | } 1790 | b->data[len] = (unsigned char) '\0'; 1791 | b->slen = len; 1792 | return BSTR_OK; 1793 | } 1794 | 1795 | #define BS_BUFF_SZ (1024) 1796 | 1797 | /* int breada (bstring b, bNread readPtr, void * parm) 1798 | * 1799 | * Use a finite buffer fread-like function readPtr to concatenate to the 1800 | * bstring b the entire contents of file-like source data in a roughly 1801 | * efficient way. 1802 | */ 1803 | int breada (bstring b, bNread readPtr, void * parm) { 1804 | int i, l, n; 1805 | 1806 | if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || 1807 | readPtr == NULL) return BSTR_ERR; 1808 | 1809 | i = b->slen; 1810 | for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) { 1811 | if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR; 1812 | l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm); 1813 | i += l; 1814 | b->slen = i; 1815 | if (i < n) break; 1816 | } 1817 | 1818 | b->data[i] = (unsigned char) '\0'; 1819 | return BSTR_OK; 1820 | } 1821 | 1822 | /* bstring bread (bNread readPtr, void * parm) 1823 | * 1824 | * Use a finite buffer fread-like function readPtr to create a bstring 1825 | * filled with the entire contents of file-like source data in a roughly 1826 | * efficient way. 1827 | */ 1828 | bstring bread (bNread readPtr, void * parm) { 1829 | bstring buff; 1830 | 1831 | if (0 > breada (buff = bfromcstr (""), readPtr, parm)) { 1832 | bdestroy (buff); 1833 | return NULL; 1834 | } 1835 | return buff; 1836 | } 1837 | 1838 | /* int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) 1839 | * 1840 | * Use an fgetc-like single character stream reading function (getcPtr) to 1841 | * obtain a sequence of characters which are concatenated to the end of the 1842 | * bstring b. The stream read is terminated by the passed in terminator 1843 | * parameter. 1844 | * 1845 | * If getcPtr returns with a negative number, or the terminator character 1846 | * (which is appended) is read, then the stream reading is halted and the 1847 | * function returns with a partial result in b. If there is an empty partial 1848 | * result, 1 is returned. If no characters are read, or there is some other 1849 | * detectable error, BSTR_ERR is returned. 1850 | */ 1851 | int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) { 1852 | int c, d, e; 1853 | 1854 | if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || 1855 | getcPtr == NULL) return BSTR_ERR; 1856 | d = 0; 1857 | e = b->mlen - 2; 1858 | 1859 | while ((c = getcPtr (parm)) >= 0) { 1860 | if (d > e) { 1861 | b->slen = d; 1862 | if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; 1863 | e = b->mlen - 2; 1864 | } 1865 | b->data[d] = (unsigned char) c; 1866 | d++; 1867 | if (c == terminator) break; 1868 | } 1869 | 1870 | b->data[d] = (unsigned char) '\0'; 1871 | b->slen = d; 1872 | 1873 | return d == 0 && c < 0; 1874 | } 1875 | 1876 | /* int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) 1877 | * 1878 | * Use an fgetc-like single character stream reading function (getcPtr) to 1879 | * obtain a sequence of characters which are concatenated to the end of the 1880 | * bstring b. The stream read is terminated by the passed in terminator 1881 | * parameter. 1882 | * 1883 | * If getcPtr returns with a negative number, or the terminator character 1884 | * (which is appended) is read, then the stream reading is halted and the 1885 | * function returns with a partial result concatentated to b. If there is 1886 | * an empty partial result, 1 is returned. If no characters are read, or 1887 | * there is some other detectable error, BSTR_ERR is returned. 1888 | */ 1889 | int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) { 1890 | int c, d, e; 1891 | 1892 | if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen || 1893 | getcPtr == NULL) return BSTR_ERR; 1894 | d = b->slen; 1895 | e = b->mlen - 2; 1896 | 1897 | while ((c = getcPtr (parm)) >= 0) { 1898 | if (d > e) { 1899 | b->slen = d; 1900 | if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR; 1901 | e = b->mlen - 2; 1902 | } 1903 | b->data[d] = (unsigned char) c; 1904 | d++; 1905 | if (c == terminator) break; 1906 | } 1907 | 1908 | b->data[d] = (unsigned char) '\0'; 1909 | b->slen = d; 1910 | 1911 | return d == 0 && c < 0; 1912 | } 1913 | 1914 | /* bstring bgets (bNgetc getcPtr, void * parm, char terminator) 1915 | * 1916 | * Use an fgetc-like single character stream reading function (getcPtr) to 1917 | * obtain a sequence of characters which are concatenated into a bstring. 1918 | * The stream read is terminated by the passed in terminator function. 1919 | * 1920 | * If getcPtr returns with a negative number, or the terminator character 1921 | * (which is appended) is read, then the stream reading is halted and the 1922 | * result obtained thus far is returned. If no characters are read, or 1923 | * there is some other detectable error, NULL is returned. 1924 | */ 1925 | bstring bgets (bNgetc getcPtr, void * parm, char terminator) { 1926 | bstring buff; 1927 | 1928 | if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) { 1929 | bdestroy (buff); 1930 | buff = NULL; 1931 | } 1932 | return buff; 1933 | } 1934 | 1935 | struct bStream { 1936 | bstring buff; /* Buffer for over-reads */ 1937 | void * parm; /* The stream handle for core stream */ 1938 | bNread readFnPtr; /* fread compatible fnptr for core stream */ 1939 | int isEOF; /* track file's EOF state */ 1940 | int maxBuffSz; 1941 | }; 1942 | 1943 | /* struct bStream * bsopen (bNread readPtr, void * parm) 1944 | * 1945 | * Wrap a given open stream (described by a fread compatible function 1946 | * pointer and stream handle) into an open bStream suitable for the bstring 1947 | * library streaming functions. 1948 | */ 1949 | struct bStream * bsopen (bNread readPtr, void * parm) { 1950 | struct bStream * s; 1951 | 1952 | if (readPtr == NULL) return NULL; 1953 | s = (struct bStream *) bstr__alloc (sizeof (struct bStream)); 1954 | if (s == NULL) return NULL; 1955 | s->parm = parm; 1956 | s->buff = bfromcstr (""); 1957 | s->readFnPtr = readPtr; 1958 | s->maxBuffSz = BS_BUFF_SZ; 1959 | s->isEOF = 0; 1960 | return s; 1961 | } 1962 | 1963 | /* int bsbufflength (struct bStream * s, int sz) 1964 | * 1965 | * Set the length of the buffer used by the bStream. If sz is zero, the 1966 | * length is not set. This function returns with the previous length. 1967 | */ 1968 | int bsbufflength (struct bStream * s, int sz) { 1969 | int oldSz; 1970 | if (s == NULL || sz < 0) return BSTR_ERR; 1971 | oldSz = s->maxBuffSz; 1972 | if (sz > 0) s->maxBuffSz = sz; 1973 | return oldSz; 1974 | } 1975 | 1976 | int bseof (const struct bStream * s) { 1977 | if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR; 1978 | return s->isEOF && (s->buff->slen == 0); 1979 | } 1980 | 1981 | /* void * bsclose (struct bStream * s) 1982 | * 1983 | * Close the bStream, and return the handle to the stream that was originally 1984 | * used to open the given stream. 1985 | */ 1986 | void * bsclose (struct bStream * s) { 1987 | void * parm; 1988 | if (s == NULL) return NULL; 1989 | s->readFnPtr = NULL; 1990 | if (s->buff) bdestroy (s->buff); 1991 | s->buff = NULL; 1992 | parm = s->parm; 1993 | s->parm = NULL; 1994 | s->isEOF = 1; 1995 | bstr__free (s); 1996 | return parm; 1997 | } 1998 | 1999 | /* int bsreadlna (bstring r, struct bStream * s, char terminator) 2000 | * 2001 | * Read a bstring terminated by the terminator character or the end of the 2002 | * stream from the bStream (s) and return it into the parameter r. This 2003 | * function may read additional characters from the core stream that are not 2004 | * returned, but will be retained for subsequent read operations. 2005 | */ 2006 | int bsreadlna (bstring r, struct bStream * s, char terminator) { 2007 | int i, l, ret, rlo; 2008 | char * b; 2009 | struct tagbstring x; 2010 | 2011 | if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 || 2012 | r->slen < 0 || r->mlen < r->slen) return BSTR_ERR; 2013 | l = s->buff->slen; 2014 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2015 | b = (char *) s->buff->data; 2016 | x.data = (unsigned char *) b; 2017 | 2018 | /* First check if the current buffer holds the terminator */ 2019 | b[l] = terminator; /* Set sentinel */ 2020 | for (i=0; b[i] != terminator; i++) ; 2021 | if (i < l) { 2022 | x.slen = i + 1; 2023 | ret = bconcat (r, &x); 2024 | s->buff->slen = l; 2025 | if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); 2026 | return BSTR_OK; 2027 | } 2028 | 2029 | rlo = r->slen; 2030 | 2031 | /* If not then just concatenate the entire buffer to the output */ 2032 | x.slen = l; 2033 | if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; 2034 | 2035 | /* Perform direct in-place reads into the destination to allow for 2036 | the minimum of data-copies */ 2037 | for (;;) { 2038 | if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; 2039 | b = (char *) (r->data + r->slen); 2040 | l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); 2041 | if (l <= 0) { 2042 | r->data[r->slen] = (unsigned char) '\0'; 2043 | s->buff->slen = 0; 2044 | s->isEOF = 1; 2045 | /* If nothing was read return with an error message */ 2046 | return BSTR_ERR & -(r->slen == rlo); 2047 | } 2048 | b[l] = terminator; /* Set sentinel */ 2049 | for (i=0; b[i] != terminator; i++) ; 2050 | if (i < l) break; 2051 | r->slen += l; 2052 | } 2053 | 2054 | /* Terminator found, push over-read back to buffer */ 2055 | i++; 2056 | r->slen += i; 2057 | s->buff->slen = l - i; 2058 | bstr__memcpy (s->buff->data, b + i, l - i); 2059 | r->data[r->slen] = (unsigned char) '\0'; 2060 | return BSTR_OK; 2061 | } 2062 | 2063 | /* int bsreadlnsa (bstring r, struct bStream * s, bstring term) 2064 | * 2065 | * Read a bstring terminated by any character in the term string or the end 2066 | * of the stream from the bStream (s) and return it into the parameter r. 2067 | * This function may read additional characters from the core stream that 2068 | * are not returned, but will be retained for subsequent read operations. 2069 | */ 2070 | int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) { 2071 | int i, l, ret, rlo; 2072 | unsigned char * b; 2073 | struct tagbstring x; 2074 | struct charField cf; 2075 | 2076 | if (s == NULL || s->buff == NULL || r == NULL || term == NULL || 2077 | term->data == NULL || r->mlen <= 0 || r->slen < 0 || 2078 | r->mlen < r->slen) return BSTR_ERR; 2079 | if (term->slen == 1) return bsreadlna (r, s, term->data[0]); 2080 | if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR; 2081 | 2082 | l = s->buff->slen; 2083 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2084 | b = (unsigned char *) s->buff->data; 2085 | x.data = b; 2086 | 2087 | /* First check if the current buffer holds the terminator */ 2088 | b[l] = term->data[0]; /* Set sentinel */ 2089 | for (i=0; !testInCharField (&cf, b[i]); i++) ; 2090 | if (i < l) { 2091 | x.slen = i + 1; 2092 | ret = bconcat (r, &x); 2093 | s->buff->slen = l; 2094 | if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1); 2095 | return BSTR_OK; 2096 | } 2097 | 2098 | rlo = r->slen; 2099 | 2100 | /* If not then just concatenate the entire buffer to the output */ 2101 | x.slen = l; 2102 | if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR; 2103 | 2104 | /* Perform direct in-place reads into the destination to allow for 2105 | the minimum of data-copies */ 2106 | for (;;) { 2107 | if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR; 2108 | b = (unsigned char *) (r->data + r->slen); 2109 | l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm); 2110 | if (l <= 0) { 2111 | r->data[r->slen] = (unsigned char) '\0'; 2112 | s->buff->slen = 0; 2113 | s->isEOF = 1; 2114 | /* If nothing was read return with an error message */ 2115 | return BSTR_ERR & -(r->slen == rlo); 2116 | } 2117 | 2118 | b[l] = term->data[0]; /* Set sentinel */ 2119 | for (i=0; !testInCharField (&cf, b[i]); i++) ; 2120 | if (i < l) break; 2121 | r->slen += l; 2122 | } 2123 | 2124 | /* Terminator found, push over-read back to buffer */ 2125 | i++; 2126 | r->slen += i; 2127 | s->buff->slen = l - i; 2128 | bstr__memcpy (s->buff->data, b + i, l - i); 2129 | r->data[r->slen] = (unsigned char) '\0'; 2130 | return BSTR_OK; 2131 | } 2132 | 2133 | /* int bsreada (bstring r, struct bStream * s, int n) 2134 | * 2135 | * Read a bstring of length n (or, if it is fewer, as many bytes as is 2136 | * remaining) from the bStream. This function may read additional 2137 | * characters from the core stream that are not returned, but will be 2138 | * retained for subsequent read operations. This function will not read 2139 | * additional characters from the core stream beyond virtual stream pointer. 2140 | */ 2141 | int bsreada (bstring r, struct bStream * s, int n) { 2142 | int l, ret, orslen; 2143 | char * b; 2144 | struct tagbstring x; 2145 | 2146 | if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 2147 | || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR; 2148 | 2149 | n += r->slen; 2150 | if (n <= 0) return BSTR_ERR; 2151 | 2152 | l = s->buff->slen; 2153 | 2154 | orslen = r->slen; 2155 | 2156 | if (0 == l) { 2157 | if (s->isEOF) return BSTR_ERR; 2158 | if (r->mlen > n) { 2159 | l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm); 2160 | if (0 >= l || l > n - r->slen) { 2161 | s->isEOF = 1; 2162 | return BSTR_ERR; 2163 | } 2164 | r->slen += l; 2165 | r->data[r->slen] = (unsigned char) '\0'; 2166 | return 0; 2167 | } 2168 | } 2169 | 2170 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2171 | b = (char *) s->buff->data; 2172 | x.data = (unsigned char *) b; 2173 | 2174 | do { 2175 | if (l + r->slen >= n) { 2176 | x.slen = n - r->slen; 2177 | ret = bconcat (r, &x); 2178 | s->buff->slen = l; 2179 | if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen); 2180 | return BSTR_ERR & -(r->slen == orslen); 2181 | } 2182 | 2183 | x.slen = l; 2184 | if (BSTR_OK != bconcat (r, &x)) break; 2185 | 2186 | l = n - r->slen; 2187 | if (l > s->maxBuffSz) l = s->maxBuffSz; 2188 | 2189 | l = (int) s->readFnPtr (b, 1, l, s->parm); 2190 | 2191 | } while (l > 0); 2192 | if (l < 0) l = 0; 2193 | if (l == 0) s->isEOF = 1; 2194 | s->buff->slen = l; 2195 | return BSTR_ERR & -(r->slen == orslen); 2196 | } 2197 | 2198 | /* int bsreadln (bstring r, struct bStream * s, char terminator) 2199 | * 2200 | * Read a bstring terminated by the terminator character or the end of the 2201 | * stream from the bStream (s) and return it into the parameter r. This 2202 | * function may read additional characters from the core stream that are not 2203 | * returned, but will be retained for subsequent read operations. 2204 | */ 2205 | int bsreadln (bstring r, struct bStream * s, char terminator) { 2206 | if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0) 2207 | return BSTR_ERR; 2208 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2209 | r->slen = 0; 2210 | return bsreadlna (r, s, terminator); 2211 | } 2212 | 2213 | /* int bsreadlns (bstring r, struct bStream * s, bstring term) 2214 | * 2215 | * Read a bstring terminated by any character in the term string or the end 2216 | * of the stream from the bStream (s) and return it into the parameter r. 2217 | * This function may read additional characters from the core stream that 2218 | * are not returned, but will be retained for subsequent read operations. 2219 | */ 2220 | int bsreadlns (bstring r, struct bStream * s, const_bstring term) { 2221 | if (s == NULL || s->buff == NULL || r == NULL || term == NULL 2222 | || term->data == NULL || r->mlen <= 0) return BSTR_ERR; 2223 | if (term->slen == 1) return bsreadln (r, s, term->data[0]); 2224 | if (term->slen < 1) return BSTR_ERR; 2225 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2226 | r->slen = 0; 2227 | return bsreadlnsa (r, s, term); 2228 | } 2229 | 2230 | /* int bsread (bstring r, struct bStream * s, int n) 2231 | * 2232 | * Read a bstring of length n (or, if it is fewer, as many bytes as is 2233 | * remaining) from the bStream. This function may read additional 2234 | * characters from the core stream that are not returned, but will be 2235 | * retained for subsequent read operations. This function will not read 2236 | * additional characters from the core stream beyond virtual stream pointer. 2237 | */ 2238 | int bsread (bstring r, struct bStream * s, int n) { 2239 | if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 2240 | || n <= 0) return BSTR_ERR; 2241 | if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR; 2242 | r->slen = 0; 2243 | return bsreada (r, s, n); 2244 | } 2245 | 2246 | /* int bsunread (struct bStream * s, const_bstring b) 2247 | * 2248 | * Insert a bstring into the bStream at the current position. These 2249 | * characters will be read prior to those that actually come from the core 2250 | * stream. 2251 | */ 2252 | int bsunread (struct bStream * s, const_bstring b) { 2253 | if (s == NULL || s->buff == NULL) return BSTR_ERR; 2254 | return binsert (s->buff, 0, b, (unsigned char) '?'); 2255 | } 2256 | 2257 | /* int bspeek (bstring r, const struct bStream * s) 2258 | * 2259 | * Return the currently buffered characters from the bStream that will be 2260 | * read prior to reads from the core stream. 2261 | */ 2262 | int bspeek (bstring r, const struct bStream * s) { 2263 | if (s == NULL || s->buff == NULL) return BSTR_ERR; 2264 | return bassign (r, s->buff); 2265 | } 2266 | 2267 | /* bstring bjoin (const struct bstrList * bl, const_bstring sep); 2268 | * 2269 | * Join the entries of a bstrList into one bstring by sequentially 2270 | * concatenating them with the sep string in between. If there is an error 2271 | * NULL is returned, otherwise a bstring with the correct result is returned. 2272 | */ 2273 | bstring bjoin (const struct bstrList * bl, const_bstring sep) { 2274 | bstring b; 2275 | int i, c, v; 2276 | 2277 | if (bl == NULL || bl->qty < 0) return NULL; 2278 | if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL; 2279 | 2280 | for (i = 0, c = 1; i < bl->qty; i++) { 2281 | v = bl->entry[i]->slen; 2282 | if (v < 0) return NULL; /* Invalid input */ 2283 | c += v; 2284 | if (c < 0) return NULL; /* Wrap around ?? */ 2285 | } 2286 | 2287 | if (sep != NULL) c += (bl->qty - 1) * sep->slen; 2288 | 2289 | b = (bstring) bstr__alloc (sizeof (struct tagbstring)); 2290 | if (NULL == b) return NULL; /* Out of memory */ 2291 | b->data = (unsigned char *) bstr__alloc (c); 2292 | if (b->data == NULL) { 2293 | bstr__free (b); 2294 | return NULL; 2295 | } 2296 | 2297 | b->mlen = c; 2298 | b->slen = c-1; 2299 | 2300 | for (i = 0, c = 0; i < bl->qty; i++) { 2301 | if (i > 0 && sep != NULL) { 2302 | bstr__memcpy (b->data + c, sep->data, sep->slen); 2303 | c += sep->slen; 2304 | } 2305 | v = bl->entry[i]->slen; 2306 | bstr__memcpy (b->data + c, bl->entry[i]->data, v); 2307 | c += v; 2308 | } 2309 | b->data[c] = (unsigned char) '\0'; 2310 | return b; 2311 | } 2312 | 2313 | #define BSSSC_BUFF_LEN (256) 2314 | 2315 | /* int bssplitscb (struct bStream * s, const_bstring splitStr, 2316 | * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) 2317 | * 2318 | * Iterate the set of disjoint sequential substrings read from a stream 2319 | * divided by any of the characters in splitStr. An empty splitStr causes 2320 | * the whole stream to be iterated once. 2321 | * 2322 | * Note: At the point of calling the cb function, the bStream pointer is 2323 | * pointed exactly at the position right after having read the split 2324 | * character. The cb function can act on the stream by causing the bStream 2325 | * pointer to move, and bssplitscb will continue by starting the next split 2326 | * at the position of the pointer after the return from cb. 2327 | * 2328 | * However, if the cb causes the bStream s to be destroyed then the cb must 2329 | * return with a negative value, otherwise bssplitscb will continue in an 2330 | * undefined manner. 2331 | */ 2332 | int bssplitscb (struct bStream * s, const_bstring splitStr, 2333 | int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { 2334 | struct charField chrs; 2335 | bstring buff; 2336 | int i, p, ret; 2337 | 2338 | if (cb == NULL || s == NULL || s->readFnPtr == NULL 2339 | || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; 2340 | 2341 | if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; 2342 | 2343 | if (splitStr->slen == 0) { 2344 | while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ; 2345 | if ((ret = cb (parm, 0, buff)) > 0) 2346 | ret = 0; 2347 | } else { 2348 | buildCharField (&chrs, splitStr); 2349 | ret = p = i = 0; 2350 | for (;;) { 2351 | if (i >= buff->slen) { 2352 | bsreada (buff, s, BSSSC_BUFF_LEN); 2353 | if (i >= buff->slen) { 2354 | if (0 < (ret = cb (parm, p, buff))) ret = 0; 2355 | break; 2356 | } 2357 | } 2358 | if (testInCharField (&chrs, buff->data[i])) { 2359 | struct tagbstring t; 2360 | unsigned char c; 2361 | 2362 | blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1)); 2363 | if ((ret = bsunread (s, &t)) < 0) break; 2364 | buff->slen = i; 2365 | c = buff->data[i]; 2366 | buff->data[i] = (unsigned char) '\0'; 2367 | if ((ret = cb (parm, p, buff)) < 0) break; 2368 | buff->data[i] = c; 2369 | buff->slen = 0; 2370 | p += i + 1; 2371 | i = -1; 2372 | } 2373 | i++; 2374 | } 2375 | } 2376 | 2377 | bdestroy (buff); 2378 | return ret; 2379 | } 2380 | 2381 | /* int bssplitstrcb (struct bStream * s, const_bstring splitStr, 2382 | * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) 2383 | * 2384 | * Iterate the set of disjoint sequential substrings read from a stream 2385 | * divided by the entire substring splitStr. An empty splitStr causes 2386 | * each character of the stream to be iterated. 2387 | * 2388 | * Note: At the point of calling the cb function, the bStream pointer is 2389 | * pointed exactly at the position right after having read the split 2390 | * character. The cb function can act on the stream by causing the bStream 2391 | * pointer to move, and bssplitscb will continue by starting the next split 2392 | * at the position of the pointer after the return from cb. 2393 | * 2394 | * However, if the cb causes the bStream s to be destroyed then the cb must 2395 | * return with a negative value, otherwise bssplitscb will continue in an 2396 | * undefined manner. 2397 | */ 2398 | int bssplitstrcb (struct bStream * s, const_bstring splitStr, 2399 | int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) { 2400 | bstring buff; 2401 | int i, p, ret; 2402 | 2403 | if (cb == NULL || s == NULL || s->readFnPtr == NULL 2404 | || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; 2405 | 2406 | if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm); 2407 | 2408 | if (NULL == (buff = bfromcstr (""))) return BSTR_ERR; 2409 | 2410 | if (splitStr->slen == 0) { 2411 | for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) { 2412 | if ((ret = cb (parm, 0, buff)) < 0) { 2413 | bdestroy (buff); 2414 | return ret; 2415 | } 2416 | buff->slen = 0; 2417 | } 2418 | return BSTR_OK; 2419 | } else { 2420 | ret = p = i = 0; 2421 | for (i=p=0;;) { 2422 | if ((ret = binstr (buff, 0, splitStr)) >= 0) { 2423 | struct tagbstring t; 2424 | blk2tbstr (t, buff->data, ret); 2425 | i = ret + splitStr->slen; 2426 | if ((ret = cb (parm, p, &t)) < 0) break; 2427 | p += i; 2428 | bdelete (buff, 0, i); 2429 | } else { 2430 | bsreada (buff, s, BSSSC_BUFF_LEN); 2431 | if (bseof (s)) { 2432 | if ((ret = cb (parm, p, buff)) > 0) ret = 0; 2433 | break; 2434 | } 2435 | } 2436 | } 2437 | } 2438 | 2439 | bdestroy (buff); 2440 | return ret; 2441 | } 2442 | 2443 | /* int bstrListCreate (void) 2444 | * 2445 | * Create a bstrList. 2446 | */ 2447 | struct bstrList * bstrListCreate (void) { 2448 | struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); 2449 | if (sl) { 2450 | sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring)); 2451 | if (!sl->entry) { 2452 | bstr__free (sl); 2453 | sl = NULL; 2454 | } else { 2455 | sl->qty = 0; 2456 | sl->mlen = 1; 2457 | } 2458 | } 2459 | return sl; 2460 | } 2461 | 2462 | /* int bstrListDestroy (struct bstrList * sl) 2463 | * 2464 | * Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate. 2465 | */ 2466 | int bstrListDestroy (struct bstrList * sl) { 2467 | int i; 2468 | if (sl == NULL || sl->qty < 0) return BSTR_ERR; 2469 | for (i=0; i < sl->qty; i++) { 2470 | if (sl->entry[i]) { 2471 | bdestroy (sl->entry[i]); 2472 | sl->entry[i] = NULL; 2473 | } 2474 | } 2475 | sl->qty = -1; 2476 | sl->mlen = -1; 2477 | bstr__free (sl->entry); 2478 | sl->entry = NULL; 2479 | bstr__free (sl); 2480 | return BSTR_OK; 2481 | } 2482 | 2483 | /* int bstrListAlloc (struct bstrList * sl, int msz) 2484 | * 2485 | * Ensure that there is memory for at least msz number of entries for the 2486 | * list. 2487 | */ 2488 | int bstrListAlloc (struct bstrList * sl, int msz) { 2489 | bstring * l; 2490 | int smsz; 2491 | size_t nsz; 2492 | if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; 2493 | if (sl->mlen >= msz) return BSTR_OK; 2494 | smsz = snapUpSize (msz); 2495 | nsz = ((size_t) smsz) * sizeof (bstring); 2496 | if (nsz < (size_t) smsz) return BSTR_ERR; 2497 | l = (bstring *) bstr__realloc (sl->entry, nsz); 2498 | if (!l) { 2499 | smsz = msz; 2500 | nsz = ((size_t) smsz) * sizeof (bstring); 2501 | l = (bstring *) bstr__realloc (sl->entry, nsz); 2502 | if (!l) return BSTR_ERR; 2503 | } 2504 | sl->mlen = smsz; 2505 | sl->entry = l; 2506 | return BSTR_OK; 2507 | } 2508 | 2509 | /* int bstrListAllocMin (struct bstrList * sl, int msz) 2510 | * 2511 | * Try to allocate the minimum amount of memory for the list to include at 2512 | * least msz entries or sl->qty whichever is greater. 2513 | */ 2514 | int bstrListAllocMin (struct bstrList * sl, int msz) { 2515 | bstring * l; 2516 | size_t nsz; 2517 | if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR; 2518 | if (msz < sl->qty) msz = sl->qty; 2519 | if (sl->mlen == msz) return BSTR_OK; 2520 | nsz = ((size_t) msz) * sizeof (bstring); 2521 | if (nsz < (size_t) msz) return BSTR_ERR; 2522 | l = (bstring *) bstr__realloc (sl->entry, nsz); 2523 | if (!l) return BSTR_ERR; 2524 | sl->mlen = msz; 2525 | sl->entry = l; 2526 | return BSTR_OK; 2527 | } 2528 | 2529 | /* int bsplitcb (const_bstring str, unsigned char splitChar, int pos, 2530 | * int (* cb) (void * parm, int ofs, int len), void * parm) 2531 | * 2532 | * Iterate the set of disjoint sequential substrings over str divided by the 2533 | * character in splitChar. 2534 | * 2535 | * Note: Non-destructive modification of str from within the cb function 2536 | * while performing this split is not undefined. bsplitcb behaves in 2537 | * sequential lock step with calls to cb. I.e., after returning from a cb 2538 | * that return a non-negative integer, bsplitcb continues from the position 2539 | * 1 character after the last detected split character and it will halt 2540 | * immediately if the length of str falls below this point. However, if the 2541 | * cb function destroys str, then it *must* return with a negative value, 2542 | * otherwise bsplitcb will continue in an undefined manner. 2543 | */ 2544 | int bsplitcb (const_bstring str, unsigned char splitChar, int pos, 2545 | int (* cb) (void * parm, int ofs, int len), void * parm) { 2546 | int i, p, ret; 2547 | 2548 | if (cb == NULL || str == NULL || pos < 0 || pos > str->slen) 2549 | return BSTR_ERR; 2550 | 2551 | p = pos; 2552 | do { 2553 | for (i=p; i < str->slen; i++) { 2554 | if (str->data[i] == splitChar) break; 2555 | } 2556 | if ((ret = cb (parm, p, i - p)) < 0) return ret; 2557 | p = i + 1; 2558 | } while (p <= str->slen); 2559 | return BSTR_OK; 2560 | } 2561 | 2562 | /* int bsplitscb (const_bstring str, const_bstring splitStr, int pos, 2563 | * int (* cb) (void * parm, int ofs, int len), void * parm) 2564 | * 2565 | * Iterate the set of disjoint sequential substrings over str divided by any 2566 | * of the characters in splitStr. An empty splitStr causes the whole str to 2567 | * be iterated once. 2568 | * 2569 | * Note: Non-destructive modification of str from within the cb function 2570 | * while performing this split is not undefined. bsplitscb behaves in 2571 | * sequential lock step with calls to cb. I.e., after returning from a cb 2572 | * that return a non-negative integer, bsplitscb continues from the position 2573 | * 1 character after the last detected split character and it will halt 2574 | * immediately if the length of str falls below this point. However, if the 2575 | * cb function destroys str, then it *must* return with a negative value, 2576 | * otherwise bsplitscb will continue in an undefined manner. 2577 | */ 2578 | int bsplitscb (const_bstring str, const_bstring splitStr, int pos, 2579 | int (* cb) (void * parm, int ofs, int len), void * parm) { 2580 | struct charField chrs; 2581 | int i, p, ret; 2582 | 2583 | if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 2584 | || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; 2585 | if (splitStr->slen == 0) { 2586 | if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0; 2587 | return ret; 2588 | } 2589 | 2590 | if (splitStr->slen == 1) 2591 | return bsplitcb (str, splitStr->data[0], pos, cb, parm); 2592 | 2593 | buildCharField (&chrs, splitStr); 2594 | 2595 | p = pos; 2596 | do { 2597 | for (i=p; i < str->slen; i++) { 2598 | if (testInCharField (&chrs, str->data[i])) break; 2599 | } 2600 | if ((ret = cb (parm, p, i - p)) < 0) return ret; 2601 | p = i + 1; 2602 | } while (p <= str->slen); 2603 | return BSTR_OK; 2604 | } 2605 | 2606 | /* int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, 2607 | * int (* cb) (void * parm, int ofs, int len), void * parm) 2608 | * 2609 | * Iterate the set of disjoint sequential substrings over str divided by the 2610 | * substring splitStr. An empty splitStr causes the whole str to be 2611 | * iterated once. 2612 | * 2613 | * Note: Non-destructive modification of str from within the cb function 2614 | * while performing this split is not undefined. bsplitstrcb behaves in 2615 | * sequential lock step with calls to cb. I.e., after returning from a cb 2616 | * that return a non-negative integer, bsplitscb continues from the position 2617 | * 1 character after the last detected split character and it will halt 2618 | * immediately if the length of str falls below this point. However, if the 2619 | * cb function destroys str, then it *must* return with a negative value, 2620 | * otherwise bsplitscb will continue in an undefined manner. 2621 | */ 2622 | int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos, 2623 | int (* cb) (void * parm, int ofs, int len), void * parm) { 2624 | int i, p, ret; 2625 | 2626 | if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 2627 | || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR; 2628 | 2629 | if (0 == splitStr->slen) { 2630 | for (i=pos; i < str->slen; i++) { 2631 | if ((ret = cb (parm, i, 1)) < 0) return ret; 2632 | } 2633 | return BSTR_OK; 2634 | } 2635 | 2636 | if (splitStr->slen == 1) 2637 | return bsplitcb (str, splitStr->data[0], pos, cb, parm); 2638 | 2639 | for (i=p=pos; i <= str->slen - splitStr->slen; i++) { 2640 | if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) { 2641 | if ((ret = cb (parm, p, i - p)) < 0) return ret; 2642 | i += splitStr->slen; 2643 | p = i; 2644 | } 2645 | } 2646 | if ((ret = cb (parm, p, str->slen - p)) < 0) return ret; 2647 | return BSTR_OK; 2648 | } 2649 | 2650 | struct genBstrList { 2651 | bstring b; 2652 | struct bstrList * bl; 2653 | }; 2654 | 2655 | static int bscb (void * parm, int ofs, int len) { 2656 | struct genBstrList * g = (struct genBstrList *) parm; 2657 | if (g->bl->qty >= g->bl->mlen) { 2658 | int mlen = g->bl->mlen * 2; 2659 | bstring * tbl; 2660 | 2661 | while (g->bl->qty >= mlen) { 2662 | if (mlen < g->bl->mlen) return BSTR_ERR; 2663 | mlen += mlen; 2664 | } 2665 | 2666 | tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen); 2667 | if (tbl == NULL) return BSTR_ERR; 2668 | 2669 | g->bl->entry = tbl; 2670 | g->bl->mlen = mlen; 2671 | } 2672 | 2673 | g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len); 2674 | g->bl->qty++; 2675 | return BSTR_OK; 2676 | } 2677 | 2678 | /* struct bstrList * bsplit (const_bstring str, unsigned char splitChar) 2679 | * 2680 | * Create an array of sequential substrings from str divided by the character 2681 | * splitChar. 2682 | */ 2683 | struct bstrList * bsplit (const_bstring str, unsigned char splitChar) { 2684 | struct genBstrList g; 2685 | 2686 | if (str == NULL || str->data == NULL || str->slen < 0) return NULL; 2687 | 2688 | g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); 2689 | if (g.bl == NULL) return NULL; 2690 | g.bl->mlen = 4; 2691 | g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); 2692 | if (NULL == g.bl->entry) { 2693 | bstr__free (g.bl); 2694 | return NULL; 2695 | } 2696 | 2697 | g.b = (bstring) str; 2698 | g.bl->qty = 0; 2699 | if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) { 2700 | bstrListDestroy (g.bl); 2701 | return NULL; 2702 | } 2703 | return g.bl; 2704 | } 2705 | 2706 | /* struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) 2707 | * 2708 | * Create an array of sequential substrings from str divided by the entire 2709 | * substring splitStr. 2710 | */ 2711 | struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) { 2712 | struct genBstrList g; 2713 | 2714 | if (str == NULL || str->data == NULL || str->slen < 0) return NULL; 2715 | 2716 | g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); 2717 | if (g.bl == NULL) return NULL; 2718 | g.bl->mlen = 4; 2719 | g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); 2720 | if (NULL == g.bl->entry) { 2721 | bstr__free (g.bl); 2722 | return NULL; 2723 | } 2724 | 2725 | g.b = (bstring) str; 2726 | g.bl->qty = 0; 2727 | if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) { 2728 | bstrListDestroy (g.bl); 2729 | return NULL; 2730 | } 2731 | return g.bl; 2732 | } 2733 | 2734 | /* struct bstrList * bsplits (const_bstring str, bstring splitStr) 2735 | * 2736 | * Create an array of sequential substrings from str divided by any of the 2737 | * characters in splitStr. An empty splitStr causes a single entry bstrList 2738 | * containing a copy of str to be returned. 2739 | */ 2740 | struct bstrList * bsplits (const_bstring str, const_bstring splitStr) { 2741 | struct genBstrList g; 2742 | 2743 | if ( str == NULL || str->slen < 0 || str->data == NULL || 2744 | splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL) 2745 | return NULL; 2746 | 2747 | g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList)); 2748 | if (g.bl == NULL) return NULL; 2749 | g.bl->mlen = 4; 2750 | g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring)); 2751 | if (NULL == g.bl->entry) { 2752 | bstr__free (g.bl); 2753 | return NULL; 2754 | } 2755 | g.b = (bstring) str; 2756 | g.bl->qty = 0; 2757 | 2758 | if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) { 2759 | bstrListDestroy (g.bl); 2760 | return NULL; 2761 | } 2762 | return g.bl; 2763 | } 2764 | 2765 | #if defined (__TURBOC__) && !defined (__BORLANDC__) 2766 | # ifndef BSTRLIB_NOVSNP 2767 | # define BSTRLIB_NOVSNP 2768 | # endif 2769 | #endif 2770 | 2771 | /* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */ 2772 | #if defined(__WATCOMC__) || defined(_MSC_VER) 2773 | #define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);} 2774 | #else 2775 | #ifdef BSTRLIB_NOVSNP 2776 | /* This is just a hack. If you are using a system without a vsnprintf, it is 2777 | not recommended that bformat be used at all. */ 2778 | #define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;} 2779 | #define START_VSNBUFF (256) 2780 | #else 2781 | 2782 | #if defined(__GNUC__) && !defined(__APPLE__) 2783 | /* Something is making gcc complain about this prototype not being here, so 2784 | I've just gone ahead and put it in. */ 2785 | extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg); 2786 | #endif 2787 | 2788 | #define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);} 2789 | #endif 2790 | #endif 2791 | 2792 | #if !defined (BSTRLIB_NOVSNP) 2793 | 2794 | #ifndef START_VSNBUFF 2795 | #define START_VSNBUFF (16) 2796 | #endif 2797 | 2798 | /* On IRIX vsnprintf returns n-1 when the operation would overflow the target 2799 | buffer, WATCOM and MSVC both return -1, while C99 requires that the 2800 | returned value be exactly what the length would be if the buffer would be 2801 | large enough. This leads to the idea that if the return value is larger 2802 | than n, then changing n to the return value will reduce the number of 2803 | iterations required. */ 2804 | 2805 | /* int bformata (bstring b, const char * fmt, ...) 2806 | * 2807 | * After the first parameter, it takes the same parameters as printf (), but 2808 | * rather than outputting results to stdio, it appends the results to 2809 | * a bstring which contains what would have been output. Note that if there 2810 | * is an early generation of a '\0' character, the bstring will be truncated 2811 | * to this end point. 2812 | */ 2813 | int bformata (bstring b, const char * fmt, ...) { 2814 | va_list arglist; 2815 | bstring buff; 2816 | int n, r; 2817 | 2818 | if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 2819 | || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; 2820 | 2821 | /* Since the length is not determinable beforehand, a search is 2822 | performed using the truncating "vsnprintf" call (to avoid buffer 2823 | overflows) on increasing potential sizes for the output result. */ 2824 | 2825 | if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; 2826 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) { 2827 | n = 1; 2828 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; 2829 | } 2830 | 2831 | for (;;) { 2832 | va_start (arglist, fmt); 2833 | exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); 2834 | va_end (arglist); 2835 | 2836 | buff->data[n] = (unsigned char) '\0'; 2837 | buff->slen = (int) (strlen) ((char *) buff->data); 2838 | 2839 | if (buff->slen < n) break; 2840 | 2841 | if (r > n) n = r; else n += n; 2842 | 2843 | if (BSTR_OK != balloc (buff, n + 2)) { 2844 | bdestroy (buff); 2845 | return BSTR_ERR; 2846 | } 2847 | } 2848 | 2849 | r = bconcat (b, buff); 2850 | bdestroy (buff); 2851 | return r; 2852 | } 2853 | 2854 | /* int bassignformat (bstring b, const char * fmt, ...) 2855 | * 2856 | * After the first parameter, it takes the same parameters as printf (), but 2857 | * rather than outputting results to stdio, it outputs the results to 2858 | * the bstring parameter b. Note that if there is an early generation of a 2859 | * '\0' character, the bstring will be truncated to this end point. 2860 | */ 2861 | int bassignformat (bstring b, const char * fmt, ...) { 2862 | va_list arglist; 2863 | bstring buff; 2864 | int n, r; 2865 | 2866 | if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 2867 | || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; 2868 | 2869 | /* Since the length is not determinable beforehand, a search is 2870 | performed using the truncating "vsnprintf" call (to avoid buffer 2871 | overflows) on increasing potential sizes for the output result. */ 2872 | 2873 | if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; 2874 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) { 2875 | n = 1; 2876 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR; 2877 | } 2878 | 2879 | for (;;) { 2880 | va_start (arglist, fmt); 2881 | exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); 2882 | va_end (arglist); 2883 | 2884 | buff->data[n] = (unsigned char) '\0'; 2885 | buff->slen = (int) (strlen) ((char *) buff->data); 2886 | 2887 | if (buff->slen < n) break; 2888 | 2889 | if (r > n) n = r; else n += n; 2890 | 2891 | if (BSTR_OK != balloc (buff, n + 2)) { 2892 | bdestroy (buff); 2893 | return BSTR_ERR; 2894 | } 2895 | } 2896 | 2897 | r = bassign (b, buff); 2898 | bdestroy (buff); 2899 | return r; 2900 | } 2901 | 2902 | /* bstring bformat (const char * fmt, ...) 2903 | * 2904 | * Takes the same parameters as printf (), but rather than outputting results 2905 | * to stdio, it forms a bstring which contains what would have been output. 2906 | * Note that if there is an early generation of a '\0' character, the 2907 | * bstring will be truncated to this end point. 2908 | */ 2909 | bstring bformat (const char * fmt, ...) { 2910 | va_list arglist; 2911 | bstring buff; 2912 | int n, r; 2913 | 2914 | if (fmt == NULL) return NULL; 2915 | 2916 | /* Since the length is not determinable beforehand, a search is 2917 | performed using the truncating "vsnprintf" call (to avoid buffer 2918 | overflows) on increasing potential sizes for the output result. */ 2919 | 2920 | if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; 2921 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) { 2922 | n = 1; 2923 | if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL; 2924 | } 2925 | 2926 | for (;;) { 2927 | va_start (arglist, fmt); 2928 | exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist); 2929 | va_end (arglist); 2930 | 2931 | buff->data[n] = (unsigned char) '\0'; 2932 | buff->slen = (int) (strlen) ((char *) buff->data); 2933 | 2934 | if (buff->slen < n) break; 2935 | 2936 | if (r > n) n = r; else n += n; 2937 | 2938 | if (BSTR_OK != balloc (buff, n + 2)) { 2939 | bdestroy (buff); 2940 | return NULL; 2941 | } 2942 | } 2943 | 2944 | return buff; 2945 | } 2946 | 2947 | /* int bvcformata (bstring b, int count, const char * fmt, va_list arglist) 2948 | * 2949 | * The bvcformata function formats data under control of the format control 2950 | * string fmt and attempts to append the result to b. The fmt parameter is 2951 | * the same as that of the printf function. The variable argument list is 2952 | * replaced with arglist, which has been initialized by the va_start macro. 2953 | * The size of the output is upper bounded by count. If the required output 2954 | * exceeds count, the string b is not augmented with any contents and a value 2955 | * below BSTR_ERR is returned. If a value below -count is returned then it 2956 | * is recommended that the negative of this value be used as an update to the 2957 | * count in a subsequent pass. On other errors, such as running out of 2958 | * memory, parameter errors or numeric wrap around BSTR_ERR is returned. 2959 | * BSTR_OK is returned when the output is successfully generated and 2960 | * appended to b. 2961 | * 2962 | * Note: There is no sanity checking of arglist, and this function is 2963 | * destructive of the contents of b from the b->slen point onward. If there 2964 | * is an early generation of a '\0' character, the bstring will be truncated 2965 | * to this end point. 2966 | */ 2967 | int bvcformata (bstring b, int count, const char * fmt, va_list arg) { 2968 | int n, r, l; 2969 | 2970 | if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL 2971 | || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR; 2972 | 2973 | if (count > (n = b->slen + count) + 2) return BSTR_ERR; 2974 | if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR; 2975 | 2976 | exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg); 2977 | b->data[b->slen + count + 2] = '\0'; 2978 | 2979 | /* Did the operation complete successfully within bounds? */ 2980 | 2981 | if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) { 2982 | b->slen = l; 2983 | return BSTR_OK; 2984 | } 2985 | 2986 | /* Abort, since the buffer was not large enough. The return value 2987 | tries to help set what the retry length should be. */ 2988 | 2989 | b->data[b->slen] = '\0'; 2990 | if (r > count+1) l = r; else { 2991 | l = count+count; 2992 | if (count > l) l = INT_MAX; 2993 | } 2994 | n = -l; 2995 | if (n > BSTR_ERR-1) n = BSTR_ERR-1; 2996 | return n; 2997 | } 2998 | 2999 | #endif 3000 | --------------------------------------------------------------------------------