annotate src/c/urweb.c @ 270:b9b02613c0c2

Parsing jobs
author Adam Chlipala <adamc@hcoop.net>
date Tue, 02 Sep 2008 10:31:16 -0400
parents 1e24a3e6d614
children 4d80d6122df1
rev   line source
adamc@117 1 #include <stdlib.h>
adamc@102 2 #include <stdio.h>
adamc@117 3 #include <string.h>
adamc@106 4 #include <ctype.h>
adamc@167 5 #include <setjmp.h>
adamc@167 6 #include <stdarg.h>
adamc@102 7
adamc@102 8 #include "types.h"
adamc@102 9
adamc@102 10 lw_unit lw_unit_v = {};
adamc@102 11
adamc@167 12 #define ERROR_BUF_LEN 1024
adamc@167 13
adamc@117 14 struct lw_context {
adamc@117 15 char *page, *page_front, *page_back;
adamc@136 16 char *heap, *heap_front, *heap_back;
adamc@144 17 char **inputs;
adamc@167 18
adamc@167 19 jmp_buf jmp_buf;
adamc@167 20
adamc@167 21 char error_message[ERROR_BUF_LEN];
adamc@117 22 };
adamc@117 23
adamc@144 24 extern int lw_inputs_len;
adamc@144 25
adamc@136 26 lw_context lw_init(size_t page_len, size_t heap_len) {
adamc@117 27 lw_context ctx = malloc(sizeof(struct lw_context));
adamc@136 28
adamc@117 29 ctx->page_front = ctx->page = malloc(page_len);
adamc@117 30 ctx->page_back = ctx->page_front + page_len;
adamc@136 31
adamc@136 32 ctx->heap_front = ctx->heap = malloc(heap_len);
adamc@136 33 ctx->heap_back = ctx->heap_front + heap_len;
adamc@136 34
adamc@144 35 ctx->inputs = calloc(lw_inputs_len, sizeof(char *));
adamc@144 36
adamc@167 37 ctx->error_message[0] = 0;
adamc@167 38
adamc@117 39 return ctx;
adamc@106 40 }
adamc@106 41
adamc@117 42 void lw_free(lw_context ctx) {
adamc@117 43 free(ctx->page);
adamc@136 44 free(ctx->heap);
adamc@144 45 free(ctx->inputs);
adamc@136 46 free(ctx);
adamc@136 47 }
adamc@136 48
adamc@167 49 void lw_reset_keep_request(lw_context ctx) {
adamc@138 50 ctx->page_front = ctx->page;
adamc@138 51 ctx->heap_front = ctx->heap;
adamc@167 52
adamc@167 53 ctx->error_message[0] = 0;
adamc@167 54 }
adamc@167 55
adamc@167 56 void lw_reset_keep_error_message(lw_context ctx) {
adamc@167 57 ctx->page_front = ctx->page;
adamc@167 58 ctx->heap_front = ctx->heap;
adamc@167 59 }
adamc@167 60
adamc@167 61 void lw_reset(lw_context ctx) {
adamc@167 62 lw_reset_keep_request(ctx);
adamc@144 63 memset(ctx->inputs, 0, lw_inputs_len * sizeof(char *));
adamc@144 64 }
adamc@144 65
adamc@167 66 void lw_handle(lw_context, char *);
adamc@167 67
adamc@167 68 failure_kind lw_begin(lw_context ctx, char *path) {
adamc@190 69 int r = setjmp(ctx->jmp_buf);
adamc@190 70
adamc@190 71 if (r == 0)
adamc@167 72 lw_handle(ctx, path);
adamc@167 73
adamc@190 74 return r;
adamc@167 75 }
adamc@167 76
adamc@167 77 void lw_error(lw_context ctx, failure_kind fk, const char *fmt, ...) {
adamc@167 78 va_list ap;
adamc@167 79 va_start(ap, fmt);
adamc@167 80
adamc@167 81 vsnprintf(ctx->error_message, ERROR_BUF_LEN, fmt, ap);
adamc@167 82
adamc@190 83 longjmp(ctx->jmp_buf, fk);
adamc@167 84 }
adamc@167 85
adamc@167 86 char *lw_error_message(lw_context ctx) {
adamc@167 87 return ctx->error_message;
adamc@167 88 }
adamc@167 89
adamc@144 90 int lw_input_num(char*);
adamc@144 91
adamc@144 92 void lw_set_input(lw_context ctx, char *name, char *value) {
adamc@144 93 int n = lw_input_num(name);
adamc@144 94
adamc@169 95 if (n < 0)
adamc@169 96 lw_error(ctx, FATAL, "Bad input name %s", name);
adamc@144 97
adamc@169 98 if (n >= lw_inputs_len)
adamc@169 99 lw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
adamc@169 100
adamc@144 101 ctx->inputs[n] = value;
adamc@144 102
adamc@144 103 printf("[%d] %s = %s\n", n, name, value);
adamc@144 104 }
adamc@144 105
adamc@144 106 char *lw_get_input(lw_context ctx, int n) {
adamc@169 107 if (n < 0)
adamc@169 108 lw_error(ctx, FATAL, "Negative input index %d", n);
adamc@169 109 if (n >= lw_inputs_len)
adamc@169 110 lw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@144 111 printf("[%d] = %s\n", n, ctx->inputs[n]);
adamc@144 112 return ctx->inputs[n];
adamc@138 113 }
adamc@138 114
adamc@190 115 char *lw_get_optional_input(lw_context ctx, int n) {
adamc@190 116 if (n < 0)
adamc@190 117 lw_error(ctx, FATAL, "Negative input index %d", n);
adamc@190 118 if (n >= lw_inputs_len)
adamc@190 119 lw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
adamc@190 120 printf("[%d] = %s\n", n, ctx->inputs[n]);
adamc@190 121 return (ctx->inputs[n] == NULL ? "" : ctx->inputs[n]);
adamc@190 122 }
adamc@190 123
adamc@136 124 static void lw_check_heap(lw_context ctx, size_t extra) {
adamc@136 125 if (ctx->heap_back - ctx->heap_front < extra) {
adamc@136 126 size_t desired = ctx->heap_back - ctx->heap_front + extra, next;
adamc@136 127 char *new_heap;
adamc@136 128
adamc@136 129 for (next = ctx->heap_back - ctx->heap_front; next < desired; next *= 2);
adamc@136 130
adamc@136 131 new_heap = realloc(ctx->heap, next);
adamc@169 132 ctx->heap_front = new_heap;
adamc@169 133 ctx->heap_back = new_heap + next;
adamc@136 134
adamc@136 135 if (new_heap != ctx->heap) {
adamc@136 136 ctx->heap = new_heap;
adamc@169 137 lw_error(ctx, UNLIMITED_RETRY, "Couldn't allocate new heap chunk contiguously");
adamc@136 138 }
adamc@136 139
adamc@169 140 ctx->heap = new_heap;
adamc@136 141 }
adamc@136 142 }
adamc@136 143
adamc@136 144 void *lw_malloc(lw_context ctx, size_t len) {
adamc@136 145 void *result;
adamc@136 146
adamc@136 147 lw_check_heap(ctx, len);
adamc@136 148
adamc@136 149 result = ctx->heap_front;
adamc@136 150 ctx->heap_front += len;
adamc@136 151 return result;
adamc@117 152 }
adamc@117 153
adamc@117 154 int lw_really_send(int sock, const void *buf, ssize_t len) {
adamc@117 155 while (len > 0) {
adamc@117 156 ssize_t n = send(sock, buf, len, 0);
adamc@117 157
adamc@117 158 if (n < 0)
adamc@117 159 return n;
adamc@117 160
adamc@117 161 buf += n;
adamc@117 162 len -= n;
adamc@117 163 }
adamc@117 164
adamc@117 165 return 0;
adamc@117 166 }
adamc@117 167
adamc@117 168 int lw_send(lw_context ctx, int sock) {
adamc@117 169 return lw_really_send(sock, ctx->page, ctx->page_front - ctx->page);
adamc@117 170 }
adamc@117 171
adamc@117 172 static void lw_check(lw_context ctx, size_t extra) {
adamc@117 173 size_t desired = ctx->page_back - ctx->page_front + extra, next;
adamc@117 174 char *new_page;
adamc@117 175
adamc@117 176 for (next = ctx->page_back - ctx->page_front; next < desired; next *= 2);
adamc@117 177
adamc@117 178 new_page = realloc(ctx->page, next);
adamc@117 179 ctx->page_front = new_page + (ctx->page_front - ctx->page);
adamc@117 180 ctx->page_back = new_page + (ctx->page_back - ctx->page);
adamc@117 181 ctx->page = new_page;
adamc@117 182 }
adamc@117 183
adamc@117 184 static void lw_writec_unsafe(lw_context ctx, char c) {
adamc@117 185 *(ctx->page_front)++ = c;
adamc@117 186 }
adamc@117 187
adamc@117 188 void lw_writec(lw_context ctx, char c) {
adamc@117 189 lw_check(ctx, 1);
adamc@117 190 lw_writec_unsafe(ctx, c);
adamc@117 191 }
adamc@117 192
adamc@117 193 static void lw_write_unsafe(lw_context ctx, const char* s) {
adamc@117 194 int len = strlen(s);
adamc@117 195 memcpy(ctx->page_front, s, len);
adamc@117 196 ctx->page_front += len;
adamc@117 197 }
adamc@117 198
adamc@117 199 void lw_write(lw_context ctx, const char* s) {
adamc@183 200 lw_check(ctx, strlen(s) + 1);
adamc@117 201 lw_write_unsafe(ctx, s);
adamc@183 202 *ctx->page_front = 0;
adamc@102 203 }
adamc@106 204
adamc@135 205
adamc@136 206 #define INTS_MAX 50
adamc@136 207 #define FLOATS_MAX 100
adamc@136 208
adamc@136 209 char *lw_Basis_attrifyInt(lw_context ctx, lw_Basis_int n) {
adamc@136 210 char *result;
adamc@136 211 int len;
adamc@136 212 lw_check_heap(ctx, INTS_MAX);
adamc@136 213 result = ctx->heap_front;
adamc@136 214 sprintf(result, "%d%n", n, &len);
adamc@137 215 ctx->heap_front += len+1;
adamc@136 216 return result;
adamc@106 217 }
adamc@106 218
adamc@136 219 char *lw_Basis_attrifyFloat(lw_context ctx, lw_Basis_float n) {
adamc@136 220 char *result;
adamc@136 221 int len;
adamc@137 222 lw_check_heap(ctx, FLOATS_MAX);
adamc@136 223 result = ctx->heap_front;
adamc@136 224 sprintf(result, "%g%n", n, &len);
adamc@137 225 ctx->heap_front += len+1;
adamc@136 226 return result;
adamc@106 227 }
adamc@106 228
adamc@136 229 char *lw_Basis_attrifyString(lw_context ctx, lw_Basis_string s) {
adamc@136 230 int len = strlen(s);
adamc@136 231 char *result, *p;
adamc@137 232 lw_check_heap(ctx, len * 6 + 1);
adamc@136 233
adamc@136 234 result = p = ctx->heap_front;
adamc@136 235
adamc@136 236 for (; *s; s++) {
adamc@136 237 char c = *s;
adamc@136 238
adamc@136 239 if (c == '"') {
adamc@136 240 strcpy(p, "&quot;");
adamc@136 241 p += 6;
adamc@136 242 } else if (c == '&') {
adamc@136 243 strcpy(p, "&amp;");
adamc@136 244 p += 5;
adamc@136 245 }
adamc@136 246 else if (isprint(c))
adamc@136 247 *p++ = c;
adamc@136 248 else {
adamc@136 249 int len2;
adamc@136 250 sprintf(p, "&#%d;%n", c, &len2);
adamc@136 251 p += len2;
adamc@136 252 }
adamc@136 253 }
adamc@136 254
adamc@137 255 *p++ = 0;
adamc@136 256 ctx->heap_front = p;
adamc@136 257 return result;
adamc@106 258 }
adamc@106 259
adamc@117 260 static void lw_Basis_attrifyInt_w_unsafe(lw_context ctx, lw_Basis_int n) {
adamc@117 261 int len;
adamc@117 262
adamc@117 263 sprintf(ctx->page_front, "%d%n", n, &len);
adamc@117 264 ctx->page_front += len;
adamc@106 265 }
adamc@106 266
adamc@117 267 void lw_Basis_attrifyInt_w(lw_context ctx, lw_Basis_int n) {
adamc@117 268 lw_check(ctx, INTS_MAX);
adamc@117 269 lw_Basis_attrifyInt_w_unsafe(ctx, n);
adamc@106 270 }
adamc@106 271
adamc@117 272 void lw_Basis_attrifyFloat_w(lw_context ctx, lw_Basis_float n) {
adamc@117 273 int len;
adamc@117 274
adamc@117 275 lw_check(ctx, FLOATS_MAX);
adamc@117 276 sprintf(ctx->page_front, "%g%n", n, &len);
adamc@117 277 ctx->page_front += len;
adamc@117 278 }
adamc@117 279
adamc@117 280 void lw_Basis_attrifyString_w(lw_context ctx, lw_Basis_string s) {
adamc@117 281 lw_check(ctx, strlen(s) * 6);
adamc@117 282
adamc@106 283 for (; *s; s++) {
adamc@106 284 char c = *s;
adamc@106 285
adamc@106 286 if (c == '"')
adamc@117 287 lw_write_unsafe(ctx, "&quot;");
adamc@136 288 else if (c == '&')
adamc@136 289 lw_write_unsafe(ctx, "&amp;");
adamc@106 290 else if (isprint(c))
adamc@117 291 lw_writec_unsafe(ctx, c);
adamc@106 292 else {
adamc@117 293 lw_write_unsafe(ctx, "&#");
adamc@117 294 lw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@117 295 lw_writec_unsafe(ctx, ';');
adamc@106 296 }
adamc@106 297 }
adamc@106 298 }
adamc@120 299
adamc@120 300
adamc@137 301 char *lw_Basis_urlifyInt(lw_context ctx, lw_Basis_int n) {
adamc@137 302 int len;
adamc@137 303 char *r;
adamc@137 304
adamc@137 305 lw_check_heap(ctx, INTS_MAX);
adamc@137 306 r = ctx->heap_front;
adamc@137 307 sprintf(r, "%d%n", n, &len);
adamc@137 308 ctx->heap_front += len+1;
adamc@137 309 return r;
adamc@120 310 }
adamc@120 311
adamc@137 312 char *lw_Basis_urlifyFloat(lw_context ctx, lw_Basis_float n) {
adamc@137 313 int len;
adamc@137 314 char *r;
adamc@137 315
adamc@137 316 lw_check_heap(ctx, FLOATS_MAX);
adamc@137 317 r = ctx->heap_front;
adamc@137 318 sprintf(r, "%g%n", n, &len);
adamc@137 319 ctx->heap_front += len+1;
adamc@137 320 return r;
adamc@120 321 }
adamc@120 322
adamc@137 323 char *lw_Basis_urlifyString(lw_context ctx, lw_Basis_string s) {
adamc@137 324 char *r, *p;
adamc@137 325
adamc@137 326 lw_check_heap(ctx, strlen(s) * 3 + 1);
adamc@137 327
adamc@137 328 for (r = p = ctx->heap_front; *s; s++) {
adamc@137 329 char c = *s;
adamc@137 330
adamc@137 331 if (c == ' ')
adamc@137 332 *p++ = '+';
adamc@137 333 else if (isalnum(c))
adamc@137 334 *p++ = c;
adamc@137 335 else {
adamc@137 336 sprintf(p, "%%%02X", c);
adamc@137 337 p += 3;
adamc@137 338 }
adamc@137 339 }
adamc@137 340
adamc@137 341 *p++ = 0;
adamc@137 342 ctx->heap_front = p;
adamc@137 343 return r;
adamc@120 344 }
adamc@120 345
adamc@186 346 char *lw_Basis_urlifyBool(lw_context ctx, lw_Basis_bool b) {
adamc@189 347 if (b == lw_Basis_False)
adamc@186 348 return "0";
adamc@186 349 else
adamc@186 350 return "1";
adamc@186 351 }
adamc@186 352
adamc@120 353 static void lw_Basis_urlifyInt_w_unsafe(lw_context ctx, lw_Basis_int n) {
adamc@120 354 int len;
adamc@120 355
adamc@120 356 sprintf(ctx->page_front, "%d%n", n, &len);
adamc@120 357 ctx->page_front += len;
adamc@120 358 }
adamc@120 359
adamc@120 360 void lw_Basis_urlifyInt_w(lw_context ctx, lw_Basis_int n) {
adamc@120 361 lw_check(ctx, INTS_MAX);
adamc@120 362 lw_Basis_urlifyInt_w_unsafe(ctx, n);
adamc@120 363 }
adamc@120 364
adamc@120 365 void lw_Basis_urlifyFloat_w(lw_context ctx, lw_Basis_float n) {
adamc@120 366 int len;
adamc@120 367
adamc@120 368 lw_check(ctx, FLOATS_MAX);
adamc@120 369 sprintf(ctx->page_front, "%g%n", n, &len);
adamc@120 370 ctx->page_front += len;
adamc@120 371 }
adamc@120 372
adamc@120 373 void lw_Basis_urlifyString_w(lw_context ctx, lw_Basis_string s) {
adamc@120 374 lw_check(ctx, strlen(s) * 3);
adamc@120 375
adamc@120 376 for (; *s; s++) {
adamc@120 377 char c = *s;
adamc@120 378
adamc@120 379 if (c == ' ')
adamc@120 380 lw_writec_unsafe(ctx, '+');
adamc@120 381 else if (isalnum(c))
adamc@120 382 lw_writec_unsafe(ctx, c);
adamc@120 383 else {
adamc@120 384 sprintf(ctx->page_front, "%%%02X", c);
adamc@120 385 ctx->page_front += 3;
adamc@120 386 }
adamc@120 387 }
adamc@120 388 }
adamc@120 389
adamc@186 390 void lw_Basis_urlifyBool_w(lw_context ctx, lw_Basis_bool b) {
adamc@189 391 if (b == lw_Basis_False)
adamc@186 392 lw_writec(ctx, '0');
adamc@186 393 else
adamc@186 394 lw_writec(ctx, '1');
adamc@186 395 }
adamc@186 396
adamc@120 397
adamc@144 398 static char *lw_unurlify_advance(char *s) {
adamc@144 399 char *new_s = strchr(s, '/');
adamc@120 400
adamc@120 401 if (new_s)
adamc@120 402 *new_s++ = 0;
adamc@120 403 else
adamc@144 404 new_s = strchr(s, 0);
adamc@144 405
adamc@144 406 return new_s;
adamc@144 407 }
adamc@144 408
adamc@186 409 lw_Basis_int lw_Basis_unurlifyInt(lw_context ctx, char **s) {
adamc@144 410 char *new_s = lw_unurlify_advance(*s);
adamc@144 411 int r;
adamc@120 412
adamc@120 413 r = atoi(*s);
adamc@120 414 *s = new_s;
adamc@120 415 return r;
adamc@120 416 }
adamc@120 417
adamc@186 418 lw_Basis_float lw_Basis_unurlifyFloat(lw_context ctx, char **s) {
adamc@144 419 char *new_s = lw_unurlify_advance(*s);
adamc@120 420 int r;
adamc@120 421
adamc@120 422 r = atof(*s);
adamc@120 423 *s = new_s;
adamc@120 424 return r;
adamc@120 425 }
adamc@120 426
adamc@169 427 static lw_Basis_string lw_unurlifyString_to(lw_context ctx, char *r, char *s) {
adamc@144 428 char *s1, *s2;
adamc@144 429 int n;
adamc@136 430
adamc@144 431 for (s1 = r, s2 = s; *s2; ++s1, ++s2) {
adamc@136 432 char c = *s2;
adamc@136 433
adamc@136 434 switch (c) {
adamc@136 435 case '+':
adamc@136 436 *s1 = ' ';
adamc@136 437 break;
adamc@136 438 case '%':
adamc@169 439 if (s2[1] == 0)
adamc@169 440 lw_error(ctx, FATAL, "Missing first character of escaped URL byte");
adamc@169 441 if (s2[2] == 0)
adamc@169 442 lw_error(ctx, FATAL, "Missing second character of escaped URL byte");
adamc@169 443 if (sscanf(s2+1, "%02X", &n) != 1)
adamc@169 444 lw_error(ctx, FATAL, "Invalid escaped URL byte starting at: %s", s2);
adamc@136 445 *s1 = n;
adamc@136 446 s2 += 2;
adamc@136 447 break;
adamc@136 448 default:
adamc@136 449 *s1 = c;
adamc@136 450 }
adamc@136 451 }
adamc@136 452 *s1++ = 0;
adamc@144 453 return s1;
adamc@144 454 }
adamc@144 455
adamc@186 456 lw_Basis_bool lw_Basis_unurlifyBool(lw_context ctx, char **s) {
adamc@186 457 char *new_s = lw_unurlify_advance(*s);
adamc@186 458 lw_Basis_bool r;
adamc@186 459
adamc@186 460 if (*s[0] == 0 || !strcmp(*s, "0") || !strcmp(*s, "off"))
adamc@189 461 r = lw_Basis_False;
adamc@186 462 else
adamc@189 463 r = lw_Basis_True;
adamc@186 464
adamc@186 465 *s = new_s;
adamc@186 466 return r;
adamc@186 467 }
adamc@186 468
adamc@186 469 lw_Basis_string lw_Basis_unurlifyString(lw_context ctx, char **s) {
adamc@144 470 char *new_s = lw_unurlify_advance(*s);
adamc@144 471 char *r, *s1, *s2;
adamc@144 472 int len, n;
adamc@144 473
adamc@200 474 len = strlen(*s);
adamc@144 475 lw_check_heap(ctx, len + 1);
adamc@144 476
adamc@144 477 r = ctx->heap_front;
adamc@200 478 ctx->heap_front = lw_unurlifyString_to(ctx, ctx->heap_front, *s);
adamc@136 479 *s = new_s;
adamc@136 480 return r;
adamc@120 481 }
adamc@135 482
adamc@135 483
adamc@136 484 char *lw_Basis_htmlifyString(lw_context ctx, lw_Basis_string s) {
adamc@137 485 char *r, *s2;
adamc@137 486
adamc@137 487 lw_check_heap(ctx, strlen(s) * 5 + 1);
adamc@137 488
adamc@137 489 for (r = s2 = ctx->heap_front; *s; s++) {
adamc@137 490 char c = *s;
adamc@137 491
adamc@137 492 switch (c) {
adamc@137 493 case '<':
adamc@137 494 strcpy(s2, "&lt;");
adamc@137 495 s2 += 4;
adamc@137 496 break;
adamc@137 497 case '&':
adamc@137 498 strcpy(s2, "&amp;");
adamc@137 499 s2 += 5;
adamc@137 500 break;
adamc@137 501 default:
adamc@137 502 if (isprint(c))
adamc@137 503 *s2++ = c;
adamc@137 504 else {
adamc@137 505 int len2;
adamc@137 506 sprintf(s2, "&#%d;%n", c, &len2);
adamc@137 507 s2 += len2;
adamc@137 508 }
adamc@137 509 }
adamc@137 510 }
adamc@137 511
adamc@137 512 *s2++ = 0;
adamc@137 513 ctx->heap_front = s2;
adamc@137 514 return r;
adamc@135 515 }
adamc@135 516
adamc@135 517 void lw_Basis_htmlifyString_w(lw_context ctx, lw_Basis_string s) {
adamc@135 518 lw_check(ctx, strlen(s) * 5);
adamc@135 519
adamc@135 520 for (; *s; s++) {
adamc@135 521 char c = *s;
adamc@135 522
adamc@135 523 switch (c) {
adamc@135 524 case '<':
adamc@135 525 lw_write_unsafe(ctx, "&lt;");
adamc@135 526 break;
adamc@135 527 case '&':
adamc@135 528 lw_write_unsafe(ctx, "&amp;");
adamc@135 529 break;
adamc@135 530 default:
adamc@135 531 if (isprint(c))
adamc@135 532 lw_writec_unsafe(ctx, c);
adamc@135 533 else {
adamc@135 534 lw_write_unsafe(ctx, "&#");
adamc@135 535 lw_Basis_attrifyInt_w_unsafe(ctx, c);
adamc@135 536 lw_writec_unsafe(ctx, ';');
adamc@135 537 }
adamc@135 538 }
adamc@135 539 }
adamc@135 540 }
adamc@180 541
adamc@180 542 lw_Basis_string lw_Basis_strcat(lw_context ctx, lw_Basis_string s1, lw_Basis_string s2) {
adamc@180 543 int len = strlen(s1) + strlen(s2) + 1;
adamc@180 544 char *s;
adamc@180 545
adamc@183 546 lw_check_heap(ctx, len);
adamc@180 547
adamc@180 548 s = ctx->heap_front;
adamc@180 549
adamc@180 550 strcpy(s, s1);
adamc@180 551 strcat(s, s2);
adamc@180 552 ctx->heap_front += len;
adamc@180 553
adamc@180 554 return s;
adamc@180 555 }