summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2014-01-15 00:46:24 +0800
committerLAN-TW <lantw44@gmail.com>2014-01-15 00:46:24 +0800
commitd02c3ba12ae931e191924d44b2b3d4193058b2d9 (patch)
tree4ca425e07dd3cc478383eaaf4003e5f26e158d44
parent562b3936c0140c215bc01ecd7ebc40e004f5b3ba (diff)
downloadsp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.gz
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.bz2
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.lz
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.xz
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.tar.zst
sp2013-d02c3ba12ae931e191924d44b2b3d4193058b2d9.zip
HW4: 基本的 HTTP 語法分析,以及更好的 signal 管理
-rw-r--r--hw4/Makefile.am7
-rwxr-xr-xhw4/autogen.sh2
-rw-r--r--hw4/cgiprog/file_reader.c71
-rw-r--r--hw4/chttpd/chttpd-conn.c89
-rw-r--r--hw4/chttpd/chttpd-server.c113
-rw-r--r--hw4/configure.ac8
-rw-r--r--hw4/l4basic/l4strv.c297
-rw-r--r--hw4/l4basic/l4strv.h76
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 */