/*
 * Utilities for qemacs.
 *
 * Copyright (c) 2000-2001 Fabrice Bellard.
 * 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
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef UTIL_H
#define UTIL_H

#include "cutils.h"

/* OS specific defines */

// XXX: should include config.h for CONFIG_WIN32 and CONFIG_HAS_TYPEOF

#ifdef CONFIG_WIN32
#define snprintf   _snprintf
#define vsnprintf  _vsnprintf
#endif

#define OWNED     /* ptr attribute for allocated data owned by a structure */

#define QASSERT(e)   do { if (!(e)) fprintf(stderr, "%s:%d: assertion failed: %s\n", __FILE__, __LINE__, #e); } while (0)

/* prevent gcc warning about shadowing a global declaration */
#define index  index__

#define MAX_WORD_SIZE  128
#define MAX_FILENAME_SIZE 1024

typedef struct FindFileState FindFileState;

#define FF_PATH     0x010  /* enumerate path argument */
#define FF_NODIR    0x020  /* only match regular files */
#define FF_NOXXDIR  0x040  /* do not match . or .. */
#define FF_ONLYDIR  0x080  /* do not match non directories */
#define FF_DEPTH    0x00f  /* max recursion depth */

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);
char *make_user_path(char *buf, int buf_size, const char *path);
char *reduce_filename(char *dest, int size, const char *filename);
char *file_load(const char *filename, int max_size, int *sizep);
int match_extension(const char *filename, const char *extlist);
int match_filename(const char *filename, const char *namespec);
int match_shell_handler(const char *p, const char *list);
// XXX: should move these to cutils?
int remove_slash(char *buf);
int append_slash(char *buf, int buf_size);
char *makepath(char *buf, int buf_size, const char *path, const char *filename);
void splitpath(char *dirname, int dirname_size,
               char *filename, int filename_size, const char *pathname);

/*---- Buffer offset functions ----*/

/* buffer offsets are 32-bit signed integers */
#define min_offset(a, b)  min_int(a, b)
#define max_offset(a, b)  max_int(a, b)
#define clamp_offset(a, b, c)  clamp_int(a, b, c)

/*---- Character classification functions ----*/

/* Character classification tests work for ASCII, Latin1 and Unicode */

extern unsigned char const qe_digit_value__[128];

static inline int qe_digit_value(char32_t c) {
    /*@API char-classes
       Get the numerical value associated with a codepoint
       @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;
}

static inline int qe_inrange(char32_t c, char32_t a, char32_t b) {
    /*@API char-classes
       Range test for codepoint values
       @argument `c` a codepoint value
       @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)
    return (unsigned int)(c - a) <= (unsigned int)(b - a);
}

static inline int qe_isspace(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents white space
       @argument `c` a codepoint value
       @return a boolean value indicating if the codepoint is white space
       @note: only ASCII whitespace and non-breaking-space are supported
     */
    /* CG: what about \v and \f */
    return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == 160);
}

static inline int qe_isblank(char32_t c) {
    /*@API char-classes
       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 (SPC and TAB) and non-breaking-space are supported
     */
    return (c == ' ' || c == '\t' || c == 160);
}

static inline int qe_isdigit(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a digit
       @argument `c` a codepoint value
       @return a boolean value indicating if the codepoint is an ASCII digit
       @note: only ASCII digits are supported
     */
    return qe_inrange(c, '0', '9');
}

static inline int qe_isdigit_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a digit or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits are supported
     */
    return (qe_inrange(c, '0', '9') || c == '_');
}

static inline int qe_isupper(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents an uppercase letter
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII uppercase letters are supported
     */
    return qe_inrange(c, 'A', 'Z');
}

static inline int qe_isupper_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents an uppercase letter or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII uppercase letters are supported
     */
    return (qe_inrange(c, 'A', 'Z') || c == '_');
}

static inline int qe_islower(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a lowercase letter
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII lowercase letters are supported
     */
    return qe_inrange(c, 'a', 'z');
}

static inline int qe_islower_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a lowercase letter or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII lowercase letters are supported
     */
    return (qe_inrange(c, 'a', 'z') || (c == '_'));
}

static inline int qe_isalpha(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a letter
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII letters are supported
     */
    return qe_inrange(c | ('a' - 'A'), 'a', 'z');
}

static inline int qe_isalpha_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a letter or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII letters are supported
     */
    return (qe_inrange(c | ('a' - 'A'), 'a', 'z') || c == '_');
}

static inline int qe_isoctdigit(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents an octal digit
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits are supported
     */
    return qe_inrange(c, '0', '7');
}

static inline int qe_isoctdigit_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents an octal digit or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits are supported
     */
    return qe_inrange(c, '0', '7') || (c == '_');
}

static inline int qe_isbindigit(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a binary digit
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits are supported
     */
    return qe_inrange(c, '0', '1');
}

static inline int qe_isbindigit_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a binary digit or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits are supported
     */
    return qe_inrange(c, '0', '1') || (c == '_');
}

static inline int qe_isxdigit(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a hexadecimal digit
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits and letters are supported
     */
    return qe_digit_value(c) < 16;
}

static inline int qe_isxdigit_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a hexadecimal digit or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII digits and letters are supported
     */
    return (qe_digit_value(c) < 16) || (c == '_');
}

static inline int qe_isalnum(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a letter or a digit
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII letters and digits are supported
     */
    return qe_digit_value(c) < 36;
}

static inline int qe_isalnum_(char32_t c) {
    /*@API char-classes
       Test if a codepoint represents a letter, a digit or an underscore
       @argument `c` a codepoint value
       @return a boolean success value
       @note: only ASCII letters and digits are supported
     */
    return (qe_digit_value(c) < 36) || (c == '_');
}

static inline int qe_isword(char32_t c) {
    /*@API char-classes
       Test if a codepoint value is part of a _word_
       @argument `c` a codepoint value
       @return a boolean success value
       @note: _word_ characters are letters, digits, underscore and any
       non ASCII codepoints. This is oversimplistic, we should use tables for
       better Unicode support.  The definition of _word_ characters should
       depend on the current mode.
     */
    return qe_isalnum_(c) || (c >= 128);
}

static inline char32_t qe_tolower(char32_t c) {
    /*@API char-classes
       Convert an uppercase letter to the corresponding lowercase letter
       @argument `c` a codepoint value
       @return the converted letter or `c` if it is not an uppercase letter
       @note: only ASCII letters are supported
     */
    return (qe_inrange(c, 'A', 'Z') ? c + 'a' - 'A' : c);
}

static inline char32_t qe_toupper(char32_t c) {
    /*@API char-classes
       Convert a lowercase letter to the corresponding uppercase letter
       @argument `c` a codepoint value
       @return the converted letter or `c` if it is not a lowercase letter
       @note: only ASCII letters are supported
     */
    return (qe_inrange(c, 'a', 'z') ? c + 'A' - 'a' : c);
}

static inline int qe_findchar(const char *str, char32_t c) {
    /*@API char-classes
       Test if a codepoint value is part of a set of ASCII characters
       @argument `str` a valid pointer to a C string
       @argument `c` a codepoint value
       @return a boolean success value: `1` if the codepoint was found in
       the string, `0` if `c` is `0` or non-ASCII or was not found in the set.
       @note: only ASCII characters are supported
     */
    return qe_inrange(c, 1, 127) && strchr(str, c) != NULL;
}

static inline int qe_indexof(const char *str, char32_t c) {
    /*@API char-classes
       Find the index of a codepoint value in a set of ASCII characters
       @argument `str` a valid pointer to a C string
       @argument `c` a codepoint value
       @return the offset of `c` in `str` if found or `-1` if `c` is not
       an ASCII character or was not found in the set.
       @note: only non null ASCII characters are supported.
       Contrary to `strchr`, `'\0'` is never found in the set.
     */
    if (qe_inrange(c, 1, 127)) {
        const char *p = strchr(str, c);
        if (p) return (int)(p - str);
    }
    return -1;
}

static inline int qe_match2(char32_t c, char32_t c1, char32_t c2) {
    /*@API char-classes
       Test if a codepoint value is one of 2 specified values
       @argument `c` a codepoint value
       @argument `c1` a codepoint value
       @argument `c2` a codepoint value
       @return a boolean success value
     */
    return c == c1 || c == c2;
}

int qe_skip_spaces(const char **pp);
int qe_strcollate(const char *s1, const char *s2);
int qe_strtobool(const char *s, int def);
void qe_strtolower(char *buf, int buf_size, const char *str);
int qe_haslower(const char *str);
int memfind(const char *list, const char *p, int len);
int strfind(const char *list, const char *s);
int strxfind(const char *list, const char *s);
const char *strmem(const char *str, const void *mem, int size);
const void *memstr(const void *buf, int size, const char *str);
int qe_memicmp(const void *p1, const void *p2, size_t count);
const char *qe_stristr(const char *s1, const char *s2);
#define stristart(str, val, ptr)   qe_stristart(str, val, ptr)
int stristart(const char *str, const char *val, const char **ptr);
int strxstart(const char *str, const char *val, const char **ptr);
int strxcmp(const char *str1, const char *str2);
int strmatchword(const char *str, const char *val, const char **ptr);
int strmatch_pat(const char *str, const char *pat, int start);
const char *sreg_match(const char *re, const char *str, int exact);
int utf8_strimatch_pat(const char *str, const char *pat, int start);
int get_str(const char **pp, char *buf, int buf_size, const char *stop);

int strsubst(char *buf, int buf_size, const char *from,
             const char *s1, const char *s2);
enum { SQB_USE_DEFAULT = 0, SQB_USE_EXTENDED = 1, SQB_USE_CONTROL = 2 };
int strquote_byte(char *dest, int size, unsigned char c, int flags);
int strquote(char *dest, int size, const char *str, int len, int flags);
int strunquote(char *dest, int size, const char *str, int len);

/*---- Unicode string functions ----*/

int ustrstart(const char32_t *str, const char *val, int *lenp);
const char32_t *ustrstr(const char32_t *str, const char *val);
int ustristart(const char32_t *str, const char *val, int *lenp);
const char32_t *ustristr(const char32_t *str, const char *val);
int umemcmp(const char32_t *s1, const char32_t *s2, size_t count);

static inline char32_t *umemcpy(char32_t *dest, const char32_t *src, size_t count) {
    return blockcpy(dest, src, count);
}

static inline char32_t *umemmove(char32_t *dest, const char32_t *src, size_t count) {
    return blockmove(dest, src, count);
}

int cp_skip_blanks(const char32_t *str, int i, int n);
int ustr_get_identifier(char *dest, int size, char32_t c,
                        const char32_t *str, int i, int n);
int ustr_get_identifier_x(char *dest, int size, char32_t c,
                          const char32_t *str, int i, int n, char32_t c1);
int ustr_get_identifier_lc(char *dest, int size, char32_t c,
                           const char32_t *str, int i, int n);
int ustr_match_str(const char32_t *str, const char *p, int *lenp);
int ustr_match_keyword(const char32_t *str, const char *keyword, int *lenp);
int utf8_get_word(char *dest, int size, char32_t c,
                  const char32_t *str, int i, int n);
int utf8_prefix_len(const char *str1, const char *str2);

static inline int check_fcall(const char32_t *str, int i) {
    /*@API utils
       Test if a parenthesis follows optional white space
       @argument `str` a valid pointer to an array of codepoints
       @argument `i` the index of the current codepoint
       @return a boolean success value
     */
    while (str[i] == ' ')
        i++;
    return str[i] == '(';
}

char *qe_encode64(const void *src, size_t len, size_t *sizep);
void *qe_decode64(const char *src, size_t len, size_t *sizep);

/*---- Allocation wrappers and utilities ----*/

void *qe_malloc_bytes(size_t size);
void *qe_mallocz_bytes(size_t size);
void *qe_malloc_dup_bytes(const void *src, size_t size);
char *qe_strdup(const char *str);
char *qe_strndup(const char *str, size_t len);
void *qe_realloc_bytes(void *pp, size_t new_size);

#if 0  /* Documentation prototypes */

T *qe_malloc(type T);
/*@API memory
   Allocate memory for an object of type `T`.
   @argument `T` the type of the object to allocate.
   @note this function is implemented as a macro.
 */

T *qe_mallocz(type T);
/*@API memory
   Allocate memory for an object of type `T`.
   The object is initialized to all bits zero.
   @argument `T` the type of the object to allocate.
   @note this function is implemented as a macro.
 */

T *qe_malloc_array(type T, size_t n);
/*@API memory
   Allocate memory for an array of objects of type `T`.
   @argument `T` the type of the object to allocate.
   @argument `n` the number of elements to allocate.
   @note this function is implemented as a macro.
 */

T *qe_mallocz_array(type T, size_t n);
/*@API memory
   Allocate memory for an array of objects of type `T`.
   The objects are initialized to all bits zero.
   @argument `T` the type of the object to allocate.
   @argument `n` the number of elements to allocate.
   @note this function is implemented as a macro.
 */

T *qe_malloc_hack(type T, size_t n);
/*@API memory
   Allocate memory for an object of type `T` with `n` extra bytes.
   @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_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 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_malloc_dup_array(const T *p, size_t n);
/*@API memory
   Allocate memory for an array of objects of type `T`. Initialize the elements
   from the array pointed to by `p`.
   @argument `T` the type of the object to allocate.
   @argument `p` a pointer to the array used for initialization.
   @argument `n` the number of elements to duplicate.
   @note this function is implemented as a macro.
   The uninitialized elements are set to all bits zero.
 */

T *qe_realloc_array(T **pp, size_t new_len);
/*@API memory
   Reallocate a block of memory to a different size.
   @argument `pp` the address of a pointer to the array to reallocate
   @argument `new_len` the new number of elements for the array.
   @return a pointer to allocated memory, aligned on the maximum
   alignment size.
   @note this function is implemented as a macro.
 */

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 is null, nothing happens.
   @note this function is implemented as a macro.
 */
#endif

#define qe_malloc(t)            ((t *)qe_malloc_bytes(sizeof(t)))
#define qe_mallocz(t)           ((t *)qe_mallocz_bytes(sizeof(t)))
#define qe_malloc_array(t, n)   ((t *)qe_malloc_bytes((n) * sizeof(t)))
#define qe_mallocz_array(t, n)  ((t *)qe_mallocz_bytes((n) * sizeof(t)))
#define qe_malloc_hack(t, n)    ((t *)qe_malloc_bytes(sizeof(t) + (n)))
#define qe_mallocz_hack(t, n)   ((t *)qe_mallocz_bytes(sizeof(t) + (n)))
// XXX: Should use (typeof(**(p)) *) if available
#define qe_malloc_dup_array(p, n)  (qe_malloc_dup_bytes(p, (n) * sizeof(*(p))))
#define qe_realloc_array(pp, n)  (qe_realloc_bytes(pp, (n) * sizeof(**(pp))))

#if 1  // to test clang -Weverything
#define qe_free(pp)    do { void *_1 = (pp), **_2 = _1; (free)(*_2); *_2 = NULL; } while (0)
#elif defined CONFIG_HAS_TYPEOF
#define qe_free(pp)    do { typeof(**(pp)) **_2 = (pp); (free)(*_2); *_2 = NULL; } while (0)
#else
#define qe_free(pp)    do if (sizeof(**(pp)) >= 0) { void *_1 = (pp), **_2 = _1; (free)(*_2); *_2 = NULL; } while (0)
#endif

/* prevent the use of regular malloc and free functions */
#ifndef free
#define free(p)       do_not_use_free!!(p)
#endif
#ifndef malloc
#define malloc(s)     do_not_use_malloc!!(s)
#endif
#ifndef realloc
#define realloc(p,s)  do_not_use_realloc!!(p,s)
#endif

/*---- StringArray functions ----*/

// 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);
void sort_strings(StringArray *cs, int (*sort_func)(const void *p1, const void *p2));
int remove_duplicate_strings(StringArray *cs);
void free_strings(StringArray *cs);

/*---- Dynamic buffers with static allocation ----*/

typedef struct buf_t buf_t;
struct buf_t {
    /*@API buf
       Fixed length character array handling
       All output functions return the number of bytes actually written to the
       output buffer and set a null terminator after any output.
     */
    char *buf;  /* pointer to the output array */
    int size;   /* size of the array pointed to by buf */
    int len;    /* length of output in buf. buf is null terminated */
    int pos;    /* output position into or beyond the end of buf */
};

static inline buf_t *buf_init(buf_t *bp, char *buf, int size) {
    /*@API buf
       Initialize a `buf_t` to output to a fixed length array.
       @argument `bp` a valid pointer to fixed length buffer
       @argument `buf` a valid pointer to a destination array of bytes
       @argument `size` the length of the destination array
       @return the `buf_t` argument.
     */
    if (size > 0) {
        bp->buf = buf;
        bp->size = size;
        *buf = '\0';
    } else {
        bp->buf = NULL;
        bp->size = 0;
    }
    bp->len = bp->pos = 0;
    return bp;
}

static inline buf_t *buf_attach(buf_t *bp, char *buf, int size, int pos) {
    /*@API buf
       Initialize a `buf_t` to output to a fixed length array at a given position.
       @argument `bp` a valid pointer to fixed length buffer
       @argument `buf` a valid pointer to a destination array of bytes
       @argument `size` the length of the destination array
       @argument `pos` the initial position for output.
       @return the `buf_t` argument.
       @note `size` must be strictly positive and `pos` must be in range: `0 <= pos < size`
       @note this function does not set a null terminator at offset `pos`.
     */
    bp->buf = buf;
    bp->size = size;
    bp->len = bp->pos = pos;
    return 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
       @return the number of bytes, or `0` if the buffer is full.
     */
    return (bp->pos < bp->size) ? bp->size - bp->pos - 1 : 0;
}

static inline int buf_put_byte(buf_t *bp, unsigned char ch) {
    /*@API buf
       Append a byte to a fixed length buffer.
       @argument `bp` a valid pointer to fixed length buffer
       @argument `ch` a byte
       @return the number of bytes actually written.
     */
    bp->pos++;
    if (bp->pos < bp->size) {
        bp->buf[bp->len++] = ch;
        bp->buf[bp->len] = '\0';
        return 1;
    }
    return 0;
}

int buf_write(buf_t *bp, const void *src, int size);

static inline int buf_puts(buf_t *bp, const char *str) {
    /*@API buf
       Append a string to a fixed length buffer.
       @argument `bp` a valid pointer to fixed length buffer
       @argument `str` a valid pointer to a C string
       @return the number of bytes actually written.
     */
    return buf_write(bp, str, strlen(str));
}

int buf_put_word(buf_t *bp, const char *str);
int buf_printf(buf_t *bp, const char *fmt, ...) qe__attr_printf(2,3);
int buf_putc_utf8(buf_t *bp, char32_t c);

/*---- Bounded constant strings used in various parse functions ----*/
typedef struct bstr_t {
    const char *s;
    int len;
} bstr_t;

static inline bstr_t bstr_make(const char *s) {
    bstr_t bs = { s, s ? strlen(s) : 0 };
    return bs;
}

bstr_t bstr_token(const char *s, int sep, const char **pp);
bstr_t bstr_get_nth(const char *s, int n);

static inline int bstr_equal(bstr_t s1, bstr_t s2) {
    /* NULL and empty strings are equivalent */
    return s1.len == s2.len && !memcmp(s1.s, s2.s, s1.len);
}

/*---- our own implementation of qsort_r() ----*/

void qe_qsort_r(void *base, size_t nmemb, size_t size, void *thunk,
                int (*compar)(void *, const void *, const void *));

/*---- key definitions and functions ----*/

int find_key_suffix(const char *str, char c);
int compose_keys(unsigned int *keys, int *nb_keys);
int get_modified_key(int key, int state);
int strtokey(const char **pp);
int strtokeys(const char *keystr, unsigned int *keys, int max_keys, const char **endp);
int buf_put_key(buf_t *out, int key);
int buf_put_keys(buf_t *out, unsigned int *keys, int nb_keys);
int buf_quote_byte(buf_t *out, unsigned char ch, int flags);
int is_shift_key(int key);

/* XXX: should use a more regular key mapping scheme:
   - 0000..001F: standard control keys: KEY_CTRL('@') to KEY_CTRL('_')
   - 0020: SPC
   - 0021..007E: ASCII characters (self insert)
   - 007F: DEL
   - 0080..DFFF: Unicode code points
   - E000..EFFF: surrogates used for function and modified keys
   - E000..E0FF: special keys: KEY_NONE, KEY_DEFAULT, KEY_INVALID
   - E100..E1FF: function keys: KEY_F1, KEY_UP ...
   - E200: M- modifier
   - E400: S- modifier (S- modified non function keys unavailable from terminal)
   - E800: C- modifier (C- modified non function keys unavailable from terminal)
   - F000..1FFFFF: Unicode code points
   If using 32-bit key codes, could use higher modifier bits:
   Make these bits consistent X11 modifier keys.
-   - 00200000: Shift (S-)     - ShiftMask      (1<<0)
-   - 00400000: Lock (L-)      - LockMask       (1<<1)
-   - 00800000: Ctrl (C-)      - ControlMask    (1<<2)
-   - 01000000: Meta (M-)      - Mod1Mask       (1<<3)
-   - 02000000: Alt (A-)       - Mod2Mask       (1<<4)
-   - 04000000: Super (s-)     - Mod3Mask       (1<<5)
-   - 08000000: Hyper (H-)     - Mod4Mask       (1<<6)
-   - 10000000: Extra (E-)     - Mod5Mask       (1<<7)

   X11 function keys are mapped in the range FF00..FFFF, including prefix keys

   - XK_Shift_L     0xffe1  // Left shift
   - XK_Shift_R     0xffe2  // Right shift
   - XK_Control_L   0xffe3  // Left control
   - XK_Control_R   0xffe4  // Right control
   - XK_Caps_Lock   0xffe5  // Caps lock
   - XK_Shift_Lock  0xffe6  // Shift lock

   - XK_Meta_L      0xffe7  // Left meta
   - XK_Meta_R      0xffe8  // Right meta
   - XK_Alt_L       0xffe9  // Left alt
   - XK_Alt_R       0xffea  // Right alt
   - XK_Super_L     0xffeb  // Left super
   - XK_Super_R     0xffec  // Right super
   - XK_Hyper_L     0xffed  // Left hyper
   - XK_Hyper_R     0xffee  // Right hyper
 */

#define KEY_CTRL(c)     ((c) & 0x001F)
#define KEY_ESC1(c)     ((c) | 0xE100)
#define KEY_META(c)     ((c) | 0xE200)
#define KEY_SHIFT(c)    ((c) | 0xE400)
#define KEY_CONTROL(c)  ((c) | 0xE800)
#define KEY_IS_ESC1(c)     ((c) >= KEY_ESC1(0) && (c) <= KEY_ESC1(0xff))
#define KEY_IS_SPECIAL(c)  ((c) >= 0xE000 && (c) < 0xF000)
#define KEY_IS_CONTROL(c)  ((unsigned int)(c) < 32 || (c) == 127)
#define KEY_IS_META(c)  (((c) & 0x1FF200) == 0xE200)
#define KEY_IS_SHIFT(c) (((c) & 0x1FF400) == 0xE400)

#define KEY_STATE_SHIFT    1
#define KEY_STATE_META     2
#define KEY_STATE_CONTROL  4
#define KEY_STATE_COMMAND  8

#define KEY_NONE        0xE000
#define KEY_DEFAULT     0xE001 /* to handle all non special keys */
#define KEY_UNKNOWN     0xE002

#define KEY_BS          KEY_CTRL('h')   // kbs
#define KEY_TAB         KEY_CTRL('i')
#define KEY_LF          KEY_CTRL('j')
#define KEY_RET         KEY_CTRL('m')
#define KEY_ESC         KEY_CTRL('[')
#define KEY_SPC         0x0020
#define KEY_DEL         127             // kbs

#define KEY_HOME        KEY_ESC1(1)     // khome
#define KEY_INSERT      KEY_ESC1(2)     // kich1
#define KEY_DELETE      KEY_ESC1(3)     // kdch1
#define KEY_END         KEY_ESC1(4)     // kend
#define KEY_PAGEUP      KEY_ESC1(5)     // kpp
#define KEY_PAGEDOWN    KEY_ESC1(6)     // knp
#define KEY_UP          KEY_ESC1(7)     // kcuu1
#define KEY_DOWN        KEY_ESC1(8)     // kcud1
#define KEY_RIGHT       KEY_ESC1(9)     // kcuf1
#define KEY_LEFT        KEY_ESC1(10)    // kcub1
#define KEY_F1          KEY_ESC1(11)
#define KEY_F2          KEY_ESC1(12)
#define KEY_F3          KEY_ESC1(13)
#define KEY_F4          KEY_ESC1(14)
#define KEY_F5          KEY_ESC1(15)
#define KEY_F6          KEY_ESC1(16)
#define KEY_F7          KEY_ESC1(17)
#define KEY_F8          KEY_ESC1(18)
#define KEY_F9          KEY_ESC1(19)
#define KEY_F10         KEY_ESC1(20)
#define KEY_F11         KEY_ESC1(21)
#define KEY_F12         KEY_ESC1(22)
#define KEY_F13         KEY_ESC1(23)
#define KEY_F14         KEY_ESC1(24)
#define KEY_F15         KEY_ESC1(25)
#define KEY_F16         KEY_ESC1(26)
#define KEY_F17         KEY_ESC1(27)
#define KEY_F18         KEY_ESC1(28)
#define KEY_F19         KEY_ESC1(29)
#define KEY_F20         KEY_ESC1(30)
/* synthetic event keys */
#define KEY_QUIT        KEY_ESC1(31)
#define KEY_CLOSE       KEY_ESC1(32)
#define KEY_EXIT        KEY_ESC1(33)

#define KEY_SHIFT_TAB   KEY_SHIFT(KEY_TAB)

/*---- Unicode and UTF-8 support ----*/

#include "wcwidth.h"

extern char32_t qe_wcunaccent(char32_t c);
extern char32_t qe_wctoupper(char32_t c);
extern char32_t qe_wctolower(char32_t c);

extern unsigned char const utf8_length[256];

static inline int utf8_is_trailing_byte(unsigned char c) {
    return (c & 0xC0) == 0x80;
}

int utf8_encode(char *q, char32_t c);
char32_t utf8_decode_strict(const char **pp);
char32_t utf8_decode(const char **pp);
char32_t utf8_decode_prev(const char **pp, const char *start);
int utf8_to_char32(char32_t *dest, int dest_length, const char *str);
int char32_to_utf8(char *dest, int dest_length, const char32_t *src, int src_length);

static inline char32_t qe_unaccent(char32_t c) {
    return c >= 0x80 ? qe_wcunaccent(c) : c;
}

static inline int qe_isaccent(char32_t c) {
    return c >= 0x300 && qe_wcwidth(c) == 0;
}

static inline int qe_iswide(char32_t c) {
    return c >= 0x01000 && qe_wcwidth(c) > 1;
}

static inline int qe_iswalpha(char32_t c) {
    return qe_isalpha(qe_unaccent(c));
}

static inline char32_t qe_iswlower(char32_t c) {
    return qe_islower(qe_unaccent(c));
}

static inline char32_t qe_iswupper(char32_t c) {
    return qe_isupper(qe_unaccent(c));
}

static inline char32_t qe_wtolower(char32_t c) {
    return c >= 0x80 ? qe_wctolower(c) : qe_tolower(c);
}

static inline char32_t qe_wtoupper(char32_t c) {
    return c >= 0x80 ? qe_wctoupper(c) : qe_toupper(c);
}

/*---- Completion types used for enumerations ----*/

typedef struct CompleteState CompleteState;
typedef void (*CompleteFunc)(CompleteState *cp, const char *str, int mode);
/* mode values for default CompleteFunc passed to complete_xxx() functions */
enum { CT_TEST, CT_GLOB, CT_IGLOB, CT_STRX, CT_SET };

#endif  /* UTIL_H */
