- http_parse_headers() now accepts a callback for handling of request/response lines
authorMichael Wallner <mike@php.net>
Mon, 11 Apr 2005 10:04:34 +0000 (10:04 +0000)
committerMichael Wallner <mike@php.net>
Mon, 11 Apr 2005 10:04:34 +0000 (10:04 +0000)
  (used in the http_message api)

http_functions.c
http_headers_api.c
http_message_api.c
php_http_headers_api.h
php_http_message_api.h

index a0eb58b3b210caf2dfd7182fad62004e7c4fbf53..aeeea5b9eca8e35be5f30ab24fb08ce2e04e5f17 100644 (file)
@@ -999,21 +999,6 @@ PHP_FUNCTION(http_build_query)
 
 PHP_FUNCTION(http_test)
 {
 
 PHP_FUNCTION(http_test)
 {
-#define HTTP_MESSAGE_STR \
-       "GET / HTTP/1.1\r\n" \
-       "Content-Type: foo/bar\r\n" \
-       "Robots: Noindex,Nofollow\r\n" \
-       "\r\n" \
-       "Body Data!\n"
-#define HTTP_MESSAGE_LEN lenof(HTTP_MESSAGE_STR)
-       http_message *msg = http_message_parse(HTTP_MESSAGE_STR, HTTP_MESSAGE_LEN);
-       char *str;
-       size_t len;
-
-       http_message_tostring(msg, &str, &len);
-
-       RETVAL_STRINGL(str, len, 0);
-       http_message_free(msg);
 }
 
 /*
 }
 
 /*
index 930c496f732ef14336c896ac6c6db064a5cdab22..933b4c8b003922b4490e8a442fd2dac30a20c8ae 100644 (file)
@@ -239,10 +239,11 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_
 /* }}} */
 
 /* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */
 /* }}} */
 
 /* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */
-PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len,
-       HashTable *headers, zend_bool prettify TSRMLS_DC)
+PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len,
+       HashTable *headers, zend_bool prettify,
+       http_parse_headers_callback_t func, void **callback_data TSRMLS_DC)
 {
 {
-       const char *colon = NULL, *line = NULL, *begin = header;
+       char *colon = NULL, *line = NULL, *begin = header;
        zval array;
 
        Z_ARRVAL(array) = headers;
        zval array;
 
        Z_ARRVAL(array) = headers;
@@ -251,16 +252,6 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len
                return FAILURE;
        }
 
                return FAILURE;
        }
 
-       /* status code */
-       if (!strncmp(header, "HTTP/1.", 7)) {
-               char *end = strstr(header, HTTP_CRLF);
-               size_t len = end - (header + lenof("HTTP/1.x "));
-               char *val = estrndup(header + lenof("HTTP/1.x "), len);
-
-               add_assoc_stringl(&array, "Status", val, len, 0);
-               header = end + 2;
-       }
-
        line = header;
 
        while (header_len >= (line - begin)) {
        line = header;
 
        while (header_len >= (line - begin)) {
@@ -271,55 +262,66 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len
                        case 0:
                                --value_len; /* we don't have CR so value length is one char less */
                        case '\n':
                        case 0:
                                --value_len; /* we don't have CR so value length is one char less */
                        case '\n':
-                               if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) {
-
-                                       /* skip empty key */
-                                       if (header != colon) {
-                                               zval **previous = NULL;
-                                               char *value = empty_string;
-                                               int keylen = colon - header;
-                                               char *key = estrndup(header, keylen);
-
-                                               if (prettify) {
-                                                       key = pretty_key(key, keylen, 1, 1);
+                               if ((!(*line - 1)) || ((*line != ' ') && (*line != '\t'))) {
+                                       /* response/request line */
+                                       if (    (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) ||
+                                                       (!strncmp(line - lenof("HTTP/1.x\r") + value_len, "HTTP/1.", lenof("HTTP/1.")))) {
+                                               if (func) {
+                                                       func(callback_data, header, header - (line + 1), &headers TSRMLS_CC);
+                                                       Z_ARRVAL(array) = headers;
                                                }
                                                }
+                                       } else
+                               
+                                       /* "header: value" pair */
+                                       if (colon) {
+
+                                               /* skip empty key */
+                                               if (header != colon) {
+                                                       zval **previous = NULL;
+                                                       char *value = empty_string;
+                                                       int keylen = colon - header;
+                                                       char *key = estrndup(header, keylen);
+
+                                                       if (prettify) {
+                                                               key = pretty_key(key, keylen, 1, 1);
+                                                       }
 
 
-                                               value_len += line - colon - 1;
-
-                                               /* skip leading ws */
-                                               while (isspace(*(++colon))) --value_len;
-                                               /* skip trailing ws */
-                                               while (isspace(colon[value_len - 1])) --value_len;
+                                                       value_len += line - colon - 1;
 
 
-                                               if (value_len > 0) {
-                                                       value = estrndup(colon, value_len);
-                                               } else {
-                                                       value_len = 0;
-                                               }
+                                                       /* skip leading ws */
+                                                       while (isspace(*(++colon))) --value_len;
+                                                       /* skip trailing ws */
+                                                       while (isspace(colon[value_len - 1])) --value_len;
 
 
-                                               /* if we already have got such a header make an array of those */
-                                               if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
-                                                       /* already an array? - just add */
-                                                       if (Z_TYPE_PP(previous) == IS_ARRAY) {
-                                                                       add_next_index_stringl(*previous, value, value_len, 0);
+                                                       if (value_len > 0) {
+                                                               value = estrndup(colon, value_len);
                                                        } else {
                                                        } else {
-                                                               /* create the array */
-                                                               zval *new_array;
-                                                               MAKE_STD_ZVAL(new_array);
-                                                               array_init(new_array);
-
-                                                               add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1);
-                                                               add_next_index_stringl(new_array, value, value_len, 0);
-                                                               add_assoc_zval(&array, key, new_array);
+                                                               value_len = 0;
                                                        }
 
                                                        }
 
-                                                       previous = NULL;
-                                               } else {
-                                                       add_assoc_stringl(&array, key, value, value_len, 0);
+                                                       /* if we already have got such a header make an array of those */
+                                                       if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
+                                                               /* already an array? - just add */
+                                                               if (Z_TYPE_PP(previous) == IS_ARRAY) {
+                                                                               add_next_index_stringl(*previous, value, value_len, 0);
+                                                               } else {
+                                                                       /* create the array */
+                                                                       zval *new_array;
+                                                                       MAKE_STD_ZVAL(new_array);
+                                                                       array_init(new_array);
+
+                                                                       add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1);
+                                                                       add_next_index_stringl(new_array, value, value_len, 0);
+                                                                       add_assoc_zval(&array, key, new_array);
+                                                               }
+
+                                                               previous = NULL;
+                                                       } else {
+                                                               add_assoc_stringl(&array, key, value, value_len, 0);
+                                                       }
+                                                       efree(key);
                                                }
                                                }
-                                               efree(key);
                                        }
                                        }
-
                                        colon = NULL;
                                        value_len = 0;
                                        header += line - header;
                                        colon = NULL;
                                        value_len = 0;
                                        header += line - header;
@@ -337,6 +339,24 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len
 }
 /* }}} */
 
 }
 /* }}} */
 
+PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC)
+{
+       zval array;
+       Z_ARRVAL(array) = *headers;
+       
+       /* response */
+       if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
+               add_assoc_stringl(&array, "Response Status", http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x \r\n"), 0);
+       } else
+       /* request */
+       if (!strncmp(http_line + line_length - lenof("HTTP/1.x\r\n"), "HTTP/1.", lenof("HTTP/1."))) {
+               char *sep = strchr(http_line, ' ');
+               
+               add_assoc_stringl(&array, "Request Method", http_line, sep - http_line, 1);
+               add_assoc_stringl(&array, "Request Uri", sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1, 1);
+       }
+}
+
 /* {{{ */
 PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC)
 {
 /* {{{ */
 PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC)
 {
index e9eccbff55fde8f22cdc3cbd5285d878ccb43a64..0dec4095e2374c4e353f229e374e3837ead41dfb 100644 (file)
 
 #include "phpstr/phpstr.h"
 
 
 #include "phpstr/phpstr.h"
 
-PHP_HTTP_API void _http_message_dtor(http_message *message)
-{
-       if (message) {
-               zend_hash_destroy(&message->hdrs);
-               phpstr_dtor(PHPSTR(message));
-               if (message->raw) {
-                       efree(message->raw);
-               }
-               if (message->type == HTTP_MSG_REQUEST) {
-                       if (message->info.request.method) {
-                               efree(message->info.request.method);
-                       }
-                       if (message->info.request.URI) {
-                               efree(message->info.request.URI);
-                       }
-               }
-       }
-}
-
-PHP_HTTP_API void _http_message_free(http_message *message)
-{
-       if (message) {
-               if (message->nested) {
-                       http_message_free(message->nested);
-               }
-               http_message_dtor(message);
-               efree(message);
-       }
-}
-
 PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type)
 {
        if (!message) {
 PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type)
 {
        if (!message) {
@@ -71,63 +41,70 @@ PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_mes
        return message;
 }
 
        return message;
 }
 
-PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool dup TSRMLS_DC)
+PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t message_length, zend_bool dup TSRMLS_DC)
 {
 {
-       char *message_start = message, *body = NULL;
-       size_t message_length = length, header_length = 0;
+       char *body = NULL;
+       size_t header_length = 0;
        http_message *msg;
 
        http_message *msg;
 
-       if (length < HTTP_MSG_MIN_SIZE) {
-               return NULL;
-       }
-       if (!message) {
+       if (message_length < HTTP_MSG_MIN_SIZE) {
                return NULL;
        }
 
                return NULL;
        }
 
-       if (!(message_start = strstr(message, HTTP_CRLF))) {
+       if (!message) {
                return NULL;
        }
 
                return NULL;
        }
 
-       msg = http_message_init();
+       msg = http_message_new();
+       msg->len = message_length;
+       msg->raw = dup ? estrndup(message, message_length) : message;
 
 
-       msg->len = length;
-       msg->raw = dup ? estrndup(message, length) : message;
-
-       // response
-       if (!strncmp(message, "HTTP/1.", lenof("HTTP/1."))) {
-               msg->type = HTTP_MSG_RESPONSE;
-               msg->info.response.http_version = atof(message + lenof("HTTP/"));
-               msg->info.response.status = atoi(message + lenof("HTTP/1.1 "));
-       } else
-       // request
-       if (!strncmp(message_start - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
-               const char *method_sep_uri = strchr(message, ' ');
-
-               msg->type = HTTP_MSG_REQUEST;
-               msg->info.request.http_version = atof(message_start - lenof("1.1"));
-               msg->info.request.method = estrndup(message, method_sep_uri - message);
-               msg->info.request.URI = estrndup(method_sep_uri + 1, message_start - method_sep_uri - 1 - lenof(" HTTP/1.1"));
+       if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
+               body += lenof(HTTP_CRLF HTTP_CRLF);
+               header_length = body - message;
        } else {
        } else {
-               http_message_free(msg);
-               return NULL;
+               header_length = message_length;
        }
        }
-
-       message_start += lenof(HTTP_CRLF);
-       message_length -= message_start - message;
-
-       if (body = strstr(message_start, HTTP_CRLF HTTP_CRLF)) {
-               body += lenof(HTTP_CRLF HTTP_CRLF);
-               header_length = body - message_start;
+       
+       http_parse_headers_cb(message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg);
+       
+       if (body) {
                phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
                phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
+       }
+       
+       return msg;
+}
+
+PHP_HTTP_API void _http_message_parse_headers_callback(void **message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC)
+{
+       http_message *old = (http_message *) *message;
+       http_message *new;
+       
+       if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
+               new = http_message_new();
+       
+               new->nested = old;
+               *message = new;
+               *headers = &new->hdrs;
        } else {
        } else {
-               header_length = message_length;
+               new = old;
        }
        }
+       
+       // response
+       if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
+               new->type = HTTP_MSG_RESPONSE;
+               new->info.response.http_version = atof(http_line + lenof("HTTP/"));
+               new->info.response.status = atoi(http_line + lenof("HTTP/1.1 "));
+       } else
+       // request
+       if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
+               const char *method_sep_uri = strchr(http_line, ' ');
 
 
-       if (SUCCESS != http_parse_headers_ex(message_start, header_length, &msg->hdrs, 1)) {
-               http_message_free(msg);
-               return NULL;
+               new->type = HTTP_MSG_REQUEST;
+               new->info.request.http_version = atof(http_line + line_length - lenof("1.1"));
+               new->info.request.method = estrndup(http_line, method_sep_uri - http_line);
+               new->info.request.URI = estrndup(method_sep_uri + 1, http_line + line_length - method_sep_uri - 1 - lenof(" HTTP/1.1"));
        }
        }
-       return msg;
 }
 
 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
 }
 
 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
@@ -138,6 +115,7 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
        zval **header;
 
        phpstr_init_ex(&str, msg->len, 1);
        zval **header;
 
        phpstr_init_ex(&str, msg->len, 1);
+       /* set sane alloc size */
        str.size = 4096;
 
        switch (msg->type)
        str.size = 4096;
 
        switch (msg->type)
@@ -158,6 +136,8 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
 
        FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
                if (key) {
 
        FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
                if (key) {
+                       zval **single_header;
+
                        switch (Z_TYPE_PP(header))
                        {
                                case IS_STRING:
                        switch (Z_TYPE_PP(header))
                        {
                                case IS_STRING:
@@ -165,13 +145,9 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
                                break;
                                
                                case IS_ARRAY:
                                break;
                                
                                case IS_ARRAY:
-                               {
-                                       zval **single_header;
-                                       
                                        FOREACH_VAL(*header, single_header) {
                                                phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
                                        }
                                        FOREACH_VAL(*header, single_header) {
                                                phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
                                        }
-                               }
                                break;
                        }
                        
                                break;
                        }
                        
@@ -181,7 +157,6 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
 
        phpstr_appends(&str, HTTP_CRLF);
        phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
 
        phpstr_appends(&str, HTTP_CRLF);
        phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
-       phpstr_fix(&str);
 
        data = phpstr_data(&str, string, length);
        if (!string) {
 
        data = phpstr_data(&str, string, length);
        if (!string) {
@@ -190,3 +165,43 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_
 
        phpstr_dtor(&str);
 }
 
        phpstr_dtor(&str);
 }
+
+PHP_HTTP_API void _http_message_dtor(http_message *message)
+{
+       if (message) {
+               zend_hash_destroy(&message->hdrs);
+               phpstr_dtor(PHPSTR(message));
+               if (message->raw) {
+                       efree(message->raw);
+               }
+               if (message->type == HTTP_MSG_REQUEST) {
+                       if (message->info.request.method) {
+                               efree(message->info.request.method);
+                       }
+                       if (message->info.request.URI) {
+                               efree(message->info.request.URI);
+                       }
+               }
+       }
+}
+
+PHP_HTTP_API void _http_message_free(http_message *message)
+{
+       if (message) {
+               if (message->nested) {
+                       http_message_free(message->nested);
+               }
+               http_message_dtor(message);
+               efree(message);
+       }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
index 033160c1b3db071f1137d6da1be7f3137537e247..7907b6203f456f7f6b54bbfbcd726662d27ab415 100644 (file)
@@ -26,9 +26,16 @@ typedef enum {
        RANGE_ERR
 } http_range_status;
 
        RANGE_ERR
 } http_range_status;
 
-#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1 TSRMLS_CC)
-#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p) TSRMLS_CC)
-PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len, HashTable *headers, zend_bool prettify TSRMLS_DC);
+typedef void (*http_parse_headers_callback_t)(void **callback_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+
+#define http_parse_headers_default_callback _http_parse_headers_default_callback
+PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+
+#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1, _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p), _http_parse_headers_default_callback, NULL TSRMLS_CC)
+#define http_parse_headers_cb(h, l, ht, p, f, d) _http_parse_headers_ex((h), (l), (ht), (p), (f), (d) TSRMLS_CC)
+PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len, HashTable *headers, zend_bool prettify, 
+       http_parse_headers_callback_t func, void *callback_data TSRMLS_DC);
 
 #define http_parse_cookie(c, ht) _http_parse_cookie((c), (ht) TSRMLS_CC)
 PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC);
 
 #define http_parse_cookie(c, ht) _http_parse_cookie((c), (ht) TSRMLS_CC)
 PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC);
index 4de90bcd6fec13257fd9cc2dd014659c03679a03..507693f28ca70b5dbb77106f975dd8f322ff39e7 100644 (file)
@@ -27,6 +27,7 @@ HttpMessage
 */
 
 #include "phpstr/phpstr.h"
 */
 
 #include "phpstr/phpstr.h"
+#include "php_http_headers_api.h"
 
 typedef enum {
        HTTP_MSG_NONE,
 
 typedef enum {
        HTTP_MSG_NONE,
@@ -64,21 +65,32 @@ struct _http_message {
 /* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */
 #define HTTP_MSG_MIN_SIZE 15
 
 /* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */
 #define HTTP_MSG_MIN_SIZE 15
 
-#define HTTP_MSG_RAW(msg) ( (msg)->dup ? msg->raw.dup : msg->raw.ptr )
-
-#define http_message_init() _http_message_init_ex(NULL, 0)
+#define http_message_new() _http_message_init_ex(NULL, 0)
+#define http_message_init(m) _http_message_init_ex((m), 0)
 #define http_message_init_ex(m, t) _http_message_init_ex((m), (t))
 #define http_message_init_ex(m, t) _http_message_init_ex((m), (t))
-#define http_message_free(m) _http_message_free((m))
-#define http_message_dtor(m) _http_message_dtor((m))
-
-PHP_HTTP_API void _http_message_dtor(http_message *message);
-PHP_HTTP_API void _http_message_free(http_message *message);
+PHP_HTTP_API http_message *_http_message_init_ex(http_message *m, http_message_type t);
 
 #define http_message_parse(m, l) http_message_parse_ex((m), (l), 1)
 #define http_message_parse_ex(m, l, d) _http_message_parse_ex((m), (l), (d) TSRMLS_CC)
 PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool duplicate TSRMLS_DC);
 
 
 #define http_message_parse(m, l) http_message_parse_ex((m), (l), 1)
 #define http_message_parse_ex(m, l, d) _http_message_parse_ex((m), (l), (d) TSRMLS_CC)
 PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool duplicate TSRMLS_DC);
 
+#define http_message_parse_headers_callback _http_message_parse_headers_callback
+PHP_HTTP_API void _http_message_parse_headers_callback(void *message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC);
+
 #define http_message_tostring(m, s, l) _http_message_tostring((m), (s), (l))
 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length);
 #define http_message_tostring(m, s, l) _http_message_tostring((m), (s), (l))
 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length);
+#define http_message_dtor(m) _http_message_dtor((m))
+PHP_HTTP_API void _http_message_dtor(http_message *message);
+
+#define http_message_free(m) _http_message_free((m))
+PHP_HTTP_API void _http_message_free(http_message *message);
 
 
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */