1 /* praat_script.cpp
2 *
3 * Copyright (C) 1993-2021 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "praatP.h"
20 #include "praat_script.h"
21 #include "sendpraat.h"
22 #include "sendsocket.h"
23 #include "UiPause.h"
24 #include "DemoEditor.h"
25
praat_findObjectFromString(Interpreter interpreter,conststring32 string)26 static integer praat_findObjectFromString (Interpreter interpreter, conststring32 string) {
27 try {
28 integer IOBJECT;
29 while (*string == U' ') string ++;
30 if (*string >= U'A' && *string <= U'Z') {
31 /*
32 Find the object by its name.
33 */
34 static MelderString buffer;
35 MelderString_copy (& buffer, string);
36 char32 *space = str32chr (buffer.string, U' ');
37 if (! space)
38 Melder_throw (U"Missing space in name.");
39 *space = U'\0';
40 char32 *className = & buffer.string [0], *givenName = space + 1;
41 WHERE_DOWN (1) {
42 Daata object = (Daata) OBJECT;
43 if (str32equ (className, Thing_className (OBJECT)) && str32equ (givenName, object -> name.get()))
44 return IOBJECT;
45 }
46 /*
47 No object with that name. Perhaps the class name was wrong?
48 */
49 ClassInfo klas = Thing_classFromClassName (className, nullptr);
50 WHERE_DOWN (1) {
51 Daata object = (Daata) OBJECT;
52 if (str32equ (klas -> className, Thing_className (OBJECT)) && str32equ (givenName, object -> name.get()))
53 return IOBJECT;
54 }
55 Melder_throw (U"No object with that name.");
56 } else {
57 /*
58 Find the object by its ID.
59 */
60 double value;
61 Interpreter_numericExpression (interpreter, string, & value);
62 integer id = (integer) value;
63 WHERE (ID == id)
64 return IOBJECT;
65 Melder_throw (U"No object with number ", id, U".");
66 }
67 } catch (MelderError) {
68 Melder_throw (U"Object \"", string, U"\" does not exist.");
69 }
70 }
71
praat_findEditorFromString(conststring32 string)72 Editor praat_findEditorFromString (conststring32 string) {
73 integer IOBJECT;
74 while (*string == U' ')
75 string ++;
76 if (*string >= U'A' && *string <= U'Z') {
77 WHERE_DOWN (1) {
78 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
79 Editor editor = theCurrentPraatObjects -> list [IOBJECT]. editors [ieditor];
80 if (editor) {
81 Melder_assert (editor -> name);
82 const char32 *space = str32chr (editor -> name.get(), U' '); // editors tend to be called like "3. Sound kanweg"
83 if (space) { // but not all
84 conststring32 name = space + 1;
85 if (str32equ (name, string))
86 return editor;
87 }
88 }
89 }
90 }
91 } else {
92 WHERE_DOWN (1) {
93 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
94 Editor editor = theCurrentPraatObjects -> list [IOBJECT]. editors [ieditor];
95 if (editor && str32equ (editor -> name.get(), string))
96 return editor;
97 }
98 }
99 }
100 Melder_throw (U"Editor \"", string, U"\" does not exist.");
101 }
102
praat_findEditorById(integer id)103 Editor praat_findEditorById (integer id) {
104 int IOBJECT;
105 WHERE (1) {
106 if (ID == id) {
107 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
108 Editor editor = theCurrentPraatObjects -> list [IOBJECT]. editors [ieditor];
109 if (editor)
110 return editor;
111 }
112 }
113 }
114 Melder_throw (U"Editor ", id, U" does not exist.");
115 }
116
parseCommaSeparatedArguments(Interpreter interpreter,char32 * arguments,structStackel * args)117 static int parseCommaSeparatedArguments (Interpreter interpreter, char32 *arguments, structStackel *args) {
118 int narg = 0, depth = 0;
119 for (char32 *p = arguments; ; p ++) {
120 bool endOfArguments = *p == U'\0';
121 if (endOfArguments || (*p == U',' && depth == 0)) {
122 if (narg == MAXIMUM_NUMBER_OF_FIELDS)
123 Melder_throw (U"Cannot have more than ", MAXIMUM_NUMBER_OF_FIELDS, U" arguments");
124 *p = U'\0';
125 Formula_Result result;
126 Interpreter_anyExpression (interpreter, arguments, & result);
127 narg ++;
128 /*
129 First remove the old contents.
130 */
131 args [narg]. reset();
132 #if STACKEL_VARIANTS_ARE_PACKED_IN_A_UNION
133 memset (& args [narg], 0, sizeof (structStackel));
134 #endif
135 /*
136 Then copy in the new contents.
137 */
138 switch (result. expressionType) {
139 case kFormula_EXPRESSION_TYPE_NUMERIC: {
140 args [narg]. which = Stackel_NUMBER;
141 args [narg]. number = result. numericResult;
142 } break; case kFormula_EXPRESSION_TYPE_STRING: {
143 args [narg]. setString (result. stringResult.move());
144 } break; case kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR: {
145 args [narg]. which = Stackel_NUMERIC_VECTOR;
146 args [narg]. numericVector = result. numericVectorResult;
147 args [narg]. owned = result. owned;
148 result. owned = false;
149 } break; case kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX: {
150 args [narg]. which = Stackel_NUMERIC_MATRIX;
151 args [narg]. numericMatrix = result. numericMatrixResult;
152 args [narg]. owned = result. owned;
153 result. owned = false;
154 } break; case kFormula_EXPRESSION_TYPE_STRING_ARRAY: {
155 args [narg]. which = Stackel_STRING_ARRAY;
156 args [narg]. stringArray = result. stringArrayResult;
157 args [narg]. owned = result. owned;
158 result. owned = false;
159 } break;
160 }
161 arguments = p + 1;
162 } else if (*p == U'(' || *p == U'[' || *p == U'{') {
163 depth ++;
164 } else if (*p == U')' || *p == U']' || *p == U'}') {
165 depth --;
166 } else if (*p == U'\"') {
167 for (;;) {
168 p ++;
169 if (*p == U'\"') {
170 if (p [1] == U'\"')
171 p ++;
172 else
173 break;
174 }
175 }
176 }
177 if (endOfArguments)
178 break;
179 }
180 return narg;
181 }
182
praat_executeCommand(Interpreter interpreter,char32 * command)183 bool praat_executeCommand (Interpreter interpreter, char32 *command) {
184 if (interpreter)
185 interpreter -> returnType = kInterpreter_ReturnType::VOID_; // clear return type to its default
186
187 static struct structStackel args [1 + MAXIMUM_NUMBER_OF_FIELDS];
188 //trace (U"praat_executeCommand: ", Melder_pointer (interpreter), U": ", command);
189 if (command [0] == U'\0' || command [0] == U'#' || command [0] == U'!' || command [0] == U';')
190 /* Skip empty lines and comments. */;
191 else if ((command [0] == U'.' || command [0] == U'+' || command [0] == U'-') && Melder_isAsciiUpperCaseLetter (command [1])) { // selection?
192 integer IOBJECT = praat_findObjectFromString (interpreter, command + 1);
193 if (command [0] == '.')
194 praat_deselectAll ();
195 if (command [0] == '-')
196 praat_deselect (IOBJECT);
197 else
198 praat_select (IOBJECT);
199 praat_show ();
200 } else if (Melder_isLetter (command [0]) && ! Melder_isUpperCaseLetter (command [0])) { // all directives start with an ASCII lower-case letter
201 if (str32nequ (command, U"select ", 7)) {
202 if (str32nequ (command + 7, U"all", 3) && (command [10] == U'\0' || command [10] == U' ' || command [10] == U'\t')) {
203 praat_selectAll ();
204 praat_show ();
205 } else {
206 integer IOBJECT = praat_findObjectFromString (interpreter, command + 7);
207 praat_deselectAll ();
208 praat_select (IOBJECT);
209 praat_show ();
210 }
211 } else if (str32nequ (command, U"plus ", 5)) {
212 integer IOBJECT = praat_findObjectFromString (interpreter, command + 5);
213 praat_select (IOBJECT);
214 praat_show ();
215 } else if (str32nequ (command, U"minus ", 6)) {
216 integer IOBJECT = praat_findObjectFromString (interpreter, command + 6);
217 praat_deselect (IOBJECT);
218 praat_show ();
219 } else if (str32nequ (command, U"echo ", 5)) {
220 MelderInfo_open ();
221 MelderInfo_write (command + 5);
222 MelderInfo_close ();
223 } else if (str32nequ (command, U"clearinfo", 9)) {
224 Melder_clearInfo ();
225 } else if (str32nequ (command, U"print ", 6)) {
226 MelderInfo_write (command + 6);
227 MelderInfo_drain ();
228 } else if (str32nequ (command, U"printtab", 8)) {
229 MelderInfo_write (U"\t");
230 MelderInfo_drain ();
231 } else if (str32nequ (command, U"printline", 9)) {
232 if (command [9] == ' ') MelderInfo_write (command + 10);
233 MelderInfo_write (U"\n");
234 MelderInfo_drain ();
235 } else if (str32nequ (command, U"fappendinfo ", 12)) {
236 if (theCurrentPraatObjects != & theForegroundPraatObjects)
237 Melder_throw (U"The script command \"fappendinfo\" is not available inside pictures.");
238 structMelderFile file { };
239 Melder_relativePathToFile (command + 12, & file);
240 MelderFile_appendText (& file, Melder_getInfo ());
241 } else if (str32nequ (command, U"unix ", 5)) {
242 if (theCurrentPraatObjects != & theForegroundPraatObjects)
243 Melder_throw (U"The script command \"unix\" is not available inside manuals.");
244 try {
245 Melder_system (command + 5);
246 } catch (MelderError) {
247 Melder_throw (U"Unix command \"", command + 5, U"\" returned error status;\n"
248 U"if you want to ignore this, use `unix_nocheck' instead of `unix'.");
249 }
250 } else if (str32nequ (command, U"unix_nocheck ", 13)) {
251 if (theCurrentPraatObjects != & theForegroundPraatObjects)
252 Melder_throw (U"The script command \"unix_nocheck\" is not available inside manuals.");
253 try {
254 Melder_system (command + 13);
255 } catch (MelderError) {
256 Melder_clearError ();
257 }
258 } else if (str32nequ (command, U"system ", 7)) {
259 if (theCurrentPraatObjects != & theForegroundPraatObjects)
260 Melder_throw (U"The script command \"system\" is not available inside manuals.");
261 try {
262 Melder_system (command + 7);
263 } catch (MelderError) {
264 Melder_throw (U"System command \"", command + 7, U"\" returned error status;\n"
265 U"if you want to ignore this, use `system_nocheck' instead of `system'.");
266 }
267 } else if (str32nequ (command, U"system_nocheck ", 15)) {
268 if (theCurrentPraatObjects != & theForegroundPraatObjects)
269 Melder_throw (U"The script command \"system_nocheck\" is not available inside manuals.");
270 try {
271 Melder_system (command + 15);
272 } catch (MelderError) {
273 Melder_clearError ();
274 }
275 } else if (str32nequ (command, U"nowarn ", 7)) {
276 autoMelderWarningOff nowarn;
277 return praat_executeCommand (interpreter, command + 7);
278 } else if (str32nequ (command, U"noprogress ", 11)) {
279 autoMelderProgressOff noprogress;
280 return praat_executeCommand (interpreter, command + 11);
281 } else if (str32nequ (command, U"nocheck ", 8)) {
282 try {
283 return praat_executeCommand (interpreter, command + 8);
284 } catch (MelderError) {
285 Melder_clearError ();
286 return false;
287 }
288 } else if (str32nequ (command, U"demo ", 5)) {
289 autoDemoOpen demo;
290 return praat_executeCommand (interpreter, command + 5);
291 } else if (str32nequ (command, U"asynchronous ", 13)) {
292 autoMelderAsynchronous asynchronous;
293 return praat_executeCommand (interpreter, command + 13);
294 } else if (str32nequ (command, U"pause ", 6) || str32equ (command, U"pause")) {
295 if (theCurrentPraatApplication -> batch)
296 return true; // in batch we ignore pause statements
297 UiPause_begin (theCurrentPraatApplication -> topShell, U"stop or continue", interpreter);
298 UiPause_comment (str32equ (command, U"pause") ? U"..." : command + 6);
299 UiPause_end (1, 1, 0, U"Continue", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, interpreter);
300 } else if (str32nequ (command, U"execute ", 8)) {
301 praat_executeScriptFromFileNameWithArguments (command + 8);
302 } else if (str32nequ (command, U"editor", 6)) {
303 if (theCurrentPraatObjects != & theForegroundPraatObjects)
304 Melder_throw (U"The script command \"editor\" is not available inside manuals.");
305 if (command [6] == U' ' && Melder_isLetter (command [7])) {
306 praatP. editor = praat_findEditorFromString (command + 7);
307 } else if (command [6] == U'\0') {
308 if (interpreter && interpreter -> editorClass) {
309 praatP. editor = praat_findEditorFromString (interpreter -> environmentName.get());
310 } else {
311 Melder_throw (U"The function \"editor\" requires an argument when called from outside an editor.");
312 }
313 } else {
314 Interpreter_voidExpression (interpreter, command);
315 }
316 } else if (str32nequ (command, U"endeditor", 9)) {
317 if (theCurrentPraatObjects != & theForegroundPraatObjects)
318 Melder_throw (U"The script command \"endeditor\" is not available inside manuals.");
319 praatP. editor = nullptr;
320 } else if (str32nequ (command, U"sendpraat ", 10)) {
321 if (theCurrentPraatObjects != & theForegroundPraatObjects)
322 Melder_throw (U"The script command \"sendpraat\" is not available inside manuals.");
323 char32 programName [41], *q = & programName [0];
324 #ifdef macintosh
325 #define SENDPRAAT_TIMEOUT 10
326 #else
327 #define SENDPRAAT_TIMEOUT 0
328 #endif
329 const char32 *p = command + 10;
330 while (*p == U' ' || *p == U'\t') p ++;
331 while (*p != U'\0' && *p != U' ' && *p != U'\t' && q < programName + 39)
332 *q ++ = *p ++;
333 *q = U'\0';
334 if (q == & programName [0])
335 Melder_throw (U"Missing program name after `sendpraat'.");
336 while (*p == U' ' || *p == U'\t') p ++;
337 if (*p == U'\0')
338 Melder_throw (U"Missing command after `sendpraat'.");
339 #if motif
340 char *result = sendpraat (XtDisplay (theCurrentPraatApplication -> topShell), Melder_peek32to8 (programName),
341 SENDPRAAT_TIMEOUT, Melder_peek32to8 (p));
342 if (result)
343 Melder_throw (Melder_peek8to32 (result), U"\nMessage to ", programName, U" not completed.");
344 #endif
345 } else if (str32nequ (command, U"sendsocket ", 11)) {
346 if (theCurrentPraatObjects != & theForegroundPraatObjects)
347 Melder_throw (U"The script command \"sendsocket\" is not available inside manuals.");
348 char32 hostName [61], *q = & hostName [0];
349 const char32 *p = command + 11;
350 while (*p == U' ' || *p == U'\t')
351 p ++;
352 while (*p != U'\0' && *p != U' ' && *p != U'\t' && q < hostName + 59)
353 *q ++ = *p ++;
354 *q = U'\0';
355 if (q == hostName)
356 Melder_throw (U"Missing host name after `sendsocket'.");
357 while (*p == U' ' || *p == U'\t') p ++;
358 if (*p == U'\0')
359 Melder_throw (U"Missing command after `sendsocket'.");
360 char *result = sendsocket (Melder_peek32to8 (hostName), Melder_peek32to8 (p));
361 if (result)
362 Melder_throw (Melder_peek8to32 (result), U"\nMessage to ", hostName, U" not completed.");
363 } else if (str32nequ (command, U"filedelete ", 11)) {
364 if (theCurrentPraatObjects != & theForegroundPraatObjects)
365 Melder_throw (U"The script command \"filedelete\" is not available inside manuals.");
366 const char32 *p = command + 11;
367 structMelderFile file { };
368 while (*p == U' ' || *p == U'\t') p ++;
369 if (*p == U'\0')
370 Melder_throw (U"Missing file name after `filedelete'.");
371 Melder_relativePathToFile (p, & file);
372 MelderFile_delete (& file);
373 } else if (str32nequ (command, U"fileappend ", 11)) {
374 if (theCurrentPraatObjects != & theForegroundPraatObjects)
375 Melder_throw (U"The script command \"fileappend\" is not available inside manuals.");
376 const char32 *p = command + 11;
377 char32 path [kMelder_MAXPATH+1], *q = & path [0];
378 while (*p == U' ' || *p == U'\t') p ++;
379 if (*p == U'\0')
380 Melder_throw (U"Missing file name after `fileappend'.");
381 if (*p == '\"') {
382 for (;;) {
383 char32 kar = * ++ p;
384 if (kar == U'\"') if (* ++ p == U'\"') *q ++ = U'\"'; else break;
385 else if (kar == U'\0') break;
386 else *q ++ = kar;
387 }
388 } else {
389 for (;;) {
390 char32 kar = * p;
391 if (kar == U'\0' || kar == U' ' || kar == U'\t') break;
392 *q ++ = kar;
393 p ++;
394 }
395 }
396 *q = U'\0';
397 if (*p == U' ' || *p == U'\t') {
398 structMelderFile file { };
399 Melder_relativePathToFile (path, & file);
400 MelderFile_appendText (& file, p + 1);
401 }
402 } else {
403 /*
404 * This must be a formula command:
405 * proc (args)
406 */
407 Interpreter_voidExpression (interpreter, command);
408 }
409 } else { /* Simulate menu choice. */
410 bool hasDots = false, hasColon = false;
411
412 /* Parse command line into command and arguments. */
413 /* The separation is formed by the three dots or a colon. */
414
415 char32 *arguments = & command [0];
416 for (arguments = & command [0]; *arguments != U'\0'; arguments ++) {
417 if (*arguments == U':') {
418 hasColon = true;
419 if (arguments [1] == U'\0') {
420 arguments = & arguments [1]; // empty string
421 } else {
422 if (arguments [1] != U' ') {
423 Melder_throw (U"There should be a space after the colon.");
424 }
425 arguments [1] = U'\0'; // new end of "command"
426 arguments += 2; // the arguments start after the space
427 }
428 break;
429 }
430 if (*arguments == U'.' && arguments [1] == U'.' && arguments [2] == U'.') {
431 hasDots = true;
432 arguments += 3;
433 if (*arguments == U'\0') {
434 // empty string
435 } else {
436 if (*arguments != U' ') {
437 Melder_throw (U"There should be a space after the three dots.");
438 }
439 *arguments = U'\0'; // new end of "command"
440 arguments ++; // the arguments start after the space
441 }
442 break;
443 }
444 }
445
446 /* See if command exists and is available; ignore separators. */
447 /* First try loose commands, then fixed commands. */
448
449 integer narg;
450 char32 command2 [200];
451 if (hasColon) {
452 narg = parseCommaSeparatedArguments (interpreter, arguments, args);
453 str32cpy (command2, command);
454 char32 *colon = str32chr (command2, U':');
455 colon [0] = colon [1] = colon [2] = U'.';
456 colon [3] = U'\0';
457 }
458 if (theCurrentPraatObjects == & theForegroundPraatObjects && praatP. editor) {
459 if (hasColon) {
460 Editor_doMenuCommand (praatP. editor, command2, narg, args, nullptr, interpreter);
461 } else {
462 Editor_doMenuCommand (praatP. editor, command, 0, nullptr, arguments, interpreter);
463 }
464 } else if (theCurrentPraatObjects != & theForegroundPraatObjects &&
465 (str32nequ (command, U"Save ", 5) ||
466 str32nequ (command, U"Write ", 6) ||
467 str32nequ (command, U"Append ", 7) ||
468 str32equ (command, U"Quit")))
469 {
470 Melder_throw (U"Commands that write files (including Quit) are not available inside manuals.");
471 } else {
472 bool theCommandIsAnExistingAction = false;
473 try {
474 if (hasColon) {
475 theCommandIsAnExistingAction = praat_doAction (command2, narg, args, interpreter);
476 } else {
477 theCommandIsAnExistingAction = praat_doAction (command, arguments, interpreter);
478 }
479 } catch (MelderError) {
480 /*
481 * We only get here if the command *was* an existing action.
482 * Anything could have gone wrong in its execution,
483 * but one invisible problem can be checked here.
484 */
485 if (hasDots && arguments [0] != U'\0' && arguments [str32len (arguments) - 1] == U' ') {
486 Melder_throw (U"It may be helpful to remove the trailing spaces in \"", arguments, U"\".");
487 } else {
488 throw;
489 }
490 }
491 if (! theCommandIsAnExistingAction) {
492 bool theCommandIsAnExistingMenuCommand = false;
493 try {
494 if (hasColon) {
495 theCommandIsAnExistingMenuCommand = praat_doMenuCommand (command2, narg, args, interpreter);
496 } else {
497 theCommandIsAnExistingMenuCommand = praat_doMenuCommand (command, arguments, interpreter);
498 }
499 } catch (MelderError) {
500 /*
501 * We only get here if the command *was* an existing menu command.
502 * Anything could have gone wrong in its execution,
503 * but one invisible problem can be checked here.
504 */
505 if (hasDots && arguments [0] != U'\0' && arguments [str32len (arguments) - 1] == U' ') {
506 Melder_throw (U"It may be helpful to remove the trailing spaces in \"", arguments, U"\".");
507 } else {
508 throw;
509 }
510 }
511 if (! theCommandIsAnExistingMenuCommand) {
512 const integer length = str32len (command);
513 if (str32nequ (command, U"ARGS ", 5)) {
514 Melder_throw (U"Command \"ARGS\" no longer supported. Instead use \"form\" and \"endform\".");
515 } else if (str32chr (command, U'=')) {
516 Melder_throw (U"Command \"", command, U"\" not recognized.\n"
517 U"Probable cause: you are trying to use a variable name that starts with a capital.");
518 } else if (length >= 1 && Melder_isHorizontalSpace (command [length - 1])) {
519 Melder_throw (U"Command \"", command, U"\" not available for current selection. "
520 U"It may be helpful to remove the trailing spaces.");
521 } else if (length >= 2 && Melder_isHorizontalSpace (command [length - 2]) && command [length - 1] == U':') {
522 Melder_throw (U"Command \"", command, U"\" not available for current selection. "
523 U"It may be helpful to remove the space before the colon.");
524 } else if (str32nequ (command, U"\"ooTextFile\"", 12)) {
525 Melder_throw (U"Command \"", command, U"\" not available for current selection. "
526 U"It is possible that this file is not a Praat script but a Praat data file that you can open with \"Read from file...\".");
527 } else {
528 Melder_throw (U"Command \"", command, U"\" not available for current selection.");
529 }
530 }
531 }
532 }
533 praat_updateSelection ();
534 }
535 return true;
536 }
537
praat_executeCommandFromStandardInput(conststring32 programName)538 void praat_executeCommandFromStandardInput (conststring32 programName) {
539 char command8 [1000]; // can be recursive
540 /*
541 FIXME: implement for Windows.
542 */
543 for (;;) {
544 printf ("%s > ", Melder_peek32to8 (programName));
545 if (! fgets (command8, 999, stdin))
546 Melder_throw (U"Cannot read input.");
547 char *newLine = strchr (command8, '\n');
548 if (newLine)
549 *newLine = '\0';
550 autostring32 command32 = Melder_8to32 (command8);
551 try {
552 (void) praat_executeCommand (nullptr, command32.get());
553 } catch (MelderError) {
554 Melder_flushError (programName, U": Command \"", Melder_peek8to32 (command8), U"\" not executed.");
555 }
556 }
557 }
558
praat_executeScriptFromFile(MelderFile file,conststring32 arguments)559 void praat_executeScriptFromFile (MelderFile file, conststring32 arguments) {
560 try {
561 autostring32 text = MelderFile_readText (file);
562 autoMelderFileSetDefaultDir dir (file); // so that relative file names can be used inside the script
563 Melder_includeIncludeFiles (& text);
564 autoInterpreter interpreter = Interpreter_createFromEnvironment (praatP.editor);
565 if (arguments) {
566 Interpreter_readParameters (interpreter.get(), text.get());
567 Interpreter_getArgumentsFromString (interpreter.get(), arguments);
568 }
569 Interpreter_run (interpreter.get(), text.get());
570 } catch (MelderError) {
571 Melder_throw (U"Script ", file, U" not completed.");
572 }
573 }
574
praat_executeScriptFromFileName(conststring32 fileName,integer narg,Stackel args)575 void praat_executeScriptFromFileName (conststring32 fileName, integer narg, Stackel args) {
576 /*
577 The argument 'fileName' is unsafe. Duplicate its contents.
578 */
579 structMelderFile file { };
580 Melder_relativePathToFile (fileName, & file);
581 try {
582 autostring32 text = MelderFile_readText (& file);
583 autoMelderFileSetDefaultDir dir (& file); // so that relative file names can be used inside the script
584 Melder_includeIncludeFiles (& text);
585 autoInterpreter interpreter = Interpreter_createFromEnvironment (praatP.editor);
586 Interpreter_readParameters (interpreter.get(), text.get());
587 Interpreter_getArgumentsFromArgs (interpreter.get(), narg, args);
588 Interpreter_run (interpreter.get(), text.get());
589 } catch (MelderError) {
590 Melder_throw (U"Script ", & file, U" not completed."); // don't refer to 'fileName', because its contents may have changed
591 }
592 }
593
praat_executeScriptFromFileNameWithArguments(conststring32 nameAndArguments)594 void praat_executeScriptFromFileNameWithArguments (conststring32 nameAndArguments) {
595 char32 path [256];
596 const char32 *p, *arguments;
597 structMelderFile file { };
598 /*
599 Split into file name and arguments.
600 */
601 p = nameAndArguments;
602 while (*p == U' ' || *p == U'\t')
603 p ++;
604 if (*p == U'\"') {
605 char32 *q = path;
606 p ++; // skip quote
607 while (*p != U'\"' && *p != U'\0')
608 * q ++ = * p ++;
609 *q = U'\0';
610 arguments = p;
611 if (*arguments == U'\"')
612 arguments ++;
613 if (*arguments == U' ')
614 arguments ++;
615 } else {
616 char32 *q = path;
617 while (*p != U' ' && *p != U'\0')
618 * q ++ = * p ++;
619 *q = U'\0';
620 arguments = p;
621 if (*arguments == U' ')
622 arguments ++;
623 }
624 Melder_relativePathToFile (path, & file);
625 praat_executeScriptFromFile (& file, arguments);
626 }
627
praatlib_executeScript(const char * text8)628 extern "C" void praatlib_executeScript (const char *text8) {
629 try {
630 autoInterpreter interpreter = Interpreter_create (nullptr, nullptr);
631 autostring32 string = Melder_8to32 (text8);
632 Interpreter_run (interpreter.get(), string.get());
633 } catch (MelderError) {
634 Melder_throw (U"Script not completed.");
635 }
636 }
637
praat_executeScriptFromText(conststring32 text)638 void praat_executeScriptFromText (conststring32 text) {
639 try {
640 autoInterpreter interpreter = Interpreter_create (nullptr, nullptr);
641 autostring32 string = Melder_dup (text); // copy, because Interpreter will change it (UGLY)
642 Interpreter_run (interpreter.get(), string.get());
643 } catch (MelderError) {
644 Melder_throw (U"Script not completed.");
645 }
646 }
647
praat_executeScriptFromDialog(UiForm dia)648 void praat_executeScriptFromDialog (UiForm dia) {
649 structMelderFile file { };
650 Melder_pathToFile (dia -> scriptFilePath.get(), & file);
651 autostring32 text = MelderFile_readText (& file);
652 autoMelderFileSetDefaultDir dir (& file);
653 Melder_includeIncludeFiles (& text);
654 autoInterpreter interpreter = Interpreter_createFromEnvironment (praatP.editor);
655 Interpreter_readParameters (interpreter.get(), text.get());
656 Interpreter_getArgumentsFromDialog (interpreter.get(), dia);
657 autoPraatBackground background;
658 Interpreter_run (interpreter.get(), text.get());
659 }
660
secondPassThroughScript(UiForm sendingForm,integer,Stackel,conststring32,Interpreter,conststring32,bool,void *)661 static void secondPassThroughScript (UiForm sendingForm, integer /* narg */, Stackel /* args */,
662 conststring32 /* sendingString_dummy */, Interpreter /* interpreter_dummy */,
663 conststring32 /* invokingButtonTitle */, bool /* modified */, void *)
664 {
665 praat_executeScriptFromDialog (sendingForm);
666 }
667
firstPassThroughScript(MelderFile file)668 static void firstPassThroughScript (MelderFile file) {
669 try {
670 autostring32 text = MelderFile_readText (file);
671 {// scope
672 autoMelderFileSetDefaultDir dir (file);
673 Melder_includeIncludeFiles (& text);
674 }
675 autoInterpreter interpreter = Interpreter_createFromEnvironment (praatP.editor);
676 if (Interpreter_readParameters (interpreter.get(), text.get()) > 0) {
677 autoUiForm form = Interpreter_createForm (interpreter.get(),
678 praatP.editor ? praatP.editor -> windowForm : theCurrentPraatApplication -> topShell,
679 Melder_fileToPath (file), secondPassThroughScript, nullptr, false
680 );
681 UiForm_destroyWhenUnmanaged (form.get());
682 UiForm_do (form.get(), false);
683 form. releaseToUser();
684 } else {
685 autoPraatBackground background;
686 praat_executeScriptFromFile (file, nullptr);
687 }
688 } catch (MelderError) {
689 Melder_throw (U"Script ", file, U" not completed.");
690 }
691 }
692
fileSelectorOkCallback(UiForm dia,integer,Stackel,conststring32,Interpreter,conststring32,bool,void *)693 static void fileSelectorOkCallback (UiForm dia, integer /* narg */, Stackel /* args */,
694 conststring32 /* sendingString_dummy */, Interpreter /* interpreter_dummy */,
695 conststring32 /* invokingButtonTitle */, bool /* modified */, void *)
696 {
697 firstPassThroughScript (UiFile_getFile (dia));
698 }
699
DO_RunTheScriptFromAnyAddedMenuCommand(UiForm,integer,Stackel,conststring32 scriptPath,Interpreter,conststring32,bool,void *)700 void DO_RunTheScriptFromAnyAddedMenuCommand (UiForm /* sendingForm_dummy */, integer /* narg */, Stackel /* args */,
701 conststring32 scriptPath, Interpreter /* interpreter */,
702 conststring32 /* invokingButtonTitle */, bool /* modified */, void *)
703 {
704 structMelderFile file { };
705 Melder_relativePathToFile (scriptPath, & file);
706 firstPassThroughScript (& file);
707 }
708
DO_RunTheScriptFromAnyAddedEditorCommand(Editor editor,conststring32 script)709 void DO_RunTheScriptFromAnyAddedEditorCommand (Editor editor, conststring32 script) {
710 praatP.editor = editor;
711 DO_RunTheScriptFromAnyAddedMenuCommand (nullptr, 0, nullptr, script, nullptr, nullptr, false, nullptr);
712 /*praatP.editor = nullptr;*/
713 }
714
715 /* End of file praat_script.cpp */
716