annotate src/c/urweb.c @ 457:360cbc202756

Request header reading works
author Adam Chlipala <adamc@hcoop.net>
date Thu, 06 Nov 2008 09:47:16 -0500
parents 322c8620bbdf
children 8f65b0fa3b29
rev   line source
adamc@436 1 #define _XOPEN_SOURCE
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@102 10
adamc@102 11 #include "types.h"
adamc@102 12
adamc@311 13 uw_unit uw_unit_v = {};
adamc@102 14
adamc@167 15 #define ERROR_BUF_LEN 1024
adamc@167 16
adamc@323 17 typedef struct regions {
adamc@323 18 struct regions *next;
adamc@323 19 } regions;
adamc@323 20
adamc@425 21 typedef struct {
adamc@425 22 void (*func)(void*);
adamc@425 23 void *arg;
adamc@425 24 } cleanup;
adamc@425 25
adamc@311 26 struct uw_context {
adamc@457 27 char *headers;
adamc@457 28
adamc@117 29 char *page, *page_front, *page_back;
adamc@136 30 char *heap, *heap_front, *heap_back;
adamc@144 31 char **inputs;
adamc@167 32
adamc@272 33 void *db;
adamc@272 34
adamc@167 35 jmp_buf jmp_buf;
adamc@167 36
adamc@323 37 regions *regions;
adamc@323 38
adamc@425 39 cleanup *cleanup, *cleanup_front, *cleanup_back;
adamc@425 40
adamc@167 41 char error_message[ERROR_BUF_LEN];
adamc@117 42 };
adamc@117 43
adamc@311 44 extern int uw_inputs_len;
adamc@144 45
adamc@311 46 uw_context uw_init(size_t page_len, size_t heap_len) {
adamc@311 47 uw_context ctx = malloc(sizeof(struct uw_context));
adamc@136 48
adamc@457 49 ctx->headers = NULL;
adamc@457 50
adamc@117 51 ctx->page_front = ctx->page = malloc(page_len);
adamc@117 52 ctx->page_back = ctx->page_front + page_len;
adamc@136 53
adamc@136 54 ctx->heap_front = ctx->heap = malloc(heap_len);
adamc@136 55 ctx->heap_back = ctx->heap_front + heap_len;
adamc@136 56
adamc@311 57 ctx->inputs = calloc(uw_inputs_len, sizeof(char *));
adamc@144 58
adamc@272 59 ctx->db = NULL;
adamc@272 60
adamc@323 61 ctx->regions = NULL;
adamc@323 62
adamc@425 63 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0);
adamc@425 64
adamc@167 65 ctx->error_message[0] = 0;
adamc@167 66
adamc@117 67 return ctx;
adamc@106 68 }
adamc@106 69
adamc@311 70 void uw_set_db(uw_context ctx, void *db) {
adamc@272 71 ctx->db = db;
adamc@272 72 }
adamc@272 73
adamc@311 74 void *uw_get_db(uw_context ctx) {
adamc@272 75 return ctx->db;
adamc@272 76 }
adamc@272 77
adamc@311 78 void uw_free(uw_context ctx) {
adamc@117 79 free(ctx->page);
adamc@136 80 free(ctx->heap);
adamc@144 81 free(ctx->inputs);
adamc@425 82 free(ctx->cleanup);
adamc@136 83 free(ctx);
adamc@136 84 }
adamc@136 85
adamc@311 86 void uw_reset_keep_request(uw_context ctx) {
adamc@138 87 ctx->page_front = ctx->page;
adamc@138 88 ctx->heap_front = ctx->heap;
adamc@323 89 ctx->regions = NULL;
adamc@425 90 ctx->cleanup_front = ctx->cleanup;
adamc@167 91
adamc@167 92 ctx->error_message[0] = 0;
adamc@167 93 }
adamc@167 94
adamc@311 95 void uw_reset_keep_error_message(uw_context ctx) {
adamc@167 96 ctx->page_front = ctx->page;
adamc@167 97 ctx->heap_front = ctx->heap;
adamc@323 98 ctx->regions = NULL;
adamc@425 99 ctx->cleanup_front = ctx->cleanup;
adamc@167 100 }
adamc@167 101
adamc@311 102 void uw_reset(uw_context ctx) {
adamc@311 103 uw_reset_keep_request(ctx);
adamc@311 104 memset(ctx->inputs, 0, uw_inputs_len * sizeof(char *));
adamc@144 105 }
adamc@144 106
adamc@311 107 void uw_db_init(uw_context);
adamc@311 108 void uw_handle(uw_context, char *);
adamc@167 109
adamc@311 110 failure_kind uw_begin_init(uw_context ctx) {
adamc@272 111 int r = setjmp(ctx->jmp_buf);
adamc@272 112
adamc@272 113 if (r == 0)
adamc@311 114 uw_db_init(ctx);
adamc@272 115
adamc@272 116 return r;
adamc@272 117 }
adamc@272 118
adamc@457 119 failure_kind uw_begin(uw_context ctx, char *headers, char *path) {
adamc@190 120 int r = setjmp(ctx->jmp_buf);
adamc@190 121
adamc@457 122 ctx->headers = headers;
adamc@457 123
adamc@190 124 if (r == 0)
adamc@311 125 uw_handle(ctx, path);
adamc@167 126
adamc@190 127 return r;
adamc@167 128 }
adamc@167 129
adamc@311 130 __attribute__((noreturn)) void uw_error(uw_context ctx, failure_kind fk, const char *fmt, ...) {
adamc@425 131 cleanup *cl;
adamc@425 132
adamc@167 133 va_list ap;
adamc@167 134 va_start(ap, fmt);
adamc@167 135
adamc@167 136 vsnprintf(ctx->error_message, ERROR_BUF_LEN, fmt, ap);
adamc@167 137
adamc@425 138 for (cl = ctx->cleanup; cl < ctx->cleanup_front; ++cl)
adamc@425 139 cl->func(cl->arg);
adamc@425 140
adamc@425 141 ctx->cleanup_front = ctx->cleanup;
adamc@425 142
adamc@190 143 longjmp(ctx->jmp_buf, fk);
adamc@167 144 }
adamc@167 145
adamc@425 146 void uw_push_cleanup(uw_context ctx, void (*func)(void *), void *arg) {
adamc@425 147 if (ctx->cleanup_front >= ctx->cleanup_back) {
adamc@425 148 int len = ctx->cleanup_back - ctx->cleanup, newLen;
adamc@425 149 if (len == 0)
adamc@425 150 newLen = 1;
adamc@425 151 else
adamc@428 152 newLen = len * 2;
adamc@425 153 ctx->cleanup = realloc(ctx->cleanup, newLen);
adamc@425 154 ctx->cleanup_front = ctx->cleanup + len;
adamc@425 155 ctx->cleanup_back = ctx->cleanup + newLen;
adamc@425 156 }
adamc@425 157
adamc@425 158 ctx->cleanup_front->func = func;
adamc@425 159 ctx->cleanup_front->arg = arg;
adamc@425 160 ++ctx->cleanup_front;
adamc@425 161 }
adamc@425 162
adamc@425 163 void uw_pop_cleanup(uw_context ctx) {
adamc@425 164 if (ctx->cleanup_front == ctx->cleanup)
adamc@425 165 uw_error(ctx, FATAL, "Attempt to pop from empty cleanup action stack");
adamc@425 166
adamc@425 167 --ctx->cleanup_front;
adamc@425 168 ctx->cleanup_front->func(ctx->cleanup_front->arg);
adamc@425 169 }
adamc@425 170
adamc@311 171 char *uw_error_message(uw_context ctx) {
adamc@167 172 return ctx->error_message;
adamc@167 173 }
adamc@167 174
adamc@311 175 int uw_input_num(char*);
adamc@144 176
adamc@311 177 void uw_set_input(uw_context ctx, char *name, char *value) {
adamc@311 178 int n = uw_input_num(name);
adamc@144 179
adamc@169 180 if (n < 0)
adamc@311 181 uw_error(ctx, FATAL, "Bad input name %s", name);
adamc@144 182
adamc@311 183 if (n >= uw_inputs_len)
adamc@311 184 uw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
adamc@169 185
adamc@144 186 ctx->inputs[n] = value;
adamc@144 187
adamc@282 188 //printf("[%d] %s = %s\n", n, name, value);
adamc@144 189 }
adamc@144 190
adamc@311 191 char *uw_get_input(uw_context ctx, int n) {
adamc@169 192 if (n < 0)
adamc@311 193 uw_error(ctx, FATAL, "Negative input index %d", n);
adamc@311 194 if (n >= uw_inputs_len)
adamc@311 195 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@282 196 //printf("[%d] = %s\n", n, ctx->inputs[n]);
adamc@144 197 return ctx->inputs[n];
adamc@138 198 }
adamc@138 199
adamc@311 200 char *uw_get_optional_input(uw_context ctx, int n) {
adamc@190 201 if (n < 0)
adamc@311 202 uw_error(ctx, FATAL, "Negative input index %d", n);
adamc@311 203 if (n >= uw_inputs_len)
adamc@311 204 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@426 205 //printf("[%d] = %s\n", n, ctx->inputs[n]);
adamc@190 206 return (ctx->inputs[n] == NULL ? "" : ctx->inputs[n]);
adamc@190 207 }
adamc@190 208
adamc@311 209 static void uw_check_heap(uw_context ctx, size_t extra) {
adamc@136 210 if (ctx->heap_back - ctx->heap_front < extra) {
adamc@321 211 size_t desired = ctx->heap_front - ctx->heap + extra, next;
adamc@136 212 char *new_heap;
adamc@136 213
adamc@317 214 next = ctx->heap_back - ctx->heap;
adamc@317 215 if (next == 0)
adamc@317 216 next = 1;
adamc@317 217 for (; next < desired; next *= 2);
adamc@136 218
adamc@136 219 new_heap = realloc(ctx->heap, next);
adamc@321 220 ctx->heap_front = new_heap + (ctx->heap_front - ctx->heap);
adamc@169 221 ctx->heap_back = new_heap + next;
adamc@136 222
adamc@136 223 if (new_heap != ctx->heap) {
adamc@136 224 ctx->heap = new_heap;
adamc@311 225 uw_error(ctx, UNLIMITED_RETRY, "Couldn't allocate new heap chunk contiguously");
adamc@136 226 }
adamc@136 227
adamc@169 228 ctx->heap = new_heap;
adamc@136 229 }
adamc@136 230 }
adamc@136 231
adamc@311 232 void *uw_malloc(uw_context ctx, size_t len) {
adamc@136 233 void *result;
adamc@136 234
adamc@311 235 uw_check_heap(ctx, len);
adamc@136 236
adamc@136 237 result = ctx->heap_front;
adamc@136 238 ctx->heap_front += len;
adamc@136 239 return result;
adamc@117 240 }
adamc@117 241
adamc@323 242 void uw_begin_region(uw_context ctx) {
adamc@323 243 regions *r = (regions *) ctx->heap_front;
adamc@323 244
adamc@323 245 uw_check_heap(ctx, sizeof(regions));
adamc@323 246
adamc@323 247 ctx->heap_front += sizeof(regions);
adamc@323 248
adamc@323 249 r->next = ctx->regions;
adamc@323 250 ctx->regions = r;
adamc@323 251 }
adamc@323 252
adamc@323 253 void uw_end_region(uw_context ctx) {
adamc@323 254 regions *r = ctx->regions;
adamc@323 255
adamc@323 256 if (r == NULL)
adamc@323 257 uw_error(ctx, FATAL, "Region stack underflow");
adamc@323 258
adamc@323 259 ctx->heap_front = (char *) r;
adamc@323 260 ctx->regions = r->next;
adamc@323 261 }
adamc@323 262
adamc@324 263 void uw_memstats(uw_context ctx) {
adamc@324 264 printf("Page: %d/%d\n", ctx->page_front - ctx->page, ctx->page_back - ctx->page);
adamc@324 265 printf("Heap: %d/%d\n", ctx->heap_front - ctx->heap, ctx->heap_back - ctx->heap);
adamc@324 266 }
adamc@324 267
adamc@436 268 int uw_really_send(int sock, const void *buf, size_t len) {
adamc@117 269 while (len > 0) {
adamc@436 270 size_t n = send(sock, buf, len, 0);
adamc@117 271
adamc@117 272 if (n < 0)
adamc@117 273 return n;
adamc@117 274
adamc@117 275 buf += n;
adamc@117 276 len -= n;
adamc@117 277 }
adamc@117 278
adamc@117 279 return 0;
adamc@117 280 }
adamc@117 281
adamc@311 282 int uw_send(uw_context ctx, int sock) {
adamc@311 283 return uw_really_send(sock, ctx->page, ctx->page_front - ctx->page);
adamc@117 284 }
adamc@117 285
adamc@311 286 static void uw_check(uw_context ctx, size_t extra) {
adamc@321 287 size_t desired = ctx->page_front - ctx->page + extra, next;
adamc@117 288 char *new_page;
adamc@117 289
adamc@317 290 next = ctx->page_back - ctx->page;
adamc@428 291 if (next < desired) {
adamc@428 292 if (next == 0)
adamc@428 293 next = 1;
adamc@428 294 for (; next < desired; next *= 2);
adamc@117 295
adamc@428 296 new_page = realloc(ctx->page, next);
adamc@428 297 ctx->page_front = new_page + (ctx->page_front - ctx->page);
adamc@428 298 ctx->page_back = new_page + next;
adamc@428 299 ctx->page = new_page;
adamc@428 300 }
adamc@117 301 }
adamc@117 302
adamc@311 303 static void uw_writec_unsafe(uw_context ctx, char c) {
adamc@117 304 *(ctx->page_front)++ = c;
adamc@117 305 }
adamc@117 306
adamc@311 307 void uw_writec(uw_context ctx, char c) {
adamc@311 308 uw_check(ctx, 1);
adamc@311 309 uw_writec_unsafe(ctx, c);
adamc@117 310 }
adamc@117 311
adamc@311 312 static void uw_write_unsafe(uw_context ctx, const char* s) {
adamc@117 313 int len = strlen(s);
adamc@117 314 memcpy(ctx->page_front, s, len);
adamc@117 315 ctx->page_front += len;
adamc@117 316 }
adamc@117 317
adamc@311 318 void uw_write(uw_context ctx, const char* s) {
adamc@311 319 uw_check(ctx, strlen(s) + 1);
adamc@311 320 uw_write_unsafe(ctx, s);
adamc@183 321 *ctx->page_front = 0;
adamc@102 322 }
adamc@106 323
adamc@135 324
adamc@311 325 char *uw_Basis_attrifyInt(uw_context ctx, uw_Basis_int n) {
adamc@136 326 char *result;
adamc@136 327 int len;
adamc@311 328 uw_check_heap(ctx, INTS_MAX);
adamc@136 329 result = ctx->heap_front;
adamc@276 330 sprintf(result, "%lld%n", n, &len);
adamc@137 331 ctx->heap_front += len+1;
adamc@136 332 return result;
adamc@106 333 }
adamc@106 334
adamc@311 335 char *uw_Basis_attrifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@136 336 char *result;
adamc@136 337 int len;
adamc@311 338 uw_check_heap(ctx, FLOATS_MAX);
adamc@136 339 result = ctx->heap_front;
adamc@136 340 sprintf(result, "%g%n", n, &len);
adamc@137 341 ctx->heap_front += len+1;
adamc@136 342 return result;
adamc@106 343 }
adamc@106 344
adamc@311 345 char *uw_Basis_attrifyString(uw_context ctx, uw_Basis_string s) {
adamc@136 346 int len = strlen(s);
adamc@136 347 char *result, *p;
adamc@311 348 uw_check_heap(ctx, len * 6 + 1);
adamc@136 349
adamc@136 350 result = p = ctx->heap_front;
adamc@136 351
adamc@136 352 for (; *s; s++) {
adamc@136 353 char c = *s;
adamc@136 354
adamc@136 355 if (c == '"') {
adamc@136 356 strcpy(p, "&quot;");
adamc@136 357 p += 6;
adamc@136 358 } else if (c == '&') {
adamc@136 359 strcpy(p, "&amp;");
adamc@136 360 p += 5;
adamc@136 361 }
adamc@136 362 else if (isprint(c))
adamc@136 363 *p++ = c;
adamc@136 364 else {
adamc@136 365 int len2;
adamc@136 366 sprintf(p, "&#%d;%n", c, &len2);
adamc@136 367 p += len2;
adamc@136 368 }
adamc@136 369 }
adamc@136 370
adamc@137 371 *p++ = 0;
adamc@136 372 ctx->heap_front = p;
adamc@136 373 return result;
adamc@106 374 }
adamc@106 375
adamc@311 376 static void uw_Basis_attrifyInt_w_unsafe(uw_context ctx, uw_Basis_int n) {
adamc@117 377 int len;
adamc@117 378
adamc@276 379 sprintf(ctx->page_front, "%lld%n", n, &len);
adamc@117 380 ctx->page_front += len;
adamc@106 381 }
adamc@106 382
adamc@428 383 uw_unit uw_Basis_attrifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@311 384 uw_check(ctx, INTS_MAX);
adamc@311 385 uw_Basis_attrifyInt_w_unsafe(ctx, n);
adamc@428 386
adamc@428 387 return uw_unit_v;
adamc@106 388 }
adamc@106 389
adamc@428 390 uw_unit uw_Basis_attrifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@117 391 int len;
adamc@117 392
adamc@311 393 uw_check(ctx, FLOATS_MAX);
adamc@117 394 sprintf(ctx->page_front, "%g%n", n, &len);
adamc@117 395 ctx->page_front += len;
adamc@428 396
adamc@428 397 return uw_unit_v;
adamc@117 398 }
adamc@117 399
adamc@428 400 uw_unit uw_Basis_attrifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@311 401 uw_check(ctx, strlen(s) * 6);
adamc@117 402
adamc@106 403 for (; *s; s++) {
adamc@106 404 char c = *s;
adamc@106 405
adamc@106 406 if (c == '"')
adamc@311 407 uw_write_unsafe(ctx, "&quot;");
adamc@136 408 else if (c == '&')
adamc@311 409 uw_write_unsafe(ctx, "&amp;");
adamc@106 410 else if (isprint(c))
adamc@311 411 uw_writec_unsafe(ctx, c);
adamc@106 412 else {
adamc@311 413 uw_write_unsafe(ctx, "&#");
adamc@311 414 uw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@311 415 uw_writec_unsafe(ctx, ';');
adamc@106 416 }
adamc@106 417 }
adamc@428 418
adamc@428 419 return uw_unit_v;
adamc@106 420 }
adamc@120 421
adamc@120 422
adamc@311 423 char *uw_Basis_urlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@137 424 int len;
adamc@137 425 char *r;
adamc@137 426
adamc@311 427 uw_check_heap(ctx, INTS_MAX);
adamc@137 428 r = ctx->heap_front;
adamc@276 429 sprintf(r, "%lld%n", n, &len);
adamc@137 430 ctx->heap_front += len+1;
adamc@137 431 return r;
adamc@120 432 }
adamc@120 433
adamc@311 434 char *uw_Basis_urlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@137 435 int len;
adamc@137 436 char *r;
adamc@137 437
adamc@311 438 uw_check_heap(ctx, FLOATS_MAX);
adamc@137 439 r = ctx->heap_front;
adamc@137 440 sprintf(r, "%g%n", n, &len);
adamc@137 441 ctx->heap_front += len+1;
adamc@137 442 return r;
adamc@120 443 }
adamc@120 444
adamc@311 445 char *uw_Basis_urlifyString(uw_context ctx, uw_Basis_string s) {
adamc@137 446 char *r, *p;
adamc@137 447
adamc@311 448 uw_check_heap(ctx, strlen(s) * 3 + 1);
adamc@137 449
adamc@137 450 for (r = p = ctx->heap_front; *s; s++) {
adamc@137 451 char c = *s;
adamc@137 452
adamc@137 453 if (c == ' ')
adamc@137 454 *p++ = '+';
adamc@137 455 else if (isalnum(c))
adamc@137 456 *p++ = c;
adamc@137 457 else {
adamc@137 458 sprintf(p, "%%%02X", c);
adamc@137 459 p += 3;
adamc@137 460 }
adamc@137 461 }
adamc@137 462
adamc@137 463 *p++ = 0;
adamc@137 464 ctx->heap_front = p;
adamc@137 465 return r;
adamc@120 466 }
adamc@120 467
adamc@311 468 char *uw_Basis_urlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 469 if (b == uw_Basis_False)
adamc@186 470 return "0";
adamc@186 471 else
adamc@186 472 return "1";
adamc@186 473 }
adamc@186 474
adamc@311 475 static void uw_Basis_urlifyInt_w_unsafe(uw_context ctx, uw_Basis_int n) {
adamc@120 476 int len;
adamc@120 477
adamc@276 478 sprintf(ctx->page_front, "%lld%n", n, &len);
adamc@120 479 ctx->page_front += len;
adamc@120 480 }
adamc@120 481
adamc@428 482 uw_unit uw_Basis_urlifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@311 483 uw_check(ctx, INTS_MAX);
adamc@311 484 uw_Basis_urlifyInt_w_unsafe(ctx, n);
adamc@428 485
adamc@428 486 return uw_unit_v;
adamc@120 487 }
adamc@120 488
adamc@428 489 uw_unit uw_Basis_urlifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@120 490 int len;
adamc@120 491
adamc@311 492 uw_check(ctx, FLOATS_MAX);
adamc@120 493 sprintf(ctx->page_front, "%g%n", n, &len);
adamc@120 494 ctx->page_front += len;
adamc@428 495
adamc@428 496 return uw_unit_v;
adamc@120 497 }
adamc@120 498
adamc@428 499 uw_unit uw_Basis_urlifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@311 500 uw_check(ctx, strlen(s) * 3);
adamc@120 501
adamc@120 502 for (; *s; s++) {
adamc@120 503 char c = *s;
adamc@120 504
adamc@120 505 if (c == ' ')
adamc@311 506 uw_writec_unsafe(ctx, '+');
adamc@120 507 else if (isalnum(c))
adamc@311 508 uw_writec_unsafe(ctx, c);
adamc@120 509 else {
adamc@120 510 sprintf(ctx->page_front, "%%%02X", c);
adamc@120 511 ctx->page_front += 3;
adamc@120 512 }
adamc@120 513 }
adamc@428 514
adamc@428 515 return uw_unit_v;
adamc@120 516 }
adamc@120 517
adamc@428 518 uw_unit uw_Basis_urlifyBool_w(uw_context ctx, uw_Basis_bool b) {
adamc@311 519 if (b == uw_Basis_False)
adamc@311 520 uw_writec(ctx, '0');
adamc@186 521 else
adamc@311 522 uw_writec(ctx, '1');
adamc@428 523
adamc@428 524 return uw_unit_v;
adamc@186 525 }
adamc@186 526
adamc@120 527
adamc@311 528 static char *uw_unurlify_advance(char *s) {
adamc@144 529 char *new_s = strchr(s, '/');
adamc@120 530
adamc@120 531 if (new_s)
adamc@120 532 *new_s++ = 0;
adamc@120 533 else
adamc@144 534 new_s = strchr(s, 0);
adamc@144 535
adamc@144 536 return new_s;
adamc@144 537 }
adamc@144 538
adamc@311 539 uw_Basis_int uw_Basis_unurlifyInt(uw_context ctx, char **s) {
adamc@311 540 char *new_s = uw_unurlify_advance(*s);
adamc@311 541 uw_Basis_int r;
adamc@120 542
adamc@276 543 r = atoll(*s);
adamc@120 544 *s = new_s;
adamc@120 545 return r;
adamc@120 546 }
adamc@120 547
adamc@311 548 uw_Basis_float uw_Basis_unurlifyFloat(uw_context ctx, char **s) {
adamc@311 549 char *new_s = uw_unurlify_advance(*s);
adamc@311 550 uw_Basis_float r;
adamc@120 551
adamc@120 552 r = atof(*s);
adamc@120 553 *s = new_s;
adamc@120 554 return r;
adamc@120 555 }
adamc@120 556
adamc@311 557 static uw_Basis_string uw_unurlifyString_to(uw_context ctx, char *r, char *s) {
adamc@144 558 char *s1, *s2;
adamc@144 559 int n;
adamc@136 560
adamc@144 561 for (s1 = r, s2 = s; *s2; ++s1, ++s2) {
adamc@136 562 char c = *s2;
adamc@136 563
adamc@136 564 switch (c) {
adamc@136 565 case '+':
adamc@136 566 *s1 = ' ';
adamc@136 567 break;
adamc@136 568 case '%':
adamc@169 569 if (s2[1] == 0)
adamc@311 570 uw_error(ctx, FATAL, "Missing first character of escaped URL byte");
adamc@169 571 if (s2[2] == 0)
adamc@311 572 uw_error(ctx, FATAL, "Missing second character of escaped URL byte");
adamc@169 573 if (sscanf(s2+1, "%02X", &n) != 1)
adamc@311 574 uw_error(ctx, FATAL, "Invalid escaped URL byte starting at: %s", s2);
adamc@136 575 *s1 = n;
adamc@136 576 s2 += 2;
adamc@136 577 break;
adamc@136 578 default:
adamc@136 579 *s1 = c;
adamc@136 580 }
adamc@136 581 }
adamc@136 582 *s1++ = 0;
adamc@144 583 return s1;
adamc@144 584 }
adamc@144 585
adamc@311 586 uw_Basis_bool uw_Basis_unurlifyBool(uw_context ctx, char **s) {
adamc@311 587 char *new_s = uw_unurlify_advance(*s);
adamc@311 588 uw_Basis_bool r;
adamc@186 589
adamc@186 590 if (*s[0] == 0 || !strcmp(*s, "0") || !strcmp(*s, "off"))
adamc@311 591 r = uw_Basis_False;
adamc@186 592 else
adamc@311 593 r = uw_Basis_True;
adamc@186 594
adamc@186 595 *s = new_s;
adamc@186 596 return r;
adamc@186 597 }
adamc@186 598
adamc@311 599 uw_Basis_string uw_Basis_unurlifyString(uw_context ctx, char **s) {
adamc@311 600 char *new_s = uw_unurlify_advance(*s);
adamc@144 601 char *r, *s1, *s2;
adamc@144 602 int len, n;
adamc@144 603
adamc@200 604 len = strlen(*s);
adamc@311 605 uw_check_heap(ctx, len + 1);
adamc@144 606
adamc@144 607 r = ctx->heap_front;
adamc@311 608 ctx->heap_front = uw_unurlifyString_to(ctx, ctx->heap_front, *s);
adamc@136 609 *s = new_s;
adamc@136 610 return r;
adamc@120 611 }
adamc@135 612
adamc@135 613
adamc@311 614 char *uw_Basis_htmlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@286 615 int len;
adamc@286 616 char *r;
adamc@286 617
adamc@311 618 uw_check_heap(ctx, INTS_MAX);
adamc@286 619 r = ctx->heap_front;
adamc@286 620 sprintf(r, "%lld%n", n, &len);
adamc@286 621 ctx->heap_front += len+1;
adamc@286 622 return r;
adamc@286 623 }
adamc@286 624
adamc@428 625 uw_unit uw_Basis_htmlifyInt_w(uw_context ctx, uw_Basis_int n) {
adamc@286 626 int len;
adamc@286 627
adamc@311 628 uw_check(ctx, INTS_MAX);
adamc@286 629 sprintf(ctx->page_front, "%lld%n", n, &len);
adamc@286 630 ctx->page_front += len;
adamc@428 631
adamc@428 632 return uw_unit_v;
adamc@286 633 }
adamc@286 634
adamc@311 635 char *uw_Basis_htmlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@286 636 int len;
adamc@286 637 char *r;
adamc@286 638
adamc@311 639 uw_check_heap(ctx, FLOATS_MAX);
adamc@286 640 r = ctx->heap_front;
adamc@286 641 sprintf(r, "%g%n", n, &len);
adamc@286 642 ctx->heap_front += len+1;
adamc@286 643 return r;
adamc@286 644 }
adamc@286 645
adamc@428 646 uw_unit uw_Basis_htmlifyFloat_w(uw_context ctx, uw_Basis_float n) {
adamc@286 647 int len;
adamc@286 648
adamc@311 649 uw_check(ctx, FLOATS_MAX);
adamc@286 650 sprintf(ctx->page_front, "%g%n", n, &len);
adamc@286 651 ctx->page_front += len;
adamc@428 652
adamc@428 653 return uw_unit_v;
adamc@286 654 }
adamc@286 655
adamc@311 656 char *uw_Basis_htmlifyString(uw_context ctx, uw_Basis_string s) {
adamc@137 657 char *r, *s2;
adamc@137 658
adamc@311 659 uw_check_heap(ctx, strlen(s) * 5 + 1);
adamc@137 660
adamc@137 661 for (r = s2 = ctx->heap_front; *s; s++) {
adamc@137 662 char c = *s;
adamc@137 663
adamc@137 664 switch (c) {
adamc@137 665 case '<':
adamc@137 666 strcpy(s2, "&lt;");
adamc@137 667 s2 += 4;
adamc@137 668 break;
adamc@137 669 case '&':
adamc@137 670 strcpy(s2, "&amp;");
adamc@137 671 s2 += 5;
adamc@137 672 break;
adamc@137 673 default:
adamc@137 674 if (isprint(c))
adamc@137 675 *s2++ = c;
adamc@137 676 else {
adamc@137 677 int len2;
adamc@137 678 sprintf(s2, "&#%d;%n", c, &len2);
adamc@137 679 s2 += len2;
adamc@137 680 }
adamc@137 681 }
adamc@137 682 }
adamc@137 683
adamc@137 684 *s2++ = 0;
adamc@137 685 ctx->heap_front = s2;
adamc@137 686 return r;
adamc@135 687 }
adamc@135 688
adamc@428 689 uw_unit uw_Basis_htmlifyString_w(uw_context ctx, uw_Basis_string s) {
adamc@321 690 uw_check(ctx, strlen(s) * 6);
adamc@135 691
adamc@135 692 for (; *s; s++) {
adamc@135 693 char c = *s;
adamc@135 694
adamc@135 695 switch (c) {
adamc@135 696 case '<':
adamc@311 697 uw_write_unsafe(ctx, "&lt;");
adamc@135 698 break;
adamc@135 699 case '&':
adamc@311 700 uw_write_unsafe(ctx, "&amp;");
adamc@135 701 break;
adamc@135 702 default:
adamc@135 703 if (isprint(c))
adamc@311 704 uw_writec_unsafe(ctx, c);
adamc@135 705 else {
adamc@311 706 uw_write_unsafe(ctx, "&#");
adamc@311 707 uw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@311 708 uw_writec_unsafe(ctx, ';');
adamc@135 709 }
adamc@135 710 }
adamc@135 711 }
adamc@428 712
adamc@428 713 return uw_unit_v;
adamc@135 714 }
adamc@180 715
adamc@311 716 uw_Basis_string uw_Basis_htmlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 717 if (b == uw_Basis_False)
adamc@286 718 return "False";
adamc@286 719 else
adamc@286 720 return "True";
adamc@286 721 }
adamc@286 722
adamc@428 723 uw_unit uw_Basis_htmlifyBool_w(uw_context ctx, uw_Basis_bool b) {
adamc@311 724 if (b == uw_Basis_False) {
adamc@311 725 uw_check(ctx, 6);
adamc@286 726 strcpy(ctx->page_front, "False");
adamc@286 727 ctx->page_front += 5;
adamc@286 728 } else {
adamc@311 729 uw_check(ctx, 5);
adamc@286 730 strcpy(ctx->page_front, "True");
adamc@286 731 ctx->page_front += 4;
adamc@286 732 }
adamc@428 733
adamc@428 734 return uw_unit_v;
adamc@286 735 }
adamc@286 736
adamc@436 737 #define TIME_FMT "%x %X"
adamc@438 738 #define TIME_FMT_PG "%Y-%m-%d %T"
adamc@436 739
adamc@436 740 uw_Basis_string uw_Basis_htmlifyTime(uw_context ctx, uw_Basis_time t) {
adamc@436 741 size_t len;
adamc@436 742 char *r;
adamc@436 743 struct tm stm;
adamc@436 744
adamc@436 745 if (localtime_r(&t, &stm)) {
adamc@436 746 uw_check_heap(ctx, TIMES_MAX);
adamc@436 747 r = ctx->heap_front;
adamc@436 748 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@436 749 ctx->heap_front += len+1;
adamc@436 750 return r;
adamc@436 751 } else
adamc@436 752 return "<i>Invalid time</i>";
adamc@436 753 }
adamc@436 754
adamc@436 755 uw_unit uw_Basis_htmlifyTime_w(uw_context ctx, uw_Basis_time t) {
adamc@436 756 size_t len;
adamc@436 757 char *r;
adamc@436 758 struct tm stm;
adamc@436 759
adamc@436 760 if (localtime_r(&t, &stm)) {
adamc@436 761 uw_check(ctx, TIMES_MAX);
adamc@436 762 r = ctx->page_front;
adamc@436 763 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@436 764 ctx->page_front += len;
adamc@436 765 } else {
adamc@436 766 uw_check(ctx, 20);
adamc@436 767 strcpy(ctx->page_front, "<i>Invalid time</i>");
adamc@436 768 ctx->page_front += 19;
adamc@436 769 }
adamc@436 770
adamc@436 771 return uw_unit_v;
adamc@436 772 }
adamc@436 773
adamc@311 774 uw_Basis_string uw_Basis_strcat(uw_context ctx, uw_Basis_string s1, uw_Basis_string s2) {
adamc@180 775 int len = strlen(s1) + strlen(s2) + 1;
adamc@180 776 char *s;
adamc@180 777
adamc@311 778 uw_check_heap(ctx, len);
adamc@180 779
adamc@180 780 s = ctx->heap_front;
adamc@180 781
adamc@180 782 strcpy(s, s1);
adamc@180 783 strcat(s, s2);
adamc@180 784 ctx->heap_front += len;
adamc@180 785
adamc@180 786 return s;
adamc@180 787 }
adamc@278 788
adamc@311 789 uw_Basis_string uw_Basis_strdup(uw_context ctx, uw_Basis_string s1) {
adamc@278 790 int len = strlen(s1) + 1;
adamc@278 791 char *s;
adamc@278 792
adamc@311 793 uw_check_heap(ctx, len);
adamc@278 794
adamc@278 795 s = ctx->heap_front;
adamc@278 796
adamc@278 797 strcpy(s, s1);
adamc@278 798 ctx->heap_front += len;
adamc@278 799
adamc@278 800 return s;
adamc@278 801 }
adamc@280 802
adamc@280 803
adamc@311 804 char *uw_Basis_sqlifyInt(uw_context ctx, uw_Basis_int n) {
adamc@281 805 int len;
adamc@281 806 char *r;
adamc@281 807
adamc@311 808 uw_check_heap(ctx, INTS_MAX + 6);
adamc@281 809 r = ctx->heap_front;
adamc@281 810 sprintf(r, "%lld::int8%n", n, &len);
adamc@281 811 ctx->heap_front += len+1;
adamc@281 812 return r;
adamc@281 813 }
adamc@281 814
adamc@311 815 char *uw_Basis_sqlifyFloat(uw_context ctx, uw_Basis_float n) {
adamc@281 816 int len;
adamc@281 817 char *r;
adamc@281 818
adamc@311 819 uw_check_heap(ctx, FLOATS_MAX + 8);
adamc@281 820 r = ctx->heap_front;
adamc@281 821 sprintf(r, "%g::float8%n", n, &len);
adamc@281 822 ctx->heap_front += len+1;
adamc@281 823 return r;
adamc@281 824 }
adamc@281 825
adamc@281 826
adamc@311 827 uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) {
adamc@280 828 char *r, *s2;
adamc@280 829
adamc@311 830 uw_check_heap(ctx, strlen(s) * 2 + 10);
adamc@280 831
adamc@280 832 r = s2 = ctx->heap_front;
adamc@280 833 *s2++ = 'E';
adamc@280 834 *s2++ = '\'';
adamc@280 835
adamc@280 836 for (; *s; s++) {
adamc@280 837 char c = *s;
adamc@280 838
adamc@280 839 switch (c) {
adamc@280 840 case '\'':
adamc@280 841 strcpy(s2, "\\'");
adamc@280 842 s2 += 2;
adamc@280 843 break;
adamc@280 844 case '\\':
adamc@280 845 strcpy(s2, "\\\\");
adamc@280 846 s2 += 2;
adamc@280 847 break;
adamc@280 848 default:
adamc@280 849 if (isprint(c))
adamc@280 850 *s2++ = c;
adamc@280 851 else {
adamc@280 852 sprintf(s2, "\\%3o", c);
adamc@280 853 s2 += 4;
adamc@280 854 }
adamc@280 855 }
adamc@280 856 }
adamc@280 857
adamc@281 858 strcpy(s2, "'::text");
adamc@281 859 ctx->heap_front = s2 + 8;
adamc@280 860 return r;
adamc@280 861 }
adamc@281 862
adamc@311 863 char *uw_Basis_sqlifyBool(uw_context ctx, uw_Basis_bool b) {
adamc@311 864 if (b == uw_Basis_False)
adamc@281 865 return "FALSE";
adamc@281 866 else
adamc@281 867 return "TRUE";
adamc@281 868 }
adamc@282 869
adamc@439 870 char *uw_Basis_sqlifyTime(uw_context ctx, uw_Basis_time t) {
adamc@439 871 size_t len;
adamc@439 872 char *r;
adamc@439 873 struct tm stm;
adamc@439 874
adamc@439 875 if (localtime_r(&t, &stm)) {
adamc@439 876 uw_check_heap(ctx, TIMES_MAX);
adamc@439 877 r = ctx->heap_front;
adamc@439 878 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@439 879 ctx->heap_front += len+1;
adamc@439 880 return r;
adamc@439 881 } else
adamc@439 882 return "<Invalid time>";
adamc@439 883 }
adamc@439 884
adamc@311 885 char *uw_Basis_ensqlBool(uw_Basis_bool b) {
adamc@311 886 static uw_Basis_int true = 1;
adamc@311 887 static uw_Basis_int false = 0;
adamc@282 888
adamc@311 889 if (b == uw_Basis_False)
adamc@282 890 return (char *)&false;
adamc@282 891 else
adamc@282 892 return (char *)&true;
adamc@282 893 }
adamc@284 894
adamc@311 895 uw_Basis_string uw_Basis_intToString(uw_context ctx, uw_Basis_int n) {
adamc@284 896 int len;
adamc@284 897 char *r;
adamc@284 898
adamc@311 899 uw_check_heap(ctx, INTS_MAX);
adamc@284 900 r = ctx->heap_front;
adamc@284 901 sprintf(r, "%lld%n", n, &len);
adamc@284 902 ctx->heap_front += len+1;
adamc@284 903 return r;
adamc@284 904 }
adamc@285 905
adamc@311 906 uw_Basis_string uw_Basis_floatToString(uw_context ctx, uw_Basis_float n) {
adamc@285 907 int len;
adamc@285 908 char *r;
adamc@285 909
adamc@311 910 uw_check_heap(ctx, FLOATS_MAX);
adamc@285 911 r = ctx->heap_front;
adamc@285 912 sprintf(r, "%g%n", n, &len);
adamc@285 913 ctx->heap_front += len+1;
adamc@285 914 return r;
adamc@285 915 }
adamc@285 916
adamc@311 917 uw_Basis_string uw_Basis_boolToString(uw_context ctx, uw_Basis_bool b) {
adamc@311 918 if (b == uw_Basis_False)
adamc@285 919 return "False";
adamc@285 920 else
adamc@285 921 return "True";
adamc@285 922 }
adamc@288 923
adamc@436 924 uw_Basis_string uw_Basis_timeToString(uw_context ctx, uw_Basis_time t) {
adamc@436 925 size_t len;
adamc@436 926 char *r;
adamc@436 927 struct tm stm;
adamc@436 928
adamc@436 929 if (localtime_r(&t, &stm)) {
adamc@436 930 uw_check_heap(ctx, TIMES_MAX);
adamc@436 931 r = ctx->heap_front;
adamc@436 932 len = strftime(r, TIMES_MAX, TIME_FMT, &stm);
adamc@436 933 ctx->heap_front += len+1;
adamc@436 934 return r;
adamc@436 935 } else
adamc@436 936 return "<Invalid time>";
adamc@436 937 }
adamc@288 938
adamc@311 939 uw_Basis_int *uw_Basis_stringToInt(uw_context ctx, uw_Basis_string s) {
adamc@288 940 char *endptr;
adamc@311 941 uw_Basis_int n = strtoll(s, &endptr, 10);
adamc@288 942
adamc@288 943 if (*s != '\0' && *endptr == '\0') {
adamc@311 944 uw_Basis_int *r = uw_malloc(ctx, sizeof(uw_Basis_int));
adamc@288 945 *r = n;
adamc@288 946 return r;
adamc@288 947 } else
adamc@288 948 return NULL;
adamc@288 949 }
adamc@289 950
adamc@311 951 uw_Basis_float *uw_Basis_stringToFloat(uw_context ctx, uw_Basis_string s) {
adamc@289 952 char *endptr;
adamc@311 953 uw_Basis_float n = strtod(s, &endptr);
adamc@289 954
adamc@289 955 if (*s != '\0' && *endptr == '\0') {
adamc@311 956 uw_Basis_float *r = uw_malloc(ctx, sizeof(uw_Basis_float));
adamc@289 957 *r = n;
adamc@289 958 return r;
adamc@289 959 } else
adamc@289 960 return NULL;
adamc@289 961 }
adamc@289 962
adamc@311 963 uw_Basis_bool *uw_Basis_stringToBool(uw_context ctx, uw_Basis_string s) {
adamc@311 964 static uw_Basis_bool true = uw_Basis_True;
adamc@311 965 static uw_Basis_bool false = uw_Basis_False;
adamc@289 966
adamc@289 967 if (!strcasecmp (s, "True"))
adamc@289 968 return &true;
adamc@289 969 else if (!strcasecmp (s, "False"))
adamc@289 970 return &false;
adamc@289 971 else
adamc@289 972 return NULL;
adamc@289 973 }
adamc@292 974
adamc@436 975 uw_Basis_time *uw_Basis_stringToTime(uw_context ctx, uw_Basis_string s) {
adamc@438 976 char *dot = strchr(s, '.'), *end = strchr(s, 0);
adamc@436 977 struct tm stm;
adamc@436 978
adamc@439 979 if (dot) {
adamc@439 980 *dot = 0;
adamc@439 981 if (strptime(s, TIME_FMT_PG, &stm) == end) {
adamc@439 982 *dot = '.';
adamc@439 983 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 984 *r = mktime(&stm);
adamc@439 985 return r;
adamc@439 986 }
adamc@439 987 else {
adamc@439 988 *dot = '.';
adamc@439 989 return NULL;
adamc@439 990 }
adamc@436 991 }
adamc@439 992 else {
adamc@439 993 if (strptime(s, TIME_FMT_PG, &stm) == end) {
adamc@439 994 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 995 *r = mktime(&stm);
adamc@439 996 return r;
adamc@439 997 }
adamc@439 998 else if (strptime(s, TIME_FMT, &stm) == end) {
adamc@439 999 uw_Basis_time *r = uw_malloc(ctx, sizeof(uw_Basis_time));
adamc@439 1000 *r = mktime(&stm);
adamc@439 1001 return r;
adamc@439 1002 }
adamc@439 1003 else
adamc@439 1004 return NULL;
adamc@439 1005 }
adamc@436 1006 }
adamc@436 1007
adamc@311 1008 uw_Basis_int uw_Basis_stringToInt_error(uw_context ctx, uw_Basis_string s) {
adamc@292 1009 char *endptr;
adamc@311 1010 uw_Basis_int n = strtoll(s, &endptr, 10);
adamc@292 1011
adamc@292 1012 if (*s != '\0' && *endptr == '\0')
adamc@292 1013 return n;
adamc@292 1014 else
adamc@311 1015 uw_error(ctx, FATAL, "Can't parse int: %s", s);
adamc@292 1016 }
adamc@293 1017
adamc@311 1018 uw_Basis_float uw_Basis_stringToFloat_error(uw_context ctx, uw_Basis_string s) {
adamc@293 1019 char *endptr;
adamc@311 1020 uw_Basis_float n = strtod(s, &endptr);
adamc@293 1021
adamc@293 1022 if (*s != '\0' && *endptr == '\0')
adamc@293 1023 return n;
adamc@293 1024 else
adamc@311 1025 uw_error(ctx, FATAL, "Can't parse float: %s", s);
adamc@293 1026 }
adamc@293 1027
adamc@311 1028 uw_Basis_bool uw_Basis_stringToBool_error(uw_context ctx, uw_Basis_string s) {
adamc@296 1029 if (!strcasecmp(s, "T") || !strcasecmp (s, "True"))
adamc@311 1030 return uw_Basis_True;
adamc@296 1031 else if (!strcasecmp(s, "F") || !strcasecmp (s, "False"))
adamc@311 1032 return uw_Basis_False;
adamc@293 1033 else
adamc@311 1034 uw_error(ctx, FATAL, "Can't parse bool: %s", s);
adamc@293 1035 }
adamc@436 1036
adamc@436 1037 uw_Basis_time uw_Basis_stringToTime_error(uw_context ctx, uw_Basis_string s) {
adamc@438 1038 char *dot = strchr(s, '.'), *end = strchr(s, 0);
adamc@436 1039 struct tm stm = {};
adamc@436 1040
adamc@438 1041 if (dot) {
adamc@438 1042 *dot = 0;
adamc@438 1043 if (strptime(s, TIME_FMT_PG, &stm)) {
adamc@438 1044 *dot = '.';
adamc@438 1045 return mktime(&stm);
adamc@438 1046 }
adamc@438 1047 else {
adamc@438 1048 *dot = '.';
adamc@438 1049 uw_error(ctx, FATAL, "Can't parse time: %s", s);
adamc@438 1050 }
adamc@438 1051 }
adamc@438 1052 else {
adamc@439 1053 if (strptime(s, TIME_FMT_PG, &stm) == end)
adamc@439 1054 return mktime(&stm);
adamc@439 1055 else if (strptime(s, TIME_FMT, &stm) == end)
adamc@438 1056 return mktime(&stm);
adamc@438 1057 else
adamc@438 1058 uw_error(ctx, FATAL, "Can't parse time: %s", s);
adamc@438 1059 }
adamc@436 1060 }
adamc@457 1061
adamc@457 1062 uw_Basis_string uw_Basis_requestHeader(uw_context ctx, uw_Basis_string h) {
adamc@457 1063 int len = strlen(h);
adamc@457 1064 char *s = ctx->headers, *p;
adamc@457 1065
adamc@457 1066 while (p = strchr(s, ':')) {
adamc@457 1067 if (p - s == len && !strncasecmp(s, h, len)) {
adamc@457 1068 s = p + 2;
adamc@457 1069 if (p = strchr(s, '\r')) {
adamc@457 1070 uw_Basis_string ret = uw_malloc(ctx, p - s + 1);
adamc@457 1071 memcpy(ret, s, p - s);
adamc@457 1072 ret[p - s] = 0;
adamc@457 1073 return ret;
adamc@457 1074 }
adamc@457 1075 else
adamc@457 1076 return NULL;
adamc@457 1077 } else {
adamc@457 1078 if (s = strchr(s, '\n'))
adamc@457 1079 ++s;
adamc@457 1080 else
adamc@457 1081 return NULL;
adamc@457 1082 }
adamc@457 1083 }
adamc@457 1084
adamc@457 1085 }