1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Lua interface by Luis Carvalho 6 * 7 * Do ":help uganda" in Vim to read copying and usage conditions. 8 * Do ":help credits" in Vim to see a list of people who contributed. 9 * See README.txt for an overview of the Vim source code. 10 */ 11 12 #include "vim.h" 13 14 #include <lua.h> 15 #include <lualib.h> 16 #include <lauxlib.h> 17 18 /* Only do the following when the feature is enabled. Needed for "make 19 * depend". */ 20 #if defined(FEAT_LUA) || defined(PROTO) 21 22 #define LUAVIM_CHUNKNAME "vim chunk" 23 #define LUAVIM_NAME "vim" 24 25 typedef buf_T *luaV_Buffer; 26 typedef win_T *luaV_Window; 27 typedef void (*msgfunc_T)(char_u *); 28 29 static const char LUAVIM_BUFFER[] = "buffer"; 30 static const char LUAVIM_WINDOW[] = "window"; 31 static const char LUAVIM_FREE[] = "luaV_free"; 32 33 #define luaV_getfield(L, s) \ 34 lua_pushlightuserdata((L), (void *)(s)); \ 35 lua_rawget((L), LUA_REGISTRYINDEX) 36 #define luaV_checksandbox(L) \ 37 if (sandbox) luaL_error((L), "not allowed in sandbox") 38 #define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg) 39 #define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg) 40 41 42 #ifdef DYNAMIC_LUA 43 44 #ifndef WIN3264 45 # include <dlfcn.h> 46 # define HANDLE void* 47 # define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL) 48 # define symbol_from_dll dlsym 49 # define close_dll dlclose 50 #else 51 # define load_dll vimLoadLib 52 # define symbol_from_dll GetProcAddress 53 # define close_dll FreeLibrary 54 #endif 55 56 /* lauxlib */ 57 #define luaL_register dll_luaL_register 58 #define luaL_typerror dll_luaL_typerror 59 #define luaL_checklstring dll_luaL_checklstring 60 #define luaL_checkinteger dll_luaL_checkinteger 61 #define luaL_optinteger dll_luaL_optinteger 62 #define luaL_checktype dll_luaL_checktype 63 #define luaL_error dll_luaL_error 64 #define luaL_loadfile dll_luaL_loadfile 65 #define luaL_loadbuffer dll_luaL_loadbuffer 66 #define luaL_newstate dll_luaL_newstate 67 #define luaL_buffinit dll_luaL_buffinit 68 #define luaL_prepbuffer dll_luaL_prepbuffer 69 #define luaL_addlstring dll_luaL_addlstring 70 #define luaL_pushresult dll_luaL_pushresult 71 /* lua */ 72 #define lua_close dll_lua_close 73 #define lua_gettop dll_lua_gettop 74 #define lua_settop dll_lua_settop 75 #define lua_pushvalue dll_lua_pushvalue 76 #define lua_replace dll_lua_replace 77 #define lua_isnumber dll_lua_isnumber 78 #define lua_isstring dll_lua_isstring 79 #define lua_type dll_lua_type 80 #define lua_rawequal dll_lua_rawequal 81 #define lua_tonumber dll_lua_tonumber 82 #define lua_tointeger dll_lua_tointeger 83 #define lua_toboolean dll_lua_toboolean 84 #define lua_tolstring dll_lua_tolstring 85 #define lua_touserdata dll_lua_touserdata 86 #define lua_pushnil dll_lua_pushnil 87 #define lua_pushnumber dll_lua_pushnumber 88 #define lua_pushinteger dll_lua_pushinteger 89 #define lua_pushlstring dll_lua_pushlstring 90 #define lua_pushstring dll_lua_pushstring 91 #define lua_pushfstring dll_lua_pushfstring 92 #define lua_pushcclosure dll_lua_pushcclosure 93 #define lua_pushboolean dll_lua_pushboolean 94 #define lua_pushlightuserdata dll_lua_pushlightuserdata 95 #define lua_getfield dll_lua_getfield 96 #define lua_rawget dll_lua_rawget 97 #define lua_createtable dll_lua_createtable 98 #define lua_newuserdata dll_lua_newuserdata 99 #define lua_getmetatable dll_lua_getmetatable 100 #define lua_setfield dll_lua_setfield 101 #define lua_rawset dll_lua_rawset 102 #define lua_rawseti dll_lua_rawseti 103 #define lua_remove dll_lua_remove 104 #define lua_setmetatable dll_lua_setmetatable 105 #define lua_call dll_lua_call 106 #define lua_pcall dll_lua_pcall 107 /* libs */ 108 #define luaopen_base dll_luaopen_base 109 #define luaopen_table dll_luaopen_table 110 #define luaopen_string dll_luaopen_string 111 #define luaopen_math dll_luaopen_math 112 #define luaopen_io dll_luaopen_io 113 #define luaopen_os dll_luaopen_os 114 #define luaopen_package dll_luaopen_package 115 #define luaopen_debug dll_luaopen_debug 116 #define luaL_openlibs dll_luaL_openlibs 117 118 /* lauxlib */ 119 void (*dll_luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l); 120 int (*dll_luaL_typerror) (lua_State *L, int narg, const char *tname); 121 const char *(*dll_luaL_checklstring) (lua_State *L, int numArg, size_t *l); 122 lua_Integer (*dll_luaL_checkinteger) (lua_State *L, int numArg); 123 lua_Integer (*dll_luaL_optinteger) (lua_State *L, int nArg, lua_Integer def); 124 void (*dll_luaL_checktype) (lua_State *L, int narg, int t); 125 int (*dll_luaL_error) (lua_State *L, const char *fmt, ...); 126 int (*dll_luaL_loadfile) (lua_State *L, const char *filename); 127 int (*dll_luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, const char *name); 128 lua_State *(*dll_luaL_newstate) (void); 129 void (*dll_luaL_buffinit) (lua_State *L, luaL_Buffer *B); 130 char *(*dll_luaL_prepbuffer) (luaL_Buffer *B); 131 void (*dll_luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); 132 void (*dll_luaL_pushresult) (luaL_Buffer *B); 133 /* lua */ 134 void (*dll_lua_close) (lua_State *L); 135 int (*dll_lua_gettop) (lua_State *L); 136 void (*dll_lua_settop) (lua_State *L, int idx); 137 void (*dll_lua_pushvalue) (lua_State *L, int idx); 138 void (*dll_lua_replace) (lua_State *L, int idx); 139 int (*dll_lua_isnumber) (lua_State *L, int idx); 140 int (*dll_lua_isstring) (lua_State *L, int idx); 141 int (*dll_lua_type) (lua_State *L, int idx); 142 int (*dll_lua_rawequal) (lua_State *L, int idx1, int idx2); 143 lua_Number (*dll_lua_tonumber) (lua_State *L, int idx); 144 lua_Integer (*dll_lua_tointeger) (lua_State *L, int idx); 145 int (*dll_lua_toboolean) (lua_State *L, int idx); 146 const char *(*dll_lua_tolstring) (lua_State *L, int idx, size_t *len); 147 void *(*dll_lua_touserdata) (lua_State *L, int idx); 148 void (*dll_lua_pushnil) (lua_State *L); 149 void (*dll_lua_pushnumber) (lua_State *L, lua_Number n); 150 void (*dll_lua_pushinteger) (lua_State *L, lua_Integer n); 151 void (*dll_lua_pushlstring) (lua_State *L, const char *s, size_t l); 152 void (*dll_lua_pushstring) (lua_State *L, const char *s); 153 const char *(*dll_lua_pushfstring) (lua_State *L, const char *fmt, ...); 154 void (*dll_lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); 155 void (*dll_lua_pushboolean) (lua_State *L, int b); 156 void (*dll_lua_pushlightuserdata) (lua_State *L, void *p); 157 void (*dll_lua_getfield) (lua_State *L, int idx, const char *k); 158 void (*dll_lua_rawget) (lua_State *L, int idx); 159 void (*dll_lua_createtable) (lua_State *L, int narr, int nrec); 160 void *(*dll_lua_newuserdata) (lua_State *L, size_t sz); 161 int (*dll_lua_getmetatable) (lua_State *L, int objindex); 162 void (*dll_lua_setfield) (lua_State *L, int idx, const char *k); 163 void (*dll_lua_rawset) (lua_State *L, int idx); 164 void (*dll_lua_rawseti) (lua_State *L, int idx, int n); 165 void (*dll_lua_remove) (lua_State *L, int idx); 166 int (*dll_lua_setmetatable) (lua_State *L, int objindex); 167 void (*dll_lua_call) (lua_State *L, int nargs, int nresults); 168 int (*dll_lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); 169 /* libs */ 170 int (*dll_luaopen_base) (lua_State *L); 171 int (*dll_luaopen_table) (lua_State *L); 172 int (*dll_luaopen_string) (lua_State *L); 173 int (*dll_luaopen_math) (lua_State *L); 174 int (*dll_luaopen_io) (lua_State *L); 175 int (*dll_luaopen_os) (lua_State *L); 176 int (*dll_luaopen_package) (lua_State *L); 177 int (*dll_luaopen_debug) (lua_State *L); 178 void (*dll_luaL_openlibs) (lua_State *L); 179 180 typedef void **luaV_function; 181 typedef struct { 182 const char *name; 183 luaV_function func; 184 } luaV_Reg; 185 186 static const luaV_Reg luaV_dll[] = { 187 /* lauxlib */ 188 {"luaL_register", (luaV_function) &dll_luaL_register}, 189 {"luaL_typerror", (luaV_function) &dll_luaL_typerror}, 190 {"luaL_checklstring", (luaV_function) &dll_luaL_checklstring}, 191 {"luaL_checkinteger", (luaV_function) &dll_luaL_checkinteger}, 192 {"luaL_optinteger", (luaV_function) &dll_luaL_optinteger}, 193 {"luaL_checktype", (luaV_function) &dll_luaL_checktype}, 194 {"luaL_error", (luaV_function) &dll_luaL_error}, 195 {"luaL_loadfile", (luaV_function) &dll_luaL_loadfile}, 196 {"luaL_loadbuffer", (luaV_function) &dll_luaL_loadbuffer}, 197 {"luaL_newstate", (luaV_function) &dll_luaL_newstate}, 198 {"luaL_buffinit", (luaV_function) &dll_luaL_buffinit}, 199 {"luaL_prepbuffer", (luaV_function) &dll_luaL_prepbuffer}, 200 {"luaL_addlstring", (luaV_function) &dll_luaL_addlstring}, 201 {"luaL_pushresult", (luaV_function) &dll_luaL_pushresult}, 202 /* lua */ 203 {"lua_close", (luaV_function) &dll_lua_close}, 204 {"lua_gettop", (luaV_function) &dll_lua_gettop}, 205 {"lua_settop", (luaV_function) &dll_lua_settop}, 206 {"lua_pushvalue", (luaV_function) &dll_lua_pushvalue}, 207 {"lua_replace", (luaV_function) &dll_lua_replace}, 208 {"lua_isnumber", (luaV_function) &dll_lua_isnumber}, 209 {"lua_isstring", (luaV_function) &dll_lua_isstring}, 210 {"lua_type", (luaV_function) &dll_lua_type}, 211 {"lua_rawequal", (luaV_function) &dll_lua_rawequal}, 212 {"lua_tonumber", (luaV_function) &dll_lua_tonumber}, 213 {"lua_tointeger", (luaV_function) &dll_lua_tointeger}, 214 {"lua_toboolean", (luaV_function) &dll_lua_toboolean}, 215 {"lua_tolstring", (luaV_function) &dll_lua_tolstring}, 216 {"lua_touserdata", (luaV_function) &dll_lua_touserdata}, 217 {"lua_pushnil", (luaV_function) &dll_lua_pushnil}, 218 {"lua_pushnumber", (luaV_function) &dll_lua_pushnumber}, 219 {"lua_pushinteger", (luaV_function) &dll_lua_pushinteger}, 220 {"lua_pushlstring", (luaV_function) &dll_lua_pushlstring}, 221 {"lua_pushstring", (luaV_function) &dll_lua_pushstring}, 222 {"lua_pushfstring", (luaV_function) &dll_lua_pushfstring}, 223 {"lua_pushcclosure", (luaV_function) &dll_lua_pushcclosure}, 224 {"lua_pushboolean", (luaV_function) &dll_lua_pushboolean}, 225 {"lua_pushlightuserdata", (luaV_function) &dll_lua_pushlightuserdata}, 226 {"lua_getfield", (luaV_function) &dll_lua_getfield}, 227 {"lua_rawget", (luaV_function) &dll_lua_rawget}, 228 {"lua_createtable", (luaV_function) &dll_lua_createtable}, 229 {"lua_newuserdata", (luaV_function) &dll_lua_newuserdata}, 230 {"lua_getmetatable", (luaV_function) &dll_lua_getmetatable}, 231 {"lua_setfield", (luaV_function) &dll_lua_setfield}, 232 {"lua_rawset", (luaV_function) &dll_lua_rawset}, 233 {"lua_rawseti", (luaV_function) &dll_lua_rawseti}, 234 {"lua_remove", (luaV_function) &dll_lua_remove}, 235 {"lua_setmetatable", (luaV_function) &dll_lua_setmetatable}, 236 {"lua_call", (luaV_function) &dll_lua_call}, 237 {"lua_pcall", (luaV_function) &dll_lua_pcall}, 238 /* libs */ 239 {"luaopen_base", (luaV_function) &dll_luaopen_base}, 240 {"luaopen_table", (luaV_function) &dll_luaopen_table}, 241 {"luaopen_string", (luaV_function) &dll_luaopen_string}, 242 {"luaopen_math", (luaV_function) &dll_luaopen_math}, 243 {"luaopen_io", (luaV_function) &dll_luaopen_io}, 244 {"luaopen_os", (luaV_function) &dll_luaopen_os}, 245 {"luaopen_package", (luaV_function) &dll_luaopen_package}, 246 {"luaopen_debug", (luaV_function) &dll_luaopen_debug}, 247 {"luaL_openlibs", (luaV_function) &dll_luaL_openlibs}, 248 {NULL, NULL} 249 }; 250 251 static HANDLE hinstLua = NULL; 252 253 static void 254 end_dynamic_lua(void) 255 { 256 if (hinstLua) 257 { 258 close_dll(hinstLua); 259 hinstLua = 0; 260 } 261 } 262 263 static int 264 lua_link_init(char *libname, int verbose) 265 { 266 const luaV_Reg *reg; 267 if (hinstLua) return OK; 268 hinstLua = load_dll(libname); 269 if (!hinstLua) 270 { 271 if (verbose) 272 EMSG2(_(e_loadlib), libname); 273 return FAIL; 274 } 275 for (reg = luaV_dll; reg->func; reg++) 276 { 277 if ((*reg->func = symbol_from_dll(hinstLua, reg->name)) == NULL) 278 { 279 close_dll(hinstLua); 280 hinstLua = 0; 281 if (verbose) 282 EMSG2(_(e_loadfunc), reg->name); 283 return FAIL; 284 } 285 } 286 return OK; 287 } 288 289 int 290 lua_enabled(int verbose) 291 { 292 return lua_link_init(DYNAMIC_LUA_DLL, verbose) == OK; 293 } 294 295 #endif /* DYNAMIC_LUA */ 296 297 298 /* ======= Internal ======= */ 299 300 static void 301 luaV_newmetatable(lua_State *L, const char *tname) 302 { 303 lua_newtable(L); 304 lua_pushlightuserdata(L, (void *) tname); 305 lua_pushvalue(L, -2); 306 lua_rawset(L, LUA_REGISTRYINDEX); 307 } 308 309 static void * 310 luaV_toudata(lua_State *L, int ud, const char *tname) 311 { 312 void *p = lua_touserdata(L, ud); 313 314 if (p != NULL) /* value is userdata? */ 315 { 316 if (lua_getmetatable(L, ud)) /* does it have a metatable? */ 317 { 318 luaV_getfield(L, tname); /* get metatable */ 319 if (lua_rawequal(L, -1, -2)) /* MTs match? */ 320 { 321 lua_pop(L, 2); /* MTs */ 322 return p; 323 } 324 } 325 } 326 return NULL; 327 } 328 329 static void * 330 luaV_checkudata(lua_State *L, int ud, const char *tname) 331 { 332 void *p = luaV_toudata(L, ud, tname); 333 if (p == NULL) luaL_typerror(L, ud, tname); 334 return p; 335 } 336 337 static void 338 luaV_pushtypval(lua_State *L, typval_T *tv) 339 { 340 if (tv == NULL) luaL_error(L, "null type"); 341 switch (tv->v_type) 342 { 343 case VAR_STRING: 344 lua_pushstring(L, (char *) tv->vval.v_string); 345 break; 346 case VAR_NUMBER: 347 lua_pushinteger(L, (int) tv->vval.v_number); 348 break; 349 #ifdef FEAT_FLOAT 350 case VAR_FLOAT: 351 lua_pushnumber(L, (lua_Number) tv->vval.v_float); 352 break; 353 #endif 354 case VAR_LIST: { 355 list_T *l = tv->vval.v_list; 356 357 if (l != NULL) 358 { 359 /* check cache */ 360 lua_pushlightuserdata(L, (void *) l); 361 lua_rawget(L, LUA_ENVIRONINDEX); 362 if (lua_isnil(L, -1)) /* not interned? */ 363 { 364 listitem_T *li; 365 int n = 0; 366 lua_pop(L, 1); /* nil */ 367 lua_newtable(L); 368 lua_pushlightuserdata(L, (void *) l); 369 lua_pushvalue(L, -2); 370 lua_rawset(L, LUA_ENVIRONINDEX); 371 for (li = l->lv_first; li != NULL; li = li->li_next) 372 { 373 luaV_pushtypval(L, &li->li_tv); 374 lua_rawseti(L, -2, ++n); 375 } 376 } 377 } 378 else lua_pushnil(L); 379 break; 380 } 381 case VAR_DICT: { 382 dict_T *d = tv->vval.v_dict; 383 384 if (d != NULL) 385 { 386 /* check cache */ 387 lua_pushlightuserdata(L, (void *) d); 388 lua_rawget(L, LUA_ENVIRONINDEX); 389 if (lua_isnil(L, -1)) /* not interned? */ 390 { 391 hashtab_T *ht = &d->dv_hashtab; 392 hashitem_T *hi; 393 int n = ht->ht_used; /* remaining items */ 394 lua_pop(L, 1); /* nil */ 395 lua_newtable(L); 396 lua_pushlightuserdata(L, (void *) d); 397 lua_pushvalue(L, -2); 398 lua_rawset(L, LUA_ENVIRONINDEX); 399 for (hi = ht->ht_array; n > 0; hi++) 400 { 401 if (!HASHITEM_EMPTY(hi)) 402 { 403 dictitem_T *di = dict_lookup(hi); 404 luaV_pushtypval(L, &di->di_tv); 405 lua_setfield(L, -2, (char *) hi->hi_key); 406 n--; 407 } 408 } 409 } 410 } 411 else lua_pushnil(L); 412 break; 413 } 414 default: 415 luaL_error(L, "invalid type"); 416 } 417 } 418 419 /* similar to luaL_addlstring, but replaces \0 with \n if toline and 420 * \n with \0 otherwise */ 421 static void 422 luaV_addlstring(luaL_Buffer *b, const char *s, size_t l, int toline) 423 { 424 while (l--) 425 { 426 if (*s == '\0' && toline) 427 luaL_addchar(b, '\n'); 428 else if (*s == '\n' && !toline) 429 luaL_addchar(b, '\0'); 430 else 431 luaL_addchar(b, *s); 432 s++; 433 } 434 } 435 436 static void 437 luaV_pushline(lua_State *L, buf_T *buf, linenr_T n) 438 { 439 const char *s = (const char *) ml_get_buf(buf, n, FALSE); 440 luaL_Buffer b; 441 luaL_buffinit(L, &b); 442 luaV_addlstring(&b, s, strlen(s), 0); 443 luaL_pushresult(&b); 444 } 445 446 static char_u * 447 luaV_toline(lua_State *L, int pos) 448 { 449 size_t l; 450 const char *s = lua_tolstring(L, pos, &l); 451 452 luaL_Buffer b; 453 luaL_buffinit(L, &b); 454 luaV_addlstring(&b, s, l, 1); 455 luaL_pushresult(&b); 456 return (char_u *) lua_tostring(L, -1); 457 } 458 459 /* pops a string s from the top of the stack and calls mf(t) for pieces t of 460 * s separated by newlines */ 461 static void 462 luaV_msgfunc(lua_State *L, msgfunc_T mf) 463 { 464 luaL_Buffer b; 465 size_t l; 466 const char *p, *s = lua_tolstring(L, -1, &l); 467 luaL_buffinit(L, &b); 468 luaV_addlstring(&b, s, l, 0); 469 luaL_pushresult(&b); 470 /* break string */ 471 p = s = lua_tolstring(L, -1, &l); 472 while (l--) 473 { 474 if (*p++ == '\0') /* break? */ 475 { 476 mf((char_u *) s); 477 s = p; 478 } 479 } 480 mf((char_u *) s); 481 lua_pop(L, 2); /* original and modified strings */ 482 } 483 484 485 /* ======= Buffer type ======= */ 486 487 static luaV_Buffer * 488 luaV_newbuffer(lua_State *L, buf_T *buf) 489 { 490 luaV_Buffer *b = (luaV_Buffer *) lua_newuserdata(L, sizeof(luaV_Buffer)); 491 *b = buf; 492 lua_pushlightuserdata(L, (void *) buf); 493 lua_pushvalue(L, -2); 494 lua_rawset(L, LUA_ENVIRONINDEX); /* env[buf] = udata */ 495 /* to avoid GC, store as key in env */ 496 lua_pushvalue(L, -1); 497 lua_pushboolean(L, 1); 498 lua_rawset(L, LUA_ENVIRONINDEX); /* env[udata] = true */ 499 /* set metatable */ 500 luaV_getfield(L, LUAVIM_BUFFER); 501 lua_setmetatable(L, -2); 502 return b; 503 } 504 505 static luaV_Buffer * 506 luaV_pushbuffer (lua_State *L, buf_T *buf) 507 { 508 luaV_Buffer *b = NULL; 509 if (buf == NULL) 510 lua_pushnil(L); 511 else { 512 lua_pushlightuserdata(L, (void *) buf); 513 lua_rawget(L, LUA_ENVIRONINDEX); 514 if (lua_isnil(L, -1)) /* not interned? */ 515 { 516 lua_pop(L, 1); 517 b = luaV_newbuffer(L, buf); 518 } 519 else 520 b = (luaV_Buffer *) lua_touserdata(L, -1); 521 } 522 return b; 523 } 524 525 /* Buffer metamethods */ 526 527 static int 528 luaV_buffer_tostring(lua_State *L) 529 { 530 lua_pushfstring(L, "%s: %p", LUAVIM_BUFFER, lua_touserdata(L, 1)); 531 return 1; 532 } 533 534 static int 535 luaV_buffer_len(lua_State *L) 536 { 537 luaV_Buffer *b = lua_touserdata(L, 1); 538 lua_pushinteger(L, (*b)->b_ml.ml_line_count); 539 return 1; 540 } 541 542 static int 543 luaV_buffer_call(lua_State *L) 544 { 545 luaV_Buffer *b = (luaV_Buffer *) lua_touserdata(L, 1); 546 lua_settop(L, 1); 547 set_curbuf(*b, DOBUF_SPLIT); 548 return 1; 549 } 550 551 static int 552 luaV_buffer_index(lua_State *L) 553 { 554 luaV_Buffer *b = (luaV_Buffer *) lua_touserdata(L, 1); 555 linenr_T n = (linenr_T) lua_tointeger(L, 2); 556 if (n > 0 && n <= (*b)->b_ml.ml_line_count) 557 luaV_pushline(L, *b, n); 558 else if (lua_isstring(L, 2)) 559 { 560 const char *s = lua_tostring(L, 2); 561 if (strncmp(s, "name", 4) == 0) 562 lua_pushstring(L, (char *) (*b)->b_sfname); 563 else if (strncmp(s, "fname", 5) == 0) 564 lua_pushstring(L, (char *) (*b)->b_ffname); 565 else if (strncmp(s, "number", 6) == 0) 566 lua_pushinteger(L, (*b)->b_fnum); 567 /* methods */ 568 else if (strncmp(s, "insert", 6) == 0 569 || strncmp(s, "next", 4) == 0 570 || strncmp(s, "previous", 8) == 0 571 || strncmp(s, "isvalid", 7) == 0) 572 { 573 lua_getmetatable(L, 1); 574 lua_getfield(L, -1, s); 575 } 576 else 577 lua_pushnil(L); 578 } 579 else 580 lua_pushnil(L); 581 return 1; 582 } 583 584 static int 585 luaV_buffer_newindex(lua_State *L) 586 { 587 luaV_Buffer *b = (luaV_Buffer *) lua_touserdata(L, 1); 588 linenr_T n = (linenr_T) luaL_checkinteger(L, 2); 589 #ifdef HAVE_SANDBOX 590 luaV_checksandbox(L); 591 #endif 592 if (n < 1 || n > (*b)->b_ml.ml_line_count) 593 luaL_error(L, "invalid line number"); 594 if (lua_isnil(L, 3)) /* delete line */ 595 { 596 buf_T *buf = curbuf; 597 curbuf = *b; 598 if (u_savedel(n, 1L) == FAIL) 599 { 600 curbuf = buf; 601 luaL_error(L, "cannot save undo information"); 602 } 603 else if (ml_delete(n, FALSE) == FAIL) 604 { 605 curbuf = buf; 606 luaL_error(L, "cannot delete line"); 607 } 608 else { 609 deleted_lines_mark(n, 1L); 610 if (*b == curwin->w_buffer) /* fix cursor in current window? */ 611 { 612 if (curwin->w_cursor.lnum >= n) 613 { 614 if (curwin->w_cursor.lnum > n) 615 { 616 curwin->w_cursor.lnum -= 1; 617 check_cursor_col(); 618 } 619 else check_cursor(); 620 changed_cline_bef_curs(); 621 } 622 invalidate_botline(); 623 } 624 } 625 curbuf = buf; 626 } 627 else if (lua_isstring(L, 3)) /* update line */ 628 { 629 buf_T *buf = curbuf; 630 curbuf = *b; 631 if (u_savesub(n) == FAIL) 632 { 633 curbuf = buf; 634 luaL_error(L, "cannot save undo information"); 635 } 636 else if (ml_replace(n, luaV_toline(L, 3), TRUE) == FAIL) 637 { 638 curbuf = buf; 639 luaL_error(L, "cannot replace line"); 640 } 641 else changed_bytes(n, 0); 642 curbuf = buf; 643 if (*b == curwin->w_buffer) 644 check_cursor_col(); 645 } 646 else 647 luaL_error(L, "wrong argument to change line"); 648 return 0; 649 } 650 651 static int 652 luaV_buffer_insert(lua_State *L) 653 { 654 luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); 655 linenr_T last = (*b)->b_ml.ml_line_count; 656 linenr_T n = (linenr_T) luaL_optinteger(L, 3, last); 657 buf_T *buf; 658 luaL_checktype(L, 2, LUA_TSTRING); 659 #ifdef HAVE_SANDBOX 660 luaV_checksandbox(L); 661 #endif 662 /* fix insertion line */ 663 if (n < 0) n = 0; 664 if (n > last) n = last; 665 /* insert */ 666 buf = curbuf; 667 curbuf = *b; 668 if (u_save(n, n + 1) == FAIL) 669 { 670 curbuf = buf; 671 luaL_error(L, "cannot save undo information"); 672 } 673 else if (ml_append(n, luaV_toline(L, 2), 0, FALSE) == FAIL) 674 { 675 curbuf = buf; 676 luaL_error(L, "cannot insert line"); 677 } 678 else 679 appended_lines_mark(n, 1L); 680 curbuf = buf; 681 update_screen(VALID); 682 return 0; 683 } 684 685 static int 686 luaV_buffer_next(lua_State *L) 687 { 688 luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); 689 luaV_pushbuffer(L, (*b)->b_next); 690 return 1; 691 } 692 693 static int 694 luaV_buffer_previous(lua_State *L) 695 { 696 luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); 697 luaV_pushbuffer(L, (*b)->b_prev); 698 return 1; 699 } 700 701 static int 702 luaV_buffer_isvalid(lua_State *L) 703 { 704 luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); 705 lua_pushlightuserdata(L, (void *) (*b)); 706 lua_rawget(L, LUA_ENVIRONINDEX); 707 lua_pushboolean(L, !lua_isnil(L, -1)); 708 return 1; 709 } 710 711 static const luaL_Reg luaV_Buffer_mt[] = { 712 {"__tostring", luaV_buffer_tostring}, 713 {"__len", luaV_buffer_len}, 714 {"__call", luaV_buffer_call}, 715 {"__index", luaV_buffer_index}, 716 {"__newindex", luaV_buffer_newindex}, 717 {"insert", luaV_buffer_insert}, 718 {"next", luaV_buffer_next}, 719 {"previous", luaV_buffer_previous}, 720 {"isvalid", luaV_buffer_isvalid}, 721 {NULL, NULL} 722 }; 723 724 725 /* ======= Window type ======= */ 726 727 static luaV_Window * 728 luaV_newwindow(lua_State *L, win_T *win) 729 { 730 luaV_Window *w = (luaV_Window *) lua_newuserdata(L, sizeof(luaV_Window)); 731 *w = win; 732 lua_pushlightuserdata(L, (void *) win); 733 lua_pushvalue(L, -2); 734 lua_rawset(L, LUA_ENVIRONINDEX); /* env[win] = udata */ 735 /* to avoid GC, store as key in env */ 736 lua_pushvalue(L, -1); 737 lua_pushboolean(L, 1); 738 lua_rawset(L, LUA_ENVIRONINDEX); /* env[udata] = true */ 739 /* set metatable */ 740 luaV_getfield(L, LUAVIM_WINDOW); 741 lua_setmetatable(L, -2); 742 return w; 743 } 744 745 static luaV_Window * 746 luaV_pushwindow(lua_State *L, win_T *win) 747 { 748 luaV_Window *w = NULL; 749 if (win == NULL) 750 lua_pushnil(L); 751 else { 752 lua_pushlightuserdata(L, (void *) win); 753 lua_rawget(L, LUA_ENVIRONINDEX); 754 if (lua_isnil(L, -1)) /* not interned? */ 755 { 756 lua_pop(L, 1); 757 w = luaV_newwindow(L, win); 758 } 759 else w = (luaV_Window *) lua_touserdata(L, -1); 760 } 761 return w; 762 } 763 764 /* Window metamethods */ 765 766 static int 767 luaV_window_tostring(lua_State *L) 768 { 769 lua_pushfstring(L, "%s: %p", LUAVIM_WINDOW, lua_touserdata(L, 1)); 770 return 1; 771 } 772 773 static int 774 luaV_window_call(lua_State *L) 775 { 776 luaV_Window *w = (luaV_Window *) lua_touserdata(L, 1); 777 lua_settop(L, 1); 778 win_goto(*w); 779 return 1; 780 } 781 782 static int 783 luaV_window_index(lua_State *L) 784 { 785 luaV_Window *w = (luaV_Window *) lua_touserdata(L, 1); 786 const char *s = luaL_checkstring(L, 2); 787 if (strncmp(s, "buffer", 6) == 0) 788 luaV_pushbuffer(L, (*w)->w_buffer); 789 else if (strncmp(s, "line", 4) == 0) 790 lua_pushinteger(L, (*w)->w_cursor.lnum); 791 else if (strncmp(s, "col", 3) == 0) 792 lua_pushinteger(L, (*w)->w_cursor.col + 1); 793 #ifdef FEAT_VERTSPLIT 794 else if (strncmp(s, "width", 5) == 0) 795 lua_pushinteger(L, W_WIDTH((*w))); 796 #endif 797 else if (strncmp(s, "height", 6) == 0) 798 lua_pushinteger(L, (*w)->w_height); 799 /* methods */ 800 else if (strncmp(s, "next", 4) == 0 801 || strncmp(s, "previous", 8) == 0 802 || strncmp(s, "isvalid", 7) == 0) 803 { 804 lua_getmetatable(L, 1); 805 lua_getfield(L, -1, s); 806 } 807 else 808 lua_pushnil(L); 809 return 1; 810 } 811 812 static int 813 luaV_window_newindex (lua_State *L) 814 { 815 luaV_Window *w = (luaV_Window *) lua_touserdata(L, 1); 816 const char *s = luaL_checkstring(L, 2); 817 int v = luaL_checkinteger(L, 3); 818 if (strncmp(s, "line", 4) == 0) 819 { 820 #ifdef HAVE_SANDBOX 821 luaV_checksandbox(L); 822 #endif 823 if (v < 1 || v > (*w)->w_buffer->b_ml.ml_line_count) 824 luaL_error(L, "line out of range"); 825 (*w)->w_cursor.lnum = v; 826 update_screen(VALID); 827 } 828 else if (strncmp(s, "col", 3) == 0) 829 { 830 #ifdef HAVE_SANDBOX 831 luaV_checksandbox(L); 832 #endif 833 (*w)->w_cursor.col = v - 1; 834 update_screen(VALID); 835 } 836 #ifdef FEAT_VERTSPLIT 837 else if (strncmp(s, "width", 5) == 0) 838 { 839 win_T *win = curwin; 840 #ifdef FEAT_GUI 841 need_mouse_correct = TRUE; 842 #endif 843 curwin = *w; 844 win_setwidth(v); 845 curwin = win; 846 } 847 #endif 848 else if (strncmp(s, "height", 6) == 0) 849 { 850 win_T *win = curwin; 851 #ifdef FEAT_GUI 852 need_mouse_correct = TRUE; 853 #endif 854 curwin = *w; 855 win_setheight(v); 856 curwin = win; 857 } 858 else 859 luaL_error(L, "invalid window property: `%s'", s); 860 return 0; 861 } 862 863 static int 864 luaV_window_next(lua_State *L) 865 { 866 luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); 867 luaV_pushwindow(L, (*w)->w_next); 868 return 1; 869 } 870 871 static int 872 luaV_window_previous(lua_State *L) 873 { 874 luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); 875 luaV_pushwindow(L, (*w)->w_prev); 876 return 1; 877 } 878 879 static int 880 luaV_window_isvalid(lua_State *L) 881 { 882 luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); 883 lua_pushlightuserdata(L, (void *) (*w)); 884 lua_rawget(L, LUA_ENVIRONINDEX); 885 lua_pushboolean(L, !lua_isnil(L, -1)); 886 return 1; 887 } 888 889 static const luaL_Reg luaV_Window_mt[] = { 890 {"__tostring", luaV_window_tostring}, 891 {"__call", luaV_window_call}, 892 {"__index", luaV_window_index}, 893 {"__newindex", luaV_window_newindex}, 894 {"next", luaV_window_next}, 895 {"previous", luaV_window_previous}, 896 {"isvalid", luaV_window_isvalid}, 897 {NULL, NULL} 898 }; 899 900 901 /* ======= Vim module ======= */ 902 903 static int 904 luaV_print(lua_State *L) 905 { 906 int i, n = lua_gettop(L); /* nargs */ 907 const char *s; 908 size_t l; 909 luaL_Buffer b; 910 luaL_buffinit(L, &b); 911 lua_getglobal(L, "tostring"); 912 for (i = 1; i <= n; i++) 913 { 914 lua_pushvalue(L, -1); /* tostring */ 915 lua_pushvalue(L, i); /* arg */ 916 lua_call(L, 1, 1); 917 s = lua_tolstring(L, -1, &l); 918 if (s == NULL) 919 return luaL_error(L, "cannot convert to string"); 920 if (i > 1) luaL_addchar(&b, ' '); /* use space instead of tab */ 921 luaV_addlstring(&b, s, l, 0); 922 lua_pop(L, 1); 923 } 924 luaL_pushresult(&b); 925 luaV_msg(L); 926 return 0; 927 } 928 929 static int 930 luaV_debug(lua_State *L) 931 { 932 lua_settop(L, 0); 933 lua_getglobal(L, "vim"); 934 lua_getfield(L, -1, "eval"); 935 lua_remove(L, -2); /* vim.eval at position 1 */ 936 for (;;) 937 { 938 const char *input; 939 size_t l; 940 lua_pushvalue(L, 1); /* vim.eval */ 941 lua_pushliteral(L, "input('lua_debug> ')"); 942 lua_call(L, 1, 1); /* return string */ 943 input = lua_tolstring(L, -1, &l); 944 if (l == 0 || strcmp(input, "cont") == 0) 945 return 0; 946 msg_putchar('\n'); /* avoid outputting on input line */ 947 if (luaL_loadbuffer(L, input, l, "=(debug command)") 948 || lua_pcall(L, 0, 0, 0)) 949 luaV_emsg(L); 950 lua_settop(L, 1); /* remove eventual returns, but keep vim.eval */ 951 } 952 } 953 954 static int 955 luaV_command(lua_State *L) 956 { 957 do_cmdline_cmd((char_u *) luaL_checkstring(L, 1)); 958 update_screen(VALID); 959 return 0; 960 } 961 962 static int 963 luaV_eval(lua_State *L) 964 { 965 typval_T *tv = eval_expr((char_u *) luaL_checkstring(L, 1), NULL); 966 if (tv == NULL) luaL_error(L, "invalid expression"); 967 luaV_pushtypval(L, tv); 968 return 1; 969 } 970 971 static int 972 luaV_beep(lua_State *L UNUSED) 973 { 974 vim_beep(); 975 return 0; 976 } 977 978 static int 979 luaV_line(lua_State *L) 980 { 981 luaV_pushline(L, curbuf, curwin->w_cursor.lnum); 982 return 1; 983 } 984 985 static int 986 luaV_buffer(lua_State *L) 987 { 988 buf_T *buf; 989 if (lua_isstring(L, 1)) /* get by number or name? */ 990 { 991 if (lua_isnumber(L, 1)) /* by number? */ 992 { 993 int n = lua_tointeger(L, 1); 994 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 995 if (buf->b_fnum == n) break; 996 } 997 else { /* by name */ 998 size_t l; 999 const char *s = lua_tolstring(L, 1, &l); 1000 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 1001 { 1002 if (buf->b_ffname == NULL || buf->b_sfname == NULL) 1003 { 1004 if (l == 0) break; 1005 } 1006 else if (strncmp(s, (char *)buf->b_ffname, l) == 0 1007 || strncmp(s, (char *)buf->b_sfname, l) == 0) 1008 break; 1009 } 1010 } 1011 if (buf == NULL) /* not found? */ 1012 lua_pushnil(L); 1013 else 1014 luaV_pushbuffer(L, buf); 1015 } 1016 else { 1017 buf = (lua_toboolean(L, 1)) ? firstbuf : curbuf; /* first buffer? */ 1018 luaV_pushbuffer(L, buf); 1019 } 1020 return 1; 1021 } 1022 1023 static int 1024 luaV_window(lua_State *L) 1025 { 1026 win_T *win; 1027 if (lua_isnumber(L, 1)) /* get by number? */ 1028 { 1029 int n = lua_tointeger(L, 1); 1030 for (win = firstwin; win != NULL; win = win->w_next, n--) 1031 if (n == 1) break; 1032 if (win == NULL) /* not found? */ 1033 lua_pushnil(L); 1034 else 1035 luaV_pushwindow(L, win); 1036 } 1037 else { 1038 win = (lua_toboolean(L, 1)) ? firstwin : curwin; /* first window? */ 1039 luaV_pushwindow(L, win); 1040 } 1041 return 1; 1042 } 1043 1044 static int 1045 luaV_open(lua_State *L) 1046 { 1047 char_u *s = NULL; 1048 #ifdef HAVE_SANDBOX 1049 luaV_checksandbox(L); 1050 #endif 1051 if (lua_isstring(L, 1)) s = (char_u *) lua_tostring(L, 1); 1052 luaV_pushbuffer(L, buflist_new(s, NULL, 1L, BLN_LISTED)); 1053 return 1; 1054 } 1055 1056 static int 1057 luaV_isbuffer(lua_State *L) 1058 { 1059 lua_pushboolean(L, luaV_toudata(L, 1, LUAVIM_BUFFER) != NULL); 1060 return 1; 1061 } 1062 1063 static int 1064 luaV_iswindow(lua_State *L) 1065 { 1066 lua_pushboolean(L, luaV_toudata(L, 1, LUAVIM_WINDOW) != NULL); 1067 return 1; 1068 } 1069 1070 /* for freeing buffer and window objects; lightuserdata as arg */ 1071 static int 1072 luaV_free(lua_State *L) 1073 { 1074 lua_pushvalue(L, 1); /* lightudata */ 1075 lua_rawget(L, LUA_ENVIRONINDEX); 1076 if (!lua_isnil(L, -1)) 1077 { 1078 lua_pushnil(L); 1079 lua_rawset(L, LUA_ENVIRONINDEX); /* env[udata] = nil */ 1080 lua_pushnil(L); 1081 lua_rawset(L, LUA_ENVIRONINDEX); /* env[lightudata] = nil */ 1082 } 1083 return 0; 1084 } 1085 1086 static const luaL_Reg luaV_module[] = { 1087 {"command", luaV_command}, 1088 {"eval", luaV_eval}, 1089 {"beep", luaV_beep}, 1090 {"line", luaV_line}, 1091 {"buffer", luaV_buffer}, 1092 {"window", luaV_window}, 1093 {"open", luaV_open}, 1094 {"isbuffer", luaV_isbuffer}, 1095 {"iswindow", luaV_iswindow}, 1096 {NULL, NULL} 1097 }; 1098 1099 static int 1100 luaopen_vim(lua_State *L) 1101 { 1102 /* set environment */ 1103 lua_newtable(L); 1104 lua_newtable(L); 1105 lua_pushliteral(L, "v"); 1106 lua_setfield(L, -2, "__mode"); 1107 lua_setmetatable(L, -2); 1108 lua_replace(L, LUA_ENVIRONINDEX); 1109 /* print */ 1110 lua_pushcfunction(L, luaV_print); 1111 lua_setglobal(L, "print"); 1112 /* debug.debug */ 1113 lua_getglobal(L, "debug"); 1114 lua_pushcfunction(L, luaV_debug); 1115 lua_setfield(L, -2, "debug"); 1116 lua_pop(L, 1); 1117 /* free */ 1118 lua_pushlightuserdata(L, (void *) LUAVIM_FREE); 1119 lua_pushcfunction(L, luaV_free); 1120 lua_rawset(L, LUA_REGISTRYINDEX); 1121 /* register */ 1122 luaV_newmetatable(L, LUAVIM_BUFFER); 1123 luaL_register(L, NULL, luaV_Buffer_mt); 1124 luaV_newmetatable(L, LUAVIM_WINDOW); 1125 luaL_register(L, NULL, luaV_Window_mt); 1126 luaL_register(L, LUAVIM_NAME, luaV_module); 1127 return 0; 1128 } 1129 1130 static lua_State * 1131 luaV_newstate(void) 1132 { 1133 lua_State *L = luaL_newstate(); 1134 luaL_openlibs(L); /* core libs */ 1135 lua_pushcfunction(L, luaopen_vim); /* vim */ 1136 lua_call(L, 0, 0); 1137 return L; 1138 } 1139 1140 static void 1141 luaV_setrange(lua_State *L, int line1, int line2) 1142 { 1143 lua_getglobal(L, LUAVIM_NAME); 1144 lua_pushinteger(L, line1); 1145 lua_setfield(L, -2, "firstline"); 1146 lua_pushinteger(L, line2); 1147 lua_setfield(L, -2, "lastline"); 1148 lua_pop(L, 1); /* vim table */ 1149 } 1150 1151 1152 /* ======= Interface ======= */ 1153 1154 static lua_State *L = NULL; 1155 1156 static int 1157 lua_is_open(void) 1158 { 1159 return L != NULL; 1160 } 1161 1162 static int 1163 lua_init(void) 1164 { 1165 if (L == NULL) 1166 { 1167 #ifdef DYNAMIC_LUA 1168 if (!lua_enabled(TRUE)) 1169 { 1170 EMSG(_("Lua library cannot be loaded.")); 1171 return FAIL; 1172 } 1173 #endif 1174 L = luaV_newstate(); 1175 } 1176 return OK; 1177 } 1178 1179 void 1180 lua_end(void) 1181 { 1182 if (L != NULL) 1183 { 1184 lua_close(L); 1185 L = NULL; 1186 #ifdef DYNAMIC_LUA 1187 end_dynamic_lua(); 1188 #endif 1189 } 1190 } 1191 1192 /* ex commands */ 1193 void 1194 ex_lua(exarg_T *eap) 1195 { 1196 char *script; 1197 if (lua_init() == FAIL) return; 1198 script = (char *) script_get(eap, eap->arg); 1199 if (!eap->skip) 1200 { 1201 char *s = (script) ? script : (char *) eap->arg; 1202 luaV_setrange(L, eap->line1, eap->line2); 1203 if (luaL_loadbuffer(L, s, strlen(s), LUAVIM_CHUNKNAME) 1204 || lua_pcall(L, 0, 0, 0)) 1205 luaV_emsg(L); 1206 } 1207 if (script != NULL) vim_free(script); 1208 } 1209 1210 void 1211 ex_luado(exarg_T *eap) 1212 { 1213 linenr_T l; 1214 const char *s = (const char *) eap->arg; 1215 luaL_Buffer b; 1216 size_t len; 1217 if (lua_init() == FAIL) return; 1218 if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) 1219 { 1220 EMSG(_("cannot save undo information")); 1221 return; 1222 } 1223 luaV_setrange(L, eap->line1, eap->line2); 1224 luaL_buffinit(L, &b); 1225 luaL_addlstring(&b, "return function(line) ", 22); /* header */ 1226 luaL_addlstring(&b, s, strlen(s)); 1227 luaL_addlstring(&b, " end", 4); /* footer */ 1228 luaL_pushresult(&b); 1229 s = lua_tolstring(L, -1, &len); 1230 if (luaL_loadbuffer(L, s, len, LUAVIM_CHUNKNAME)) 1231 { 1232 luaV_emsg(L); 1233 lua_pop(L, 1); /* function body */ 1234 return; 1235 } 1236 lua_call(L, 0, 1); 1237 lua_replace(L, -2); /* function -> body */ 1238 for (l = eap->line1; l <= eap->line2; l++) 1239 { 1240 lua_pushvalue(L, -1); /* function */ 1241 luaV_pushline(L, curbuf, l); /* current line as arg */ 1242 if (lua_pcall(L, 1, 1, 0)) 1243 { 1244 luaV_emsg(L); 1245 break; 1246 } 1247 if (lua_isstring(L, -1)) /* update line? */ 1248 { 1249 #ifdef HAVE_SANDBOX 1250 luaV_checksandbox(L); 1251 #endif 1252 ml_replace(l, luaV_toline(L, -1), TRUE); 1253 changed_bytes(l, 0); 1254 lua_pop(L, 1); /* result from luaV_toline */ 1255 } 1256 lua_pop(L, 1); /* line */ 1257 } 1258 lua_pop(L, 1); /* function */ 1259 check_cursor(); 1260 update_screen(NOT_VALID); 1261 } 1262 1263 void 1264 ex_luafile(exarg_T *eap) 1265 { 1266 if (lua_init() == FAIL) 1267 return; 1268 if (!eap->skip) 1269 { 1270 luaV_setrange(L, eap->line1, eap->line2); 1271 if (luaL_loadfile(L, (char *) eap->arg) || lua_pcall(L, 0, 0, 0)) 1272 luaV_emsg(L); 1273 } 1274 } 1275 1276 /* buffer */ 1277 void 1278 lua_buffer_free(buf_T *buf) 1279 { 1280 if (!lua_is_open()) return; 1281 luaV_getfield(L, LUAVIM_FREE); 1282 lua_pushlightuserdata(L, (void *) buf); 1283 lua_call(L, 1, 0); 1284 } 1285 1286 /* window */ 1287 void 1288 lua_window_free(win_T *win) 1289 { 1290 if (!lua_is_open()) return; 1291 luaV_getfield(L, LUAVIM_FREE); 1292 lua_pushlightuserdata(L, (void *) win); 1293 lua_call(L, 1, 0); 1294 } 1295 1296 #endif 1297