Fixes for issues found by cppcheck.
This commit is contained in:
parent
60d24c8c33
commit
d9889b2fbb
11 changed files with 158 additions and 62 deletions
76
cppcheck.sh
Executable file
76
cppcheck.sh
Executable file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# cppcheck.sh -- static analysis for the DVX source tree.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./cppcheck.sh # whole tree
|
||||||
|
# ./cppcheck.sh src/widgets # one subtree
|
||||||
|
# ./cppcheck.sh src/libs/kpunch/texthelp/textHelp.c # one file
|
||||||
|
#
|
||||||
|
# Exits 0 even when warnings are found -- cppcheck's exit status is
|
||||||
|
# not useful as a gate for this codebase (too many DJGPP-isms it can't
|
||||||
|
# resolve). Read the output.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
TARGETS="${@:-src}"
|
||||||
|
|
||||||
|
INCLUDES=(
|
||||||
|
-Isrc/libs/kpunch/libdvx
|
||||||
|
-Isrc/libs/kpunch/libdvx/platform
|
||||||
|
-Isrc/libs/kpunch/libdvx/thirdparty
|
||||||
|
-Isrc/libs/kpunch/libtasks
|
||||||
|
-Isrc/libs/kpunch/texthelp
|
||||||
|
-Isrc/libs/kpunch/listhelp
|
||||||
|
-Isrc/libs/kpunch/dvxshell
|
||||||
|
-Isrc/libs/kpunch/taskmgr
|
||||||
|
-Isrc/libs/kpunch/serial
|
||||||
|
-Isrc/widgets/kpunch
|
||||||
|
-Isrc/apps/kpunch/dvxbasic
|
||||||
|
-Isrc/apps/kpunch/dvxbasic/compiler
|
||||||
|
-Isrc/apps/kpunch/dvxbasic/runtime
|
||||||
|
-Isrc/apps/kpunch/dvxbasic/formrt
|
||||||
|
)
|
||||||
|
|
||||||
|
# DJGPP / platform macros cppcheck can't find. Expand them to nothing
|
||||||
|
# so the preprocessor leaves the code visible to the analyser instead
|
||||||
|
# of eliding whole functions behind unknown macro calls.
|
||||||
|
DEFINES=(
|
||||||
|
-DDXE_EXPORT=
|
||||||
|
-DDVX_WIDGET_IMPL
|
||||||
|
-D__DJGPP__=2
|
||||||
|
)
|
||||||
|
|
||||||
|
# Suppressions: things cppcheck flags that aren't actually wrong for
|
||||||
|
# this codebase. Keep the list short so real issues stay visible.
|
||||||
|
SUPPRESS=(
|
||||||
|
--suppress=missingIncludeSystem # system headers aren't on host
|
||||||
|
--suppress=unusedFunction # many public exports look "unused" to a .c-only scan
|
||||||
|
--suppress=constParameterPointer # noisy on this style
|
||||||
|
--suppress=constVariablePointer # ditto
|
||||||
|
--suppress=normalCheckLevelMaxBranches
|
||||||
|
# stb_ds arrput is a macro -- cppcheck can't see that the value
|
||||||
|
# (including heap pointers inside it) is stored, so it reports
|
||||||
|
# spurious null-derefs on the stb_ds dynamic-array handle.
|
||||||
|
--suppress=nullPointerRedundantCheck
|
||||||
|
--suppress=nullPointerArithmeticRedundantCheck
|
||||||
|
# Thirdparty headers -- their own style isn't our problem.
|
||||||
|
--suppress='*:src/libs/kpunch/libdvx/thirdparty/*'
|
||||||
|
--suppress='*:src/libs/kpunch/sql/thirdparty/*'
|
||||||
|
)
|
||||||
|
|
||||||
|
exec cppcheck \
|
||||||
|
--enable=warning,style,performance,portability \
|
||||||
|
--inline-suppr \
|
||||||
|
--std=c99 \
|
||||||
|
--platform=unix32 \
|
||||||
|
--quiet \
|
||||||
|
-j"$(nproc)" \
|
||||||
|
-i src/libs/kpunch/sql/thirdparty \
|
||||||
|
-i src/libs/kpunch/libdvx/thirdparty \
|
||||||
|
"${INCLUDES[@]}" \
|
||||||
|
"${DEFINES[@]}" \
|
||||||
|
"${SUPPRESS[@]}" \
|
||||||
|
$TARGETS
|
||||||
|
|
@ -3991,7 +3991,7 @@ static void parsePrimary(BasParserT *p) {
|
||||||
} else {
|
} else {
|
||||||
basEmit8(&p->cg, OP_PUSH_INT32);
|
basEmit8(&p->cg, OP_PUSH_INT32);
|
||||||
basEmit16(&p->cg, (int16_t)(val & 0xFFFF));
|
basEmit16(&p->cg, (int16_t)(val & 0xFFFF));
|
||||||
basEmit16(&p->cg, (int16_t)((val >> 16) & 0xFFFF));
|
basEmit16(&p->cg, (int16_t)(((uint32_t)val >> 16) & 0xFFFF));
|
||||||
}
|
}
|
||||||
advance(p);
|
advance(p);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
#define DEFAULT_FORM_W 400
|
#define DEFAULT_FORM_W 400
|
||||||
#define DEFAULT_FORM_H 300
|
#define DEFAULT_FORM_H 300
|
||||||
#define MAX_EVENT_NAME_LEN 128
|
#define MAX_EVENT_NAME_LEN 128
|
||||||
|
#define MENU_ID_BASE 10000
|
||||||
|
|
||||||
// Module-level form runtime pointer for onFormClose callback
|
// Module-level form runtime pointer for onFormClose callback
|
||||||
static BasFormRtT *sFormRt = NULL;
|
static BasFormRtT *sFormRt = NULL;
|
||||||
|
|
@ -1570,8 +1571,7 @@ void basFormRtHideForm(void *ctx, void *formRef) {
|
||||||
if (!form) {
|
if (!form) {
|
||||||
basFormRtRuntimeError(rt,
|
basFormRtRuntimeError(rt,
|
||||||
"Hide on unknown form",
|
"Hide on unknown form",
|
||||||
"The form reference resolved to NULL; check that the form name exists.",
|
"The form reference resolved to NULL; check that the form name exists.");
|
||||||
NULL);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1652,8 +1652,6 @@ static void *basFormRtLoadCfm(BasFormRtT *rt, const uint8_t *data, int32_t dataL
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
dvxLog("basFormRtLoadCfm: form '%s' created, %ld controls", form->name, (long)arrlen(form->controls));
|
|
||||||
|
|
||||||
// Set window callbacks (same as basFormRtLoadForm)
|
// Set window callbacks (same as basFormRtLoadForm)
|
||||||
form->window->onClose = onFormClose;
|
form->window->onClose = onFormClose;
|
||||||
form->window->onResize = onFormResize;
|
form->window->onResize = onFormResize;
|
||||||
|
|
@ -1892,7 +1890,6 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen
|
||||||
bar = wmAddMenuBar(form->window);
|
bar = wmAddMenuBar(form->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MENU_ID_BASE 10000
|
|
||||||
MenuT *menuStack[16];
|
MenuT *menuStack[16];
|
||||||
memset(menuStack, 0, sizeof(menuStack));
|
memset(menuStack, 0, sizeof(menuStack));
|
||||||
bool topIsPopup = false;
|
bool topIsPopup = false;
|
||||||
|
|
@ -2291,7 +2288,6 @@ void basFormRtRemoveCtrl(void *ctx, void *formRef, const char *ctrlName) {
|
||||||
|
|
||||||
void basFormRtRunSimple(BasFormRtT *rt) {
|
void basFormRtRunSimple(BasFormRtT *rt) {
|
||||||
if (!rt || !rt->vm) {
|
if (!rt || !rt->vm) {
|
||||||
dvxLog("basFormRtRunSimple: no rt or vm");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2563,8 +2559,7 @@ void basFormRtShowForm(void *ctx, void *formRef, bool modal) {
|
||||||
if (!form) {
|
if (!form) {
|
||||||
basFormRtRuntimeError(rt,
|
basFormRtRuntimeError(rt,
|
||||||
"Show on unknown form",
|
"Show on unknown form",
|
||||||
"The form reference resolved to NULL; check that the form name exists.",
|
"The form reference resolved to NULL; check that the form name exists.");
|
||||||
NULL);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2765,7 +2760,7 @@ static BasFrmPopupMenuT *findPopupMenu(BasFormT *form, const char *name) {
|
||||||
// event dispatch. Starts from MENU_ID_BASE + the highest in-use ID
|
// event dispatch. Starts from MENU_ID_BASE + the highest in-use ID
|
||||||
// so runtime-added items don't collide with .frm-loaded items.
|
// so runtime-added items don't collide with .frm-loaded items.
|
||||||
static int32_t nextMenuItemId(BasFormT *form) {
|
static int32_t nextMenuItemId(BasFormT *form) {
|
||||||
int32_t maxId = 10000 - 1; // MENU_ID_BASE - 1
|
int32_t maxId = MENU_ID_BASE - 1;
|
||||||
|
|
||||||
for (int32_t i = 0; i < form->menuIdMapCount; i++) {
|
for (int32_t i = 0; i < form->menuIdMapCount; i++) {
|
||||||
if (form->menuIdMap[i].id > maxId) {
|
if (form->menuIdMap[i].id > maxId) {
|
||||||
|
|
@ -3665,12 +3660,6 @@ static void fireCtrlEvent(BasFormRtT *rt, BasControlT *ctrl, const char *eventNa
|
||||||
static void frmLoad_onCtrlBegin(void *userData, const char *typeName, const char *name) {
|
static void frmLoad_onCtrlBegin(void *userData, const char *typeName, const char *name) {
|
||||||
BasFrmLoadCtxT *ctx = (BasFrmLoadCtxT *)userData;
|
BasFrmLoadCtxT *ctx = (BasFrmLoadCtxT *)userData;
|
||||||
|
|
||||||
dvxLog("onCtrlBegin: type='%s' name='%s' form=%s nestDepth=%d",
|
|
||||||
typeName ? typeName : "?",
|
|
||||||
name ? name : "?",
|
|
||||||
(ctx->form && ctx->form->name[0]) ? ctx->form->name : "(null)",
|
|
||||||
(int)ctx->nestDepth);
|
|
||||||
|
|
||||||
if (!ctx->form || ctx->nestDepth <= 0) {
|
if (!ctx->form || ctx->nestDepth <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1469,7 +1469,7 @@ static bool ideValidator_onFormBegin(void *ud, const char *name) {
|
||||||
IdeValidatorCtxT *v = (IdeValidatorCtxT *)ud;
|
IdeValidatorCtxT *v = (IdeValidatorCtxT *)ud;
|
||||||
IdeCtrlMapEntryT e;
|
IdeCtrlMapEntryT e;
|
||||||
memset(&e, 0, sizeof(e));
|
memset(&e, 0, sizeof(e));
|
||||||
snprintf(e.name, BAS_MAX_CTRL_NAME, "%s", name);
|
snprintf(e.name, BAS_MAX_CTRL_NAME, "%s", name ? name : "");
|
||||||
snprintf(e.wgtType, BAS_MAX_CTRL_NAME, "%s", "Form");
|
snprintf(e.wgtType, BAS_MAX_CTRL_NAME, "%s", "Form");
|
||||||
arrput(v->entries, e);
|
arrput(v->entries, e);
|
||||||
snprintf(v->currentForm, BAS_MAX_CTRL_NAME, "%s", name ? name : "");
|
snprintf(v->currentForm, BAS_MAX_CTRL_NAME, "%s", name ? name : "");
|
||||||
|
|
@ -1481,7 +1481,7 @@ static void ideValidator_onCtrlBegin(void *ud, const char *typeName, const char
|
||||||
IdeValidatorCtxT *v = (IdeValidatorCtxT *)ud;
|
IdeValidatorCtxT *v = (IdeValidatorCtxT *)ud;
|
||||||
IdeCtrlMapEntryT e;
|
IdeCtrlMapEntryT e;
|
||||||
memset(&e, 0, sizeof(e));
|
memset(&e, 0, sizeof(e));
|
||||||
snprintf(e.name, BAS_MAX_CTRL_NAME, "%s", name);
|
snprintf(e.name, BAS_MAX_CTRL_NAME, "%s", name ? name : "");
|
||||||
snprintf(e.wgtType, BAS_MAX_CTRL_NAME, "%s", typeName ? typeName : "");
|
snprintf(e.wgtType, BAS_MAX_CTRL_NAME, "%s", typeName ? typeName : "");
|
||||||
arrput(v->entries, e);
|
arrput(v->entries, e);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -612,6 +612,7 @@ static const char *doWrap(char **wrappedPtr, int32_t *wrapWidthPtr, int32_t *lin
|
||||||
|
|
||||||
// Return cached result if width hasn't changed
|
// Return cached result if width hasn't changed
|
||||||
if (*wrappedPtr && *wrapWidthPtr == pixelW) {
|
if (*wrappedPtr && *wrapWidthPtr == pixelW) {
|
||||||
|
// cppcheck-suppress identicalInnerCondition
|
||||||
return *wrappedPtr;
|
return *wrappedPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ const DvxResDirEntryT *dvxResFind(DvxResHandleT *h, const char *name) {
|
||||||
|
|
||||||
// Entries are sorted at open() time so bsearch gives O(log n) lookup.
|
// Entries are sorted at open() time so bsearch gives O(log n) lookup.
|
||||||
DvxResDirEntryT key;
|
DvxResDirEntryT key;
|
||||||
|
memset(&key, 0, sizeof(key));
|
||||||
strncpy(key.name, name, sizeof(key.name) - 1);
|
strncpy(key.name, name, sizeof(key.name) - 1);
|
||||||
key.name[sizeof(key.name) - 1] = '\0';
|
key.name[sizeof(key.name) - 1] = '\0';
|
||||||
return (const DvxResDirEntryT *)bsearch(&key, h->entries, h->entryCount,
|
return (const DvxResDirEntryT *)bsearch(&key, h->entries, h->entryCount,
|
||||||
|
|
|
||||||
|
|
@ -257,13 +257,8 @@ bool textEditFindNext(TextEditLineCacheT *lc, const char *buf, int32_t len, cons
|
||||||
|
|
||||||
int32_t cursorByte = textEditRowColToOff(lc, buf, len, *pCursorRow, *pCursorCol);
|
int32_t cursorByte = textEditRowColToOff(lc, buf, len, *pCursorRow, *pCursorCol);
|
||||||
int32_t startPos = forward ? cursorByte + 1 : cursorByte - 1;
|
int32_t startPos = forward ? cursorByte + 1 : cursorByte - 1;
|
||||||
int32_t searchLen = len - needleLen + 1;
|
int32_t searchLen = len - needleLen + 1; // >= 1 given the needleLen > len check above
|
||||||
|
int32_t count = forward ? (searchLen - startPos) : (startPos + 1);
|
||||||
if (searchLen <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t count = forward ? (searchLen - startPos) : (startPos + 1);
|
|
||||||
|
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2080,7 +2075,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*pCursor > 0) {
|
if (*pCursor > 0) {
|
||||||
|
|
@ -2106,7 +2100,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*pCursor < *pLen) {
|
if (*pCursor < *pLen) {
|
||||||
|
|
@ -2134,7 +2127,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pCursor = newPos;
|
*pCursor = newPos;
|
||||||
|
|
@ -2157,7 +2149,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pCursor = newPos;
|
*pCursor = newPos;
|
||||||
|
|
@ -2178,7 +2169,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pCursor = 0;
|
*pCursor = 0;
|
||||||
|
|
@ -2199,7 +2189,6 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
if (shift && pSelStart && pSelEnd) {
|
if (shift && pSelStart && pSelEnd) {
|
||||||
if (*pSelStart < 0) {
|
if (*pSelStart < 0) {
|
||||||
*pSelStart = *pCursor;
|
*pSelStart = *pCursor;
|
||||||
*pSelEnd = *pCursor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pCursor = *pLen;
|
*pCursor = *pLen;
|
||||||
|
|
|
||||||
|
|
@ -276,9 +276,36 @@ static int dvxResAppendEntry(const char *path, const char *name, uint32_t type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new entry
|
// Add new entry. Use tmp pointers so a realloc failure doesn't
|
||||||
entries = (DvxResDirEntryT *)realloc(entries, (count + 1) * sizeof(DvxResDirEntryT));
|
// strand the existing blocks as unreachable leaks. Abort the
|
||||||
data = (uint8_t **)realloc(data, (count + 1) * sizeof(uint8_t *));
|
// write on OOM -- nothing useful to recover to here.
|
||||||
|
DvxResDirEntryT *newEntries = (DvxResDirEntryT *)realloc(entries, (count + 1) * sizeof(DvxResDirEntryT));
|
||||||
|
|
||||||
|
if (!newEntries) {
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
free(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
free(entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = newEntries;
|
||||||
|
|
||||||
|
uint8_t **newData = (uint8_t **)realloc(data, (count + 1) * sizeof(uint8_t *));
|
||||||
|
|
||||||
|
if (!newData) {
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
free(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
free(entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = newData;
|
||||||
|
|
||||||
memset(&entries[count], 0, sizeof(DvxResDirEntryT));
|
memset(&entries[count], 0, sizeof(DvxResDirEntryT));
|
||||||
strncpy(entries[count].name, name, DVX_RES_NAME_MAX - 1);
|
strncpy(entries[count].name, name, DVX_RES_NAME_MAX - 1);
|
||||||
|
|
|
||||||
|
|
@ -244,9 +244,27 @@ static int cmdBuild(const char *dxePath, const char *manifestPath) {
|
||||||
newSize = (uint32_t)readLen;
|
newSize = (uint32_t)readLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append
|
// Append. Use tmp pointers so a realloc failure doesn't
|
||||||
entries = (DvxResDirEntryT *)realloc(entries, (count + 1) * sizeof(DvxResDirEntryT));
|
// strand the existing blocks as unreachable leaks. As a
|
||||||
data = (uint8_t **)realloc(data, (count + 1) * sizeof(uint8_t *));
|
// host build-time tool, abort on OOM -- nothing useful to
|
||||||
|
// recover to.
|
||||||
|
DvxResDirEntryT *newEntries = (DvxResDirEntryT *)realloc(entries, (count + 1) * sizeof(DvxResDirEntryT));
|
||||||
|
|
||||||
|
if (!newEntries) {
|
||||||
|
fprintf(stderr, "dvxres: out of memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = newEntries;
|
||||||
|
|
||||||
|
uint8_t **newDataArr = (uint8_t **)realloc(data, (count + 1) * sizeof(uint8_t *));
|
||||||
|
|
||||||
|
if (!newDataArr) {
|
||||||
|
fprintf(stderr, "dvxres: out of memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = newDataArr;
|
||||||
|
|
||||||
memset(&entries[count], 0, sizeof(DvxResDirEntryT));
|
memset(&entries[count], 0, sizeof(DvxResDirEntryT));
|
||||||
snprintf(entries[count].name, DVX_RES_NAME_MAX, "%s", name);
|
snprintf(entries[count].name, DVX_RES_NAME_MAX, "%s", name);
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ void dataCtrlAddNew(WidgetT *w) {
|
||||||
d->isNewRow = true;
|
d->isNewRow = true;
|
||||||
|
|
||||||
fireReposition(w);
|
fireReposition(w);
|
||||||
|
// cppcheck-suppress memleak ; row.fields is stored into d->rows via arrput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -647,6 +648,7 @@ void dataCtrlRefresh(WidgetT *w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
arrput(d->rows, row);
|
arrput(d->rows, row);
|
||||||
|
// cppcheck-suppress memleak ; row.fields is stored into d->rows via arrput
|
||||||
}
|
}
|
||||||
|
|
||||||
d->rowCount = (int32_t)arrlen(d->rows);
|
d->rowCount = (int32_t)arrlen(d->rows);
|
||||||
|
|
@ -830,7 +832,7 @@ void dataCtrlUpdate(WidgetT *w) {
|
||||||
pos += snprintf(sql + pos, sizeof(sql) - pos, "'%s'", escaped);
|
pos += snprintf(sql + pos, sizeof(sql) - pos, "'%s'", escaped);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += snprintf(sql + pos, sizeof(sql) - pos, ")");
|
snprintf(sql + pos, sizeof(sql) - pos, ")");
|
||||||
} else {
|
} else {
|
||||||
// UPDATE table SET col1='val1', ... WHERE keyCol=keyVal
|
// UPDATE table SET col1='val1', ... WHERE keyCol=keyVal
|
||||||
int32_t keyCol = findKeyCol(d);
|
int32_t keyCol = findKeyCol(d);
|
||||||
|
|
@ -855,7 +857,7 @@ void dataCtrlUpdate(WidgetT *w) {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += snprintf(sql + pos, sizeof(sql) - pos, " WHERE %s=%s", d->colNames[keyCol], keyVal);
|
snprintf(sql + pos, sizeof(sql) - pos, " WHERE %s=%s", d->colNames[keyCol], keyVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
dvxSqlExec(db, sql);
|
dvxSqlExec(db, sql);
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,14 @@
|
||||||
// 2. TextArea -- multi-line text editor with row/col cursor,
|
// 2. TextArea -- multi-line text editor with row/col cursor,
|
||||||
// dual-axis scrolling, and full selection/clipboard support.
|
// dual-axis scrolling, and full selection/clipboard support.
|
||||||
//
|
//
|
||||||
// Shared infrastructure (clipboard, multi-click detection, word
|
// Shared text-editing infrastructure lives in the texthelp library
|
||||||
// boundary logic, cross-widget selection clearing, and the
|
// (src/libs/kpunch/texthelp/textHelp.c/h): clipboard, multi-click
|
||||||
// single-line editing engine widgetTextEditOnKey) lives in
|
// detection, word-boundary helpers, cross-widget selection clearing,
|
||||||
// widgetCore.c so it can be linked from any widget DXE.
|
// the single-line edit engine, and the full multi-line edit engine
|
||||||
|
// (cache, paint, mouse/drag, key handler, find/replace, goto-line,
|
||||||
|
// get-word-at-cursor). This widget is a thin consumer that owns
|
||||||
|
// widget chrome (border, gutter, scrollbars, colorizer callbacks)
|
||||||
|
// and routes editing calls into the library via TextEditMultiT.
|
||||||
//
|
//
|
||||||
// All text editing is done in-place in fixed-size char buffers
|
// All text editing is done in-place in fixed-size char buffers
|
||||||
// allocated at widget creation. No dynamic resizing -- this keeps
|
// allocated at widget creation. No dynamic resizing -- this keeps
|
||||||
|
|
@ -51,22 +55,6 @@
|
||||||
// "low" end for deletion/copy is always min(start, end). The
|
// "low" end for deletion/copy is always min(start, end). The
|
||||||
// -1 sentinel means "no selection".
|
// -1 sentinel means "no selection".
|
||||||
//
|
//
|
||||||
// Clipboard is a simple static buffer (4KB). This is a process-wide
|
|
||||||
// clipboard, not per-widget and not OS-integrated (DOS has no
|
|
||||||
// clipboard API). Text cut/copied from any widget is available to
|
|
||||||
// paste in any other widget.
|
|
||||||
//
|
|
||||||
// Multi-click detection uses clock() timestamps with a 500ms
|
|
||||||
// threshold and 4px spatial tolerance. Double-click selects word,
|
|
||||||
// triple-click selects line (TextArea) or all (TextInput).
|
|
||||||
//
|
|
||||||
// Cross-widget selection clearing: when a widget gains selection,
|
|
||||||
// clearOtherSelections() deselects any other widget that had an
|
|
||||||
// active selection. This prevents the confusing visual state of
|
|
||||||
// multiple selected ranges across different widgets. The tracking
|
|
||||||
// uses sLastSelectedWidget to achieve O(1) clearing rather than
|
|
||||||
// walking the entire widget tree.
|
|
||||||
//
|
|
||||||
// Masked input: a special TextInput mode where the buffer is
|
// Masked input: a special TextInput mode where the buffer is
|
||||||
// pre-filled from a mask pattern (e.g., "###-##-####" for SSN).
|
// pre-filled from a mask pattern (e.g., "###-##-####" for SSN).
|
||||||
// '#' accepts digits, 'A' accepts letters, '*' accepts any
|
// '#' accepts digits, 'A' accepts letters, '*' accepts any
|
||||||
|
|
@ -161,6 +149,11 @@ typedef struct {
|
||||||
// Match the ANSI terminal cursor blink rate (CURSOR_MS in widgetAnsiTerm.c)
|
// Match the ANSI terminal cursor blink rate (CURSOR_MS in widgetAnsiTerm.c)
|
||||||
#define CURSOR_BLINK_MS 250
|
#define CURSOR_BLINK_MS 250
|
||||||
|
|
||||||
|
// Default buffer sizes when the caller passes maxLen <= 0 and when
|
||||||
|
// the BASIC iface registers a control without specifying a size.
|
||||||
|
#define TEXTINPUT_DEFAULT_BUF 256
|
||||||
|
#define TEXTAREA_DEFAULT_BUF 65536
|
||||||
|
|
||||||
// sCursorBlinkOn is defined in widgetCore.c (shared state)
|
// sCursorBlinkOn is defined in widgetCore.c (shared state)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1228,7 +1221,7 @@ WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w->data = ti;
|
w->data = ti;
|
||||||
int32_t bufSize = maxLen > 0 ? maxLen + 1 : 256;
|
int32_t bufSize = maxLen > 0 ? maxLen + 1 : TEXTINPUT_DEFAULT_BUF;
|
||||||
ti->buf = (char *)malloc(bufSize);
|
ti->buf = (char *)malloc(bufSize);
|
||||||
ti->bufSize = bufSize;
|
ti->bufSize = bufSize;
|
||||||
|
|
||||||
|
|
@ -1970,7 +1963,7 @@ static const WgtIfaceT sIfaceTextInput = {
|
||||||
.events = NULL,
|
.events = NULL,
|
||||||
.eventCount = 0,
|
.eventCount = 0,
|
||||||
.createSig = WGT_CREATE_PARENT_INT,
|
.createSig = WGT_CREATE_PARENT_INT,
|
||||||
.createArgs = { 256 },
|
.createArgs = { TEXTINPUT_DEFAULT_BUF },
|
||||||
.defaultEvent = "Change"
|
.defaultEvent = "Change"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1983,7 +1976,7 @@ static const WgtIfaceT sIfaceTextArea = {
|
||||||
.events = NULL,
|
.events = NULL,
|
||||||
.eventCount = 0,
|
.eventCount = 0,
|
||||||
.createSig = WGT_CREATE_PARENT_INT,
|
.createSig = WGT_CREATE_PARENT_INT,
|
||||||
.createArgs = { 65536 },
|
.createArgs = { TEXTAREA_DEFAULT_BUF },
|
||||||
.defaultEvent = "Change"
|
.defaultEvent = "Change"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue