Ticket #4187: SlangScriptingForMC.patch
File SlangScriptingForMC.patch, 86.4 KB (added by psprint, 3 years ago) |
---|
-
lib/fileloc.h
From 2cf267d8a0cfda61fdc94696bbc997c8369a43d1 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Mon, 25 Jan 2021 13:54:14 -0600 Subject: Slang Scripting Support. --- lib/fileloc.h | 2 + lib/keybind.c | 140 +++- lib/keybind.h | 31 +- lib/mcconfig/paths.c | 3 + lib/widget/dialog.c | 4 + lib/widget/wtools.c | 64 ++ lib/widget/wtools.h | 11 + misc/grow_shrink_integer.plugin.sl | 102 +++ misc/init.sl | 49 ++ src/Makefile.am | 5 +- src/editor/edit.c | 26 + src/editor/editbuffer.c | 122 ++++ src/editor/editbuffer.h | 4 + src/editor/editcmd.c | 58 +- src/editor/editwidget.c | 1 + src/main.c | 11 + src/setup.c | 18 +- src/slang_api_functions.c | 423 ++++++++++++ src/slang_api_functions.h | 52 ++ src/slang_api_functions_glue.c | 871 ++++++++++++++++++++++++ src/slang_engine.c | 275 ++++++++ src/{vfs/fish/fish.h => slang_engine.h} | 19 +- 22 files changed, 2212 insertions(+), 79 deletions(-) create mode 100644 misc/grow_shrink_integer.plugin.sl create mode 100644 misc/init.sl create mode 100644 src/slang_api_functions.c create mode 100644 src/slang_api_functions.h create mode 100644 src/slang_api_functions_glue.c create mode 100644 src/slang_engine.c copy src/{vfs/fish/fish.h => slang_engine.h} (72%) diff --git a/lib/fileloc.h b/lib/fileloc.h index c7d3bc625..44c3b5efd 100644
a b 30 30 #define CHARSETS_LIST "mc.charsets" 31 31 #define MC_LIB_EXT "mc.ext" 32 32 #define MC_MACRO_FILE "mc.macros" 33 #define MC_SLANG_INIT_FILE "init.sl" 34 #define MC_PLUGIN_DIR "plugin" 33 35 34 36 #define FISH_PREFIX "fish" 35 37 -
lib/keybind.c
diff --git a/lib/keybind.c b/lib/keybind.c index abd44d3e2..4fd8aaed6 100644
a b 38 38 39 39 /*** global variables ****************************************************************************/ 40 40 41 int new_dynamic_command_id = 100000; 42 41 43 /*** file scope macro definitions ****************************************************************/ 42 44 43 45 #define ADD_KEYMAP_NAME(name) \ … … 47 49 48 50 /*** file scope variables ************************************************************************/ 49 51 50 static name_keymap_t command_names [] = {52 static name_keymap_t command_names_start[] = { 51 53 /* common */ 52 54 ADD_KEYMAP_NAME (InsertChar), 53 55 ADD_KEYMAP_NAME (Enter), … … static name_keymap_t command_names[] = { 375 377 {NULL, CK_IgnoreKey} 376 378 }; 377 379 378 /* *INDENT-OFF* */ 379 static const size_t num_command_names = G_N_ELEMENTS (command_names) - 1; 380 /* *INDENT-ON* */ 380 static name_keymap_t *command_names = NULL; 381 382 static size_t num_command_names = 0; 383 384 static gboolean has_been_sorted = FALSE; 381 385 382 386 /*** file scope functions ************************************************************************/ 383 387 /* --------------------------------------------------------------------------------------------- */ 384 388 389 /* Initializes the dynamic command names array. */ 390 static void 391 init_command_names (void) 392 { 393 keybind_add_new_action (NULL, -1); 394 } 395 396 /* --------------------------------------------------------------------------------------------- */ 397 385 398 static int 386 399 name_keymap_comparator (const void *p1, const void *p2) 387 400 { … … name_keymap_comparator (const void *p1, const void *p2) 396 409 static inline void 397 410 sort_command_names (void) 398 411 { 399 static gboolean has_been_sorted = FALSE; 412 if (!command_names) 413 init_command_names (); 400 414 401 415 if (!has_been_sorted) 402 416 { 403 417 qsort (command_names, num_command_names, 404 418 sizeof (command_names[0]), &name_keymap_comparator); 419 405 420 has_been_sorted = TRUE; 406 421 } 407 422 } … … sort_command_names (void) 409 424 /* --------------------------------------------------------------------------------------------- */ 410 425 411 426 static void 412 keymap_add (GArray * keymap, long key, long cmd, const char *caption )427 keymap_add (GArray * keymap, long key, long cmd, const char *caption, key_origin_t origin) 413 428 { 414 429 if (key != 0 && cmd != CK_IgnoreKey) 415 430 { … … keymap_add (GArray * keymap, long key, long cmd, const char *caption) 417 432 418 433 new_bind.key = key; 419 434 new_bind.command = cmd; 435 new_bind.origin = origin; 420 436 g_snprintf (new_bind.caption, sizeof (new_bind.caption), "%s", caption); 421 437 g_array_append_val (keymap, new_bind); 422 438 } … … keymap_add (GArray * keymap, long key, long cmd, const char *caption) 426 442 /*** public functions ****************************************************************************/ 427 443 /* --------------------------------------------------------------------------------------------- */ 428 444 445 /* Adds a dynamic command name. Can be used to initialize the dynamic 446 command names array by passing NULL as the first parameter. */ 447 int 448 keybind_add_new_action (const char *new_command_name, int new_ck_id) 449 { 450 name_keymap_t search_key = { 0 }; 451 name_keymap_t *old_command_names = command_names, *res = NULL, *src_commands; 452 int ret = 0, /* Default return code → no command name added */ 453 size = 0; 454 455 /* Make a private copy of the command name. */ 456 search_key.name = new_command_name = g_strdup (new_command_name); 457 458 /* Count the size of the existing command names array. */ 459 460 src_commands = command_names ? command_names : command_names_start; 461 for (; src_commands[size].name; size++); 462 463 /* Initialize the size variable of the command names array. */ 464 num_command_names = size; 465 466 /* Check if the key already exists, i.e.: if the command is already registered. */ 467 if (search_key.name != NULL && strlen (search_key.name)) 468 { 469 res = bsearch (&search_key, src_commands, 470 num_command_names, sizeof (command_names_start[0]), name_keymap_comparator); 471 } 472 473 /* 474 * a) Such command already exists or, b) no command given and the registry is already 475 * initialized (the first bootstrap invocation has been already run) ? If so, then exit doing 476 * no action. Note that the CK ID of the existing command is not updated and remains the same. 477 */ 478 479 if (res != NULL || (!new_command_name && old_command_names)) 480 { 481 return ret; 482 } 483 484 /* Allocate new space and copy the old keymap names. */ 485 command_names = (name_keymap_t *) calloc (size + (new_command_name ? 2 : 1), 486 sizeof (name_keymap_t)); 487 488 /* Extending an existing array? */ 489 if (old_command_names != NULL) 490 { 491 /* ...yes. Copy from `old_command_names` dynamic array. */ 492 memcpy (command_names, old_command_names, (size + 1) * sizeof (name_keymap_t)); 493 494 /* Release the previous command names after copying them. */ 495 free (old_command_names); 496 } 497 else 498 { 499 /* ...no. Copy the initial, compiled in command names into the dynamic array. */ 500 memcpy (command_names, command_names_start, sizeof (command_names_start)); 501 } 502 503 /* Add a new keymap if requested (i.e.: if given any non-NULL name). */ 504 if (new_command_name != NULL) 505 { 506 /* Insert the new command name at the end, then follow an empty sentinel element. */ 507 command_names[size].name = new_command_name; 508 command_names[size].val = new_ck_id; 509 command_names[size + 1].name = NULL; 510 command_names[size + 1].val = CK_IgnoreKey; 511 512 /* Increase the command name count. */ 513 num_command_names++; 514 515 /* Sort the new array. */ 516 has_been_sorted = FALSE; 517 sort_command_names (); 518 519 /* Return that a new command name has been registered. */ 520 ret = 1; 521 } 522 return ret; 523 } 524 525 /* --------------------------------------------------------------------------------------------- */ 526 429 527 void 430 keybind_cmd_bind (GArray * keymap, const char *keybind, long action )528 keybind_cmd_bind (GArray * keymap, const char *keybind, long action, key_origin_t origin) 431 529 { 432 530 char *caption = NULL; 433 531 long key; 434 532 435 533 key = lookup_key (keybind, &caption); 436 keymap_add (keymap, key, action, caption );534 keymap_add (keymap, key, action, caption, origin); 437 535 g_free (caption); 438 536 } 439 537 … … const char * 459 557 keybind_lookup_actionname (long action) 460 558 { 461 559 size_t i; 560 const char *name = NULL; 462 561 463 562 for (i = 0; command_names[i].name != NULL; i++) 464 563 if (command_names[i].val == action) 465 return command_names[i].name; 564 { 565 name = command_names[i].name; 566 break; 567 } 466 568 467 return NULL; 569 570 return name; 571 } 572 573 /* --------------------------------------------------------------------------------------------- */ 574 575 /* Finds and returns the ·origin· field of the key bound to the given CK action. */ 576 key_origin_t 577 keybind_lookup_keymap_origin (const global_keymap_t * keymap, long action) 578 { 579 if (keymap != NULL) 580 { 581 size_t i; 582 583 for (i = 0; keymap[i].key != 0; i++) 584 if (keymap[i].command == action) 585 return keymap[i].origin; 586 } 587 return ORIGIN_UNKNOWN; 468 588 } 469 589 470 590 /* --------------------------------------------------------------------------------------------- */ -
lib/keybind.h
diff --git a/lib/keybind.h b/lib/keybind.h index af019df09..a3f1cd268 100644
a b enum 346 346 CK_MergeOther 347 347 }; 348 348 349 /* 350 * The values of the key binding «origin» indicator field in the global_keymap_t entry for the 351 * binding. It informs about from where the key binding comes from (the binary, a config file, 352 * a slang script / plugin). 353 */ 354 355 typedef enum 356 { 357 /* 358 * A special, error value used when the keymap entry for the given action cannot be found, 359 * when looking up origin field by the CK action code. 360 */ 361 ORIGIN_UNKNOWN = -1, 362 363 /* 364 * Assign 0, so that omision of the field in the global_keymap_t initializer will lead to this. 365 * Meaning: the `mc` binary is the origin of the keybinding. 366 */ 367 ORIGIN_COMPILE_TIME = 0, 368 369 ORIGIN_FILE, /* The binding comes from a file. */ 370 ORIGIN_SLANG_SCRIPT /* The binding comes from S-Lang script execution (e.g.: a plugin) */ 371 } key_origin_t; 372 349 373 /*** structures declarations (and typedefs of structures)*****************************************/ 350 374 351 375 typedef struct name_keymap_t … … typedef struct global_keymap_t 368 392 long key; 369 393 long command; 370 394 char caption[KEYMAP_SHORTCUT_LENGTH]; 395 key_origin_t origin; 371 396 } global_keymap_t; 372 397 373 398 /*** global variables defined in .c file *********************************************************/ 374 399 400 extern int new_dynamic_command_id; 401 375 402 /*** declarations of public functions ************************************************************/ 376 403 377 void keybind_cmd_bind (GArray * keymap, const char *keybind, long action); 404 int keybind_add_new_action (const char *new_command_name, int new_ck_id); 405 void keybind_cmd_bind (GArray * keymap, const char *keybind, long action, key_origin_t origin); 378 406 long keybind_lookup_action (const char *name); 379 407 const char *keybind_lookup_actionname (long action); 408 key_origin_t keybind_lookup_keymap_origin (const global_keymap_t * keymap, long action); 380 409 const char *keybind_lookup_keymap_shortcut (const global_keymap_t * keymap, long action); 381 410 long keybind_lookup_keymap_command (const global_keymap_t * keymap, long key); 382 411 -
lib/mcconfig/paths.c
diff --git a/lib/mcconfig/paths.c b/lib/mcconfig/paths.c index e111dc7a2..33ca890b4 100644
a b static const struct 72 72 { "cedit" PATH_SEP_STR "menu", &mc_config_str, EDIT_HOME_MENU }, 73 73 { "panels.ini", &mc_config_str, MC_PANELS_FILE }, 74 74 75 /* Plugins (S-Lang scripting). */ 76 { "plugin", &mc_config_str, MC_PLUGIN_DIR }, 77 75 78 /* User should move this file with applying some changes in file */ 76 79 { "", &mc_config_str, MC_FILEBIND_FILE }, 77 80 -
lib/widget/dialog.c
diff --git a/lib/widget/dialog.c b/lib/widget/dialog.c index b8a08f029..2aca8f8a0 100644
a b frontend_dlg_run (WDialog * h) 313 313 314 314 widget_update_cursor (wh); 315 315 316 /* Emit any postponed message boxes. */ 317 if (are_postponed_messages ()) 318 display_postponed_messages (); 319 316 320 /* Clear interrupt flag */ 317 321 tty_got_interrupt (); 318 322 d_key = tty_get_event (&event, GROUP (h)->mouse_status == MOU_REPEAT, TRUE); -
lib/widget/wtools.c
diff --git a/lib/widget/wtools.c b/lib/widget/wtools.c index 5c5dc4487..d1104d8bc 100644
a b 50 50 51 51 /*** file scope type declarations ****************************************************************/ 52 52 53 typedef struct postponed_msg_s 54 { 55 int flags; 56 char *title; 57 char *text; 58 } postponed_msg_t; 59 53 60 /*** file scope variables ************************************************************************/ 54 61 62 static GSList *postponed_msgs = NULL; 63 55 64 static WDialog *last_query_dlg; 56 65 57 66 static int sel_pos = 0; … … wtools_parent_call_string (void *routine, int argc, ...) 268 277 /*** public functions ****************************************************************************/ 269 278 /* --------------------------------------------------------------------------------------------- */ 270 279 280 /* Return truth if there are some queued postponed messages. */ 281 gboolean 282 are_postponed_messages (void) 283 { 284 return postponed_msgs != NULL; 285 } 286 287 /* --------------------------------------------------------------------------------------------- */ 288 289 void 290 postponed_message (int flags_, const char *title, const char *text, ...) 291 { 292 va_list args; 293 294 /* Initialize a heap allocated, message container struct. */ 295 postponed_msg_t *msg_heap; 296 msg_heap = g_new (postponed_msg_t, 1); 297 298 va_start (args, text); 299 300 *msg_heap = (postponed_msg_t) 301 { 302 flags_, g_strdup (title), g_strdup_vprintf (text, args)}; 303 304 va_end (args); 305 306 /* Append the heap pointer to the GSList. */ 307 postponed_msgs = g_slist_append (postponed_msgs, msg_heap); 308 } 309 310 /* --------------------------------------------------------------------------------------------- */ 311 312 void 313 display_postponed_messages () 314 { 315 GSList *it; 316 317 for (it = postponed_msgs; it != NULL; it = it->next) 318 { 319 postponed_msg_t *msg = (postponed_msg_t *) it->data; 320 321 /* Show the dialog with the saved message body and flags. */ 322 message (msg->flags, msg->title, msg->text); 323 324 /* Release the fields of the struct. */ 325 g_free (msg->title); 326 g_free (msg->text); 327 } 328 329 /* Release whole list and also call g_free on each element. */ 330 g_slist_free_full (g_steal_pointer (&postponed_msgs), g_free); 331 } 332 333 /* --------------------------------------------------------------------------------------------- */ 334 271 335 /** Used to ask questions to the user */ 272 336 int 273 337 query_dialog (const char *header, const char *text, int flags, int count, ...) -
lib/widget/wtools.h
diff --git a/lib/widget/wtools.h b/lib/widget/wtools.h index cd0bc3253..b1f7a9b47 100644
a b struct simple_status_msg_t 61 61 62 62 /*** declarations of public functions ************************************************************/ 63 63 64 /* 65 * A simple message queue that postpones the messages until MC enters the main loop. It allows to 66 * use dialog messages early during the initialization, e.g.: to signal problems during it. Message 67 * will appear when UI is fully initialized and drawn. 68 */ 69 70 gboolean are_postponed_messages (void); 71 void postponed_message (int flags, const char *title, const char *text, ...) 72 __attribute__ ((format (printf, 3, 4))); 73 void display_postponed_messages (void); 74 64 75 /* The input dialogs */ 65 76 char *input_dialog (const char *header, const char *text, 66 77 const char *history_name, const char *def_text, -
new file misc/grow_shrink_integer.plugin.sl
diff --git a/misc/grow_shrink_integer.plugin.sl b/misc/grow_shrink_integer.plugin.sl new file mode 100644 index 000000000..ad91db04e
- + 1 % Copy this file to ~/.config/mc/plugin/, it'll be automatically loaded at startup. 2 3 define grow_shrink_int__get_current_or_next_number() { 4 variable pos = -1, bol_offset, eol_offset, start_digit_offset, 5 t = "", found, start_digit_seen, c1, c2; 6 7 bol_offset = mc->cure_get_bol (); 8 eol_offset = mc->cure_get_eol (); 9 10 % Find the start of the number (a word, actually) 11 for (pos = mc->cure_cursor_offset(); pos >= bol_offset; pos--) 12 { 13 c1 = mc->cure_get_byte (pos); 14 c2 = mc->cure_get_byte (pos - 1); 15 16 if (not isdigit(c1)) 17 break; 18 if (not isspace (c1) && isspace (c2)) 19 break; 20 } 21 22 % Find the end of the number (a word, actually) 23 found=0; 24 start_digit_offset=0; 25 start_digit_seen=0; 26 for (; pos <= eol_offset; pos++) 27 { 28 c1 = mc->cure_get_byte (pos); 29 c2 = mc->cure_get_byte (pos + 1); 30 31 % Append the byte to the string 32 if (isdigit (c1)) { 33 if (not start_digit_seen) { 34 start_digit_seen=1; 35 start_digit_offset=pos; 36 } 37 found = 1; t += char(c1); 38 } 39 40 if (isdigit (c1) && not isdigit (c2)) { 41 found += 1; 42 break; 43 } 44 } 45 46 % Any number found? 47 % If not, return an empty string and minus one position 48 if (found == 2) { 49 % Include any minus sign 50 c1 = mc->cure_get_byte (start_digit_offset-1); 51 if (c1 == '-') 52 t = char(c1) + t; 53 } 54 return t, pos; 55 } 56 57 % A backend function for the two next public functions 58 define grow_shrink_int__increment(direction) { 59 variable pos, cpos, number = 0; 60 variable number_str, new_number_buf; 61 variable number_len = 0, new_number_len = 0, idx; 62 63 % Get the number 64 (number_str, pos) = grow_shrink_int__get_current_or_next_number(); 65 % Has been a number found? 66 if (strlen(number_str) > 0) { 67 number_len = strlen(number_str); 68 % Convert the string into integer to increment it 69 number = atoll(number_str); 70 number += (direction > 0) ? 1 : -1; 71 new_number_buf = string(number); 72 new_number_len = strlen(new_number_buf); 73 74 % Move the cursor to the found number 75 cpos = mc->cure_cursor_offset(); 76 mc->cure_cursor_move (pos-cpos); 77 % Delete the existing number 78 mc->cure_delete(); 79 for (idx = 0; idx < number_len-1; idx ++) 80 mc->cure_backspace(); 81 % Insert updated number 82 for (idx = 0; idx < new_number_len; idx ++) 83 mc->cure_insert_ahead(new_number_buf[new_number_len-idx-1]); 84 } 85 86 % Return the updated number. Just for fun :) Maybe it'll find some use one day 87 return number; 88 } 89 90 % The end user function for incrementing an integer 91 define grow_shrink_int__grow_int() { 92 return grow_shrink_int__increment(1); 93 } 94 95 % The end user function for taking 1 from an integer 96 define grow_shrink_int__shrink_int() { 97 return grow_shrink_int__increment(-1); 98 } 99 100 % Register the default key bindings – Alt-a for grow, Alt-x for shrink. 101 mc->editor_map_key_to_func("GrowInteger", "alt-a", "grow_shrink_int__grow_int"); 102 mc->editor_map_key_to_func("ShrinkInteger", "alt-x", "grow_shrink_int__shrink_int"); -
new file misc/init.sl
diff --git a/misc/init.sl b/misc/init.sl new file mode 100644 index 000000000..9f77dcfa5
- + 1 % MCEdit Startup Script in S-Lang Scripting Language 2 % 3 % See: 4 % – https://www.jedsoft.org/slang/doc/html/slang.html, 5 % for a reference on the language. 6 7 % Increase to 2 to have confirmation messages after loading this script and other plugins. 8 mc_loglevel=1; 9 10 % A function showing a listbox: 11 define listbox_display_function() { 12 variable items = ["This is a listbox", "It's displayed from…", 13 "…`init.sl` S-Lang startup script"]; 14 15 variable sel = mc->listbox(5, 35, "Welcome",items); 16 17 if (sel >= 0) 18 mc->message("You have selected:", "item #" + string(sel+1) + ": " + items[sel]); 19 else 20 mc->message("Info", "No selection have been made"); 21 22 return 1; 23 } 24 25 % A function causing runtime error: 26 define divide_by_zero() { 27 variable string = "Divided by 0 on purpose"; 28 variable mc = 1 / 0; 29 } 30 31 % A function to present backtrace functionality: 32 define b_function() { 33 variable tmp_variable = 5; 34 divide_by_zero(); 35 return 0; 36 } 37 38 % A function to present backtrace functionality: 39 define a_function() { 40 b_function(); 41 return 0; 42 } 43 44 45 % Show an error message on Ctrl-t: 46 mc->editor_map_key_to_func("DivBy0Action", "ctrl-t", "a_function"); 47 48 % Show a listbox on Alt-y: 49 mc->editor_map_key_to_func("ListboxAction", "alt-y", "listbox_display_function"); -
src/Makefile.am
diff --git a/src/Makefile.am b/src/Makefile.am index e883ab4bd..3f8100352 100644
a b libinternal_la_SOURCES = \ 80 80 setup.c setup.h \ 81 81 textconf.c textconf.h \ 82 82 usermenu.c usermenu.h \ 83 util.c util.h 83 util.c util.h \ 84 slang_engine.c slang_engine.h \ 85 slang_api_functions.c \ 86 slang_api_functions_glue.c 84 87 85 88 if CHARSET 86 89 libinternal_la_SOURCES += selcodepage.c selcodepage.h -
src/editor/edit.c
diff --git a/src/editor/edit.c b/src/editor/edit.c index edda1f832..53b9c059c 100644
a b 42 42 #include <sys/stat.h> 43 43 #include <stdint.h> /* UINTMAX_MAX */ 44 44 #include <stdlib.h> 45 #include <slang.h> 45 46 46 47 #include "lib/global.h" 47 48 … … 65 66 66 67 #include "src/setup.h" /* option_tab_spacing */ 67 68 #include "src/keybind-defaults.h" 69 #include "src/slang_engine.h" 68 70 69 71 #include "edit-impl.h" 70 72 #include "editwidget.h" … … void 3248 3250 edit_execute_cmd (WEdit * edit, long command, int char_for_insertion) 3249 3251 { 3250 3252 Widget *w = WIDGET (edit); 3253 GSList *slang_code = NULL; 3254 /* Check if the command is a S-Lang script registered command */ 3255 if ((slang_code = get_command_callback (command)) != NULL) 3256 { 3257 int ret_api, ret_api2 = -1, ret_fun = 0; 3258 ret_api = SLang_execute_function (slang_code->data); 3259 if (ret_api == 0) 3260 message (D_ERROR, "S-Lang plugin error", "Function doesn't exist."); 3251 3261 3262 else if (ret_api < 0 || -1 == (ret_api2 = SLang_pop_int (&ret_fun))) 3263 { 3264 /* 3265 * If the function returned 1, then edit_execute_cmd() will continue, otherwise it will 3266 * exit. This feature can be used to tap into build in commands and moderate/replace 3267 * their execution. 3268 */ 3269 if (SLang_get_error ()) 3270 { 3271 SLang_restart (1); 3272 SLang_set_error (0); 3273 } 3274 } 3275 if (ret_api <= 0 || ret_api2 <= 0 || !ret_fun) 3276 return; 3277 } 3252 3278 if (command == CK_WindowFullscreen) 3253 3279 { 3254 3280 edit_toggle_fullscreen (edit); -
src/editor/editbuffer.c
diff --git a/src/editor/editbuffer.c b/src/editor/editbuffer.c index 101918bef..d3f78271f 100644
a b 33 33 34 34 #include <stdlib.h> 35 35 #include <string.h> 36 #include <ctype.h> 36 37 #include <sys/types.h> 37 38 38 39 #include "lib/global.h" … … edit_buffer_get_eol (const edit_buffer_t * buf, off_t current) 384 385 return current; 385 386 } 386 387 388 /* --------------------------------------------------------------------------------------------- */ 389 /** 390 * Find first character of current word. If jump_spaces is TRUE, then any whitespace gap on the 391 left is being ignored (i.e.: jumped across for the preceding word). */ 392 393 gboolean 394 edit_buffer_find_word_start (const edit_buffer_t * buf, gboolean jump_spaces, off_t * word_start, 395 gsize * word_len) 396 { 397 int c, spc_count = 0; 398 off_t i = 1; 399 gboolean have_ending_char = FALSE; 400 401 /* STARTUP: Initialize the output variables to a reasonable initial values. */ 402 *word_start = buf->curs1; 403 *word_len = (gsize) 0; 404 405 /* Return nothing if at begin of file. */ 406 if (buf->curs1 <= 0) 407 return FALSE; 408 409 /* A first peek at the first preceding char. */ 410 c = edit_buffer_get_previous_byte (buf); 411 /* Return if the word is empty (i.e.: the char is a space and we're not jumping over them). */ 412 if ((!jump_spaces && isspace (c)) || c == '\n') 413 return FALSE; 414 415 /* Skip any whitespace. */ 416 if (jump_spaces && isspace (c)) 417 { 418 spc_count++; 419 /* Jump over all whitespace on the left. Initial i++ means: a skip of the first space (move 420 * on to processing next char before it). */ 421 for (i++; buf->curs1 - i >= 0; i++) 422 { 423 c = edit_buffer_get_byte (buf, buf->curs1 - i); 424 if (!isspace (c)) 425 { 426 break; 427 } 428 else if (c == '\n') 429 /* Preliminary new line → return nothing. This function works in current line. */ 430 return FALSE; 431 spc_count++; 432 } 433 } 434 435 /* Whitespace till the beginning of the buffer? */ 436 if (isspace (c)) 437 return FALSE; 438 439 /* Accept this (either initial or one found after skipping spaces) char and move on, if 440 * there are any left chars in buffer. */ 441 i++; 442 443 /* 444 * Word boundary char right after optional spaces → accept only it. 445 * This means that a single word boundary char constitutes a word of length 1, either 446 * after spaces (skipped above ↑) or immediately before cursor. 447 */ 448 449 if (is_break_char (c)) 450 /* i variable points to the single char, skip iterating more. */ 451 have_ending_char = TRUE; 452 453 /* Search start of word to be completed. */ 454 for (; !have_ending_char && buf->curs1 - i >= 0; i++) 455 { 456 c = edit_buffer_get_byte (buf, buf->curs1 - i); 457 458 if (is_break_char (c)) 459 /* 460 * The isdigit() condition here was too specific for this general function (it was 461 * excluding any words starting with a digit). 462 */ 463 break; 464 } 465 466 /* Success – a word has been properly found and delimited. */ 467 *word_start = buf->curs1 - i + 1; /* Save start found to result variable */ 468 *word_len = (gsize) i - 1 - spc_count; /* …and word length */ 469 470 return TRUE; 471 } 472 473 /* --------------------------------------------------------------------------------------------- */ 474 475 /* Gets the word on the left of the cursor. 476 * 477 * @param buf The edit buffer. 478 * @param jump_spaces Should any whitespace gap be jumped over? If not, it'll cause empty result. 479 * @param initial Initial contents of the result. 480 * @param release_on_empty Should the initial g_string be released when returning NULL. 481 * @return g_string with the word or NULL if the word is empty. 482 */ 483 GString * 484 edit_buffer_get_left_whole_word (const edit_buffer_t * buf, gboolean jump_spaces, GString * initial, 485 gboolean release_on_empty) 486 { 487 GString *ret = initial; 488 gsize i, word_len = 0; 489 off_t word_start = 0; 490 491 /* Search start of word left of cursor. */ 492 if (!edit_buffer_find_word_start (buf, jump_spaces, &word_start, &word_len)) 493 { 494 if (initial && release_on_empty) 495 g_string_free (initial, TRUE); 496 return NULL; 497 } 498 499 /* ret = g_strdup_printf ("\\b%.*s[a-zA-Z_0-9]+", word_len, bufpos); */ 500 if (!ret) 501 ret = g_string_sized_new (32); 502 503 for (i = 0; i < word_len; i++) 504 g_string_append_c (ret, edit_buffer_get_byte (buf, word_start + i)); 505 506 return ret; 507 } 508 387 509 /* --------------------------------------------------------------------------------------------- */ 388 510 /** 389 511 * Get word from specified offset. -
src/editor/editbuffer.h
diff --git a/src/editor/editbuffer.h b/src/editor/editbuffer.h index 9d65e71eb..33dd8f548 100644
a b int edit_buffer_get_prev_utf (const edit_buffer_t * buf, off_t byte_index, int * 46 46 long edit_buffer_count_lines (const edit_buffer_t * buf, off_t first, off_t last); 47 47 off_t edit_buffer_get_bol (const edit_buffer_t * buf, off_t current); 48 48 off_t edit_buffer_get_eol (const edit_buffer_t * buf, off_t current); 49 gboolean edit_buffer_find_word_start (const edit_buffer_t * buf, gboolean jump_spaces, 50 off_t * word_start, gsize * word_len); 51 GString *edit_buffer_get_left_whole_word (const edit_buffer_t * buf, gboolean jump_spaces, 52 GString * initial, gboolean release_on_empty); 49 53 GString *edit_buffer_get_word_from_pos (const edit_buffer_t * buf, off_t start_pos, off_t * start, 50 54 gsize * cut); 51 55 -
src/editor/editcmd.c
diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c index 0d2caa923..6ec993e4d 100644
a b pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc) 1127 1127 } 1128 1128 } 1129 1129 1130 /* --------------------------------------------------------------------------------------------- */1131 /** find first character of current word */1132 1133 static gboolean1134 edit_find_word_start (const edit_buffer_t * buf, off_t * word_start, gsize * word_len)1135 {1136 int c;1137 off_t i;1138 1139 /* return if at begin of file */1140 if (buf->curs1 <= 0)1141 return FALSE;1142 1143 c = edit_buffer_get_previous_byte (buf);1144 /* return if not at end or in word */1145 if (is_break_char (c))1146 return FALSE;1147 1148 /* search start of word to be completed */1149 for (i = 1;; i++)1150 {1151 int last;1152 1153 last = c;1154 c = edit_buffer_get_byte (buf, buf->curs1 - i - 1);1155 1156 if (is_break_char (c))1157 {1158 /* return if word starts with digit */1159 if (isdigit (last))1160 return FALSE;1161 1162 break;1163 }1164 }1165 1166 /* success */1167 *word_start = buf->curs1 - i; /* start found */1168 *word_len = (gsize) i;1169 1170 return TRUE;1171 }1172 1173 1130 /* --------------------------------------------------------------------------------------------- */ 1174 1131 /** 1175 1132 * Get current word under cursor … … edit_complete_word_cmd (WEdit * edit) 3340 3297 GString *compl[MAX_WORD_COMPLETIONS]; /* completions */ 3341 3298 3342 3299 /* search start of word to be completed */ 3343 if (!edit_ find_word_start (&edit->buffer, &word_start, &word_len))3300 if (!edit_buffer_find_word_start (&edit->buffer, FALSE, &word_start, &word_len)) 3344 3301 return; 3345 3302 3346 3303 /* prepare match expression */ … … edit_get_match_keyword_cmd (WEdit * edit) 3508 3465 gsize word_len = 0, max_len = 0; 3509 3466 int num_def = 0; 3510 3467 gsize i; 3511 off_t word_start = 0;3512 3468 GString *match_expr; 3513 3469 char *path = NULL; 3514 3470 char *ptr = NULL; … … edit_get_match_keyword_cmd (WEdit * edit) 3519 3475 for (i = 0; i < MAX_DEFINITIONS; i++) 3520 3476 def_hash[i].filename = NULL; 3521 3477 3522 /* search start of word to be completed */3523 if (!edit_find_word_start (&edit->buffer, &word_start, &word_len))3524 return;3525 3526 3478 /* prepare match expression */ 3527 match_expr = g_string_sized_new (word_len); 3528 for (i = 0; i < word_len; i++) 3529 g_string_append_c (match_expr, edit_buffer_get_byte (&edit->buffer, word_start + i)); 3530 3479 match_expr = edit_buffer_get_left_whole_word (&edit->buffer, TRUE, NULL, FALSE); 3480 if (match_expr == NULL) 3481 return; 3531 3482 ptr = g_get_current_dir (); 3532 3483 path = g_strconcat (ptr, PATH_SEP_STR, (char *) NULL); 3533 3484 g_free (ptr); … … edit_get_match_keyword_cmd (WEdit * edit) 3554 3505 g_free (path); 3555 3506 3556 3507 max_len = MAX_WIDTH_DEF_DIALOG; 3557 word_len = 0;3558 3508 if (num_def > 0) 3559 3509 editcmd_dialog_select_definition_show (edit, match_expr->str, max_len, word_len, 3560 3510 (etags_hash_t *) & def_hash, num_def); -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..5f553a8cb 100644
a b 61 61 #include "src/filemanager/cmd.h" /* save_setup_cmd() */ 62 62 #include "src/learn.h" /* learn_keys() */ 63 63 #include "src/args.h" /* mcedit_arg_t */ 64 #include "src/slang_engine.h" 64 65 65 66 #include "edit-impl.h" 66 67 #include "editwidget.h" -
src/main.c
diff --git a/src/main.c b/src/main.c index d691bcb2a..dd8b0b7ea 100644
a b 62 62 #include "filemanager/ext.h" /* flush_extension_file() */ 63 63 #include "filemanager/command.h" /* cmdline */ 64 64 #include "filemanager/panel.h" /* panalized_panel */ 65 #include "editor/editwidget.h" 65 66 66 67 #include "vfs/plugins_init.h" 67 68 … … 77 78 #include "selcodepage.h" 78 79 #endif /* HAVE_CHARSET */ 79 80 81 #include "slang_engine.h" 80 82 #include "consaver/cons.saver.h" /* cons_saver_pid */ 81 83 82 84 /*** global variables ****************************************************************************/ … … main (int argc, char *argv[]) 456 458 } 457 459 } 458 460 461 /* Initialize the S-Lang interpreter and load `init.sl` file. */ 462 slang_init_engine (); 463 464 /* 465 * Load the plugins before the setup so that the commands that are being 466 * created in them can be bound with a key. 467 */ 468 slang_plugins_init (); 469 459 470 /* Program main loop */ 460 471 if (mc_global.midnight_shutdown) 461 472 exit_code = EXIT_SUCCESS; -
src/setup.c
diff --git a/src/setup.c b/src/setup.c index 77c07649d..fce4f18a9 100644
a b load_keymap_from_section (const char *section_name, GArray * keymap, mc_config_t 784 784 long action; 785 785 786 786 action = keybind_lookup_action (*profile_keys); 787 788 /* 789 * Dynamically created commands – registered on demand. If the command doesn't exist, 790 * meaning that it isn't a compile-time, original command, then extend the internal 791 * data structures with a new command entry, assigning it an unique CK id. Such command 792 * can then be legally mapped to a key. 793 */ 794 if (action <= 0) 795 { 796 int added; 797 added = keybind_add_new_action (*profile_keys, new_dynamic_command_id); 798 action = added ? new_dynamic_command_id : 0; /* check if CK ID was assigned */ 799 new_dynamic_command_id += added; /* if yes, increment the root CK ID variable */ 800 } 801 802 /* Do the binding if the command has been found (i.e.: its CK ID is known). */ 787 803 if (action > 0) 788 804 { 789 805 gchar **curr_values; 790 806 791 807 for (curr_values = values; *curr_values != NULL; curr_values++) 792 keybind_cmd_bind (keymap, *curr_values, action );808 keybind_cmd_bind (keymap, *curr_values, action, ORIGIN_FILE); 793 809 } 794 810 795 811 g_strfreev (values); -
new file src/slang_api_functions.c
diff --git a/src/slang_api_functions.c b/src/slang_api_functions.c new file mode 100644 index 000000000..be961d969
- + 1 /* 2 Implementation of functions supplied to S-Lang interpreter. 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file slang_api_functions.c 27 * \brief Implementation of functions exported/added to S-Lang interpreter. 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 * 31 * Here are functions implemented that are automatically exported to S-Lang interpreter by Slirp 32 * utility. They connect the interpreter with mc process and allow to direct it to perform some 33 * tasks like getting a word from current buffer, moving a cursor, etc. 34 */ 35 36 37 #include <config.h> 38 39 #include "lib/global.h" 40 #include "lib/widget.h" 41 #include "lib/util.h" 42 43 #include "src/slang_engine.h" 44 #include "src/slang_api_functions.h" 45 #include "src/keybind-defaults.h" 46 #include "src/editor/editwidget.h" 47 48 /*** global variables ****************************************************************************/ 49 50 /*** file scope macro definitions ****************************************************************/ 51 52 /*** file scope type declarations ****************************************************************/ 53 54 /*** file scope variables ************************************************************************/ 55 56 /*** file scope functions ************************************************************************/ 57 /* --------------------------------------------------------------------------------------------- */ 58 59 /* Gets current editor object. */ 60 static WEdit * 61 get_cure (void) 62 { 63 GList *dialog = top_dlg; 64 65 /* Search first fullscreen dialog */ 66 for (; dialog != NULL; dialog = g_list_next (dialog)) 67 if ((WIDGET (dialog->data)->pos_flags & WPOS_FULLSCREEN) != 0) 68 break; 69 if (dialog) 70 return (WEdit *) (GROUP (dialog->data)->current->data); 71 else 72 return NULL; 73 } 74 75 /* --------------------------------------------------------------------------------------------- */ 76 77 /* 78 * Updates possibly reallocated keymap pointer in all open editors. 79 */ 80 81 static gboolean 82 update_editor_keymaps (global_keymap_t * keymap) 83 { 84 WEdit *e; 85 WGroup *g; 86 gboolean ret = FALSE; 87 88 e = get_cure (); 89 if (e == NULL) 90 return ret; 91 g = GROUP (WIDGET (e)->owner); 92 if (g == NULL) 93 return ret; 94 95 editor_map = keymap; 96 for (GList * it = g->widgets; it != NULL; it = g_list_next (it)) 97 { 98 if (edit_widget_is_editor (WIDGET (it->data))) 99 { 100 WIDGET (it->data)->keymap = keymap; 101 ret = TRUE; 102 } 103 } 104 105 return ret; 106 } 107 108 /* --------------------------------------------------------------------------------------------- */ 109 /*** public functions ****************************************************************************/ 110 /* --------------------------------------------------------------------------------------------- */ 111 112 /* 113 * S-LANG FUNCTION: _cure_cursor_move(offset) 114 */ 115 116 void 117 slang_api__cure_cursor_move (int offset) 118 { 119 edit_cursor_move (get_cure (), offset); 120 } 121 122 /* --------------------------------------------------------------------------------------------- */ 123 124 /* 125 * S-LANG FUNCTION: cure_cursor_offset() 126 * RETURN VALUE: The offset in bytes of the current cursor position 127 */ 128 129 int 130 slang_api__cure_cursor_offset (void) 131 { 132 return get_cure ()->buffer.curs1; 133 } 134 135 /* --------------------------------------------------------------------------------------------- */ 136 137 /* 138 * S-LANG FUNCTION: cure_get_eol(byte_idx) 139 * RETURN VALUE: the byte offset of the end of the current line 140 */ 141 142 int 143 slang_api__cure_get_eol (void) 144 { 145 return edit_buffer_get_current_eol (&get_cure ()->buffer); 146 } 147 148 /* --------------------------------------------------------------------------------------------- */ 149 150 /* 151 * S-LANG FUNCTION: cure_get_bol(byte_idx) 152 * RETURN VALUE: the byte offset of the beginning of the current line 153 */ 154 155 int 156 slang_api__cure_get_bol (void) 157 { 158 return edit_buffer_get_current_bol (&get_cure ()->buffer); 159 } 160 161 /* --------------------------------------------------------------------------------------------- */ 162 163 /* 164 * S-LANG FUNCTION: cure_get_byte(byte_idx) 165 * RETURN VALUE: the byte at the given index in the currently open file 166 */ 167 168 int 169 slang_api__cure_get_byte (int byte_idx) 170 { 171 return edit_buffer_get_byte (&get_cure ()->buffer, byte_idx); 172 } 173 174 /* --------------------------------------------------------------------------------------------- */ 175 176 /* 177 * S-LANG FUNCTION: cure_get_left_whole_word(byte_idx) 178 * RETURN VALUE: word the byte at the given index in the currently open file 179 */ 180 181 char * 182 slang_api__cure_get_left_whole_word (int skip_space) 183 { 184 GString *res_gstr; 185 char *res; 186 res_gstr = edit_buffer_get_left_whole_word (&get_cure ()->buffer, skip_space, NULL, FALSE); 187 188 /* Ensure a non-null result. */ 189 if (!res_gstr) 190 res_gstr = g_string_new (""); 191 192 /* Return inner buffer of GString. */ 193 res = res_gstr->str; 194 g_string_free (res_gstr, FALSE); 195 196 return res; 197 } 198 199 /* --------------------------------------------------------------------------------------------- */ 200 201 /* 202 * S-LANG FUNCTION: cure_delete() 203 * RETURN VALUE: The code of the deleted character 204 */ 205 206 int 207 slang_api__cure_delete (void) 208 { 209 return edit_buffer_delete (&get_cure ()->buffer); 210 } 211 212 /* --------------------------------------------------------------------------------------------- */ 213 214 /* 215 * S-LANG FUNCTION: cure_backspace() 216 * RETURN VALUE: The code of the deleted character 217 */ 218 219 int 220 slang_api__cure_backspace (void) 221 { 222 return edit_buffer_backspace (&get_cure ()->buffer); 223 } 224 225 /* --------------------------------------------------------------------------------------------- */ 226 227 /* 228 * S-LANG FUNCTION: cure_insert_ahead() 229 */ 230 231 void 232 slang_api__cure_insert_ahead (int c) 233 { 234 edit_buffer_insert_ahead (&get_cure ()->buffer, c); 235 } 236 237 /* --------------------------------------------------------------------------------------------- */ 238 239 /* 240 * S-LANG FUNCTION: listbox( width, height, [ items…, NULL ], help_anchor ) 241 * RETURN VALUE: the index of the selected element or -1 if cancelled. 242 */ 243 244 /* A S-Lang script function which allows to display a centered dialog with a listbox 245 in it and then get the index of the selected item, if any. */ 246 int 247 slang_api__listbox (int h, int w, char *title, char **items, unsigned long size) 248 { 249 Listbox *listb; 250 char **cur_item; 251 int selected; 252 unsigned long i = 0; 253 254 /* Use an utility function to create the dialog with the listbox in it. */ 255 listb = create_listbox_window (h, w, title, ""); 256 257 /* Add the requested elements to the listbox. */ 258 for (cur_item = items; i < size && *cur_item != NULL; i++, cur_item++) 259 { 260 listbox_add_item (listb->list, LISTBOX_APPEND_AT_END, 's', *cur_item, NULL, FALSE); 261 } 262 263 /* Run the dialog and get and then return the index of the selected item. */ 264 selected = run_listbox (listb); 265 return selected; 266 } 267 268 /* --------------------------------------------------------------------------------------------- */ 269 270 /* 271 * S-LANG FUNCTION: listbox_with_data( width, height, [ items…, NULL ], [ data…, NULL ], help_anchor ) 272 * RETURN VALUE: the associated data of the selected element or NULL if cancelled. 273 */ 274 275 /* A S-Lang script function which allows to display a centered dialog with a listbox 276 in it and then get the associated data of the selected item, if any (otherwise, 277 i.e.: if canceled, it returns "". The data has to be an array of char* strings 278 (ended with NULL). */ 279 char * 280 slang_api__listbox_with_data (int h, int w, char *title, char **items, unsigned long size, 281 char **data, unsigned long size2) 282 { 283 Listbox *listb; 284 char **cur_item, **cur_data = data; 285 char *selected; 286 unsigned long p, q = 0; 287 288 /* Use an utility function to create the dialog with the listbox in it. */ 289 listb = create_listbox_window (h, w, title, ""); 290 291 /* Add the requested elements to the listbox, also passing the associated data. */ 292 for (p = 0, cur_item = items; p < size && *cur_item != NULL; p++, cur_item++) 293 { 294 listbox_add_item (listb->list, LISTBOX_APPEND_AT_END, 's', *cur_item, *cur_data, FALSE); 295 296 /* Advance the item data pointer, respecting its size. */ 297 if (q < size2) 298 { 299 q++; 300 cur_data++; 301 } 302 } 303 304 /* Run the dialog and get and then return the associated data of the selected 305 item, duplicating its allocation (for S-Lang scripting host memory 306 management). The data has to be a char* string. */ 307 selected = run_listbox_with_data (listb, NULL); 308 return selected ? g_strdup (selected) : NULL; //g_strdup(""); 309 } 310 311 /* --------------------------------------------------------------------------------------------- */ 312 313 /* 314 * S-LANG FUNCTION: listbox_auto(title, [items…, NULL]) 315 * RETURN VALUE: the index of the selected item or -1 if cancelled. 316 */ 317 318 /* 319 * Shows a listbox with the height and width automatically adapted for the 320 * contents and the title. 321 */ 322 int 323 slang_api__listbox_auto (char *title, char **items, unsigned long size) 324 { 325 return slang_api__listbox (5, 5, title, items, size); 326 } 327 328 /* --------------------------------------------------------------------------------------------- */ 329 330 /* 331 * S-LANG FUNCTION: message(title, body) 332 */ 333 334 /* Shows a message popup with the given title and body. */ 335 void 336 slang_api__message (const char *title, const char *body) 337 { 338 message (D_NORMAL, title, "%s", body); 339 } 340 341 /* --------------------------------------------------------------------------------------------- */ 342 343 /* 344 * S-LANG FUNCTION: set_action_hook(command, function_name, user_data) 345 * RETURN VALUE: The number of the registered callbacks after the addition 346 */ 347 348 int 349 slang_api__set_action_hook (const char *command, const char *function_name, const char *user_data) 350 { 351 int value, ins_ret; 352 GSList *new_element = NULL; 353 354 /* Get the integer value code for the command name `command`. */ 355 value = keybind_lookup_action (command); 356 357 new_element = g_slist_append (new_element, (char *) function_name); 358 new_element = g_slist_append (new_element, (char *) user_data); 359 360 /* Insert the `function_name` into the hash table. */ 361 ins_ret = g_hash_table_insert (action_hook_functions, GINT_TO_POINTER (value), new_element); 362 363 /* Increase the element count accordingly. */ 364 num_action_hook_functions += ins_ret; 365 return num_action_hook_functions; 366 } 367 368 /* --------------------------------------------------------------------------------------------- */ 369 370 /* 371 * S-LANG FUNCTION: editor_map_key(key_combination, command_name) 372 * RETURN VALUE: 1 or 0 indicating if the binding was properly added 373 */ 374 int 375 slang_api__editor_map_key_to_action (const char *command_name, const char *key_combination) 376 { 377 int value; 378 value = keybind_lookup_action (command_name); 379 if (value == CK_IgnoreKey) 380 /* No such command found - return 0. */ 381 return FALSE; 382 383 /* Overriding the bindings coming from a keymap *file* is not allowed for S-Lang plugins. */ 384 if (keybind_lookup_keymap_origin (editor_map, value) != ORIGIN_FILE) 385 { 386 keybind_cmd_bind (editor_keymap, key_combination, value, ORIGIN_SLANG_SCRIPT); 387 /* Update editors' keymap pointers to the GArray's data in case it is reallocated. */ 388 update_editor_keymaps ((global_keymap_t *) editor_keymap->data); 389 } 390 else 391 { 392 /* Returning -1 will mean that action exists, but its key mapping cannot be altered. */ 393 return -TRUE; 394 } 395 return TRUE; 396 } 397 398 /* --------------------------------------------------------------------------------------------- */ 399 400 /* 401 * S-LANG FUNCTION: editor_map_key_to_func(new_command_name, key_combination, function_name) 402 * RETURN VALUE: 1 403 * 404 * This is a convenience function that equals to calling the following 3 function 405 * mc->add_new_action("CustomCommand", <some-int-ID>); 406 * mc->set_action_hook("CustomCommand", "a_slang_function_name"); 407 * mc->editor_map_key("CustomCommand", "alt-y"); 408 * 409 * with the only difference that the ID of the command is chosen automatically. 410 */ 411 int 412 slang_api__editor_map_key_to_func (const char *new_command_name, 413 const char *key_combination, const char *function_name) 414 { 415 int ret; 416 keybind_add_new_action (new_command_name, new_dynamic_command_id++); 417 slang_api__set_action_hook (new_command_name, function_name, NULL); 418 ret = slang_api__editor_map_key_to_action (new_command_name, key_combination); 419 420 return ret; 421 } 422 423 /* --------------------------------------------------------------------------------------------- */ -
new file src/slang_api_functions.h
diff --git a/src/slang_api_functions.h b/src/slang_api_functions.h new file mode 100644 index 000000000..2c29a3448
- + 1 #ifndef MC__SLANG_API_FUNCTIONS_H 2 #define MC__SLANG_API_FUNCTIONS_H 3 4 /*** typedefs(not structures) and defined constants **********************************************/ 5 6 /*** enums ***************************************************************************************/ 7 8 /*** structures declarations (and typedefs of structures)*****************************************/ 9 10 /*** global variables defined in .c file *********************************************************/ 11 12 /*** declarations of public functions ************************************************************/ 13 14 /* Movement functions */ 15 void slang_api__cure_cursor_move (int offset); 16 17 /* Getting offsets */ 18 int slang_api__cure_cursor_offset (void); 19 int slang_api__cure_get_eol (void); 20 int slang_api__cure_get_bol (void); 21 22 /* Getting data from buffer */ 23 char *slang_api__cure_get_left_whole_word (int skip_space); 24 int slang_api__cure_get_byte (int byte_idx); 25 26 /* Editing functions */ 27 int slang_api__cure_delete (void); 28 int slang_api__cure_backspace (void); 29 void slang_api__cure_insert_ahead (int c); 30 31 /* Dialog functions */ 32 int slang_api__listbox (int h, int w, char *title, char **items, unsigned long size); 33 char *slang_api__listbox_with_data (int h, int w, char *title, char **items, unsigned long size, 34 char **data, unsigned long size2); 35 int slang_api__listbox_auto (char *title, char **items, unsigned long size); 36 void slang_api__message (const char *title, const char *body); 37 38 /* Action hooks */ 39 int slang_api__set_action_hook (const char *command, const char *function_name, 40 const char *user_data); 41 #ifndef MC__KEYBIND_H 42 int keybind_add_new_action (const char *new_command_name, int new_ck_id); 43 #endif 44 45 /* Key bindings */ 46 int slang_api__editor_map_key_to_action (const char *key_combination, const char *command_name); 47 int slang_api__editor_map_key_to_func (const char *new_command_name, 48 const char *key_combination, const char *function_name); 49 50 /*** inline functions ****************************************************************************/ 51 52 #endif -
new file src/slang_api_functions_glue.c
diff --git a/src/slang_api_functions_glue.c b/src/slang_api_functions_glue.c new file mode 100644 index 000000000..ca71c281d
- + 1 #define HAVE_LONG_LONG 1 2 /* * This file was generated by SLIRP, the (Sl)ang (I)nte(r)face (P)ackage, 3 * a S-Lang module code generator for C, C++, and FORTRAN. 4 * 5 * Copyright (C) 2003-2009 Massachusetts Institute of Technology 6 * Copyright (C) 2002 Michael S. Noble <mnoble@space.mit.edu> 7 * 8 * SLIRP is free software, and may be used under the conditions stipulated 9 * in the COPYRIGHT agreement bundled within the SLIRP distribution. 10 */ 11 #include <stdlib.h> /* SLIRP common {{{ */ 12 #include <unistd.h> 13 #include <string.h> 14 #include <stdarg.h> 15 #include <ctype.h> 16 #include <slang.h> 17 18 #define SAFE_DEREF_OPAQUE(po) (po == NULL ? NULL : po->instance) 19 #define SAFE_DEREF_ARRAY(pa) (pa == NULL ? NULL : pa->data) 20 #define SLang_pop_array(x) SLang_pop_array(x, 1) 21 #define SLang_pop_string(x) ( (SLang_peek_at_stack() == SLANG_NULL_TYPE && (SLdo_pop() || 1)) ? ((*x = NULL) || 1): SLang_pop_slstring(x)) 22 #define SLang_push_size_t SLang_push_ulong 23 #define SLang_push_ptrdiff_t SLang_push_long 24 25 #define pop_defaultable(argno, kind, type, obj, value) \ 26 (SLang_Num_Function_Args >= argno ? SLang_pop_##kind ( (type*)&obj) : ((obj = value) || ( (void*)&obj > (void*)0) ) ) 27 28 #ifdef __cplusplus 29 #define LINKAGE "C" 30 #else 31 #define LINKAGE 32 #endif 33 34 #define USAGE(msg) \ 35 {SLang_verror(SL_USAGE_ERROR, (char*)"Usage: %s", msg); return;} 36 37 static void Slirp_usage(int i, int j, int flags); 38 39 static int slang_abi_mismatch(void) 40 { 41 long module_abi = SLANG_VERSION / 10000; 42 long app_abi = SLang_Version / 10000; 43 44 if (module_abi != app_abi) { 45 SLang_verror(SL_APPLICATION_ERROR, (char*) 46 "S-Lang library abi mismatch\nmodule: %s, application: %s", 47 SLANG_VERSION_STRING, SLang_Version_String); 48 return 1; 49 } 50 51 return 0; 52 } /* }}} */ 53 54 static char *slns; /* slang namespace active at time of module load */ 55 #include "slang_api_functions.h" 56 #define BEGIN_DECLS 57 #define END_DECLS 58 static unsigned char map_scalars_to_refs = 0; 59 60 typedef struct _Slirp_Ref { /* Ref handling code {{{ */ 61 #define REF_FLAG_FREE_DATA 0x01 62 #define REF_FLAG_IS_OPAQUE 0x02 63 #define REF_FLAG_COLUMN_MAJOR 0x04 64 #define REF_FLAG_ARRAY_EXPECTED 0x08 65 unsigned int flags; /* Slirp_Ref encapsulates S-Lang */ 66 SLtype sltype; /* array, ref, and MMT types, the */ 67 size_t sizeof_type; /* latter two of which are seen as */ 68 void **data; /* pointing to a single object */ 69 unsigned int vstride; /* how to find "next" vectored elem */ 70 SLang_Array_Type *array; 71 SLang_Ref_Type *ref; 72 SLang_MMT_Type *mmt; /* mmt supports passing around C */ 73 } Slirp_Ref; /* ptr arrays of indeterminate size */ 74 75 static Slirp_Ref* ref_new(SLtype t,size_t typesize, void *d,unsigned int flags) 76 { 77 Slirp_Ref *ref; 78 if ((ref = (Slirp_Ref*)SLcalloc( sizeof(Slirp_Ref), 1)) != NULL) { 79 ref->sltype = t; 80 ref->sizeof_type = typesize; 81 ref->flags = flags; 82 ref->data = (void**)d; 83 *ref->data = NULL; 84 } 85 return ref; 86 } 87 88 static int ref_finalize(Slirp_Ref *r) /* {{{ */ 89 { 90 int status = 0; 91 if (r == NULL) return 0; 92 93 if (r->ref) { 94 95 void *ref_value = NULL; SLtype ref_type = 0; double dc[2]; 96 97 if (r->flags & REF_FLAG_IS_OPAQUE) { /* wrap aggregates/opaques */ 98 #ifdef NUM_RESERVED_OPAQUES /* in mmt before ref assign */ 99 void *opaqval = *r->data; /* the mmt w/be freed when */ 100 if (opaqval == NULL) { /* the S-Lang object goes */ 101 ref_value = NULL; /* out of scope */ 102 ref_type = SLANG_NULL_TYPE; 103 } 104 else { 105 SLang_MMT_Type *mmt = create_opaque_mmt(r->sltype, opaqval, 0); 106 ref_value = &mmt; 107 ref_type = r->sltype; 108 } 109 #endif 110 } 111 else { 112 ref_type = r->sltype; 113 if (ref_type == SLANG_COMPLEX_TYPE && r->sizeof_type < sizeof(dc)) { 114 float *fc = (float*) r->data; 115 dc[0] = fc[0]; 116 dc[1] = fc[1]; 117 ref_value = dc; 118 } 119 else 120 ref_value = r->data; 121 } 122 123 status = SLang_assign_to_ref (r->ref, ref_type, ref_value); 124 SLang_free_ref(r->ref); 125 } 126 else if (r->array) { 127 #ifdef HAVE_FORTRAN_CODE 128 if ((r->flags & REF_FLAG_COLUMN_MAJOR) && TRANSPOSE(1,r->array) == -1) 129 return -1; 130 #endif 131 SLang_free_array(r->array); 132 } 133 else if (r->mmt) 134 SLang_free_mmt(r->mmt); 135 136 if (r->flags & REF_FLAG_FREE_DATA) 137 SLfree( (char*) r->data ); 138 139 SLfree((char*)r); 140 return status; 141 } /* }}} */ 142 143 static void finalize_refs(unsigned int nargs, ...) /* {{{ */ 144 { 145 va_list ap; 146 va_start(ap, nargs); 147 while (nargs--) ref_finalize(va_arg(ap, Slirp_Ref *)); 148 va_end(ap); 149 } /* }}} */ 150 151 static unsigned int ref_get_size(Slirp_Ref *r, int which_dimension) /* {{{ */ 152 { 153 if (r->array) { 154 if (which_dimension == 0) 155 return (unsigned int)r->array->num_elements; 156 else if (which_dimension < 0) 157 return r->array->num_dims; 158 else { 159 which_dimension--; 160 if ((unsigned int)which_dimension < r->array->num_dims) 161 return (unsigned int)r->array->dims[which_dimension]; 162 else 163 return 0; 164 } 165 } 166 167 return 1; 168 } /* }}} */ 169 170 extern LINKAGE int _SLang_get_class_type (SLtype t); /* quasi-public */ 171 172 #ifdef NUM_RESERVED_OPAQUES 173 static SLtype sltype_to_opaque_ptr_type(SLtype sltype) /*{{{*/ 174 { 175 Reserved_Opaque_Type *pt; 176 177 if (sltype > Last_Reserved_Opaque_Type) return opaque_ptr_Type; 178 if (sltype == void_ptr_Type) return void_ptr_Type; 179 180 pt = Reserved_Opaque_Types; /* sequential search, but s/b < O(n) */ 181 while (pt->name) { /* since list is ordered by expected */ 182 if (pt->masked_type == sltype) /* frequency of use for each SLtype */ 183 return *pt->type; 184 pt++; 185 } 186 return 0; 187 } /*}}}*/ 188 #endif 189 190 static int try_pop_mmt(SLtype type, SLang_MMT_Type **mmt) /*{{{*/ 191 { 192 static SLang_Name_Type *cl_type_func; /* SLang_pop_mmt doesn't */ 193 int classtype; /* validate that type is */ 194 /* an MMT, so we do here */ 195 *mmt = NULL; /* FIXME: remove v2.0.7 */ 196 197 if (cl_type_func == NULL) 198 cl_type_func = SLang_get_function( (char*) "__class_type"); 199 200 if (cl_type_func == NULL) 201 return 0; 202 203 if (-1 == SLang_push_datatype(type) || /* do the hard way, */ 204 -1 == SLexecute_function(cl_type_func) || /* as C api lacks */ 205 -1 == SLang_pop_int(&classtype)) /* get_class_type() */ 206 return -1; 207 208 if (classtype == SLANG_CLASS_TYPE_MMT) { 209 *mmt = SLang_pop_mmt(type); 210 return 1; 211 } 212 return 0; 213 } /*}}}*/ 214 215 #define POP_FLAG_NULLABLE 0x1 216 #define POP_FLAG_VECTORIZE 0x2 217 static int pop_array_or_ref(Slirp_Ref *r, int flags, int defaultable) /*{{{*/ 218 { 219 SLtype type; 220 unsigned int i, objtype; 221 #ifdef NUM_RESERVED_OPAQUES 222 unsigned int is_opaque; 223 #endif 224 225 if (r == NULL) { 226 SLang_verror(SL_INTRINSIC_ERROR, (char*)"Attempted NULL reference (out of memory?)"); 227 return -1; 228 } 229 230 if (defaultable && SLang_Num_Function_Args < defaultable) { 231 r->ref = NULL; /* observe that only NULL can be */ 232 *r->data = NULL; /* assigned as the default value */ 233 return 0; 234 } 235 236 objtype = SLang_peek_at_stack(); 237 238 if ((flags & POP_FLAG_NULLABLE) && objtype == SLANG_NULL_TYPE) { 239 r->ref = NULL; /* nullable flag: a pointer arg for */ 240 *r->data = NULL; /* which NULL is a legitimate value */ 241 return SLang_pop_null (); 242 } 243 244 type = r->sltype; 245 246 #ifdef NUM_RESERVED_OPAQUES 247 is_opaque =(type >= First_Opaque_Type && sltype_to_slirp_type(type) != NULL); 248 if (is_opaque) r->flags |= REF_FLAG_IS_OPAQUE; 249 #endif 250 251 switch(objtype) { 252 253 case SLANG_ARRAY_TYPE: 254 255 if (SLang_pop_array_of_type(&r->array, type) == -1) 256 return -1; 257 258 #ifdef HAVE_FORTRAN_CODE 259 if (r->flags & REF_FLAG_COLUMN_MAJOR) { 260 if (flags & POP_FLAG_VECTORIZE) /* vectorizable arrs*/ 261 r->flags ^= REF_FLAG_COLUMN_MAJOR; /* r not transposed */ 262 else if (TRANSPOSE(0,r->array) == -1) 263 return -1; 264 } 265 #endif 266 267 i = r->array->num_elements; 268 #ifdef NUM_RESERVED_OPAQUES 269 if (is_opaque) { 270 Slirp_Opaque *ot; 271 SLang_MMT_Type** mmts = (SLang_MMT_Type**)r->array->data; 272 void **arr = (void**)SLmalloc(i * sizeof(void*) ); 273 if (arr == NULL) return -1; 274 275 while (i--) { 276 ot = (Slirp_Opaque*) SLang_object_from_mmt (mmts[i]); 277 if (ot == NULL) { 278 SLfree((char*)arr); 279 return -1; 280 } 281 arr[i] = ot->instance; 282 } 283 284 *r->data = (void*)arr; r->data = (void**)arr; 285 r->flags |= REF_FLAG_FREE_DATA; 286 } 287 else 288 #endif 289 if (type == SLANG_COMPLEX_TYPE && 290 r->sizeof_type < r->array->sizeof_type) { 291 double *dc = (double*) r->array->data; 292 float *fc = (float *) SLmalloc(i * r->sizeof_type); 293 if (fc == NULL) return -1; 294 *r->data = fc; r->data = (void**)fc; 295 while (i--) { *fc++ = (float) *dc++; *fc++ = (float) *dc++; } 296 r->flags |= REF_FLAG_FREE_DATA; 297 } 298 else { 299 *r->data = r->array->data; 300 r->data = (void**)*r->data; 301 } 302 303 break; 304 305 case SLANG_REF_TYPE: 306 307 /* Refs can only send values one-way (C to S-Lang, not reverse) */ 308 if (SLang_pop_ref(&r->ref) == -1) 309 return -1; 310 311 /* Ref is assumed to point to a scalar instance of the */ 312 /* refd type, so declare enough space to hold one such. */ 313 *r->data = (void*)SLmalloc(r->sizeof_type); 314 if (*r->data == NULL) return -1; 315 memset(*r->data, 0, r->sizeof_type); 316 r->flags |= REF_FLAG_FREE_DATA; 317 r->data = (void**)*r->data; 318 break; 319 320 /* Allow scalars to used as if they were 1-element arrays */ 321 case SLANG_CHAR_TYPE: case SLANG_UCHAR_TYPE: 322 case SLANG_SHORT_TYPE: case SLANG_USHORT_TYPE: 323 case SLANG_INT_TYPE: case SLANG_UINT_TYPE: 324 case SLANG_LONG_TYPE: case SLANG_ULONG_TYPE: 325 case SLANG_FLOAT_TYPE: case SLANG_DOUBLE_TYPE: 326 case SLANG_COMPLEX_TYPE: case SLANG_STRING_TYPE: 327 328 /* Accomodate FORTRAN-style pass by reference semantics */ 329 if (map_scalars_to_refs && 330 SLang_pop_array_of_type(&r->array,type) == 0) { 331 332 *r->data = (void*)SLmalloc(r->sizeof_type); 333 if (*r->data == NULL) return -1; 334 335 if (r->sizeof_type == r->array->sizeof_type) 336 memcpy(*r->data, r->array->data, r->sizeof_type); 337 else if (type == SLANG_COMPLEX_TYPE) { 338 double *dc = (double*) r->array->data; 339 float *fc = (float*) *r->data; 340 fc[0] = (float)dc[0]; 341 fc[1] = (float)dc[1]; 342 } 343 else { 344 SLang_verror(SL_TYPE_MISMATCH, (char*) 345 "mismatched type sizes, when popping scalar as ref"); 346 SLang_free_array(r->array); 347 return -1; 348 } 349 350 r->data = (void**)*r->data; 351 r->flags |= REF_FLAG_FREE_DATA; 352 /* Nullify to distinguish between vectored/non-vectored args */ 353 SLang_free_array(r->array); r->array = NULL; 354 break; 355 } /* intentional fallthrough */ 356 357 default: 358 359 #ifdef NUM_RESERVED_OPAQUES 360 if (objtype >= First_Opaque_Type && 361 sltype_to_slirp_type(objtype) != NULL) { 362 363 if (!(flags & POP_FLAG_VECTORIZE)) 364 type = sltype_to_opaque_ptr_type(type); 365 366 if (type) { 367 368 Slirp_Opaque *otp; 369 if (SLang_pop_opaque(type, NULL, &otp) == -1) 370 return -1; 371 372 if (flags & POP_FLAG_VECTORIZE) { 373 void **arr = (void**) SLmalloc(sizeof(void*)); 374 if (arr == NULL) return -1; 375 arr[0] = otp->instance; 376 *r->data = arr; 377 r->flags |= REF_FLAG_FREE_DATA; 378 } 379 else 380 *r->data = otp->instance; 381 382 r->data = (void**)*r->data; 383 r->mmt = otp->mmt; 384 return 0; 385 } 386 } 387 else 388 #endif 389 if ( try_pop_mmt(objtype, &r->mmt) == 1 && 390 (*r->data = SLang_object_from_mmt (r->mmt)) != NULL) { 391 r->data = (void**)*r->data; /* not flagged for freeing */ 392 return 0; 393 } 394 395 SLang_verror(SL_TYPE_MISMATCH, (char*) 396 "context requires array, ref, or opaque pointer"); 397 return -1; 398 } 399 return 0; 400 } /*}}}*/ 401 /* }}} */ 402 403 /* Wrapper functions */ /* {{{ */ 404 static void sl_set_action_hook (void) 405 { 406 int retval; 407 char* arg1; 408 char* arg2; 409 char* arg3; 410 int issue_usage = 1; 411 412 if (SLang_Num_Function_Args != 3) goto usage_label; 413 if (-1 == SLang_pop_string((char**)&arg3)) goto usage_label; 414 if (-1 == SLang_pop_string((char**)&arg2)) goto free_and_return_3; 415 if (-1 == SLang_pop_string((char**)&arg1)) goto free_and_return_2; 416 issue_usage = 0; 417 418 retval = slang_api__set_action_hook(arg1, arg2, arg3); 419 (void)SLang_push_int(retval); 420 goto free_and_return; 421 free_and_return: 422 /* drop */ 423 SLang_free_slstring(arg1); 424 free_and_return_2: 425 SLang_free_slstring(arg2); 426 free_and_return_3: 427 SLang_free_slstring(arg3); 428 usage_label: 429 if (issue_usage) Slirp_usage (0, 0, 0); 430 } 431 432 static void sl_cure_backspace (void) 433 { 434 int retval; 435 int issue_usage = 1; 436 437 if (SLang_Num_Function_Args != 0) goto usage_label; 438 issue_usage = 0; 439 440 retval = slang_api__cure_backspace(); 441 (void)SLang_push_int(retval); 442 goto free_and_return; 443 free_and_return: 444 /* drop */ 445 usage_label: 446 if (issue_usage) Slirp_usage (1, 1, 0); 447 } 448 449 static void sl_add_new_action (void) 450 { 451 int retval; 452 char* arg1; 453 int arg2; 454 int issue_usage = 1; 455 456 if (SLang_Num_Function_Args != 2) goto usage_label; 457 if (-1 == SLang_pop_int((int*)&arg2)) goto usage_label; 458 if (-1 == SLang_pop_string((char**)&arg1)) goto usage_label; 459 issue_usage = 0; 460 461 retval = keybind_add_new_action(arg1, arg2); 462 (void)SLang_push_int(retval); 463 goto free_and_return; 464 free_and_return: 465 /* drop */ 466 SLang_free_slstring(arg1); 467 usage_label: 468 if (issue_usage) Slirp_usage (2, 2, 0); 469 } 470 471 static void sl_editor_map_key_to_func (void) 472 { 473 int retval; 474 char* arg1; 475 char* arg2; 476 char* arg3; 477 int issue_usage = 1; 478 479 if (SLang_Num_Function_Args != 3) goto usage_label; 480 if (-1 == SLang_pop_string((char**)&arg3)) goto usage_label; 481 if (-1 == SLang_pop_string((char**)&arg2)) goto free_and_return_3; 482 if (-1 == SLang_pop_string((char**)&arg1)) goto free_and_return_2; 483 issue_usage = 0; 484 485 retval = slang_api__editor_map_key_to_func(arg1, arg2, arg3); 486 (void)SLang_push_int(retval); 487 goto free_and_return; 488 free_and_return: 489 /* drop */ 490 SLang_free_slstring(arg1); 491 free_and_return_2: 492 SLang_free_slstring(arg2); 493 free_and_return_3: 494 SLang_free_slstring(arg3); 495 usage_label: 496 if (issue_usage) Slirp_usage (3, 3, 0); 497 } 498 499 static void sl_editor_map_key_to_action (void) 500 { 501 int retval; 502 char* arg1; 503 char* arg2; 504 int issue_usage = 1; 505 506 if (SLang_Num_Function_Args != 2) goto usage_label; 507 if (-1 == SLang_pop_string((char**)&arg2)) goto usage_label; 508 if (-1 == SLang_pop_string((char**)&arg1)) goto free_and_return_2; 509 issue_usage = 0; 510 511 retval = slang_api__editor_map_key_to_action(arg1, arg2); 512 (void)SLang_push_int(retval); 513 goto free_and_return; 514 free_and_return: 515 /* drop */ 516 SLang_free_slstring(arg1); 517 free_and_return_2: 518 SLang_free_slstring(arg2); 519 usage_label: 520 if (issue_usage) Slirp_usage (4, 4, 0); 521 } 522 523 static void sl_cure_get_eol (void) 524 { 525 int retval; 526 int issue_usage = 1; 527 528 if (SLang_Num_Function_Args != 0) goto usage_label; 529 issue_usage = 0; 530 531 retval = slang_api__cure_get_eol(); 532 (void)SLang_push_int(retval); 533 goto free_and_return; 534 free_and_return: 535 /* drop */ 536 usage_label: 537 if (issue_usage) Slirp_usage (5, 5, 0); 538 } 539 540 static void sl_cure_get_bol (void) 541 { 542 int retval; 543 int issue_usage = 1; 544 545 if (SLang_Num_Function_Args != 0) goto usage_label; 546 issue_usage = 0; 547 548 retval = slang_api__cure_get_bol(); 549 (void)SLang_push_int(retval); 550 goto free_and_return; 551 free_and_return: 552 /* drop */ 553 usage_label: 554 if (issue_usage) Slirp_usage (6, 6, 0); 555 } 556 557 static void sl_cure_delete (void) 558 { 559 int retval; 560 int issue_usage = 1; 561 562 if (SLang_Num_Function_Args != 0) goto usage_label; 563 issue_usage = 0; 564 565 retval = slang_api__cure_delete(); 566 (void)SLang_push_int(retval); 567 goto free_and_return; 568 free_and_return: 569 /* drop */ 570 usage_label: 571 if (issue_usage) Slirp_usage (7, 7, 0); 572 } 573 574 static void sl_cure_cursor_move (void) 575 { 576 int arg1; 577 int issue_usage = 1; 578 579 if (SLang_Num_Function_Args != 1) goto usage_label; 580 if (-1 == SLang_pop_int((int*)&arg1)) goto usage_label; 581 issue_usage = 0; 582 583 slang_api__cure_cursor_move( arg1); 584 goto free_and_return; 585 free_and_return: 586 /* drop */ 587 usage_label: 588 if (issue_usage) Slirp_usage (8, 8, 0); 589 } 590 591 static void sl_cure_cursor_offset (void) 592 { 593 int retval; 594 int issue_usage = 1; 595 596 if (SLang_Num_Function_Args != 0) goto usage_label; 597 issue_usage = 0; 598 599 retval = slang_api__cure_cursor_offset(); 600 (void)SLang_push_int(retval); 601 goto free_and_return; 602 free_and_return: 603 /* drop */ 604 usage_label: 605 if (issue_usage) Slirp_usage (9, 9, 0); 606 } 607 608 static void sl_listbox_with_data (void) 609 { 610 char* retval; 611 int arg1; 612 int arg2; 613 char* arg3; 614 char** arg4; 615 Slirp_Ref *arg4_r = ref_new(SLANG_STRING_TYPE,sizeof(char*),&arg4,0x0); 616 unsigned long arg5; 617 char** arg6; 618 Slirp_Ref *arg6_r = ref_new(SLANG_STRING_TYPE,sizeof(char*),&arg6,0x0); 619 unsigned long arg7; 620 int issue_usage = 1; 621 622 if (SLang_Num_Function_Args != 5) goto usage_label; 623 if (-1 == pop_array_or_ref( arg6_r, 0x0, 0)) goto usage_label; 624 if (-1 == pop_array_or_ref( arg4_r, 0x0, 0)) goto usage_label; 625 if (-1 == SLang_pop_string((char**)&arg3)) goto usage_label; 626 if (-1 == SLang_pop_int((int*)&arg2)) goto free_and_return_3; 627 if (-1 == SLang_pop_int((int*)&arg1)) goto free_and_return_3; 628 issue_usage = 0; 629 630 arg5 = (unsigned long) ref_get_size(arg4_r, 1); 631 arg7 = (unsigned long) ref_get_size(arg6_r, 1); 632 retval = slang_api__listbox_with_data( arg1, arg2, arg3, arg4, arg5, arg6, arg7); 633 (void)SLang_push_malloced_string(retval); 634 goto free_and_return; 635 free_and_return: 636 /* drop */ 637 free_and_return_3: 638 SLang_free_slstring(arg3); 639 usage_label: 640 if (issue_usage) Slirp_usage (10, 10, 0); 641 finalize_refs(2,arg4_r,arg6_r); 642 } 643 644 static void sl_cure_get_left_whole_word (void) 645 { 646 char* retval; 647 int arg1; 648 int issue_usage = 1; 649 650 if (SLang_Num_Function_Args != 1) goto usage_label; 651 if (-1 == SLang_pop_int((int*)&arg1)) goto usage_label; 652 issue_usage = 0; 653 654 retval = slang_api__cure_get_left_whole_word( arg1); 655 (void)SLang_push_malloced_string(retval); 656 goto free_and_return; 657 free_and_return: 658 /* drop */ 659 usage_label: 660 if (issue_usage) Slirp_usage (11, 11, 0); 661 } 662 663 static void sl_listbox (void) 664 { 665 int retval; 666 int arg1; 667 int arg2; 668 char* arg3; 669 char** arg4; 670 Slirp_Ref *arg4_r = ref_new(SLANG_STRING_TYPE,sizeof(char*),&arg4,0x0); 671 unsigned long arg5; 672 int issue_usage = 1; 673 674 if (SLang_Num_Function_Args != 4) goto usage_label; 675 if (-1 == pop_array_or_ref( arg4_r, 0x0, 0)) goto usage_label; 676 if (-1 == SLang_pop_string((char**)&arg3)) goto usage_label; 677 if (-1 == SLang_pop_int((int*)&arg2)) goto free_and_return_3; 678 if (-1 == SLang_pop_int((int*)&arg1)) goto free_and_return_3; 679 issue_usage = 0; 680 681 arg5 = (unsigned long) ref_get_size(arg4_r, 1); 682 retval = slang_api__listbox( arg1, arg2, arg3, arg4, arg5); 683 (void)SLang_push_int(retval); 684 goto free_and_return; 685 free_and_return: 686 /* drop */ 687 free_and_return_3: 688 SLang_free_slstring(arg3); 689 usage_label: 690 if (issue_usage) Slirp_usage (12, 12, 0); 691 finalize_refs(1,arg4_r); 692 } 693 694 static void sl_message (void) 695 { 696 char* arg1; 697 char* arg2; 698 int issue_usage = 1; 699 700 if (SLang_Num_Function_Args != 2) goto usage_label; 701 if (-1 == SLang_pop_string((char**)&arg2)) goto usage_label; 702 if (-1 == SLang_pop_string((char**)&arg1)) goto free_and_return_2; 703 issue_usage = 0; 704 705 slang_api__message(arg1, arg2); 706 goto free_and_return; 707 free_and_return: 708 /* drop */ 709 SLang_free_slstring(arg1); 710 free_and_return_2: 711 SLang_free_slstring(arg2); 712 usage_label: 713 if (issue_usage) Slirp_usage (13, 13, 0); 714 } 715 716 static void sl_listbox_auto (void) 717 { 718 int retval; 719 char* arg1; 720 char** arg2; 721 Slirp_Ref *arg2_r = ref_new(SLANG_STRING_TYPE,sizeof(char*),&arg2,0x0); 722 unsigned long arg3; 723 int issue_usage = 1; 724 725 if (SLang_Num_Function_Args != 2) goto usage_label; 726 if (-1 == pop_array_or_ref( arg2_r, 0x0, 0)) goto usage_label; 727 if (-1 == SLang_pop_string((char**)&arg1)) goto usage_label; 728 issue_usage = 0; 729 730 arg3 = (unsigned long) ref_get_size(arg2_r, 1); 731 retval = slang_api__listbox_auto(arg1, arg2, arg3); 732 (void)SLang_push_int(retval); 733 goto free_and_return; 734 free_and_return: 735 /* drop */ 736 SLang_free_slstring(arg1); 737 usage_label: 738 if (issue_usage) Slirp_usage (14, 14, 0); 739 finalize_refs(1,arg2_r); 740 } 741 742 static void sl_cure_get_byte (void) 743 { 744 int retval; 745 int arg1; 746 int issue_usage = 1; 747 748 if (SLang_Num_Function_Args != 1) goto usage_label; 749 if (-1 == SLang_pop_int((int*)&arg1)) goto usage_label; 750 issue_usage = 0; 751 752 retval = slang_api__cure_get_byte( arg1); 753 (void)SLang_push_int(retval); 754 goto free_and_return; 755 free_and_return: 756 /* drop */ 757 usage_label: 758 if (issue_usage) Slirp_usage (15, 15, 0); 759 } 760 761 static void sl_cure_insert_ahead (void) 762 { 763 int arg1; 764 int issue_usage = 1; 765 766 if (SLang_Num_Function_Args != 1) goto usage_label; 767 if (-1 == SLang_pop_int((int*)&arg1)) goto usage_label; 768 issue_usage = 0; 769 770 slang_api__cure_insert_ahead( arg1); 771 goto free_and_return; 772 free_and_return: 773 /* drop */ 774 usage_label: 775 if (issue_usage) Slirp_usage (16, 16, 0); 776 } 777 778 779 static SLang_Intrin_Fun_Type slang_api_functions_Funcs [] = 780 { 781 MAKE_INTRINSIC_0((char*)"set_action_hook",sl_set_action_hook,SLANG_VOID_TYPE), 782 MAKE_INTRINSIC_0((char*)"cure_backspace",sl_cure_backspace,SLANG_VOID_TYPE), 783 MAKE_INTRINSIC_0((char*)"add_new_action",sl_add_new_action,SLANG_VOID_TYPE), 784 MAKE_INTRINSIC_0((char*)"editor_map_key_to_func",sl_editor_map_key_to_func,SLANG_VOID_TYPE), 785 MAKE_INTRINSIC_0((char*)"editor_map_key_to_action",sl_editor_map_key_to_action,SLANG_VOID_TYPE), 786 MAKE_INTRINSIC_0((char*)"cure_get_eol",sl_cure_get_eol,SLANG_VOID_TYPE), 787 MAKE_INTRINSIC_0((char*)"cure_get_bol",sl_cure_get_bol,SLANG_VOID_TYPE), 788 MAKE_INTRINSIC_0((char*)"cure_delete",sl_cure_delete,SLANG_VOID_TYPE), 789 MAKE_INTRINSIC_0((char*)"cure_cursor_move",sl_cure_cursor_move,SLANG_VOID_TYPE), 790 MAKE_INTRINSIC_0((char*)"cure_cursor_offset",sl_cure_cursor_offset,SLANG_VOID_TYPE), 791 MAKE_INTRINSIC_0((char*)"listbox_with_data",sl_listbox_with_data,SLANG_VOID_TYPE), 792 MAKE_INTRINSIC_0((char*)"cure_get_left_whole_word",sl_cure_get_left_whole_word,SLANG_VOID_TYPE), 793 MAKE_INTRINSIC_0((char*)"listbox",sl_listbox,SLANG_VOID_TYPE), 794 MAKE_INTRINSIC_0((char*)"message",sl_message,SLANG_VOID_TYPE), 795 MAKE_INTRINSIC_0((char*)"listbox_auto",sl_listbox_auto,SLANG_VOID_TYPE), 796 MAKE_INTRINSIC_0((char*)"cure_get_byte",sl_cure_get_byte,SLANG_VOID_TYPE), 797 MAKE_INTRINSIC_0((char*)"cure_insert_ahead",sl_cure_insert_ahead,SLANG_VOID_TYPE), 798 SLANG_END_INTRIN_FUN_TABLE 799 }; /* }}} */ 800 801 static const char* usage_strings[] = { /* {{{ */ 802 "int = set_action_hook(string,string,string)", 803 "int = cure_backspace()", 804 "int = add_new_action(string,int)", 805 "int = editor_map_key_to_func(string,string,string)", 806 "int = editor_map_key_to_action(string,string)", 807 "int = cure_get_eol()", 808 "int = cure_get_bol()", 809 "int = cure_delete()", 810 "cure_cursor_move(int)", 811 "int = cure_cursor_offset()", 812 "string = listbox_with_data(int,int,string,string[],string[])", 813 "string = cure_get_left_whole_word(int)", 814 "int = listbox(int,int,string,string[])", 815 "message(string,string)", 816 "int = listbox_auto(string,string[])", 817 "int = cure_get_byte(int)", 818 "cure_insert_ahead(int)", 819 NULL 820 }; /* }}} */ 821 822 static void Slirp_usage(int i, int last, int flags) /* {{{ */ 823 { 824 char *indent; 825 int npop = SLstack_depth(); 826 if (npop > SLang_Num_Function_Args) npop = SLang_Num_Function_Args; 827 SLdo_pop_n(npop); 828 if (last == i) 829 indent = (char*)"Usage: "; 830 else { 831 indent = (char*)"\t"; 832 SLang_verror(SL_USAGE_ERROR, (char*)"Usage: one of"); 833 } 834 do 835 SLang_verror(SL_USAGE_ERROR, (char*)"%s%s", indent, usage_strings[i++]); 836 while (i < last); 837 if (flags & 0x2) 838 SLang_verror(SL_USAGE_ERROR, 839 (char*)"\tThis function has been vectorized and parallelized."); 840 else if (flags & 0x1) 841 SLang_verror(SL_USAGE_ERROR, (char*)"\tThis function has been vectorized."); 842 } /* }}} */ 843 844 #define SLIRP_VERSION_STRING pre2.0.0-34 845 #define SLIRP_VERSION_NUMBER 20000 846 SLANG_MODULE(slang_api_functions); 847 int init_slang_api_functions_module_ns(char *ns_name) /* {{{ */ 848 { 849 SLang_NameSpace_Type *ns = NULL; 850 851 if (slang_abi_mismatch()) return -1; 852 if (ns_name != NULL) { 853 ns = SLns_create_namespace (ns_name); 854 if (ns == NULL || 855 (slns = SLmalloc(strlen(ns_name)+1)) == NULL) 856 return -1; 857 strcpy(slns, ns_name); 858 } 859 860 861 #ifdef HAVE_OPAQUE_IVARS 862 if (-1 == set_opaque_ivar_types(slang_api_functions_Opaque_IVars) || 863 -1 == SLns_add_intrin_var_table(ns,slang_api_functions_Opaque_IVars,NULL)) 864 return -1; 865 #endif 866 867 if ( -1 == SLns_add_intrin_fun_table (ns,slang_api_functions_Funcs,(char*)"__slang_api_functions__")) 868 return -1; 869 870 return 0; 871 } /* }}} */ -
new file src/slang_engine.c
diff --git a/src/slang_engine.c b/src/slang_engine.c new file mode 100644 index 000000000..7bb01730a
- + 1 /* 2 Implements S-Lang scripting for MC. 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski, 2021 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file slang_engine.c 27 * \brief Internals of the S-Lang scripting for MC 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 * 31 * Functions that initialize S-Lang interpreter, load init.sl initialization script, load all 32 * plugins from the plugin directory, catch errors and display them in dialogs, etc. 33 */ 34 35 #include <config.h> 36 37 #include <slang.h> 38 #include <ctype.h> 39 40 #include "lib/global.h" 41 42 #include "lib/fileloc.h" 43 #include "lib/widget.h" 44 #include "lib/widget/wtools.h" 45 #include "lib/util.h" 46 47 #include "src/slang_engine.h" 48 #include "src/filemanager/dir.h" 49 50 /*** global variables ****************************************************************************/ 51 52 int num_action_hook_functions = 0; 53 GHashTable *action_hook_functions = NULL; 54 55 /*** file scope macro definitions ****************************************************************/ 56 57 /*** file scope type declarations ****************************************************************/ 58 59 /*** file scope variables ************************************************************************/ 60 61 static int mc_loglevel = 0; 62 static GString *last_error = NULL; 63 static char *last_fname = NULL; 64 65 /*** file scope functions ************************************************************************/ 66 /* --------------------------------------------------------------------------------------------- */ 67 68 static void 69 slang_error_handler (char *error_msg) 70 { 71 if (!last_error) 72 last_error = g_string_sized_new (255); 73 74 /* Truncate previous message. */ 75 g_string_truncate (last_error, 0); 76 /* Append error message (copying it) and a two new lines. */ 77 g_string_append (last_error, "Error in script:"); 78 79 /* Does message contain a file path? If so, append a newline, else a space. */ 80 if (strchr (error_msg, '/')) 81 g_string_append_c (last_error, '\n'); 82 else 83 g_string_append_c (last_error, ' '); 84 85 /* Append message. */ 86 g_string_append (last_error, error_msg); 87 g_string_append (last_error, "\n\n"); 88 89 /* Append a traceback header. */ 90 g_string_append (last_error, "Traceback:\n"); 91 } 92 93 /* --------------------------------------------------------------------------------------------- */ 94 95 static void 96 slang_dump_handler (char *traceback) 97 { 98 char *last_fragment_search_str, **tb_bits; 99 100 if (!last_error) 101 last_error = g_string_sized_new (255); 102 103 /* Split to check for `/path:<number>:other` pattern. If detected, add preceding new line. */ 104 tb_bits = g_strsplit (traceback, ":", 3); 105 if (strchr (tb_bits[0], '/') != NULL && tb_bits[1] && isdigit (tb_bits[1][0]) && tb_bits[2]) 106 g_string_append (last_error, "\n"); 107 g_strfreev (tb_bits); 108 109 /* Append traceback into error message buffer. */ 110 if (traceback) 111 g_string_append (last_error, traceback); 112 113 /* Search string to detect last part of traceback (i.e.: if it's top function traceback). */ 114 last_fragment_search_str = g_strdup_printf (":%s:", last_fname ? last_fname : ""); 115 116 /* If it's final message, then schedule a message display with complete error buffer. */ 117 if (g_strstr_len (traceback, -1, last_fragment_search_str) != NULL || 118 g_strstr_len (traceback, -1, ":<top-level>:") != NULL) 119 postponed_message (D_ERROR, " S-Lang script error ", "%s", last_error->str); 120 121 g_free (last_fragment_search_str); 122 } 123 124 /* --------------------------------------------------------------------------------------------- */ 125 126 static void 127 slang_exit_hook_handler (char *fname) 128 { 129 /* g_free is NULL safe, no need to check. */ 130 g_free (last_fname); 131 last_fname = g_strdup (fname); 132 } 133 134 /* --------------------------------------------------------------------------------------------- */ 135 /*** public functions ****************************************************************************/ 136 /* --------------------------------------------------------------------------------------------- */ 137 138 /* Slirp doesn't provide a header so the declaration is placed here to mute warning. */ 139 int init_slang_api_functions_module_ns (char *ns_name); 140 141 int 142 slang_init_engine (void) 143 { 144 const char *slang_init_path; 145 146 /* Handler for the (generated first) basic error message. */ 147 SLang_Error_Hook = slang_error_handler; 148 /* Handler for the (generated second) detailed traceback message. */ 149 SLang_Dump_Routine = slang_dump_handler; 150 /* Used to detect the last fragment of a traceback. */ 151 SLang_Exit_Function = slang_exit_hook_handler; 152 153 /* Set traceback level - do a full traceback via the dump handler. */ 154 SLang_Traceback = SL_TB_FULL; 155 156 /* Create the hash table for hooks. */ 157 action_hook_functions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); 158 159 /* Init S-Lang subsystems. */ 160 if (-1 == SLang_init_all ()) 161 return FALSE; 162 163 /* Add `mc_loglevel` variable that controls execution information messages. */ 164 if (-1 == SLadd_intrinsic_variable ((char *) "mc_loglevel", 165 (VOID_STAR) & mc_loglevel, SLANG_INT_TYPE, 0)) 166 { 167 postponed_message (D_ERROR, "MC Plugin Subsystem Error", "%s\n%s", 168 "Couldn't add mc_loglevel to S-Lang interpreter.", 169 "Something is very wrong."); 170 } 171 172 /* Init the `mc` namespace. */ 173 init_slang_api_functions_module_ns ((char *) "mc"); 174 175 /* Establish path to the init.sl file. */ 176 slang_init_path = 177 mc_build_filename (mc_global.sysconfig_dir, MC_SLANG_INIT_FILE, (char *) NULL); 178 179 /* Source `init.sl` into the S-Lang interpreter. */ 180 if (-1 == SLang_load_file ((char *) slang_init_path)) 181 { 182 /* Clear the error and reset the interpreter */ 183 SLang_restart (1); 184 SLang_set_error (0); 185 if (mc_loglevel >= 2) 186 postponed_message (D_ERROR, "MC Plugin Subsystem Error", 187 "Couldn't load init.sl startup script."); 188 } 189 else 190 { 191 if (mc_loglevel >= 2) 192 postponed_message (D_NORMAL, "MC Plugin Subsystem", 193 "Correctly loaded init.sl startup script."); 194 } 195 return 1; 196 } 197 198 /* --------------------------------------------------------------------------------------------- */ 199 200 int 201 slang_plugins_init (void) 202 { 203 int ret = TRUE, /* Default is OK return value */ 204 i; 205 const char *plugin_dir_path; 206 dir_list dir; 207 vfs_path_t *path; 208 file_entry_t *ptr; 209 dir_sort_options_t sort_options = { 0, 1, 0 }; /* A case-sensitive sort */ 210 211 dir.size = DIR_LIST_MIN_SIZE; 212 dir.list = g_new (file_entry_t, DIR_LIST_MIN_SIZE); 213 dir.len = 0; 214 dir.callback = NULL; 215 216 /* Get the VFS path for the plugins directory. */ 217 plugin_dir_path = mc_config_get_full_path (MC_PLUGIN_DIR); 218 path = vfs_path_from_str (plugin_dir_path); 219 220 if (!dir_list_load (&dir, path, (GCompareFunc) strcmp, &sort_options, "*.sl")) 221 { 222 postponed_message (D_ERROR, "MC Plugin Subsystem", "Couldn't read the plugin folder."); 223 return FALSE; 224 } 225 226 for (i = 0, ptr = dir.list; i < dir.len; i++, ptr++) 227 { 228 char *pl_path; 229 230 if (DIR_IS_DOT (ptr->fname) || DIR_IS_DOTDOT (ptr->fname)) 231 continue; 232 /* Construct full path to the found plugin (a text file with an .sl extension) script. */ 233 pl_path = mc_build_filename (plugin_dir_path, ptr->fname, (char *) NULL); 234 235 if (!pl_path) 236 continue; 237 238 /* Load the script with the interpreter. */ 239 if (-1 == SLang_load_file ((char *) pl_path) || SLang_get_error ()) 240 { 241 /* Error occurred: clear the error and reset the interpreter. */ 242 SLang_restart (1); 243 SLang_set_error (0); 244 245 if (mc_loglevel >= 2) 246 postponed_message (D_ERROR, "MC Plugin Subsystem", "Couldn't load the plugin: %s", 247 pl_path); 248 249 ret = FALSE; 250 } 251 else 252 { 253 254 /* Report if the loglevel is 2 or more. */ 255 if (mc_loglevel >= 2) 256 postponed_message (D_NORMAL, "MC Plugin Subsystem", "Plugin: %s loaded correctly", 257 pl_path); 258 } 259 g_free (pl_path); 260 } 261 262 return ret; 263 } 264 265 /* --------------------------------------------------------------------------------------------- */ 266 267 /* A function that looks up the command associated S-Lang code callback. */ 268 GSList * 269 get_command_callback (int ck_id) 270 { 271 GSList *value = g_hash_table_lookup (action_hook_functions, GINT_TO_POINTER (ck_id)); 272 return value; 273 } 274 275 /* --------------------------------------------------------------------------------------------- */ -
.h
diff --git a/src/vfs/fish/fish.h b/src/slang_engine.h similarity index 72% copy from src/vfs/fish/fish.h copy to src/slang_engine.h index 5d834cbd2..d8c5ac474 100644
old new 1 2 /** 3 * \file 4 * \brief Header: Virtual File System: FISH implementation for transfering files over 5 * shell connections 6 */ 7 8 9 #ifndef MC__VFS_FISH_H 10 #define MC__VFS_FISH_H 1 #ifndef MC__SLANG_ENGINE_H 2 #define MC__SLANG_ENGINE_H 11 3 12 4 /*** typedefs(not structures) and defined constants **********************************************/ 13 5 … … 17 9 18 10 /*** global variables defined in .c file *********************************************************/ 19 11 20 extern int fish_directory_timeout; 12 extern int num_action_hook_functions; 13 extern GHashTable *action_hook_functions; 21 14 22 15 /*** declarations of public functions ************************************************************/ 23 16 24 void vfs_init_fish (void); 17 int slang_init_engine (void); 18 int slang_plugins_init (void); 19 GSList *get_command_callback (int ck_id); 25 20 26 21 /*** inline functions ****************************************************************************/ 27 22