diff --git a/Makefile b/Makefile index c3f8b3e..52d55ea 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ endif all: cweb.c cweb.h cozisnews$(EXT) -cweb.c cweb.h: +cweb.c cweb.h: src/main.c src/main.h python amalg.py sqlite3.o: demo/sqlite3.c diff --git a/amalg.py b/amalg.py index 9a999ff..4be30c3 100644 --- a/amalg.py +++ b/amalg.py @@ -12,7 +12,7 @@ class Amalgamator: self.out += "// " + file + "\n" self.out += "////////////////////////////////////////////////////////////////////////////////////////\n" self.out += "\n" - self.out += "#line 1 \"" + file + "\"\n" + #self.out += "#line 1 \"" + file + "\"\n" self.out += open(file).read() if len(self.out) > 0 and self.out[len(self.out)-1] != '\n': diff --git a/cweb.c b/cweb.c index fb45766..d0ddc16 100644 --- a/cweb.c +++ b/cweb.c @@ -5,7 +5,6 @@ // 3p/wl.h //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/wl.h" #include #include @@ -101,7 +100,6 @@ void wl_append (WL_Runtime *rt); // 3p/chttp.h //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/chttp.h" #ifndef HTTP_AMALGAMATION #define HTTP_AMALGAMATION @@ -633,7 +631,6 @@ int http_serve (char *addr, int port, HTTP_Router *router); // 3p/chttp.c //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/chttp.c" #ifndef HTTP_NOINCLUDE #include "chttp.h" #endif @@ -6203,7 +6200,6 @@ int http_serve(char *addr, int port, HTTP_Router *router) // 3p/crypt_blowfish.h //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/crypt_blowfish.h" /* * Written by Solar Designer in 2000-2011. * No copyright is claimed, and the software is hereby placed in the public @@ -6236,7 +6232,6 @@ extern char *_crypt_gensalt_blowfish_rn(const char *prefix, // 3p/crypt_blowfish.c //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/crypt_blowfish.c" /* * The crypt_blowfish homepage is: * @@ -7149,7 +7144,6 @@ char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count, // 3p/wl.c //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "3p/wl.c" #include #include #include @@ -12985,7 +12979,6 @@ void wl_runtime_dump(WL_Runtime *rt) // src/main.c //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "src/main.c" #include "sqlite3.h" #include "wl.h" #include "chttp.h" @@ -13099,7 +13092,8 @@ CWEB_VArg cweb_varg_from_ull (long long ull) { return (CWEB_VArg) { CWEB_VA CWEB_VArg cweb_varg_from_f (float f) { return (CWEB_VArg) { CWEB_VARG_TYPE_F, .f=f }; } CWEB_VArg cweb_varg_from_d (double d) { return (CWEB_VArg) { CWEB_VARG_TYPE_D, .d=d }; } CWEB_VArg cweb_varg_from_b (bool b) { return (CWEB_VArg) { CWEB_VARG_TYPE_B, .b=b }; } -CWEB_VArg cweb_varg_from_str (HTTP_String str) { return (CWEB_VArg) { CWEB_VARG_TYPE_STR, .str=str }; } +CWEB_VArg cweb_varg_from_str (CWEB_String str) { return (CWEB_VArg) { CWEB_VARG_TYPE_STR, .str=str }; } +CWEB_VArg cweb_varg_from_hash (CWEB_PasswordHash hash) { return (CWEB_VArg) { CWEB_VARG_TYPE_HASH, .hash=hash }; } CWEB_VArg cweb_varg_from_pc (char *pc) { return (CWEB_VArg) { CWEB_VARG_TYPE_PC, .pc=pc }; } CWEB_VArg cweb_varg_from_ps (short *ps) { return (CWEB_VArg) { CWEB_VARG_TYPE_PS, .ps=ps }; } CWEB_VArg cweb_varg_from_pi (int *pi) { return (CWEB_VArg) { CWEB_VARG_TYPE_PI, .pi=pi }; } @@ -13118,8 +13112,8 @@ CWEB_VArg cweb_varg_from_pull (long long *pull) { return (CWEB_VArg) { CWEB_VA CWEB_VArg cweb_varg_from_pf (float *pf) { return (CWEB_VArg) { CWEB_VARG_TYPE_PF, .pf=pf }; } CWEB_VArg cweb_varg_from_pd (double *pd) { return (CWEB_VArg) { CWEB_VARG_TYPE_PD, .pd=pd }; } CWEB_VArg cweb_varg_from_pb (bool *pb) { return (CWEB_VArg) { CWEB_VARG_TYPE_PB, .pb=pb }; } -CWEB_VArg cweb_varg_from_pstr (HTTP_String *pstr) { return (CWEB_VArg) { CWEB_VARG_TYPE_PSTR, .pstr=pstr }; } - +CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr) { return (CWEB_VArg) { CWEB_VARG_TYPE_PSTR, .pstr=pstr }; } +CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash) { return (CWEB_VArg) { CWEB_VARG_TYPE_PHASH, .phash=phash }; } ///////////////////////////////////////////////////////////////// // RANDOM @@ -13816,6 +13810,11 @@ CWEB_Request *cweb_wait(CWEB *cweb) return req; } +bool cweb_match_endpoint(CWEB_Request *req, CWEB_String str) +{ + return http_streq(req->req->url.path, (HTTP_String) { str.ptr, str.len }); +} + CWEB_String cweb_get_param_s(CWEB_Request *req, CWEB_String name) { HTTP_String res = http_get_param(req->req->body, @@ -13859,12 +13858,155 @@ void cweb_respond(CWEB_Request *req, int status, CWEB_String content) http_response_builder_done(req->builder); } -void cweb_respond_redirect(CWEB_Request *req, CWEB_String target) +typedef struct { + char *dst; + int cap; + int len; +} StaticOutputBuffer; + +static void append_to_output(StaticOutputBuffer *out, char *src, int len) { - char location[128]; - int ret = snprintf(location, sizeof(location), "Location: %.*s", target.len, target.ptr); - if (ret < 0 || ret >= SIZEOF(location)) { - // TODO + int unused = out->cap - out->len; + if (unused > 0) + memcpy(out->dst + out->len, src, MIN(len, unused)); + out->len += len; +} + +static void append_to_output_u64(StaticOutputBuffer *out, uint64_t n) +{ + // TODO +} + +static void append_to_output_s64(StaticOutputBuffer *out, int64_t n) +{ + // TODO +} + +static void append_to_output_ptr(StaticOutputBuffer *out, void *p) +{ + // TODO +} + +static void value_to_output(StaticOutputBuffer *out, CWEB_VArg arg) +{ + switch (arg.type) { + case CWEB_VARG_TYPE_C : append_to_output(out, &arg.c, 1); break; + case CWEB_VARG_TYPE_S : append_to_output_s64(out, arg.s); break; + case CWEB_VARG_TYPE_I : append_to_output_s64(out, arg.i); break; + case CWEB_VARG_TYPE_L : append_to_output_s64(out, arg.l); break; + case CWEB_VARG_TYPE_LL : append_to_output_s64(out, arg.ll); break; + case CWEB_VARG_TYPE_SC : append_to_output_s64(out, arg.sc); break; + case CWEB_VARG_TYPE_SS : append_to_output_s64(out, arg.ss); break; + case CWEB_VARG_TYPE_SI : append_to_output_s64(out, arg.si); break; + case CWEB_VARG_TYPE_SL : append_to_output_s64(out, arg.sl); break; + case CWEB_VARG_TYPE_SLL : append_to_output_s64(out, arg.sll); break; + case CWEB_VARG_TYPE_UC : append_to_output_u64(out, arg.uc); break; + case CWEB_VARG_TYPE_US : append_to_output_u64(out, arg.us); break; + case CWEB_VARG_TYPE_UI : append_to_output_u64(out, arg.ui); break; + case CWEB_VARG_TYPE_UL : append_to_output_u64(out, arg.ul); break; + case CWEB_VARG_TYPE_ULL : append_to_output_u64(out, arg.ull); break; + case CWEB_VARG_TYPE_F : append_to_output_f64(out, arg.f); break; + case CWEB_VARG_TYPE_D : append_to_output_u64(out, arg.d); break; + case CWEB_VARG_TYPE_B : append_to_output(out, arg.b ? "true" : "false", arg.b ? 4: 5);break; + case CWEB_VARG_TYPE_STR : append_to_output(out, arg.str.ptr, arg.str.len); break; + case CWEB_VARG_TYPE_HASH : append_to_output(out, arg.hash.data, strlen(arg.hash.data)); break; + case CWEB_VARG_TYPE_PC : append_to_output_ptr(out, arg.pc); break; + case CWEB_VARG_TYPE_PS : append_to_output_ptr(out, arg.ps); break; + case CWEB_VARG_TYPE_PI : append_to_output_ptr(out, arg.pi); break; + case CWEB_VARG_TYPE_PL : append_to_output_ptr(out, arg.pl); break; + case CWEB_VARG_TYPE_PLL : append_to_output_ptr(out, arg.pll); break; + case CWEB_VARG_TYPE_PSC : append_to_output_ptr(out, arg.psc); break; + case CWEB_VARG_TYPE_PSS : append_to_output_ptr(out, arg.pss); break; + case CWEB_VARG_TYPE_PSI : append_to_output_ptr(out, arg.psi); break; + case CWEB_VARG_TYPE_PSL : append_to_output_ptr(out, arg.psl); break; + case CWEB_VARG_TYPE_PSLL : append_to_output_ptr(out, arg.psll); break; + case CWEB_VARG_TYPE_PUC : append_to_output_ptr(out, arg.puc); break; + case CWEB_VARG_TYPE_PUS : append_to_output_ptr(out, arg.pus); break; + case CWEB_VARG_TYPE_PUI : append_to_output_ptr(out, arg.pui); break; + case CWEB_VARG_TYPE_PUL : append_to_output_ptr(out, arg.pul); break; + case CWEB_VARG_TYPE_PULL : append_to_output_ptr(out, arg.pull); break; + case CWEB_VARG_TYPE_PF : append_to_output_ptr(out, arg.pf); break; + case CWEB_VARG_TYPE_PD : append_to_output_ptr(out, arg.pd); break; + case CWEB_VARG_TYPE_PB : append_to_output_ptr(out, arg.pb); break; + case CWEB_VARG_TYPE_PSTR : append_to_output_ptr(out, arg.pstr); break; + case CWEB_VARG_TYPE_PHASH: append_to_output_ptr(out, arg.phash); break; + } +} + +static void evaluate_format(StaticOutputBuffer *out, CWEB_String format, CWEB_VArgs args) +{ + char *src = format.ptr; + int len = format.len; + int cur = 0; + int arg_idx = 0; + + for (;;) { + + int off = cur; + while (cur < len && src[cur] != '{' && src[cur] != '\\') + cur++; + + if (cur > off) + append_to_output(out, src + off, cur - off); + + if (cur == len) + break; + cur++; + + if (src[cur-1] == '{') { + + while (cur < len && src[cur] != '}') + cur++; + + if (cur < len) { + assert(src[cur] == '}'); + cur++; + } + + if (arg_idx < args.len) { + value_to_output(out, args.ptr[arg_idx]); + arg_idx++; + } + + } else { + assert(src[cur-1] == '\\'); + if (cur < len) { + append_to_output(out, &src[cur], 1); + cur++; + } + } + } +} + +static CWEB_String evaluate_format_for_request_impl(CWEB_Request *req, CWEB_String format, CWEB_VArgs args) +{ + StaticOutputBuffer out = { + .dst = req->arena.ptr + req->arena.cur, + .cap = req->arena.len - req->arena.cur, + .len = 0 + }; + evaluate_format(&out, format, args); + if (out.len > req->arena.len - req->arena.cur) + return (CWEB_String) { NULL, 0 }; + req->arena.cur += out.len; + return (CWEB_String) { out.dst, out.len }; +} +#define evaluate_format_for_request(req, format, ...) evaluate_format_for_request_impl((req), (format), CWEB_VARGS(__VA_ARGS__)) + +void cweb_respond_redirect_impl(CWEB_Request *req, CWEB_String target_format, CWEB_VArgs args) +{ + CWEB_String target = evaluate_format_for_request_impl(req, target_format, args); + if (target.len == 0) { + http_response_builder_status(req->builder, 500); + http_response_builder_done(req->builder); + return; + } + + CWEB_String location_header = evaluate_format_for_request(req, CWEB_STR("Location: {}"), target); + if (location_header.len == 0) { + http_response_builder_status(req->builder, 500); + http_response_builder_done(req->builder); + return; } http_response_builder_status(req->builder, 303); @@ -13875,7 +14017,7 @@ void cweb_respond_redirect(CWEB_Request *req, CWEB_String target) http_response_builder_done(req->builder); return; } - http_response_builder_header(req->builder, (HTTP_String) { location, ret }); + http_response_builder_header(req->builder, (HTTP_String) { location_header.ptr, location_header.len }); http_response_builder_done(req->builder); } diff --git a/cweb.h b/cweb.h index e25a7a0..84710cd 100644 --- a/cweb.h +++ b/cweb.h @@ -7,7 +7,6 @@ // src/main.h //////////////////////////////////////////////////////////////////////////////////////// -#line 1 "src/main.h" #include #include @@ -18,6 +17,10 @@ typedef struct { int len; } CWEB_String; +typedef struct { + char data[61]; +} CWEB_PasswordHash; + CWEB_String cweb_trim(CWEB_String s); bool cweb_streq(CWEB_String a, CWEB_String b); @@ -41,6 +44,7 @@ typedef enum { CWEB_VARG_TYPE_D, CWEB_VARG_TYPE_B, CWEB_VARG_TYPE_STR, + CWEB_VARG_TYPE_HASH, CWEB_VARG_TYPE_PC, CWEB_VARG_TYPE_PS, CWEB_VARG_TYPE_PI, @@ -60,6 +64,7 @@ typedef enum { CWEB_VARG_TYPE_PD, CWEB_VARG_TYPE_PB, CWEB_VARG_TYPE_PSTR, + CWEB_VARG_TYPE_PHASH, } CWEB_VArgType; typedef struct { @@ -84,6 +89,7 @@ typedef struct { double d; bool b; CWEB_String str; + CWEB_PasswordHash hash; char *pc; short *ps; int *pi; @@ -103,9 +109,15 @@ typedef struct { double *pd; bool *pb; CWEB_String *pstr; + CWEB_PasswordHash *phash; }; } CWEB_VArg; +typedef struct { + int len; + CWEB_VArg *ptr; +} CWEB_VArgs; + CWEB_VArg cweb_varg_from_c (char c); CWEB_VArg cweb_varg_from_s (short s); CWEB_VArg cweb_varg_from_i (int i); @@ -125,6 +137,7 @@ CWEB_VArg cweb_varg_from_f (float f); CWEB_VArg cweb_varg_from_d (double d); CWEB_VArg cweb_varg_from_b (bool b); CWEB_VArg cweb_varg_from_str (CWEB_String str); +CWEB_VArg cweb_varg_from_hash (CWEB_PasswordHash hash); CWEB_VArg cweb_varg_from_pc (char *pc); CWEB_VArg cweb_varg_from_ps (short *ps); CWEB_VArg cweb_varg_from_pi (int *pi); @@ -144,8 +157,9 @@ CWEB_VArg cweb_varg_from_pf (float *pf); CWEB_VArg cweb_varg_from_pd (double *pd); CWEB_VArg cweb_varg_from_pb (bool *pb); CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); +CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash); -#define CWEB_VARG(X) (_Generic((X), \ +#define __CWEB_HELPER_ARG(X) (_Generic((X), \ char : cweb_varg_from_c, \ short : cweb_varg_from_s, \ int : cweb_varg_from_i, \ @@ -165,6 +179,7 @@ CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); double : cweb_varg_from_d, \ bool : cweb_varg_from_b, \ CWEB_String : cweb_varg_from_str, \ + CWEB_PasswordHash : cweb_varg_from_hash, \ char* : cweb_varg_from_pc, \ short* : cweb_varg_from_ps, \ int* : cweb_varg_from_pi, \ @@ -183,21 +198,26 @@ CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); float* : cweb_varg_from_pf, \ double* : cweb_varg_from_pd, \ bool* : cweb_varg_from_pb, \ - CWEB_String* : cweb_varg_from_pstr \ + CWEB_String* : cweb_varg_from_pstr, \ + CWEB_PasswordHash* : cweb_varg_from_phash \ ))(X) -typedef struct { - int len; - CWEB_VArg *ptr; -} CWEB_VArgs; +// Helper macros +#define __CWEB_HELPER_DISPATCH_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N +#define __CWEB_HELPER_CONCAT_0(A, B) A ## B +#define __CWEB_HELPER_CONCAT_1(A, B) __CWEB_HELPER_CONCAT_0(A, B) +#define __CWEB_HELPER_ARGS_0() (CWEB_VArgs) { 0, (CWEB_VArg[]) {}} +#define __CWEB_HELPER_ARGS_1(a) (CWEB_VArgs) { 1, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a) }} +#define __CWEB_HELPER_ARGS_2(a, b) (CWEB_VArgs) { 2, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b) }} +#define __CWEB_HELPER_ARGS_3(a, b, c) (CWEB_VArgs) { 3, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c) }} +#define __CWEB_HELPER_ARGS_4(a, b, c, d) (CWEB_VArgs) { 4, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d) }} +#define __CWEB_HELPER_ARGS_5(a, b, c, d, e) (CWEB_VArgs) { 5, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e) }} +#define __CWEB_HELPER_ARGS_6(a, b, c, d, e, f) (CWEB_VArgs) { 6, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f) }} +#define __CWEB_HELPER_ARGS_7(a, b, c, d, e, f, g) (CWEB_VArgs) { 7, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g) }} +#define __CWEB_HELPER_ARGS_8(a, b, c, d, e, f, g, h) (CWEB_VArgs) { 8, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g), __CWEB_HELPER_ARG(h) }} +#define __CWEB_COUNT_ARGS(...) __CWEB_HELPER_DISPATCH_N(DUMMY, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#define CWEB_VARGS_1(a) (CWEB_VArgs) {1, (CWEB_VArg[]) { CWEB_VARG(a) } } -#define CWEB_VARGS_2(a, b) (CWEB_VArgs) {2, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b) } } -#define CWEB_VARGS_3(a, b, c) (CWEB_VArgs) {3, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c) } } -#define CWEB_VARGS_4(a, b, c, d) (CWEB_VArgs) {4, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c), CWEB_VARG(d) } } -#define CWEB_VARGS_5(a, b, c, d, e) (CWEB_VArgs) {5, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c), CWEB_VARG(d), CWEB_VARG(e) } } -#define CWEB_DISPATCH__(_1, _2, _3, _4, _5, NAME, ...) NAME -#define CWEB_VARGS(...) CWEB_DISPATCH__(__VA_ARGS__, CWEB_VARGS_5, CWEB_VARGS_4, CWEB_VARGS_3, CWEB_VARGS_2, CWEB_VARGS_1)(__VA_ARGS__) +#define CWEB_VARGS(...) __CWEB_HELPER_CONCAT_1(__CWEB_HELPER_ARGS_, __CWEB_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) typedef struct CWEB CWEB; typedef struct CWEB_Request CWEB_Request; @@ -222,6 +242,7 @@ int cweb_set_session_user_id(CWEB_Request *req, int user_id); ////////////////////////////////////// // Request +bool cweb_match_endpoint(CWEB_Request *req, CWEB_String str); CWEB_String cweb_get_param_s(CWEB_Request *req, CWEB_String name); int cweb_get_param_i(CWEB_Request *req, CWEB_String name); @@ -229,9 +250,11 @@ int cweb_get_param_i(CWEB_Request *req, CWEB_String name); // Response void cweb_respond_basic(CWEB_Request *req, int status, CWEB_String content); -void cweb_respond_redirect(CWEB_Request *req, CWEB_String target); +void cweb_respond_redirect_impl(CWEB_Request *req, CWEB_String target_format, CWEB_VArgs args); void cweb_respond_template(CWEB_Request *req, int status, CWEB_String template_file, int resource_id); +#define cweb_respond_redirect(req, format, ...) cweb_respond_redirect_impl((req), (format), CWEB_VARGS(__VA_ARGS__)) + ////////////////////////////////////// // Database @@ -251,10 +274,6 @@ void cweb_free_query_result(CWEB_QueryResult *res); ////////////////////////////////////// // Password -typedef struct { - char data[61]; -} CWEB_PasswordHash; - int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *hash); int cweb_check_password(char *pass, int passlen, CWEB_PasswordHash hash); #endif // CWEB_AMALGAMATION diff --git a/demo/main.c b/demo/main.c index 3383b73..4dd2f63 100644 --- a/demo/main.c +++ b/demo/main.c @@ -125,7 +125,7 @@ static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass) return 0; } - ret = check_password(pass.ptr, pass.len, hash); + ret = cweb_check_password(pass.ptr, pass.len, hash); if (ret < 0) { cweb_free_query_result(&res); return -500; @@ -140,7 +140,7 @@ static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass) static void endpoint_api_login(CWEB *cweb, CWEB_Request *req) { - if (cweb_get_user_id(req) != -1) { + if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } @@ -157,7 +157,7 @@ static void endpoint_api_login(CWEB *cweb, CWEB_Request *req) cweb_respond_basic(req, 200, CWEB_STR("
Invalid credentials
")); return; } - if (cweb_set_user_id(req, ret) < 0) { + if (cweb_set_session_user_id(req, ret) < 0) { // TODO } @@ -166,7 +166,7 @@ static void endpoint_api_login(CWEB *cweb, CWEB_Request *req) static void endpoint_api_signup(CWEB *cweb, CWEB_Request *req) { - if (cweb_get_user_id(req) != -1) { + if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } @@ -199,7 +199,7 @@ static void endpoint_api_signup(CWEB *cweb, CWEB_Request *req) cweb_respond_basic(req, 400, CWEB_STR("
Internal error
")); return; } - cweb_set_user_id(req, insert_id); + cweb_set_session_user_id(req, insert_id); cweb_respond_redirect(req, CWEB_STR("/index")); } @@ -259,7 +259,7 @@ static void endpoint_api_post(CWEB *cweb, CWEB_Request *req) } int post_id = (int) insert_id; - cweb_respond_redirect(req, "/post?id=%d", post_id); + cweb_respond_redirect(req, CWEB_STR("/post?id={}"), post_id); } static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req) @@ -294,7 +294,7 @@ static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req) return; } - cweb_respond_redirect(req, "/post?id=%d", parent_post); + cweb_respond_redirect(req, CWEB_STR("/post?id={}"), parent_post); } static void endpoint_index(CWEB *cweb, CWEB_Request *req) diff --git a/src/main.c b/src/main.c index 172b5b4..a7c6322 100644 --- a/src/main.c +++ b/src/main.c @@ -111,7 +111,8 @@ CWEB_VArg cweb_varg_from_ull (long long ull) { return (CWEB_VArg) { CWEB_VA CWEB_VArg cweb_varg_from_f (float f) { return (CWEB_VArg) { CWEB_VARG_TYPE_F, .f=f }; } CWEB_VArg cweb_varg_from_d (double d) { return (CWEB_VArg) { CWEB_VARG_TYPE_D, .d=d }; } CWEB_VArg cweb_varg_from_b (bool b) { return (CWEB_VArg) { CWEB_VARG_TYPE_B, .b=b }; } -CWEB_VArg cweb_varg_from_str (HTTP_String str) { return (CWEB_VArg) { CWEB_VARG_TYPE_STR, .str=str }; } +CWEB_VArg cweb_varg_from_str (CWEB_String str) { return (CWEB_VArg) { CWEB_VARG_TYPE_STR, .str=str }; } +CWEB_VArg cweb_varg_from_hash (CWEB_PasswordHash hash) { return (CWEB_VArg) { CWEB_VARG_TYPE_HASH, .hash=hash }; } CWEB_VArg cweb_varg_from_pc (char *pc) { return (CWEB_VArg) { CWEB_VARG_TYPE_PC, .pc=pc }; } CWEB_VArg cweb_varg_from_ps (short *ps) { return (CWEB_VArg) { CWEB_VARG_TYPE_PS, .ps=ps }; } CWEB_VArg cweb_varg_from_pi (int *pi) { return (CWEB_VArg) { CWEB_VARG_TYPE_PI, .pi=pi }; } @@ -130,8 +131,8 @@ CWEB_VArg cweb_varg_from_pull (long long *pull) { return (CWEB_VArg) { CWEB_VA CWEB_VArg cweb_varg_from_pf (float *pf) { return (CWEB_VArg) { CWEB_VARG_TYPE_PF, .pf=pf }; } CWEB_VArg cweb_varg_from_pd (double *pd) { return (CWEB_VArg) { CWEB_VARG_TYPE_PD, .pd=pd }; } CWEB_VArg cweb_varg_from_pb (bool *pb) { return (CWEB_VArg) { CWEB_VARG_TYPE_PB, .pb=pb }; } -CWEB_VArg cweb_varg_from_pstr (HTTP_String *pstr) { return (CWEB_VArg) { CWEB_VARG_TYPE_PSTR, .pstr=pstr }; } - +CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr) { return (CWEB_VArg) { CWEB_VARG_TYPE_PSTR, .pstr=pstr }; } +CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash) { return (CWEB_VArg) { CWEB_VARG_TYPE_PHASH, .phash=phash }; } ///////////////////////////////////////////////////////////////// // RANDOM @@ -876,12 +877,155 @@ void cweb_respond(CWEB_Request *req, int status, CWEB_String content) http_response_builder_done(req->builder); } -void cweb_respond_redirect(CWEB_Request *req, CWEB_String target) +typedef struct { + char *dst; + int cap; + int len; +} StaticOutputBuffer; + +static void append_to_output(StaticOutputBuffer *out, char *src, int len) { - char location[128]; - int ret = snprintf(location, sizeof(location), "Location: %.*s", target.len, target.ptr); - if (ret < 0 || ret >= SIZEOF(location)) { - // TODO + int unused = out->cap - out->len; + if (unused > 0) + memcpy(out->dst + out->len, src, MIN(len, unused)); + out->len += len; +} + +static void append_to_output_u64(StaticOutputBuffer *out, uint64_t n) +{ + // TODO +} + +static void append_to_output_s64(StaticOutputBuffer *out, int64_t n) +{ + // TODO +} + +static void append_to_output_ptr(StaticOutputBuffer *out, void *p) +{ + // TODO +} + +static void value_to_output(StaticOutputBuffer *out, CWEB_VArg arg) +{ + switch (arg.type) { + case CWEB_VARG_TYPE_C : append_to_output(out, &arg.c, 1); break; + case CWEB_VARG_TYPE_S : append_to_output_s64(out, arg.s); break; + case CWEB_VARG_TYPE_I : append_to_output_s64(out, arg.i); break; + case CWEB_VARG_TYPE_L : append_to_output_s64(out, arg.l); break; + case CWEB_VARG_TYPE_LL : append_to_output_s64(out, arg.ll); break; + case CWEB_VARG_TYPE_SC : append_to_output_s64(out, arg.sc); break; + case CWEB_VARG_TYPE_SS : append_to_output_s64(out, arg.ss); break; + case CWEB_VARG_TYPE_SI : append_to_output_s64(out, arg.si); break; + case CWEB_VARG_TYPE_SL : append_to_output_s64(out, arg.sl); break; + case CWEB_VARG_TYPE_SLL : append_to_output_s64(out, arg.sll); break; + case CWEB_VARG_TYPE_UC : append_to_output_u64(out, arg.uc); break; + case CWEB_VARG_TYPE_US : append_to_output_u64(out, arg.us); break; + case CWEB_VARG_TYPE_UI : append_to_output_u64(out, arg.ui); break; + case CWEB_VARG_TYPE_UL : append_to_output_u64(out, arg.ul); break; + case CWEB_VARG_TYPE_ULL : append_to_output_u64(out, arg.ull); break; + case CWEB_VARG_TYPE_F : append_to_output_f64(out, arg.f); break; + case CWEB_VARG_TYPE_D : append_to_output_u64(out, arg.d); break; + case CWEB_VARG_TYPE_B : append_to_output(out, arg.b ? "true" : "false", arg.b ? 4: 5);break; + case CWEB_VARG_TYPE_STR : append_to_output(out, arg.str.ptr, arg.str.len); break; + case CWEB_VARG_TYPE_HASH : append_to_output(out, arg.hash.data, strlen(arg.hash.data)); break; + case CWEB_VARG_TYPE_PC : append_to_output_ptr(out, arg.pc); break; + case CWEB_VARG_TYPE_PS : append_to_output_ptr(out, arg.ps); break; + case CWEB_VARG_TYPE_PI : append_to_output_ptr(out, arg.pi); break; + case CWEB_VARG_TYPE_PL : append_to_output_ptr(out, arg.pl); break; + case CWEB_VARG_TYPE_PLL : append_to_output_ptr(out, arg.pll); break; + case CWEB_VARG_TYPE_PSC : append_to_output_ptr(out, arg.psc); break; + case CWEB_VARG_TYPE_PSS : append_to_output_ptr(out, arg.pss); break; + case CWEB_VARG_TYPE_PSI : append_to_output_ptr(out, arg.psi); break; + case CWEB_VARG_TYPE_PSL : append_to_output_ptr(out, arg.psl); break; + case CWEB_VARG_TYPE_PSLL : append_to_output_ptr(out, arg.psll); break; + case CWEB_VARG_TYPE_PUC : append_to_output_ptr(out, arg.puc); break; + case CWEB_VARG_TYPE_PUS : append_to_output_ptr(out, arg.pus); break; + case CWEB_VARG_TYPE_PUI : append_to_output_ptr(out, arg.pui); break; + case CWEB_VARG_TYPE_PUL : append_to_output_ptr(out, arg.pul); break; + case CWEB_VARG_TYPE_PULL : append_to_output_ptr(out, arg.pull); break; + case CWEB_VARG_TYPE_PF : append_to_output_ptr(out, arg.pf); break; + case CWEB_VARG_TYPE_PD : append_to_output_ptr(out, arg.pd); break; + case CWEB_VARG_TYPE_PB : append_to_output_ptr(out, arg.pb); break; + case CWEB_VARG_TYPE_PSTR : append_to_output_ptr(out, arg.pstr); break; + case CWEB_VARG_TYPE_PHASH: append_to_output_ptr(out, arg.phash); break; + } +} + +static void evaluate_format(StaticOutputBuffer *out, CWEB_String format, CWEB_VArgs args) +{ + char *src = format.ptr; + int len = format.len; + int cur = 0; + int arg_idx = 0; + + for (;;) { + + int off = cur; + while (cur < len && src[cur] != '{' && src[cur] != '\\') + cur++; + + if (cur > off) + append_to_output(out, src + off, cur - off); + + if (cur == len) + break; + cur++; + + if (src[cur-1] == '{') { + + while (cur < len && src[cur] != '}') + cur++; + + if (cur < len) { + assert(src[cur] == '}'); + cur++; + } + + if (arg_idx < args.len) { + value_to_output(out, args.ptr[arg_idx]); + arg_idx++; + } + + } else { + assert(src[cur-1] == '\\'); + if (cur < len) { + append_to_output(out, &src[cur], 1); + cur++; + } + } + } +} + +static CWEB_String evaluate_format_for_request_impl(CWEB_Request *req, CWEB_String format, CWEB_VArgs args) +{ + StaticOutputBuffer out = { + .dst = req->arena.ptr + req->arena.cur, + .cap = req->arena.len - req->arena.cur, + .len = 0 + }; + evaluate_format(&out, format, args); + if (out.len > req->arena.len - req->arena.cur) + return (CWEB_String) { NULL, 0 }; + req->arena.cur += out.len; + return (CWEB_String) { out.dst, out.len }; +} +#define evaluate_format_for_request(req, format, ...) evaluate_format_for_request_impl((req), (format), CWEB_VARGS(__VA_ARGS__)) + +void cweb_respond_redirect_impl(CWEB_Request *req, CWEB_String target_format, CWEB_VArgs args) +{ + CWEB_String target = evaluate_format_for_request_impl(req, target_format, args); + if (target.len == 0) { + http_response_builder_status(req->builder, 500); + http_response_builder_done(req->builder); + return; + } + + CWEB_String location_header = evaluate_format_for_request(req, CWEB_STR("Location: {}"), target); + if (location_header.len == 0) { + http_response_builder_status(req->builder, 500); + http_response_builder_done(req->builder); + return; } http_response_builder_status(req->builder, 303); @@ -892,7 +1036,7 @@ void cweb_respond_redirect(CWEB_Request *req, CWEB_String target) http_response_builder_done(req->builder); return; } - http_response_builder_header(req->builder, (HTTP_String) { location, ret }); + http_response_builder_header(req->builder, (HTTP_String) { location_header.ptr, location_header.len }); http_response_builder_done(req->builder); } diff --git a/src/main.h b/src/main.h index 46c4d31..dcb9673 100644 --- a/src/main.h +++ b/src/main.h @@ -8,6 +8,10 @@ typedef struct { int len; } CWEB_String; +typedef struct { + char data[61]; +} CWEB_PasswordHash; + CWEB_String cweb_trim(CWEB_String s); bool cweb_streq(CWEB_String a, CWEB_String b); @@ -31,6 +35,7 @@ typedef enum { CWEB_VARG_TYPE_D, CWEB_VARG_TYPE_B, CWEB_VARG_TYPE_STR, + CWEB_VARG_TYPE_HASH, CWEB_VARG_TYPE_PC, CWEB_VARG_TYPE_PS, CWEB_VARG_TYPE_PI, @@ -50,6 +55,7 @@ typedef enum { CWEB_VARG_TYPE_PD, CWEB_VARG_TYPE_PB, CWEB_VARG_TYPE_PSTR, + CWEB_VARG_TYPE_PHASH, } CWEB_VArgType; typedef struct { @@ -74,6 +80,7 @@ typedef struct { double d; bool b; CWEB_String str; + CWEB_PasswordHash hash; char *pc; short *ps; int *pi; @@ -93,9 +100,15 @@ typedef struct { double *pd; bool *pb; CWEB_String *pstr; + CWEB_PasswordHash *phash; }; } CWEB_VArg; +typedef struct { + int len; + CWEB_VArg *ptr; +} CWEB_VArgs; + CWEB_VArg cweb_varg_from_c (char c); CWEB_VArg cweb_varg_from_s (short s); CWEB_VArg cweb_varg_from_i (int i); @@ -115,6 +128,7 @@ CWEB_VArg cweb_varg_from_f (float f); CWEB_VArg cweb_varg_from_d (double d); CWEB_VArg cweb_varg_from_b (bool b); CWEB_VArg cweb_varg_from_str (CWEB_String str); +CWEB_VArg cweb_varg_from_hash (CWEB_PasswordHash hash); CWEB_VArg cweb_varg_from_pc (char *pc); CWEB_VArg cweb_varg_from_ps (short *ps); CWEB_VArg cweb_varg_from_pi (int *pi); @@ -134,8 +148,9 @@ CWEB_VArg cweb_varg_from_pf (float *pf); CWEB_VArg cweb_varg_from_pd (double *pd); CWEB_VArg cweb_varg_from_pb (bool *pb); CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); +CWEB_VArg cweb_varg_from_phash(CWEB_PasswordHash *phash); -#define CWEB_VARG(X) (_Generic((X), \ +#define __CWEB_HELPER_ARG(X) (_Generic((X), \ char : cweb_varg_from_c, \ short : cweb_varg_from_s, \ int : cweb_varg_from_i, \ @@ -155,6 +170,7 @@ CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); double : cweb_varg_from_d, \ bool : cweb_varg_from_b, \ CWEB_String : cweb_varg_from_str, \ + CWEB_PasswordHash : cweb_varg_from_hash, \ char* : cweb_varg_from_pc, \ short* : cweb_varg_from_ps, \ int* : cweb_varg_from_pi, \ @@ -173,21 +189,26 @@ CWEB_VArg cweb_varg_from_pstr (CWEB_String *pstr); float* : cweb_varg_from_pf, \ double* : cweb_varg_from_pd, \ bool* : cweb_varg_from_pb, \ - CWEB_String* : cweb_varg_from_pstr \ + CWEB_String* : cweb_varg_from_pstr, \ + CWEB_PasswordHash* : cweb_varg_from_phash \ ))(X) -typedef struct { - int len; - CWEB_VArg *ptr; -} CWEB_VArgs; +// Helper macros +#define __CWEB_HELPER_DISPATCH_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N +#define __CWEB_HELPER_CONCAT_0(A, B) A ## B +#define __CWEB_HELPER_CONCAT_1(A, B) __CWEB_HELPER_CONCAT_0(A, B) +#define __CWEB_HELPER_ARGS_0() (CWEB_VArgs) { 0, (CWEB_VArg[]) {}} +#define __CWEB_HELPER_ARGS_1(a) (CWEB_VArgs) { 1, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a) }} +#define __CWEB_HELPER_ARGS_2(a, b) (CWEB_VArgs) { 2, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b) }} +#define __CWEB_HELPER_ARGS_3(a, b, c) (CWEB_VArgs) { 3, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c) }} +#define __CWEB_HELPER_ARGS_4(a, b, c, d) (CWEB_VArgs) { 4, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d) }} +#define __CWEB_HELPER_ARGS_5(a, b, c, d, e) (CWEB_VArgs) { 5, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e) }} +#define __CWEB_HELPER_ARGS_6(a, b, c, d, e, f) (CWEB_VArgs) { 6, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f) }} +#define __CWEB_HELPER_ARGS_7(a, b, c, d, e, f, g) (CWEB_VArgs) { 7, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g) }} +#define __CWEB_HELPER_ARGS_8(a, b, c, d, e, f, g, h) (CWEB_VArgs) { 8, (CWEB_VArg[]) { __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g), __CWEB_HELPER_ARG(h) }} +#define __CWEB_COUNT_ARGS(...) __CWEB_HELPER_DISPATCH_N(DUMMY, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#define CWEB_VARGS_1(a) (CWEB_VArgs) {1, (CWEB_VArg[]) { CWEB_VARG(a) } } -#define CWEB_VARGS_2(a, b) (CWEB_VArgs) {2, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b) } } -#define CWEB_VARGS_3(a, b, c) (CWEB_VArgs) {3, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c) } } -#define CWEB_VARGS_4(a, b, c, d) (CWEB_VArgs) {4, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c), CWEB_VARG(d) } } -#define CWEB_VARGS_5(a, b, c, d, e) (CWEB_VArgs) {5, (CWEB_VArg[]) { CWEB_VARG(a), CWEB_VARG(b), CWEB_VARG(c), CWEB_VARG(d), CWEB_VARG(e) } } -#define CWEB_DISPATCH__(_1, _2, _3, _4, _5, NAME, ...) NAME -#define CWEB_VARGS(...) CWEB_DISPATCH__(__VA_ARGS__, CWEB_VARGS_5, CWEB_VARGS_4, CWEB_VARGS_3, CWEB_VARGS_2, CWEB_VARGS_1)(__VA_ARGS__) +#define CWEB_VARGS(...) __CWEB_HELPER_CONCAT_1(__CWEB_HELPER_ARGS_, __CWEB_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) typedef struct CWEB CWEB; typedef struct CWEB_Request CWEB_Request; @@ -220,9 +241,11 @@ int cweb_get_param_i(CWEB_Request *req, CWEB_String name); // Response void cweb_respond_basic(CWEB_Request *req, int status, CWEB_String content); -void cweb_respond_redirect(CWEB_Request *req, CWEB_String target); +void cweb_respond_redirect_impl(CWEB_Request *req, CWEB_String target_format, CWEB_VArgs args); void cweb_respond_template(CWEB_Request *req, int status, CWEB_String template_file, int resource_id); +#define cweb_respond_redirect(req, format, ...) cweb_respond_redirect_impl((req), (format), CWEB_VARGS(__VA_ARGS__)) + ////////////////////////////////////// // Database @@ -242,9 +265,5 @@ void cweb_free_query_result(CWEB_QueryResult *res); ////////////////////////////////////// // Password -typedef struct { - char data[61]; -} CWEB_PasswordHash; - int cweb_hash_password(char *pass, int passlen, int cost, CWEB_PasswordHash *hash); int cweb_check_password(char *pass, int passlen, CWEB_PasswordHash hash); diff --git a/test.c b/test.c new file mode 100644 index 0000000..f95ae1c --- /dev/null +++ b/test.c @@ -0,0 +1,22 @@ +// Helper macros +#define __CWEB_HELPER_DISPATCH_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N +#define __CWEB_HELPER_CONCAT_0(A, B) A ## B +#define __CWEB_HELPER_CONCAT_1(A, B) __CWEB_HELPER_CONCAT_0(A, B) +#define __CWEB_HELPER_ARG(a) (a) +#define __CWEB_HELPER_ARGS_0() (CWEB_VArgs) { 0 } +#define __CWEB_HELPER_ARGS_1(a) (CWEB_VArgs) { 1, __CWEB_HELPER_ARG(a) } +#define __CWEB_HELPER_ARGS_2(a, b) (CWEB_VArgs) { 2, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b) } +#define __CWEB_HELPER_ARGS_3(a, b, c) (CWEB_VArgs) { 3, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c) } +#define __CWEB_HELPER_ARGS_4(a, b, c, d) (CWEB_VArgs) { 4, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d) } +#define __CWEB_HELPER_ARGS_5(a, b, c, d, e) (CWEB_VArgs) { 5, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e) } +#define __CWEB_HELPER_ARGS_6(a, b, c, d, e, f) (CWEB_VArgs) { 6, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f) } +#define __CWEB_HELPER_ARGS_7(a, b, c, d, e, f, g) (CWEB_VArgs) { 7, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g) } +#define __CWEB_HELPER_ARGS_8(a, b, c, d, e, f, g, h) (CWEB_VArgs) { 8, __CWEB_HELPER_ARG(a), __CWEB_HELPER_ARG(b), __CWEB_HELPER_ARG(c), __CWEB_HELPER_ARG(d), __CWEB_HELPER_ARG(e), __CWEB_HELPER_ARG(f), __CWEB_HELPER_ARG(g), __CWEB_HELPER_ARG(h) } +#define __CWEB_COUNT_ARGS(...) __CWEB_HELPER_DISPATCH_N(DUMMY, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define CWEB_ARGS(...) __CWEB_HELPER_CONCAT_1(__CWEB_HELPER_ARGS_, __CWEB_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) + +CWEB_ARGS() +CWEB_ARGS(1) +CWEB_ARGS(1, 2) +CWEB_ARGS(1, 2, 3)