/* ltexlib.c
Copyright 2006-2010 Taco Hoekwater <
[email protected]>
This file is part of LuaTeX.
LuaTeX is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
LuaTeX is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU General Public License along
with LuaTeX; if not, see <
http://www.gnu.org/licenses/>. */
#include "lua/luatex-api.h"
#include "ptexlib.h"
static const char _svn_version[] =
"$Id: ltexlib.c 4274 2011-05-18 11:23:45Z taco $ $URL:
http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/lua/ltexlib.c $";
#define attribute(A) eqtb[attribute_base+(A)].hh.rh
#define dimen(A) eqtb[scaled_base+(A)].hh.rh
#undef skip
#define skip(A) eqtb[skip_base+(A)].hh.rh
#define mu_skip(A) eqtb[mu_skip_base+(A)].hh.rh
#define count(A) eqtb[count_base+(A)].hh.rh
#define box(A) equiv(box_base+(A))
typedef struct {
char *text;
unsigned int tsize;
void *next;
boolean partial;
int cattable;
} rope;
typedef struct {
rope *head;
rope *tail;
char complete; /* currently still writing ? */
} spindle;
#define PARTIAL_LINE 1
#define FULL_LINE 0
#define write_spindle spindles[spindle_index]
#define read_spindle spindles[(spindle_index-1)]
static int spindle_size = 0;
static spindle *spindles = NULL;
static int spindle_index = 0;
static void luac_store(lua_State * L, int i, int partial, int cattable)
{
char *st;
const char *sttemp;
size_t tsize;
rope *rn = NULL;
sttemp = lua_tolstring(L, i, &tsize);
st = xmalloc((unsigned) (tsize + 1));
memcpy(st, sttemp, (tsize + 1));
if (st) {
luacstrings++;
rn = (rope *) xmalloc(sizeof(rope));
rn->text = st;
rn->tsize = (unsigned) tsize;
rn->partial = partial;
rn->cattable = cattable;
rn->next = NULL;
if (write_spindle.head == NULL) {
assert(write_spindle.tail == NULL);
write_spindle.head = rn;
} else {
write_spindle.tail->next = rn;
}
write_spindle.tail = rn;
write_spindle.complete = 0;
}
}
static int do_luacprint(lua_State * L, int partial, int deftable)
{
int i, n;
int cattable = deftable;
int startstrings = 1;
n = lua_gettop(L);
if (cattable != NO_CAT_TABLE) {
if (lua_type(L, 1) == LUA_TNUMBER && n > 1) {
lua_number2int(cattable, lua_tonumber(L, 1));
startstrings = 2;
if (cattable != -1 && cattable != -2 && !valid_catcode_table(cattable)) {
cattable = DEFAULT_CAT_TABLE;
}
}
}
if (lua_type(L, startstrings) == LUA_TTABLE) {
for (i = 1;; i++) {
lua_rawgeti(L, startstrings, i);
if (lua_isstring(L, -1)) {
luac_store(L, -1, partial, cattable);
lua_pop(L, 1);
} else {
break;
}
}
} else {
for (i = startstrings; i <= n; i++) {
if (!lua_isstring(L, i)) {
luaL_error(L, "no string to print");
}
luac_store(L, i, partial, cattable);
}
}
return 0;
}
static int luacwrite(lua_State * L)
{
return do_luacprint(L, FULL_LINE, NO_CAT_TABLE);
}
static int luacprint(lua_State * L)
{
return do_luacprint(L, FULL_LINE, DEFAULT_CAT_TABLE);
}
static int luacsprint(lua_State * L)
{
return do_luacprint(L, PARTIAL_LINE, DEFAULT_CAT_TABLE);
}
static int luactprint(lua_State * L)
{
int i, j, n;
int cattable, startstrings;
n = lua_gettop(L);
for (i = 1; i <= n; i++) {
cattable = DEFAULT_CAT_TABLE;
startstrings = 1;
if (lua_type(L, i) != LUA_TTABLE) {
luaL_error(L, "no string to print");
}
lua_pushvalue(L, i); /* push the table */
lua_pushnumber(L, 1);
lua_gettable(L, -2);
if (lua_type(L, -1) == LUA_TNUMBER) {
lua_number2int(cattable, lua_tonumber(L, -1));
startstrings = 2;
if (cattable != -1 && cattable != -2 && !valid_catcode_table(cattable)) {
cattable = DEFAULT_CAT_TABLE;
}
}
lua_pop(L, 1);
for (j = startstrings;; j++) {
lua_pushnumber(L, j);
lua_gettable(L, -2);
if (lua_type(L, -1) == LUA_TSTRING) {
luac_store(L, -1, PARTIAL_LINE, cattable);
lua_pop(L, 1);
} else {
lua_pop(L, 1);
break;
}
}
lua_pop(L, 1); /* pop the table */
}
return 0;
}
int luacstring_cattable(void)
{
return (int) read_spindle.tail->cattable;
}
int luacstring_partial(void)
{
return read_spindle.tail->partial;
}
int luacstring_final_line(void)
{
return (read_spindle.tail->next == NULL);
}
int luacstring_input(void)
{
char *st;
int ret;
rope *t = read_spindle.head;
if (!read_spindle.complete) {
read_spindle.complete = 1;
read_spindle.tail = NULL;
}
if (t == NULL) {
if (read_spindle.tail != NULL)
free(read_spindle.tail);
read_spindle.tail = NULL;
return 0;
}
if (t->text != NULL) {
st = t->text;
/* put that thing in the buffer */
last = first;
ret = last;
check_buffer_overflow(last + (int) t->tsize);
while (t->tsize-- > 0)
buffer[last++] = (packed_ASCII_code) * st++;
if (!t->partial) {
while (last - 1 > ret && buffer[last - 1] == ' ')
last--;
}
free(t->text);
t->text = NULL;
}
if (read_spindle.tail != NULL) { /* not a one-liner */
free(read_spindle.tail);
}
read_spindle.tail = t;
read_spindle.head = t->next;
return 1;
}
/* open for reading, and make a new one for writing */
void luacstring_start(int n)
{
(void) n; /* for -W */
spindle_index++;
if (spindle_size == spindle_index) { /* add a new one */
spindles =
xrealloc(spindles,
(unsigned) (sizeof(spindle) *
(unsigned) (spindle_size + 1)));
spindles[spindle_index].head = NULL;
spindles[spindle_index].tail = NULL;
spindles[spindle_index].complete = 0;
spindle_size++;
}
}
/* close for reading */
void luacstring_close(int n)
{
rope *next, *t;
(void) n; /* for -W */
next = read_spindle.head;
while (next != NULL) {
if (next->text != NULL)
free(next->text);
t = next;
next = next->next;
free(t);
}
read_spindle.head = NULL;
if (read_spindle.tail != NULL)
free(read_spindle.tail);
read_spindle.tail = NULL;
read_spindle.complete = 0;
spindle_index--;
}
/* local (static) versions */
#define check_index_range(j,s) \
if (j<0 || j > 65535) { \
luaL_error(L, "incorrect index value %d for tex.%s()", (int)j, s); }
static const char *scan_integer_part(lua_State * L, const char *ss, int *ret,
int *radix_ret)
{
boolean negative = false; /* should the answer be negated? */
int m; /* |$2^{31}$ / radix|, the threshold of danger */
int d; /* the digit just scanned */
boolean vacuous; /* have no digits appeared? */
boolean OK_so_far; /* has an error message been issued? */
int radix = 0; /* the radix of the integer */
int c = 0; /* the current character */
const char *s; /* where we stopped in the string |ss| */
integer cur_val = 0; /* return value */
s = ss;
do {
do {
c = *s++;
} while (c && c == ' ');
if (c == '-') {
negative = !negative;
c = '+';
}
} while (c == '+');
radix = 10;
m = 214748364;
if (c == '\'') {
radix = 8;
m = 02000000000;
c = *s++;
} else if (c == '"') {
radix = 16;
m = 01000000000;
c = *s++;
}
vacuous = true;
cur_val = 0;
OK_so_far = true;
/* Accumulate the constant until |cur_tok| is not a suitable digit */
while (1) {
if ((c < '0' + radix) && (c >= '0') && (c <= '0' + 9)) {
d = c - '0';
} else if (radix == 16) {
if ((c <= 'A' + 5) && (c >= 'A')) {
d = c - 'A' + 10;
} else if ((c <= 'a' + 5) && (c >= 'a')) {
d = c - 'a' + 10;
} else {
break;
}
} else {
break;
}
vacuous = false;
if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) {
if (OK_so_far) {
luaL_error(L, "Number too big");
cur_val = infinity;
OK_so_far = false;
}
} else {
cur_val = cur_val * radix + d;
}
c = *s++;
}
if (vacuous) {
/* Express astonishment that no number was here */
luaL_error(L, "Missing number, treated as zero");
}
if (negative)
cur_val = -cur_val;
*ret = cur_val;
*radix_ret = radix;
if (c != ' ' && s > ss)
s--;
return s;
}
#define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
static const char *scan_dimen_part(lua_State * L, const char *ss, int *ret)
/* sets |cur_val| to a dimension */
{
boolean negative = false; /* should the answer be negated? */
int f = 0; /* numerator of a fraction whose denominator is $2^{16}$ */
int num, denom; /* conversion ratio for the scanned units */
int k; /* number of digits in a decimal fraction */
scaled v; /* an internal dimension */
int save_cur_val; /* temporary storage of |cur_val| */
int arith_error = false;
int c; /* the current character */
const char *s = ss; /* where we are in the string */
int radix = 0; /* the current radix */
int rdig[18]; /* to save the |dig[]| array */
int saved_tex_remainder; /* to save |tex_remainder| */
int saved_arith_error; /* to save |arith_error| */
int saved_cur_val; /* to save the global |cur_val| */
saved_tex_remainder = tex_remainder;
saved_arith_error = arith_error;
saved_cur_val = cur_val;
/* Get the next non-blank non-sign... */
do {
/* Get the next non-blank non-call token */
do {
c = *s++;
} while (c && c == ' ');
if (c == '-') {
negative = !negative;
c = '+';
}
} while (c == '+');
if (c == ',') {
c = '.';
}
if (c != '.') {
s = scan_integer_part(L, (s > ss ? (s - 1) : ss), &cur_val, &radix);
c = *s;
} else {
radix = 10;
cur_val = 0;
c = *(--s);
}
if (c == ',')
c = '.';
if ((radix == 10) && (c == '.')) {
/* Scan decimal fraction */
for (k = 0; k < 18; k++)
rdig[k] = dig[k];
k = 0;
s++; /* get rid of the '.' */
while (1) {
c = *s++;
if ((c > '0' + 9) || (c < '0'))
break;
if (k < 17) { /* digits for |k>=17| cannot affect the result */
dig[k++] = c - '0';
}
}
f = round_decimals(k);
if (c != ' ')
c = *(--s);
for (k = 0; k < 18; k++)
dig[k] = rdig[k];
}
if (cur_val < 0) { /* in this case |f=0| */
negative = !negative;
cur_val = -cur_val;
}
/* Scan for (u)units that are internal dimensions;
|goto attach_sign| with |cur_val| set if found */
save_cur_val = cur_val;
/* Get the next non-blank non-call... */
do {
c = *s++;
} while (c && c == ' ');
if (c != ' ')
c = *(--s);
if (strncmp(s, "em", 2) == 0) {
s += 2;
v = (quad(get_cur_font()));
} else if (strncmp(s, "ex", 2) == 0) {
s += 2;
v = (x_height(get_cur_font()));
} else if (strncmp(s, "px", 2) == 0) {
s += 2;
v = dimen_par(pdf_px_dimen_code);
} else {
goto NOT_FOUND;
}
c = *s++;
if (c != ' ') {
c = *(--s);
}
cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
goto ATTACH_SIGN;
NOT_FOUND:
/* Scan for (m)\.{mu} units and |goto attach_fraction| */
if (strncmp(s, "mu", 2) == 0) {
s += 2;
goto ATTACH_FRACTION;
}
if (strncmp(s, "true", 4) == 0) {
/* Adjust (f)for the magnification ratio */
s += 4;
prepare_mag();
if (int_par(mag_code) != 1000) {
cur_val = xn_over_d(cur_val, 1000, int_par(mag_code));
f = (1000 * f + 0200000 * tex_remainder) / int_par(mag_code);
cur_val = cur_val + (f / 0200000);
f = f % 0200000;
}
do {
c = *s++;
} while (c && c == ' ');
c = *(--s);
}
if (strncmp(s, "pt", 2) == 0) {
s += 2;
goto ATTACH_FRACTION; /* the easy case */
}
/* Scan for (a)all other units and adjust |cur_val| and |f| accordingly;
|goto done| in the case of scaled points */
if (strncmp(s, "in", 2) == 0) {
s += 2;
set_conversion(7227, 100);
} else if (strncmp(s, "pc", 2) == 0) {
s += 2;
set_conversion(12, 1);
} else if (strncmp(s, "cm", 2) == 0) {
s += 2;
set_conversion(7227, 254);
} else if (strncmp(s, "mm", 2) == 0) {
s += 2;
set_conversion(7227, 2540);
} else if (strncmp(s, "bp", 2) == 0) {
s += 2;
set_conversion(7227, 7200);
} else if (strncmp(s, "dd", 2) == 0) {
s += 2;
set_conversion(1238, 1157);
} else if (strncmp(s, "cc", 2) == 0) {
s += 2;
set_conversion(14856, 1157);
} else if (strncmp(s, "nd", 2) == 0) {
s += 2;
set_conversion(685, 642);
} else if (strncmp(s, "nc", 2) == 0) {
s += 2;
set_conversion(1370, 107);
} else if (strncmp(s, "sp", 2) == 0) {
s += 2;
goto DONE;
} else {
/* Complain about unknown unit and |goto done2| */
luaL_error(L, "Illegal unit of measure (pt inserted)");
goto DONE2;
}
cur_val = xn_over_d(cur_val, num, denom);
f = (num * f + 0200000 * tex_remainder) / denom;
cur_val = cur_val + (f / 0200000);
f = f % 0200000;
DONE2:
ATTACH_FRACTION:
if (cur_val >= 040000)
arith_error = true;
else
cur_val = cur_val * 65536 + f;
DONE:
/* Scan an optional space */
c = *s++;
if (c != ' ')
s--;
ATTACH_SIGN:
if (arith_error || (abs(cur_val) >= 010000000000)) {
/* Report that this dimension is out of range */
luaL_error(L, "Dimension too large");
cur_val = max_dimen;
}
if (negative)
cur_val = -cur_val;
*ret = cur_val;
tex_remainder = saved_tex_remainder;
arith_error = saved_arith_error;
cur_val = saved_cur_val;
return s;
}
int dimen_to_number(lua_State * L, const char *s)
{
int j = 0;
const char *d = scan_dimen_part(L, s, &j);
if (*d) {
luaL_error(L, "conversion failed (trailing junk?)");
j = 0;
}
return j;
}
static int tex_scaledimen(lua_State * L)
{ /* following vsetdimen() */
int sp;
if (!lua_isnumber(L, 1)) {
if (lua_isstring(L, 1)) {
sp = dimen_to_number(L, lua_tostring(L, 1));
} else {
luaL_error(L, "argument must be a string or a number");
return 0;
}
} else {
lua_number2int(sp, lua_tonumber(L, 1));
}
lua_pushnumber(L, sp);
return 1;
}
static int texerror (lua_State * L)
{
int i, n, l;
const char **errhlp = NULL;
const char *error = luaL_checkstring(L,1);
n = lua_gettop(L);
if (n==2 && lua_type(L, n) == LUA_TTABLE) {
l = 1; /* |errhlp| is terminated by a NULL entry */
for (i = 1;; i++) {
lua_rawgeti(L, n, i);
if (lua_isstring(L, -1)) {
l++;
lua_pop(L, 1);
} else {
lua_pop(L, 1);
break;
}
}
if (l>1) {
errhlp = xmalloc(l * sizeof(char *));
memset(errhlp,0,l * sizeof(char *));
for (i = 1;; i++) {
lua_rawgeti(L, n, i);
if (lua_isstring(L, -1)) {
errhlp[(i-1)] = lua_tostring(L,-1);
lua_pop(L, 1);
} else {
break;
}
}
}
}
deletions_allowed = false;
tex_error(error, errhlp);
if (errhlp)
xfree(errhlp);
deletions_allowed = true;
return 0;
}
static int get_item_index(lua_State * L, int i, int base)
{
size_t kk;
int k;
int cur_cs;
const char *s;
if (lua_type(L, i) == LUA_TSTRING) {
s = lua_tolstring(L, i, &kk);
cur_cs = string_lookup(s, kk);
if (cur_cs == undefined_control_sequence || cur_cs == undefined_cs_cmd) {
k = -1; /* guarandeed invalid */
} else {
k = (equiv(cur_cs) - base);
}
} else {
k = (int) luaL_checkinteger(L, i);
}
return k;
}
static int vsetdimen(lua_State * L, int is_global)
{
int i, j, err;
int k;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
i = lua_gettop(L);
j = 0;
/* find the value */
if (!lua_isnumber(L, i)) {
if (lua_isstring(L, i)) {
j = dimen_to_number(L, lua_tostring(L, i));
} else {
luaL_error(L, "unsupported value type");
}
} else {
lua_number2int(j, lua_tonumber(L, i));
}
k = get_item_index(L, (i - 1), scaled_base);
check_index_range(k, "setdimen");
err = set_tex_dimen_register(k, j);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int setdimen(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsetdimen(L, isglobal);
}
static int getdimen(lua_State * L)
{
int j;
int k;
k = get_item_index(L, lua_gettop(L), scaled_base);
check_index_range(k, "getdimen");
j = get_tex_dimen_register(k);
lua_pushnumber(L, j);
return 1;
}
static int vsetskip(lua_State * L, int is_global)
{
int i, err;
halfword *j;
int k;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
i = lua_gettop(L);
j = check_isnode(L, i); /* the value */
k = get_item_index(L, (i - 1), skip_base);
check_index_range(k, "setskip"); /* the index */
err = set_tex_skip_register(k, *j);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int setskip(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsetskip(L, isglobal);
}
static int getskip(lua_State * L)
{
halfword j;
int k;
k = get_item_index(L, lua_gettop(L), skip_base);
check_index_range(k, "getskip");
j = get_tex_skip_register(k);
lua_nodelib_push_fast(L, j);
return 1;
}
static int vsetcount(lua_State * L, int is_global)
{
int i, j, err;
int k;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
i = lua_gettop(L);
j = (int) luaL_checkinteger(L, i);
k = get_item_index(L, (i - 1), count_base);
check_index_range(k, "setcount");
err = set_tex_count_register(k, j);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int setcount(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsetcount(L, isglobal);
}
static int getcount(lua_State * L)
{
int j;
int k;
k = get_item_index(L, lua_gettop(L), count_base);
check_index_range(k, "getcount");
j = get_tex_count_register(k);
lua_pushnumber(L, j);
return 1;
}
static int vsetattribute(lua_State * L, int is_global)
{
int i, j, err;
int k;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
i = lua_gettop(L);
j = (int) luaL_checkinteger(L, i);
k = get_item_index(L, (i - 1), attribute_base);
check_index_range(k, "setattribute");
err = set_tex_attribute_register(k, j);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int setattribute(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsetattribute(L, isglobal);
}
static int getattribute(lua_State * L)
{
int j;
int k;
k = get_item_index(L, lua_gettop(L), attribute_base);
check_index_range(k, "getattribute");
j = get_tex_attribute_register(k);
lua_pushnumber(L, j);
return 1;
}
static int vsettoks(lua_State * L, int is_global)
{
int i, err;
int k;
lstring str;
char *s;
const char *ss;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
i = lua_gettop(L);
if (!lua_isstring(L, i)) {
luaL_error(L, "unsupported value type");
}
ss = lua_tolstring(L, i, &str.l);
s = xmalloc (str.l+1);
memcpy (s, ss, str.l+1);
str.s = (unsigned char *)s;
k = get_item_index(L, (i - 1), toks_base);
check_index_range(k, "settoks");
err = set_tex_toks_register(k, str);
xfree(str.s);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int settoks(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsettoks(L, isglobal);
}
static int gettoks(lua_State * L)
{
int k;
str_number t;
char *ss;
k = get_item_index(L, lua_gettop(L), toks_base);
check_index_range(k, "gettoks");
t = get_tex_toks_register(k);
ss = makecstring(t);
lua_pushstring(L, ss);
free(ss);
flush_str(t);
return 1;
}
static int get_box_id(lua_State * L, int i)
{
const char *s;
int cur_cs, cur_cmd;
size_t k = 0;
int j = -1;
if (lua_type(L, i) == LUA_TSTRING) {
s = lua_tolstring(L, i, &k);
cur_cs = string_lookup(s, k);
cur_cmd = eq_type(cur_cs);
if (cur_cmd == char_given_cmd ||
cur_cmd == math_given_cmd || cur_cmd == omath_given_cmd) {
j = equiv(cur_cs);
}
} else {
lua_number2int(j, lua_tonumber(L, (i)));
}
return j;
}
static int getbox(lua_State * L)
{
int k, t;
k = get_box_id(L, -1);
check_index_range(k, "getbox");
t = get_tex_box_register(k);
nodelist_to_lua(L, t);
return 1;
}
static int vsetbox(lua_State * L, int is_global)
{
int i, j, k, err;
int save_global_defs = int_par(global_defs_code);
if (is_global)
int_par(global_defs_code) = 1;
k = get_box_id(L, -2);
check_index_range(k, "setbox");
i = get_tex_box_register(k);
if (lua_isboolean(L, -1)) {
j = lua_toboolean(L, -1);
if (j == 0)
j = null;
else
return 0;
} else {
j = nodelist_from_lua(L);
if (j != null && type(j) != hlist_node && type(j) != vlist_node) {
luaL_error(L, "setbox: incompatible node type (%s)\n",
get_node_name(type(j), subtype(j)));
return 0;
}
}
err = set_tex_box_register(k, j);
int_par(global_defs_code) = save_global_defs;
if (err) {
luaL_error(L, "incorrect value");
}
return 0;
}
static int setbox(lua_State * L)
{
int isglobal = 0;
int n = lua_gettop(L);
if (n == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
return vsetbox(L, isglobal);
}
#define check_char_range(j,s,lim) \
if (j<0 || j >= lim) { \
luaL_error(L, "incorrect character value %d for tex.%s()", (int)j, s); }
static int setcode (lua_State *L, void (*setone)(int,halfword,quarterword),
void (*settwo)(int,halfword,quarterword), const char *name, int lim)
{
int ch;
halfword val, ucval;
int level = cur_level;
int n = lua_gettop(L);
int f = 1;
if (n>1 && lua_type(L,1) == LUA_TTABLE)
f++;
if (n>2 && lua_isstring(L, f)) {
const char *s = lua_tostring(L, f);
if (strcmp(s, "global") == 0) {
level = level_one;
f++;
}
}
ch = (int) luaL_checkinteger(L, f);
check_char_range(ch, name, 65536*17);
val = (halfword) luaL_checkinteger(L, f+1);
check_char_range(val, name, lim);
(setone)(ch, val, level);
if (settwo != NULL && n-f == 2) {
ucval = (halfword) luaL_checkinteger(L, f+2);
check_char_range(ucval, name, lim);
(settwo)(ch, ucval, level);
}
return 0;
}
static int setlccode(lua_State * L)
{
return setcode(L, &set_lc_code, &set_uc_code, "setlccode", 65536*17);
}
static int getlccode(lua_State * L)
{
int ch = (int) luaL_checkinteger(L, -1);
check_char_range(ch, "getlccode", 65536*17);
lua_pushnumber(L, get_lc_code(ch));
return 1;
}
static int setuccode(lua_State * L)
{
return setcode(L, &set_uc_code, &set_lc_code, "setuccode", 65536*17);
}
static int getuccode(lua_State * L)
{
int ch = (int) luaL_checkinteger(L, -1);
check_char_range(ch, "getuccode", 65536*17);
lua_pushnumber(L, get_uc_code(ch));
return 1;
}
static int setsfcode(lua_State * L)
{
return setcode(L, &set_sf_code, NULL, "setsfcode", 32768);
}
static int getsfcode(lua_State * L)
{
int ch = (int) luaL_checkinteger(L, -1);
check_char_range(ch, "getsfcode", 65536*17);
lua_pushnumber(L, get_sf_code(ch));
return 1;
}
static int setcatcode(lua_State * L)
{
int ch;
halfword val;
int level = cur_level;
int cattable = int_par(cat_code_table_code);
int n = lua_gettop(L);
int f = 1;
if (n>1 && lua_type(L,1) == LUA_TTABLE)
f++;
if (n>2 && lua_isstring(L, f)) {
const char *s = lua_tostring(L, f);
if (strcmp(s, "global") == 0) {
level = level_one;
f++;
}
}
if (n-f == 2) {
cattable = (int) luaL_checkinteger(L, -3);
}
ch = (int) luaL_checkinteger(L, -2);
check_char_range(ch, "setcatcode", 65536*17);
val = (halfword) luaL_checkinteger(L, -1);
check_char_range(val, "setcatcode", 16);
set_cat_code(cattable, ch, val, level);
return 0;
}
static int getcatcode(lua_State * L)
{
int cattable = int_par(cat_code_table_code);
int ch = (int) luaL_checkinteger(L, -1);
if (lua_gettop(L)>=2 && lua_type(L,-2)==LUA_TNUMBER) {
cattable = luaL_checkinteger(L, -2);
}
check_char_range(ch, "getcatcode", 65536*17);
lua_pushnumber(L, get_cat_code(cattable, ch));
return 1;
}
static int setmathcode(lua_State * L)
{
int ch;
halfword cval, fval, chval;
int level = cur_level;
int n = lua_gettop(L);
int f = 1;
if (n>1 && lua_type(L,1) == LUA_TTABLE)
f++;
if (n>2 && lua_isstring(L, f)) {
const char *s = lua_tostring(L, f);
if (strcmp(s, "global") == 0) {
level = level_one;
f++;
}
}
if (n-f!=1 || lua_type(L,f+1) != LUA_TTABLE) {
luaL_error(L, "Bad arguments for tex.setmathcode()");
}
ch = (int) luaL_checkinteger(L, -2);
check_char_range(ch, "setmathcode", 65536*17);
lua_rawgeti(L, -1, 1);
cval = (halfword) luaL_checkinteger(L, -1);
lua_rawgeti(L, -2, 2);
fval = (halfword) luaL_checkinteger(L, -1);
lua_rawgeti(L, -3, 3);
chval = (halfword) luaL_checkinteger(L, -1);
lua_pop(L,3);
check_char_range(cval, "setmathcode", 8);
check_char_range(fval, "setmathcode", 256);
check_char_range(chval, "setmathcode", 65536*17);
set_math_code(ch, xetex_mathcode, cval,fval, chval, (quarterword) (level));
return 0;
}
static int getmathcode(lua_State * L)
{
mathcodeval mval = { 0, 0, 0, 0 };
int ch = (int) luaL_checkinteger(L, -1);
check_char_range(ch, "getmathcode", 65536*17);
mval = get_math_code(ch);
lua_newtable(L);
lua_pushnumber(L,mval.class_value);
lua_rawseti(L, -2, 1);
lua_pushnumber(L,mval.family_value);
lua_rawseti(L, -2, 2);
lua_pushnumber(L,mval.character_value);
lua_rawseti(L, -2, 3);
return 1;
}
static int setdelcode(lua_State * L)
{
int ch;
halfword sfval, scval, lfval, lcval;
int level = cur_level;
int n = lua_gettop(L);
int f = 1;
if (n>1 && lua_type(L,1) == LUA_TTABLE)
f++;
if (n>2 && lua_isstring(L, f)) {
const char *s = lua_tostring(L, f);
if (strcmp(s, "global") == 0) {
level = level_one;
f++;
}
}
if (n-f!=1 || lua_type(L,f+1) != LUA_TTABLE) {
luaL_error(L, "Bad arguments for tex.setdelcode()");
}
ch = (int) luaL_checkinteger(L, -2);
check_char_range(ch, "setdelcode", 65536*17);
lua_rawgeti(L, -1, 1);
sfval = (halfword) luaL_checkinteger(L, -1);
lua_rawgeti(L, -2, 2);
scval = (halfword) luaL_checkinteger(L, -1);
lua_rawgeti(L, -3, 3);
lfval = (halfword) luaL_checkinteger(L, -1);
lua_rawgeti(L, -4, 4);
lcval = (halfword) luaL_checkinteger(L, -1);
lua_pop(L,4);
check_char_range(sfval, "setdelcode", 256);
check_char_range(scval, "setdelcode", 65536*17);
check_char_range(lfval, "setdelcode", 256);
check_char_range(lcval, "setdelcode", 65536*17);
set_del_code(ch, xetex_mathcode, sfval, scval, lfval, lcval, (quarterword) (level));
return 0;
}
static int getdelcode(lua_State * L)
{
delcodeval mval = { 0, 0, 0, 0, 0, 0 };
int ch = (int) luaL_checkinteger(L, -1);
check_char_range(ch, "getdelcode", 65536*17);
mval = get_del_code(ch);
/* lua_pushnumber(L, mval.class_value); */
/* lua_pushnumber(L, mval.origin_value); */
lua_newtable(L);
lua_pushnumber(L,mval.small_family_value);
lua_rawseti(L, -2, 1);
lua_pushnumber(L,mval.small_character_value);
lua_rawseti(L, -2, 2);
lua_pushnumber(L,mval.large_family_value);
lua_rawseti(L, -2, 3);
lua_pushnumber(L,mval.large_character_value);
lua_rawseti(L, -2, 4);
return 1;
}
static int settex(lua_State * L)
{
const char *st;
int i, j, texstr;
size_t k;
int cur_cs, cur_cmd;
int isglobal = 0;
j = 0;
i = lua_gettop(L);
if (lua_isstring(L, (i - 1))) {
st = lua_tolstring(L, (i - 1), &k);
texstr = maketexlstring(st, k);
if (is_primitive(texstr)) {
if (i == 3 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
isglobal = 1;
}
cur_cs = string_lookup(st, k);
flush_str(texstr);
cur_cmd = eq_type(cur_cs);
if (is_int_assign(cur_cmd)) {
if (lua_isnumber(L, i)) {
int luai;
lua_number2int(luai, lua_tonumber(L, i));
assign_internal_value((isglobal ? 4 : 0),
equiv(cur_cs), luai);
} else {
luaL_error(L, "unsupported value type");
}
} else if (is_dim_assign(cur_cmd)) {
if (!lua_isnumber(L, i)) {
if (lua_isstring(L, i)) {
j = dimen_to_number(L, lua_tostring(L, i));
} else {
luaL_error(L, "unsupported value type");
}
} else {
lua_number2int(j, lua_tonumber(L, i));
}
assign_internal_value((isglobal ? 4 : 0), equiv(cur_cs), j);
} else if (is_glue_assign(cur_cmd)) {
halfword *j = check_isnode(L, i); /* the value */
{ int a = isglobal;
define(equiv(cur_cs), assign_glue_cmd, *j);
}
} else if (is_toks_assign(cur_cmd)) {
if (lua_isstring(L, i)) {
j = tokenlist_from_lua(L); /* uses stack -1 */
assign_internal_value((isglobal ? 4 : 0), equiv(cur_cs), j);
} else {
luaL_error(L, "unsupported value type");
}
} else {
/* people may want to add keys that are also primitives
(|tex.wd| for example) so creating an error is not
right here */
if (lua_istable(L, (i - 2)))
lua_rawset(L, (i - 2));
/* luaL_error(L, "unsupported tex internal assignment"); */
}
} else {
if (lua_istable(L, (i - 2)))
lua_rawset(L, (i - 2));
}
} else {
if (lua_istable(L, (i - 2)))
lua_rawset(L, (i - 2));
}
return 0;
}
static int do_convert(lua_State * L, int cur_code)
{
int texstr;
int i = -1;
char *str = NULL;
switch (cur_code) {
case pdf_creation_date_code: /* ? */
case pdf_insert_ht_code: /* arg <register int> */
case pdf_ximage_bbox_code: /* arg 2 ints */
case lua_code: /* arg complex */
case lua_escape_string_code: /* arg token list */
case pdf_colorstack_init_code: /* arg complex */
case left_margin_kern_code: /* arg box */
case right_margin_kern_code: /* arg box */
break;
case string_code: /* arg token */
case meaning_code: /* arg token */
break;
/* the next fall through, and come from 'official' indices! */
case font_name_code: /* arg fontid */
case font_identifier_code: /* arg fontid */
case pdf_font_name_code: /* arg fontid */
case pdf_font_objnum_code: /* arg fontid */
case pdf_font_size_code: /* arg fontid */
case uniform_deviate_code: /* arg int */
case number_code: /* arg int */
case roman_numeral_code: /* arg int */
case pdf_page_ref_code: /* arg int */
case pdf_xform_name_code: /* arg int */
if (lua_gettop(L) < 1) {
/* error */
}
lua_number2int(i, lua_tonumber(L, 1)); /* these fall through! */
default:
texstr = the_convert_string(cur_code, i);
if (texstr) {
str = makecstring(texstr);
flush_str(texstr);
}
}
if (str) {
lua_pushstring(L, str);
free(str);
} else {
lua_pushnil(L);
}
return 1;
}
static int do_scan_internal(lua_State * L, int cur_cmd, int cur_code)
{
int texstr;
char *str = NULL;
int save_cur_val, save_cur_val_level;
save_cur_val = cur_val;
save_cur_val_level = cur_val_level;
scan_something_simple(cur_cmd, cur_code);
if (cur_val_level == int_val_level ||
cur_val_level == dimen_val_level || cur_val_level == attr_val_level) {
lua_pushnumber(L, cur_val);
} else if (cur_val_level == glue_val_level) {
lua_nodelib_push_fast(L, cur_val);
} else { /* dir_val_level, mu_val_level, tok_val_level */
texstr = the_scanned_result();
str = makecstring(texstr);
if (str) {
lua_pushstring(L, str);
free(str);
} else {
lua_pushnil(L);
}
flush_str(texstr);
}
cur_val = save_cur_val;
cur_val_level = save_cur_val_level;
return 1;
}
static int do_lastitem(lua_State * L, int cur_code)
{
int retval = 1;
switch (cur_code) {
/* the next two do not actually exist */
case lastattr_code:
case attrexpr_code:
lua_pushnil(L);
break;
/* the expressions do something complicated with arguments, yuck */
case numexpr_code:
case dimexpr_code:
case glueexpr_code:
case muexpr_code:
lua_pushnil(L);
break;
/* these read a glue or muglue, todo */
case mu_to_glue_code:
case glue_to_mu_code:
case glue_stretch_order_code:
case glue_shrink_order_code:
case glue_stretch_code:
case glue_shrink_code:
lua_pushnil(L);
break;
/* these read a fontid and a char, todo */
case font_char_wd_code:
case font_char_ht_code:
case font_char_dp_code:
case font_char_ic_code:
lua_pushnil(L);
break;
/* these read an integer, todo */
case par_shape_length_code:
case par_shape_indent_code:
case par_shape_dimen_code:
lua_pushnil(L);
break;
case lastpenalty_code:
case lastkern_code:
case lastskip_code:
case last_node_type_code:
case input_line_no_code:
case badness_code:
case pdftex_version_code:
case pdf_last_obj_code:
case pdf_last_xform_code:
case pdf_last_ximage_code:
case pdf_last_ximage_pages_code:
case pdf_last_annot_code:
case pdf_last_x_pos_code:
case pdf_last_y_pos_code:
case pdf_retval_code:
case pdf_last_ximage_colordepth_code:
case random_seed_code:
case pdf_last_link_code:
case luatex_version_code:
case Aleph_version_code:
case Omega_version_code:
case Aleph_minor_version_code:
case Omega_minor_version_code:
case eTeX_minor_version_code:
case eTeX_version_code:
case current_group_level_code:
case current_group_type_code:
case current_if_level_code:
case current_if_type_code:
case current_if_branch_code:
retval = do_scan_internal(L, last_item_cmd, cur_code);
break;
default:
lua_pushnil(L);
break;
}
return retval;
}
static int tex_setmathparm(lua_State * L)
{
int i, j;
int k;
int n;
int l = cur_level;
n = lua_gettop(L);
if ((n == 3) || (n == 4)) {
if (n == 4 && lua_isstring(L, 1)) {
const char *s = lua_tostring(L, 1);
if (strcmp(s, "global") == 0)
l = 1;
}
i = luaL_checkoption(L, (n - 2), NULL, math_param_names);
j = luaL_checkoption(L, (n - 1), NULL, math_style_names);
lua_number2int(k, lua_tonumber(L, n));
def_math_param(i, j, (scaled) k, l);
}
return 0;
}
static int tex_getmathparm(lua_State * L)
{
int i, j;
scaled k;
if ((lua_gettop(L) == 2)) {
i = luaL_checkoption(L, 1, NULL, math_param_names);
j = luaL_checkoption(L, 2, NULL, math_style_names);
k = get_math_param(i, j);
lua_pushnumber(L, k);
}
return 1;
}
static int getfontname(lua_State * L)
{
return do_convert(L, font_name_code);
}
static int getfontidentifier(lua_State * L)
{
return do_convert(L, font_identifier_code);
}
static int getpdffontname(lua_State * L)
{
return do_convert(L, pdf_font_name_code);
}
static int getpdffontobjnum(lua_State * L)
{
return do_convert(L, pdf_font_objnum_code);
}
static int getpdffontsize(lua_State * L)
{
return do_convert(L, pdf_font_size_code);
}
static int getuniformdeviate(lua_State * L)
{
return do_convert(L, uniform_deviate_code);
}
static int getnumber(lua_State * L)
{
return do_convert(L, number_code);
}
static int getromannumeral(lua_State * L)
{
return do_convert(L, roman_numeral_code);
}
static int getpdfpageref(lua_State * L)
{
return do_convert(L, pdf_page_ref_code);
}
static int getpdfxformname(lua_State * L)
{
return do_convert(L, pdf_xform_name_code);
}
static int get_parshape(lua_State * L)
{
int n;
halfword par_shape_ptr = equiv(par_shape_loc);
if (par_shape_ptr != 0) {
int m = 1;
n = vinfo(par_shape_ptr + 1);
lua_createtable(L, n, 0);
while (m <= n) {
lua_createtable(L, 2, 0);
lua_pushnumber(L, vlink((par_shape_ptr) + (2 * (m - 1)) + 2));
lua_rawseti(L, -2, 1);
lua_pushnumber(L, vlink((par_shape_ptr) + (2 * (m - 1)) + 3));
lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, m);
m++;
}
} else {
lua_pushnil(L);
}
return 1;
}
static int gettex(lua_State * L)
{
int cur_cs = -1;
int retval = 1; /* default is to return nil */
if (lua_isstring(L, 2)) { /* 1 == 'tex' */
int texstr;
size_t k;
const char *st = lua_tolstring(L, 2, &k);
texstr = maketexlstring(st, k);
cur_cs = prim_lookup(texstr); /* not found == relax == 0 */
flush_str(texstr);
}
if (cur_cs > 0) {
int cur_cmd, cur_code;
cur_cmd = get_prim_eq_type(cur_cs);
cur_code = get_prim_equiv(cur_cs);
switch (cur_cmd) {
case last_item_cmd:
retval = do_lastitem(L, cur_code);
break;
case convert_cmd:
retval = do_convert(L, cur_code);
break;
case assign_toks_cmd:
case assign_int_cmd:
case assign_attr_cmd:
case assign_dir_cmd:
case assign_dimen_cmd:
case assign_glue_cmd:
case assign_mu_glue_cmd:
case set_aux_cmd:
case set_prev_graf_cmd:
case set_page_int_cmd:
case set_page_dimen_cmd:
case char_given_cmd:
case math_given_cmd:
case omath_given_cmd:
retval = do_scan_internal(L, cur_cmd, cur_code);
break;
case set_tex_shape_cmd:
retval = get_parshape(L);
break;
default:
lua_pushnil(L);
break;
}
} else {
lua_rawget(L, 1); /* fetch other index from table */
}
return retval;
}
static int getlist(lua_State * L)
{
const char *str;
if (lua_isstring(L, 2)) {
str = lua_tostring(L, 2);
if (strcmp(str, "page_ins_head") == 0) {
if (vlink(page_ins_head) == page_ins_head)
lua_pushnumber(L, null);
else
lua_pushnumber(L, vlink(page_ins_head));
lua_nodelib_push(L);
} else if (strcmp(str, "contrib_head") == 0) {
lua_pushnumber(L, vlink(contrib_head));
lua_nodelib_push(L);
} else if (strcmp(str, "page_head") == 0) {
lua_pushnumber(L, vlink(page_head));
lua_nodelib_push(L);
} else if (strcmp(str, "temp_head") == 0) {
lua_pushnumber(L, vlink(temp_head));
lua_nodelib_push(L);
} else if (strcmp(str, "hold_head") == 0) {
lua_pushnumber(L, vlink(hold_head));
lua_nodelib_push(L);
} else if (strcmp(str, "adjust_head") == 0) {
lua_pushnumber(L, vlink(adjust_head));
lua_nodelib_push(L);
} else if (strcmp(str, "best_page_break") == 0) {
lua_pushnumber(L, best_page_break);
lua_nodelib_push(L);
} else if (strcmp(str, "least_page_cost") == 0) {
lua_pushnumber(L, least_page_cost);
} else if (strcmp(str, "best_size") == 0) {
lua_pushnumber(L, best_size);
} else if (strcmp(str, "pre_adjust_head") == 0) {
lua_pushnumber(L, vlink(pre_adjust_head));
lua_nodelib_push(L);
} else if (strcmp(str, "align_head") == 0) {
lua_pushnumber(L, vlink(align_head));
lua_nodelib_push(L);
} else {
lua_pushnil(L);
}
} else {
lua_pushnil(L);
}
return 1;
}
static int setlist(lua_State * L)
{
halfword *n_ptr;
const char *str;
halfword n = 0;
if (lua_isstring(L, 2)) {
str = lua_tostring(L, 2);
if (strcmp(str, "best_size") == 0) {
best_size = (int) lua_tointeger(L, 3);
} else if (strcmp(str, "least_page_cost") == 0) {
least_page_cost = (int) lua_tointeger(L, 3);
} else {
if (!lua_isnil(L, 3)) {
n_ptr = check_isnode(L, 3);
n = *n_ptr;
}
if (strcmp(str, "page_ins_head") == 0) {
if (n == 0) {
vlink(page_ins_head) = page_ins_head;
} else {
halfword m;
vlink(page_ins_head) = n;
m = tail_of_list(n);
vlink(m) = page_ins_head;
}
} else if (strcmp(str, "contrib_head") == 0) {
vlink(contrib_head) = n;
if (n == 0) {
contrib_tail = contrib_head;
}
} else if (strcmp(str, "best_page_break") == 0) {
best_page_break = n;
} else if (strcmp(str, "page_head") == 0) {
vlink(page_head) = n;
page_tail = (n == 0 ? page_head : tail_of_list(n));
} else if (strcmp(str, "temp_head") == 0) {
vlink(temp_head) = n;
} else if (strcmp(str, "hold_head") == 0) {
vlink(hold_head) = n;
} else if (strcmp(str, "adjust_head") == 0) {
vlink(adjust_head) = n;
adjust_tail = (n == 0 ? adjust_head : tail_of_list(n));
} else if (strcmp(str, "pre_adjust_head") == 0) {
vlink(pre_adjust_head) = n;
pre_adjust_tail = (n == 0 ? pre_adjust_head : tail_of_list(n));
} else if (strcmp(str, "align_head") == 0) {
vlink(align_head) = n;
}
}
}
return 0;
}
#define NEST_METATABLE "luatex.nest"
static int lua_nest_getfield(lua_State * L)
{
list_state_record *r, **rv = lua_touserdata(L, -2);
const char *field = lua_tostring(L, -1);
r = *rv;
if (strcmp(field, "mode") == 0) {
lua_pushnumber(L, r->mode_field);
} else if (strcmp(field, "head") == 0) {
lua_nodelib_push_fast(L, r->head_field);
} else if (strcmp(field, "tail") == 0) {
lua_nodelib_push_fast(L, r->tail_field);
} else if (strcmp(field, "delimptr") == 0) {
lua_pushnumber(L, r->eTeX_aux_field);
lua_nodelib_push(L);
} else if (strcmp(field, "prevgraf") == 0) {
lua_pushnumber(L, r->pg_field);
} else if (strcmp(field, "modeline") == 0) {
lua_pushnumber(L, r->ml_field);
} else if (strcmp(field, "prevdepth") == 0) {
lua_pushnumber(L, r->prev_depth_field);
} else if (strcmp(field, "spacefactor") == 0) {
lua_pushnumber(L, r->space_factor_field);
} else if (strcmp(field, "noad") == 0) {
lua_pushnumber(L, r->incompleat_noad_field);
lua_nodelib_push(L);
} else if (strcmp(field, "dirs") == 0) {
lua_pushnumber(L, r->dirs_field);
lua_nodelib_push(L);
} else if (strcmp(field, "mathdir") == 0) {
lua_pushboolean(L, r->math_field);
} else if (strcmp(field, "mathstyle") == 0) {
lua_pushnumber(L, r->math_style_field);
} else {
lua_pushnil(L);
}
return 1;
}
static int lua_nest_setfield(lua_State * L)
{
halfword *n;
int i;
list_state_record *r, **rv = lua_touserdata(L, -3);
const char *field = lua_tostring(L, -2);
r = *rv;
if (strcmp(field, "mode") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->mode_field = i;
} else if (strcmp(field, "head") == 0) {
n = check_isnode(L, -1);
r->head_field = *n;
} else if (strcmp(field, "tail") == 0) {
n = check_isnode(L, -1);
r->tail_field = *n;
} else if (strcmp(field, "delimptr") == 0) {
n = check_isnode(L, -1);
r->eTeX_aux_field = *n;
} else if (strcmp(field, "prevgraf") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->pg_field = i;
} else if (strcmp(field, "modeline") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->ml_field = i;
} else if (strcmp(field, "prevdepth") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->prev_depth_field = i;
} else if (strcmp(field, "spacefactor") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->space_factor_field = i;
} else if (strcmp(field, "noad") == 0) {
n = check_isnode(L, -1);
r->incompleat_noad_field = *n;
} else if (strcmp(field, "dirs") == 0) {
n = check_isnode(L, -1);
r->dirs_field = *n;
} else if (strcmp(field, "mathdir") == 0) {
r->math_field = lua_toboolean(L, -1);
} else if (strcmp(field, "mathstyle") == 0) {
lua_number2int(i, lua_tonumber(L, -1));
r->math_style_field = i;
}
return 0;
}
static const struct luaL_reg nest_m[] = {
{"__index", lua_nest_getfield},
{"__newindex", lua_nest_setfield},
{NULL, NULL} /* sentinel */
};
static void init_nest_lib(lua_State * L)
{
luaL_newmetatable(L, NEST_METATABLE);
luaL_register(L, NULL, nest_m);
lua_pop(L, 1);
}
static int getnest(lua_State * L)
{
int ptr;
list_state_record **nestitem;
if (lua_isnumber(L, 2)) {
lua_number2int(ptr, lua_tonumber(L, 2));
if (ptr >= 0 && ptr <= nest_ptr) {
nestitem = lua_newuserdata(L, sizeof(list_state_record *));
*nestitem = &nest[ptr];
luaL_getmetatable(L, NEST_METATABLE);
lua_setmetatable(L, -2);
} else {
lua_pushnil(L);
}
} else if (lua_isstring(L, 2)) {
const char *s = lua_tostring(L, 2);
if (strcmp(s, "ptr") == 0) {
lua_pushnumber(L, nest_ptr);
} else {
lua_pushnil(L);
}
} else {
lua_pushnil(L);
}
return 1;
}
static int setnest(lua_State * L)
{
luaL_error(L, "You can't modify the semantic nest array directly");
return 2;
}
static int do_integer_error(double m)
{
const char *help[] =
{ "I can only go up to 2147483647='17777777777=" "7FFFFFFF,",
"so I'm using that number instead of yours.",
NULL
};
tex_error("Number too big", help);
return (m > 0.0 ? infinity : -infinity);
}
static int tex_roundnumber(lua_State * L)
{
double m = (double) lua_tonumber(L, 1) + 0.5;
if (abs(m) > (double) infinity)
lua_pushnumber(L, do_integer_error(m));
else
lua_pushnumber(L, floor(m));
return 1;
}
static int tex_scaletable(lua_State * L)
{
double delta = luaL_checknumber(L, 2);
if (lua_istable(L, 1)) {
lua_newtable(L); /* the new table is at index 3 */
lua_pushnil(L);
while (lua_next(L, 1) != 0) { /* numeric value */
lua_pushvalue(L, -2);
lua_insert(L, -2);
if (lua_isnumber(L, -1)) {
double m = (double) lua_tonumber(L, -1) * delta + 0.5;
lua_pop(L, 1);
if (abs(m) > (double) infinity)
lua_pushnumber(L, do_integer_error(m));
else
lua_pushnumber(L, floor(m));
}
lua_rawset(L, 3);
}
} else if (lua_isnumber(L, 1)) {
double m = (double) lua_tonumber(L, 1) * delta + 0.5;
if (abs(m) > (double) infinity)
lua_pushnumber(L, do_integer_error(m));
else
lua_pushnumber(L, floor(m));
} else {
lua_pushnil(L);
}
return 1;
}
#define hash_text(A) hash[(A)].rh
static int tex_definefont(lua_State * L)
{
const char *csname;
int f, u;
str_number t;
size_t l;
int i = 1;
int a = 0;
if (!no_new_control_sequence) {
const char *help[] =
{ "You can't create a new font inside a \\csname\\endcsname pair",
NULL
};
tex_error("Definition active", help);
}
if ((lua_gettop(L) == 3) && lua_isboolean(L, 1)) {
a = lua_toboolean(L, 1);
i = 2;
}
csname = luaL_checklstring(L, i, &l);
f = (int) luaL_checkinteger(L, (i + 1));
t = maketexlstring(csname, l);
no_new_control_sequence = 0;
u = string_lookup(csname, l);
no_new_control_sequence = 1;
if (a)
geq_define(u, set_font_cmd, f);
else
eq_define(u, set_font_cmd, f);
eqtb[font_id_base + f] = eqtb[u];
hash_text(font_id_base + f) = t;
return 0;
}
static int tex_hashpairs(lua_State * L)
{
int cmd, chr;
str_number s = 0;
int cs = 1;
lua_newtable(L);
while (cs < eqtb_size) {
s = hash_text(cs);
if (s > 0) {
char *ss = makecstring(s);
lua_pushstring(L, ss);
free(ss);
cmd = eq_type(cs);
chr = equiv(cs);
make_token_table(L, cmd, chr, cs);
lua_rawset(L, -3);
}
cs++;
}
return 1;
}
static int tex_primitives(lua_State * L)
{
int cmd, chr;
str_number s = 0;
int cs = 0;
lua_newtable(L);
while (cs < prim_size) {
s = get_prim_text(cs);
if (s > 0) {
char *ss = makecstring(s);
lua_pushstring(L, ss);
free(ss);
cmd = get_prim_eq_type(cs);
chr = get_prim_equiv(cs);
make_token_table(L, cmd, chr, 0);
lua_rawset(L, -3);
}
cs++;
}
return 1;
}
static int tex_extraprimitives(lua_State * L)
{
int n, i;
int mask = 0;
str_number s = 0;
int cs = 0;
n = lua_gettop(L);
if (n == 0) {
mask = etex_command + aleph_command + omega_command +
pdftex_command + luatex_command;
} else {
for (i = 1; i <= n; i++) {
if (lua_isstring(L, i)) {
const char *s = lua_tostring(L, i);
if (strcmp(s, "etex") == 0) {
mask |= etex_command;
} else if (strcmp(s, "tex") == 0) {
mask |= tex_command;
} else if (strcmp(s, "core") == 0) {
mask |= core_command;
} else if (strcmp(s, "pdftex") == 0) {
mask |= pdftex_command;
} else if (strcmp(s, "aleph") == 0) {
mask |= aleph_command;
} else if (strcmp(s, "omega") == 0) {
mask |= omega_command;
} else if (strcmp(s, "luatex") == 0) {
mask |= luatex_command;
}
}
}
}
lua_newtable(L);
i = 1;
while (cs < prim_size) {
s = get_prim_text(cs);
if (s > 0) {
if (get_prim_origin(cs) & mask) {
char *ss = makecstring(s);
lua_pushstring(L, ss);
free(ss);
lua_rawseti(L, -2, i++);
}
}
cs++;
}
return 1;
}
static int tex_enableprimitives(lua_State * L)
{
int n = lua_gettop(L);
if (n != 2) {
luaL_error(L, "wrong number of arguments");
} else {
size_t l;
int i;
const char *pre = luaL_checklstring(L, 1, &l);
if (lua_istable(L, 2)) {
int nncs = no_new_control_sequence;
no_new_control_sequence = true;
i = 1;
while (1) {
lua_rawgeti(L, 2, i);
if (lua_isstring(L, 3)) {
const char *prim = lua_tostring(L, 3);
str_number s = maketexstring(prim);
halfword prim_val = prim_lookup(s);
if (prim_val != undefined_primitive) {
char *newprim;
int val;
size_t newl;
halfword cur_cmd = get_prim_eq_type(prim_val);
halfword cur_chr = get_prim_equiv(prim_val);
if (strncmp(pre, prim, l) != 0) { /* not a prefix */
newl = strlen(prim) + l;
newprim = (char *) xmalloc((unsigned) (newl + 1));
strcpy(newprim, pre);
strcat(newprim + l, prim);
} else {
newl = strlen(prim);
newprim = (char *) xmalloc((unsigned) (newl + 1));
strcpy(newprim, prim);
}
val = string_lookup(newprim, newl);
if (val == undefined_control_sequence ||
eq_type(val) == undefined_cs_cmd) {
primitive_def(newprim, newl, (quarterword) cur_cmd,
cur_chr);
}
free(newprim);
}
flush_str(s);
} else {
lua_pop(L, 1);
break;
}
lua_pop(L, 1);
i++;
}
lua_pop(L, 1); /* the table */
no_new_control_sequence = nncs;
} else {
luaL_error(L, "Expected an array of names as second argument");
}
}
return 0;
}
#define get_int_par(A,B,C) do { \
lua_pushstring(L,(A)); \
lua_gettable(L,-2); \
if (lua_type(L, -1) == LUA_TNUMBER) { \
lua_number2int(B,lua_tonumber(L, -1)); \
} else { \
B = (C); \
} \
lua_pop(L,1); \
} while (0)
#define get_intx_par(A,B,C,D,E) do { \
lua_pushstring(L,(A)); \
lua_gettable(L,-2); \
if (lua_type(L, -1) == LUA_TNUMBER) { \
lua_number2int(B,lua_tonumber(L, -1)); \
D = null; \
} else if (lua_type(L, -1) == LUA_TTABLE){ \
B = 0; \
D = nodelib_topenalties(L, lua_gettop(L)); \
} else { \
B = (C); \
D = (E); \
} \
lua_pop(L,1); \
} while (0)
#define get_dimen_par(A,B,C) do { \
lua_pushstring(L,(A)); \
lua_gettable(L,-2); \
if (lua_type(L, -1) == LUA_TNUMBER) { \
lua_number2int(B,lua_tonumber(L, -1)); \
} else { \
B = (C); \
} \
lua_pop(L,1); \
} while (0)
#define get_glue_par(A,B,C) do { \
lua_pushstring(L,(A)); \
lua_gettable(L,-2); \
if (lua_type(L, -1) != LUA_TNIL) { \
B = *check_isnode(L, -1); \
} else { \
B = (C); \
} \
lua_pop(L,1); \
} while (0)
static halfword nodelib_toparshape(lua_State * L, int i)
{
halfword p;
int n = 0;
int width, indent, j;
/* find |n| */
lua_pushnil(L);
while (lua_next(L, i) != 0) {
n++;
lua_pop(L, 1);
}
if (n == 0)
return null;
p = new_node(shape_node, 2 * (n + 1) + 1);
vinfo(p + 1) = n;
/* fill |p| */
lua_pushnil(L);
j = 0;
while (lua_next(L, i) != 0) {
/* don't give an error for non-tables, we may add special syntaxes at some point */
j++;
if (lua_type(L, i) == LUA_TTABLE) {
lua_rawgeti(L, -1, 1); /* indent */
if (lua_type(L, -1) == LUA_TNUMBER) {
lua_number2int(indent, lua_tonumber(L, -1));
lua_pop(L, 1);
lua_rawgeti(L, -1, 2); /* width */
if (lua_type(L, -1) == LUA_TNUMBER) {
lua_number2int(width, lua_tonumber(L, -1));
lua_pop(L, 1);
varmem[p + 2 * j].cint = indent;
varmem[p + 2 * j + 1].cint = width;
}
}
}
lua_pop(L, 1);
}
return p;
}
/* penalties */
static halfword nodelib_topenalties(lua_State * L, int i)
{
halfword p;
int n = 0;
int j;
/* find |n| */
lua_pushnil(L);
while (lua_next(L, i) != 0) {
n++;
lua_pop(L, 1);
}
if (n == 0)
return null;
p = new_node(shape_node, 2 * ((n / 2) + 1) + 1 + 1);
vinfo(p + 1) = (n / 2) + 1;
varmem[p + 2].cint = n;
lua_pushnil(L);
j = 2;
while (lua_next(L, i) != 0) {
j++;
if (lua_isnumber(L, -1)) {
int pen = 0;
lua_number2int(pen, lua_tonumber(L, -1));
varmem[p+j].cint = pen;
}
lua_pop(L, 1);
}
if (!odd(n))
varmem[p+j+1].cint = 0;
return p;
}
static int tex_run_linebreak(lua_State * L)
{
halfword *j;
halfword p;
halfword final_par_glue;
int paragraph_dir = 0;
/* locally initialized parameters for line breaking */
int pretolerance, tracingparagraphs, tolerance, looseness, hyphenpenalty,
exhyphenpenalty, pdfadjustspacing, adjdemerits, pdfprotrudechars,
linepenalty, lastlinefit, doublehyphendemerits, finalhyphendemerits,
hangafter, interlinepenalty, widowpenalty, clubpenalty, brokenpenalty;
halfword emergencystretch, hangindent, hsize, leftskip, rightskip,
pdfeachlineheight, pdfeachlinedepth, pdffirstlineheight,
pdflastlinedepth, pdfignoreddimen, parshape;
int fewest_demerits = 0, actual_looseness = 0;
halfword clubpenalties, interlinepenalties, widowpenalties;
int save_vlink_tmp_head;
/* push a new nest level */
push_nest();
save_vlink_tmp_head = vlink(temp_head);
j = check_isnode(L, 1); /* the value */
vlink(temp_head) = *j;
p = *j;
if ((!is_char_node(vlink(*j)))
&& ((type(vlink(*j)) == whatsit_node)
&& (subtype(vlink(*j)) == local_par_node))) {
paragraph_dir = local_par_dir(vlink(*j));
}
while (vlink(p) != null)
p = vlink(p);
final_par_glue = p;
/* initialize local parameters */
if (lua_gettop(L) != 2 || lua_type(L, 2) != LUA_TTABLE) {
lua_checkstack(L, 3);
lua_newtable(L);
}
lua_pushstring(L, "pardir");
lua_gettable(L, -2);
if (lua_type(L, -1) == LUA_TSTRING) {
paragraph_dir = nodelib_getdir(L, -1);
}
lua_pop(L, 1);
lua_pushstring(L, "parshape");
lua_gettable(L, -2);
if (lua_type(L, -1) == LUA_TTABLE) {
parshape = nodelib_toparshape(L, lua_gettop(L));
} else {
parshape = equiv(par_shape_loc);
}
lua_pop(L, 1);
get_int_par("pretolerance", pretolerance, int_par(pretolerance_code));
get_int_par("tracingparagraphs", tracingparagraphs,
int_par(tracing_paragraphs_code));
get_int_par("tolerance", tolerance, int_par(tolerance_code));
get_int_par("looseness", looseness, int_par(looseness_code));
get_int_par("hyphenpenalty", hyphenpenalty, int_par(hyphen_penalty_code));
get_int_par("exhyphenpenalty", exhyphenpenalty,
int_par(ex_hyphen_penalty_code));
get_int_par("pdfadjustspacing", pdfadjustspacing,
int_par(pdf_adjust_spacing_code));
get_int_par("adjdemerits", adjdemerits, int_par(adj_demerits_code));
get_int_par("pdfprotrudechars", pdfprotrudechars,
int_par(pdf_protrude_chars_code));
get_int_par("linepenalty", linepenalty, int_par(line_penalty_code));
get_int_par("lastlinefit", lastlinefit, int_par(last_line_fit_code));
get_int_par("doublehyphendemerits", doublehyphendemerits,
int_par(double_hyphen_demerits_code));
get_int_par("finalhyphendemerits", finalhyphendemerits,
int_par(final_hyphen_demerits_code));
get_int_par("hangafter", hangafter, int_par(hang_after_code));
get_intx_par("interlinepenalty", interlinepenalty,int_par(inter_line_penalty_code),
interlinepenalties, equiv(inter_line_penalties_loc));
get_intx_par("clubpenalty", clubpenalty, int_par(club_penalty_code),
clubpenalties, equiv(club_penalties_loc));
get_intx_par("widowpenalty", widowpenalty, int_par(widow_penalty_code),
widowpenalties, equiv(widow_penalties_loc));
get_int_par("brokenpenalty", brokenpenalty, int_par(broken_penalty_code));
get_dimen_par("emergencystretch", emergencystretch,
dimen_par(emergency_stretch_code));
get_dimen_par("hangindent", hangindent, dimen_par(hang_indent_code));
get_dimen_par("hsize", hsize, dimen_par(hsize_code));
get_glue_par("leftskip", leftskip, glue_par(left_skip_code));
get_glue_par("rightskip", rightskip, glue_par(right_skip_code));
get_dimen_par("pdfeachlineheight", pdfeachlineheight,
dimen_par(pdf_each_line_height_code));
get_dimen_par("pdfeachlinedepth", pdfeachlinedepth,
dimen_par(pdf_each_line_depth_code));
get_dimen_par("pdffirstlineheight", pdffirstlineheight,
dimen_par(pdf_first_line_height_code));
get_dimen_par("pdflastlinedepth", pdflastlinedepth,
dimen_par(pdf_last_line_depth_code));
get_dimen_par("pdfignoreddimen", pdfignoreddimen,
dimen_par(pdf_ignored_dimen_code));
ext_do_line_break(paragraph_dir,
pretolerance, tracingparagraphs, tolerance,
emergencystretch,
looseness, hyphenpenalty, exhyphenpenalty,
pdfadjustspacing,
parshape,
adjdemerits, pdfprotrudechars,
linepenalty, lastlinefit,
doublehyphendemerits, finalhyphendemerits,
hangindent, hsize, hangafter, leftskip, rightskip,
pdfeachlineheight, pdfeachlinedepth,
pdffirstlineheight, pdflastlinedepth,
interlinepenalties,
interlinepenalty, clubpenalty,
clubpenalties,
widowpenalties,
widowpenalty, brokenpenalty,
final_par_glue, pdfignoreddimen);
/* return the generated list, and its prevdepth */
get_linebreak_info (&fewest_demerits, &actual_looseness) ;
lua_nodelib_push_fast(L, vlink(cur_list.head_field));
lua_newtable(L);
lua_pushstring(L, "demerits");
lua_pushnumber(L, fewest_demerits);
lua_settable(L, -3);
lua_pushstring(L, "looseness");
lua_pushnumber(L, actual_looseness);
lua_settable(L, -3);
lua_pushstring(L, "prevdepth");
lua_pushnumber(L, cur_list.prev_depth_field);
lua_settable(L, -3);
lua_pushstring(L, "prevgraf");
lua_pushnumber(L, cur_list.pg_field);
lua_settable(L, -3);
/* restore nest stack */
vlink(temp_head) = save_vlink_tmp_head;
pop_nest();
if (parshape != equiv(par_shape_loc))
flush_node(parshape);
return 2;
}
static int tex_shipout(lua_State * L)
{
int boxnum = get_box_id(L, 1);
ship_out(static_pdf, box(boxnum), SHIPPING_PAGE);
box(boxnum) = null;
return 0;
}
static int tex_badness(lua_State * L)
{
scaled t,s;
lua_number2int(t,lua_tonumber(L,1));
lua_number2int(s,lua_tonumber(L,2));
lua_pushnumber(L, badness(t,s));
return 1;
}
static int tex_run_boot(lua_State * L)
{
int n = lua_gettop(L);
const char *format = NULL;
if (n >= 1) {
ini_version = 0;
format = luaL_checkstring(L, 1);
} else {
ini_version = 1;
}
if (main_initialize()) { /* > 0 = failure */
lua_pushboolean(L, 0); /* false */
return 1;
}
if (format) {
if (!zopen_w_input(&fmt_file, format, DUMP_FORMAT, FOPEN_RBIN_MODE)) {
lua_pushboolean(L, 0); /* false */
return 1;
}
if (!load_fmt_file(format)) {
zwclose(fmt_file);
lua_pushboolean(L, 0); /* false */
return 1;
}
zwclose(fmt_file);
}
fix_date_and_time();
if (format == NULL)
make_pdftex_banner();
random_seed = (microseconds * 1000) + (epochseconds % 1000000);
init_randoms(random_seed);
initialize_math();
fixup_selector(log_opened);
check_texconfig_init();
text_dir_ptr = new_dir(0);
history = spotless; /* ready to go! */
/* Initialize synctex primitive */
synctexinitcommand();
/* tex is ready to go, now */
unhide_lua_table(Luas, "tex", tex_table_id);
unhide_lua_table(Luas, "pdf", pdf_table_id);
unhide_lua_table(Luas, "token", token_table_id);
unhide_lua_table(Luas, "node", node_table_id);
lua_pushboolean(L, 1); /* true */
return 1;
}
static int tex_run_main(lua_State * L)
{
(void) L;
main_control();
return 0;
}
static int tex_run_end(lua_State * L)
{
(void) L;
final_cleanup(); /* prepare for death */
close_files_and_terminate();
do_final_end();
return 0;
}
void init_tex_table(lua_State * L)
{
lua_createtable(L, 0, 3);
lua_pushcfunction(L, tex_run_boot);
lua_setfield(L, -2, "initialize");
lua_pushcfunction(L, tex_run_main);
lua_setfield(L, -2, "run");
lua_pushcfunction(L, tex_run_end);
lua_setfield(L, -2, "finish");
lua_setglobal(L, "tex");
}
static const struct luaL_reg texlib[] = {
{"run", tex_run_main}, /* may be needed */
{"finish", tex_run_end}, /* may be needed */
{"write", luacwrite},
{"write", luacwrite},
{"print", luacprint},
{"tprint", luactprint},
{"error", texerror},
{"sprint", luacsprint},
{"set", settex},
{"get", gettex},
{"setdimen", setdimen},
{"getdimen", getdimen},
{"setskip", setskip},
{"getskip", getskip},
{"setattribute", setattribute},
{"getattribute", getattribute},
{"setcount", setcount},
{"getcount", getcount},
{"settoks", settoks},
{"gettoks", gettoks},
{"setbox", setbox},
{"getbox", getbox},
{"setlist", setlist},
{"getlist", getlist},
{"setnest", setnest},
{"getnest", getnest},
{"setcatcode", setcatcode},
{"getcatcode", getcatcode},
{"setdelcode", setdelcode},
{"getdelcode", getdelcode},
{"setlccode", setlccode},
{"getlccode", getlccode},
{"setmathcode", setmathcode},
{"getmathcode", getmathcode},
{"setsfcode", setsfcode},
{"getsfcode", getsfcode},
{"setuccode", setuccode},
{"getuccode", getuccode},
{"round", tex_roundnumber},
{"scale", tex_scaletable},
{"sp", tex_scaledimen},
{"fontname", getfontname},
{"fontidentifier", getfontidentifier},
{"pdffontname", getpdffontname},
{"pdffontobjnum", getpdffontobjnum},
{"pdffontsize", getpdffontsize},
{"uniformdeviate", getuniformdeviate},
{"number", getnumber},
{"romannumeral", getromannumeral},
{"pdfpageref", getpdfpageref},
{"pdfxformname", getpdfxformname},
{"definefont", tex_definefont},
{"hashtokens", tex_hashpairs},
{"primitives", tex_primitives},
{"extraprimitives", tex_extraprimitives},
{"enableprimitives", tex_enableprimitives},
{"shipout", tex_shipout},
{"badness", tex_badness},
{"setmath", tex_setmathparm},
{"getmath", tex_getmathparm},
{"linebreak", tex_run_linebreak},
{NULL, NULL} /* sentinel */
};
int luaopen_tex(lua_State * L)
{
luaL_register(L, "tex", texlib);
/* *INDENT-OFF* */
make_table(L, "attribute", "getattribute", "setattribute");
make_table(L, "skip", "getskip", "setskip");
make_table(L, "dimen", "getdimen", "setdimen");
make_table(L, "count", "getcount", "setcount");
make_table(L, "toks", "gettoks", "settoks");
make_table(L, "box", "getbox", "setbox");
make_table(L, "sfcode", "getsfcode", "setsfcode");
make_table(L, "lccode", "getlccode", "setlccode");
make_table(L, "uccode", "getuccode", "setuccode");
make_table(L, "catcode", "getcatcode", "setcatcode");
make_table(L, "mathcode", "getmathcode", "setmathcode");
make_table(L, "delcode", "getdelcode", "setdelcode");
make_table(L, "lists", "getlist", "setlist");
make_table(L, "nest", "getnest", "setnest");
/* *INDENT-ON* */
init_nest_lib(L);
/* make the meta entries */
/* fetch it back */
luaL_newmetatable(L, "tex_meta");
lua_pushstring(L, "__index");
lua_pushcfunction(L, gettex);
lua_settable(L, -3);
lua_pushstring(L, "__newindex");
lua_pushcfunction(L, settex);
lua_settable(L, -3);
lua_setmetatable(L, -2); /* meta to itself */
/* initialize the I/O stack: */
spindles = xmalloc(sizeof(spindle));
spindle_index = 0;
spindles[0].head = NULL;
spindles[0].tail = NULL;
spindle_size = 1;
/* a somewhat odd place for this assert, maybe */
assert(command_names[data_cmd].command_offset == data_cmd);
return 1;
}