Skip to content

Commit

Permalink
vstring: Make vStringPut*() usage more forgiving
Browse files Browse the repository at this point in the history
vStringPut() and friends take an `unsigned char` as an `int`, similar
to ctype functions, in order to be easy to use with `fgetc()`-style
functions.

However, this API is also used on regular C strings, which require a
cast if the value is larger than 0x7F (127) on systems where `char` is
a signed type.

In order to make the API easier to use, as it's easy to forget the cast
when working with `char`, introduce wrapper macros that add the cast
when called with `char`.  The cast is conditional so the underlying
implementation can still verify the value is in the valid range when
called with an `int` (to catch erroneous calls with EOF or other values
that do not fit a `char`).

Note that this will still not work properly if the caller has an
incorrect cast on its side, like e.g. `vStringPut(s, (int) c)` where
`c` is a `char`, as there's only so many magic tricks up our sleeves.
These calls should be updated to either be `vStringPut(s, c)` with the
added macros, or `vStringPut(s, (unsigned char) c)` if one wants to be
explicit -- yet no need to go all the trouble to make them
`vStringPut(s, (int) (unsigned char) c)`, as the `unsigned char` to
`int` conversion is implicit and safe.

Based off a suggestion from Masatake YAMATO <yamato@redhat.com>
  • Loading branch information
b4n committed May 23, 2023
1 parent a7802b5 commit e6e018b
Showing 1 changed file with 12 additions and 3 deletions.
15 changes: 12 additions & 3 deletions main/vstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ CTAGS_INLINE void vStringPutNewlinAgainUnsafe (vString *const string)
string->buffer [string->length++] = '\n';
}

CTAGS_INLINE void vStringPut (vString *const string, const int c)
CTAGS_INLINE void vStringPutImpl (vString *const string, const int c)
{
/* verify the given character is an unsigned char value */
Assert (c >= 0 && c <= 0xff);
Expand All @@ -115,8 +115,12 @@ CTAGS_INLINE void vStringPut (vString *const string, const int c)
string->buffer [++string->length] = '\0';
}

CTAGS_INLINE bool vStringPutWithLimit (vString *const string, const int c,
unsigned int maxlen)
#define vStringPut(s, c) (sizeof(c) == sizeof(char) \
? vStringPutImpl((s), (unsigned char) (c)) \
: vStringPutImpl((s), (c)))

CTAGS_INLINE bool vStringPutWithLimitImpl (vString *const string, const int c,
unsigned int maxlen)
{
if (vStringLength (string) < maxlen || maxlen == 0)
{
Expand All @@ -126,4 +130,9 @@ CTAGS_INLINE bool vStringPutWithLimit (vString *const string, const int c,
return false;
}

#define vStringPutWithLimit(s, c, l) \
(sizeof(c) == sizeof(char) \
? vStringPutWithLimitImpl((s), (unsigned char) (c), (l)) \
: vStringPutWithLimitImpl((s), (c), (l)))

#endif /* CTAGS_MAIN_VSTRING_H */

0 comments on commit e6e018b

Please sign in to comment.