praat_script.cpp 25 KB

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