author: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300
committer: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300
commit: bfc1508d26c89c9a36d2d9a827fe2c4ed128884d
parent: c836ae3775cf72f17e0b7e3792d156fdb389bee3
Commit Summary:
Diffstat:
1 file changed, 586 insertions, 0 deletions
diff --git a/csvncgi/strbuf.c b/csvncgi/strbuf.c new file mode 100644 index 0000000..e336982 --- /dev/null +++ b/csvncgi/strbuf.c @@ -0,0 +1,708 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <sys/sysinfo.h> +#include <sys/types.h> +#include <stdint.h> +#include <dirent.h> +#include <sys/stat.h> /* chmod(2) */ +#include <sys/file.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> /* strdup(3) */ +#include <libgen.h> /* basename(3) */ +#include <ctype.h> /* tolower(3) */ +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <pwd.h> +#include <grp.h> +#include <stdarg.h> +#include <unistd.h> + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include <pcre2.h> + +#include <nls.h> + +#include <defs.h> +#include <wrapper.h> +#include <strbuf.h> + + +struct symbol *strbuf_envtab = (struct symbol *)0; + +struct symbol *envtab_install( struct symbol **sym, const char *n, const char *v, envtab_error fatal ) +{ + struct symbol *ep = NULL; + size_t len; + + if( !sym || !n || !v || !fatal ) + return (struct symbol *)NULL; + + ep = (struct symbol *)malloc( sizeof(struct symbol) ); + if( !ep ) + { + fatal( "cannot allocate memory for symbol '%s'", n ); + } + bzero( (void *)ep, sizeof(struct symbol) ); + + len = strlen( n ) + 1; + ep->name = (char *)malloc( len ); + if( !ep->name ) + { + fatal( "cannot allocate memory for symbol name '%s'", n ); + } + bzero( (void *)ep->name, len ); + strncpy( ep->name, n, len ); + + len = strlen( v ) + 1; + ep->value = (char *)malloc( len ); + if( !ep->value ) + { + fatal( "cannot allocate memory for symbol value '%s'", v ); + } + bzero( (void *)ep->value, len ); + strncpy( ep->value, v, len ); + + ep->next = *sym; + *sym = ep; + + return ep; +} + +struct symbol *envtab_lookup( struct symbol **sym, const char *n ) +{ + struct symbol *ep = NULL; + + if( !sym || !n ) + return (struct symbol *)NULL; + + for( ep = *sym; ep != (struct symbol *)0; ep = ep->next ) + if( strcmp( ep->name, n ) == 0 ) return( ep ); + + return (struct symbol *)NULL; +} + +void envtab_release( struct symbol **sym ) +{ + struct symbol *tab = *sym; + + while( tab ) + { + struct symbol *ep = tab; + tab = ep->next; + if( ep->name ) free( ep->name ); + if( ep->value ) free( ep->value ); + free( ep ); + } + *sym = (struct symbol *)NULL; +} + + +#define STRBUF_ERRMSG_SIZE 4096 + +void strbuf_fatal( const char *fmt, ... ) +{ + va_list arg_ptr; + char buf[STRBUF_ERRMSG_SIZE]; + char msg[STRBUF_ERRMSG_SIZE]; + char *format = "%s: %s\n"; + + va_start( arg_ptr, fmt ); + + vsnprintf( msg, STRBUF_ERRMSG_SIZE, (const void *)fmt, arg_ptr ); + + va_end( arg_ptr ); /* Reset variable arguments. */ + + snprintf( buf, STRBUF_ERRMSG_SIZE, format, "strbuf", msg ); + + (void)write( STDERR_FILENO, buf, strlen( buf ) ); + + exit( 1 ); +} + +char strbuf_slopbuf[1]; + +#define alloc_nz(x) (((x)+16)*3/2) + +static void alloc_grow( struct strbuf *sb, size_t size ) +{ + if( size > sb->alloc ) + { + if( alloc_nz(sb->alloc) < (size) ) + sb->alloc = size; + else + sb->alloc = alloc_nz(sb->alloc); + + sb->buf = xrealloc( (void *)sb->buf, sb->alloc ); + memset( (void *)(sb->buf + sb->len), 0, (size_t)(sb->alloc - sb->len) ); + } +} + +void strbuf_grow( struct strbuf *sb, size_t extra ) +{ + int new_buf = !sb->alloc; + + if( new_buf ) + sb->buf = NULL; + + alloc_grow( sb, sb->len + extra + 1 ); + + if( new_buf ) + sb->buf[0] = '\0'; +} + +/* + Функцию strbuf_init() надо вызывать в том случае, когда необходимо + задать внешнюю функцию обработки фатальной ошибки. Функция обработки + фатальной ошибки, в отличие от стандартной, может выводить как JSON + ответы, так и файлы 404.html, 50x.html, используя собственный буфер. + */ +void strbuf_init( struct strbuf *sb, strbuf_error fatal, size_t hint ) +{ + sb->alloc = sb->len = 0; + sb->fatal = strbuf_fatal; + sb->buf = strbuf_slopbuf; + + if( fatal ) + sb->fatal = fatal; + + if( hint ) + strbuf_grow( sb, hint ); +} + +void strbuf_release( struct strbuf *sb ) +{ + if( sb->alloc ) + { + free( sb->buf ); + strbuf_init( sb, (strbuf_error)0, 0 ); + } +} + +char *strbuf_detach( struct strbuf *sb, size_t *sz ) +{ + char *ret; + strbuf_grow( sb, 0 ); + ret = sb->buf; + if( sz ) + *sz = sb->len; + strbuf_init( sb, sb->fatal, 0 ); + return ret; +} + +void strbuf_attach( struct strbuf *sb, void *buf, size_t len, size_t alloc ) +{ + strbuf_release( sb ); + sb->buf = buf; + sb->len = len; + sb->alloc = alloc; + strbuf_grow( sb, 0 ); + sb->buf[sb->len] = '\0'; +} + +void strbuf_ltrim( struct strbuf *sb ) +{ + char *b = sb->buf; + while( sb->len > 0 && isspace(*b) ) + { + b++; + sb->len--; + } + memmove( sb->buf, b, sb->len ); + sb->buf[sb->len] = '\0'; +} + +void strbuf_rtrim( struct strbuf *sb ) +{ + while( sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]) ) + sb->len--; + sb->buf[sb->len] = '\0'; +} + +void strbuf_trim( struct strbuf *sb ) +{ + strbuf_rtrim( sb ); + strbuf_ltrim( sb ); +} + + +void strbuf_trim_trailing_dir_sep( struct strbuf *sb ) +{ + while( sb->len > 0 && is_dir_sep((unsigned char)sb->buf[sb->len - 1]) ) + sb->len--; + sb->buf[sb->len] = '\0'; +} + +void strbuf_trim_trailing_newline( struct strbuf *sb ) +{ + if( sb->len > 0 && sb->buf[sb->len - 1] == '\n' ) + { + if( --sb->len > 0 && sb->buf[sb->len - 1] == '\r' ) + --sb->len; + sb->buf[sb->len] = '\0'; + } +} + +int strbuf_cmp( const struct strbuf *a, const struct strbuf *b ) +{ + size_t len = a->len < b->len ? a->len: b->len; + int cmp = memcmp( a->buf, b->buf, len ); + if( cmp ) + return cmp; + return a->len < b->len ? -1: a->len != b->len; +} + + +/* Adding data to the buffer */ + +void strbuf_add( struct strbuf *sb, const void *data, size_t len ) +{ + strbuf_grow( sb, len ); + memcpy( sb->buf + sb->len, data, len ); + strbuf_setlen( sb, sb->len + len ); +} + +void strbuf_addbuf( struct strbuf *sb, const struct strbuf *sb2 ) +{ + strbuf_grow( sb, sb2->len ); + memcpy( sb->buf + sb->len, sb2->buf, sb2->len ); + strbuf_setlen( sb, sb->len + sb2->len ); +} + +void strbuf_addbuf_percentquote( struct strbuf *dst, const struct strbuf *src ) +{ + size_t i, len = src->len; + + for (i = 0; i < len; i++) { + if( src->buf[i] == '%' ) + strbuf_addch( dst, '%' ); + strbuf_addch( dst, src->buf[i] ); + } +} + +void strbuf_addchars( struct strbuf *sb, int c, size_t n ) +{ + strbuf_grow( sb, n ); + memset( sb->buf + sb->len, c, n ); + strbuf_setlen( sb, sb->len + n ); +} + +void strbuf_vaddf( struct strbuf *sb, const char *fmt, va_list ap ) +{ + int len; + va_list cp; + + if( !strbuf_avail( sb ) ) + strbuf_grow( sb, 64 ); + va_copy( cp, ap ); + len = vsnprintf( sb->buf + sb->len, sb->alloc - sb->len, fmt, cp ); + va_end( cp ); + if( len < 0 ) + sb->fatal( "your vsnprintf is broken (returned %d)", len ); + if( len > strbuf_avail( sb ) ) + { + strbuf_grow( sb, len ); + len = vsnprintf( sb->buf + sb->len, sb->alloc - sb->len, fmt, ap ); + if( len > strbuf_avail( sb ) ) + sb->fatal( "your vsnprintf is broken (insatiable)" ); + } + strbuf_setlen( sb, sb->len + len ); +} + +void strbuf_addf( struct strbuf *sb, const char *fmt, ... ) +{ + va_list ap; + va_start( ap, fmt ); + strbuf_vaddf( sb, fmt, ap ); + va_end( ap ); +} + + +size_t strbuf_fread( struct strbuf *sb, FILE *fp ) +{ + size_t ret, nb = 64, read = 0; + size_t oldalloc = sb->alloc; + + if( !sb || !fp ) return read; + + do + { + strbuf_grow( sb, nb ); + ret = fread( sb->buf + sb->len, 1, nb, fp ); + if( ret > 0 ) + { + strbuf_setlen( sb, sb->len + ret ); + read += ret; + } + else if( oldalloc == 0 ) + { + strbuf_release( sb ); + return ret; + } + + } while( ret == nb ); + + return read; +} + + +#define NAMELEN_MAX 128 + +size_t strbuf_env_fread( struct strbuf *sb, FILE *fp ) +{ + size_t read = 0; + char *ln, line[STRBUF_MAXLINE], retline[STRBUF_MAXLINE]; + + if( !sb || !fp ) return read; + + bzero( (void *)line, STRBUF_MAXLINE ); + bzero( (void *)retline, STRBUF_MAXLINE ); + + while( (ln = fgets( line, STRBUF_MAXLINE, fp )) ) + { + char *start = NULL, *stop = NULL; + char *sp = ln; + + if( (start = strstr( sp, "${" )) && (stop = strstr( sp, "}" )) && ((stop - start) > 1) ) + { + struct symbol *sym = NULL; + char *lp = retline; + + do + { + /* may be multiple variables on a single line: */ + + *start = '\0'; *stop++ = '\0'; + if( (sym = envtab_lookup( &strbuf_envtab, start+2 )) && sym->value && sym->value[0] ) + { + strncpy( lp, (const char *)sp, (size_t)(start - sp + 1) ); + lp += (start - sp); + strcpy( lp, (const char *)sym->value ); + lp += strlen( sym->value ); + strcpy( lp, (const char *)stop ); + sp = stop; + } + else + { + strncpy( lp, (const char *)sp, (size_t)(start - sp + 1) ); + lp += (start - sp); + strcpy( lp, (const char *)stop ); + sp = stop; + } + + } while( (start = strstr( sp, "${" )) && (stop = strstr( sp, "}" )) && ((stop - start) > 1) ); + + strbuf_addstr( sb, retline ); + read += strlen( retline );; + } + else + { + strbuf_addstr( sb, line ); + read += strlen( line );; + } + + } /* End of while( ln ) */ + + return read; +} + +ssize_t strbuf_read( struct strbuf *sb, int fd, size_t hint ) +{ + size_t oldlen = sb->len; + size_t oldalloc = sb->alloc; + + strbuf_grow(sb, hint ? hint : 8192); + for( ;; ) + { + ssize_t want = sb->alloc - sb->len - 1; + ssize_t got = read_in_full( fd, sb->buf + sb->len, want ); + + if( got < 0 ) + { + if( oldalloc == 0 ) + strbuf_release( sb ); + else + strbuf_setlen( sb, oldlen ); + return -1; + } + sb->len += got; + if( got < want ) + break; + strbuf_grow( sb, 8192 ); + } + + sb->buf[sb->len] = '\0'; + return sb->len - oldlen; +} + + +size_t strbuf_fwrite( struct strbuf *sb, FILE *fp ) +{ + return sb->len ? fwrite( sb->buf, 1, sb->len, fp ) : 0; +} + +ssize_t strbuf_write( struct strbuf *sb, int fd ) +{ + return sb->len ? write( fd, (const void *)sb->buf, sb->len ) : 0; +} + + +/* XML quoted: */ + +void strbuf_addstr_xml_quoted( struct strbuf *sb, const char *s ) +{ + while( *s ) + { + size_t len = strcspn( s, "\"<>&" ); + strbuf_add( sb, s, len ); + s += len; + + switch( *s ) + { + case '"': + strbuf_addstr( sb, """ ); + break; + case '<': + strbuf_addstr( sb, "<" ); + break; + case '>': + strbuf_addstr( sb, ">" ); + break; + case '&': + strbuf_addstr( sb, "&" ); + break; + case 0: + return; + } + s++; + } +} + +static int is_html_quoted( const char *str ) +{ + int rc = 0, error = 0; + PCRE2_SIZE offset = 0; + const char pattern[] = "^(&[#A-Za-z0-9]*;)"; + + pcre2_match_data *match; + + pcre2_code *regexp = pcre2_compile( (PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0, &error, &offset, NULL ); + if( regexp == NULL ) + { + return 0; /* PCRE compilation failed */ + } + + match = pcre2_match_data_create_from_pattern( regexp, NULL ); + + rc = pcre2_match( regexp, (PCRE2_SPTR)str, (PCRE2_SIZE)strlen(str), 0, 0, match, NULL ); /* sizeof(match)/sizeof(match[0]) */ + if( rc < 0 ) + { + /* not match */ + pcre2_match_data_free( match ); + pcre2_code_free( regexp ); + return 0; + } + else + { + /* match */ + pcre2_match_data_free( match ); + pcre2_code_free( regexp ); + return 1; + } +} + +void strbuf_addstr_html_quoted( struct strbuf *sb, const char *s ) +{ + while( *s ) + { + size_t len = strcspn( s, "\"<>&" ); + strbuf_add( sb, s, len ); + s += len; + + switch( *s ) + { + case '"': + strbuf_addstr( sb, """ ); + break; + case '<': + strbuf_addstr( sb, "<" ); + break; + case '>': + strbuf_addstr( sb, ">" ); + break; + case '&': + if( !is_html_quoted( s ) ) + strbuf_addstr( sb, "&" ); + else + strbuf_addch( sb, *s ); + break; + case 0: + return; + } + s++; + } +} + + +/* urlencode: */ + +int is_rfc3986_reserved_or_unreserved( char ch ) +{ + if( is_rfc3986_unreserved(ch) ) + return 1; + switch( ch ) + { + case '!': case '*': case '\'': case '(': case ')': case ';': + case ':': case '@': case '&': case '=': case '+': case '$': + case ',': case '/': case '?': case '#': case '[': case ']': + return 1; + } + return 0; +} + +int is_rfc3986_unreserved( char ch ) +{ + return isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~'; +} + +static void +strbuf_add_urlencode( struct strbuf *sb, const char *s, size_t len, + char_predicate allow_unencoded_fn ) +{ + strbuf_grow( sb, len ); + while( len-- ) + { + char ch = *s++; + if( allow_unencoded_fn(ch) ) + strbuf_addch( sb, ch ); + else + strbuf_addf( sb, "%%%02x", (unsigned char)ch ); + } +} + +void strbuf_addstr_urlencode( struct strbuf *sb, const char *s, char_predicate allow_unencoded_fn ) +{ + strbuf_add_urlencode( sb, s, strlen(s), allow_unencoded_fn ); +} + + +/* humanise: */ + +static void strbuf_humanise( struct strbuf *sb, off_t bytes, int humanise_rate ) +{ + if( bytes > 1 << 30 ) + { + strbuf_addf( sb, + humanise_rate == 0 ? + /* TRANSLATORS: IEC 80000-13:2008 gibibyte */ + _("%u.%2.2u GiB") : + /* TRANSLATORS: IEC 80000-13:2008 gibibyte/second */ + _("%u.%2.2u GiB/s"), + (unsigned)(bytes >> 30), + (unsigned)(bytes & ((1 << 30) - 1)) / 10737419 ); + } + else if( bytes > 1 << 20 ) + { + unsigned x = bytes + 5243; /* for rounding */ + strbuf_addf( sb, + humanise_rate == 0 ? + /* TRANSLATORS: IEC 80000-13:2008 mebibyte */ + _("%u.%2.2u MiB") : + /* TRANSLATORS: IEC 80000-13:2008 mebibyte/second */ + _("%u.%2.2u MiB/s"), + x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20 ); + } + else if( bytes > 1 << 10 ) + { + unsigned x = bytes + 5; /* for rounding */ + strbuf_addf( sb, + humanise_rate == 0 ? + /* TRANSLATORS: IEC 80000-13:2008 kibibyte */ + _("%u.%2.2u KiB") : + /* TRANSLATORS: IEC 80000-13:2008 kibibyte/second */ + _("%u.%2.2u KiB/s"), + x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10 ); + } + else + { + strbuf_addf( sb, + humanise_rate == 0 ? + /* TRANSLATORS: IEC 80000-13:2008 byte */ + Q_("%u byte", "%u bytes", (unsigned)bytes) : + /* TRANSLATORS: IEC 80000-13:2008 byte/second */ + Q_("%u byte/s", "%u bytes/s", (unsigned)bytes), + (unsigned)bytes ); + } +} + +void strbuf_humanise_bytes( struct strbuf *sb, off_t bytes ) +{ + strbuf_humanise( sb, bytes, 0 ); +} + +void strbuf_humanise_rate( struct strbuf *sb, off_t bytes ) +{ + strbuf_humanise( sb, bytes, 1 ); +} + + +int is_directory( const char *path ) +{ + struct stat st; + return ( !stat( path, &st ) && S_ISDIR(st.st_mode) ); +} + +void strbuf_selfdir( struct strbuf *sb ) +{ + char path[PATH_MAX]; + ssize_t len; + + bzero( (void *)path, PATH_MAX ); + + len = readlink( "/proc/self/exe", &path[0], (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + strbuf_addstr( sb, (const char *)dirname( (char *)&path[0] ) ); + } + else + sb->fatal( "cannot get selfdir" ); +} + +void strbuf_relpath( struct strbuf *sb, const char *path ) +{ + struct strbuf self = STRBUF_INIT; + char p[PATH_MAX]; + + bzero( (void *)p, PATH_MAX ); + + if( realpath( path, (char *)&p[0] ) == NULL ) + { + sb->fatal( "cannot get relative path of '%s'", path ); + } + + strbuf_init( &self, sb->fatal, 0 ); + strbuf_selfdir( &self ); + + strbuf_addstr( sb, (const char *)&p[self.len] ); + + strbuf_release( &self ); +} + +void strbuf_abspath( struct strbuf *sb, const char *path ) +{ + char p[PATH_MAX]; + + if( realpath( path, (char *)&p[0] ) == NULL ) + { + sb->fatal( "cannot get absolute path of '%s'", path ); + } + + strbuf_addstr( sb, (char *)&p[0] ); +}