/*********************************************************************************************************************************** Get the list of WAL files ready to be pushed according to PostgreSQL ***********************************************************************************************************************************/ static StringList * archivePushReadyList(const String *walPath) { FUNCTION_LOG_BEGIN(logLevelTrace); FUNCTION_LOG_PARAM(STRING, walPath); FUNCTION_LOG_END(); ASSERT(walPath != NULL); StringList *result = NULL; MEM_CONTEXT_TEMP_BEGIN() { result = strLstNew(); // Read the ready files from the archive_status directory StringList *readyListRaw = strLstSort( storageListP( storagePg(), strNewFmt("%s/" PG_PATH_ARCHIVE_STATUS, strPtr(walPath)), .expression = STRDEF("\\" STATUS_EXT_READY "$"), .errorOnMissing = true), sortOrderAsc); for (unsigned int readyIdx = 0; readyIdx < strLstSize(readyListRaw); readyIdx++) { strLstAdd( result, strSubN(strLstGet(readyListRaw, readyIdx), 0, strSize(strLstGet(readyListRaw, readyIdx)) - STATUS_EXT_READY_SIZE)); } strLstMove(result, MEM_CONTEXT_OLD()); } MEM_CONTEXT_TEMP_END(); FUNCTION_LOG_RETURN(STRING_LIST, result); }
/* * prepare_body * * Builds a lexeme sequence with the expansion of the body of * a macro. */ static int prepare_body (macroctx_t mctx, expansion_t *curexp, lexseq_t *result) { expr_ctx_t ctx = mctx->ectx; parse_ctx_t pctx = expr_parse_ctx(ctx); lexctx_t lctx = parser_lexmemctx(pctx); struct macrodecl_s *macro = name_extraspace(curexp->curmacro); lexeme_t *lex; name_t *np; static strdesc_t mend = STRDEF("<macro-end>"); // Prepare the grouper and separator lexemes, in case // they are needed (only applicable to iterative macros) if (curexp->count == 0 && macro->type == MACRO_ITER) { curexp->sep = parser_punct_separator(pctx); lex = parser_punct_grouper(pctx, 0); if (lex != 0) { lexseq_instail(result, lex); } curexp->closer = parser_punct_grouper(pctx, 1); } // Associate the iterative-formals with any actuals if (macro->type == MACRO_ITER) { nameref_t *iformal; lextype_t lt, terms[1] = { LEXTYPE_DELIM_COMMA }; for (iformal = namereflist_head(¯o->ilist); iformal != 0; iformal = iformal->tq_next) { strdesc_t *namestr = name_string(iformal->np); np = macparam_special(curexp->expscope, namestr, 0); parse_lexeme_seq(pctx, &curexp->remaining, QL_MACRO, terms, 1, macparam_lexseq(np), <); } } // Expand the macro parameters here, instead of relying on the parser // to do this while processing the expansion. If we were to wait, we'd // have to push a new scope on the parser's stack and remove it when we're // done -- but the expansion could declare other names, and those would // get declared in the scope we're removing. for (lex = lexseq_head(¯o->body); lex != 0; lex = lexeme_next(lex)) { if (curexp->expscope != 0 && lexeme_boundtype(lex) == LEXTYPE_NAME) { lexseq_t seq; lexseq_init(&seq); if (macparam_lookup(lctx, curexp->expscope, lexeme_text(lex), &seq) != 0) { lexseq_append(result, &seq); } else { lexseq_instail(result, lexeme_copy(lctx, lex)); } } else { lexseq_instail(result, lexeme_copy(lctx, lex)); } } lexseq_instail(result, lexeme_create(lctx, LEXTYPE_MACROEND, &mend)); curexp->count += 1; return 1; } /* prepare_body */
/* * lexer_filename * * Given a filename index (from a lexeme's position), * return the relevant filename. This is invoked through * the 'filename_fetch' function pointer by the logger. */ static strdesc_t * lexer_filename (void *vctx, int filename_index) { lexer_ctx_t lctx = vctx; struct saved_filename_s *sf; static strdesc_t internalsrc = STRDEF("<Internal source>"); if (filename_index == -1) { return &internalsrc; } for (sf = lctx->saved_filenames; sf != 0; sf = sf->next) { if (sf->filename_index == filename_index) { return sf->filename; } } return 0; } /* lexer_filename */
/* * macros_init * * Module initialization routine. */ macroctx_t macros_init (scopectx_t kwdscope, expr_ctx_t ctx) { namectx_t namectx = scope_namectx(kwdscope); lexctx_t lctx = expr_lexmemctx(ctx); parse_ctx_t pctx = expr_parse_ctx(ctx); macroctx_t mctx = malloc(sizeof(struct macroctx_s)); nametype_vectors_t vec; int i; for (i = 0; i < sizeof(macro_names)/sizeof(macro_names[0]); i++) { name_declare(kwdscope, ¯o_names[i], 0, 0, 0, 0); } memset(mctx, 0, sizeof(struct macroctx_s)); mctx->ectx = ctx; mctx->state = EXP_NORMAL; lextype_register(lctx, mctx, LEXTYPE_NAME_MACRO, macro_bind); lextype_register(lctx, ctx, LEXTYPE_NAME_MAC_PARAM, macparam_bind); lextype_register(lctx, mctx, LEXTYPE_MACROEND, macroend_bind); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_REMAINING, handle_specials); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_COUNT, handle_specials); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_LENGTH, handle_specials); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_EXITMACRO, handle_exits); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_EXITITER, handle_exits); parser_lexfunc_register(pctx, mctx, LEXTYPE_LXF_ERRORMACRO, handle_exits); memset(&vec, 0, sizeof(vec)); vec.typesize = sizeof(struct macrodecl_s); vec.typeinit = macro_initdata; vec.typefree = macro_freedata; vec.typecopy = macro_copydata; vec.typeser = macro_serialize; vec.typedes = macro_deserialize; nametype_dataop_register(namectx, LEXTYPE_NAME_MACRO, &vec, ctx); memset(&vec, 0, sizeof(vec)); vec.typesize = sizeof(lexseq_t); vec.typeinit = macparam_initdata; vec.typefree = macparam_freedata; vec.typecopy = macparam_copydata; vec.typeser = macparam_serialize; vec.typedes = macparam_deserialize; nametype_dataop_register(namectx, LEXTYPE_NAME_MAC_PARAM, &vec, ctx); for (i = 0; i < sizeof(predeclared_macros)/sizeof(predeclared_macros[0]); i++) { name_t *np; struct macrodecl_s *m; np = name_declare(kwdscope, &predeclared_macros[i], 0, 0, 0, &m); if (np == 0 || m == 0) { expr_signal(ctx, STC__INTCMPERR, "macros_init"); continue; } m->type = MACRO_COND; // Index 0 is the %BLISSM[] macro if (i == 0) { strdesc_t pctremain = STRDEF("%REMAINING"); lexeme_t *lex = lexeme_create(lctx, LEXTYPE_LXF_REMAINING, &pctremain); lexseq_instail(&m->body, lex); } // All other macro bodies are empty } return mctx; } /* macros_init */
/* * macro_expand * * Expands a macro. */ static int macro_expand (macroctx_t mctx, name_t *macroname, lexseq_t *result) { struct macrodecl_s *macro = name_extraspace(macroname); expr_ctx_t ctx = mctx->ectx; parse_ctx_t pctx = expr_parse_ctx(ctx); lexctx_t lctx = parser_lexmemctx(pctx); expansion_t *curexp = expansion_alloc(mctx); expansion_t *prev_exp; lextype_t lt; lexeme_t *lex; lexseq_t extras; name_t *np; scopectx_t expscope; int which; int nactuals; punctclass_t pcl; lextype_t psep; lextype_t terms[3]; static strdesc_t comma = STRDEF(","); if (macro == 0 || curexp == 0) { expr_signal(ctx, STC__INTCMPERR, "macro_expand"); return 1; } memset(curexp, 0, sizeof(struct expansion_s)); // We save the punctuation class here, since it will get // munged by the parsing of the macro parameters. parser_punctclass_get(pctx, &pcl, &psep); prev_exp = 0; if (macro->type == MACRO_COND) { for (prev_exp = mctx->curexp; prev_exp != 0 && prev_exp->curmacro != macroname; prev_exp = prev_exp->next); } nactuals = 0; lexseq_init(&extras); // Simple macros with no formal arguments get no processing // of parameter lists whatsoever. if (macro->type == MACRO_SIMPLE && namereflist_length(¯o->plist) == 0) { expscope = 0; which = 3; } else { // For keyword macros, prime the scope with the declared // formals, so we can inherit the default values. if (macro->type == MACRO_KWD) { expscope = scope_copy(macro->ptable, 0); } else { expscope = scope_begin(scope_namectx(parser_scope_get(pctx)), 0); } lt = parser_next(pctx, QL_NORMAL, &lex); if (macro->type == MACRO_KWD) { if (lt != LEXTYPE_DELIM_LPAR) { expr_signal(ctx, STC__DELIMEXP, "("); parser_insert(pctx, lex); return 1; } which = 0; lexeme_free(lctx, lex); } else { for (which = 0; lt != openers[which] && which < 3; which++); if (which >= 3 && namereflist_length(¯o->plist) > 0) { expr_signal(ctx, STC__DELIMEXP, "("); parser_insert(pctx, lex); return 1; } if (which >= 3) { parser_insert(pctx, lex); } else { lexeme_free(lctx, lex); } } } // If we had a match on an opener, process // the actual parameters. if (which < 3) { nameref_t *formal; lexseq_t val; terms[0] = LEXTYPE_DELIM_COMMA; terms[1] = closers[which]; formal = namereflist_head(¯o->plist); while (1) { // Keyword macro actuals are of the form name=value // For positionals, grab the next formal-parameter name, // or set np to NULL to add the actual to %REMAINING. if (macro->type == MACRO_KWD) { lt = parser_next(pctx, QL_NAME, &lex); if (lexeme_boundtype(lex) != LEXTYPE_NAME) { expr_signal(ctx, STC__NAMEEXP); lexeme_free(lctx, lex); break; } np = name_search(macro->ptable, lex->text.ptr, lex->text.len, 0); if (np == 0) { expr_signal(ctx, STC__INTCMPERR, "macro_expand[2]"); } lexeme_free(lctx, lex); if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_OP_ASSIGN, 0, 1)) { expr_signal(ctx, STC__OPEREXP, "="); break; } } else if (nactuals < namereflist_length(¯o->plist)) { np = formal->np; formal = formal->tq_next; } else { np = 0; } lexseq_init(&val); // Now parse the actual-parameter, which can be an expression if (!parse_lexeme_seq(pctx, 0, QL_NAME, terms, 2, &val, <)) { lexseq_free(lctx, &val); break; } // If we are recursively expanding a conditional macro and // there are no parameters, we're done - no expansion. if (prev_exp != 0 && lexseq_length(&val) == 0 && nactuals == 0) { scope_end(expscope); free(curexp); return 1; } if (np == 0) { if (lexseq_length(&extras) > 0) { lexseq_instail(&extras, lexeme_create(lctx, LEXTYPE_DELIM_COMMA, &comma)); } lexseq_append(&extras, &val); } else { name_t *actual; // Associate the actual with the formal. For keyword // macros, the scope_copy() above sets a special "no check" // flag that allows each name to be redeclared once. // name_declare() clears this flag, so we can catch genuine // redeclarations. actual = macparam_special(expscope, name_string(np), &val); if (actual == 0) { expr_signal(ctx, STC__INTCMPERR, "macro_expand[3]"); } lexseq_free(lctx, &val); } nactuals += 1; if (lt == closers[which]) { break; } if (lt != LEXTYPE_DELIM_COMMA) { expr_signal(ctx, STC__DELIMEXP, ","); break; } } /* while (1) */ if (lt != closers[which]) { expr_signal(ctx, STC__DELIMEXP, "closer"); lexseq_free(lctx, &extras); scope_end(expscope); return 1; } if (nactuals < namereflist_length(¯o->plist)) { name_t *anp; while (formal != 0) { anp = macparam_special(expscope, name_string(formal->np), 0); if (anp == 0) { expr_signal(ctx, STC__INTCMPERR, "macro_expand[4]"); } formal = formal->tq_next; } } } /* if which < 3 */ // The macro actual parameters are now processed; hook // the scope into the current hierarchy, restore the punctuation // class to what it was before we parsed the actuals, and // generate the expansion sequence. parser_punctclass_set(pctx, pcl, psep); curexp->count = (prev_exp == 0 ? 0 : prev_exp->count); curexp->expscope = expscope; curexp->curmacro = macroname; curexp->next = mctx->curexp; curexp->nactuals = nactuals; lexseq_append(&curexp->remaining, &extras); mctx->curexp = curexp; return prepare_body(mctx, curexp, result); } /* macro_expand */
/* * parse_fields * * Recursive routine for defining individual fields * and field-sets. If the 'fldset' parameter is null, * we check for the SET keyword and call ourselves again * to parse the fields in the set, if that keyword is * encountered. */ static int parse_fields (expr_ctx_t ctx, scopectx_t scope, namereflist_t *fldset) { parse_ctx_t pctx = expr_parse_ctx(ctx); namectx_t namectx = expr_namectx(ctx); strdesc_t *fldname; textpos_t fpos; lexseq_t *fseq; lexeme_t *lex; name_t *fldnp; namedef_t ndef; lextype_t delims[2] = { LEXTYPE_DELIM_COMMA, LEXTYPE_DELIM_RBRACK }; int which; static strdesc_t zero = STRDEF("0"); while (1) { if (!parse_decl_name(pctx, &fldname, &fpos)) { expr_signal(ctx, STC__NAMEEXP); break; } memset(&ndef, 0, sizeof(ndef)); ndef.name = fldname->ptr; ndef.namelen = fldname->len; ndef.flags = NAME_M_DECLARED; if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_OP_ASSIGN, 0, 1)) { expr_signal(ctx, STC__OPEREXP, "="); } // Check for fieldset and recurse if (fldset == 0 && parser_expect(pctx, QL_NORMAL, LEXTYPE_KWD_SET, 0, 1)) { namereflist_t *frefs; ndef.lt = LEXTYPE_NAME_FIELDSET; fldnp = name_declare(scope, &ndef, fpos, 0, 0, &frefs); if (fldnp == 0) { expr_signal(ctx, STC__INTCMPERR, "parse_fields[1]"); break; } if (!parse_fields(ctx, scope, frefs)) { expr_signal(ctx, STC__SYNTAXERR); return 0; } if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_KWD_TES, 0, 1)) { expr_signal(ctx, STC__KWDEXP, "TES"); } if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_COMMA, 0, 1)) { break; } continue; // set defined; move on to the next name } // Just a regular field ndef.lt = LEXTYPE_NAME_FIELD; fldnp = name_declare(scope, &ndef, fpos, 0, 0, &fseq); if (fldnp == 0) { expr_signal(ctx, STC__INTCMPERR, "parse_fields[2]"); break; } if (fldset != 0) { namereflist_instail(fldset, nameref_alloc(namectx, fldnp)); } if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_LBRACK, 0, 1)) { expr_signal(ctx, STC__DELIMEXP, "]"); } while (1) { if (expr_parse_ctce(ctx, &lex, 0)) { lexseq_instail(fseq, lex); } else { expr_signal(ctx, STC__EXPCTCE); lexseq_instail(fseq, lexeme_create(parser_lexmemctx(pctx), LEXTYPE_NUMERIC, &zero)); } which = parser_expect_oneof(pctx, QL_NORMAL, delims, 2, &lex, 1); if (which != 0) { if (which < 0) { expr_signal(ctx, STC__DELIMEXP, ","); } break; } lexseq_instail(fseq, lex); } if (!parser_expect(pctx, QL_NORMAL, LEXTYPE_DELIM_COMMA, 0, 1)) { break; } } /* while */ return 1; } /* parse_fields */
* information about the access and * allocation parameters and the macro * bodies for each. */ struct strudef_s { scopectx_t acctbl, allotbl; namereflist_t accformals; namereflist_t alloformals; lexseq_t accbody, allobody; }; /* * Some commonly-used data used throughout this module. */ static lextype_t bodyends[] = { LEXTYPE_DELIM_COMMA, LEXTYPE_DELIM_SEMI }; static strdesc_t leftparen = STRDEF("("); static strdesc_t rightparen = STRDEF(")"); static strdesc_t zero = STRDEF("0"); static namedef_t mynames[] = { NAMEDEF("%FIELDEXPAND", LEXTYPE_LXF_FIELDEXPAND, NAME_M_RESERVED), NAMEDEF("%SIZE", LEXTYPE_LXF_SIZE, NAME_M_RESERVED) }; /* * The following definitions are used in the initialization * routine to set up the predeclared structures. It was simpler * to provide these in text form and run them through the parser * than to set up the structures manually. There are different * flavors of these definitions, based on whether or not the * target machine allows for UNIT and EXT parameters. */
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("strNew(), strNewBuf(), strNewN(), strEmpty(), strPtr(), strSize(), and strFree()")) { // We don't want this struct to grow since there are generally a lot of strings, so make sure it doesn't grow without us // knowing about it TEST_RESULT_UINT(sizeof(StringConst), TEST_64BIT() ? 16 : 12, "check StringConst struct size"); // Test the size macro TEST_RESULT_VOID(CHECK_SIZE(555), "valid size"); TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes"); String *string = strNew("static string"); TEST_RESULT_STR(strPtr(string), "static string", "new with static string"); TEST_RESULT_INT(strSize(string), 13, "check size"); TEST_RESULT_BOOL(strEmpty(string), false, "is not empty"); TEST_RESULT_INT(strlen(strPtr(string)), 13, "check size with strlen()"); TEST_RESULT_CHAR(strPtr(string)[2], 'a', "check character"); TEST_RESULT_VOID(strFree(string), "free string"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_STR(strPtr(strNewN("testmorestring", 4)), "test", "new string with size limit"); // ------------------------------------------------------------------------------------------------------------------------- Buffer *buffer = bufNew(8); memcpy(bufPtr(buffer), "12345678", 8); bufUsedSet(buffer, 8); TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "12345678", "new string from buffer"); // ------------------------------------------------------------------------------------------------------------------------- string = strNewFmt("formatted %s %04d", "string", 1); TEST_RESULT_STR(strPtr(string), "formatted string 0001", "new with formatted string"); TEST_RESULT_PTR(strPtr(NULL), NULL, "null string pointer"); TEST_RESULT_VOID(strFree(string), "free string"); TEST_RESULT_VOID(strFree(NULL), "free null string"); } // ***************************************************************************************************************************** if (testBegin("STRING_STATIC()")) { TEST_RESULT_STR(strPtr(TEST_STRING), "a very interesting string!", "check static string"); TEST_RESULT_STR(strPtr(strSubN(TEST_STRING, 0, 6)), "a very", "read-only strSub() works"); } // ***************************************************************************************************************************** if (testBegin("strBase() and strPath()")) { TEST_RESULT_STR(strPtr(strBase(STRDEF(""))), "", "empty string"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/"))), "", "/ only"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/file"))), "file", "root file"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/dir1/dir2/file"))), "file", "subdirectory file"); TEST_RESULT_STR(strPtr(strPath(STRDEF(""))), "", "empty string"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/"))), "/", "/ only"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/file"))), "/", "root path"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/dir1/dir2/file"))), "/dir1/dir2", "subdirectory file"); } // ***************************************************************************************************************************** if (testBegin("strCat(), strCatChr(), and strCatFmt()")) { String *string = strNew("XXXX"); String *string2 = strNew("ZZZZ"); TEST_RESULT_STR(strPtr(strCat(string, "YYYY")), "XXXXYYYY", "cat string"); TEST_RESULT_SIZE(string->extra, 4, "check extra"); TEST_RESULT_STR(strPtr(strCatFmt(string, "%05d", 777)), "XXXXYYYY00777", "cat formatted string"); TEST_RESULT_SIZE(string->extra, 6, "check extra"); TEST_RESULT_STR(strPtr(strCatChr(string, '!')), "XXXXYYYY00777!", "cat chr"); TEST_RESULT_SIZE(string->extra, 5, "check extra"); TEST_RESULT_STR(strPtr(string2), "ZZZZ", "check unaltered string"); } // ***************************************************************************************************************************** if (testBegin("strDup()")) { const String *string = STRDEF("duplicated string"); String *stringDup = strDup(string); TEST_RESULT_STR(strPtr(stringDup), strPtr(string), "duplicated strings match"); TEST_RESULT_PTR(strDup(NULL), NULL, "duplicate null string"); } // ***************************************************************************************************************************** if (testBegin("strBeginsWith() and strBeginsWithZ()")) { TEST_RESULT_BOOL(strBeginsWith(STRDEF(""), STRDEF("aaa")), false, "empty string"); TEST_RESULT_BOOL(strBeginsWith(STRDEF("astring"), STRDEF("")), true, "empty begins with"); TEST_RESULT_BOOL(strBeginsWithZ(STRDEF("astring"), "astr"), true, "partial begins with"); TEST_RESULT_BOOL(strBeginsWithZ(STRDEF("astring"), "astring"), true, "equal strings"); } // ***************************************************************************************************************************** if (testBegin("strEndsWith() and strEndsWithZ()")) { TEST_RESULT_BOOL(strEndsWith(STRDEF(""), STRDEF(".doc")), false, "empty string"); TEST_RESULT_BOOL(strEndsWith(STRDEF("astring"), STRDEF("")), true, "empty ends with"); TEST_RESULT_BOOL(strEndsWithZ(STRDEF("astring"), "ing"), true, "partial ends with"); TEST_RESULT_BOOL(strEndsWithZ(STRDEF("astring"), "astring"), true, "equal strings"); } // ***************************************************************************************************************************** if (testBegin("strEq(), strEqZ(), strCmp(), strCmpZ()")) { TEST_RESULT_BOOL(strEq(STRDEF("equalstring"), STRDEF("equalstring")), true, "strings equal"); TEST_RESULT_BOOL(strEq(STRDEF("astring"), STRDEF("anotherstring")), false, "strings not equal"); TEST_RESULT_BOOL(strEq(STRDEF("astring"), STRDEF("bstring")), false, "equal length strings not equal"); TEST_RESULT_INT(strCmp(STRDEF("equalstring"), STRDEF("equalstring")), 0, "strings equal"); TEST_RESULT_INT(strCmp(STRDEF("a"), STRDEF("b")), -1, "a < b"); TEST_RESULT_INT(strCmp(STRDEF("b"), STRDEF("a")), 1, "b > a"); TEST_RESULT_BOOL(strEqZ(STRDEF("equalstring"), "equalstring"), true, "strings equal"); TEST_RESULT_BOOL(strEqZ(STRDEF("astring"), "anotherstring"), false, "strings not equal"); TEST_RESULT_BOOL(strEqZ(STRDEF("astring"), "bstring"), false, "equal length strings not equal"); TEST_RESULT_INT(strCmpZ(STRDEF("equalstring"), "equalstring"), 0, "strings equal"); TEST_RESULT_INT(strCmpZ(STRDEF("a"), "b"), -1, "a < b"); TEST_RESULT_INT(strCmpZ(STRDEF("b"), "a"), 1, "b > a"); } // ***************************************************************************************************************************** if (testBegin("strFirstUpper(), strFirstLower(), strUpper(), strLower()")) { TEST_RESULT_STR(strPtr(strFirstUpper(strNew(""))), "", "empty first upper"); TEST_RESULT_STR(strPtr(strFirstUpper(strNew("aaa"))), "Aaa", "first upper"); TEST_RESULT_STR(strPtr(strFirstUpper(strNew("Aaa"))), "Aaa", "first already upper"); TEST_RESULT_STR(strPtr(strFirstLower(strNew(""))), "", "empty first lower"); TEST_RESULT_STR(strPtr(strFirstLower(strNew("AAA"))), "aAA", "first lower"); TEST_RESULT_STR(strPtr(strFirstLower(strNew("aAA"))), "aAA", "first already lower"); TEST_RESULT_STR(strPtr(strLower(strNew("K123aBc"))), "k123abc", "all lower"); TEST_RESULT_STR(strPtr(strLower(strNew("k123abc"))), "k123abc", "already lower"); TEST_RESULT_STR(strPtr(strLower(strNew("C"))), "c", "char lower"); TEST_RESULT_STR(strPtr(strLower(strNew(""))), "", "empty lower"); TEST_RESULT_STR(strPtr(strUpper(strNew("K123aBc"))), "K123ABC", "all upper"); TEST_RESULT_STR(strPtr(strUpper(strNew("K123ABC"))), "K123ABC", "already upper"); TEST_RESULT_STR(strPtr(strUpper(strNew("c"))), "C", "char upper"); TEST_RESULT_STR(strPtr(strUpper(strNew(""))), "", "empty upper"); } // ***************************************************************************************************************************** if (testBegin("strQuote()")) { TEST_RESULT_STR(strPtr(strQuote(STRDEF("abcd"), STRDEF("'"))), "'abcd'", "quote string"); } // ***************************************************************************************************************************** if (testBegin("strReplaceChr()")) { TEST_RESULT_STR(strPtr(strReplaceChr(strNew("ABCD"), 'B', 'R')), "ARCD", "replace chr"); } // ***************************************************************************************************************************** if (testBegin("strSub() and strSubN()")) { TEST_RESULT_STR(strPtr(strSub(STRDEF("ABCD"), 2)), "CD", "sub string"); TEST_RESULT_STR(strPtr(strSubN(STRDEF("ABCD"), 1, 2)), "BC", "sub string with length"); } // ***************************************************************************************************************************** if (testBegin("strTrim()")) { TEST_RESULT_STR(strPtr(strTrim(strNew(""))), "", "trim empty"); TEST_RESULT_STR(strPtr(strTrim(strNew("X"))), "X", "no trim (one char)"); TEST_RESULT_STR(strPtr(strTrim(strNew("no-trim"))), "no-trim", "no trim (string)"); TEST_RESULT_STR(strPtr(strTrim(strNew(" \t\r\n"))), "", "all whitespace"); TEST_RESULT_STR(strPtr(strTrim(strNew(" \tbegin-only"))), "begin-only", "trim begin"); TEST_RESULT_STR(strPtr(strTrim(strNew("end-only\t "))), "end-only", "trim end"); TEST_RESULT_STR(strPtr(strTrim(strNew("\n\rboth\r\n"))), "both", "trim both"); TEST_RESULT_STR(strPtr(strTrim(strNew("begin \r\n\tend"))), "begin \r\n\tend", "ignore whitespace in middle"); } // ***************************************************************************************************************************** if (testBegin("strChr() and strTrunc()")) { TEST_RESULT_INT(strChr(STRDEF("abcd"), 'c'), 2, "c found"); TEST_RESULT_INT(strChr(STRDEF("abcd"), 'C'), -1, "capital C not found"); TEST_RESULT_INT(strChr(STRDEF("abcd"), 'i'), -1, "i not found"); TEST_RESULT_INT(strChr(STRDEF(""), 'x'), -1, "empty string - x not found"); String *val = strNew("abcdef"); TEST_ERROR( strTrunc(val, (int)(strSize(val) + 1)), AssertError, "assertion 'idx >= 0 && (size_t)idx <= this->size' failed"); TEST_ERROR(strTrunc(val, -1), AssertError, "assertion 'idx >= 0 && (size_t)idx <= this->size' failed"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'd'))), "abc", "simple string truncated"); strCat(val, "\r\n to end"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'n'))), "abc\r\n to e", "complex string truncated"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'a'))), "", "complete string truncated - empty string"); TEST_RESULT_INT(strSize(val), 0, "0 size"); TEST_RESULT_STR(strPtr(strTrunc(val, 0)), "", "test coverage of empty string - no error thrown for index 0"); } // ***************************************************************************************************************************** if (testBegin("strToLog() and strObjToLog()")) { TEST_RESULT_STR(strPtr(strToLog(STRDEF("test"))), "{\"test\"}", "format string"); TEST_RESULT_STR(strPtr(strToLog(NULL)), "null", "format null string"); char buffer[256]; TEST_RESULT_UINT(strObjToLog(NULL, (StrObjToLogFormat)strToLog, buffer, sizeof(buffer)), 4, "format null string"); TEST_RESULT_STR(buffer, "null", "check null string"); TEST_RESULT_UINT(strObjToLog(STRDEF("teststr"), (StrObjToLogFormat)strToLog, buffer, sizeof(buffer)), 11, "format string"); TEST_RESULT_STR(buffer, "{\"teststr\"}", "check string"); } // ***************************************************************************************************************************** if (testBegin("strSizeFormat()")) { TEST_RESULT_STR(strPtr(strSizeFormat(0)), "0B", "zero bytes"); TEST_RESULT_STR(strPtr(strSizeFormat(1023)), "1023B", "1023 bytes"); TEST_RESULT_STR(strPtr(strSizeFormat(1024)), "1KB", "1 KB"); TEST_RESULT_STR(strPtr(strSizeFormat(2200)), "2.1KB", "2.1 KB"); TEST_RESULT_STR(strPtr(strSizeFormat(1048576)), "1MB", "1 MB"); TEST_RESULT_STR(strPtr(strSizeFormat(20162900)), "19.2MB", "19.2 MB"); TEST_RESULT_STR(strPtr(strSizeFormat(1073741824)), "1GB", "1 GB"); TEST_RESULT_STR(strPtr(strSizeFormat(1073741824 + 107374183)), "1.1GB", "1.1 GB"); TEST_RESULT_STR(strPtr(strSizeFormat(UINT64_MAX)), "17179869183GB", "uint64 max"); } // ***************************************************************************************************************************** if (testBegin("strLstNew(), strLstAdd*(), strLstGet(), strLstMove(), strLstSize(), and strLstFree()")) { // Add strings to the list // ------------------------------------------------------------------------------------------------------------------------- StringList *list = NULL; MEM_CONTEXT_TEMP_BEGIN() { list = strLstNew(); for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++) { if (listIdx == 0) { TEST_RESULT_PTR(strLstAdd(list, NULL), list, "add null item"); } else TEST_RESULT_PTR(strLstAdd(list, strNewFmt("STR%02d", listIdx)), list, "add item %d", listIdx); } strLstMove(list, MEM_CONTEXT_OLD()); } MEM_CONTEXT_TEMP_END(); TEST_RESULT_INT(strLstSize(list), 9, "list size"); // Read them back and check values // ------------------------------------------------------------------------------------------------------------------------- for (unsigned int listIdx = 0; listIdx < strLstSize(list); listIdx++) { if (listIdx == 0) { TEST_RESULT_PTR(strLstGet(list, listIdx), NULL, "check null item"); } else TEST_RESULT_STR(strPtr(strLstGet(list, listIdx)), strPtr(strNewFmt("STR%02u", listIdx)), "check item %u", listIdx); } TEST_RESULT_VOID(strLstFree(list), "free string list"); TEST_RESULT_VOID(strLstFree(NULL), "free null string list"); }
char main_input[FNAME_LEN]; liststate_t *cur_state; liststate_t main_state; unsigned int blockdepth; unsigned int pagenum; unsigned int nlines; int require_depth; int haveabuf; size_t linelen; char header1[132]; char header2[132]; char linebuf[132]; }; static strdesc_t loptswitch[LISTOPT_COUNT] = { STRDEF("SOURCE"), STRDEF("REQUIRE"), STRDEF("EXPAND"), STRDEF("TRACE"), STRDEF("LIBRARY"), STRDEF("OBJECT"), STRDEF("ASSEMBLY"), STRDEF("SYMBOLIC"), STRDEF("BINARY"), STRDEF("COMMENTARY") }; static lextype_t lopt_lextypes[] = { LEXTYPE_NAME_TOGGLE_OFF, LEXTYPE_NAME_TOGGLE_ON, LEXTYPE_DCL_REQUIRE, LEXTYPE_DCL_LIBRARY }; static int listopt_handler (parse_ctx_t pctx, void *vctx, int togidx, lextype_t dtype, name_t *togname) { lstgctx_t ctx = vctx; int set_on = (name_type(togname) == LEXTYPE_NAME_TOGGLE_ON);