util: simplifications and doc fixes

* add `qe_shell_match` to replace `fnmatch`
* fix some documentation errors
This commit is contained in:
Charlie Gordon 2026-05-15 22:29:56 +02:00
commit ca1eb2ff88
3 changed files with 85 additions and 63 deletions

View file

@ -214,7 +214,7 @@ Note: `size` must be strictly positive and `pos` must be in range: `0 <= pos < s
Note: this function does not set a null terminator at offset `pos`.
### `int buf_avail(buf_t *bp);`
### `int buf_avail(const buf_t *bp);`
Compute the number of bytes available in the destination array
@ -576,6 +576,8 @@ Get the numerical value associated with a codepoint
Return the corresponding numerical value, or 255 for none
ie: `'0'` -> `0`, `'1'` -> `1`, `'a'` -> 10, `'Z'` -> 35
Note: only ASCII digits are supported
### `int qe_findchar(const char *str, char32_t c);`
Test if a codepoint value is part of a set of ASCII characters
@ -615,6 +617,8 @@ Range test for codepoint values
Return a boolean value indicating if the codepoint is inside the range
Note: both boundaries are included in the range
### `int qe_isalnum(char32_t c);`
Test if a codepoint represents a letter or a digit
@ -683,7 +687,7 @@ Test if a codepoint represents blank space
Return a boolean value indicating if the codepoint is blank space
Note: only ASCII blanks and non-breaking-space are supported
Note: only ASCII blanks (SPC and TAB) and non-breaking-space are supported
### `int qe_isdigit(char32_t c);`
@ -845,10 +849,7 @@ Note: only ASCII letters are supported
Free the allocated memory pointed to by a pointer whose address is passed.
* argument `pp` the address of a possibly null pointer. This pointer is set
to `NULL` after freeing the memory. If the pointer memory is null,
nothing happens.
* argument `n` the number of bytes to allocate in addition to the size of type `T`.
to `NULL` after freeing the memory. If the pointer is null, nothing happens.
Note: this function is implemented as a macro.
@ -936,17 +937,6 @@ The objects are initialized to all bits zero.
Note: this function is implemented as a macro.
### `T *qe_mallocz_array(type T, size_t n);`
Allocate memory for an object of type `T` with `n` extra bytes.
The object and the extra space is initialized to all bits zero.
* argument `T` the type of the object to allocate.
* argument `n` the number of bytes to allocate in addition to the size of type `T`.
Note: this function is implemented as a macro.
### `void *qe_mallocz_bytes(size_t size);`
Allocate a block of memory of a given size in bytes initialized
@ -957,6 +947,17 @@ to all bits zero.
Return a pointer to allocated memory, aligned on the maximum
alignment size.
### `T *qe_mallocz_hack(type T, size_t n);`
Allocate memory for an object of type `T` with `n` extra bytes.
The object and the extra space are initialized to all bits zero.
* argument `T` the type of the object to allocate.
* argument `n` the number of bytes to allocate in addition to the size of type `T`.
Note: this function is implemented as a macro.
### `T *qe_realloc_array(T **pp, size_t new_len);`
Reallocate a block of memory to a different size.
@ -1946,7 +1947,7 @@ Get the next match in a directory enumeration.
* argument `filename_size_max` the length if the `filename` destination
array in bytes.
Return `0` if there is a match, `-1` if no more files matche the pattern.
Return `0` if there is a match, `-1` if no more files match the pattern.
### `FindFileState *find_file_open(const char *path, const char *pattern, int flags);`
@ -2374,6 +2375,21 @@ and insertion sort for small chunks. The GNU lib C on linux also
has a function `qsort_r()` with similar semantics but a different
calling convention.
### `int qe_shell_match(const char *string, const char *pattern);`
Test whether a filename or pathname matches a shell-style pattern
* argument `string` a valid pointer to a string representing a
filename
* argument `pattern` a valid pointer to a file pattern using `?`
and `*` with the classic semantics used by unix shells
Return non zero if `string` matches `pattern`.
Note: this function is a simplified version of POSIX function
`fnmatch` defined in header `<fnmatch.h>`
### `int qe_skip_spaces(const char **pp);`
Skip white space at the beginning of the string pointed to by `*pp`.

43
util.c
View file

@ -2,7 +2,7 @@
* Utilities for qemacs.
*
* Copyright (c) 2001 Fabrice Bellard.
* Copyright (c) 2002-2025 Charlie Gordon.
* Copyright (c) 2002-2026 Charlie Gordon.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -35,38 +35,40 @@
#include "config.h" /* for CONFIG_WIN32 */
#include "util.h"
#ifdef CONFIG_WIN32
/* XXX: not sufficient, but OK for basic operations */
// XXX: should use own own function in all cases
int fnmatch(const char *pattern, const char *string, int flags) {
int qe_shell_match(const char *string, const char *pattern) {
/*@API utils
Test whether a filename or pathname matches a shell-style pattern
@argument `string` a valid pointer to a string representing a
filename
@argument `pattern` a valid pointer to a file pattern using `?`
and `*` with the classic semantics used by unix shells
@return non zero if `string` matches `pattern`.
@note this function is a simplified version of POSIX function
`fnmatch` defined in header `<fnmatch.h>`
*/
char c;
while ((c = *pattern++) != '\0') {
if (c == '*') {
if (*pattern == '\0')
return 0;
return 1;
while (*string) {
if (!fnmatch(pattern, string))
return 0;
if (qe_shell_match(string, pattern))
return 1;
string++;
}
return FNM_NOMATCH;
return 0;
} else
if (c == '?') {
if (*string++ == '\0')
return FNM_NOMATCH;
return 0;
} else
if (c != *string++) {
return FNM_NOMATCH;
return 0;
}
}
return *string ? FNM_NOMATCH : 0;
return *string == '\0';
}
#else
#include <fnmatch.h>
#endif
#define MAX_FILENAME_SIZE 1024 /* Size for a filename buffer */
struct FindFileState {
@ -89,6 +91,7 @@ FindFileState *find_file_open(const char *path, const char *pattern, int flags)
semantics used by unix shells
@return a pointer to an opaque FindFileState structure.
*/
// XXX: should check if pattern has wildcards
FindFileState *s;
s = qe_mallocz(FindFileState);
@ -108,8 +111,9 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
@argument `filename` a valid pointer to an array for the file name.
@argument `filename_size_max` the length if the `filename` destination
array in bytes.
@return `0` if there is a match, `-1` if no more files matche the pattern.
@return `0` if there is a match, `-1` if no more files match the pattern.
*/
// XXX: should match wildcards in directory names
struct dirent *dirent;
const char *p;
@ -169,8 +173,7 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) {
if (s->flags & FF_ONLYDIR)
continue;
}
// XXX: should use our own fnmatch version
if (fnmatch(s->pattern, dirent->d_name, 0) == 0) {
if (qe_shell_match(dirent->d_name, s->pattern)) {
makepath(filename, filename_size_max,
s->dirpath, dirent->d_name);
return 0;

53
util.h
View file

@ -2,7 +2,7 @@
* Utilities for qemacs.
*
* Copyright (c) 2000-2001 Fabrice Bellard.
* Copyright (c) 2000-2025 Charlie Gordon.
* Copyright (c) 2000-2026 Charlie Gordon.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -47,21 +47,6 @@
#define MAX_WORD_SIZE 128
#define MAX_FILENAME_SIZE 1024
/* string arrays */
typedef struct StringItem {
void *opaque; /* opaque data that the user can use */
char selected; /* true if selected */
char group; /* used to group sorted items */
char str[1];
} StringItem;
typedef struct StringArray {
int nb_allocated;
int nb_items;
StringItem **items;
} StringArray;
#define NULL_STRINGARRAY { 0, 0, NULL }
typedef struct FindFileState FindFileState;
#define FF_PATH 0x010 /* enumerate path argument */
@ -73,6 +58,7 @@ typedef struct FindFileState FindFileState;
FindFileState *find_file_open(const char *path, const char *pattern, int flags);
int find_file_next(FindFileState *s, char *filename, int filename_size_max);
void find_file_close(FindFileState **sp);
int qe_shell_match(const char *pattern, const char *string);
int is_directory(const char *path);
int is_filepattern(const char *filespec);
char *canonicalize_path(char *buf, int buf_size, const char *path);
@ -107,6 +93,7 @@ static inline int qe_digit_value(char32_t c) {
@argument `c` a codepoint value
@return the corresponding numerical value, or 255 for none
ie: `'0'` -> `0`, `'1'` -> `1`, `'a'` -> 10, `'Z'` -> 35
@note: only ASCII digits are supported
*/
return c < 128 ? qe_digit_value__[c] : 255;
}
@ -118,6 +105,7 @@ static inline int qe_inrange(char32_t c, char32_t a, char32_t b) {
@argument `a` the minimum codepoint value for the range
@argument `b` the maximum codepoint value for the range
@return a boolean value indicating if the codepoint is inside the range
@note: both boundaries are included in the range
*/
//return c >= a && c <= b;
//CG: assuming a <= b and wrap around semantics for (c - a) and (b - a)
@ -140,7 +128,7 @@ static inline int qe_isblank(char32_t c) {
Test if a codepoint represents blank space
@argument `c` a codepoint value
@return a boolean value indicating if the codepoint is blank space
@note: only ASCII blanks and non-breaking-space are supported
@note: only ASCII blanks (SPC and TAB) and non-breaking-space are supported
*/
return (c == ' ' || c == '\t' || c == 160);
}
@ -501,10 +489,10 @@ T *qe_malloc_hack(type T, size_t n);
@note this function is implemented as a macro.
*/
T *qe_mallocz_array(type T, size_t n);
T *qe_mallocz_hack(type T, size_t n);
/*@API memory
Allocate memory for an object of type `T` with `n` extra bytes.
The object and the extra space is initialized to all bits zero.
The object and the extra space are initialized to all bits zero.
@argument `T` the type of the object to allocate.
@argument `n` the number of bytes to allocate in addition to the size of type `T`.
@note this function is implemented as a macro.
@ -535,9 +523,7 @@ void qe_free(T **pp);
/*@API memory
Free the allocated memory pointed to by a pointer whose address is passed.
@argument `pp` the address of a possibly null pointer. This pointer is set
to `NULL` after freeing the memory. If the pointer memory is null,
nothing happens.
@argument `n` the number of bytes to allocate in addition to the size of type `T`.
to `NULL` after freeing the memory. If the pointer is null, nothing happens.
@note this function is implemented as a macro.
*/
#endif
@ -575,6 +561,21 @@ void qe_free(T **pp);
// XXX: Should unify these string packages but keep API simple
/* string arrays */
typedef struct StringItem {
void *opaque; /* opaque data that the user can use */
char selected; /* true if selected */
char group; /* used to group sorted items */
char str[1];
} StringItem;
typedef struct StringArray {
int nb_allocated;
int nb_items;
StringItem **items;
} StringArray;
#define NULL_STRINGARRAY { 0, 0, NULL }
StringItem *set_string(StringArray *cs, int index, const char *str, int group);
StringItem *add_string(StringArray *cs, const char *str, int group);
int remove_string(StringArray *cs, const char *str);
@ -634,7 +635,7 @@ static inline buf_t *buf_attach(buf_t *bp, char *buf, int size, int pos) {
return bp;
}
static inline int buf_avail(buf_t *bp) {
static inline int buf_avail(const buf_t *bp) {
/*@API buf
Compute the number of bytes available in the destination array
@argument `bp` a valid pointer to fixed length buffer
@ -650,11 +651,13 @@ static inline int buf_put_byte(buf_t *bp, unsigned char ch) {
@argument `ch` a byte
@return the number of bytes actually written.
*/
if (bp->pos + 1 < bp->size) {
bp->pos++;
if (bp->pos < bp->size) {
bp->buf[bp->len++] = ch;
bp->buf[bp->len] = '\0';
return 1;
}
return bp->pos++;
return 0;
}
int buf_write(buf_t *bp, const void *src, int size);