From 3e60bf665766160ee963aa484a44b959c3d4c414 Mon Sep 17 00:00:00 2001 From: scottmk Date: Mon, 31 Aug 2009 03:48:26 +0000 Subject: [PATCH] 1. Add comments to be recognized by doxygen or other such documentation generators. 2. Change the values returned by buffer_fadd(), buffer_add(), buffer_add_n(), and buffer_add_char() to make them more consistent. In practice we never pay any attention to the return values anyway. git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@1763 9efc2488-bf62-4759-914b-345cdb29e865 --- include/opensrf/utils.h | 117 ++++++++++++++- src/libopensrf/utils.c | 319 +++++++++++++++++++++++++++++++++++----- 2 files changed, 390 insertions(+), 46 deletions(-) diff --git a/include/opensrf/utils.h b/include/opensrf/utils.h index 912698d..c259b7d 100644 --- a/include/opensrf/utils.h +++ b/include/opensrf/utils.h @@ -14,6 +14,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +/** + @file utils.h + + @brief Prototypes for various low-level utility functions, and related macros. + + Many of these facilities concern the growing_buffer structure, + a sort of poor man's string class that allocates more space for + itself as needed. +*/ + #ifndef UTILS_H #define UTILS_H @@ -34,7 +44,11 @@ extern "C" { #endif #include "md5.h" - +/** + @brief Macro version of safe_malloc() + @param Pointer to be updated to point to newly allocated memory + @param How many bytes to allocate +*/ #define OSRF_MALLOC(ptr, size) \ do {\ size_t _size = size; \ @@ -48,9 +62,17 @@ extern "C" { } while(0) #ifdef NDEBUG -// The original ... replace with noop once no more errors occur in NDEBUG mode #define osrf_clearbuf( s, n ) memset( s, 0, n ) #else +/** + @brief Fills a buffer with binary zeros (normal mode) or exclamation points (debugging mode) + @param s Pointer to buffer + @param n Length of buffer + + This macro is used to help ferret out code that inappropriately assumes that a newly + allocated buffer is filled with binary zeros. No code should rely on it to do + anything in particular. Someday it may turn into a no-op. + */ #define osrf_clearbuf( s, n ) \ do { \ char * clearbuf_temp_s = (s); \ @@ -60,6 +82,12 @@ extern "C" { } while( 0 ) #endif +/** + @brief Macro version of buffer_add() + @param gb Pointer to a growing_buffer + @param data Pointer to the string to be appended +*/ + #define OSRF_BUFFER_ADD(gb, data) \ do {\ int _tl; \ @@ -74,6 +102,12 @@ extern "C" { }\ } while(0) +/** + @brief Macro version of buffer_add_n() + @param gb Pointer to a growing_buffer + @param data Pointer to the bytes to be appended + @param n How many characters to append +*/ #define OSRF_BUFFER_ADD_N(gb, data, n) \ do {\ growing_buffer* gb__ = gb; \ @@ -89,6 +123,11 @@ extern "C" { }\ } while(0) +/** + @brief Macro version of buffer_add_char() + @param gb Pointer to a growing buffer + @param c Character to be appended +*/ #define OSRF_BUFFER_ADD_CHAR(gb, c)\ do {\ growing_buffer* _gb = gb;\ @@ -103,6 +142,10 @@ extern "C" { }\ }while(0) +/** + @brief Macro version of buffer_reset() + @param gb Pointer to the growing_buffer to be reset +*/ #define OSRF_BUFFER_RESET(gb) \ do {\ growing_buffer* _gb = gb;\ @@ -110,10 +153,23 @@ extern "C" { _gb->n_used = 0;\ }while(0) +/** + @brief Resolves to a const pointer to the string inside a growing_buffer + @param Pointer to a growing_buffier +*/ #define OSRF_BUFFER_C_STR( x ) ((const char *) (x)->buf) -/* turns a va_list into a string */ +/** + @brief Turn a printf-style format string and a va_list into a string. + @param x A printf-style format string. + + This macro can work only in a variadic function. + + The resulting string is constructed in a local buffer, whose address is + given by the pointer VA_BUF, This buffer is NOT allocated dynamically, + so don't try to free it. +*/ #define VA_LIST_TO_STRING(x) \ unsigned long __len = 0;\ va_list args; \ @@ -130,7 +186,13 @@ extern "C" { va_end(a_copy); \ char* VA_BUF = _b; \ -/* turns a long into a string */ +/** + @brief Format a long into a string. + @param l A long + + The long is formatted into a local buffer whose address is given by the pointer + LONG_TO_STRING. This buffer is NOT allocated dynamically, so don't try to free it. +*/ #define LONG_TO_STRING(l) \ unsigned int __len = snprintf(NULL, 0, "%ld", l) + 2;\ char __b[__len]; \ @@ -138,6 +200,13 @@ extern "C" { snprintf(__b, __len - 1, "%ld", l); \ char* LONGSTR = __b; +/** + @brief Format a double into a string. + @param l A double + + The double is formatted into a local buffer whose address is given by the pointer + LONG_TO_STRING. This buffer is NOT allocated dynamically, so don't try to free it. + */ #define DOUBLE_TO_STRING(l) \ unsigned int __len = snprintf(NULL, 0, "%f", l) + 2; \ char __b[__len]; \ @@ -145,6 +214,13 @@ extern "C" { snprintf(__b, __len - 1, "%f", l); \ char* DOUBLESTR = __b; +/** + @brief Format a long double into a string. + @param l A long double + + The long double is formatted into a local buffer whose address is given by the pointer + LONG_TO_STRING. This buffer is NOT allocated dynamically, so don't try to free it. + */ #define LONG_DOUBLE_TO_STRING(l) \ unsigned int __len = snprintf(NULL, 0, "%Lf", l) + 2; \ char __b[__len]; \ @@ -153,6 +229,13 @@ extern "C" { char* LONGDOUBLESTR = __b; +/** + @brief Format an int into a string. + @param l An int + + The int is formatted into a local buffer whose address is given by the pointer + LONG_TO_STRING. This buffer is NOT allocated dynamically, so don't try to free it. + */ #define INT_TO_STRING(l) \ unsigned int __len = snprintf(NULL, 0, "%d", l) + 2; \ char __b[__len]; \ @@ -184,6 +267,9 @@ extern "C" { +/** + @brief The maximum buffer size for a growing_buffer +*/ #define BUFFER_MAX_SIZE 10485760 /* these are evil and should be condemned @@ -192,7 +278,7 @@ extern "C" { set_proc_title. the title is only allowed to be as big as the initial process name of the process (full size of argv[]). - truncation may occurr. + truncation may occur. */ int init_proc_title( int argc, char* argv[] ); int set_proc_title( const char* format, ... ); @@ -206,13 +292,30 @@ void* safe_calloc(int size); // --------------------------------------------------------------------------------- // Generic growing buffer. Add data all you want // --------------------------------------------------------------------------------- +/** + @brief A poor man's string class in C. + + A growing_buffer stores a character string. Related functions append data + and otherwise manage the string, allocating more memory automatically as needed + when the string gets too big for its buffer. + + A growing_buffer is designed for text, not binary data. In particular: if you + try to store embedded nuls in one, something bad will almost certainly happen. +*/ struct growing_buffer_struct { + /** @brief Pointer to the internal buffer */ char *buf; + /** @brief Length of the stored string */ int n_used; + /** @brief Size of the internal buffer */ int size; }; typedef struct growing_buffer_struct growing_buffer; +/** + @brief The length of the string stored by a growing_buffer. + @param x A pointer to the growing buffer. +*/ #define buffer_length(x) (x)->n_used growing_buffer* buffer_init( int initial_num_bytes); @@ -267,14 +370,14 @@ double get_timestamp_millis( void ); int stringisnum(const char* s); -/** +/* Calculates the md5 of the text provided. The returned string must be freed by the caller. */ char* md5sum( const char* text, ... ); -/** +/* Checks the validity of the file descriptor returns -1 if the file descriptor is invalid returns 0 if the descriptor is OK diff --git a/src/libopensrf/utils.c b/src/libopensrf/utils.c index 39a9235..358dd27 100644 --- a/src/libopensrf/utils.c +++ b/src/libopensrf/utils.c @@ -12,10 +12,33 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +/** + @file utils.c + + @brief A collection of various low-level utility functions. + + About half of these functions concern the growing_buffer structure, + a sort of poor man's string class that allocates more space for + itself as needed. +*/ #include #include #include +/** + @brief A thin wrapper for malloc(). + + @param size How many bytes to allocate. + @return a pointer to the allocated memory. + + If the allocation fails, safe_malloc calls exit(). Consequently the + calling code doesn't have to check for NULL. + + Currently safe_malloc() initializes the allocated buffer to all-bits-zero. + However the calling code should not rely on this behavior, because it is + likely to change. If you need your buffer filled with all-bits-zero, then + call safe_calloc() instead, or fill it yourself by calling memset(). +*/ inline void* safe_malloc( int size ) { void* ptr = (void*) malloc( size ); if( ptr == NULL ) { @@ -26,6 +49,15 @@ inline void* safe_malloc( int size ) { return ptr; } +/** + @brief A thin wrapper for calloc(). + + @param size How many bytes to allocate. + @return a pointer to the allocated memory. + + If the allocation fails, safe_calloc calls exit(). Consequently the + calling code doesn't have to check for NULL. + */ inline void* safe_calloc( int size ) { void* ptr = (void*) calloc( 1, size ); if( ptr == NULL ) { @@ -35,18 +67,30 @@ inline void* safe_calloc( int size ) { return ptr; } -/**************** - The following static variables, and the following two functions, - overwrite the argv array passed to main(). The purpose is to - change the program name as reported by ps and similar utilities. - - Warning: this code makes the non-portable assumption that the - strings to which argv[] points are contiguous in memory. The - C Standard makes no such guarantee. - ****************/ +/** + @brief Saves a pointer to the beginning of the argv[] array from main(). See init_proc_title(). +*/ static char** global_argv = NULL; +/** + @brief Saves the length of the argv[] array from main(). See init_proc_title(). + */ static int global_argv_size = 0; +/** + @brief Save the size and location of the argv[] array. + @param argc The argc parameter to main(). + @param argv The argv parameter to main(). + @return zero. + + Save a pointer to the argv[] array in the local static variable + global_argv. Add up the lengths of the strings in the argv[] + array, subtract 2, and store the result in the local variable + global_argv_size. + + The return value, being invariant, is useless. + + This function prepares for a subsequent call to set_proc_title(). +*/ int init_proc_title( int argc, char* argv[] ) { global_argv = argv; @@ -63,14 +107,39 @@ int init_proc_title( int argc, char* argv[] ) { return 0; } +/** + @brief Replace the name of the running executable. + @param format A printf-style format string. Subsequent parameters, if any, + provide values to be formatted and inserted into the format string. + @return Length of the resulting string (or what the length would be if the + receiving buffer were big enough to hold it), or -1 in case of an encoding + error. Note: because some older versions of snprintf() don't work correctly, + this function may return -1 if the string is truncated for lack of space. + + Formats a string as directed, and uses it to replace the name of the + currently running executable. This replacement string is what will + be seen and reported by utilities such as ps and top. + + The replacement string goes into a location identified by a previous call + to init_proc_title(). + + WARNING: this function makes assumptions about the memory layout of + the argv[] array. ANSI C does not guarantee that these assumptions + are correct. +*/ int set_proc_title( const char* format, ... ) { VA_LIST_TO_STRING(format); osrf_clearbuf( *(global_argv), global_argv_size); return snprintf( *(global_argv), global_argv_size, VA_BUF ); } +/** + @brief Determine current date and time to high precision. + @return Current date and time as seconds since the Epoch. -/* utility method for profiling */ + Used for profiling. The time resolution is system-dependent but is no finer + than microseconds. +*/ double get_timestamp_millis( void ) { struct timeval tv; gettimeofday(&tv, NULL); @@ -79,7 +148,18 @@ double get_timestamp_millis( void ) { } -/* setting/clearing file flags */ +/** + @brief Set designated file status flags for an open file descriptor. + @param fd The file descriptor to be tweaked. + @param flags A set of bitflags. + @return 0 if successful, -1 or if not. + + Whatever bits are set in the flags parameter become set in the file status flags of + the file descriptor -- subject to the limitation that the only bits affected (at + least on Linux) are O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK. + + See also clr_fl(). +*/ int set_fl( int fd, int flags ) { int val; @@ -95,6 +175,18 @@ int set_fl( int fd, int flags ) { return 0; } +/** + @brief Clear designated file status flags for an open file descriptor. + @param fd The file descriptor to be tweaked. + @param flags A set of bitflags. + @return 0 if successful, or -1 if not. + + Whatever bits are set in the flags parameter become cleared in the file status flags + of the file descriptor -- subject to the limitation that the only bits affected (at + least on Linux) are O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK. + + See also set_fl(). + */ int clr_fl( int fd, int flags ) { int val; @@ -110,15 +202,33 @@ int clr_fl( int fd, int flags ) { return 0; } +/** + @brief Determine how lohg a string will be after printf-style formatting. + @param format The format string. + @param args The variable-length list of arguments + @return If successful: the length of the string that would be created, plus 1 for + a terminal nul, plus another 1, presumably to be on the safe side. If unsuccessful + due to a formatting error: 1. + + WARNINGS: the first parameter is not checked for NULL. The return value in case of an + error is not obviously sensible. +*/ long va_list_size(const char* format, va_list args) { int len = 0; len = vsnprintf(NULL, 0, format, args); va_end(args); - len += 2; - return len; + return len + 2; } +/** + @brief Format a printf-style string into a newly allocated buffer. + @param format The format string. Subsequent parameters, if any, will be + formatted and inserted into the resulting string. + @return A pointer to the string so created. + + The calling code is responsible for freeing the string. +*/ char* va_list_to_string(const char* format, ...) { long len = 0; @@ -143,6 +253,19 @@ char* va_list_to_string(const char* format, ...) { // Flesh out a ubiqitous growing string buffer // --------------------------------------------------------------------------------- +/** + @brief Create a growing_buffer containing an empty string. + @param num_initial_bytes The initial size of the internal buffer, not counting the + terminal nul. + @return A pointer to the newly created growing_buffer. + + The value of num_initial_bytes should typically be a plausible guess of how big + the string will ever be. However the guess doesn't have to accurate, because more + memory will be allocated as needed. + + The calling code is responsible for freeing the growing_buffer by calling buffer_free() + or buffer_release(). +*/ growing_buffer* buffer_init(int num_initial_bytes) { if( num_initial_bytes > BUFFER_MAX_SIZE ) return NULL; @@ -160,16 +283,19 @@ growing_buffer* buffer_init(int num_initial_bytes) { } -/* Expand the internal buffer of a growing_buffer so that it */ -/* will accommodate a specified string length. Return 0 if */ -/* successful, or 1 otherwise. */ - -/* Note that we do not check to see if the buffer is already */ -/* big enough. It is the responsibility of the calling */ -/* function to call this only when necessary. */ +/** + @brief Allocate more memory for a growing_buffer. + @param gb A pointer to the growing_buffer. + @return 0 if successful, or 1 if not. + This function fails if it is asked to allocate BUFFER_MAX_SIZE + or more bytes. +*/ static int buffer_expand( growing_buffer* gb, size_t total_len ) { + // We do not check to see if the buffer is already big enough. It is the + //responsibility of the calling function to call this only when necessary. + // Make sure the request is not excessive if( total_len >= BUFFER_MAX_SIZE ) { @@ -203,9 +329,19 @@ static int buffer_expand( growing_buffer* gb, size_t total_len ) { } +/** + @brief Append a formatted string to a growing_buffer. + @param gb A pointer to the growing_buffer. + @param format A printf-style format string. Subsequent parameters, if any, will be + formatted and inserted into the resulting string. + @return If successful,the length of the resulting string; otherwise -1. + + This function fails if either of the first two parameters is NULL, + or if the resulting string requires BUFFER_MAX_SIZE or more bytes. +*/ int buffer_fadd(growing_buffer* gb, const char* format, ... ) { - if(!gb || !format) return 0; + if(!gb || !format) return -1; long len = 0; va_list args; @@ -224,16 +360,21 @@ int buffer_fadd(growing_buffer* gb, const char* format, ... ) { va_end(a_copy); return buffer_add(gb, buf); - } +/** + @brief Appends a string to a growing_buffer. + @param gb A pointer to the growing_buffer. + @param data A pointer to the string to be appended. + @return If successful, the length of the resulting string; or if not, -1. +*/ int buffer_add(growing_buffer* gb, const char* data) { - if(!(gb && data)) return 0; + if(!(gb && data)) return -1; int data_len = strlen( data ); - if(data_len == 0) return 0; + if(data_len == 0) return gb->n_used; int total_len = data_len + gb->n_used; @@ -247,12 +388,18 @@ int buffer_add(growing_buffer* gb, const char* data) { return total_len; } -/** Append a specified number of characters to a growing_buffer. - If the characters so appended include an embedded nul, the results - are likely to be unhappy. +/** + @brief Append a specified number of characters to a growing_buffer. + @param gb A pointer to the growing_buffer. + @param data A pointer to the characters to be appended. + @param n How many characters to be append. + @return If sccessful, the length of the resulting string; or if not, -1. + + If the characters to be appended include an embedded nul byte, it will be appended + along with the others. The results are likely to be unpleasant. */ int buffer_add_n(growing_buffer* gb, const char* data, size_t n) { - if(!(gb && data)) return 0; + if(!(gb && data)) return -1; if(n == 0) return 0; @@ -270,6 +417,11 @@ int buffer_add_n(growing_buffer* gb, const char* data, size_t n) { } +/** + @brief Reset a growing_buffer so that it contains an empty string. + @param gb A pointer to the growing_buffer. + @return 0 if successful, -1 if not. +*/ int buffer_reset( growing_buffer *gb){ if( gb == NULL ) { return -1; } if( gb->buf == NULL ) { return -1; } @@ -279,9 +431,16 @@ int buffer_reset( growing_buffer *gb){ return gb->n_used; } -/* Return a pointer to the text within a growing_buffer, */ -/* while destroying the growing_buffer itself. */ +/** + @brief Free a growing_buffer and return a pointer to the string inside. + @param gb A pointer to the growing_buffer. + @return A pointer to the string previously contained by the growing buffer. + The calling code is responsible for freeing the string. + + This function is equivalent to buffer_data() followed by buffer_free(). However + it is more efficient, because it avoids calls to strudup and free(). +*/ char* buffer_release( growing_buffer* gb) { char* s = gb->buf; s[gb->n_used] = '\0'; @@ -289,20 +448,37 @@ char* buffer_release( growing_buffer* gb) { return s; } -/* Destroy a growing_buffer and the text it contains */ - +/** + @brief Free a growing_buffer and its contents. + @param gb A pointer to the growing_buffer. + @return 1 if successful, or 0 if not (because the input parameter is NULL). +*/ int buffer_free( growing_buffer* gb ) { - if( gb == NULL ) + if( gb == NULL ) return 0; free( gb->buf ); free( gb ); return 1; } +/** + @brief Create a copy of the string inside a growing_buffer. + @param gb A pointer to the growing_buffer. + @return A pointer to the newly created copy. + + The growing_buffer itself is not affected. + + The calling code is responsible for freeing the string. +*/ char* buffer_data( const growing_buffer *gb) { return strdup( gb->buf ); } +/** + @brief Remove the last character from a growing_buffer. + @param gb A pointer to the growing_buffer. + @return The character removed (or '\0' if the string is already empty). +*/ char buffer_chomp(growing_buffer* gb) { char c = '\0'; if(gb && gb->n_used > 0) { @@ -314,6 +490,15 @@ char buffer_chomp(growing_buffer* gb) { } +/** + @brief Append a single character to a growing_buffer. + @param gb A pointer to the growing_buffer. + @param c The character to be appended. + @return The length of the resulting string. + + If the character appended is a nul byte (i.e. '\0') it will still be appended as if + it were a normal character. The results are likely to be unpleasant. +*/ int buffer_add_char(growing_buffer* gb, char c ) { if(gb && gb->buf) { @@ -326,12 +511,35 @@ int buffer_add_char(growing_buffer* gb, char c ) { gb->buf[ gb->n_used ] = c; gb->buf[ ++gb->n_used ] = '\0'; - } - - return gb->n_used; + return gb->n_used; + } else + return 0; } +/** + @brief Translate a UTF8 string into escaped ASCII, suitable for JSON. + @param string The input string to be translated. + @param size The length of the input string (need not be accurate). + @param full_escape Boolean; true turns on the escaping of certain + special characters. + @return A pointer to the translated version of the string. + + Deprecated. Use buffer_append_utf8() instead. + + If full_escape is non-zero, the translation will escape certain + certain characters with a backslash, according to the conventions + used in C and other languages: quotation marks, bell characters, + form feeds, horizontal tabs, carriage returns, line feeds, and + backslashes. A character with a numerical value less than 32, and + not one of the special characters mentioned above, will be + translated to a backslash followed by four hexadecimal characters. + + If full_escape is zero, the translation will (incorrectly) leave + these characters unescaped and unchanged. + + The calling code is responsible for freeing the returned string. +*/ char* uescape( const char* string, int size, int full_escape ) { if( NULL == string ) @@ -436,7 +644,14 @@ char* uescape( const char* string, int size, int full_escape ) { } -// A function to turn a process into a daemon +/** + @brief Become a proper daemon. + @return 0 if successful, or -1 if not. + + Call fork(). The parent exits. The child moves to the root + directory, detaches from the terminal, and redirects the + standard streams (stdin, stdout, stderr) to /dev/null. +*/ int daemonize( void ) { pid_t f = fork(); @@ -468,9 +683,17 @@ int daemonize( void ) { } -/* Return 1 if the string represents an integer, */ -/* as recognized by strtol(); Otherwise return 0. */ +/** + @brief Determine whether a string represents a decimal integer. + @param s A pointer to the string. + @return 1 if the string represents a decimal integer, or 0 if it + doesn't. + To qualify as a decimal integer, the string must consist entirely + of optional leading white space, an optional leading sign, and + one or more decimal digits. In addition, the number must be + representable as a long. +*/ int stringisnum(const char* s) { char* w; strtol(s, &w, 10); @@ -479,6 +702,17 @@ int stringisnum(const char* s) { +/** + @brief Translate a printf-style formatted string into an MD5 message digest. + @param text The format string. Subsequent parameters, if any, provide values to be + formatted and inserted into the format string. + @return A pointer to a string of 32 hexadecimal characters. + + The calling code is responsible for freeing the returned string. + + This function is a wrapper for some public domain routines written by David Madore, + Ron Rivest, and Colin Plumb. +*/ char* md5sum( const char* text, ... ) { struct md5_ctx ctx; @@ -507,6 +741,13 @@ char* md5sum( const char* text, ... ) { } +/** + @brief Determine whether a given file descriptor is valid. + @param fd The file descriptor to be checked. + @return 0 if the file descriptor is valid, or -1 if it isn't. + + The most likely reason a file descriptor would be invalid is if it isn't open. +*/ int osrfUtilsCheckFileDescriptor( int fd ) { fd_set tmpset; -- 2.43.2