Merge branch 'tb/ban-strtok'

Mark strtok() and strtok_r() to be banned.

* tb/ban-strtok:
  banned.h: mark `strtok()` and `strtok_r()` as banned
  t/helper/test-json-writer.c: avoid using `strtok()`
  t/helper/test-oidmap.c: avoid using `strtok()`
  t/helper/test-hashmap.c: avoid using `strtok()`
  string-list: introduce `string_list_setlen()`
  string-list: multi-delimiter `string_list_split_in_place()`
This commit is contained in:
Junio C Hamano 2023-05-02 10:13:35 -07:00
commit d699e27bd4
12 changed files with 161 additions and 51 deletions

View File

@ -18,6 +18,10 @@
#define strncpy(x,y,n) BANNED(strncpy)
#undef strncat
#define strncat(x,y,n) BANNED(strncat)
#undef strtok
#define strtok(x,y) BANNED(strtok)
#undef strtok_r
#define strtok_r(x,y,z) BANNED(strtok_r)
#undef sprintf
#undef vsprintf

View File

@ -1685,11 +1685,11 @@ static int get_schedule_cmd(const char **cmd, int *is_available)
if (is_available)
*is_available = 0;
string_list_split_in_place(&list, testing, ',', -1);
string_list_split_in_place(&list, testing, ",", -1);
for_each_string_list_item(item, &list) {
struct string_list pair = STRING_LIST_INIT_NODUP;
if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
if (string_list_split_in_place(&pair, item->string, ":", 2) != 2)
continue;
if (!strcmp(*cmd, pair.items[0].string)) {

2
diff.c
View File

@ -139,7 +139,7 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
int i;
if (*params_copy)
string_list_split_in_place(&params, params_copy, ',', -1);
string_list_split_in_place(&params, params_copy, ",", -1);
for (i = 0; i < params.nr; i++) {
const char *p = params.items[i].string;
if (!strcmp(p, "changes")) {

View File

@ -964,7 +964,7 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
char *globs_copy = xstrdup(globs);
int i;
string_list_split_in_place(&split, globs_copy, ':', -1);
string_list_split_in_place(&split, globs_copy, ":", -1);
string_list_remove_empty_items(&split, 0);
for (i = 0; i < split.nr; i++)

View File

@ -651,7 +651,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
snapshot->buf,
snapshot->eof - snapshot->buf);
string_list_split_in_place(&traits, p, ' ', -1);
string_list_split_in_place(&traits, p, " ", -1);
if (unsorted_string_list_has_string(&traits, "fully-peeled"))
snapshot->peeled = PEELED_FULLY;

View File

@ -203,6 +203,15 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
list->nr = list->alloc = 0;
}
void string_list_setlen(struct string_list *list, size_t nr)
{
if (list->strdup_strings)
BUG("cannot setlen a string_list which owns its entries");
if (nr > list->nr)
BUG("cannot grow a string_list with setlen");
list->nr = nr;
}
struct string_list_item *string_list_append_nodup(struct string_list *list,
char *string)
{
@ -301,7 +310,7 @@ int string_list_split(struct string_list *list, const char *string,
}
int string_list_split_in_place(struct string_list *list, char *string,
int delim, int maxsplit)
const char *delim, int maxsplit)
{
int count = 0;
char *p = string, *end;
@ -315,7 +324,7 @@ int string_list_split_in_place(struct string_list *list, char *string,
string_list_append(list, p);
return count;
}
end = strchr(p, delim);
end = strpbrk(p, delim);
if (end) {
*end = '\0';
string_list_append(list, p);

View File

@ -134,6 +134,16 @@ typedef void (*string_list_clear_func_t)(void *p, const char *str);
/** Call a custom clear function on each util pointer */
void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
/*
* Set the length of a string_list to `nr`, provided that (a) `list`
* does not own its own storage, and (b) that `nr` is no larger than
* `list->nr`.
*
* Useful when "shrinking" `list` to write over existing entries that
* are no longer used without reallocating.
*/
void string_list_setlen(struct string_list *list, size_t nr);
/**
* Apply `func` to each item. If `func` returns nonzero, the
* iteration aborts and the return value is propagated.
@ -270,5 +280,5 @@ int string_list_split(struct string_list *list, const char *string,
* list->strdup_strings must *not* be set.
*/
int string_list_split_in_place(struct string_list *list, char *string,
int delim, int maxsplit);
const char *delim, int maxsplit);
#endif /* STRING_LIST_H */

View File

@ -2,6 +2,7 @@
#include "git-compat-util.h"
#include "hashmap.h"
#include "strbuf.h"
#include "string-list.h"
struct test_entry
{
@ -150,6 +151,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds)
*/
int cmd__hashmap(int argc, const char **argv)
{
struct string_list parts = STRING_LIST_INIT_NODUP;
struct strbuf line = STRBUF_INIT;
int icase;
struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase);
@ -159,21 +161,26 @@ int cmd__hashmap(int argc, const char **argv)
/* process commands from stdin */
while (strbuf_getline(&line, stdin) != EOF) {
char *cmd, *p1 = NULL, *p2 = NULL;
char *cmd, *p1, *p2;
unsigned int hash = 0;
struct test_entry *entry;
/* break line into command and up to two parameters */
cmd = strtok(line.buf, DELIM);
string_list_setlen(&parts, 0);
string_list_split_in_place(&parts, line.buf, DELIM, 2);
string_list_remove_empty_items(&parts, 0);
/* ignore empty lines */
if (!cmd || *cmd == '#')
if (!parts.nr)
continue;
if (!*parts.items[0].string || *parts.items[0].string == '#')
continue;
p1 = strtok(NULL, DELIM);
if (p1) {
cmd = parts.items[0].string;
p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
if (p1)
hash = icase ? strihash(p1) : strhash(p1);
p2 = strtok(NULL, DELIM);
}
if (!strcmp("add", cmd) && p1 && p2) {
@ -260,6 +267,7 @@ int cmd__hashmap(int argc, const char **argv)
}
}
string_list_clear(&parts, 0);
strbuf_release(&line);
hashmap_clear_and_free(&map, struct test_entry, ent);
return 0;

View File

@ -1,5 +1,6 @@
#include "test-tool.h"
#include "json-writer.h"
#include "string-list.h"
static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
@ -394,35 +395,41 @@ static int unit_tests(void)
return 0;
}
static void get_s(int line_nr, char **s_in)
struct line {
struct string_list *parts;
size_t consumed_nr;
int nr;
};
static void get_s(struct line *line, char **s_in)
{
*s_in = strtok(NULL, " ");
if (!*s_in)
die("line[%d]: expected: <s>", line_nr);
if (line->consumed_nr > line->parts->nr)
die("line[%d]: expected: <s>", line->nr);
*s_in = line->parts->items[line->consumed_nr++].string;
}
static void get_i(int line_nr, intmax_t *s_in)
static void get_i(struct line *line, intmax_t *s_in)
{
char *s;
char *endptr;
get_s(line_nr, &s);
get_s(line, &s);
*s_in = strtol(s, &endptr, 10);
if (*endptr || errno == ERANGE)
die("line[%d]: invalid integer value", line_nr);
die("line[%d]: invalid integer value", line->nr);
}
static void get_d(int line_nr, double *s_in)
static void get_d(struct line *line, double *s_in)
{
char *s;
char *endptr;
get_s(line_nr, &s);
get_s(line, &s);
*s_in = strtod(s, &endptr);
if (*endptr || errno == ERANGE)
die("line[%d]: invalid float value", line_nr);
die("line[%d]: invalid float value", line->nr);
}
static int pretty;
@ -453,6 +460,7 @@ static char *get_trimmed_line(char *buf, int buf_size)
static int scripted(void)
{
struct string_list parts = STRING_LIST_INIT_NODUP;
struct json_writer jw = JSON_WRITER_INIT;
char buf[MAX_LINE_LENGTH];
char *line;
@ -470,66 +478,77 @@ static int scripted(void)
die("expected first line to be 'object' or 'array'");
while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
struct line state = { 0 };
char *verb;
char *key;
char *s_value;
intmax_t i_value;
double d_value;
line_nr++;
state.parts = &parts;
state.nr = ++line_nr;
verb = strtok(line, " ");
/* break line into command and zero or more tokens */
string_list_setlen(&parts, 0);
string_list_split_in_place(&parts, line, " ", -1);
string_list_remove_empty_items(&parts, 0);
/* ignore empty lines */
if (!parts.nr || !*parts.items[0].string)
continue;
verb = parts.items[state.consumed_nr++].string;
if (!strcmp(verb, "end")) {
jw_end(&jw);
}
else if (!strcmp(verb, "object-string")) {
get_s(line_nr, &key);
get_s(line_nr, &s_value);
get_s(&state, &key);
get_s(&state, &s_value);
jw_object_string(&jw, key, s_value);
}
else if (!strcmp(verb, "object-int")) {
get_s(line_nr, &key);
get_i(line_nr, &i_value);
get_s(&state, &key);
get_i(&state, &i_value);
jw_object_intmax(&jw, key, i_value);
}
else if (!strcmp(verb, "object-double")) {
get_s(line_nr, &key);
get_i(line_nr, &i_value);
get_d(line_nr, &d_value);
get_s(&state, &key);
get_i(&state, &i_value);
get_d(&state, &d_value);
jw_object_double(&jw, key, i_value, d_value);
}
else if (!strcmp(verb, "object-true")) {
get_s(line_nr, &key);
get_s(&state, &key);
jw_object_true(&jw, key);
}
else if (!strcmp(verb, "object-false")) {
get_s(line_nr, &key);
get_s(&state, &key);
jw_object_false(&jw, key);
}
else if (!strcmp(verb, "object-null")) {
get_s(line_nr, &key);
get_s(&state, &key);
jw_object_null(&jw, key);
}
else if (!strcmp(verb, "object-object")) {
get_s(line_nr, &key);
get_s(&state, &key);
jw_object_inline_begin_object(&jw, key);
}
else if (!strcmp(verb, "object-array")) {
get_s(line_nr, &key);
get_s(&state, &key);
jw_object_inline_begin_array(&jw, key);
}
else if (!strcmp(verb, "array-string")) {
get_s(line_nr, &s_value);
get_s(&state, &s_value);
jw_array_string(&jw, s_value);
}
else if (!strcmp(verb, "array-int")) {
get_i(line_nr, &i_value);
get_i(&state, &i_value);
jw_array_intmax(&jw, i_value);
}
else if (!strcmp(verb, "array-double")) {
get_i(line_nr, &i_value);
get_d(line_nr, &d_value);
get_i(&state, &i_value);
get_d(&state, &d_value);
jw_array_double(&jw, i_value, d_value);
}
else if (!strcmp(verb, "array-true"))
@ -552,6 +571,7 @@ static int scripted(void)
printf("%s\n", jw.json.buf);
jw_release(&jw);
string_list_clear(&parts, 0);
return 0;
}

View File

@ -4,6 +4,7 @@
#include "oidmap.h"
#include "setup.h"
#include "strbuf.h"
#include "string-list.h"
/* key is an oid and value is a name (could be a refname for example) */
struct test_entry {
@ -25,6 +26,7 @@ struct test_entry {
*/
int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
{
struct string_list parts = STRING_LIST_INIT_NODUP;
struct strbuf line = STRBUF_INIT;
struct oidmap map = OIDMAP_INIT;
@ -35,19 +37,24 @@ int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
/* process commands from stdin */
while (strbuf_getline(&line, stdin) != EOF) {
char *cmd, *p1 = NULL, *p2 = NULL;
char *cmd, *p1, *p2;
struct test_entry *entry;
struct object_id oid;
/* break line into command and up to two parameters */
cmd = strtok(line.buf, DELIM);
string_list_setlen(&parts, 0);
string_list_split_in_place(&parts, line.buf, DELIM, 2);
string_list_remove_empty_items(&parts, 0);
/* ignore empty lines */
if (!cmd || *cmd == '#')
if (!parts.nr)
continue;
if (!*parts.items[0].string || *parts.items[0].string == '#')
continue;
p1 = strtok(NULL, DELIM);
if (p1)
p2 = strtok(NULL, DELIM);
cmd = parts.items[0].string;
p1 = parts.nr >= 1 ? parts.items[1].string : NULL;
p2 = parts.nr >= 2 ? parts.items[2].string : NULL;
if (!strcmp("put", cmd) && p1 && p2) {
@ -108,6 +115,7 @@ int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
}
}
string_list_clear(&parts, 0);
strbuf_release(&line);
oidmap_free(&map, 1);
return 0;

View File

@ -62,7 +62,7 @@ int cmd__string_list(int argc, const char **argv)
struct string_list list = STRING_LIST_INIT_NODUP;
int i;
char *s = xstrdup(argv[2]);
int delim = *argv[3];
const char *delim = argv[3];
int maxsplit = atoi(argv[4]);
i = string_list_split_in_place(&list, s, delim, maxsplit);
@ -111,7 +111,7 @@ int cmd__string_list(int argc, const char **argv)
*/
if (sb.len && sb.buf[sb.len - 1] == '\n')
strbuf_setlen(&sb, sb.len - 1);
string_list_split_in_place(&list, sb.buf, '\n', -1);
string_list_split_in_place(&list, sb.buf, "\n", -1);
string_list_sort(&list);

View File

@ -18,6 +18,14 @@ test_split () {
"
}
test_split_in_place() {
cat >expected &&
test_expect_success "split (in place) $1 at $2, max $3" "
test-tool string-list split_in_place '$1' '$2' '$3' >actual &&
test_cmp expected actual
"
}
test_split "foo:bar:baz" ":" "-1" <<EOF
3
[0]: "foo"
@ -61,6 +69,49 @@ test_split ":" ":" "-1" <<EOF
[1]: ""
EOF
test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF
10
[0]: "foo"
[1]: ""
[2]: ""
[3]: "bar"
[4]: ""
[5]: ""
[6]: "baz"
[7]: ""
[8]: ""
[9]: ""
EOF
test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF
1
[0]: "foo:;:bar:;:baz"
EOF
test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF
2
[0]: "foo"
[1]: ";:bar:;:baz"
EOF
test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF
3
[0]: "foo"
[1]: ""
[2]: ":bar:;:baz"
EOF
test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF
7
[0]: "foo"
[1]: ""
[2]: ""
[3]: "bar"
[4]: ""
[5]: ""
[6]: ""
EOF
test_expect_success "test filter_string_list" '
test "x-" = "x$(test-tool string-list filter - y)" &&
test "x-" = "x$(test-tool string-list filter no y)" &&