diff --git a/qe-manual.md b/qe-manual.md index 5e79c34..f07bc18 100644 --- a/qe-manual.md +++ b/qe-manual.md @@ -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 `` + ### `int qe_skip_spaces(const char **pp);` Skip white space at the beginning of the string pointed to by `*pp`. diff --git a/util.c b/util.c index e14ec1c..c94480d 100644 --- a/util.c +++ b/util.c @@ -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 `` + */ 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 -#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; diff --git a/util.h b/util.h index 5a9a893..af81fc9 100644 --- a/util.h +++ b/util.h @@ -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);