/* gretl - The Gnu Regression, Econometrics and Time-series Library * Copyright (C) 1999-2000 Ramu Ramanathan and Allin Cottrell * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* interactive client program for libgretl - uses the GNU readline library if available */ #include #include #include #include #define GRETLCLI #include "libgretl.h" #include "monte_carlo.h" #include "var.h" #include "system.h" #include "gretl_restrict.h" #include "gretl_func.h" #include "modelspec.h" #include "libset.h" #include "forecast.h" #include "cmd_private.h" #include "objstack.h" #include "gretl_xml.h" #include "gretl_panel.h" #include "usermat.h" #ifdef WIN32 # include #else # include # include # include # include #endif enum { ERRFATAL_AUTO, ERRFATAL_FORCE }; #ifdef HAVE_READLINE # include /* readline functions from complete.c */ extern char *rl_gets (char **line_read, const char *prompt); extern void initialize_readline (void); #endif /* HAVE_READLINE */ char cmdfile[MAXLEN]; char outfile[MAXLEN]; char hdrfile[MAXLEN]; char syscmd[MAXLEN]; PATHS paths; /* useful paths */ PRN *cmdprn; FILE *fb; int errfatal, batch; int runit; int data_status; char linebak[MAXLINE]; /* for storing comments */ char *line_read; static int exec_line (ExecState *s, double ***pZ, DATAINFO **ppdinfo); static int push_input_file (FILE *fp); static FILE *pop_input_file (void); static int saved_object_action (const char *line, double ***pZ, DATAINFO *pdinfo, MODEL **models, PRN *prn); static void usage(void) { logo(); printf(_("\nYou may supply the name of a data file on the command line.\n" "Options:\n" " -b or --batch Process a command script and exit.\n" " -r or --run Run a script then hand control to command line.\n" " -h or --help Print this info and exit.\n" " -v or --version Print version info and exit.\n" " -e or --english Force use of English rather than translation.\n" " -q or --basque Force use of Basque translation.\n" "Example of batch mode usage:\n" " gretlcli -b myfile.inp >myfile.out\n" "Example of run mode usage:\n" " gretlcli -r myfile.inp\n")); exit(EXIT_SUCCESS); } #ifndef WIN32 int make_userdir (void) { const char *userdir = gretl_user_dir(); DIR *dir = NULL; int err = 0; if ((dir = opendir(userdir)) == NULL) { err = mkdir(userdir, 0755); if (err) { fprintf(stderr, _("Couldn't create user directory %s\n"), userdir); } else { fprintf(stderr, _("Created user directory %s\n"), userdir); } } else { closedir(dir); } return err; } #endif /* WIN32 */ void gretl_abort (char *line) { fprintf(stderr, _("\ngretlcli: error executing script: halting\n")); fprintf(stderr, "> %s\n", line); exit(EXIT_FAILURE); } void noalloc (const char *str) { fprintf(stderr, _("Couldn't allocate memory for %s\n"), str); exit(EXIT_FAILURE); } void file_get_line (char *line, CMD *cmd) { clear(line, MAXLINE); fgets(line, MAXLINE - 1, fb); if (*line == '\0') { strcpy(line, "quit"); } else { *linebak = 0; strncat(linebak, line, MAXLINE - 1); } if (!strncmp(line, "noecho", 6)) { set_gretl_echo(0); } if (gretl_echo_on() && cmd->ci == RUN && batch && *line == '(') { printf("%s", line); *linebak = 0; } } #ifdef ENABLE_NLS void nls_init (void) { # ifdef WIN32 char gretldir[MAXLEN], LOCALEDIR[MAXLEN]; if (read_reg_val(HKEY_LOCAL_MACHINE, "gretl", "gretldir", gretldir)) { return; } sprintf(LOCALEDIR, "%s\\locale", gretldir); # endif /* WIN32 */ setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); iso_gettext("@CLI_INIT"); putenv("LC_NUMERIC="); setlocale(LC_NUMERIC, ""); reset_local_decpoint(); } static void force_language (int f) { if (f == ENGLISH) { setlocale(LC_ALL, "C"); } else if (f == BASQUE) { # ifdef WIN32 setlocale(LC_ALL, "eu"); # else setlocale(LC_ALL, "eu_ES"); # endif } # ifdef WIN32 if (f == ENGLISH) { SetEnvironmentVariable("LC_ALL", "C"); putenv("LC_ALL=C"); } else if (f == BASQUE) { SetEnvironmentVariable("LC_ALL", "eu"); putenv("LC_ALL=eu"); } # endif } #endif /* ENABLE_NLS */ static int clear_data (CMD *cmd, double ***pZ, DATAINFO **ppdinfo, MODEL **models) { DATAINFO *pdinfo; int err = 0; *paths.datfile = 0; err = restore_full_sample(pZ, ppdinfo, NULL); pdinfo = *ppdinfo; if (pZ != NULL && *pZ != NULL) { free_Z(*pZ, pdinfo); *pZ = NULL; } clear_datainfo(pdinfo, CLEAR_FULL); data_status = 0; clear_model(models[0]); clear_model(models[1]); free_modelspec(); libgretl_session_cleanup(); reset_model_count(); gretl_cmd_destroy_context(cmd); return err; } static void get_a_filename (char *fname) { *fname = 0; fgets(fname, MAXLEN - 1, stdin); } static int get_an_input_line (ExecState *s, double ***pZ, DATAINFO *pdinfo) { int coding = gretl_compiling_function() || gretl_compiling_loop(); int err = 0; if (runit || batch) { /* reading from script file */ file_get_line(s->line, s->cmd); } else { /* interactive use */ #ifdef HAVE_READLINE rl_gets(&line_read, (coding)? "> " : "? "); if (line_read == NULL) { strcpy(s->line, "quit"); } else { strcpy(s->line, line_read); } #else printf("%s", (coding)? "> " : "? "); fflush(stdout); file_get_line(); /* note: "file" = stdin here */ #endif } return err; } /* allow for backslash continuation of lines */ static int maybe_get_input_line_continuation (char *line) { char tmp[MAXLINE]; int err = 0; if (!strncmp(line, "quit", 4)) { return 0; } while (top_n_tail(line)) { tmp[0] = '\0'; if (batch || runit) { fgets(tmp, MAXLINE - 1, fb); } else { #ifdef HAVE_READLINE rl_gets(&line_read, "> "); strcpy(tmp, line_read); #else fgets(tmp, MAXLINE - 1, stdin); #endif /* HAVE_READLINE */ } if (*tmp != '\0') { if (strlen(line) + strlen(tmp) > MAXLINE - 1) { fprintf(stderr, _("Maximum length of command line " "(%d bytes) exceeded\n"), MAXLINE); err = 1; break; } else { strcat(line, tmp); compress_spaces(line); } } } return err; } static void set_errfatal (int code) { static int hoe = -1; if (hoe < 0) { hoe = get_halt_on_error(); } if (code == ERRFATAL_FORCE) { /* for contexts where continuation on error is bound to make a big mess */ errfatal = 1; } else if (hoe == 0) { /* we've been explicitly told to keep going on error */ errfatal = 0; } else { /* default: errors are fatal in batch mode only */ errfatal = batch; } } int main (int argc, char *argv[]) { double **Z = NULL; DATAINFO *datainfo = NULL; MODEL **models = NULL; ExecState state; char *line = NULL; int cli_get_data = 0; char filearg[MAXLEN]; char runfile[MAXLEN]; CMD cmd; PRN *prn; int err = 0; #ifdef ENABLE_NLS nls_init(); #endif datainfo = datainfo_new(); if (datainfo == NULL) { noalloc(_("data information")); } if (argc > 1) { int force_lang = 0; int opt = parseopt((const char **) argv, argc, filearg, &force_lang); switch (opt) { case OPT_BATCH: batch = 1; if (*filearg == '\0') usage(); strcpy(runfile, filearg); cli_get_data = 1; break; case OPT_HELP: case OPT_DBOPEN: case OPT_WEBDB: usage(); break; case OPT_VERSION: logo(); exit(EXIT_SUCCESS); break; case OPT_RUNIT: runit = 1; if (*filearg == '\0') { usage(); } strcpy(runfile, filearg); cli_get_data = 1; break; default: break; } #ifdef ENABLE_NLS if (force_lang) { force_language(force_lang); if (argc == 2) { cli_get_data = 1; } } #endif } else { cli_get_data = 1; } #ifdef WIN32 if (!batch) { bind_textdomain_codeset(PACKAGE, "CP850"); } #endif libgretl_init(); logo(); session_time(NULL); prn = gretl_print_new(GRETL_PRINT_STDOUT); line = malloc(MAXLINE); if (line == NULL) { noalloc(_("command line")); } gretl_set_paths(&paths, OPT_D); /* defaults, not gui */ #ifdef WIN32 cli_read_registry(argv[0], &paths); gretl_set_paths(&paths, OPT_NONE); /* not defaults; use registry info */ #else make_userdir(); #endif /* WIN32 */ if (!batch) { strcpy(cmdfile, gretl_user_dir()); strcat(cmdfile, "session.inp"); cmdprn = gretl_print_new_with_filename(cmdfile); if (cmdprn == NULL) { printf(_("Can't open file to save commands\n")); return EXIT_FAILURE; } } if (!cli_get_data) { char given_file[MAXLEN]; int ftype; strcpy(given_file, filearg); strcpy(paths.datfile, filearg); ftype = detect_filetype(paths.datfile, &paths, prn); switch (ftype) { case GRETL_UNRECOGNIZED: case GRETL_NATIVE_DB: case GRETL_RATS_DB: exit(EXIT_FAILURE); break; case GRETL_NATIVE_DATA: err = gretl_get_data(&Z, &datainfo, paths.datfile, &paths, DATA_NONE, prn); break; case GRETL_XML_DATA: err = gretl_read_gdt(&Z, &datainfo, paths.datfile, &paths, DATA_NONE, prn, 0); break; case GRETL_CSV_DATA: err = import_csv(&Z, &datainfo, paths.datfile, prn); break; case GRETL_OCTAVE: err = import_octave(&Z, &datainfo, paths.datfile, prn); break; case GRETL_BOX_DATA: err = import_box(&Z, &datainfo, paths.datfile, prn); break; case GRETL_GNUMERIC: case GRETL_EXCEL: case GRETL_WF1: case GRETL_DTA: err = import_other(&Z, &datainfo, ftype, paths.datfile, prn); break; case GRETL_SCRIPT: runit = 1; strcpy(runfile, paths.datfile); clear(paths.datfile, MAXLEN); cli_get_data = 1; break; default: break; } if (!cli_get_data) { if (err) { errmsg(err, prn); if (err == E_FOPEN) { show_paths(&paths); } return EXIT_FAILURE; } data_status = 1; if (!batch) { pprintf(cmdprn, "open %s\n", given_file); } } } /* allocate memory for models */ models = allocate_working_models(2); if (models == NULL) { noalloc("models"); } gretl_cmd_init(&cmd); gretl_exec_state_init(&state, 0, line, &cmd, models, prn); /* print list of variables */ if (data_status) { varlist(datainfo, prn); } /* check for help file */ if (!batch) { FILE *fp = fopen(paths.helpfile, "r"); if (fp != NULL) { printf(_("\n\"help\" gives a list of commands\n")); fclose(fp); } else { printf(_("help file %s is not accessible\n"), paths.helpfile); show_paths(&paths); } } if (!batch) { fb = stdin; push_input_file(fb); } if (!batch && !runit && !data_status) { fprintf(stderr, _("Type \"open filename\" to open a data set\n")); } #ifdef HAVE_READLINE if (!batch) initialize_readline(); #endif if (batch || runit) { /* re-initialize: will be incremented by "run" cmd */ runit = 0; sprintf(line, "run %s", runfile); exec_line(&state, &Z, &datainfo); } /* should we stop immediately on error, in batch mode? */ set_errfatal(ERRFATAL_AUTO); /* main command loop */ while (strcmp(cmd.word, "quit") && fb != NULL) { char linecopy[MAXLINE]; int overflow; if (err && errfatal) { gretl_abort(linecopy); } if (gretl_execute_loop()) { if (gretl_loop_exec(&state, &Z, &datainfo)) { return 1; } set_errfatal(ERRFATAL_AUTO); } else { err = get_an_input_line(&state, &Z, datainfo); if (err) { errmsg(err, prn); continue; } } overflow = maybe_get_input_line_continuation(line); if (overflow) { break; } else { strcpy(linecopy, line); err = exec_line(&state, &Z, &datainfo); } } /* end of get commands loop */ /* leak check -- try explicitly freeing all memory allocated */ destroy_working_models(models, 2); destroy_dataset(Z, datainfo); if (fb != stdin && fb != NULL) { fclose(fb); } free(line); free_modelspec(); gretl_print_destroy(prn); gretl_cmd_free(&cmd); libgretl_cleanup(); return 0; } static void printf_strip (char *s) { int i, n; while (isspace((unsigned char) *s)) s++; n = strlen(s); for (i=n-1; i>0; i--) { if (isspace(s[i]) || s[i] == '\r') s[i] = '\0'; else break; } if (gretl_compiling_loop()) { printf("> %s\n", s); } else { printf("%s\n", s); } } static void cli_do_autofit_plot (MODEL *pmod, double ***pZ, DATAINFO *pdinfo, PRN *prn) { int plotlist[3]; int err = 0; plotlist[0] = 2; plotlist[1] = gretl_model_get_depvar(pmod); plotlist[2] = varindex(pdinfo, "autofit"); err = gnuplot(plotlist, NULL, (const double **) *pZ, pdinfo, OPT_B | OPT_O | OPT_T); if (err) { pputs(prn, _("gnuplot command failed\n")); } } static void cli_exec_callback (ExecState *s, double ***pZ, DATAINFO *pdinfo) { int ci = s->cmd->ci; if (ci == FIT && dataset_is_time_series(pdinfo)) { cli_do_autofit_plot(s->models[0], pZ, pdinfo, s->prn); } else if (ci == VAR || ci == VECM) { maybe_stack_var(s->var, s->cmd); } } static int cli_open_append (CMD *cmd, const char *line, double ***pZ, DATAINFO **ppdinfo, MODEL **models, PRN *prn) { DataOpenCode ocode = DATA_NONE; DATAINFO *pdinfo = *ppdinfo; char datfile[MAXLEN] = {0}; char response[3]; int k, dbdata = 0; int err = 0; err = getopenfile(line, datfile, &paths, (cmd->opt & OPT_W)? OPT_W : OPT_NONE); if (err) { errmsg(err, prn); return err; } if (cmd->opt & OPT_W) { k = GRETL_NATIVE_DB_WWW; } else if (cmd->opt & OPT_B) { k = GRETL_BOX_DATA; } else if (cmd->opt & OPT_O) { k = GRETL_CSV_DATA; } else { k = detect_filetype(datfile, &paths, prn); } dbdata = (k == GRETL_NATIVE_DB || k == GRETL_NATIVE_DB_WWW || k == GRETL_RATS_DB || k == GRETL_PCGIVE_DB); if (data_status && !batch && !dbdata && cmd->ci != APPEND && strcmp(datfile, paths.datfile)) { fprintf(stderr, _("Opening a new data file closes the " "present one. Proceed? (y/n) ")); fgets(response, sizeof response, stdin); if (*response != 'y' && *response != 'Y') { pprintf(prn, _("OK, staying with current data set\n")); return 0; } } if (data_status) { if (dbdata || cmd->ci == APPEND) { ocode = DATA_APPEND; } else { clear_data(cmd, pZ, ppdinfo, models); pdinfo = *ppdinfo; } } if (k == GRETL_CSV_DATA) { err = import_csv(pZ, ppdinfo, datfile, prn); } else if (k == GRETL_OCTAVE) { err = import_octave(pZ, ppdinfo, datfile, prn); } else if (k == GRETL_BOX_DATA) { err = import_box(pZ, ppdinfo, datfile, prn); } else if (WORKSHEET_IMPORT(k)) { err = import_other(pZ, ppdinfo, k, datfile, prn); } else if (k == GRETL_XML_DATA) { err = gretl_read_gdt(pZ, ppdinfo, datfile, &paths, ocode, prn, 0); } else if (dbdata) { err = set_db_name(datfile, k, &paths, prn); } else { err = gretl_get_data(pZ, ppdinfo, datfile, &paths, ocode, prn); } pdinfo = *ppdinfo; if (err) { errmsg(err, prn); return err; } if (!dbdata && cmd->ci != APPEND) { strncpy(paths.datfile, datfile, MAXLEN - 1); } data_status = 1; if (pdinfo->v > 0 && !dbdata) { varlist(pdinfo, prn); } return err; } static int exec_line (ExecState *s, double ***pZ, DATAINFO **ppdinfo) { DATAINFO *pdinfo = *ppdinfo; char *line = s->line; CMD *cmd = s->cmd; PRN *prn = s->prn; MODEL **models = s->models; int old_runit = runit; unsigned char eflag; char runfile[MAXLEN]; int k, err = 0; if (string_is_blank(line)) { return 0; } if (gretl_compiling_function()) { err = gretl_function_append_line(line); if (err) { errmsg(err, prn); } else { pprintf(cmdprn, "%s\n", line); } return err; } if (!s->in_comment) { /* catch requests relating to saved objects, which are not really "commands" as such */ k = saved_object_action(line, pZ, pdinfo, models, prn); if (k == 1) return 0; /* action was OK */ if (k == -1) return 1; /* action was faulty */ /* are we ready for this? */ if (!data_status && !cmd_ignore(cmd) && !ready_for_command(line)) { fprintf(stderr, _("You must open a data file first\n")); return 1; } } /* tell libgretl if we're in batch mode */ gretl_set_batch_mode(batch); /* if we're stacking commands for a loop, parse "lightly" */ if (gretl_compiling_loop()) { err = get_command_index(line, cmd, pdinfo); } else { err = parse_command_line(line, cmd, pZ, pdinfo); } if (err) { errmsg(err, prn); return err; } /* are we in a multi-line comment block? */ s->in_comment = (cmd_ignore(cmd))? 1 : 0; /* if in batch mode, echo comments from input */ if (batch && cmd->ci == CMD_COMMENT && gretl_echo_on()) { printf_strip(linebak); } if (cmd->ci < 0) { /* there's nothing there */ return 0; } if (s->sys != NULL && cmd->ci != END && cmd->ci != EQUATION && cmd->ci != SYSTEM) { printf(_("Command '%s' ignored; not valid within equation system\n"), line); gretl_equation_system_destroy(s->sys); s->sys = NULL; return 1; } if (cmd->ci == LOOP && !batch && !runit) { pputs(prn, _("Enter commands for loop. " "Type 'endloop' to get out\n")); } if (cmd->ci == LOOP || gretl_compiling_loop()) { /* accumulating loop commands */ if (!ok_in_loop(cmd->ci)) { printf(_("Command '%s' ignored; not available in loop mode\n"), line); } else { if (gretl_echo_on() && (!gretl_compiling_loop() || batch || runit)) { eflag = gretl_compiling_loop()? CMD_STACKING : CMD_BATCH_MODE; /* straight visual echo */ echo_cmd(cmd, pdinfo, line, eflag, prn); } err = gretl_loop_append_line(s, pZ, pdinfo); if (err) { set_errfatal(ERRFATAL_FORCE); errmsg(err, prn); } else if (!batch && !runit) { /* echo to record */ echo_cmd(cmd, pdinfo, line, CMD_RECORDING, cmdprn); } } return err; } if (gretl_echo_on()) { /* visual feedback, not recording */ eflag = (batch || runit)? CMD_BATCH_MODE : CMD_CLI; echo_cmd(cmd, pdinfo, line, eflag, prn); } check_for_loop_only_options(cmd->ci, cmd->opt, prn); s->callback = cli_exec_callback; switch (cmd->ci) { case DELEET: if (get_matrix_by_name(cmd->param)) { err = user_matrix_destroy_by_name(cmd->param, prn); if (err) { errmsg(err, prn); } break; } if (complex_subsampled()) { pputs(prn, _("Can't delete a variable when in sub-sample" " mode\n")); break; } err = dataset_drop_listed_variables(cmd->list, pZ, pdinfo, &k); if (err) { pputs(prn, _("Failed to shrink the data set")); } else { if (k) { pputs(prn, _("Take note: variables have been renumbered")); pputc(prn, '\n'); } maybe_list_vars(pdinfo, prn); } break; case GNUPLOT: case SCATTERS: if (cmd->ci == GNUPLOT) { if ((cmd->opt & OPT_Z) && (cmd->list[0] != 3 || !gretl_isdummy(pdinfo->t1, pdinfo->t2, (*pZ)[cmd->list[3]]))) { pputs(prn, _("You must supply three variables, the last of " "which is a dummy variable\n(with values 1 or 0)\n")); break; } err = gnuplot(cmd->list, cmd->param, (const double **) *pZ, pdinfo, (cmd->opt | OPT_B)); } else { err = multi_scatters(cmd->list, (const double **) *pZ, pdinfo, (cmd->opt | OPT_B)); } if (err) { pputs(prn, _("gnuplot command failed\n")); } else if (batch) { pprintf(prn, _("wrote %s\n"), gretl_plotfile()); } break; case HELP: cli_help(cmd->param, paths.helpfile, 1, prn); break; case OPEN: case APPEND: err = cli_open_append(cmd, line, pZ, ppdinfo, models, prn); pdinfo = *ppdinfo; break; case NULLDATA: k = gretl_int_from_string(cmd->param, (const double **) *pZ, pdinfo, &err); if (!err && k < 2) { err = 1; } if (err) { pputs(prn, _("Data series length count missing or invalid\n")); break; } if (data_status) { clear_data(cmd, pZ, ppdinfo, models); pdinfo = *ppdinfo; } err = open_nulldata(pZ, pdinfo, data_status, k, prn); if (err) { pputs(prn, _("Failed to create empty data set\n")); } else { data_status = 1; } break; case QUIT: if (runit) { *s->runfile = '\0'; runit--; fclose(fb); fb = pop_input_file(); if (fb == NULL) { pputs(prn, _("Done\n")); } else { strcpy(cmd->word, "endrun"); /* overwrite "quit" */ } break; } printf(_("commands saved as %s\n"), cmdfile); gretl_print_destroy(cmdprn); if (cmd->opt & OPT_X) { break; } printf(_("type a filename to store output (enter to quit): ")); get_a_filename(outfile); top_n_tail(outfile); if (*outfile != 0 && *outfile != '\n' && *outfile != '\r' && strcmp(outfile, "q")) { const char *udir = gretl_user_dir(); printf(_("writing session output to %s%s\n"), udir, outfile); #ifdef WIN32 sprintf(syscmd, "\"%s\\gretlcli\" -b \"%s\" > \"%s%s\"", paths.gretldir, cmdfile, udir, outfile); /* WinExec(syscmd, SW_SHOWMINIMIZED); */ system(syscmd); #else sprintf(syscmd, "gretlcli -b \"%s\" > \"%s%s\"", cmdfile, udir, outfile); gretl_spawn(syscmd); #endif printf("%s\n", syscmd); } break; case RUN: case INCLUDE: err = getopenfile(line, runfile, &paths, OPT_S); if (err) { pputs(prn, _("Command is malformed\n")); break; } if (cmd->ci == INCLUDE && gretl_is_xml_file(runfile)) { err = load_user_XML_file(runfile); if (err) { pprintf(prn, _("Error reading %s\n"), runfile); } break; } if (!strcmp(runfile, s->runfile)) { pprintf(prn, _("Infinite loop detected in script\n")); err = 1; break; } if (fb != NULL) { push_input_file(fb); } if ((fb = fopen(runfile, "r")) == NULL) { fprintf(stderr, _("Couldn't open script \"%s\"\n"), runfile); fb = pop_input_file(); } else { strcpy(s->runfile, runfile); fprintf(stderr, _("%s opened OK\n"), runfile); if (cmd->ci == INCLUDE) { pprintf(cmdprn, "include \"%s\"\n", runfile); } else { pprintf(cmdprn, "run \"%s\"\n", runfile); } runit++; } break; default: err = gretl_cmd_exec(s, pZ, ppdinfo); pdinfo = *ppdinfo; break; } if (!err && (is_model_cmd(cmd->word) || s->alt_model) && !is_quiet_model_test(cmd->ci, cmd->opt)) { attach_subsample_to_model(models[0], pdinfo); #if MSPEC_DEBUG fprintf(stderr, "\ngretlcli: saving spec: model.ID = %d, model_count = %d\n", (models[0])->ID, get_model_count()); #endif if (is_model_cmd(cmd->word)) { err = modelspec_save(models[0]); } maybe_stack_model(models[0], cmd, prn); } if (gretl_system_save_flag_set(s->sys)) { /* only warrants action in GUI program */ gretl_system_unset_save_flag(s->sys); s->sys = NULL; } if (!err && cmd->ci != QUIT && gretl_echo_on() && !batch && !old_runit) { /* record a successful interactive command */ echo_cmd(cmd, pdinfo, line, CMD_RECORDING, cmdprn); } return err; } /* apparatus for keeping track of input stream */ #define N_STACKED_FILES 8 static int nfiles; static FILE *fstack[N_STACKED_FILES]; static int push_input_file (FILE *fp) { int err = 0; if (nfiles >= N_STACKED_FILES) { err = 1; } else { fstack[nfiles++] = fp; } return err; } static FILE *pop_input_file (void) { FILE *ret = NULL; if (nfiles > 0) { ret = fstack[--nfiles]; } return ret; } #include "cli_object.c"