diff options
author | LAN-TW <lantw44@gmail.com> | 2014-01-15 00:46:24 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2014-01-15 00:46:24 +0800 |
commit | d02c3ba12ae931e191924d44b2b3d4193058b2d9 (patch) | |
tree | 4ca425e07dd3cc478383eaaf4003e5f26e158d44 | |
parent | 562b3936c0140c215bc01ecd7ebc40e004f5b3ba (diff) | |
download | sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.gz sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.zst sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.zip |
HW4: 基本的 HTTP 語法分析,以及更好的 signal 管理
-rw-r--r-- | hw4/Makefile.am | 7 | ||||
-rwxr-xr-x | hw4/autogen.sh | 2 | ||||
-rw-r--r-- | hw4/cgiprog/file_reader.c | 71 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-conn.c | 89 | ||||
-rw-r--r-- | hw4/chttpd/chttpd-server.c | 113 | ||||
-rw-r--r-- | hw4/configure.ac | 8 | ||||
-rw-r--r-- | hw4/l4basic/l4strv.c | 297 | ||||
-rw-r--r-- | hw4/l4basic/l4strv.h | 76 |
8 files changed, 605 insertions, 58 deletions
diff --git a/hw4/Makefile.am b/hw4/Makefile.am index b04879a..0bbd28c 100644 --- a/hw4/Makefile.am +++ b/hw4/Makefile.am @@ -4,12 +4,17 @@ NULL = bin_PROGRAMS = cgish-httpd noinst_LIBRARIES = libl4basic.a +cgidir = $(datadir)/$(PACKAGE) +cgi_PROGRAMS = file_reader + libl4basic_a_SOURCES = \ l4basic/memwrap.h \ l4basic/memwrap.c \ l4basic/l4common.h \ l4basic/l4array.h \ l4basic/l4array.c \ + l4basic/l4strv.h \ + l4basic/l4strv.c \ l4basic/l4list.h \ l4basic/l4list.c \ l4basic/l4str.h \ @@ -32,3 +37,5 @@ cgish_httpd_SOURCES = \ $(NULL) cgish_httpd_CFLAGS = -pthread -I$(top_srcdir)/l4basic -I$(top_srcdir)/chttpd cgish_httpd_LDADD = $(top_builddir)/libl4basic.a + +file_reader_SOURCES = cgiprog/file_reader.c diff --git a/hw4/autogen.sh b/hw4/autogen.sh index 70d6dde..485ed48 100755 --- a/hw4/autogen.sh +++ b/hw4/autogen.sh @@ -17,7 +17,7 @@ echo "==> Downloading l4basic files" git clone "$1" "tmp" cd "tmp" -for i in l4common.h l4list.[ch] l4posix.[ch] l4str.[ch] l4array.[ch] +for i in l4common.h l4list.[ch] l4array.[ch] l4strv.[ch] l4str.[ch] l4posix.[ch] do generate_file "$i" "../l4basic/l4basic.sed" done diff --git a/hw4/cgiprog/file_reader.c b/hw4/cgiprog/file_reader.c new file mode 100644 index 0000000..dec8b30 --- /dev/null +++ b/hw4/cgiprog/file_reader.c @@ -0,0 +1,71 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define NL "\015\012" + +int main (int argc, char* argv[]) { + char* qs = getenv ("QUERY_STRING"); + if (qs == NULL || *qs == '\0') { + fputs ( + "HTTP/1.1 400 Bad Request" NL + "Content-Type: text/html" NL NL + "<html><head><title>400 Bad Request</title></head>" NL + "<body><p>Query string is missing</p>" + "</body></html>" NL, stdout); + return 1; + } + + if (strncmp (qs, "filename=", 9)) { + fputs ( + "HTTP/1.1 400 Bad Request" NL + "Content-Type: text/html" NL NL + "<html><head><title>400 Bad Request</title></head>" NL + "<body><p>Query string is not valid</p>" + "</body></html>" NL, stdout); + return 1; + } + + char* fn = qs + 9; + int fd = open (fn, O_RDONLY); + if (fd < 0) { + switch (errno) { + case ENOENT: + printf ( + "HTTP/1.1 404 Not Found" NL + "Content-Type: text/html" NL NL + "<html><head><title>404 Not Found</title></head>" NL + "<body><p>File `%s\' cannot be found on the system</p>" NL + "</body></html>" NL, fn); + return 2; + case EACCES: + printf ( + "HTTP/1.1 403 Forbidden" NL + "Content-Type: text/html" NL NL + "<html><head><title>403 Forbidden</title></head>" NL + "<body><p>Access to `%s\' is denied</p>" NL + "</body></html>" NL, fn); + return 3; + default: + printf ( + "HTTP/1.1 500 Internal Server Error" NL + "Content-Type: text/html" NL NL + "<html><head><title>500 Internal Server Error</title></head>" NL + "<body><p>%s</p>" NL + "</body></html>" NL, strerror (errno)); + return 4; + } + } + + fputs ("Content-Type: application/octet-stream" NL NL, stdout); + fflush (stdout); + + char buf[4096]; + ssize_t r; + for (; (r = read (fd, buf, 4096)) > 0; write (1, buf, r)); + + return 0; +} diff --git a/hw4/chttpd/chttpd-conn.c b/hw4/chttpd/chttpd-conn.c index 893661d..538f079 100644 --- a/hw4/chttpd/chttpd-conn.c +++ b/hw4/chttpd/chttpd-conn.c @@ -9,9 +9,12 @@ #include "chttpd-log.h" #include "chttpd-server.h" #include <l4array.h> +#include <l4strv.h> #include <l4list.h> #include <l4str.h> +#include <l4posix.h> +#include <ctype.h> #include <errno.h> #include <pthread.h> #include <signal.h> @@ -19,11 +22,43 @@ #include <stdio.h> #include <unistd.h> +#define NL "\015\012" + +enum { + HTTP_STATUS_200_OK, + HTTP_STATUS_400_BAD_REQUEST, + HTTP_STATUS_404_NOT_FOUND, + HTTP_STATUS_500_INTERNAL_SERVER_ERROR +}; + +static char* http_status_hdr[] = { + [HTTP_STATUS_200_OK] = "HTTP/1.1 200 OK" NL, + [HTTP_STATUS_400_BAD_REQUEST] = "HTTP/1.1 400 Bad Request" NL, + [HTTP_STATUS_404_NOT_FOUND] = "HTTP/1.1 404 Not Found" NL, + [HTTP_STATUS_500_INTERNAL_SERVER_ERROR] = "HTTP/1.1 500 Internal Server Error" NL +}; + +static char* http_status_msg[] = { + [HTTP_STATUS_400_BAD_REQUEST] = + "\n<html><head><title>400 Bad Request</title></head>" NL + "<body><p>You browser sent a broken HTTP request." NL + "<a href=\"http://tools.ietf.org/search/rfc2616#section-10.4.1\">" + "[400]</a></p>" + "</body></html>" NL +}; + +static inline void http_status_write (int connfd, int status_enum) { + lbs_posix_write_all (connfd, http_status_hdr[status_enum], 0); + lbs_posix_write_all (connfd, NL, 2); + lbs_posix_write_all (connfd, http_status_msg[status_enum], 0); +} + #define CHTTPD_CONN_THREAD_INIT \ ChttpdConn* conn = ptr_to_ChttpdConn; \ ChttpdServer* server = conn->server; \ ChttpdLog* hlog = conn->hlog; \ unsigned long long id = conn->id; \ + int connfd = conn->connfd; \ pthread_sigmask (SIG_SETMASK, &server->conn_mask, NULL); #define CHTTPD_CONN_THREAD_DESTROY \ @@ -60,6 +95,7 @@ void* chttpd_conn_admin (void* ptr_to_ChttpdConn) { CHTTPD_CONN_THREAD_INIT; chttpd_log_write (hlog, "[%4llu] sorry, function not implemented", id); + chttpd_log_write (hlog, "[%4llu] fd is %d", connfd); CHTTPD_CONN_THREAD_DESTROY; } @@ -81,7 +117,7 @@ void* chttpd_conn_http (void* ptr_to_ChttpdConn) { size_t data_offset; while (true) { - ssize_t r = read (conn->connfd, conn->buf, CHTTPD_CONN_BUF_SIZE); + ssize_t r = read (connfd, conn->buf, CHTTPD_CONN_BUF_SIZE); if (r < 0) { if (errno == EINTR || errno == EAGAIN) { continue; @@ -113,6 +149,57 @@ void* chttpd_conn_http (void* ptr_to_ChttpdConn) { *hdr_start = '\0'; hdr_start += 2; } + chttpd_log_write (hlog, "[%4llu] client request: %s", id, method_start); + + /* Parse method line */ + char* tokstore; + char* ma1 = strtok_r (method_start, " \t", &tokstore); + if (ma1 == NULL) { + chttpd_log_write (hlog, "[%4llu] 400 Bad Request: URL not found", id); + http_status_write (connfd, HTTP_STATUS_400_BAD_REQUEST); + goto http_exit; + } + char* ma2 = strtok_r (NULL, " \t", &tokstore); + if (ma2 == NULL) { + chttpd_log_write (hlog, "[%4llu] 400 Bad Request: What?", id); + http_status_write (connfd, HTTP_STATUS_400_BAD_REQUEST); + goto http_exit; + } else { + if (*ma2 != '/') { + chttpd_log_write (hlog, + "[%4llu] 400 Bad Request: URL not started with /", id); + http_status_write (connfd, HTTP_STATUS_400_BAD_REQUEST); + goto http_exit; + } + } + char* ma3 = strtok_r (NULL, " \t", &tokstore); + + char* request_method = ma1; + char* request_uri = ma2 + 1; + char* server_protocol = (ma3 == NULL) ? "" : ma3; + char* query_string = strtok_r (request_uri, "?", &tokstore); + query_string = (query_string == NULL) ? "" : query_string; + +#ifdef SPHW_RESTRICTION + for (char* p = request_uri; *p != '\0'; p++) { + if (!(*p == '_' || isalnum (*p))) { + chttpd_log_write (hlog, "[%4llu] 400 Bad Request: character" + " `%c\' in request URI is not allowed", id, *p); + http_status_write (connfd, HTTP_STATUS_400_BAD_REQUEST); + goto http_exit; + } + } + for (char* p = query_string; *p != '\0'; p++) { + if (!(*p == '_' || *p == '=' || *p == '&' || isalnum (*p))) { + chttpd_log_write (hlog, "[%4llu] 400 Bad Request: character" + " `%c\' in query string is not allowed", id, *p); + http_status_write (connfd, HTTP_STATUS_400_BAD_REQUEST); + goto http_exit; + } + } +#endif + + http_exit: lbs_array_unref (hdr_buf); diff --git a/hw4/chttpd/chttpd-server.c b/hw4/chttpd/chttpd-server.c index 7d540d0..ba1eea6 100644 --- a/hw4/chttpd/chttpd-server.c +++ b/hw4/chttpd/chttpd-server.c @@ -273,7 +273,7 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { /* Check whether we are going to shutdown the server */ if (server_shutdown) { - server_shutdown = 0; + server_shutdown++; pthread_mutex_lock (&chttpd_server_notify_mutex); chttpd_server_notify = 1; pthread_mutex_unlock (&chttpd_server_notify_mutex); @@ -336,68 +336,69 @@ static void chttpd_main_loop (ChttpdLog* hlog, LbsListMeta* slist) { nfds = 0; } - /* Log current server info */ - chttpd_log_write_str (hlog, "[Server status change notification]"); + if (server_notify || server_shutdown < 1) { + chttpd_log_write_str (hlog, "[Server status change notification]"); - unsigned long long count; - pthread_rwlock_rdlock (&chttpd_server_count_lock); - count = chttpd_server_count; - pthread_rwlock_unlock (&chttpd_server_count_lock); + unsigned long long count; + pthread_rwlock_rdlock (&chttpd_server_count_lock); + count = chttpd_server_count; + pthread_rwlock_unlock (&chttpd_server_count_lock); - chttpd_log_write (hlog, "%lld connections has been accepted", count); + chttpd_log_write (hlog, "%lld connections has been accepted", count); - unsigned c = 0; - for (iter = slist->first; iter != NULL; iter = iter->next, c++) { - ChttpdServer* server = iter->data; - struct sockaddr_storage addr; - char* type; - size_t conn_count; - - pthread_rwlock_rdlock (&server->lock); - addr = server->addr; - type = server->attr_admin ? "ADMIN" : "HTTP"; - conn_count = server->conn->len; - pthread_rwlock_unlock (&server->lock); + unsigned c = 0; + for (iter = slist->first; iter != NULL; iter = iter->next, c++) { + ChttpdServer* server = iter->data; + struct sockaddr_storage addr; + char* type; + size_t conn_count; + + pthread_rwlock_rdlock (&server->lock); + addr = server->addr; + type = server->attr_admin ? "ADMIN" : "HTTP"; + conn_count = server->conn->len; + pthread_rwlock_unlock (&server->lock); - char ipstr[INET6_ADDRSTRLEN]; - _Static_assert (INET6_ADDRSTRLEN >= INET_ADDRSTRLEN, - "Why IPv6 address is shorter than IPv4 address?"); - switch (addr.ss_family) { - case AF_UNIX: - CHTTPD_SOCKET_SOCKADDR_UN_SET_NULL (SOCKADDR_UN (&addr)); - chttpd_log_write (hlog, - "Server %u: type %s, UNIX socket, path %s" - " (%zu active connections)", - c, type, SOCKADDR_UN (&addr)->sun_path, conn_count); - break; - case AF_INET: - if (!inet_ntop (AF_INET, &(SOCKADDR_IN (&addr)->sin_addr), - ipstr, INET_ADDRSTRLEN)) { - strcpy (ipstr, "unknown"); - } - chttpd_log_write (hlog, "Server %u: type %s, " - "IPv4 socket, address %s, port %" PRIu16 - " (%zu active connections)", c, type, ipstr, - ntohs (SOCKADDR_IN (&addr)->sin_port), conn_count); - break; - case AF_INET6: - if (!inet_ntop (AF_INET6, &(SOCKADDR_IN6 (&addr)->sin6_addr), - ipstr, INET6_ADDRSTRLEN)) { - strcpy (ipstr, "unknown"); - } - chttpd_log_write (hlog, "Server %u: type %s, " - "IPv6 socket, address %s, port %" PRIu16 - " (%zu active connections)", c, type, ipstr, - ntohs (SOCKADDR_IN6 (&addr)->sin6_port), conn_count); - break; - default: - chttpd_log_write (hlog, - "Server %u: type %s, unknown socket", c, type); - break; + char ipstr[INET6_ADDRSTRLEN]; + _Static_assert (INET6_ADDRSTRLEN >= INET_ADDRSTRLEN, + "Why IPv6 address is shorter than IPv4 address?"); + switch (addr.ss_family) { + case AF_UNIX: + CHTTPD_SOCKET_SOCKADDR_UN_SET_NULL (SOCKADDR_UN (&addr)); + chttpd_log_write (hlog, + "Server %u: type %s, UNIX socket, path %s" + " (%zu active connections)", + c, type, SOCKADDR_UN (&addr)->sun_path, conn_count); + break; + case AF_INET: + if (!inet_ntop (AF_INET, &(SOCKADDR_IN (&addr)->sin_addr), + ipstr, INET_ADDRSTRLEN)) { + strcpy (ipstr, "unknown"); + } + chttpd_log_write (hlog, "Server %u: type %s, " + "IPv4 socket, address %s, port %" PRIu16 + " (%zu active connections)", c, type, ipstr, + ntohs (SOCKADDR_IN (&addr)->sin_port), conn_count); + break; + case AF_INET6: + if (!inet_ntop (AF_INET6, &(SOCKADDR_IN6 (&addr)->sin6_addr), + ipstr, INET6_ADDRSTRLEN)) { + strcpy (ipstr, "unknown"); + } + chttpd_log_write (hlog, "Server %u: type %s, " + "IPv6 socket, address %s, port %" PRIu16 + " (%zu active connections)", c, type, ipstr, + ntohs (SOCKADDR_IN6 (&addr)->sin6_port), conn_count); + break; + default: + chttpd_log_write (hlog, + "Server %u: type %s, unknown socket", c, type); + break; + } } + chttpd_log_write (hlog, "%u servers are active", slist->len); } - chttpd_log_write (hlog, "%u servers are active", slist->len); } else { pthread_mutex_unlock (&chttpd_server_notify_mutex); } diff --git a/hw4/configure.ac b/hw4/configure.ac index 894d32e..8a2dc0b 100644 --- a/hw4/configure.ac +++ b/hw4/configure.ac @@ -35,5 +35,13 @@ AC_FUNC_MALLOC AC_FUNC_REALLOC AC_FUNC_STRERROR_R +# Misc options +AC_ARG_ENABLE([restriction], + [AS_HELP_STRING([--enable-restriction], + [Enable some extra restriction to meet homework requirement])], + [enable_restriction="$enableval"], [enable_restriction="no"]) +AS_IF([test "x$enable_restriction" = "xyes"], + [AC_DEFINE([SPHW_RESTRICTION], [1], [SPHW restriction])]) + AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/hw4/l4basic/l4strv.c b/hw4/l4basic/l4strv.c new file mode 100644 index 0000000..b05ad5b --- /dev/null +++ b/hw4/l4basic/l4strv.c @@ -0,0 +1,297 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ + +#include "l4strv.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +LbsStrv* lbs_strv_new_with_max (size_t max) { + LbsStrv* strv = xmalloc (sizeof (LbsStrv)); + if (strv == NULL) { + return NULL; + } + + if (lbs_array_init_with_max (&strv->array, sizeof (LbsArray), max) < 0) { + free (strv); + return NULL; + } + + strv->ref_count = 1; + strv->is_alloc = true; + return strv; +} + +int lbs_strv_init_with_max (LbsStrv* strv, size_t max) { + if (lbs_array_init_with_max (&strv->array, sizeof (LbsArray), max) < 0) { + return -1; + } + + strv->ref_count = 1; + strv->is_alloc = false; + return 0; +} + +void* lbs_strv_ref_generic (void* strv_generic) { + LbsStrv* strv = LBS_STRV (strv_generic); + strv->ref_count++; + return strv; +} + +void lbs_strv_unref_generic (void* strv_generic) { + if (strv_generic == NULL) { + return; + } + LbsStrv* strv = LBS_STRV (strv_generic); + strv->ref_count--; + if (strv->ref_count <= 0) { + lbs_strv_free (strv); + } +} + +void lbs_strv_free_generic (void* strv_generic) { + if (strv_generic == NULL) { + return; + } + LbsStrv* strv = LBS_STRV (strv_generic); + size_t i = 0; + for (; i < lbs_strv_get_len (strv); i++) { + LbsArray* str_wrapper = lbs_strv_get_str_wrapper (strv, i); + lbs_array_unref (str_wrapper); + } + lbs_array_unref (&strv->array); + if (strv->is_alloc) { + free (strv); + } +} + +char* lbs_strv_dup_str (LbsStrv* strv, size_t stri) { + LbsArray* str_wrapper = lbs_strv_get_str_wrapper (strv, stri); + size_t len = lbs_array_get_len (str_wrapper); + char* str = lbs_array_get_data (str_wrapper); + + char* str_new = xmalloc (len + sizeof (char)); + strncpy (str_new, str, len); + str_new[len] = '\0'; + + return str_new; +} + +int lbs_strv_append_char (LbsStrv* strv, size_t stri, char chr) { + if (chr == '\0') { + return -1; + } + + if (stri >= lbs_strv_get_len (strv)) { + stri = lbs_strv_get_len (strv); + LbsArray str_struct; + LbsArray* str = &str_struct; + if (lbs_array_init (str, sizeof (char)) < 0) { + return -1; + } + if (lbs_array_append_data (&strv->array, str) < 0) { + lbs_array_unref (str); + return -1; + } + } + LbsArray* str = lbs_strv_get_str_wrapper (strv, stri); + return lbs_array_append_data (str, &chr); +} + +int lbs_strv_append_str_empty (LbsStrv* strv) { + LbsArray str_struct, *str = &str_struct; + if (lbs_array_init (str, sizeof (char)) < 0) { + return -1; + } + if (lbs_array_append_data (&strv->array, &str_struct) < 0) { + lbs_array_unref (str); + return -1; + } + return 0; +} + +int lbs_strv_append_str (LbsStrv* strv, const char* bstr) { + if (bstr == NULL) { + return -1; + } + + size_t len = strlen (bstr); + char* str_copy = xmalloc (sizeof (char) * len); + if (str_copy == NULL) { + return -1; + } + strncpy (str_copy, bstr, len); + + LbsArray str_struct, *str = &str_struct; + if (lbs_array_make_struct (str, sizeof (char), len, len, str_copy) == NULL) + { + free (str_copy); + return -1; + } + + if (lbs_array_append_data (&strv->array, &str_struct) < 0) { + lbs_array_unref (str); + free (str_copy); + return -1; + } + + return 0; +} + +int lbs_strv_append_strv (LbsStrv* strv, const char* const* bstrv) { + if (bstrv == NULL) { + return -1; + } + + int i; + for (i = 0; bstrv[i] != NULL; i++) { + if (lbs_strv_append_str (strv, bstrv[i]) < 0) { + return -1; + } + } + return 0; +} + +int lbs_strv_remove_str (LbsStrv* strv) { + size_t len = lbs_strv_get_len (strv); + if (len <= 0) { + return -1; + } + + lbs_array_unref (lbs_strv_get_str_wrapper (strv, len - 1)); + return lbs_array_remove (&strv->array); +} + +int lbs_strv_minimize (LbsStrv* strv) { + if (lbs_array_minimize (&strv->array) < 0) { + return -1; + } + + size_t i; + size_t len = lbs_strv_get_len (strv); + for (i = 0; i < len; i++) { + LbsArray* str = lbs_strv_get_str_wrapper (strv, i); + if (lbs_array_minimize (str) < 0) { + return -1; + } + } + + return 0; +} + +char** lbs_strv_copy_strv (LbsStrv* strv) { + size_t len = lbs_strv_get_len (strv); + char** bstrv = xmalloc (sizeof (char*) * (len + 1)); + if (bstrv == NULL) { + return NULL; + } + + size_t i; + for (i = 0; i < len; i++) { + LbsArray* str = lbs_strv_get_str_wrapper (strv, i); + size_t str_len = lbs_array_get_len (str); + char* bstr = xmalloc (sizeof (char) * (str_len + 1)); + + strncpy (bstr, lbs_array_get_data (str), str_len); + bstr[str_len] = '\0'; + bstrv[i] = bstr; + } + bstrv[len] = NULL; + return bstrv; +} + +char** lbs_strv_drop_struct (LbsStrv* strv) { + char** bstrv = lbs_strv_copy_strv (strv); + if (bstrv == NULL) { + return NULL; + } + + lbs_strv_free (strv); + return bstrv; +} + +char** lbs_strv_generic_build (const char* str_first, ...) { + LbsArray bstrv_w_struct, *bstrv_w = &bstrv_w_struct; + va_list ap; + char* str; + char* str_copy; + size_t str_len; + + if (lbs_array_init (bstrv_w, sizeof (void*)) < 0) { + return NULL; + } + + va_start (ap, str_first); + + str_len = strlen (str_first); + str_copy = xmalloc (sizeof (char) * (str_len + 1)); + if (str_copy == NULL) { + lbs_array_unref (bstrv_w); + va_end (ap); + return NULL; + } + strcpy (str_copy, str_first); + + if (lbs_array_append_ptr (bstrv_w, str_copy) < 0) { + free (str_copy); + lbs_array_unref (bstrv_w); + va_end (ap); + return NULL; + } + + + while ((str = va_arg (ap, char*)) != NULL) { + str_len = strlen (str); + str_copy = xmalloc (sizeof (char) * (str_len + 1)); + if (str_copy == NULL) { + lbs_array_unref (bstrv_w); + va_end (ap); + return NULL; + } + strcpy (str_copy, str); + + if (lbs_array_append_ptr (bstrv_w, str_copy) < 0) { + free (str_copy); + lbs_array_unref (bstrv_w); + va_end (ap); + return NULL; + } + } + if (lbs_array_append_ptr (bstrv_w, NULL) < 0) { + lbs_array_unref (bstrv_w); + va_end (ap); + return NULL; + } + + va_end (ap); + + return lbs_array_drop_struct (bstrv_w); +} + +int lbs_strv_generic_cmp ( + const char* const* bstrv1, const char* const* bstrv2) { + int i; + for (i = 0; bstrv1[i] != NULL && bstrv2[i] != NULL; i++) { + int r = strcmp (bstrv1[i], bstrv2[i]); + if (r != 0) { + return r; + } + } + + if (bstrv1[i] != NULL) { + return 1; + } + if (bstrv2[i] != NULL) { + return -1; + } + + return 0; +} + +void lbs_strv_generic_free (char** bstrv) { + int i; + for (i = 0; bstrv[i] != NULL; i++) { + free (bstrv[i]); + } + free (bstrv); +} diff --git a/hw4/l4basic/l4strv.h b/hw4/l4basic/l4strv.h new file mode 100644 index 0000000..60e5bb1 --- /dev/null +++ b/hw4/l4basic/l4strv.h @@ -0,0 +1,76 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef LBS_STRV_H +#define LBS_STRV_H + +#include <l4common.h> +#include <l4array.h> + +typedef struct LbsStrvStruct { + /*< private >*/ + LbsArray array; /* data */ + unsigned ref_count; /* reference count */ + bool is_alloc; /* is allocated using xmalloc */ +} LbsStrv; + +#define LBS_STRV(x) ((LbsStrv*)(x)) +#define LBS_STRV_GENERIC(x) ((char**)(x)) +#define LBS_STRV_GENERIC_CONST(x) ((const char* const*)(x)) + +#define lbs_strv_new() (lbs_strv_new_with_max (0)) +LbsStrv* lbs_strv_new_with_max (size_t max); + +#define lbs_strv_init(strv) (lbs_strv_init_with_max (strv, 0)) +int lbs_strv_init_with_max (LbsStrv* strv, size_t max); + +#define lbs_strv_ref(strv) \ + (lbs_strv_ref_generic (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*))) +#define lbs_strv_unref(strv) \ + (lbs_strv_unref_generic (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*))) +void* lbs_strv_ref_generic (void* strv); +void lbs_strv_unref_generic (void* strv); + +#define lbs_strv_free(array) \ + (lbs_strv_free_generic (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*))) +void lbs_strv_free_generic (void* strv); + +#define lbs_strv_get_len(strv) \ + (lbs_array_get_len (&(LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->array))) +#define lbs_strv_get_max(strv) \ + (lbs_array_get_max (&(LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->array))) +#define lbs_strv_get_ref_count(strv) \ + (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->ref_count) +#define lbs_strv_get_is_alloc(strv) \ + (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->is_alloc) +#define lbs_strv_set_len(strv,len) \ + (lbs_array_set_len (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->array, (len))) +#define lbs_strv_set_max(strv,max) \ + (lbs_array_set_max (LBS_COMMON_CHECK_TYPE ((strv), LbsStrv*)->array, (max))) + +char* lbs_strv_dup_str (LbsStrv* strv, size_t stri); +#define lbs_strv_get_str_wrapper(strv,stri) \ + (&(lbs_array_v (&((strv)->array), LbsArray, (stri)))) +#define lbs_strv_get_str_len(strv,stri) \ + (lbs_array_get_len (lbs_strv_get_str_wrapper ((strv), (stri)))) +#define lbs_strv_get_str_not_null_terminated(strv, stri) \ + ((char*)(lbs_array_get_data (lbs_strv_get_str_wrapper ((strv), (stri))))) +#define lbs_strv_char(strv,stri,chri) \ + (lbs_array_v (lbs_strv_get_str_wrapper (strv, stri), char, (chri))) + +int lbs_strv_append_char (LbsStrv* strv, size_t stri, char chr); +int lbs_strv_append_str_empty (LbsStrv* strv); +int lbs_strv_append_str (LbsStrv* strv, const char* bstr); +int lbs_strv_append_strv (LbsStrv* strv, const char* const* bstrv); +int lbs_strv_remove_str (LbsStrv* strv); +#define lbs_strv_remove_char(strv,stri) \ + (lbs_array_remove (lbs_strv_get_str_wrapper ((strv), (stri)))) + +int lbs_strv_minimize (LbsStrv* strv); +char** lbs_strv_copy_strv (LbsStrv* strv); +char** lbs_strv_drop_struct (LbsStrv* strv); + +char** lbs_strv_generic_build (const char* str, ...); +int lbs_strv_generic_cmp (const char* const* bstrv1, + const char* const* bstrv2); +void lbs_strv_generic_free (char** bstrv); + +#endif /* LBS_STRV_H */ |