annotate src/c/urweb.c @ 759:67cd8326f743

subforms working
author Adam Chlipala <adamc@hcoop.net>
date Thu, 30 Apr 2009 13:47:46 -0400
parents 8ce31c052dce
children 21f6d2e65685
rev   line source
adamc@741 1 #define _XOPEN_SOURCE 500
adamc@436 2
adamc@117 3 #include <stdlib.h>
adamc@102 4 #include <stdio.h>
adamc@117 5 #include <string.h>
adamc@457 6 #include <strings.h>
adamc@106 7 #include <ctype.h>
adamc@167 8 #include <setjmp.h>
adamc@167 9 #include <stdarg.h>
adamc@682 10 #include <assert.h>
adamc@741 11 #include <ctype.h>
adamc@102 12
adamc@667 13 #include <pthread.h>
adamc@667 14
adamc@102 15 #include "types.h"
adamc@102 16
adamc@311 17 uw_unit uw_unit_v = {};
adamc@102 18
adamc@667 19
adamc@667 20 // Socket extras
adamc@667 21
adamc@667 22 int uw_really_send(int sock, const void *buf, size_t len) {
adamc@667 23 while (len > 0) {
adamc@667 24 size_t n = send(sock, buf, len, 0);
adamc@667 25
adamc@667 26 if (n < 0)
adamc@667 27 return n;
adamc@667 28
adamc@667 29 buf += n;
adamc@667 30 len -= n;
adamc@667 31 }
adamc@667 32
adamc@667 33 return 0;
adamc@667 34 }
adamc@667 35
adamc@667 36
adamc@667 37 // Buffers
adamc@667 38
adamc@667 39 typedef struct {
adamc@667 40 char *start, *front, *back;
adamc@667 41 } buf;
adamc@667 42
adamc@667 43 static void buf_init(buf *b, size_t s) {
adamc@667 44 b->front = b->start = malloc(s);
adamc@667 45 b->back = b->front + s;
adamc@667 46 }
adamc@667 47
adamc@667 48 static void buf_free(buf *b) {
adamc@667 49 free(b->start);
adamc@667 50 }
adamc@667 51
adamc@667 52 static void buf_reset(buf *b) {
adamc@667 53 b->front = b->start;
adamc@667 54 }
adamc@667 55
adamc@667 56 static void buf_check(buf *b, size_t extra) {
adamc@667 57 if (b->back - b->front < extra) {
adamc@667 58 size_t desired = b->front - b->start + extra, next;
adamc@667 59 char *new_heap;
adamc@667 60
adamc@667 61 next = b->back - b->start;
adamc@667 62 if (next == 0)
adamc@667 63 next = 1;
adamc@667 64 for (; next < desired; next *= 2);
adamc@667 65
adamc@667 66 new_heap = realloc(b->start, next);
adamc@667 67 b->front = new_heap + (b->front - b->start);
adamc@667 68 b->back = new_heap + next;
adamc@667 69 b->start = new_heap;
adamc@667 70 }
adamc@667 71 }
adamc@667 72
adamc@667 73 static size_t buf_used(buf *b) {
adamc@667 74 return b->front - b->start;
adamc@667 75 }
adamc@667 76
adamc@667 77 static size_t buf_avail(buf *b) {
adamc@667 78 return b->back - b->start;
adamc@667 79 }
adamc@667 80
adamc@668 81 static void buf_append(buf *b, const char *s, size_t len) {
adamc@672 82 buf_check(b, len+1);
adamc@668 83 memcpy(b->front, s, len);
adamc@668 84 b->front += len;
adamc@672 85 *b->front = 0;
adamc@668 86 }
adamc@667 87
adamc@668 88
adamc@672 89 // Persistent state types
adamc@667 90
adamc@667 91 typedef enum { UNUSED, USED } usage;
adamc@667 92
adamc@667 93 typedef struct client {
adamc@682 94 unsigned id;
adamc@667 95 usage mode;
adamc@682 96 int pass;
adamc@682 97 struct client *next;
adamc@689 98 pthread_mutex_t lock, pull_lock;
adamc@682 99 buf msgs;
adamc@682 100 int sock;
adamc@682 101 time_t last_contact;
adamc@682 102 unsigned n_channels;
adamc@685 103 unsigned refcount;
adamc@667 104 } client;
adamc@667 105
adamc@672 106
adamc@672 107 // Persistent client state
adamc@672 108
adamc@682 109 static client **clients, *clients_free, *clients_used;
adamc@682 110 static unsigned n_clients;
adamc@667 111
adamc@667 112 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
adamc@667 113
adamc@682 114 static client *new_client() {
adamc@667 115 client *c;
adamc@667 116
adamc@667 117 pthread_mutex_lock(&clients_mutex);
adamc@667 118
adamc@667 119 if (clients_free) {
adamc@667 120 c = clients_free;
adamc@682 121 clients_free = clients_free->next;
adamc@667 122 }
adamc@667 123 else {
adamc@667 124 ++n_clients;
adamc@667 125 clients = realloc(clients, sizeof(client) * n_clients);
adamc@667 126 c = malloc(sizeof(client));
adamc@667 127 c->id = n_clients-1;
adamc@682 128 pthread_mutex_init(&c->lock, NULL);
adamc@689 129 pthread_mutex_init(&c->pull_lock, NULL);
adamc@682 130 buf_init(&c->msgs, 0);
adamc@667 131 clients[n_clients-1] = c;
adamc@667 132 }
adamc@667 133
adamc@682 134 pthread_mutex_lock(&c->lock);
adamc@667 135 c->mode = USED;
adamc@682 136 c->pass = rand();
adamc@682 137 c->sock = -1;
adamc@682 138 c->last_contact = time(NULL);
adamc@682 139 buf_reset(&c->msgs);
adamc@682 140 c->n_channels = 0;
adamc@685 141 c->refcount = 0;
adamc@682 142 pthread_mutex_unlock(&c->lock);
adamc@682 143
adamc@682 144 c->next = clients_used;
adamc@682 145 clients_used = c;
adamc@667 146
adamc@667 147 pthread_mutex_unlock(&clients_mutex);
adamc@667 148
adamc@667 149 return c;
adamc@667 150 }
adamc@667 151
adamc@685 152 static void use_client(client *c) {
adamc@685 153 pthread_mutex_lock(&c->lock);
adamc@685 154 ++c->refcount;
adamc@685 155 pthread_mutex_unlock(&c->lock);
adamc@689 156 pthread_mutex_lock(&c->pull_lock);
adamc@685 157 }
adamc@685 158
adamc@685 159 static void release_client(client *c) {
adamc@689 160 pthread_mutex_unlock(&c->pull_lock);
adamc@685 161 pthread_mutex_lock(&c->lock);
adamc@685 162 --c->refcount;
adamc@685 163 pthread_mutex_unlock(&c->lock);
adamc@689 164
adamc@685 165 }
adamc@685 166
adamc@667 167 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n";
adamc@667 168
adamc@682 169 static client *find_client(unsigned id) {
adamc@667 170 client *c;
adamc@667 171
adamc@667 172 pthread_mutex_lock(&clients_mutex);
adamc@667 173
adamc@667 174 if (id >= n_clients) {
adamc@667 175 pthread_mutex_unlock(&clients_mutex);
adamc@668 176 return NULL;
adamc@667 177 }
adamc@667 178
adamc@667 179 c = clients[id];
adamc@667 180
adamc@668 181 pthread_mutex_unlock(&clients_mutex);
adamc@668 182 return c;
adamc@668 183 }
adamc@668 184
adamc@682 185 void uw_client_connect(unsigned id, int pass, int sock) {
adamc@682 186 client *c = find_client(id);
adamc@668 187
adamc@668 188 if (c == NULL) {
adamc@667 189 close(sock);
adamc@682 190 fprintf(stderr, "Out-of-bounds client request (%u)\n", id);
adamc@667 191 return;
adamc@667 192 }
adamc@667 193
adamc@682 194 pthread_mutex_lock(&c->lock);
adamc@671 195
adamc@682 196 if (c->mode != USED) {
adamc@682 197 pthread_mutex_unlock(&c->lock);
adamc@667 198 close(sock);
adamc@682 199 fprintf(stderr, "Client request for unused slot (%u)\n", id);
adamc@667 200 return;
adamc@667 201 }
adamc@667 202
adamc@682 203 if (pass != c->pass) {
adamc@682 204 pthread_mutex_unlock(&c->lock);
adamc@682 205 close(sock);
adamc@682 206 fprintf(stderr, "Wrong client password (%u, %d)\n", id, pass);
adamc@682 207 return;
adamc@667 208 }
adamc@667 209
adamc@682 210 if (c->sock != -1) {
adamc@682 211 close(c->sock);
adamc@682 212 c->sock = -1;
adamc@682 213 }
adamc@667 214
adamc@682 215 c->last_contact = time(NULL);
adamc@682 216
adamc@682 217 if (buf_used(&c->msgs) > 0) {
adamc@667 218 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1);
adamc@682 219 uw_really_send(sock, c->msgs.start, buf_used(&c->msgs));
adamc@682 220 buf_reset(&c->msgs);
adamc@667 221 close(sock);
adamc@667 222 }
adamc@668 223 else
adamc@682 224 c->sock = sock;
adamc@667 225
adamc@682 226 pthread_mutex_unlock(&c->lock);
adamc@667 227 }
adamc@667 228
adamc@682 229 static void free_client(client *c) {
adamc@683 230 printf("Freeing client %u\n", c->id);
adamc@667 231
adamc@682 232 c->mode = UNUSED;
adamc@682 233 c->pass = -1;
adamc@667 234
adamc@682 235 c->next = clients_free;
adamc@682 236 clients_free = c;
adamc@667 237 }
adamc@667 238
adamc@682 239 static uw_Basis_channel new_channel(client *c) {
adamc@682 240 uw_Basis_channel ch = {c->id, c->n_channels++};
adamc@668 241 return ch;
adamc@668 242 }
adamc@668 243
adamc@685 244 static void client_send(client *c, buf *msg) {
adamc@685 245 pthread_mutex_lock(&c->lock);
adamc@671 246
adamc@682 247 if (c->sock != -1) {
adamc@682 248 uw_really_send(c->sock, begin_msgs, sizeof(begin_msgs) - 1);
adamc@682 249 uw_really_send(c->sock, msg->start, buf_used(msg));
adamc@682 250 close(c->sock);
adamc@682 251 c->sock = -1;
adamc@682 252 } else
adamc@682 253 buf_append(&c->msgs, msg->start, buf_used(msg));
adamc@671 254
adamc@685 255 pthread_mutex_unlock(&c->lock);
adamc@668 256 }
adamc@668 257
adamc@668 258
adamc@668 259 // Global entry points
adamc@668 260
adamc@668 261 void uw_global_init() {
adamc@668 262 srand(time(NULL) ^ getpid());
adamc@668 263
adamc@668 264 clients = malloc(0);
adamc@668 265 }
adamc@668 266
adamc@668 267
adamc@667 268 // Single-request state
adamc@667 269
adamc@167 270 #define ERROR_BUF_LEN 1024
adamc@167 271
adamc@323 272 typedef struct regions {
adamc@323 273 struct regions *next;
adamc@323 274 } regions;
adamc@323 275
adamc@425 276 typedef struct {
adamc@425 277 void (*func)(void*);
adamc@425 278 void *arg;
adamc@425 279 } cleanup;
adamc@425 280
adamc@671 281 typedef struct {
adamc@682 282 unsigned client;
adamc@671 283 buf msgs;
adamc@682 284 } delta;
adamc@671 285
adamc@737 286 typedef enum {
adamc@759 287 UNSET, NORMAL, FIL, SUBFORM, SUBFORMS, ENTRY
adamc@737 288 } input_kind;
adamc@737 289
adamc@756 290 typedef struct input {
adamc@737 291 input_kind kind;
adamc@737 292 union {
adamc@737 293 char *normal;
adamc@739 294 uw_Basis_file file;
adamc@756 295 struct {
adamc@759 296 struct input *fields, *parent;
adamc@756 297 } subform;
adamc@759 298 struct {
adamc@759 299 struct input *entries, *parent;
adamc@759 300 } subforms;
adamc@759 301 struct {
adamc@759 302 struct input *fields, *next, *parent;
adamc@759 303 } entry;
adamc@737 304 } data;
adamc@737 305 } input;
adamc@737 306
adamc@311 307 struct uw_context {
adamc@458 308 char *headers, *headers_end;
adamc@457 309
adamc@666 310 buf outHeaders, page, heap, script;
adamc@759 311 input *inputs, *subinputs, *cur_container;
adamc@756 312 size_t n_subinputs, used_subinputs;
adamc@167 313
adamc@565 314 int source_count;
adamc@562 315
adamc@272 316 void *db;
adamc@272 317
adamc@167 318 jmp_buf jmp_buf;
adamc@167 319
adamc@323 320 regions *regions;
adamc@323 321
adamc@425 322 cleanup *cleanup, *cleanup_front, *cleanup_back;
adamc@425 323
adamc@667 324 const char *script_header, *url_prefix;
adamc@643 325
adamc@736 326 int needs_push, needs_sig;
adamc@693 327
adamc@682 328 size_t n_deltas, used_deltas;
adamc@682 329 delta *deltas;
adamc@671 330
adamc@673 331 int timeout;
adamc@673 332
adamc@682 333 client *client;
adamc@682 334
adamc@167 335 char error_message[ERROR_BUF_LEN];
adamc@117 336 };
adamc@117 337
adamc@683 338 extern int uw_inputs_len, uw_timeout;
adamc@144 339
adamc@687 340 uw_context uw_init() {
adamc@311 341 uw_context ctx = malloc(sizeof(struct uw_context));
adamc@136 342
adamc@458 343 ctx->headers = ctx->headers_end = NULL;
adamc@457 344
adamc@687 345 buf_init(&ctx->outHeaders, 0);
adamc@687 346 buf_init(&ctx->page, 0);
adamc@687 347 buf_init(&ctx->heap, 0);
adamc@698 348 buf_init(&ctx->script, 1);
adamc@667 349 ctx->script.start[0] = 0;
adamc@136 350
adamc@737 351 ctx->inputs = calloc(uw_inputs_len, sizeof(input));
adamc@759 352 ctx->cur_container = NULL;
adamc@756 353 ctx->subinputs = malloc(0);
adamc@756 354 ctx->n_subinputs = ctx->used_subinputs = 0;
adamc@144 355
adamc@272 356 ctx->db = NULL;
adamc@272 357
adamc@323 358 ctx->regions = NULL;
adamc@323 359
adamc@425 360 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0);
adamc@425 361
adamc@643 362 ctx->script_header = "";
adamc@667 363 ctx->url_prefix = "/";
adamc@693 364 ctx->needs_push = 0;
adamc@736 365 ctx->needs_sig = 0;
adamc@643 366
adamc@167 367 ctx->error_message[0] = 0;
adamc@167 368
adamc@565 369 ctx->source_count = 0;
adamc@562 370
adamc@682 371 ctx->n_deltas = ctx->used_deltas = 0;
adamc@671 372 ctx->deltas = malloc(0);
adamc@671 373
adamc@673 374 ctx->timeout = uw_timeout;
adamc@673 375
adamc@700 376 ctx->client = NULL;
adamc@700 377
adamc@700 378 ctx->error_message[0] = 0;
adamc@700 379
adamc@117 380 return ctx;
adamc@106 381 }
adamc@106 382
adamc@311 383 void uw_set_db(uw_context ctx, void *db) {
adamc@272 384 ctx->db = db;
adamc@272 385 }
adamc@272 386
adamc@311 387 void *uw_get_db(uw_context ctx) {
adamc@272 388 return ctx->db;
adamc@272 389 }
adamc@272 390
adamc@311 391 void uw_free(uw_context ctx) {
adamc@671 392 size_t i;
adamc@671 393
adamc@666 394 buf_free(&ctx->outHeaders);
adamc@666 395 buf_free(&ctx->script);
adamc@666 396 buf_free(&ctx->page);
adamc@666 397 buf_free(&ctx->heap);
adamc@144 398 free(ctx->inputs);
adamc@756 399 free(ctx->subinputs);
adamc@425 400 free(ctx->cleanup);
adamc@671 401
adamc@682 402 for (i = 0; i < ctx->n_deltas; ++i)
adamc@671 403 buf_free(&ctx->deltas[i].msgs);
adamc@671 404
adamc@136 405 free(ctx);
adamc@136 406 }
adamc@136 407
adamc@562 408 void uw_reset_keep_error_message(uw_context ctx) {
adamc@756 409 size_t i;
adamc@756 410
adamc@666 411 buf_reset(&ctx->outHeaders);
adamc@666 412 buf_reset(&ctx->script);
adamc@667 413 ctx->script.start[0] = 0;
adamc@666 414 buf_reset(&ctx->page);
adamc@666 415 buf_reset(&ctx->heap);
adamc@323 416 ctx->regions = NULL;
adamc@425 417 ctx->cleanup_front = ctx->cleanup;
adamc@565 418 ctx->source_count = 0;
adamc@682 419 ctx->used_deltas = 0;
adamc@682 420 ctx->client = NULL;
adamc@759 421 ctx->cur_container = NULL;
adamc@167 422 }
adamc@167 423
adamc@562 424 void uw_reset_keep_request(uw_context ctx) {
adamc@562 425 uw_reset_keep_error_message(ctx);
adamc@562 426 ctx->error_message[0] = 0;
adamc@167 427 }
adamc@167 428
adamc@311 429 void uw_reset(uw_context ctx) {
adamc@311 430 uw_reset_keep_request(ctx);
adamc@737 431 memset(ctx->inputs, 0, uw_inputs_len * sizeof(input));
adamc@756 432 memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input));
adamc@756 433 ctx->used_subinputs = 0;
adamc@144 434 }
adamc@144 435
adamc@311 436 void uw_db_init(uw_context);
adamc@311 437 void uw_handle(uw_context, char *);
adamc@167 438
adamc@311 439 failure_kind uw_begin_init(uw_context ctx) {
adamc@272 440 int r = setjmp(ctx->jmp_buf);
adamc@272 441
adamc@272 442 if (r == 0)
adamc@311 443 uw_db_init(ctx);
adamc@272 444
adamc@272 445 return r;
adamc@272 446 }
adamc@272 447
adamc@458 448 void uw_set_headers(uw_context ctx, char *headers) {
adamc@458 449 char *s = headers, *s2;
adamc@458 450 ctx->headers = headers;
adamc@458 451
adamc@458 452 while (s2 = strchr(s, '\r')) {
adamc@458 453 s = s2;
adamc@458 454
adamc@458 455 if (s[1] == 0)
adamc@458 456 break;
adamc@458 457
adamc@458 458 *s = 0;
adamc@458 459 s += 2;
adamc@458 460 }
adamc@458 461
adamc@458 462 ctx->headers_end = s;
adamc@458 463 }
adamc@458 464
adamc@742 465 void uw_headers_moved(uw_context ctx, char *headers) {
adamc@742 466 ctx->headers_end = headers + (ctx->headers_end - ctx->headers);
adamc@742 467 ctx->headers = headers;
adamc@742 468 }
adamc@742 469
adamc@671 470 int uw_db_begin(uw_context);
adamc@167 471
adamc@311 472 __attribute__((noreturn)) void uw_error(uw_context ctx, failure_kind fk, const char *fmt, ...) {
adamc@425 473 cleanup *cl;
adamc@425 474
adamc@167 475 va_list ap;
adamc@167 476 va_start(ap, fmt);
adamc@167 477
adamc@167 478 vsnprintf(ctx->error_message, ERROR_BUF_LEN, fmt, ap);
adamc@167 479
adamc@425 480 for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl)
adamc@425 481 cl->func(cl->arg);
adamc@425 482
adamc@425 483 ctx->cleanup_front = ctx->cleanup;
adamc@425 484
adamc@190 485 longjmp(ctx->jmp_buf, fk);
adamc@167 486 }
adamc@167 487
adamc@425 488 void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) {
adamc@425 489 if (ctx->cleanup_front >= ctx->cleanup_back) {
adamc@425 490 int len = ctx->cleanup_back - ctx->cleanup, newLen;
adamc@425 491 if (len == 0)
adamc@425 492 newLen = 1;
adamc@425 493 else
adamc@428 494 newLen = len * 2;
adamc@470 495 ctx->cleanup = realloc(ctx->cleanup, newLen * sizeof(cleanup));
adamc@425 496 ctx->cleanup_front = ctx->cleanup + len;
adamc@425 497 ctx->cleanup_back = ctx->cleanup + newLen;
adamc@425 498 }
adamc@425 499
adamc@425 500 ctx->cleanup_front->func = func;
adamc@425 501 ctx->cleanup_front->arg = arg;
adamc@425 502 ++ctx->cleanup_front;
adamc@425 503 }
adamc@425 504
adamc@682 505 uw_Basis_string uw_Basis_requestHeader(uw_context ctx, uw_Basis_string h) {
adamc@682 506 int len = strlen(h);
adamc@682 507 char *s = ctx->headers, *p;
adamc@682 508
adamc@682 509 while (p = strchr(s, ':')) {
adamc@682 510 if (p - s == len && !strncasecmp(s, h, len)) {
adamc@682 511 return p + 2;
adamc@682 512 } else {
adamc@682 513 if ((s = strchr(p, 0)) && s < ctx->headers_end)
adamc@682 514 s += 2;
adamc@682 515 else
adamc@682 516 return NULL;
adamc@682 517 }
adamc@682 518 }
adamc@682 519
adamc@682 520 return NULL;
adamc@682 521 }
adamc@682 522
adamc@682 523 void uw_login(uw_context ctx) {
adamc@693 524 if (ctx->needs_push) {
adamc@682 525 char *id_s, *pass_s;
adamc@682 526
adamc@682 527 if ((id_s = uw_Basis_requestHeader(ctx, "UrWeb-Client"))
adamc@682 528 && (pass_s = uw_Basis_requestHeader(ctx, "UrWeb-Pass"))) {
adamc@682 529 unsigned id = atoi(id_s);
adamc@682 530 int pass = atoi(pass_s);
adamc@682 531 client *c = find_client(id);
adamc@682 532
adamc@682 533 if (c == NULL)
adamc@682 534 uw_error(ctx, FATAL, "Unknown client ID in HTTP headers (%s, %s)", id_s, pass_s);
adamc@682 535 else {
adamc@685 536 use_client(c);
adamc@682 537 ctx->client = c;
adamc@682 538
adamc@682 539 if (c->mode != USED)
adamc@682 540 uw_error(ctx, FATAL, "Stale client ID (%u) in subscription request", id);
adamc@682 541 if (c->pass != pass)
adamc@682 542 uw_error(ctx, FATAL, "Wrong client password (%u, %d) in subscription request", id, pass);
adamc@682 543 }
adamc@682 544 } else {
adamc@682 545 client *c = new_client();
adamc@685 546 use_client(c);
adamc@682 547 ctx->client = c;
adamc@682 548 }
adamc@682 549 }
adamc@682 550 }
adamc@682 551
adamc@671 552 failure_kind uw_begin(uw_context ctx, char *path) {
adamc@671 553 int r = setjmp(ctx->jmp_buf);
adamc@671 554
adamc@671 555 if (r == 0) {
adamc@671 556 if (uw_db_begin(ctx))
adamc@671 557 uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN");
adamc@682 558
adamc@671 559 uw_handle(ctx, path);
adamc@671 560 }
adamc@671 561
adamc@671 562 return r;
adamc@671 563 }
adamc@671 564
adamc@682 565 uw_Basis_client uw_Basis_self(uw_context ctx, uw_unit u) {
adamc@682 566 if (ctx->client == NULL)
adamc@682 567 uw_error(ctx, FATAL, "Call to Basis.self() from page that has only server-side code");
adamc@682 568
adamc@682 569 return ctx->client->id;
adamc@682 570 }
adamc@682 571
adamc@425 572 void uw_pop_cleanup(uw_context ctx) {
adamc@425 573 if (ctx->cleanup_front == ctx->cleanup)
adamc@425 574 uw_error(ctx, FATAL, "Attempt to pop from empty cleanup action stack");
adamc@425 575
adamc@425 576 --ctx->cleanup_front;
adamc@425 577 ctx->cleanup_front->func(ctx->cleanup_front->arg);
adamc@425 578 }
adamc@425 579
adamc@311 580 char *uw_error_message(uw_context ctx) {
adamc@167 581 return ctx->error_message;
adamc@167 582 }
adamc@167 583
adamc@737 584 extern int uw_input_num(const char*);
adamc@144 585
adamc@759 586 static input *INP(uw_context ctx) {
adamc@759 587 if (ctx->cur_container == NULL)
adamc@759 588 return ctx->inputs;
adamc@759 589 else if (ctx->cur_container->kind == SUBFORM)
adamc@759 590 return ctx->cur_container->data.subform.fields;
adamc@759 591 else if (ctx->cur_container->kind == ENTRY)
adamc@759 592 return ctx->cur_container->data.entry.fields;
adamc@759 593 else
adamc@759 594 uw_error(ctx, FATAL, "INP: Wrong kind");
adamc@759 595 }
adamc@759 596
adamc@759 597 static void adjust_input(input *x, size_t offset) {
adamc@759 598 switch (x->kind) {
adamc@759 599 case SUBFORM:
adamc@759 600 x->data.subform.fields += offset;
adamc@759 601 if (x->data.subform.parent != NULL)
adamc@759 602 x->data.subform.parent += offset;
adamc@759 603 break;
adamc@759 604 case SUBFORMS:
adamc@759 605 if (x->data.subforms.entries != NULL)
adamc@759 606 x->data.subforms.entries += offset;
adamc@759 607 if (x->data.subforms.parent != NULL)
adamc@759 608 x->data.subforms.parent += offset;
adamc@759 609 break;
adamc@759 610 case ENTRY:
adamc@759 611 x->data.entry.fields += offset;
adamc@759 612 if (x->data.entry.next != NULL)
adamc@759 613 x->data.entry.next += offset;
adamc@759 614 if (x->data.entry.parent != NULL)
adamc@759 615 x->data.entry.parent += offset;
adamc@759 616 }
adamc@759 617 }
adamc@759 618
adamc@759 619 static input *check_input_space(uw_context ctx, size_t len) {
adamc@759 620 size_t i;
adamc@759 621 input *r;
adamc@759 622
adamc@759 623 if (ctx->used_subinputs + len >= ctx->n_subinputs) {
adamc@759 624 input *new_subinputs = realloc(ctx->subinputs, sizeof(input) * (ctx->used_subinputs + len));
adamc@759 625 size_t offset = new_subinputs - ctx->subinputs;
adamc@759 626
adamc@759 627 for (i = 0; i < ctx->used_subinputs; ++i)
adamc@759 628 adjust_input(&new_subinputs[i], offset);
adamc@759 629 for (i = 0; i < uw_inputs_len; ++i)
adamc@759 630 adjust_input(&ctx->inputs[i], offset);
adamc@759 631
adamc@759 632 if (ctx->cur_container >= ctx->subinputs && ctx->cur_container < ctx->subinputs + ctx->n_subinputs)
adamc@759 633 ctx->cur_container += offset;
adamc@759 634
adamc@759 635 ctx->n_subinputs = ctx->used_subinputs + len;
adamc@759 636 ctx->subinputs = new_subinputs;
adamc@759 637 }
adamc@759 638
adamc@759 639 r = &ctx->subinputs[ctx->used_subinputs];
adamc@759 640
adamc@759 641 for (i = 0; i < len; ++i)
adamc@759 642 ctx->subinputs[ctx->used_subinputs++].kind = UNUSED;
adamc@759 643
adamc@759 644 return r;
adamc@759 645 }
adamc@756 646
adamc@737 647 void uw_set_input(uw_context ctx, const char *name, char *value) {
adamc@756 648 if (!strcasecmp(name, ".b")) {
adamc@756 649 int n = uw_input_num(value);
adamc@756 650 input *inps;
adamc@756 651
adamc@756 652 if (n < 0)
adamc@756 653 uw_error(ctx, FATAL, "Bad subform name %s", value);
adamc@756 654
adamc@756 655 if (n >= uw_inputs_len)
adamc@756 656 uw_error(ctx, FATAL, "For subform name %s, index %d is out of range", value, n);
adamc@756 657
adamc@759 658 inps = check_input_space(ctx, uw_inputs_len);
adamc@759 659
adamc@759 660 INP(ctx)[n].kind = SUBFORM;
adamc@759 661 INP(ctx)[n].data.subform.parent = ctx->cur_container;
adamc@759 662 INP(ctx)[n].data.subform.fields = inps;
adamc@759 663 ctx->cur_container = &INP(ctx)[n];
adamc@756 664 } else if (!strcasecmp(name, ".e")) {
adamc@756 665 input *tmp;
adamc@756 666
adamc@759 667 if (ctx->cur_container == NULL)
adamc@756 668 uw_error(ctx, FATAL, "Unmatched subform closer");
adamc@756 669
adamc@759 670 tmp = ctx->cur_container;
adamc@759 671 switch (tmp->kind) {
adamc@759 672 case SUBFORM:
adamc@759 673 ctx->cur_container = tmp->data.subform.parent;
adamc@759 674 tmp->data.subform.parent = NULL;
adamc@759 675 break;
adamc@759 676 case SUBFORMS:
adamc@759 677 ctx->cur_container = tmp->data.subforms.parent;
adamc@759 678 tmp->data.subforms.parent = NULL;
adamc@759 679 break;
adamc@759 680 case ENTRY:
adamc@759 681 ctx->cur_container = tmp->data.entry.parent;
adamc@759 682 break;
adamc@759 683 default:
adamc@759 684 uw_error(ctx, FATAL, "uw_set_input: Wrong kind");
adamc@759 685 }
adamc@759 686 } else if (!strcasecmp(name, ".s")) {
adamc@759 687 int n = uw_input_num(value);
adamc@759 688
adamc@759 689 if (n < 0)
adamc@759 690 uw_error(ctx, FATAL, "Bad subforms name %s", value);
adamc@759 691
adamc@759 692 if (n >= uw_inputs_len)
adamc@759 693 uw_error(ctx, FATAL, "For subforms name %s, index %d is out of range", value, n);
adamc@759 694
adamc@759 695 INP(ctx)[n].kind = SUBFORMS;
adamc@759 696 INP(ctx)[n].data.subforms.parent = ctx->cur_container;
adamc@759 697 INP(ctx)[n].data.subforms.entries = NULL;
adamc@759 698 ctx->cur_container = &INP(ctx)[n];
adamc@759 699 } else if (!strcasecmp(name, ".i")) {
adamc@759 700 input *inps;
adamc@759 701
adamc@759 702 if (!ctx->cur_container)
adamc@759 703 uw_error(ctx, FATAL, "New entry without container");
adamc@759 704
adamc@759 705 if (ctx->cur_container->kind != SUBFORMS)
adamc@759 706 uw_error(ctx, FATAL, "Bad kind for entry parent");
adamc@759 707
adamc@759 708 inps = check_input_space(ctx, uw_inputs_len + 1);
adamc@759 709
adamc@759 710 inps->kind = ENTRY;
adamc@759 711 inps->data.entry.parent = ctx->cur_container;
adamc@759 712 inps->data.entry.next = ctx->cur_container->data.subforms.entries;
adamc@759 713 ctx->cur_container->data.subforms.entries = inps;
adamc@759 714
adamc@759 715 inps->data.entry.fields = inps+1;
adamc@759 716 ctx->cur_container = inps;
adamc@756 717 } else {
adamc@756 718 int n = uw_input_num(name);
adamc@756 719
adamc@756 720 if (n < 0)
adamc@756 721 uw_error(ctx, FATAL, "Bad input name %s", name);
adamc@756 722
adamc@756 723 if (n >= uw_inputs_len)
adamc@756 724 uw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
adamc@756 725
adamc@756 726 INP(ctx)[n].kind = NORMAL;
adamc@756 727 INP(ctx)[n].data.normal = value;
adamc@756 728 }
adamc@144 729 }
adamc@144 730
adamc@311 731 char *uw_get_input(uw_context ctx, int n) {
adamc@169 732 if (n < 0)
adamc@311 733 uw_error(ctx, FATAL, "Negative input index %d", n);
adamc@311 734 if (n >= uw_inputs_len)
adamc@311 735 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@737 736
adamc@756 737 switch (INP(ctx)[n].kind) {
adamc@737 738 case UNSET:
adamc@737 739 return NULL;
adamc@739 740 case FIL:
adamc@739 741 uw_error(ctx, FATAL, "Tried to read a file form input as normal");
adamc@756 742 case SUBFORM:
adamc@756 743 uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
adamc@759 744 case SUBFORMS:
adamc@759 745 uw_error(ctx, FATAL, "Tried to read a subforms form input as normal");
adamc@759 746 case ENTRY:
adamc@759 747 uw_error(ctx, FATAL, "Tried to read an entry form input as normal");
adamc@737 748 case NORMAL:
adamc@756 749 return INP(ctx)[n].data.normal;
adamc@737 750 default:
adamc@737 751 uw_error(ctx, FATAL, "Impossible input kind");
adamc@737 752 }
adamc@138 753 }
adamc@138 754
adamc@311 755 char *uw_get_optional_input(uw_context ctx, int n) {
adamc@190 756 if (n < 0)
adamc@311 757 uw_error(ctx, FATAL, "Negative input index %d", n);
adamc@311 758 if (n >= uw_inputs_len)
adamc@311 759 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@737 760
adamc@756 761 switch (INP(ctx)[n].kind) {
adamc@737 762 case UNSET:
adamc@737 763 return "";
adamc@739 764 case FIL:
adamc@739 765 uw_error(ctx, FATAL, "Tried to read a file form input as normal");
adamc@756 766 case SUBFORM:
adamc@756 767 uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
adamc@759 768 case SUBFORMS:
adamc@759 769 uw_error(ctx, FATAL, "Tried to read a subforms form input as normal");
adamc@759 770 case ENTRY:
adamc@759 771 uw_error(ctx, FATAL, "Tried to read an entry form input as normal");
adamc@737 772 case NORMAL:
adamc@756 773 return INP(ctx)[n].data.normal;
adamc@737 774 default:
adamc@737 775 uw_error(ctx, FATAL, "Impossible input kind");
adamc@737 776 }
adamc@737 777 }
adamc@737 778
adamc@739 779 void uw_set_file_input(uw_context ctx, const char *name, uw_Basis_file f) {
adamc@737 780 int n = uw_input_num(name);
adamc@737 781
adamc@737 782 if (n < 0)
adamc@737 783 uw_error(ctx, FATAL, "Bad file input name %s", name);
adamc@737 784
adamc@737 785 if (n >= uw_inputs_len)
adamc@737 786 uw_error(ctx, FATAL, "For file input name %s, index %d is out of range", name, n);
adamc@737 787
adamc@739 788 ctx->inputs[n].kind = FIL;
adamc@739 789 ctx->inputs[n].data.file = f;
adamc@737 790 }
adamc@737 791
adamc@739 792 void *uw_malloc(uw_context ctx, size_t len);
adamc@739 793
adamc@739 794 uw_Basis_file uw_get_file_input(uw_context ctx, int n) {
adamc@737 795 if (n < 0)
adamc@737 796 uw_error(ctx, FATAL, "Negative file input index %d", n);
adamc@737 797 if (n >= uw_inputs_len)
adamc@737 798 uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n);
adamc@737 799
adamc@756 800 switch (INP(ctx)[n].kind) {
adamc@737 801 case UNSET:
adamc@737 802 {
adamc@739 803 char *data = uw_malloc(ctx, 0);
adamc@740 804 uw_Basis_file f = {NULL, "", {0, data}};
adamc@739 805 return f;
adamc@737 806 }
adamc@739 807 case FIL:
adamc@756 808 return INP(ctx)[n].data.file;
adamc@737 809 case NORMAL:
adamc@737 810 uw_error(ctx, FATAL, "Tried to read a normal form input as files");
adamc@756 811 case SUBFORM:
adamc@756 812 uw_error(ctx, FATAL, "Tried to read a subform form input as files");
adamc@759 813 case SUBFORMS:
adamc@759 814 uw_error(ctx, FATAL, "Tried to read a subforms form input as files");
adamc@759 815 case ENTRY:
adamc@759 816 uw_error(ctx, FATAL, "Tried to read an entry form input as files");
adamc@737 817 default:
adamc@737 818 uw_error(ctx, FATAL, "Impossible input kind");
adamc@737 819 }
adamc@190 820 }
adamc@190 821
adamc@756 822 void uw_enter_subform(uw_context ctx, int n) {
adamc@756 823 if (n < 0)
adamc@756 824 uw_error(ctx, FATAL, "Negative subform index %d", n);
adamc@756 825 if (n >= uw_inputs_len)
adamc@756 826 uw_error(ctx, FATAL, "Out-of-bounds subform index %d", n);
adamc@756 827
adamc@756 828 switch (INP(ctx)[n].kind) {
adamc@756 829 case UNSET:
adamc@756 830 uw_error(ctx, FATAL, "Missing subform");
adamc@756 831 case FIL:
adamc@756 832 uw_error(ctx, FATAL, "Tried to read a file form input as subform");
adamc@756 833 case NORMAL:
adamc@756 834 uw_error(ctx, FATAL, "Tried to read a normal form input as subform");
adamc@759 835 case SUBFORMS:
adamc@759 836 uw_error(ctx, FATAL, "Tried to read a subforms form input as subform");
adamc@759 837 case ENTRY:
adamc@759 838 uw_error(ctx, FATAL, "Tried to read an entry form input as subform");
adamc@756 839 case SUBFORM:
adamc@759 840 INP(ctx)[n].data.subform.parent = ctx->cur_container;
adamc@759 841 ctx->cur_container = INP(ctx)[n].data.subform.fields;
adamc@756 842 return;
adamc@756 843 default:
adamc@756 844 uw_error(ctx, FATAL, "Impossible input kind");
adamc@756 845 }
adamc@756 846 }
adamc@756 847
adamc@756 848 void uw_leave_subform(uw_context ctx) {
adamc@756 849 input *tmp;
adamc@756 850
adamc@759 851 if (ctx->cur_container == NULL)
adamc@756 852 uw_error(ctx, FATAL, "Unmatched uw_leave_subform");
adamc@756 853
adamc@759 854 tmp = ctx->cur_container;
adamc@759 855 ctx->cur_container = tmp->data.subform.parent;
adamc@759 856 tmp->data.subform.parent = NULL;
adamc@759 857 }
adamc@759 858
adamc@759 859 int uw_enter_subforms(uw_context ctx, int n) {
adamc@759 860 input *inps;
adamc@759 861
adamc@759 862 if (n < 0)
adamc@759 863 uw_error(ctx, FATAL, "Negative subforms index %d", n);
adamc@759 864 if (n >= uw_inputs_len)
adamc@759 865 uw_error(ctx, FATAL, "Out-of-bounds subforms index %d", n);
adamc@759 866
adamc@759 867 switch (INP(ctx)[n].kind) {
adamc@759 868 case UNSET:
adamc@759 869 uw_error(ctx, FATAL, "Missing subforms");
adamc@759 870 case FIL:
adamc@759 871 uw_error(ctx, FATAL, "Tried to read a file form input as subforms");
adamc@759 872 case NORMAL:
adamc@759 873 uw_error(ctx, FATAL, "Tried to read a normal form input %p as subforms", &INP(ctx)[n]);
adamc@759 874 case SUBFORM:
adamc@759 875 uw_error(ctx, FATAL, "Tried to read a subform form input as subforms");
adamc@759 876 case ENTRY:
adamc@759 877 uw_error(ctx, FATAL, "Tried to read an entry form input as subforms");
adamc@759 878 case SUBFORMS:
adamc@759 879 inps = INP(ctx)[n].data.subforms.entries;
adamc@759 880 if (inps) {
adamc@759 881 INP(ctx)[n].data.subforms.parent = ctx->cur_container;
adamc@759 882 ctx->cur_container = INP(ctx)[n].data.subforms.entries;
adamc@759 883 return 1;
adamc@759 884 } else
adamc@759 885 return 0;
adamc@759 886 default:
adamc@759 887 uw_error(ctx, FATAL, "Impossible input kind");
adamc@759 888 }
adamc@759 889 }
adamc@759 890
adamc@759 891 int uw_next_entry(uw_context ctx) {
adamc@759 892 if (ctx->cur_container == NULL)
adamc@759 893 uw_error(ctx, FATAL, "uw_next_entry(NULL)");
adamc@759 894
adamc@759 895 switch (ctx->cur_container->kind) {
adamc@759 896 case UNSET:
adamc@759 897 uw_error(ctx, FATAL, "Missing entry");
adamc@759 898 case FIL:
adamc@759 899 uw_error(ctx, FATAL, "Tried to read a file form input as entry");
adamc@759 900 case NORMAL:
adamc@759 901 uw_error(ctx, FATAL, "Tried to read a normal form input as entry");
adamc@759 902 case SUBFORM:
adamc@759 903 uw_error(ctx, FATAL, "Tried to read a subform form input as entry");
adamc@759 904 case SUBFORMS:
adamc@759 905 uw_error(ctx, FATAL, "Tried to read a subforms form input as entry");
adamc@759 906 case ENTRY:
adamc@759 907 if (ctx->cur_container->data.entry.next) {
adamc@759 908 ctx->cur_container = ctx->cur_container->data.entry.next;
adamc@759 909 return 1;
adamc@759 910 } else {
adamc@759 911 ctx->cur_container = ctx->cur_container->data.entry.parent->data.subforms.parent;
adamc@759 912 return 0;
adamc@759 913 }
adamc@759 914 default:
adamc@759 915 uw_error(ctx, FATAL, "Impossible input kind");
adamc@759 916 }
adamc@756 917 }
adamc@756 918
adamc@643 919 void uw_set_script_header(uw_context ctx, const char *s) {
adamc@643 920 ctx->script_header = s;
adamc@643 921 }
adamc@643 922
adamc@667 923 void uw_set_url_prefix(uw_context ctx, const char *s) {
adamc@667 924 ctx->url_prefix = s;
adamc@667 925 }
adamc@667 926
adamc@693 927 void uw_set_needs_push(uw_context ctx, int n) {
adamc@693 928 ctx->needs_push = n;
adamc@693 929 }
adamc@693 930
adamc@736 931 void uw_set_needs_sig(uw_context ctx, int n) {
adamc@736 932 ctx->needs_sig = n;
adamc@736 933 }
adamc@736 934
adamc@667 935
adamc@667 936 static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc) {
adamc@666 937 if (b->back - b->front < extra) {
adamc@666 938 size_t desired = b->front - b->start + extra, next;
adamc@136 939 char *new_heap;
adamc@136 940
adamc@666 941 next = b->back - b->start;
adamc@317 942 if (next == 0)
adamc@317 943 next = 1;
adamc@317 944 for (; next < desired; next *= 2);
adamc@136 945
adamc@666 946 new_heap = realloc(b->start, next);
adamc@666 947 b->front = new_heap + (b->front - b->start);
adamc@666 948 b->back = new_heap + next;
adamc@136 949
adamc@667 950 if (new_heap != b->start) {
adamc@666 951 b->start = new_heap;
adamc@666 952 uw_error(ctx, UNLIMITED_RETRY, "Couldn't allocate new %s contiguously", desc);
adamc@136 953 }
adamc@136 954
adamc@666 955 b->start = new_heap;
adamc@136 956 }
adamc@136 957 }
adamc@136 958
adamc@666 959 static void uw_check_heap(uw_context ctx, size_t extra) {
adamc@667 960 buf_check_ctx(ctx, &ctx->heap, extra, "heap chunk");
adamc@666 961 }
adamc@666 962
adamc@311 963 void *uw_malloc(uw_context ctx, size_t len) {
adamc@136 964 void *result;
adamc@136 965
adamc@311 966 uw_check_heap(ctx, len);
adamc@136 967
adamc@666 968 result = ctx->heap.front;
adamc@666 969 ctx->heap.front += len;
adamc@136 970 return result;
adamc@117 971 }
adamc@117 972
adamc@323 973 void uw_begin_region(uw_context ctx) {
adamc@666 974 regions *r = (regions *) ctx->heap.front;
adamc@323 975
adamc@323 976 uw_check_heap(ctx, sizeof(regions));
adamc@323 977
adamc@666 978 ctx->heap.front += sizeof(regions);
adamc@323 979
adamc@323 980 r->next = ctx->regions;
adamc@323 981 ctx->regions = r;
adamc@323 982 }
adamc@323 983
adamc@323 984 void uw_end_region(uw_context ctx) {
adamc@323 985 regions *r = ctx->regions;
adamc@323 986
adamc@323 987 if (r == NULL)
adamc@323 988 uw_error(ctx, FATAL, "Region stack underflow");
adamc@323 989
adamc@666 990 ctx->heap.front = (char *) r;
adamc@323 991 ctx->regions = r->next;
adamc@323 992 }
adamc@323 993
adamc@324 994 void uw_memstats(uw_context ctx) {
adamc@666 995 printf("Headers: %d/%d\n", buf_used(&ctx->outHeaders), buf_avail(&ctx->outHeaders));
adamc@666 996 printf("Script: %d/%d\n", buf_used(&ctx->script), buf_avail(&ctx->script));
adamc@666 997 printf("Page: %d/%d\n", buf_used(&ctx->page), buf_avail(&ctx->page));
adamc@666 998 printf("Heap: %d/%d\n", buf_used(&ctx->heap), buf_avail(&ctx->heap));
adamc@324 999 }
adamc@324 1000
adamc@311 1001 int uw_send(uw_context ctx, int sock) {
adamc@666 1002 int n = uw_really_send(sock, ctx->outHeaders.start, ctx->outHeaders.front - ctx->outHeaders.start);
adamc@462 1003
adamc@462 1004 if (n < 0)
adamc@462 1005 return n;
adamc@462 1006
adamc@462 1007 n = uw_really_send(sock, "\r\n", 2);
adamc@462 1008
adamc@462 1009 if (n < 0)
adamc@462 1010 return n;
adamc@462 1011
adamc@666 1012 return uw_really_send(sock, ctx->page.start, ctx->page.front - ctx->page.start);
adamc@462 1013 }
adamc@462 1014
adamc@462 1015 static void uw_check_headers(uw_context ctx, size_t extra) {
adamc@667 1016 buf_check(&ctx->outHeaders, extra);
adamc@462 1017 }
adamc@462 1018
adamc@462 1019 void uw_write_header(uw_context ctx, uw_Basis_string s) {
adamc@462 1020 int len = strlen(s);
adamc@462 1021
adamc@462 1022 uw_check_headers(ctx, len + 1);
adamc@666 1023 strcpy(ctx->outHeaders.front, s);
adamc@666 1024 ctx->outHeaders.front += len;
adamc@562 1025 }
adamc@562 1026
adamc@562 1027 static void uw_check_script(uw_context ctx, size_t extra) {
adamc@667 1028 buf_check(&ctx->script, extra);
adamc@562 1029 }
adamc@562 1030
adamc@562 1031 void uw_write_script(uw_context ctx, uw_Basis_string s) {
adamc@562 1032 int len = strlen(s);
adamc@562 1033
adamc@562 1034 uw_check_script(ctx, len + 1);
adamc@666 1035 strcpy(ctx->script.front, s);
adamc@666 1036 ctx->script.front += len;
adamc@562 1037 }
adamc@562 1038
adamc@645 1039 const char *uw_Basis_get_script(uw_context ctx, uw_unit u) {
adamc@667 1040 if (ctx->script_header[0] == 0)
adamc@667 1041 return "";
adamc@696 1042 else if (buf_used(&ctx->script) == 0)
adamc@696 1043 return ctx->script_header;
adamc@667 1044 else {
adamc@695 1045 char *r = uw_malloc(ctx, strlen(ctx->script_header) + 42 + buf_used(&ctx->script));
adamc@695 1046 sprintf(r, "%s<script type=\"text/javascript\">%s</script>",
adamc@673 1047 ctx->script_header,
adamc@673 1048 ctx->script.start);
adamc@565 1049 return r;
adamc@565 1050 }
adamc@565 1051 }
adamc@565 1052
adamc@695 1053 uw_Basis_string uw_Basis_maybe_onload(uw_context ctx, uw_Basis_string s) {
adamc@695 1054 if (s[0] == 0)
adamc@695 1055 return "";
adamc@695 1056 else {
adamc@695 1057 char *r = uw_malloc(ctx, 11 + strlen(s));
adamc@695 1058 sprintf(r, " onload='%s'", s);
adamc@695 1059 return r;
adamc@695 1060 }
adamc@695 1061 }
adamc@695 1062
adamc@736 1063 extern uw_Basis_string uw_cookie_sig(uw_context);
adamc@736 1064
adamc@694 1065 const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) {
adamc@736 1066 if (ctx->client == NULL) {
adamc@736 1067 if (ctx->needs_sig) {
adamc@736 1068 char *sig = uw_cookie_sig(ctx);
adamc@736 1069 char *r = uw_malloc(ctx, strlen(sig) + 8);
adamc@736 1070 sprintf(r, "sig=\"%s\";", sig);
adamc@736 1071 return r;
adamc@736 1072 }
adamc@736 1073 else
adamc@736 1074 return "";
adamc@736 1075 } else {
adamc@736 1076 char *sig = ctx->needs_sig ? uw_cookie_sig(ctx) : "";
adamc@736 1077 char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->url_prefix)
adamc@736 1078 + (ctx->needs_sig ? strlen(sig) + 7 : 0));
adamc@736 1079 sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s%s%slistener();",
adamc@682 1080 ctx->client->id,
adamc@682 1081 ctx->client->pass,
adamc@679 1082 ctx->url_prefix,
adamc@736 1083 ctx->timeout,
adamc@736 1084 ctx->needs_sig ? "sig=\"" : "",
adamc@736 1085 sig,
adamc@736 1086 ctx->needs_sig ? "\";" : "");
adamc@679 1087 return r;
adamc@668 1088 }
adamc@667 1089 }
adamc@667 1090
adamc@574 1091 uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) {
adamc@574 1092 char *r, *s2;
adamc@574 1093
adamc@574 1094 uw_check_heap(ctx, strlen(s) * 4 + 2);
adamc@574 1095
adamc@666 1096 r = s2 = ctx->heap.front;
adamc@574 1097 *s2++ = '"';
adamc@574 1098
adamc@574 1099 for (; *s; s++) {
adamc@574 1100 char c = *s;
adamc@574 1101
adamc@574 1102 switch (c) {
adamc@574 1103 case '"':
adamc@574 1104 strcpy(s2, "\\\"");
adamc@574 1105 s2 += 2;
adamc@574 1106 break;
adamc@574 1107 case '\\':
adamc@574 1108 strcpy(s2, "\\\\");
adamc@574 1109 s2 += 2;
adamc@574 1110 break;
adamc@574 1111 default:
adamc@574 1112 if (isprint(c))
adamc@574 1113 *s2++ = c;
adamc@574 1114 else {
adamc@574 1115 sprintf(s2, "\\%3o", c);
adamc@574 1116 s2 += 4;
adamc@574 1117 }
adamc@574 1118 }
adamc@574 1119 }
adamc@574 1120
adamc@574 1121 strcpy(s2, "\"");
adamc@666 1122 ctx->heap.front = s2 + 1;
adamc@574 1123 return r;
adamc@574 1124 }
adamc@574 1125
adamc@574 1126 uw_Basis_string uw_Basis_jsifyString_ws(uw_context ctx, uw_Basis_string s) {
adamc@574 1127 char *r, *s2;
adamc@574 1128
adamc@574 1129 uw_check_script(ctx, strlen(s) * 4 + 2);
adamc@574 1130
adamc@666 1131 r = s2 = ctx->script.front;
adamc@574 1132 *s2++ = '"';
adamc@574 1133
adamc@574 1134 for (; *s; s++) {
adamc@574 1135 char c = *s;
adamc@574 1136
adamc@574 1137 switch (c) {
adamc@577 1138 case '\'':
adamc@574 1139 strcpy(s2, "\\\"");
adamc@574 1140 s2 += 2;
adamc@574 1141 break;
adamc@574 1142 case '\\':
adamc@574 1143 strcpy(s2, "\\\\");
adamc@574 1144 s2 += 2;
adamc@574 1145 break;
adamc@574 1146 default:
adamc@574 1147 if (isprint(c))
adamc@574 1148 *s2++ = c;
adamc@574 1149 else {
adamc@574 1150 sprintf(s2, "\\%3o", c);
adamc@574 1151 s2 += 4;
adamc@574 1152 }
adamc@574 1153 }
adamc@574 1154 }
adamc@574 1155
adamc@574 1156 strcpy(s2, "\"");
adamc@666 1157 ctx->script.front = s2 + 1;
adamc@574 1158 return r;
adamc@574 1159 }
adamc@574 1160
adamc@682 1161 char *uw_Basis_jsifyChannel(uw_context ctx, uw_Basis_channel chn) {
adamc@682 1162 if (ctx->client == NULL || chn.cli != ctx->client->id)
adamc@682 1163 return "null";
adamc@682 1164 else {
adamc@682 1165 int len;
adamc@682 1166 char *r;
adamc@682 1167
adamc@682 1168 uw_check_heap(ctx, INTS_MAX + 1);
adamc@682 1169 r = ctx->heap.front;
adamc@682 1170 sprintf(r, "%u%n", chn.chn, &len);
adamc@682 1171 ctx->heap.front += len+1;
adamc@682 1172 return r;
adamc@682 1173 }
adamc@682 1174 }
adamc@682 1175
adamc@577 1176 uw_Basis_int uw_Basis_new_client_source(uw_context ctx, uw_Basis_string s) {
adamc@577 1177 int len;
adamc@577 1178 size_t s_len = strlen(s);
adamc@562 1179
adamc@577 1180 uw_check_script(ctx, 12 + INTS_MAX + s_len);
adamc@666 1181 sprintf(ctx->script.front, "var s%d=sc(%n", ctx->source_count, &len);
adamc@666 1182 ctx->script.front += len;
adamc@666 1183 strcpy(ctx->script.front, s);
adamc@666 1184 ctx->script.front += s_len;
adamc@666 1185 strcpy(ctx->script.front, ");");
adamc@666 1186 ctx->script.front += 2;
adamc@562 1187
adamc@565 1188 return ctx->source_count++;
adamc@117 1189 }
adamc@117 1190
adamc@577 1191 uw_unit uw_Basis_set_client_source(uw_context ctx, uw_Basis_int n, uw_Basis_string s) {
adamc@577 1192 int len;
adamc@577 1193 size_t s_len = strlen(s);
adamc@577 1194
adamc@577 1195 uw_check_script(ctx, 6 + INTS_MAX + s_len);
adamc@666 1196 sprintf(ctx->script.front, "s%d.v=%n", (int)n, &len);
adamc@666 1197 ctx->script.front += len;
adamc@666 1198 strcpy(ctx->script.front, s);
adamc@666 1199 ctx->script.front += s_len;
adamc@666 1200 strcpy(ctx->script.front, ";");
adamc@666 1201 ctx->script.front++;
adamc@577 1202
adamc@577 1203 return uw_unit_v;
adamc@577 1204 }
adamc@577 1205
adamc@311 1206 static void uw_check(uw_context ctx, size_t extra) {
adamc@667 1207 buf_check(&ctx->page, extra);
adamc@117 1208 }
adamc@117 1209
adamc@311 1210 static void uw_writec_unsafe(uw_context ctx, char c) {
adamc@666 1211 *(ctx->page.front)++ = c;
adamc@117 1212 }
adamc@117 1213
adamc@311 1214 void uw_writec(uw_context ctx, char c) {
adamc@311 1215 uw_check(ctx, 1);
adamc@311 1216 uw_writec_unsafe(ctx, c);
adamc@117 1217 }
adamc@117 1218
adamc@311 1219 static void uw_write_unsafe(uw_context ctx, const char* s) {
adamc@117 1220 int len = strlen(s);
adamc@666 1221 memcpy(ctx->page.front, s, len);
adamc@666 1222 ctx->page.front += len;
adamc@117 1223 }
adamc@117 1224
adamc@311 1225 void uw_write(uw_context ctx, const char* s) {
adamc@311 1226 uw_check(ctx, strlen(s) + 1);
adamc@311 1227 uw_write_unsafe(ctx, s);
adamc@666 1228 *ctx->page.front = 0;
adamc@102 1229 }
adamc@106 1230
adamc@311 1231 char *uw_Basis_attrifyInt(uw_context ctx, uw_Basis_int n) {
adamc@136 1232 char *result;
adamc@136 1233 int len;
adamc@311 1234 uw_check_heap(ctx, INTS_MAX);
adamc@666 1235 result = ctx->heap.front;
adamc@276 1236 sprintf(result, "%lld%n", n, &len);
adamc@666 1237 ctx->heap.front += len+1;
adamc@136 1238 return result;
adamc@106 1239 }
adamc@106 1240
adamc@311 1241 char *uw_Basis_attrifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@136 1242 char *result;
adamc@136 1243 int len;
adamc@311 1244 uw_check_heap(ctx, FLOATS_MAX);
adamc@666 1245 result = ctx->heap.front;
adamc@136 1246 sprintf(result, "%g%n", n, &len);
adamc@666 1247 ctx->heap.front += len+1;
adamc@136 1248 return result;
adamc@106 1249 }
adamc@106 1250
adamc@311 1251 char *uw_Basis_attrifyString(uw_context ctx, uw_Basis_string s) {
adamc@136 1252 int len = strlen(s);
adamc@136 1253 char *result, *p;
adamc@311 1254 uw_check_heap(ctx, len * 6 + 1);
adamc@136 1255
adamc@666 1256 result = p = ctx->heap.front;
adamc@136 1257
adamc@136 1258 for (; *s; s++) {
adamc@136 1259 char c = *s;
adamc@136 1260
adamc@136 1261 if (c == '"') {
adamc@136 1262 strcpy(p, "&quot;");
adamc@136 1263 p += 6;
adamc@136 1264 } else if (c == '&') {
adamc@136 1265 strcpy(p, "&amp;");
adamc@136 1266 p += 5;
adamc@136 1267 }
adamc@136 1268 else if (isprint(c))
adamc@136 1269 *p++ = c;
adamc@136 1270 else {
adamc@136 1271 int len2;
adamc@136 1272 sprintf(p, "&#%d;%n", c, &len2);
adamc@136 1273 p += len2;
adamc@136 1274 }
adamc@136 1275 }
adamc@136 1276
adamc@137 1277 *p++ = 0;
adamc@666 1278 ctx->heap.front = p;
adamc@136 1279 return result;
adamc@106 1280 }
adamc@106 1281
adamc@721 1282 char *uw_Basis_attrifyCss_class(uw_context ctx, uw_Basis_css_class s) {
adamc@721 1283 return s;
adamc@721 1284 }
adamc@721 1285
adamc@311 1286 static void uw_Basis_attrifyInt_w_unsafe(uw_context ctx, uw_Basis_int n) {
adamc@117 1287 int len;
adamc@117 1288
adamc@666 1289 sprintf(ctx->page.front, "%lld%n", n, &len);
adamc@666 1290 ctx->page.front += len;
adamc@106 1291 }
adamc@106 1292
adamc@428 1293 uw_unit uw_Basis_attrifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@311 1294 uw_check(ctx, INTS_MAX);
adamc@311 1295 uw_Basis_attrifyInt_w_unsafe(ctx, n);
adamc@428 1296
adamc@428 1297 return uw_unit_v;
adamc@106 1298 }
adamc@106 1299
adamc@428 1300 uw_unit uw_Basis_attrifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@117 1301 int len;
adamc@117 1302
adamc@311 1303 uw_check(ctx, FLOATS_MAX);
adamc@666 1304 sprintf(ctx->page.front, "%g%n", n, &len);
adamc@666 1305 ctx->page.front += len;
adamc@428 1306
adamc@428 1307 return uw_unit_v;
adamc@117 1308 }
adamc@117 1309
adamc@428 1310 uw_unit uw_Basis_attrifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@311 1311 uw_check(ctx, strlen(s) * 6);
adamc@117 1312
adamc@106 1313 for (; *s; s++) {
adamc@106 1314 char c = *s;
adamc@106 1315
adamc@106 1316 if (c == '"')
adamc@311 1317 uw_write_unsafe(ctx, "&quot;");
adamc@136 1318 else if (c == '&')
adamc@311 1319 uw_write_unsafe(ctx, "&amp;");
adamc@106 1320 else if (isprint(c))
adamc@311 1321 uw_writec_unsafe(ctx, c);
adamc@106 1322 else {
adamc@311 1323 uw_write_unsafe(ctx, "&#");
adamc@311 1324 uw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@311 1325 uw_writec_unsafe(ctx, ';');
adamc@106 1326 }
adamc@106 1327 }
adamc@428 1328
adamc@428 1329 return uw_unit_v;
adamc@106 1330 }
adamc@120 1331
adamc@120 1332
adamc@311 1333 char *uw_Basis_urlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@137 1334 int len;
adamc@137 1335 char *r;
adamc@137 1336
adamc@311 1337 uw_check_heap(ctx, INTS_MAX);
adamc@666 1338 r = ctx->heap.front;
adamc@276 1339 sprintf(r, "%lld%n", n, &len);
adamc@666 1340 ctx->heap.front += len+1;
adamc@137 1341 return r;
adamc@120 1342 }
adamc@120 1343
adamc@682 1344 char *uw_Basis_urlifyChannel(uw_context ctx, uw_Basis_channel chn) {
adamc@682 1345 if (ctx->client == NULL || chn.cli != ctx->client->id)
adamc@682 1346 return "";
adamc@682 1347 else {
adamc@682 1348 int len;
adamc@682 1349 char *r;
adamc@679 1350
adamc@682 1351 uw_check_heap(ctx, INTS_MAX + 1);
adamc@682 1352 r = ctx->heap.front;
adamc@682 1353 sprintf(r, "%u%n", chn.chn, &len);
adamc@682 1354 ctx->heap.front += len+1;
adamc@682 1355 return r;
adamc@682 1356 }
adamc@679 1357 }
adamc@679 1358
adamc@311 1359 char *uw_Basis_urlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@137 1360 int len;
adamc@137 1361 char *r;
adamc@137 1362
adamc@311 1363 uw_check_heap(ctx, FLOATS_MAX);
adamc@666 1364 r = ctx->heap.front;
adamc@137 1365 sprintf(r, "%g%n", n, &len);
adamc@666 1366 ctx->heap.front += len+1;
adamc@137 1367 return r;
adamc@120 1368 }
adamc@120 1369
adamc@311 1370 char *uw_Basis_urlifyString(uw_context ctx, uw_Basis_string s) {
adamc@137 1371 char *r, *p;
adamc@137 1372
adamc@311 1373 uw_check_heap(ctx, strlen(s) * 3 + 1);
adamc@137 1374
adamc@666 1375 for (r = p = ctx->heap.front; *s; s++) {
adamc@137 1376 char c = *s;
adamc@137 1377
adamc@137 1378 if (c == ' ')
adamc@137 1379 *p++ = '+';
adamc@137 1380 else if (isalnum(c))
adamc@137 1381 *p++ = c;
adamc@137 1382 else {
adamc@137 1383 sprintf(p, "%%%02X", c);
adamc@137 1384 p += 3;
adamc@137 1385 }
adamc@137 1386 }
adamc@137 1387
adamc@137 1388 *p++ = 0;
adamc@666 1389 ctx->heap.front = p;
adamc@137 1390 return r;
adamc@120 1391 }
adamc@120 1392
adamc@311 1393 char *uw_Basis_urlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 1394 if (b == uw_Basis_False)
adamc@186 1395 return "0";
adamc@186 1396 else
adamc@186 1397 return "1";
adamc@186 1398 }
adamc@186 1399
adamc@311 1400 static void uw_Basis_urlifyInt_w_unsafe(uw_context ctx, uw_Basis_int n) {
adamc@120 1401 int len;
adamc@120 1402
adamc@666 1403 sprintf(ctx->page.front, "%lld%n", n, &len);
adamc@666 1404 ctx->page.front += len;
adamc@120 1405 }
adamc@120 1406
adamc@428 1407 uw_unit uw_Basis_urlifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@311 1408 uw_check(ctx, INTS_MAX);
adamc@311 1409 uw_Basis_urlifyInt_w_unsafe(ctx, n);
adamc@428 1410
adamc@428 1411 return uw_unit_v;
adamc@120 1412 }
adamc@120 1413
adamc@682 1414 uw_unit uw_Basis_urlifyChannel_w(uw_context ctx, uw_Basis_channel chn) {
adamc@682 1415 if (ctx->client != NULL && chn.cli == ctx->client->id) {
adamc@682 1416 int len;
adamc@682 1417
adamc@682 1418 uw_check(ctx, INTS_MAX + 1);
adamc@682 1419 sprintf(ctx->page.front, "%u%n", chn.chn, &len);
adamc@682 1420 ctx->page.front += len;
adamc@682 1421 }
adamc@682 1422
adamc@679 1423 return uw_unit_v;
adamc@679 1424 }
adamc@679 1425
adamc@428 1426 uw_unit uw_Basis_urlifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@120 1427 int len;
adamc@120 1428
adamc@311 1429 uw_check(ctx, FLOATS_MAX);
adamc@666 1430 sprintf(ctx->page.front, "%g%n", n, &len);
adamc@666 1431 ctx->page.front += len;
adamc@428 1432
adamc@428 1433 return uw_unit_v;
adamc@120 1434 }
adamc@120 1435
adamc@488 1436 uw_Basis_string uw_Basis_urlifyTime(uw_context ctx, uw_Basis_time t) {
adamc@488 1437 return uw_Basis_urlifyInt(ctx, t);
adamc@488 1438 }
adamc@488 1439
adamc@428 1440 uw_unit uw_Basis_urlifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@311 1441 uw_check(ctx, strlen(s) * 3);
adamc@120 1442
adamc@120 1443 for (; *s; s++) {
adamc@120 1444 char c = *s;
adamc@120 1445
adamc@120 1446 if (c == ' ')
adamc@311 1447 uw_writec_unsafe(ctx, '+');
adamc@120 1448 else if (isalnum(c))
adamc@311 1449 uw_writec_unsafe(ctx, c);
adamc@120 1450 else {
adamc@666 1451 sprintf(ctx->page.front, "%%%02X", c);
adamc@666 1452 ctx->page.front += 3;
adamc@120 1453 }
adamc@120 1454 }
adamc@428 1455
adamc@428 1456 return uw_unit_v;
adamc@120 1457 }
adamc@120 1458
adamc@428 1459 uw_unit uw_Basis_urlifyBool_w(uw_context ctx, uw_Basis_bool b) {
adamc@311 1460 if (b == uw_Basis_False)
adamc@311 1461 uw_writec(ctx, '0');
adamc@186 1462 else
adamc@311 1463 uw_writec(ctx, '1');
adamc@428 1464
adamc@428 1465 return uw_unit_v;
adamc@186 1466 }
adamc@186 1467
adamc@120 1468
adamc@311 1469 static char *uw_unurlify_advance(char *s) {
adamc@144 1470 char *new_s = strchr(s, '/');
adamc@120 1471
adamc@120 1472 if (new_s)
adamc@120 1473 *new_s++ = 0;
adamc@120 1474 else
adamc@144 1475 new_s = strchr(s, 0);
adamc@144 1476
adamc@144 1477 return new_s;
adamc@144 1478 }
adamc@144 1479
adamc@311 1480 uw_Basis_int uw_Basis_unurlifyInt(uw_context ctx, char **s) {
adamc@311 1481 char *new_s = uw_unurlify_advance(*s);
adamc@311 1482 uw_Basis_int r;
adamc@120 1483
adamc@276 1484 r = atoll(*s);
adamc@120 1485 *s = new_s;
adamc@120 1486 return r;
adamc@120 1487 }
adamc@120 1488
adamc@311 1489 uw_Basis_float uw_Basis_unurlifyFloat(uw_context ctx, char **s) {
adamc@311 1490 char *new_s = uw_unurlify_advance(*s);
adamc@311 1491 uw_Basis_float r;
adamc@120 1492
adamc@120 1493 r = atof(*s);
adamc@120 1494 *s = new_s;
adamc@120 1495 return r;
adamc@120 1496 }
adamc@120 1497
adamc@488 1498 uw_Basis_time uw_Basis_unurlifyTime(uw_context ctx, char **s) {
adamc@488 1499 return uw_Basis_unurlifyInt(ctx, s);
adamc@488 1500 }
adamc@488 1501
adamc@311 1502 static uw_Basis_string uw_unurlifyString_to(uw_context ctx, char *r, char *s) {
adamc@144 1503 char *s1, *s2;
adamc@144 1504 int n;
adamc@136 1505
adamc@144 1506 for (s1 = r, s2 = s; *s2; ++s1, ++s2) {
adamc@136 1507 char c = *s2;
adamc@136 1508
adamc@136 1509 switch (c) {
adamc@136 1510 case '+':
adamc@136 1511 *s1 = ' ';
adamc@136 1512 break;
adamc@136 1513 case '%':
adamc@169 1514 if (s2[1] == 0)
adamc@311 1515 uw_error(ctx, FATAL, "Missing first character of escaped URL byte");
adamc@169 1516 if (s2[2] == 0)
adamc@311 1517 uw_error(ctx, FATAL, "Missing second character of escaped URL byte");
adamc@169 1518 if (sscanf(s2+1, "%02X", &n) != 1)
adamc@311 1519 uw_error(ctx, FATAL, "Invalid escaped URL byte starting at: %s", s2);
adamc@136 1520 *s1 = n;
adamc@136 1521 s2 += 2;
adamc@136 1522 break;
adamc@136 1523 default:
adamc@136 1524 *s1 = c;
adamc@136 1525 }
adamc@136 1526 }
adamc@136 1527 *s1++ = 0;
adamc@144 1528 return s1;
adamc@144 1529 }
adamc@144 1530
adamc@311 1531 uw_Basis_bool uw_Basis_unurlifyBool(uw_context ctx, char **s) {
adamc@311 1532 char *new_s = uw_unurlify_advance(*s);
adamc@311 1533 uw_Basis_bool r;
adamc@186 1534
adamc@186 1535 if (*s[0] == 0 || !strcmp(*s, "0") || !strcmp(*s, "off"))
adamc@311 1536 r = uw_Basis_False;
adamc@186 1537 else
adamc@311 1538 r = uw_Basis_True;
adamc@186 1539
adamc@186 1540 *s = new_s;
adamc@186 1541 return r;
adamc@186 1542 }
adamc@186 1543
adamc@311 1544 uw_Basis_string uw_Basis_unurlifyString(uw_context ctx, char **s) {
adamc@311 1545 char *new_s = uw_unurlify_advance(*s);
adamc@144 1546 char *r, *s1, *s2;
adamc@144 1547 int len, n;
adamc@144 1548
adamc@200 1549 len = strlen(*s);
adamc@311 1550 uw_check_heap(ctx, len + 1);
adamc@144 1551
adamc@666 1552 r = ctx->heap.front;
adamc@666 1553 ctx->heap.front = uw_unurlifyString_to(ctx, ctx->heap.front, *s);
adamc@136 1554 *s = new_s;
adamc@136 1555 return r;
adamc@120 1556 }
adamc@135 1557
adamc@135 1558
adamc@311 1559 char *uw_Basis_htmlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@286 1560 int len;
adamc@286 1561 char *r;
adamc@286 1562
adamc@311 1563 uw_check_heap(ctx, INTS_MAX);
adamc@666 1564 r = ctx->heap.front;
adamc@286 1565 sprintf(r, "%lld%n", n, &len);
adamc@666 1566 ctx->heap.front += len+1;
adamc@286 1567 return r;
adamc@286 1568 }
adamc@286 1569
adamc@428 1570 uw_unit uw_Basis_htmlifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@286 1571 int len;
adamc@286 1572
adamc@311 1573 uw_check(ctx, INTS_MAX);
adamc@666 1574 sprintf(ctx->page.front, "%lld%n", n, &len);
adamc@666 1575 ctx->page.front += len;
adamc@428 1576
adamc@428 1577 return uw_unit_v;
adamc@286 1578 }
adamc@286 1579
adamc@311 1580 char *uw_Basis_htmlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@286 1581 int len;
adamc@286 1582 char *r;
adamc@286 1583
adamc@311 1584 uw_check_heap(ctx, FLOATS_MAX);
adamc@666 1585 r = ctx->heap.front;
adamc@286 1586 sprintf(r, "%g%n", n, &len);
adamc@666 1587 ctx->heap.front += len+1;
adamc@286 1588 return r;
adamc@286 1589 }
adamc@286 1590
adamc@428 1591 uw_unit uw_Basis_htmlifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@286 1592 int len;
adamc@286 1593
adamc@311 1594 uw_check(ctx, FLOATS_MAX);
adamc@666 1595 sprintf(ctx->page.front, "%g%n", n, &len);
adamc@666 1596 ctx->page.front += len;
adamc@428 1597
adamc@428 1598 return uw_unit_v;
adamc@286 1599 }
adamc@286 1600
adamc@311 1601 char *uw_Basis_htmlifyString(uw_context ctx, uw_Basis_string s) {
adamc@137 1602 char *r, *s2;
adamc@137 1603
adamc@311 1604 uw_check_heap(ctx, strlen(s) * 5 + 1);
adamc@137 1605
adamc@666 1606 for (r = s2 = ctx->heap.front; *s; s++) {
adamc@137 1607 char c = *s;
adamc@137 1608
adamc@137 1609 switch (c) {
adamc@137 1610 case '<':
adamc@137 1611 strcpy(s2, "&lt;");
adamc@137 1612 s2 += 4;
adamc@137 1613 break;
adamc@137 1614 case '&':
adamc@137 1615 strcpy(s2, "&amp;");
adamc@137 1616 s2 += 5;
adamc@137 1617 break;
adamc@137 1618 default:
adamc@137 1619 if (isprint(c))
adamc@137 1620 *s2++ = c;
adamc@137 1621 else {
adamc@137 1622 int len2;
adamc@137 1623 sprintf(s2, "&#%d;%n", c, &len2);
adamc@137 1624 s2 += len2;
adamc@137 1625 }
adamc@137 1626 }
adamc@137 1627 }
adamc@137 1628
adamc@137 1629 *s2++ = 0;
adamc@666 1630 ctx->heap.front = s2;
adamc@137 1631 return r;
adamc@135 1632 }
adamc@135 1633
adamc@428 1634 uw_unit uw_Basis_htmlifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@321 1635 uw_check(ctx, strlen(s) * 6);
adamc@135 1636
adamc@135 1637 for (; *s; s++) {
adamc@135 1638 char c = *s;
adamc@135 1639
adamc@135 1640 switch (c) {
adamc@135 1641 case '<':
adamc@311 1642 uw_write_unsafe(ctx, "&lt;");
adamc@135 1643 break;
adamc@135 1644 case '&':
adamc@311 1645 uw_write_unsafe(ctx, "&amp;");
adamc@135 1646 break;
adamc@135 1647 default:
adamc@135 1648 if (isprint(c))
adamc@311 1649 uw_writec_unsafe(ctx, c);
adamc@135 1650 else {
adamc@311 1651 uw_write_unsafe(ctx, "&#");
adamc@311 1652 uw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@311 1653 uw_writec_unsafe(ctx, ';');
adamc@135 1654 }
adamc@135 1655 }
adamc@135 1656 }
adamc@428 1657
adamc@428 1658 return uw_unit_v;
adamc@135 1659 }
adamc@180 1660
adamc@311 1661 uw_Basis_string uw_Basis_htmlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 1662 if (b == uw_Basis_False)
adamc@286 1663 return "False";
adamc@286 1664 else
adamc@286 1665 return "True";
adamc@286 1666 }
adamc@286 1667
adamc@428 1668 uw_unit uw_Basis_htmlifyBool_w(uw_context ctx, uw_Basis_bool b) {
adamc@311 1669 if (b == uw_Basis_False) {
adamc@311 1670 uw_check(ctx, 6);
adamc@666 1671 strcpy(ctx->page.front, "False");
adamc@666 1672 ctx->page.front += 5;
adamc@286 1673 } else {
adamc@311 1674 uw_check(ctx, 5);
adamc@666 1675 strcpy(ctx->page.front, "True");
adamc@666 1676 ctx->page.front += 4;
adamc@286 1677 }
adamc@428 1678
adamc@428 1679 return uw_unit_v;
adamc@286 1680 }
adamc@286 1681
adamc@436 1682 #define TIME_FMT "%x %X"
adamc@438 1683 #define TIME_FMT_PG "%Y-%m-%d %T"
adamc@436 1684
adamc@436 1685 uw_Basis_string uw_Basis_htmlifyTime(uw_context ctx, uw_Basis_time t) {
adamc@436 1686 size_t len;
adamc@436 1687 char *r;
adamc@436 1688 struct tm stm;
adamc@436 1689
adamc@436 1690 if (localtime_r(&t, &stm)) {
adamc@436 1691 uw_check_heap(ctx, TIMES_MAX);
adamc@666 1692 r = ctx->heap.front;
adamc@436 1693 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@666 1694 ctx->heap.front += len+1;
adamc@436 1695 return r;
adamc@436 1696 } else
adamc@436 1697 return "<i>Invalid time</i>";
adamc@436 1698 }
adamc@436 1699
adamc@436 1700 uw_unit uw_Basis_htmlifyTime_w(uw_context ctx, uw_Basis_time t) {
adamc@436 1701 size_t len;
adamc@436 1702 char *r;
adamc@436 1703 struct tm stm;
adamc@436 1704
adamc@436 1705 if (localtime_r(&t, &stm)) {
adamc@436 1706 uw_check(ctx, TIMES_MAX);
adamc@666 1707 r = ctx->page.front;
adamc@436 1708 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@666 1709 ctx->page.front += len;
adamc@436 1710 } else {
adamc@436 1711 uw_check(ctx, 20);
adamc@666 1712 strcpy(ctx->page.front, "<i>Invalid time</i>");
adamc@666 1713 ctx->page.front += 19;
adamc@436 1714 }
adamc@436 1715
adamc@436 1716 return uw_unit_v;
adamc@436 1717 }
adamc@436 1718
adamc@311 1719 uw_Basis_string uw_Basis_strcat(uw_context ctx, uw_Basis_string s1, uw_Basis_string s2) {
adamc@180 1720 int len = strlen(s1) + strlen(s2) + 1;
adamc@180 1721 char *s;
adamc@180 1722
adamc@311 1723 uw_check_heap(ctx, len);
adamc@180 1724
adamc@666 1725 s = ctx->heap.front;
adamc@180 1726
adamc@180 1727 strcpy(s, s1);
adamc@180 1728 strcat(s, s2);
adamc@666 1729 ctx->heap.front += len;
adamc@180 1730
adamc@180 1731 return s;
adamc@180 1732 }
adamc@278 1733
adamc@737 1734 uw_Basis_string uw_strdup(uw_context ctx, uw_Basis_string s1) {
adamc@278 1735 int len = strlen(s1) + 1;
adamc@278 1736 char *s;
adamc@278 1737
adamc@311 1738 uw_check_heap(ctx, len);
adamc@278 1739
adamc@666 1740 s = ctx->heap.front;
adamc@278 1741
adamc@278 1742 strcpy(s, s1);
adamc@666 1743 ctx->heap.front += len;
adamc@278 1744
adamc@278 1745 return s;
adamc@278 1746 }
adamc@280 1747
adamc@737 1748 uw_Basis_string uw_maybe_strdup(uw_context ctx, uw_Basis_string s1) {
adamc@493 1749 if (s1)
adamc@737 1750 return uw_strdup(ctx, s1);
adamc@493 1751 else
adamc@493 1752 return NULL;
adamc@493 1753 }
adamc@493 1754
adamc@742 1755 char *uw_memdup(uw_context ctx, const char *p, size_t len) {
adamc@742 1756 char *r = uw_malloc(ctx, len);
adamc@742 1757 memcpy(r, p, len);
adamc@742 1758 return r;
adamc@742 1759 }
adamc@280 1760
adamc@311 1761 char *uw_Basis_sqlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@281 1762 int len;
adamc@281 1763 char *r;
adamc@281 1764
adamc@311 1765 uw_check_heap(ctx, INTS_MAX + 6);
adamc@666 1766 r = ctx->heap.front;
adamc@281 1767 sprintf(r, "%lld::int8%n", n, &len);
adamc@666 1768 ctx->heap.front += len+1;
adamc@281 1769 return r;
adamc@281 1770 }
adamc@281 1771
adamc@467 1772 char *uw_Basis_sqlifyIntN(uw_context ctx, uw_Basis_int *n) {
adamc@467 1773 if (n == NULL)
adamc@467 1774 return "NULL";
adamc@467 1775 else
adamc@467 1776 return uw_Basis_sqlifyInt(ctx, *n);
adamc@467 1777 }
adamc@467 1778
adamc@311 1779 char *uw_Basis_sqlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@281 1780 int len;
adamc@281 1781 char *r;
adamc@281 1782
adamc@311 1783 uw_check_heap(ctx, FLOATS_MAX + 8);
adamc@666 1784 r = ctx->heap.front;
adamc@281 1785 sprintf(r, "%g::float8%n", n, &len);
adamc@666 1786 ctx->heap.front += len+1;
adamc@281 1787 return r;
adamc@281 1788 }
adamc@281 1789
adamc@467 1790 char *uw_Basis_sqlifyFloatN(uw_context ctx, uw_Basis_float *n) {
adamc@467 1791 if (n == NULL)
adamc@467 1792 return "NULL";
adamc@467 1793 else
adamc@467 1794 return uw_Basis_sqlifyFloat(ctx, *n);
adamc@467 1795 }
adamc@467 1796
adamc@281 1797
adamc@311 1798 uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) {
adamc@280 1799 char *r, *s2;
adamc@280 1800
adamc@311 1801 uw_check_heap(ctx, strlen(s) * 2 + 10);
adamc@280 1802
adamc@666 1803 r = s2 = ctx->heap.front;
adamc@280 1804 *s2++ = 'E';
adamc@280 1805 *s2++ = '\'';
adamc@280 1806
adamc@280 1807 for (; *s; s++) {
adamc@280 1808 char c = *s;
adamc@280 1809
adamc@280 1810 switch (c) {
adamc@280 1811 case '\'':
adamc@280 1812 strcpy(s2, "\\'");
adamc@280 1813 s2 += 2;
adamc@280 1814 break;
adamc@280 1815 case '\\':
adamc@280 1816 strcpy(s2, "\\\\");
adamc@280 1817 s2 += 2;
adamc@280 1818 break;
adamc@280 1819 default:
adamc@280 1820 if (isprint(c))
adamc@280 1821 *s2++ = c;
adamc@280 1822 else {
adamc@737 1823 sprintf(s2, "\\%03o", c);
adamc@280 1824 s2 += 4;
adamc@280 1825 }
adamc@280 1826 }
adamc@280 1827 }
adamc@280 1828
adamc@281 1829 strcpy(s2, "'::text");
adamc@666 1830 ctx->heap.front = s2 + 8;
adamc@280 1831 return r;
adamc@280 1832 }
adamc@281 1833
adamc@737 1834 uw_Basis_string uw_Basis_sqlifyBlob(uw_context ctx, uw_Basis_blob b) {
adamc@737 1835 char *r, *s2;
adamc@737 1836 size_t i;
adamc@737 1837
adamc@737 1838 uw_check_heap(ctx, b.size * 5 + 11);
adamc@737 1839
adamc@737 1840 r = s2 = ctx->heap.front;
adamc@737 1841 *s2++ = 'E';
adamc@737 1842 *s2++ = '\'';
adamc@737 1843
adamc@737 1844 for (i = 0; i < b.size; ++i) {
adamc@737 1845 char c = b.data[i];
adamc@737 1846
adamc@737 1847 switch (c) {
adamc@737 1848 case '\'':
adamc@737 1849 strcpy(s2, "\\'");
adamc@737 1850 s2 += 2;
adamc@737 1851 break;
adamc@737 1852 case '\\':
adamc@737 1853 strcpy(s2, "\\\\\\\\");
adamc@737 1854 s2 += 4;
adamc@737 1855 break;
adamc@737 1856 default:
adamc@737 1857 if (isprint(c))
adamc@737 1858 *s2++ = c;
adamc@737 1859 else {
adamc@737 1860 sprintf(s2, "\\\\%03o", c);
adamc@737 1861 s2 += 5;
adamc@737 1862 }
adamc@737 1863 }
adamc@737 1864 }
adamc@737 1865
adamc@737 1866 strcpy(s2, "'::bytea");
adamc@737 1867 ctx->heap.front = s2 + 9;
adamc@737 1868 return r;
adamc@737 1869 }
adamc@737 1870
adamc@682 1871 char *uw_Basis_sqlifyChannel(uw_context ctx, uw_Basis_channel chn) {
adamc@682 1872 int len;
adamc@682 1873 char *r;
adamc@682 1874 unsigned long long combo = ((unsigned long long)chn.cli << 32) | chn.chn;
adamc@682 1875
adamc@682 1876 uw_check_heap(ctx, INTS_MAX + 7);
adamc@682 1877 r = ctx->heap.front;
adamc@682 1878 sprintf(r, "%lld::int8%n", combo, &len);
adamc@682 1879 ctx->heap.front += len+1;
adamc@682 1880 return r;
adamc@682 1881 }
adamc@682 1882
adamc@682 1883 char *uw_Basis_attrifyChannel(uw_context ctx, uw_Basis_channel chn) {
adamc@682 1884 int len;
adamc@682 1885 char *r;
adamc@682 1886 unsigned long long combo = ((unsigned long long)chn.cli << 32) | chn.chn;
adamc@682 1887
adamc@682 1888 uw_check_heap(ctx, INTS_MAX + 1);
adamc@682 1889 r = ctx->heap.front;
adamc@682 1890 sprintf(r, "%lld%n", combo, &len);
adamc@682 1891 ctx->heap.front += len+1;
adamc@682 1892 return r;
adamc@682 1893 }
adamc@682 1894
adamc@682 1895 char *uw_Basis_sqlifyClient(uw_context ctx, uw_Basis_client cli) {
adamc@682 1896 int len;
adamc@682 1897 char *r;
adamc@682 1898
adamc@682 1899 uw_check_heap(ctx, INTS_MAX + 7);
adamc@682 1900 r = ctx->heap.front;
adamc@682 1901 sprintf(r, "%u::int4%n", cli, &len);
adamc@682 1902 ctx->heap.front += len+1;
adamc@682 1903 return r;
adamc@682 1904 }
adamc@682 1905
adamc@682 1906 char *uw_Basis_attrifyClient(uw_context ctx, uw_Basis_client cli) {
adamc@682 1907 int len;
adamc@682 1908 char *r;
adamc@682 1909
adamc@682 1910 uw_check_heap(ctx, INTS_MAX + 1);
adamc@682 1911 r = ctx->heap.front;
adamc@682 1912 sprintf(r, "%u%n", cli, &len);
adamc@682 1913 ctx->heap.front += len+1;
adamc@682 1914 return r;
adamc@682 1915 }
adamc@682 1916
adamc@467 1917 uw_Basis_string uw_Basis_sqlifyStringN(uw_context ctx, uw_Basis_string s) {
adamc@467 1918 if (s == NULL)
adamc@467 1919 return "NULL";
adamc@467 1920 else
adamc@467 1921 return uw_Basis_sqlifyString(ctx, s);
adamc@467 1922 }
adamc@467 1923
adamc@311 1924 char *uw_Basis_sqlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 1925 if (b == uw_Basis_False)
adamc@281 1926 return "FALSE";
adamc@281 1927 else
adamc@281 1928 return "TRUE";
adamc@281 1929 }
adamc@282 1930
adamc@467 1931 char *uw_Basis_sqlifyBoolN(uw_context ctx, uw_Basis_bool *b) {
adamc@467 1932 if (b == NULL)
adamc@467 1933 return "NULL";
adamc@467 1934 else
adamc@467 1935 return uw_Basis_sqlifyBool(ctx, *b);
adamc@467 1936 }
adamc@467 1937
adamc@439 1938 char *uw_Basis_sqlifyTime(uw_context ctx, uw_Basis_time t) {
adamc@439 1939 size_t len;
adamc@682 1940 char *r, *s;
adamc@682 1941 struct tm stm;
adamc@682 1942
adamc@682 1943 if (localtime_r(&t, &stm)) {
adamc@682 1944 s = uw_malloc(ctx, TIMES_MAX);
adamc@682 1945 len = strftime(s, TIMES_MAX, TIME_FMT, &stm);
adamc@682 1946 r = uw_malloc(ctx, len + 14);
adamc@682 1947 sprintf(r, "'%s'::timestamp", s);
adamc@682 1948 return r;
adamc@682 1949 } else
adamc@682 1950 return "<Invalid time>";
adamc@682 1951 }
adamc@682 1952
adamc@682 1953 char *uw_Basis_attrifyTime(uw_context ctx, uw_Basis_time t) {
adamc@682 1954 size_t len;
adamc@439 1955 char *r;
adamc@439 1956 struct tm stm;
adamc@439 1957
adamc@439 1958 if (localtime_r(&t, &stm)) {
adamc@439 1959 uw_check_heap(ctx, TIMES_MAX);
adamc@666 1960 r = ctx->heap.front;
adamc@439 1961 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@666 1962 ctx->heap.front += len+1;
adamc@439 1963 return r;
adamc@439 1964 } else
adamc@439 1965 return "<Invalid time>";
adamc@439 1966 }
adamc@439 1967
adamc@467 1968 char *uw_Basis_sqlifyTimeN(uw_context ctx, uw_Basis_time *t) {
adamc@467 1969 if (t == NULL)
adamc@467 1970 return "NULL";
adamc@467 1971 else
adamc@467 1972 return uw_Basis_sqlifyTime(ctx, *t);
adamc@467 1973 }
adamc@467 1974
adamc@311 1975 char *uw_Basis_ensqlBool(uw_Basis_bool b) {
adamc@311 1976 static uw_Basis_int true = 1;
adamc@311 1977 static uw_Basis_int false = 0;
adamc@282 1978
adamc@311 1979 if (b == uw_Basis_False)
adamc@282 1980 return (char *)&false;
adamc@282 1981 else
adamc@282 1982 return (char *)&true;
adamc@282 1983 }
adamc@284 1984
adamc@311 1985 uw_Basis_string uw_Basis_intToString(uw_context ctx, uw_Basis_int n) {
adamc@284 1986 int len;
adamc@284 1987 char *r;
adamc@284 1988
adamc@311 1989 uw_check_heap(ctx, INTS_MAX);
adamc@666 1990 r = ctx->heap.front;
adamc@284 1991 sprintf(r, "%lld%n", n, &len);
adamc@666 1992 ctx->heap.front += len+1;
adamc@284 1993 return r;
adamc@284 1994 }
adamc@285 1995
adamc@311 1996 uw_Basis_string uw_Basis_floatToString(uw_context ctx, uw_Basis_float n) {
adamc@285 1997 int len;
adamc@285 1998 char *r;
adamc@285 1999
adamc@311 2000 uw_check_heap(ctx, FLOATS_MAX);
adamc@666 2001 r = ctx->heap.front;
adamc@285 2002 sprintf(r, "%g%n", n, &len);
adamc@666 2003 ctx->heap.front += len+1;
adamc@285 2004 return r;
adamc@285 2005 }
adamc@285 2006
adamc@311 2007 uw_Basis_string uw_Basis_boolToString(uw_context ctx, uw_Basis_bool b) {
adamc@311 2008 if (b == uw_Basis_False)
adamc@285 2009 return "False";
adamc@285 2010 else
adamc@285 2011 return "True";
adamc@285 2012 }
adamc@288 2013
adamc@436 2014 uw_Basis_string uw_Basis_timeToString(uw_context ctx, uw_Basis_time t) {
adamc@436 2015 size_t len;
adamc@436 2016 char *r;
adamc@436 2017 struct tm stm;
adamc@436 2018
adamc@436 2019 if (localtime_r(&t, &stm)) {
adamc@436 2020 uw_check_heap(ctx, TIMES_MAX);
adamc@666 2021 r = ctx->heap.front;
adamc@436 2022 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@666 2023 ctx->heap.front += len+1;
adamc@436 2024 return r;
adamc@436 2025 } else
adamc@436 2026 return "<Invalid time>";
adamc@436 2027 }
adamc@288 2028
adamc@311 2029 uw_Basis_int *uw_Basis_stringToInt(uw_context ctx, uw_Basis_string s) {
adamc@288 2030 char *endptr;
adamc@311 2031 uw_Basis_int n = strtoll(s, &endptr, 10);
adamc@288 2032
adamc@288 2033 if (*s != '\0' && *endptr == '\0') {
adamc@311 2034 uw_Basis_int *r = uw_malloc(ctx, sizeof(uw_Basis_int));
adamc@288 2035 *r = n;
adamc@288 2036 return r;
adamc@288 2037 } else
adamc@288 2038 return NULL;
adamc@288 2039 }
adamc@289 2040
adamc@311 2041 uw_Basis_float *uw_Basis_stringToFloat(uw_context ctx, uw_Basis_string s) {
adamc@289 2042 char *endptr;
adamc@311 2043 uw_Basis_float n = strtod(s, &endptr);
adamc@289 2044
adamc@289 2045 if (*s != '\0' && *endptr == '\0') {
adamc@311 2046 uw_Basis_float *r = uw_malloc(ctx, sizeof(uw_Basis_float));
adamc@289 2047 *r = n;
adamc@289 2048 return r;
adamc@289 2049 } else
adamc@289 2050 return NULL;
adamc@289 2051 }
adamc@289 2052
adamc@311 2053 uw_Basis_bool *uw_Basis_stringToBool(uw_context ctx, uw_Basis_string s) {
adamc@311 2054 static uw_Basis_bool true = uw_Basis_True;
adamc@311 2055 static uw_Basis_bool false = uw_Basis_False;
adamc@289 2056
adamc@289 2057 if (!strcasecmp (s, "True"))
adamc@289 2058 return &true;
adamc@289 2059 else if (!strcasecmp (s, "False"))
adamc@289 2060 return &false;
adamc@289 2061 else
adamc@289 2062 return NULL;
adamc@289 2063 }
adamc@292 2064
adamc@436 2065 uw_Basis_time *uw_Basis_stringToTime(uw_context ctx, uw_Basis_string s) {
adamc@438 2066 char *dot = strchr(s, '.'), *end = strchr(s, 0);
adamc@436 2067 struct tm stm;
adamc@436 2068
adamc@439 2069 if (dot) {
adamc@439 2070 *dot = 0;
adamc@439 2071 if (strptime(s, TIME_FMT_PG, &stm) == end) {
adamc@439 2072 *dot = '.';
adamc@439 2073 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 2074 *r = mktime(&stm);
adamc@439 2075 return r;
adamc@439 2076 }
adamc@439 2077 else {
adamc@439 2078 *dot = '.';
adamc@439 2079 return NULL;
adamc@439 2080 }
adamc@436 2081 }
adamc@439 2082 else {
adamc@439 2083 if (strptime(s, TIME_FMT_PG, &stm) == end) {
adamc@439 2084 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 2085 *r = mktime(&stm);
adamc@439 2086 return r;
adamc@439 2087 }
adamc@439 2088 else if (strptime(s, TIME_FMT, &stm) == end) {
adamc@439 2089 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 2090 *r = mktime(&stm);
adamc@439 2091 return r;
adamc@439 2092 }
adamc@439 2093 else
adamc@439 2094 return NULL;
adamc@439 2095 }
adamc@436 2096 }
adamc@436 2097
adamc@311 2098 uw_Basis_int uw_Basis_stringToInt_error(uw_context ctx, uw_Basis_string s) {
adamc@292 2099 char *endptr;
adamc@311 2100 uw_Basis_int n = strtoll(s, &endptr, 10);
adamc@292 2101
adamc@292 2102 if (*s != '\0' && *endptr == '\0')
adamc@292 2103 return n;
adamc@292 2104 else
adamc@311 2105 uw_error(ctx, FATAL, "Can't parse int: %s", s);
adamc@292 2106 }
adamc@293 2107
adamc@682 2108 #include <errno.h>
adamc@682 2109
adamc@678 2110 uw_Basis_channel uw_Basis_stringToChannel_error(uw_context ctx, uw_Basis_string s) {
adamc@682 2111 unsigned long long n;
adamc@682 2112
adamc@682 2113 if (sscanf(s, "%llu", &n) < 1)
adamc@682 2114 uw_error(ctx, FATAL, "Can't parse channel: %s", s);
adamc@682 2115 else {
adamc@682 2116 uw_Basis_channel ch = {n >> 32, n & ((1ull << 32) - 1)};
adamc@682 2117 return ch;
adamc@682 2118 }
adamc@682 2119 }
adamc@682 2120
adamc@682 2121 uw_Basis_client uw_Basis_stringToClient_error(uw_context ctx, uw_Basis_string s) {
adamc@678 2122 char *endptr;
adamc@682 2123 unsigned long n = strtoul(s, &endptr, 10);
adamc@678 2124
adamc@678 2125 if (*s != '\0' && *endptr == '\0')
adamc@678 2126 return n;
adamc@678 2127 else
adamc@682 2128 uw_error(ctx, FATAL, "Can't parse client: %s", s);
adamc@678 2129 }
adamc@678 2130
adamc@311 2131 uw_Basis_float uw_Basis_stringToFloat_error(uw_context ctx, uw_Basis_string s) {
adamc@293 2132 char *endptr;
adamc@311 2133 uw_Basis_float n = strtod(s, &endptr);
adamc@293 2134
adamc@293 2135 if (*s != '\0' && *endptr == '\0')
adamc@293 2136 return n;
adamc@293 2137 else
adamc@311 2138 uw_error(ctx, FATAL, "Can't parse float: %s", s);
adamc@293 2139 }
adamc@293 2140
adamc@311 2141 uw_Basis_bool uw_Basis_stringToBool_error(uw_context ctx, uw_Basis_string s) {
adamc@296 2142 if (!strcasecmp(s, "T") || !strcasecmp (s, "True"))
adamc@311 2143 return uw_Basis_True;
adamc@296 2144 else if (!strcasecmp(s, "F") || !strcasecmp (s, "False"))
adamc@311 2145 return uw_Basis_False;
adamc@293 2146 else
adamc@311 2147 uw_error(ctx, FATAL, "Can't parse bool: %s", s);
adamc@293 2148 }
adamc@436 2149
adamc@436 2150 uw_Basis_time uw_Basis_stringToTime_error(uw_context ctx, uw_Basis_string s) {
adamc@438 2151 char *dot = strchr(s, '.'), *end = strchr(s, 0);
adamc@436 2152 struct tm stm = {};
adamc@436 2153
adamc@438 2154 if (dot) {
adamc@438 2155 *dot = 0;
adamc@438 2156 if (strptime(s, TIME_FMT_PG, &stm)) {
adamc@438 2157 *dot = '.';
adamc@438 2158 return mktime(&stm);
adamc@438 2159 }
adamc@438 2160 else {
adamc@438 2161 *dot = '.';
adamc@438 2162 uw_error(ctx, FATAL, "Can't parse time: %s", s);
adamc@438 2163 }
adamc@438 2164 }
adamc@438 2165 else {
adamc@439 2166 if (strptime(s, TIME_FMT_PG, &stm) == end)
adamc@439 2167 return mktime(&stm);
adamc@439 2168 else if (strptime(s, TIME_FMT, &stm) == end)
adamc@438 2169 return mktime(&stm);
adamc@438 2170 else
adamc@438 2171 uw_error(ctx, FATAL, "Can't parse time: %s", s);
adamc@438 2172 }
adamc@436 2173 }
adamc@457 2174
adamc@742 2175 uw_Basis_blob uw_Basis_stringToBlob_error(uw_context ctx, uw_Basis_string s, size_t len) {
adamc@742 2176 char *r = ctx->heap.front;
adamc@742 2177 uw_Basis_blob b = {len, r};
adamc@742 2178
adamc@742 2179 uw_check_heap(ctx, len);
adamc@742 2180
adamc@742 2181 while (*s) {
adamc@742 2182 if (s[0] == '\\') {
adamc@742 2183 if (s[1] == '\\') {
adamc@742 2184 *r++ = '\\';
adamc@742 2185 s += 2;
adamc@742 2186 } else if (isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) {
adamc@742 2187 *r++ = (s[1] - '0') * 8 * 8 + ((s[2] - '0') * 8) + (s[3] - '0');
adamc@742 2188 s += 4;
adamc@742 2189 }
adamc@742 2190 else {
adamc@742 2191 *r++ = '\\';
adamc@742 2192 ++s;
adamc@742 2193 }
adamc@742 2194 } else {
adamc@742 2195 *r++ = s[0];
adamc@742 2196 ++s;
adamc@742 2197 }
adamc@742 2198 }
adamc@742 2199
adamc@742 2200 b.size = r - ctx->heap.front;
adamc@742 2201 ctx->heap.front = r;
adamc@742 2202 return b;
adamc@742 2203 }
adamc@742 2204
adamc@463 2205 uw_Basis_string uw_Basis_get_cookie(uw_context ctx, uw_Basis_string c) {
adamc@463 2206 int len = strlen(c);
adamc@666 2207 char *s = ctx->headers, *p = ctx->outHeaders.start;
adamc@474 2208
adamc@474 2209 while (p = strstr(p, "\nSet-Cookie: ")) {
adamc@474 2210 char *p2;
adamc@474 2211 p += 13;
adamc@474 2212 p2 = strchr(p, '=');
adamc@474 2213
adamc@474 2214 if (p2) {
adamc@474 2215 size_t sz = strcspn(p2+1, ";\r\n");
adamc@474 2216
adamc@474 2217 if (!strncasecmp(p, c, p2 - p)) {
adamc@474 2218 char *ret = uw_malloc(ctx, sz + 1);
adamc@474 2219 memcpy(ret, p2+1, sz);
adamc@474 2220 ret[sz] = 0;
adamc@474 2221 return ret;
adamc@474 2222 }
adamc@474 2223 }
adamc@474 2224 }
adamc@463 2225
adamc@463 2226 while (p = strchr(s, ':')) {
adamc@476 2227 if (!strncasecmp(s, "Cookie: ", 8)) {
adamc@476 2228 p += 2;
adamc@476 2229 while (1) {
adamc@476 2230 if (!strncmp(p, c, len)
adamc@476 2231 && p + len < ctx->headers_end && p[len] == '=')
adamc@476 2232 return p + 1 + len;
adamc@476 2233 else if (p = strchr(p, ';'))
adamc@476 2234 p += 2;
adamc@476 2235 else if ((s = strchr(s, 0)) && s < ctx->headers_end) {
adamc@476 2236 s += 2;
adamc@476 2237 break;
adamc@476 2238 }
adamc@476 2239 else
adamc@476 2240 return NULL;
adamc@476 2241 }
adamc@463 2242 } else {
adamc@463 2243 if ((s = strchr(p, 0)) && s < ctx->headers_end)
adamc@463 2244 s += 2;
adamc@463 2245 else
adamc@463 2246 return NULL;
adamc@463 2247 }
adamc@463 2248 }
adamc@457 2249 }
adamc@462 2250
adamc@466 2251 uw_unit uw_Basis_set_cookie(uw_context ctx, uw_Basis_string prefix, uw_Basis_string c, uw_Basis_string v) {
adamc@462 2252 uw_write_header(ctx, "Set-Cookie: ");
adamc@462 2253 uw_write_header(ctx, c);
adamc@462 2254 uw_write_header(ctx, "=");
adamc@462 2255 uw_write_header(ctx, v);
adamc@466 2256 uw_write_header(ctx, "; path=");
adamc@466 2257 uw_write_header(ctx, prefix);
adamc@462 2258 uw_write_header(ctx, "\r\n");
adamc@462 2259
adamc@462 2260 return uw_unit_v;
adamc@462 2261 }
adamc@667 2262
adamc@682 2263 static delta *allocate_delta(uw_context ctx, unsigned client) {
adamc@682 2264 unsigned i;
adamc@682 2265 delta *d;
adamc@671 2266
adamc@682 2267 for (i = 0; i < ctx->used_deltas; ++i)
adamc@682 2268 if (ctx->deltas[i].client == client)
adamc@682 2269 return &ctx->deltas[i];
adamc@671 2270
adamc@682 2271 if (ctx->used_deltas >= ctx->n_deltas) {
adamc@682 2272 ctx->deltas = realloc(ctx->deltas, sizeof(delta) * ++ctx->n_deltas);
adamc@682 2273 buf_init(&ctx->deltas[ctx->n_deltas-1].msgs, 0);
adamc@682 2274 }
adamc@671 2275
adamc@682 2276 d = &ctx->deltas[ctx->used_deltas++];
adamc@682 2277 d->client = client;
adamc@682 2278 buf_reset(&d->msgs);
adamc@682 2279 return d;
adamc@671 2280 }
adamc@671 2281
adamc@668 2282 uw_Basis_channel uw_Basis_new_channel(uw_context ctx, uw_unit u) {
adamc@682 2283 if (ctx->client == NULL)
adamc@682 2284 uw_error(ctx, FATAL, "Attempt to create channel on request not associated with a persistent connection");
adamc@671 2285
adamc@682 2286 return new_channel(ctx->client);
adamc@668 2287 }
adamc@668 2288
adamc@668 2289 uw_unit uw_Basis_send(uw_context ctx, uw_Basis_channel chn, uw_Basis_string msg) {
adamc@682 2290 delta *d = allocate_delta(ctx, chn.cli);
adamc@682 2291 size_t len;
adamc@682 2292 int preLen;
adamc@682 2293 char pre[INTS_MAX + 2];
adamc@668 2294
adamc@682 2295 len = strlen(msg);
adamc@682 2296
adamc@682 2297 sprintf(pre, "%u\n%n", chn.chn, &preLen);
adamc@682 2298
adamc@682 2299 buf_append(&d->msgs, pre, preLen);
adamc@682 2300 buf_append(&d->msgs, msg, len);
adamc@682 2301 buf_append(&d->msgs, "\n", 1);
adamc@671 2302
adamc@671 2303 return uw_unit_v;
adamc@668 2304 }
adamc@671 2305
adamc@671 2306 int uw_db_commit(uw_context);
adamc@671 2307 int uw_db_rollback(uw_context);
adamc@671 2308
adamc@671 2309 void uw_commit(uw_context ctx) {
adamc@682 2310 unsigned i;
adamc@671 2311
adamc@700 2312 if (uw_db_commit(ctx))
adamc@671 2313 uw_error(ctx, FATAL, "Error running SQL COMMIT");
adamc@682 2314
adamc@682 2315 for (i = 0; i < ctx->used_deltas; ++i) {
adamc@682 2316 delta *d = &ctx->deltas[i];
adamc@682 2317 client *c = find_client(d->client);
adamc@682 2318
adamc@682 2319 assert (c != NULL && c->mode == USED);
adamc@682 2320
adamc@685 2321 client_send(c, &d->msgs);
adamc@682 2322 }
adamc@682 2323
adamc@682 2324 if (ctx->client)
adamc@685 2325 release_client(ctx->client);
adamc@671 2326 }
adamc@671 2327
adamc@671 2328 int uw_rollback(uw_context ctx) {
adamc@682 2329 if (ctx->client)
adamc@685 2330 release_client(ctx->client);
adamc@671 2331
adamc@671 2332 return uw_db_rollback(ctx);
adamc@671 2333 }
adamc@683 2334
adamc@683 2335
adamc@683 2336 // "Garbage collection"
adamc@683 2337
adamc@683 2338 void uw_expunger(uw_context ctx, uw_Basis_client cli);
adamc@683 2339
adamc@683 2340 static failure_kind uw_expunge(uw_context ctx, uw_Basis_client cli) {
adamc@683 2341 int r = setjmp(ctx->jmp_buf);
adamc@683 2342
adamc@684 2343 if (r == 0) {
adamc@684 2344 if (uw_db_begin(ctx))
adamc@684 2345 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@683 2346 uw_expunger(ctx, cli);
adamc@684 2347 if (uw_db_commit(ctx))
adamc@684 2348 uw_error(ctx, FATAL, "Error running SQL COMMIT");
adamc@684 2349 }
adamc@683 2350
adamc@683 2351 return r;
adamc@683 2352 }
adamc@683 2353
adamc@683 2354 void uw_prune_clients(uw_context ctx) {
adamc@683 2355 client *c, *next, *prev = NULL;
adamc@683 2356 time_t cutoff;
adamc@683 2357
adamc@683 2358 cutoff = time(NULL) - uw_timeout;
adamc@683 2359
adamc@683 2360 pthread_mutex_lock(&clients_mutex);
adamc@683 2361
adamc@683 2362 for (c = clients_used; c; c = next) {
adamc@683 2363 next = c->next;
adamc@683 2364 pthread_mutex_lock(&c->lock);
adamc@685 2365 if (c->last_contact < cutoff && c->refcount == 0) {
adamc@683 2366 failure_kind fk = UNLIMITED_RETRY;
adamc@683 2367 if (prev)
adamc@683 2368 prev->next = next;
adamc@683 2369 else
adamc@683 2370 clients_used = next;
adamc@684 2371 uw_reset(ctx);
adamc@683 2372 while (fk == UNLIMITED_RETRY) {
adamc@683 2373 fk = uw_expunge(ctx, c->id);
adamc@684 2374 if (fk == UNLIMITED_RETRY) {
adamc@684 2375 uw_db_rollback(ctx);
adamc@684 2376 printf("Unlimited retry during expunge: %s\n", uw_error_message(ctx));
adamc@683 2377 }
adamc@683 2378 }
adamc@684 2379 if (fk == SUCCESS)
adamc@684 2380 free_client(c);
adamc@684 2381 else {
adamc@684 2382 uw_db_rollback(ctx);
adamc@683 2383 printf("Expunge blocked by error: %s\n", uw_error_message(ctx));
adamc@684 2384 }
adamc@683 2385 }
adamc@683 2386 else
adamc@683 2387 prev = c;
adamc@683 2388 pthread_mutex_unlock(&c->lock);
adamc@683 2389 }
adamc@683 2390
adamc@683 2391 pthread_mutex_unlock(&clients_mutex);
adamc@683 2392 }
adamc@687 2393
adamc@687 2394 void uw_initializer(uw_context ctx);
adamc@687 2395
adamc@687 2396 failure_kind uw_initialize(uw_context ctx) {
adamc@687 2397 int r = setjmp(ctx->jmp_buf);
adamc@687 2398
adamc@687 2399 if (r == 0) {
adamc@687 2400 if (uw_db_begin(ctx))
adamc@687 2401 uw_error(ctx, FATAL, "Error running SQL BEGIN");
adamc@687 2402 uw_initializer(ctx);
adamc@687 2403 if (uw_db_commit(ctx))
adamc@687 2404 uw_error(ctx, FATAL, "Error running SQL COMMIT");
adamc@687 2405 }
adamc@687 2406
adamc@687 2407 return r;
adamc@687 2408 }
adamc@717 2409
adamc@717 2410 uw_Basis_string uw_Basis_bless(uw_context ctx, uw_Basis_string s) {
adamc@717 2411 return s;
adamc@717 2412 }
adamc@734 2413
adamc@741 2414 uw_Basis_string uw_Basis_blessMime(uw_context ctx, uw_Basis_string s) {
adamc@741 2415 char *s2;
adamc@741 2416
adamc@741 2417 for (s2 = s; *s2; ++s2)
adamc@741 2418 if (!isalnum(*s2) && *s2 != '/' && *s2 != '-' && *s2 != '.')
adamc@741 2419 uw_error(ctx, FATAL, "MIME type \"%s\" contains invalid character %c\n", s, *s2);
adamc@741 2420
adamc@741 2421 return s;
adamc@741 2422 }
adamc@741 2423
adamc@734 2424 uw_Basis_string uw_unnull(uw_Basis_string s) {
adamc@734 2425 return s ? s : "";
adamc@734 2426 }
adamc@734 2427
adamc@734 2428 extern int uw_hash_blocksize;
adamc@734 2429
adamc@734 2430 uw_Basis_string uw_Basis_makeSigString(uw_context ctx, uw_Basis_string sig) {
adamc@734 2431 uw_Basis_string r = uw_malloc(ctx, 2 * uw_hash_blocksize + 1);
adamc@734 2432 int i;
adamc@734 2433
adamc@734 2434 for (i = 0; i < uw_hash_blocksize; ++i)
adamc@734 2435 sprintf(&r[2*i], "%.02X", ((unsigned char *)sig)[i]);
adamc@734 2436
adamc@734 2437 return r;
adamc@734 2438 }
adamc@734 2439
adamc@734 2440 uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) {
adamc@734 2441 return uw_cookie_sig(ctx);
adamc@734 2442 }
adamc@737 2443
adamc@737 2444 uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) {
adamc@737 2445 return f.name;
adamc@737 2446 }
adamc@737 2447
adamc@740 2448 uw_Basis_string uw_Basis_fileMimeType(uw_context ctx, uw_Basis_file f) {
adamc@740 2449 return f.type;
adamc@740 2450 }
adamc@740 2451
adamc@745 2452 uw_Basis_int uw_Basis_blobSize(uw_context ctx, uw_Basis_blob b) {
adamc@745 2453 return b.size;
adamc@745 2454 }
adamc@745 2455
adamc@737 2456 uw_Basis_blob uw_Basis_fileData(uw_context ctx, uw_Basis_file f) {
adamc@737 2457 return f.data;
adamc@737 2458 }
adamc@741 2459
adamc@741 2460 __attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, uw_Basis_string mimeType) {
adamc@741 2461 cleanup *cl;
adamc@741 2462 int len;
adamc@741 2463
adamc@741 2464 buf_reset(&ctx->outHeaders);
adamc@741 2465 buf_reset(&ctx->page);
adamc@741 2466
adamc@741 2467 uw_write_header(ctx, "HTTP/1.1 200 OK\r\nContent-Type: ");
adamc@741 2468 uw_write_header(ctx, mimeType);
adamc@741 2469 uw_write_header(ctx, "\r\nContent-Length: ");
adamc@741 2470 buf_check(&ctx->outHeaders, INTS_MAX);
adamc@741 2471 sprintf(ctx->outHeaders.front, "%d%n", b.size, &len);
adamc@741 2472 ctx->outHeaders.front += len;
adamc@741 2473 uw_write_header(ctx, "\r\n");
adamc@741 2474
adamc@741 2475 buf_append(&ctx->page, b.data, b.size);
adamc@741 2476
adamc@741 2477 for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl)
adamc@741 2478 cl->func(cl->arg);
adamc@741 2479
adamc@741 2480 ctx->cleanup_front = ctx->cleanup;
adamc@741 2481
adamc@741 2482 longjmp(ctx->jmp_buf, RETURN_BLOB);
adamc@741 2483 }