cgi-bin/tek/lib/luahtml.c
author Timm S. Mueller <tmueller@neoscientists.org>
Fri, 24 Oct 2008 01:35:27 +0200
changeset 251 2de5931b723d
parent 241 c6c81629f54e
permissions -rw-r--r--
tek.os.posix has been moved to tek.lib.posix, module initialization procedure
simplified, added statvfs() under Linux
tmueller@193
     1
tmueller@193
     2
/*
tmueller@193
     3
**	tek.lib.luahtml - Lua+HTML parser/decoding library
tmueller@193
     4
**	Written by Timm S. Mueller <tmueller at neoscientists.org>
tmueller@193
     5
**	See copyright notice in COPYRIGHT
tmueller@193
     6
*/
tmueller@0
     7
tmueller@0
     8
#include <ctype.h>
tmueller@0
     9
#include <stdlib.h>
tmueller@0
    10
#include <string.h>
tmueller@0
    11
#include <lua.h>
tmueller@0
    12
#include <lualib.h>
tmueller@0
    13
#include <lauxlib.h>
tmueller@0
    14
tmueller@0
    15
tmueller@0
    16
#define topfile(L)	((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
tmueller@0
    17
tmueller@0
    18
tmueller@0
    19
static FILE *tofile (lua_State *L) {
tmueller@0
    20
  FILE **f = topfile(L);
tmueller@0
    21
  if (*f == NULL)
tmueller@0
    22
    luaL_error(L, "attempt to use a closed file");
tmueller@0
    23
  return *f;
tmueller@0
    24
}
tmueller@0
    25
tmueller@0
    26
tmueller@0
    27
static unsigned char *encodeutf8(unsigned char *buf, int c)
tmueller@0
    28
{
tmueller@0
    29
	if (c < 128)
tmueller@0
    30
	{
tmueller@0
    31
		*buf++ = c;
tmueller@0
    32
	}
tmueller@0
    33
	else if (c < 2048)
tmueller@0
    34
	{
tmueller@0
    35
		*buf++ = 0xc0 + (c >> 6);
tmueller@0
    36
		*buf++ = 0x80 + (c & 0x3f);
tmueller@0
    37
	}
tmueller@0
    38
	else if (c < 65536)
tmueller@0
    39
	{
tmueller@0
    40
		*buf++ = 0xe0 + (c >> 12);
tmueller@0
    41
		*buf++ = 0x80 + ((c & 0xfff) >> 6);
tmueller@0
    42
		*buf++ = 0x80 + (c & 0x3f);
tmueller@0
    43
	}
tmueller@0
    44
	else if (c < 2097152)
tmueller@0
    45
	{
tmueller@0
    46
		*buf++ = 0xf0 + (c >> 18);
tmueller@0
    47
		*buf++ = 0x80 + ((c & 0x3ffff) >> 12);
tmueller@0
    48
		*buf++ = 0x80 + ((c & 0xfff) >> 6);
tmueller@0
    49
		*buf++ = 0x80 + (c & 0x3f);
tmueller@0
    50
	}
tmueller@0
    51
	else if (c < 67108864)
tmueller@0
    52
	{
tmueller@0
    53
		*buf++ = 0xf8 + (c >> 24);
tmueller@0
    54
		*buf++ = 0x80 + ((c & 0xffffff) >> 18);
tmueller@0
    55
		*buf++ = 0x80 + ((c & 0x3ffff) >> 12);
tmueller@0
    56
		*buf++ = 0x80 + ((c & 0xfff) >> 6);
tmueller@0
    57
		*buf++ = 0x80 + (c & 0x3f);
tmueller@0
    58
	}
tmueller@0
    59
	else
tmueller@0
    60
	{
tmueller@0
    61
		*buf++ = 0xfc + (c >> 30);
tmueller@0
    62
		*buf++ = 0x80 + ((c & 0x3fffffff) >> 24);
tmueller@0
    63
		*buf++ = 0x80 + ((c & 0xffffff) >> 18);
tmueller@0
    64
		*buf++ = 0x80 + ((c & 0x3ffff) >> 12);
tmueller@0
    65
		*buf++ = 0x80 + ((c & 0xfff) >> 6);
tmueller@0
    66
		*buf++ = 0x80 + (c & 0x3f);
tmueller@0
    67
	}
tmueller@0
    68
	return buf;
tmueller@0
    69
}
tmueller@0
    70
tmueller@0
    71
tmueller@0
    72
struct utf8reader
tmueller@0
    73
{
tmueller@0
    74
	int (*readchar)(struct utf8reader *);
tmueller@0
    75
	int accu, numa, min, bufc;
tmueller@0
    76
	const unsigned char *src;
tmueller@0
    77
	size_t srclen;
tmueller@0
    78
	FILE *file;
tmueller@0
    79
	void *udata;
tmueller@0
    80
};
tmueller@0
    81
tmueller@244
    82
tmueller@0
    83
static int readstring(struct utf8reader *rd)
tmueller@0
    84
{
tmueller@0
    85
	if (rd->srclen == 0)
tmueller@0
    86
		return -1;
tmueller@0
    87
	rd->srclen--;
tmueller@0
    88
	return *rd->src++;
tmueller@0
    89
}
tmueller@0
    90
tmueller@244
    91
tmueller@0
    92
static int readfile(struct utf8reader *rd)
tmueller@0
    93
{
tmueller@0
    94
	return fgetc(rd->file);
tmueller@0
    95
}
tmueller@0
    96
tmueller@244
    97
tmueller@0
    98
static int readutf8(struct utf8reader *rd)
tmueller@0
    99
{
tmueller@0
   100
	int c;
tmueller@0
   101
	for (;;)
tmueller@0
   102
	{
tmueller@0
   103
		if (rd->bufc >= 0)
tmueller@0
   104
		{
tmueller@0
   105
			c = rd->bufc;
tmueller@0
   106
			rd->bufc = -1;
tmueller@0
   107
		}
tmueller@0
   108
		else
tmueller@0
   109
			c = rd->readchar(rd);
tmueller@241
   110
tmueller@0
   111
		if (c < 0)
tmueller@0
   112
			return c;
tmueller@0
   113
tmueller@0
   114
		if (c == 254 || c == 255)
tmueller@0
   115
			break;
tmueller@241
   116
tmueller@0
   117
		if (c < 128)
tmueller@0
   118
		{
tmueller@0
   119
			if (rd->numa > 0)
tmueller@0
   120
			{
tmueller@0
   121
				rd->bufc = c;
tmueller@0
   122
				break;
tmueller@0
   123
			}
tmueller@0
   124
			return c;
tmueller@0
   125
		}
tmueller@0
   126
		else if (c < 192)
tmueller@0
   127
		{
tmueller@0
   128
			if (rd->numa == 0)
tmueller@0
   129
				break;
tmueller@0
   130
			rd->accu <<= 6;
tmueller@0
   131
			rd->accu += c - 128;
tmueller@0
   132
			rd->numa--;
tmueller@0
   133
			if (rd->numa == 0)
tmueller@0
   134
			{
tmueller@0
   135
				if (rd->accu == 0 || rd->accu < rd->min ||
tmueller@0
   136
					(rd->accu >= 55296 && rd->accu <= 57343))
tmueller@0
   137
					break;
tmueller@0
   138
				c = rd->accu;
tmueller@0
   139
				rd->accu = 0;
tmueller@0
   140
				return c;
tmueller@0
   141
			}
tmueller@0
   142
		}
tmueller@0
   143
		else
tmueller@0
   144
		{
tmueller@0
   145
			if (rd->numa > 0)
tmueller@0
   146
			{
tmueller@0
   147
				rd->bufc = c;
tmueller@0
   148
				break;
tmueller@0
   149
			}
tmueller@241
   150
tmueller@0
   151
			if (c < 224)
tmueller@0
   152
			{
tmueller@0
   153
				rd->min = 128;
tmueller@0
   154
				rd->accu = c - 192;
tmueller@0
   155
				rd->numa = 1;
tmueller@0
   156
			}
tmueller@0
   157
			else if (c < 240)
tmueller@0
   158
			{
tmueller@0
   159
				rd->min = 2048;
tmueller@0
   160
				rd->accu = c - 224;
tmueller@0
   161
				rd->numa = 2;
tmueller@0
   162
			}
tmueller@0
   163
			else if (c < 248)
tmueller@0
   164
			{
tmueller@0
   165
				rd->min = 65536;
tmueller@0
   166
				rd->accu = c - 240;
tmueller@0
   167
				rd->numa = 3;
tmueller@0
   168
			}
tmueller@0
   169
			else if (c < 252)
tmueller@0
   170
			{
tmueller@0
   171
				rd->min = 2097152;
tmueller@0
   172
				rd->accu = c - 248;
tmueller@0
   173
				rd->numa = 4;
tmueller@0
   174
			}
tmueller@0
   175
			else
tmueller@0
   176
			{
tmueller@0
   177
				rd->min = 67108864;
tmueller@0
   178
				rd->accu = c - 252;
tmueller@0
   179
				rd->numa = 5;
tmueller@0
   180
			}
tmueller@0
   181
		}
tmueller@0
   182
	}
tmueller@0
   183
	/* bad char */
tmueller@0
   184
	rd->accu = 0;
tmueller@0
   185
	rd->numa = 0;
tmueller@0
   186
	return 65533;
tmueller@0
   187
}
tmueller@0
   188
tmueller@0
   189
tmueller@0
   190
typedef enum { PARSER_UNDEF = -1, PARSER_HTML, PARSER_OPEN1, PARSER_OPEN2,
tmueller@0
   191
PARSER_CODE, PARSER_VAR, PARSER_CLOSE } parser_state_t;
tmueller@0
   192
tmueller@0
   193
tmueller@0
   194
static unsigned char *outchar(lua_State *L, unsigned char *buf, parser_state_t state, int c)
tmueller@0
   195
{
tmueller@0
   196
	if (state == PARSER_HTML)
tmueller@0
   197
	{
tmueller@0
   198
		if (c > 127 || c == '[' || c == ']')
tmueller@0
   199
			return buf + sprintf((char *) buf, "&#%02d;", c);
tmueller@0
   200
	}
tmueller@0
   201
	else if (state == PARSER_CODE)
tmueller@0
   202
	{
tmueller@0
   203
		if (c > 127)
tmueller@0
   204
			return encodeutf8(buf, c);
tmueller@0
   205
	}
tmueller@0
   206
	else if (c > 127)
tmueller@0
   207
		luaL_error(L, "Non-ASCII character outside code or HTML context");
tmueller@241
   208
tmueller@0
   209
	*buf++ = c;
tmueller@0
   210
	return buf;
tmueller@0
   211
}
tmueller@0
   212
tmueller@0
   213
tmueller@0
   214
struct readdata
tmueller@0
   215
{
tmueller@0
   216
	/* buffer including " " .. outfunc .. "(": */
tmueller@0
   217
	unsigned char buf0[256];
tmueller@0
   218
	/* pointer into buf0 past outfunc: */
tmueller@0
   219
	unsigned char *buf;
tmueller@0
   220
	/* html+lua parser state: */
tmueller@0
   221
	parser_state_t state;
tmueller@0
   222
	/* utf8 reader state: */
tmueller@0
   223
	struct utf8reader utf8;
tmueller@0
   224
};
tmueller@0
   225
tmueller@0
   226
tmueller@0
   227
static const char *readparsed(lua_State *L, void *udata, size_t *sz)
tmueller@0
   228
{
tmueller@0
   229
	struct readdata *rd = udata;
tmueller@0
   230
	parser_state_t news = rd->state;
tmueller@0
   231
	int c;
tmueller@241
   232
tmueller@0
   233
	while ((c = readutf8(&rd->utf8)) >= 0)
tmueller@0
   234
	{
tmueller@0
   235
		switch (news)
tmueller@0
   236
		{
tmueller@0
   237
			case PARSER_UNDEF:
tmueller@0
   238
				if (c == '<')
tmueller@0
   239
				{
tmueller@0
   240
					news = PARSER_OPEN1;
tmueller@0
   241
					continue;
tmueller@0
   242
				}
tmueller@0
   243
				rd->state = PARSER_HTML;
tmueller@0
   244
				rd->buf[0] = '[';
tmueller@0
   245
				rd->buf[1] = '[';
tmueller@0
   246
				*sz = outchar(L, rd->buf + 2, rd->state, c) - rd->buf0;
tmueller@0
   247
				return (char *) rd->buf0;
tmueller@241
   248
tmueller@0
   249
			case PARSER_HTML:
tmueller@0
   250
				if (c == '<')
tmueller@0
   251
				{
tmueller@0
   252
					news = PARSER_OPEN1;
tmueller@0
   253
					continue;
tmueller@0
   254
				}
tmueller@0
   255
				break;
tmueller@241
   256
tmueller@0
   257
			case PARSER_OPEN1:
tmueller@0
   258
				if (c == '%')
tmueller@0
   259
				{
tmueller@0
   260
					news = PARSER_OPEN2;
tmueller@0
   261
					continue;
tmueller@0
   262
				}
tmueller@0
   263
				rd->buf[0] = '<';
tmueller@0
   264
				rd->buf[1] = c;
tmueller@0
   265
				*sz = 2;
tmueller@0
   266
				return (char *) rd->buf;
tmueller@0
   267
tmueller@0
   268
			case PARSER_OPEN2:
tmueller@0
   269
				if (c == '=')
tmueller@0
   270
				{
tmueller@0
   271
					if (rd->state == PARSER_UNDEF)
tmueller@0
   272
					{
tmueller@0
   273
						rd->state = PARSER_VAR;
tmueller@0
   274
						*sz = rd->buf - rd->buf0;
tmueller@0
   275
						return (char *) rd->buf0;
tmueller@0
   276
					}
tmueller@0
   277
					rd->state = PARSER_VAR;
tmueller@241
   278
					strcpy((char *) rd->buf, "]])");
tmueller@241
   279
					memcpy(rd->buf + 3, rd->buf0, rd->buf - rd->buf0);
tmueller@241
   280
					*sz = 3 + rd->buf - rd->buf0;
tmueller@0
   281
					return (char *) rd->buf;
tmueller@0
   282
				}
tmueller@241
   283
tmueller@0
   284
				if (rd->state == PARSER_UNDEF)
tmueller@0
   285
					rd->state = PARSER_CODE;
tmueller@0
   286
				else
tmueller@0
   287
				{
tmueller@0
   288
					rd->state = PARSER_CODE;
tmueller@0
   289
					rd->buf[0] = ']';
tmueller@0
   290
					rd->buf[1] = ']';
tmueller@0
   291
					rd->buf[2] = ')';
tmueller@0
   292
					rd->buf[3] = ' ';
tmueller@0
   293
					rd->buf[4] = c;
tmueller@0
   294
					*sz = 5;
tmueller@0
   295
					return (char *) rd->buf;
tmueller@0
   296
				}
tmueller@0
   297
				break;
tmueller@241
   298
tmueller@0
   299
			case PARSER_VAR:
tmueller@0
   300
			case PARSER_CODE:
tmueller@0
   301
				if (c == '%')
tmueller@0
   302
				{
tmueller@0
   303
					news = PARSER_CLOSE;
tmueller@0
   304
					continue;
tmueller@0
   305
				}
tmueller@0
   306
				break;
tmueller@241
   307
tmueller@0
   308
			case PARSER_CLOSE:
tmueller@0
   309
				if (c == '>')
tmueller@0
   310
				{
tmueller@241
   311
					size_t len;
tmueller@0
   312
					if (rd->state == PARSER_CODE)
tmueller@0
   313
					{
tmueller@0
   314
						rd->state = PARSER_HTML;
tmueller@0
   315
						rd->buf[0] = '[';
tmueller@0
   316
						rd->buf[1] = '[';
tmueller@0
   317
						*sz = rd->buf + 2 - rd->buf0;
tmueller@0
   318
						return (char *) rd->buf0;
tmueller@0
   319
					}
tmueller@0
   320
					rd->state = PARSER_HTML;
tmueller@241
   321
					strcpy((char *) rd->buf, " or \"nil\")");
tmueller@241
   322
					memcpy(rd->buf + 10, rd->buf0, rd->buf - rd->buf0);
tmueller@241
   323
					len = 10 + rd->buf - rd->buf0;
tmueller@241
   324
					rd->buf[len++] = '[';
tmueller@241
   325
					rd->buf[len++] = '[';
tmueller@241
   326
					*sz = len;
tmueller@0
   327
					return (char *) rd->buf;
tmueller@0
   328
				}
tmueller@0
   329
				rd->buf[0] = '%';
tmueller@0
   330
				rd->buf[1] = c;
tmueller@0
   331
				*sz = 2;
tmueller@0
   332
				return (char *) rd->buf;
tmueller@0
   333
		}
tmueller@241
   334
tmueller@0
   335
		*sz = outchar(L, rd->buf, rd->state, c) - rd->buf;
tmueller@0
   336
		return (char *) rd->buf;
tmueller@0
   337
	}
tmueller@241
   338
tmueller@0
   339
	rd->state = PARSER_UNDEF;
tmueller@0
   340
	if (news == PARSER_HTML)
tmueller@0
   341
	{
tmueller@0
   342
		*sz = 4;
tmueller@0
   343
		return "]]) ";
tmueller@0
   344
	}
tmueller@241
   345
tmueller@0
   346
	return NULL;
tmueller@0
   347
}
tmueller@0
   348
tmueller@0
   349
tmueller@0
   350
static int load(lua_State *L)
tmueller@0
   351
{
tmueller@0
   352
	struct readdata rd;
tmueller@0
   353
	const char *outfunc = lua_tostring(L, 2);
tmueller@0
   354
	const char *chunkname = lua_tostring(L, 3);
tmueller@0
   355
	int res;
tmueller@241
   356
tmueller@0
   357
	if (lua_isuserdata(L, 1))
tmueller@0
   358
	{
tmueller@0
   359
		rd.utf8.file = tofile(L);
tmueller@0
   360
		rd.utf8.readchar = readfile;
tmueller@0
   361
	}
tmueller@0
   362
	else
tmueller@0
   363
	{
tmueller@0
   364
		rd.utf8.src = (unsigned char *) lua_tolstring(L, 1, &rd.utf8.srclen);
tmueller@0
   365
		rd.utf8.readchar = readstring;
tmueller@0
   366
	}
tmueller@241
   367
tmueller@0
   368
	rd.utf8.accu = 0;
tmueller@0
   369
	rd.utf8.numa = 0;
tmueller@0
   370
	rd.utf8.bufc = -1;
tmueller@241
   371
tmueller@0
   372
	rd.state = PARSER_UNDEF;
tmueller@0
   373
	strcpy((char *) rd.buf0, " ");
tmueller@0
   374
	strcat((char *) rd.buf0, outfunc);
tmueller@0
   375
	strcat((char *) rd.buf0, "(");
tmueller@0
   376
	rd.buf = rd.buf0 + strlen((char *) rd.buf0);
tmueller@241
   377
tmueller@0
   378
	res = lua_load(L, readparsed, &rd, chunkname);
tmueller@0
   379
	if (res == 0)
tmueller@0
   380
		return 1;
tmueller@241
   381
tmueller@0
   382
	lua_pushnil(L);
tmueller@0
   383
	lua_insert(L, -2);
tmueller@0
   384
	/* nil, message on stack */
tmueller@0
   385
	return 2;
tmueller@0
   386
}
tmueller@0
   387
tmueller@0
   388
tmueller@244
   389
/*****************************************************************************/
tmueller@244
   390
tmueller@244
   391
tmueller@244
   392
static int encodeform(lua_State *L)
tmueller@244
   393
{
tmueller@244
   394
	struct readdata rd;
tmueller@244
   395
	char sbuf[16];
tmueller@244
   396
	luaL_Buffer b;
tmueller@244
   397
	int c;
tmueller@244
   398
tmueller@244
   399
	if (lua_isuserdata(L, 1))
tmueller@244
   400
	{
tmueller@244
   401
		rd.utf8.file = tofile(L);
tmueller@244
   402
		rd.utf8.readchar = readfile;
tmueller@244
   403
	}
tmueller@244
   404
	else
tmueller@244
   405
	{
tmueller@244
   406
		rd.utf8.src = (unsigned char *) lua_tolstring(L, 1, &rd.utf8.srclen);
tmueller@244
   407
		rd.utf8.readchar = readstring;
tmueller@244
   408
	}
tmueller@244
   409
tmueller@244
   410
	luaL_buffinit(L, &b);
tmueller@244
   411
tmueller@244
   412
	rd.utf8.accu = 0;
tmueller@244
   413
	rd.utf8.numa = 0;
tmueller@244
   414
	rd.utf8.bufc = -1;
tmueller@244
   415
tmueller@244
   416
	while ((c = readutf8(&rd.utf8)) >= 0)
tmueller@244
   417
	{
tmueller@244
   418
		switch(c)
tmueller@244
   419
		{
tmueller@244
   420
			case 34:
tmueller@244
   421
				luaL_addlstring(&b, "&quot;", 6);
tmueller@244
   422
				break;
tmueller@244
   423
			case 38:
tmueller@244
   424
				luaL_addlstring(&b, "&amp;", 5);
tmueller@244
   425
				break;
tmueller@244
   426
			case 60:
tmueller@244
   427
				luaL_addlstring(&b, "&lt;", 4);
tmueller@244
   428
				break;
tmueller@244
   429
			case 62:
tmueller@244
   430
				luaL_addlstring(&b, "&gt;", 4);
tmueller@244
   431
				break;
tmueller@244
   432
			default:
tmueller@244
   433
				if (c == 91 || c == 93 || c > 126)
tmueller@244
   434
				{
tmueller@244
   435
					sprintf(sbuf, "&#%03d;", c);
tmueller@244
   436
					luaL_addstring(&b, sbuf);
tmueller@244
   437
				}
tmueller@244
   438
				else
tmueller@244
   439
					luaL_addchar(&b, c);
tmueller@244
   440
		}
tmueller@244
   441
	}
tmueller@244
   442
tmueller@244
   443
	luaL_pushresult(&b);
tmueller@244
   444
	return 1;
tmueller@244
   445
}
tmueller@244
   446
tmueller@244
   447
tmueller@244
   448
/*****************************************************************************/
tmueller@244
   449
tmueller@244
   450
tmueller@0
   451
static const luaL_Reg lib[] =
tmueller@0
   452
{
tmueller@0
   453
	{ "load", load },
tmueller@244
   454
	{ "encodeform", encodeform },
tmueller@0
   455
	{ NULL, NULL }
tmueller@0
   456
};
tmueller@0
   457
tmueller@0
   458
tmueller@188
   459
int luaopen_tek_lib_luahtml(lua_State *L)
tmueller@0
   460
{
tmueller@188
   461
	luaL_register(L, "tek.lib.luahtml", lib);
tmueller@0
   462
	return 0;
tmueller@0
   463
}