чување fdd05361bfeb8eac18c6d0653b22d9639211e148
родитељ c7a458f3a45badfd8094e5cd69e10d556a6ab846
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Sun, 22 Mar 2026 20:20:54 +0000
Replace sscanf with new function: strtonum, for more robust number
parsing
Diffstat:
измењених датотека: 8, додавања: 134(+), брисања: 12(-)
diff --git a/Makefile b/Makefile
@@ -28,7 +28,7 @@ makedate:
makeversion:
sh lib/makeversion -l
-poe.o: poe.c config.h draw.h limit.h po.h termbox.h util.h version.h
+poe.o: poe.c config.h draw.h limit.h po.h strtonum.h termbox.h util.h version.h
$(MANPAGE): $(MANPAGE).in date version
diff --git a/config.Haiku b/config.Haiku
@@ -5,9 +5,10 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
DESTDIR = /boot/home/config/non-packaged
INSTALL = install
LIBS =
-SRC = draw.c limit.c po.c poe.c util.c
-HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
-OBJS = draw.o limit.o po.o poe.o util.o
+SRC = draw.c limit.c po.c poe.c strtonum.c util.c
+HEADERS = config.h draw.h limit.h po.h strtonum.h termbox.h util.h \
+ version.h
+OBJS = draw.o limit.o po.o poe.o strtonum.o util.o
PREFIX =
PROG = poe
diff --git a/config.Linux b/config.Linux
@@ -3,9 +3,10 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-D_XOPEN_SOURCE=700 -DNDEBUG
INSTALL = install
LIBS =
-SRC = draw.c limit.c po.c poe.c util.c
-HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
-OBJS = draw.o limit.o po.o poe.o util.o
+SRC = draw.c limit.c po.c poe.c strtonum.c util.c
+HEADERS = config.h draw.h limit.h po.h strtonum.h termbox.h util.h \
+ version.h
+OBJS = draw.o limit.o po.o poe.o strtonum.o util.o
PREFIX = /usr/local
PROG = poe
diff --git a/config.OpenBSD b/config.OpenBSD
@@ -3,9 +3,10 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-D_XOPEN_SOURCE=700 -D_BSD_SOURCE -DNDEBUG
INSTALL = install
LIBS =
-SRC = draw.c limit.c po.c poe.c util.c
-HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
-OBJS = draw.o limit.o po.o poe.o util.o
+SRC = draw.c limit.c po.c poe.c strtonum.c util.c
+HEADERS = config.h draw.h limit.h po.h strtonum.h termbox.h util.h \
+ version.h
+OBJS = draw.o limit.o po.o poe.o strtonum.o util.o
PREFIX = /usr/local
PROG = poe
diff --git a/po.c b/po.c
@@ -14,6 +14,7 @@
#include "config.h"
#include "limit.h"
#include "po.h"
+#include "strtonum.h"
#include "util.h"
#include "version.h"
@@ -1335,6 +1336,7 @@ set_nplurals(struct DrawState* state, struct PoEntry* entry)
uint32_t* ppf;
uint32_t* temp;
char* ptoken = token;
+ int errornum;
int read_number = 0;
int nplurals_len = 0;
@@ -1376,7 +1378,9 @@ set_nplurals(struct DrawState* state, struct PoEntry* entry)
}
if (*ptoken)
{
- if (sscanf(ptoken, "%d", &state->nplurals) < 1)
+ state->nplurals
+ = strtonumber(ptoken, 0, INT_MAX, &errornum);
+ if (errornum != STN_NONE)
{
state->error = ERR_BAD_NPLURALS;
return 1;
diff --git a/poe.c b/poe.c
@@ -33,6 +33,7 @@
#include "config.h"
#include "limit.h"
#include "po.h"
+#include "strtonum.h"
#include "util.h"
#include "version.h"
@@ -2592,6 +2593,7 @@ main(int argc, char** argv)
long lineno = 1;
int dir_access;
int dir_access_errno;
+ int errornum;
int optind = 1;
int result;
int saved_errno = 0;
@@ -2695,7 +2697,8 @@ main_options_loop:
|| !strcmp(argv[optind], "--wrap-width="))
{
optind++;
- if (sscanf(argv[optind], "%d", &wrap_width) < 1)
+ wrap_width = strtonumber(argv[optind], 0, INT_MAX, &errornum);
+ if (errornum != STN_NONE)
{
if (*argv[optind] == '-')
state.error = ERR_NO_CL_ARG_PARAM;
diff --git a/strtonum.c b/strtonum.c
@@ -0,0 +1,96 @@
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version. Copyright (C) 2026 Страхиња Радић.
+ * See the file LICENSE for exact copyright and license details. */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "strtonum.h"
+
+/* Returns 0 on no error, otherwise returns 1 after outputting error message */
+int
+stn_handle_error(const char* str, const long long minval,
+ const long long maxval, const int errornum)
+{
+ switch (errornum)
+ {
+ case STN_NOTNUM:
+ fprintf(stderr, "strtonumber: Not a number: %s\n", str);
+ return 1;
+ case STN_INVLIMITS:
+ fprintf(stderr, "strtonumber: Invalid limit specification\n");
+ return 1;
+ case STN_UNDERFLOW:
+ fprintf(stderr, "strtonumber: Underflow: %s < %lld\n", str,
+ minval);
+ return 1;
+ case STN_OVERFLOW:
+ fprintf(stderr, "strtonumber: Overflow: %s > %lld\n", str,
+ maxval);
+ return 1;
+ default:;
+ }
+ return 0;
+}
+
+/* - Returns the converted number or 0 on error (*not* indicative of error; see
+ * below)
+ * - Clears errno (discards any previous value) because of strtoll
+ * - Instead, returns any errors in errornum (if non-NULL);
+ * *errornum == STN_NONE indicates success, any other value indicates error */
+long long
+strtonumber(const char* str, const long long minval, const long long maxval,
+ int* errornum)
+{
+ long long n;
+ char* end = NULL;
+
+ if (minval > maxval)
+ {
+ if (errornum)
+ *errornum = STN_INVLIMITS;
+ return 0;
+ }
+ errno = 0;
+ if (errornum)
+ *errornum = STN_NONE;
+ n = strtoll(str, &end, 10);
+ if (!*str || *end)
+ {
+ errno = 0;
+ if (errornum)
+ *errornum = STN_NOTNUM;
+ return 0;
+ }
+ else if (errno == ERANGE)
+ {
+ errno = 0;
+ if (errornum)
+ *errornum
+ = n == LLONG_MIN ? STN_UNDERFLOW : STN_OVERFLOW;
+ return 0;
+ }
+ else
+ {
+ errno = 0;
+ if (n > maxval)
+ {
+ if (errornum)
+ *errornum = STN_OVERFLOW;
+ return 0;
+ }
+ else if (n < minval)
+ {
+ if (errornum)
+ *errornum = STN_UNDERFLOW;
+ return 0;
+ }
+ else
+ return n;
+ }
+ /* Not reached */
+ return n;
+}
diff --git a/strtonum.h b/strtonum.h
@@ -0,0 +1,16 @@
+/* This program is licensed under the terms of GNU GPL v3 or (at your option)
+ * any later version. Copyright (C) 2026 Страхиња Радић.
+ * See the file LICENSE for exact copyright and license details. */
+
+enum {
+ STN_NONE,
+ STN_NOTNUM,
+ STN_INVLIMITS,
+ STN_UNDERFLOW,
+ STN_OVERFLOW,
+};
+
+int stn_handle_error(const char* str, const long long minval,
+ const long long maxval, const int errornum);
+long long strtonumber(const char* str, const long long minval,
+ const long long maxval, int* errornum);