// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2011 Andrea Mazzoleni #ifndef __SUPPORT_H #define __SUPPORT_H #include "app.h" /****************************************************************************/ /* lock */ /** * Initialize and destroy the locks. */ void lock_init(void); void lock_done(void); /** * Lock used for printf. * * In Windows printf() is not atomic, and multiple threads * will have output interleaved. * * Note that even defining __USE_MINGW_ANSI_STDIO the problem persists. * * See for example: * * Weird output when I use pthread and printf. * http://stackoverflow.com/questions/13190254/weird-output-when-i-use-pthread-and-printf * * This is also required in other OS because we split output in stdlog in * two fprintf calls. */ void lock_msg(void); void unlock_msg(void); /** * Lock used for memory counter. */ void lock_memory(void); void unlock_memory(void); /****************************************************************************/ /* random */ /** * Seed the random pseudo random number generator */ void random_seed(uint64_t seed); /** * Reseed the random pseudo random number generator with the tick count */ void random_reseed(void); /** * Return a pseudo random number of 8 bits */ unsigned char random_u8(void); /** * Return a pseudo random number of 64 bits */ uint64_t random_u64(void); /****************************************************************************/ /* error */ /** * Return if the error is at hardware level */ int is_hw(int err); /** * Log errors with standard messages */ void log_fatal_errno(int err, const char* name); void log_error_errno(int err, const char* name); /****************************************************************************/ /* log */ /** * Pointer to log function. */ typedef void log_ptr(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3))); /** * Fatal error messages. * * Messages printed before an early termination. * * These messages go in the log file and in stderr unconditionally. */ void vlog_fatal(int err, const char* format, va_list ap, const char* post); void log_fatal(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3))); /** * Unexpected error messages. * * Messages reporting error conditions that don't prevent the program to run. * * Some of them could be also serious errors, like "silent errors". * In such case, the summary result is always printed as error, * and we are sure to notify the user in some way. * * These messages go in the log file if specified, otherwise they go in stderr. */ void vlog_error(int err, const char* format, va_list ap, const char* post); void log_error(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3))); /** * Expected error messages, without fallback to stderr. * * These errors are "someway" expected, and then they never go to screen. * For example, when undeleting missing files, the messages for missing files * are not shown. * * These messages go in the log file if specified, otherwise they are lost. */ void log_expected(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3))); /** * Tag messages. * * Messages are in tag format, like "tag:entry:...". * * These messages never go on the screen, but only in the log file if specified. * * Note that this function, allows not \n terminated strings. * * These messages are buffered. Use msg_flush() to flush them. */ void log_tag(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Flush the log. */ void log_flush(void); /****************************************************************************/ /* message */ /** * Pointer to msg function. */ typedef void msg_ptr(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Message levels. * * The levels control the amount of information printed on the screen. * Note that log_fatal(), log_error(), log_expected() and log_tag() are not affected by this option. * * From the most quiet to the most verbose. */ #define MSG_STATUS -3 #define MSG_INFO -2 #define MSG_PROGRESS -1 #define MSG_BAR 0 #define MSG_VERBOSE 1 /** * Selected message level. */ extern int msg_level; /** * State messages. * * Messages that tell what the program is doing or did, but limited to few lines. * They are status information, and summary results. */ void msg_status(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Info messages. * * Messages that tell what was done. * Potentially a lot of messages are possible. They can still be on the screen, * as losing them we don't lose information. * * These messages never go in the log file, because there is always a corresponding log_tag(). */ void msg_info(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Progress messages. * * Message that tell the progress of program. * * These messages also go in the log file. */ void msg_progress(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Progress bar messages. * * Message that show the percentage of the progress. * * These messages never go in the log file. * * These messages are buffered. Use msg_flush() to flush them. */ void msg_bar(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Verbose messages. * * Message that tell what is already expected. * * These messages also go in the log file. */ void msg_verbose(const char* format, ...) __attribute__((format(attribute_printf, 1, 2))); /** * Flush the output. */ void msg_flush(void); /****************************************************************************/ /* print */ /** * Print a repeated char. */ void printc(char c, size_t pad); /** * Print a string with right space padding. */ void printr(const char* str, size_t pad); /** * Print a string with left space padding. */ void printl(const char* str, size_t pad); /** * Print a probability with space padding. */ void printp(double v, size_t pad); /****************************************************************************/ /* string */ #define ESC_MAX (PATH_MAX * 2 + 1) #define ESC_POOL_MAX 8 /** * Escape a string for the log. * * Chars ':', '\n', '\r' and '\' are escaped to '\d', '\\n', '\\r' and '\\'. */ const char* esc_tag(const char* str); /** * Get a dynamically allocated buffer of ESC_MAX size from a circular pool. */ char* esc_buf(void); /** * Escape a string for the shell. * * \param buffer Preallocated buffer of ESC_MAX size. */ const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer); static inline const char* esc_shell(const char* str, char* buffer) { return esc_shell_multi(&str, 1, buffer); } /** * Polish a string. * * Not printable chars are replaced by spaces. * * Note that the passed string is modified. */ char* strpolish(char* s); /** * Split a string into tokens in-place. * * Scans @str for tokens separated by any character in @delimiters, * null-terminates each token by overwriting its trailing delimiter, * and stores a pointer to each token's start in @split_map. * * Leading and consecutive delimiters are silently skipped, so the * output never contains empty tokens. * * The input string is modified in-place; @split_map entries point * directly into @str and are only valid for its lifetime. * * \param split_map Output array receiving pointers to each token. * \param split_max Capacity of @split_map; at most this many tokens are stored. * \param str Input string to tokenise (modified in-place). * \param delimiters Null-terminated set of delimiter characters. * \return Number of tokens stored in @split_map. * * Example: * char buf[] = " one,two,,three "; * char* map[8]; * unsigned n = strsplit(map, 8, buf, " ,"); * // n == 3, map[] == { "one", "two", "three" } */ unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters); /** * Split a command-line string into an argument vector, * applying the quoting rules of the platform's native shell. * * The input string is modified in-place: quotes and escape characters * are stripped and each token is null-terminated. Whitespace is the * implicit token delimiter on both platforms. * * bash quoting (Linux / macOS): * 'single quotes' - Everything literal; no escapes processed inside. * "double quotes" - '\' only escapes $, `, ", '\', and newline; * all other backslashes are kept as-is. * \x (unquoted) - Next character taken literally (even whitespace). * * cmd.exe quoting (Windows): * "double quotes" - Toggle quoted mode; inside, '^' is NOT special. * ^x (unquoted) - Next character taken literally. * Single quotes and '\' carry no special meaning in either mode. * * \param split_map Output array receiving pointers to each argument. * \param split_max Capacity of @split_map; at most this many tokens are stored. * \param str Input command-line string (modified in-place). * \return Number of arguments stored in @split_map. * * Example (bash): * char buf[] = "one 'two three' \"say \\\"hi\\\"\" back\\slash"; * char* map[8]; * unsigned n = argsplit(map, 8, buf); * // n==4, map[] == { "one", "two three", "say \"hi\"", "backslash" } * * Example (cmd.exe): * char buf[] = "one \"two three\" ^&safe ^\"quoted^\""; * char* map[8]; * unsigned n = argsplit(map, 8, buf); * // n==4, map[] == { "one", "two three", "&safe", "\"quoted\"" } */ unsigned argsplit(const char** split_map, unsigned split_max, char* str); /** * Trim spaces from the start and the end */ char* strtrim(char* s); /** * Lower case */ char* strlwr(char* s); /* * Find the first occurrence of 'needle' in 'haystack' only if it * appears as a separate word (space, number or string boundaries). * * Returns a pointer to the beginning of the match, or NULL if not found. */ char* worddigitstr(const char* haystack, const char* needle); /** * Convert a string to an integer, with the same semantics as strtol. */ int strtoi(const char* nptr, char** endptr, int base); /** * Convert a string to an unsigned integer, with the same semantics as strtoul. */ unsigned strtou(const char* nptr, char** endptr, int base); /****************************************************************************/ /* path */ /** * Copy a path limiting the size. * Abort if too long. */ void pathcpy(char* dst, size_t size, const char* src); /** * Concatenate a path limiting the size. * Abort if too long. */ void pathcat(char* dst, size_t size, const char* src); /** * Concatenate a path limiting the size knowing the length. * Abort if too long. */ void pathcatl(char* dst, size_t dst_len, size_t size, const char* src); /** * Concatenate a path limiting the size. * Abort if too long. */ void pathcatc(char* dst, size_t size, char c); /** * Import a path limiting the size. * In Windows all the backslash are converted to the C standard of forward slash. * Abort if too long. */ void pathimport(char* dst, size_t size, const char* src); /** * Export a path limiting the size. * In Windows all the C slashes are converted to the Windows backslash. * Abort if too long. */ void pathexport(char* dst, size_t size, const char* src); /** * Print a path. * Abort if too long. */ void pathprint(char* dst, size_t size, const char* format, ...) __attribute__((format(attribute_printf, 3, 4))); /** * Ensure the presence of a terminating slash, if it isn't empty. * Abort if too long. */ void pathslash(char* dst, size_t size); /** * Cut everything after the latest slash. * * If the string doesn't contain any slash, it returns the empty string. */ void pathcut(char* dst); /** * Given a string ending with slash, remove that ending slash and any char up to the previous slash. * * If there are not more slash, it returns the empty string. */ void pathup(char* dst); /** * Compare two paths. * In Windows it's case insensitive and assumes \ equal at /. */ int pathcmp(const char* a, const char* b); /** * Compare two paths up to n chars. * In Windows it's case insensitive and assumes \ equal at /. */ int pathncmp(const char* a, const char* b, size_t n); /** * Check if the root is matching the start of the specified path. * * The root MUST end with a /. * * In Windows it's case insensitive and assumes \ equal at /. */ int path_is_root_of(const char* root, const char* path); /****************************************************************************/ /* file-system */ /** * Create all the ancestor directories if missing. * The file name, after the last /, is ignored. */ int mkancestor(const char* file); /** * Change the modification time of an open file. */ int fmtime(int f, int64_t mtime_sec, int mtime_nsec); /** * Change the modification time of a file or link. * Note that links are NOT deferenced. */ int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec); /****************************************************************************/ /* advise */ /** * Advise modes. */ #define ADVISE_DEFAULT 0 /**< Default mode. */ #define ADVISE_NONE 1 /**< Bare read/write mode. */ #define ADVISE_SEQUENTIAL 2 /**< Sequential mode. */ #define ADVISE_FLUSH 3 /**< Flush mode. */ #define ADVISE_FLUSH_WINDOW 4 /**< Flush mode with a window of 8MB. */ #define ADVISE_DISCARD 5 /**< Discard the cache after every operation. */ #define ADVISE_DISCARD_WINDOW 6 /**< Discard the cache with a window of 8MB. */ #define ADVISE_DIRECT 7 /**< Direct mode. */ #define ADVISE_WINDOW_SIZE (8 * 1024 * 1024) /**< Window size. */ struct advise_struct { int mode; data_off_t dirty_begin; data_off_t dirty_end; }; void advise_init(struct advise_struct* advise, int mode); int advise_flags(struct advise_struct* advise); int advise_open(struct advise_struct* advise, int f); int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size); int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size); void advise_close(struct advise_struct* advise, int f); /****************************************************************************/ /* memory */ /** * Return the size of the cumulative allocated memory. */ size_t malloc_counter_get(void); /** * Safe malloc. * If no memory is available, it aborts. */ void* malloc_nofail(size_t size); /** * Safe malloc with calloc calling arguments. * If no memory is available, it aborts. */ void* nalloc_nofail(size_t count, size_t size); /** * Safe calloc. * If no memory is available, it aborts. */ void* calloc_nofail(size_t count, size_t size); /** * Safe strdup. * If no memory is available, it aborts. */ char* strdup_nofail(const char* str); /** * Helper for printing an error about a failed allocation. */ void malloc_fail(size_t size); /****************************************************************************/ /* smartctl */ /** * Read smartctl attributes from a stream. * Return 0 on success. */ int smartctl_attribute(OS_FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interf); /** * Flush smartctl output from a stream. */ int smartctl_flush(OS_FILE* f, const char* file, const char* name); /** * Extract the temperature from the SMART info. * Return -1 if missing */ int smart_temp(devinfo_t* devinfo); /****************************************************************************/ /* thread */ #if HAVE_THREAD /** * Control when to signal the condition variables. * * Default is inside the mutex. * * Ensure to change that before starting any thread. */ extern int thread_cond_signal_outside; /** * Thread wrappers to handle error conditions. */ void thread_mutex_init(thread_mutex_t* mutex); void thread_mutex_destroy(thread_mutex_t* mutex); void thread_mutex_lock(thread_mutex_t* mutex); void thread_mutex_unlock(thread_mutex_t* mutex); void thread_cond_init(thread_cond_t* cond); void thread_cond_destroy(thread_cond_t* cond); void thread_cond_signal(thread_cond_t* cond); void thread_cond_broadcast(thread_cond_t* cond); void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex); void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex); void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex); void thread_create(thread_id_t* thread, void* (*func)(void*), void* arg); void thread_join(thread_id_t thread, void** retval); void thread_yield(void); int thread_key_create(thread_key_t* key, void (*destructor)(void*)); int thread_key_delete(thread_key_t key); void* thread_getspecific(thread_key_t key); int thread_setspecific(thread_key_t key, void* value); #endif /****************************************************************************/ /* match */ /* * Wild match function. * * If match_sub is !=0, it matches sub directory. Specifically it matches if the * string "t" is not fully consumed and the next character to consume is a /. * * - ? matches any single character except / * - * matches any sequence of characters except / * - ** (nearby a /) matches everything including / * - ** (not near a /) like * * - ##/ reduces to nothing in addition to the normal matching of ** (using # instead of * to not mess the C comment) * - [...] matches character classes with support for ranges and negation lile [!...] or [^...], except / * * \return 0 if it matches */ int wnmatch_sub(const char* p, const char* t, int match_sub); static inline int wnmatch(const char* p, const char* t) { return wnmatch_sub(p, t, 0); } #endif