From 5b66163a4d2bd94405efe323dbf3d26715791627 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 20 Dec 2024 13:38:46 -0800 Subject: [PATCH] iverilog: Allow tool path to have spaces in it When invoking the various subcommands make sure the path to the executable is quoted. This allows it to contain spaces. On Windows quoting the command passed to `system()` or `popen()` is not enough. The default behavior under windows will strip quotes from the beginning and end of the command string under certain conditions. For this reason implement `system()` and `popen()` equivalents that do not suffer from this. Signed-off-by: Lars-Peter Clausen --- Makefile.in | 12 +- cppcheck.sup | 4 + driver/Makefile.in | 7 +- driver/cppcheck.sup | 4 + driver/main.c | 21 +-- ivlpp/Makefile.in | 7 +- ivlpp/cppcheck.sup | 4 + ivlpp/lexor.lex | 7 +- libmisc/CmdExec.c | 302 ++++++++++++++++++++++++++++++++++++++++++++ libmisc/CmdExec.h | 36 ++++++ pform.cc | 14 +- 11 files changed, 389 insertions(+), 29 deletions(-) create mode 100644 libmisc/CmdExec.c create mode 100644 libmisc/CmdExec.h diff --git a/Makefile.in b/Makefile.in index 2503db9c7..5da73df61 100644 --- a/Makefile.in +++ b/Makefile.in @@ -59,6 +59,8 @@ SUBDIRS += driver-vpi # rebuild the lexor_keyword.cc file. If we do, then we want to use the # local version instead of the one is $(srcdir). vpath lexor_keyword.cc . +vpath %.c $(srcdir)/libmisc +vpath %.c $(srcdir) vpath %.cc $(srcdir)/libmisc vpath %.cc $(srcdir) @@ -109,7 +111,8 @@ M = LineInfo.o StringHeap.o TT = t-dll.o t-dll-api.o t-dll-expr.o t-dll-proc.o t-dll-analog.o FF = cprop.o exposenodes.o nodangle.o synth.o synth2.o syn-rules.o -O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ +C_OBJ = CmdExec.o +CXX_OBJ = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \ elab_scope.o elab_sig.o elab_sig_analog.o elab_type.o \ emit.o eval_attrib.o \ @@ -128,6 +131,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PFunction.o \ PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o PTimingCheck.o \ PTask.o PUdp.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) +O = $(C_OBJ) $(CXX_OBJ) all: dep config.h _pli_types.h version_tag.h version_base.h ivl@EXEEXT@ $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true @@ -174,7 +178,7 @@ ifneq (@srcdir@,.) endif rm -rf autom4te.cache -cppcheck: $(O:.o=.cc) $(srcdir)/dosify.c +cppcheck: $(C_OBJ:.o=.c) $(CXX_OBJ:.o=.cc) $(srcdir)/dosify.c cppcheck --enable=all --std=c99 --std=c++11 -f \ --check-level=exhaustive \ --suppressions-list=$(srcdir)/cppcheck-global.sup \ @@ -232,6 +236,10 @@ endif $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d +%.o: %.c config.h | dep + $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o + mv $*.d dep/$*.d + # Here are some explicit dependencies needed to get things going. main.o: main.cc version_tag.h diff --git a/cppcheck.sup b/cppcheck.sup index f4e70d72d..52f232c17 100644 --- a/cppcheck.sup +++ b/cppcheck.sup @@ -93,6 +93,10 @@ unusedFunction:vpi_modules.cc:90 // vpi_sim_control() unusedFunction:vpi_modules.cc:110 +// CmdExec.c is shared by multiple executables, so each cppcheck target sees +// some exported helpers that are used only by another target. +unusedFunction:libmisc/CmdExec.c + // These are the functions that the compiler exports to the targets. //ivl_branch_island() unusedFunction:t-dll-api.cc:39 diff --git a/driver/Makefile.in b/driver/Makefile.in index 91e472816..e41fdc99f 100644 --- a/driver/Makefile.in +++ b/driver/Makefile.in @@ -31,6 +31,7 @@ builddir=@builddir@ top_builddir=@top_builddir@ VPATH = $(srcdir) +vpath %.c $(srcdir)/../libmisc bindir = $(exec_prefix)/bin libdir = $(exec_prefix)/lib @@ -50,16 +51,16 @@ MAN = @MAN@ PS2PDF = @PS2PDF@ ifeq (@srcdir@,.) -INCLUDE_PATH = -I. -I.. +INCLUDE_PATH = -I. -I.. -I../libmisc else -INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. +INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../libmisc endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = main.o substit.o cflexor.o cfparse.o +O = main.o substit.o cflexor.o cfparse.o CmdExec.o all: dep iverilog@EXEEXT@ iverilog.man diff --git a/driver/cppcheck.sup b/driver/cppcheck.sup index 82ce9a74e..13dd860cf 100644 --- a/driver/cppcheck.sup +++ b/driver/cppcheck.sup @@ -11,6 +11,10 @@ memleakOnRealloc nullPointerArithmeticOutOfMemory nullPointerOutOfMemory +// CmdExec.c is shared by multiple executables, so this target sees helpers +// that are used only by another target. +unusedFunction:../libmisc/CmdExec.c + // Errors/limitations in the generated yacc and lex files duplicateBreak:cflexor.lex constVariablePointer:cfparse.y diff --git a/driver/main.c b/driver/main.c index 95d02e91b..5ce3a5e6c 100644 --- a/driver/main.c +++ b/driver/main.c @@ -97,6 +97,7 @@ extern const char*optarg; #endif # include "globals.h" +# include "CmdExec.h" #include "cfparse_misc.h" /* cfparse() */ #include "ivl_alloc.h" @@ -335,16 +336,16 @@ static int t_version_only(void) free(source_path); fflush(0); - snprintf(tmp, sizeof tmp, "%s%civlpp -V", ivlpp_dir, sep); - rc = system(tmp); + snprintf(tmp, sizeof tmp, "\"%s%civlpp\" -V", ivlpp_dir, sep); + rc = ivl_run_cmd(tmp, verbose_flag); if (rc != 0) { fprintf(stderr, "Unable to get version from \"%s\"\n", tmp); } fflush(0); - snprintf(tmp, sizeof tmp, "%s%civl -V -C\"%s\" -C\"%s\"", ivl_dir, sep, + snprintf(tmp, sizeof tmp, "\"%s%civl\" -V -C\"%s\" -C\"%s\"", ivl_dir, sep, iconfig_path, iconfig_common_path); - rc = system(tmp); + rc = ivl_run_cmd(tmp, verbose_flag); if (rc != 0) { fprintf(stderr, "Unable to get version from \"%s\"\n", tmp); } @@ -363,7 +364,7 @@ static int t_version_only(void) static void build_preprocess_command(int e_flag) { - snprintf(tmp, sizeof tmp, "%s%civlpp%s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s", + snprintf(tmp, sizeof tmp, "\"%s%civlpp\"%s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s", ivlpp_dir, sep, verbose_flag ? " -v" : "", e_flag ? "" : " -L", @@ -395,7 +396,7 @@ static int t_preprocess_only(void) if (verbose_flag) printf("preprocess: %s\n", cmd); - rc = system(cmd); + rc = ivl_run_cmd(cmd, verbose_flag); remove(source_path); free(source_path); @@ -449,7 +450,7 @@ static int t_compile(void) #endif /* Build the ivl command. */ - snprintf(tmp, sizeof tmp, "%s%civl", ivl_dir, sep); + snprintf(tmp, sizeof tmp, "\"%s%civl\"", ivl_dir, sep); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -496,7 +497,7 @@ static int t_compile(void) printf("translate: %s\n", cmd); - rc = system(cmd); + rc = ivl_run_cmd(cmd, verbose_flag); if ( ! getenv("IVERILOG_ICONFIG")) { remove(source_path); free(source_path); @@ -1484,7 +1485,7 @@ int main(int argc, char **argv) if (vhdlpp_work == 0) vhdlpp_work = "ivl_vhdl_work"; - fprintf(defines_file, "vhdlpp:%s%cvhdlpp\n", vhdlpp_dir, sep); + fprintf(defines_file, "vhdlpp:\"%s%cvhdlpp\"\n", vhdlpp_dir, sep); fprintf(defines_file, "vhdlpp-work:%s\n", vhdlpp_work); for (unsigned idx = 0 ; idx < vhdlpp_libdir_cnt ; idx += 1) fprintf(defines_file, "vhdlpp-libdir:%s\n", vhdlpp_libdir[idx]); @@ -1547,7 +1548,7 @@ int main(int argc, char **argv) /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ - fprintf(iconfig_file, "ivlpp:%s%civlpp %s -L -F\"%s\" -P\"%s\"\n", + fprintf(iconfig_file, "ivlpp:\"%s%civlpp\" %s -L -F\"%s\" -P\"%s\"\n", ivlpp_dir, sep, strchr(warning_flags, 'r') ? "-Wredef-all" : strchr(warning_flags, 'R') ? "-Wredef-chg" : "", diff --git a/ivlpp/Makefile.in b/ivlpp/Makefile.in index 7fdd9e2fb..5811f53b1 100644 --- a/ivlpp/Makefile.in +++ b/ivlpp/Makefile.in @@ -26,6 +26,7 @@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = $(srcdir) +vpath %.c $(srcdir)/../libmisc bindir = @bindir@ libdir = @libdir@ @@ -38,16 +39,16 @@ INSTALL_DATA = @INSTALL_DATA@ LEX = @LEX@ ifeq (@srcdir@,.) -INCLUDE_PATH = -I. -I.. +INCLUDE_PATH = -I. -I.. -I../libmisc else -INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. +INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../libmisc endif CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = main.o lexor.o +O = main.o lexor.o CmdExec.o all: ivlpp@EXEEXT@ diff --git a/ivlpp/cppcheck.sup b/ivlpp/cppcheck.sup index e380f98ce..0aa1bbfb5 100644 --- a/ivlpp/cppcheck.sup +++ b/ivlpp/cppcheck.sup @@ -6,6 +6,10 @@ memleakOnRealloc nullPointerArithmeticOutOfMemory nullPointerOutOfMemory +// CmdExec.c is shared by multiple executables, so this target sees helpers +// that are used only by another target. +unusedFunction:../libmisc/CmdExec.c + // Errors/limitations in the generated yacc and lex files ctunullpointerOutOfMemory:lexor.lex memleakOnRealloc:lexor.lex diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index 6da2aaafe..dc354784f 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -27,6 +27,7 @@ # include # include +# include "CmdExec.h" # include "globals.h" # include "ivl_alloc.h" @@ -2128,12 +2129,12 @@ static void open_input_file(struct include_stack_t*isp) cmdlen += liblen; char*cmd = malloc(cmdlen); - snprintf(cmd, cmdlen, "%s -w\"%s\"%s %s", vhdlpp_path, vhdlpp_work, libs, isp->path); + snprintf(cmd, cmdlen, "%s -w\"%s\"%s \"%s\"", vhdlpp_path, vhdlpp_work, libs, isp->path); if (verbose_flag) fprintf(stderr, "Invoke vhdlpp: %s\n", cmd); - isp->file = popen(cmd, "r"); - isp->file_close = pclose; + isp->file = ivl_run_cmd_pipe(cmd); + isp->file_close = ivl_close_cmd_pipe; free(libs); free(cmd); diff --git a/libmisc/CmdExec.c b/libmisc/CmdExec.c new file mode 100644 index 000000000..5aefb0abc --- /dev/null +++ b/libmisc/CmdExec.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2026 Lars-Peter Clausen + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "CmdExec.h" + +# include +# include + +#ifdef __MINGW32__ +# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 +# endif +# if !defined(WINVER) || WINVER < 0x0600 +# undef WINVER +# define WINVER 0x0600 +# endif +# include +# include +# include +# include + +static char *get_cmd_exe(void) +{ + DWORD n = GetEnvironmentVariableA("COMSPEC", NULL, 0); + char *buf; + + if (n > 0) { + buf = (char *)malloc(n); + if (buf == NULL) + return NULL; + + DWORD len = GetEnvironmentVariableA("COMSPEC", buf, n); + + if (len > 0 && len < n) + return buf; + + free(buf); + } + + /* Fallback. This avoids searching PATH for cmd.exe. */ + { + char sysdir[MAX_PATH]; + UINT len = GetSystemDirectoryA(sysdir, sizeof(sysdir)); + + if (len > 0 && len < sizeof(sysdir)) { + static const char suffix[] = "\\cmd.exe"; + size_t need = strlen(sysdir) + sizeof(suffix); + + buf = (char *)malloc(need); + if (buf == NULL) + return NULL; + + strcpy(buf, sysdir); + strcat(buf, suffix); + return buf; + } + } + + return _strdup("cmd.exe"); +} + +/* + * On Windows `system()` runs the string as cmd.exe /C . + * `popen()` similarly runs the string through CMD.exe and attaches one side of + * a pipe. + * + * cmd.exe has special quote handling for /C. Quote characters are preserved + * only if all of the following are true: + * - there is no /S switch; + * - there are exactly two quote characters; + * - there are no special characters between them, where special is one + * of &<>()@^|; + * - there is whitespace between them; + * - the string between them is the name of an executable file. + * + * For an input like "C:\Program Files\...\ivlpp" -F"...\defs" "input file.v" + * the quotes at the beginning and the end get stripped, which results in + * incorrect behavior. + * + * Below are versions of `system()` and `popen()` that call cmd.exe with /C /S, + * which will preserve the quotes. Select cmd.exe from COMSPEC, with a fallback + * to the system directory, and pass it as the application name so PATH is not + * searched. + * + * Also pass /D to suppress AutoRun commands so a registry setting cannot + * modify this parsing path. + */ +static int start_cmd_process(const char *cmd, HANDLE stdout_handle, + PROCESS_INFORMATION *pi, + DWORD *last_error) +{ + STARTUPINFOEX siex; + STARTUPINFO si; + STARTUPINFO *startup_info; + HANDLE child_stdout = NULL; + HANDLE inherit_handles[3]; + DWORD creation_flags = 0; + BOOL inherit = FALSE; + SIZE_T attr_size = 0; + unsigned inherit_count = 0; + char *cmd_exe = get_cmd_exe(); + size_t cmd2len; + char *cmd2; + LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL; + int attr_list_initialized = 0; + int rc = -1; + + if (last_error != NULL) + *last_error = 0; + + if (cmd_exe == NULL) + return -1; + + cmd2len = strlen(cmd_exe) + strlen(cmd) + 16; + cmd2 = (char *)malloc(cmd2len); + if (cmd2 == NULL) + goto out; + + snprintf(cmd2, cmd2len, "\"%s\" /D /S /C \"%s\"", cmd_exe, cmd); + + memset(&si, 0x00, sizeof(si)); + si.cb = sizeof(si); + startup_info = &si; + + if (stdout_handle != NULL) { + HANDLE std_input = GetStdHandle(STD_INPUT_HANDLE); + HANDLE std_error = GetStdHandle(STD_ERROR_HANDLE); + + memset(&siex, 0x00, sizeof(siex)); + siex.StartupInfo.cb = sizeof(siex); + siex.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + + if (!DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), + &child_stdout, 0, TRUE, DUPLICATE_SAME_ACCESS)) + goto out; + + /* The normal system()/popen() path already relies on standard + * handles being inheritable. Only the pipe writer is duplicated. + */ + if (std_input != NULL && std_input != INVALID_HANDLE_VALUE) + inherit_handles[inherit_count++] = std_input; + inherit_handles[inherit_count++] = child_stdout; + if (std_error != NULL && std_error != INVALID_HANDLE_VALUE) + inherit_handles[inherit_count++] = std_error; + + siex.StartupInfo.hStdInput = std_input; + siex.StartupInfo.hStdOutput = child_stdout; + siex.StartupInfo.hStdError = std_error; + + InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size); + attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_size); + if (attr_list == NULL) + goto out; + if (!InitializeProcThreadAttributeList(attr_list, 1, 0, &attr_size)) + goto out; + attr_list_initialized = 1; + if (!UpdateProcThreadAttribute(attr_list, 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + inherit_handles, + inherit_count * sizeof(inherit_handles[0]), + NULL, NULL)) + goto out; + + siex.lpAttributeList = attr_list; + startup_info = &siex.StartupInfo; + creation_flags = EXTENDED_STARTUPINFO_PRESENT; + inherit = TRUE; + } + memset(pi, 0x00, sizeof(*pi)); + + if (!CreateProcess(cmd_exe, cmd2, NULL, NULL, inherit, + creation_flags, NULL, NULL, startup_info, pi)) { + if (last_error != NULL) + *last_error = GetLastError(); + goto out; + } + + rc = 0; + +out: + if (attr_list_initialized) + DeleteProcThreadAttributeList(attr_list); + if (attr_list != NULL) + free(attr_list); + if (child_stdout != NULL) + CloseHandle(child_stdout); + free(cmd2); + free(cmd_exe); + return rc; +} +#endif + +int ivl_run_cmd(const char *cmd, int verbose) +{ + if (verbose) + fprintf(stderr, "Executing: %s", cmd); + +#ifdef __MINGW32__ + DWORD exit_code = 1; + DWORD last_error; + PROCESS_INFORMATION pi; + + if (start_cmd_process(cmd, NULL, &pi, &last_error) != 0) { + if (last_error != 0) + fprintf(stderr, "CreateProcess failed (%lu).\n", last_error); + return -1; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return exit_code; +#else + return system(cmd); +#endif +} + +FILE *ivl_run_cmd_pipe(const char *cmd) +{ +#ifdef __MINGW32__ + SECURITY_ATTRIBUTES sa; + HANDLE read_pipe = NULL; + HANDLE write_pipe = NULL; + PROCESS_INFORMATION pi; + int fd; + FILE *fp; + + memset(&sa, 0x00, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = FALSE; + + if (!CreatePipe(&read_pipe, &write_pipe, &sa, 0)) + return NULL; + + if (start_cmd_process(cmd, write_pipe, &pi, NULL) != 0) { + CloseHandle(read_pipe); + CloseHandle(write_pipe); + return NULL; + } + + CloseHandle(write_pipe); + CloseHandle(pi.hThread); + + /* Match popen("r") text-mode semantics. The lexer expects CRLF from + * Windows command output to be normalized before it sees `line directives. + */ + fd = _open_osfhandle((intptr_t)read_pipe, _O_RDONLY | _O_TEXT); + if (fd < 0) { + CloseHandle(read_pipe); + TerminateProcess(pi.hProcess, 1); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + return NULL; + } + + fp = _fdopen(fd, "rt"); + if (fp == NULL) { + _close(fd); + TerminateProcess(pi.hProcess, 1); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + return NULL; + } + + /* The child owns the pipe writer. We do not need the process handle + * unless we want pclose() style exit status. + */ + CloseHandle(pi.hProcess); + return fp; +#else + return popen(cmd, "r"); +#endif +} + +int ivl_close_cmd_pipe(FILE *fp) +{ +#ifdef __MINGW32__ + return fclose(fp); +#else + return pclose(fp); +#endif +} diff --git a/libmisc/CmdExec.h b/libmisc/CmdExec.h new file mode 100644 index 000000000..7da9fb8b5 --- /dev/null +++ b/libmisc/CmdExec.h @@ -0,0 +1,36 @@ +#ifndef IVL_CmdExec_H +#define IVL_CmdExec_H +/* + * Copyright (c) 2026 Lars-Peter Clausen + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include + +#ifdef __cplusplus +extern "C" { +#endif + +int ivl_run_cmd(const char *cmd, int verbose); +FILE *ivl_run_cmd_pipe(const char *cmd); +int ivl_close_cmd_pipe(FILE *fp); + +#ifdef __cplusplus +} +#endif + +#endif /* IVL_CmdExec_H */ diff --git a/pform.cc b/pform.cc index 0a62724e8..f35effd21 100644 --- a/pform.cc +++ b/pform.cc @@ -44,6 +44,7 @@ # include # include +# include "CmdExec.h" # include "ivl_assert.h" # include "ivl_alloc.h" @@ -3466,17 +3467,14 @@ int pform_parse(const char*path) if (strcmp(path, "-") == 0) { vl_input = stdin; } else if (ivlpp_string) { - char*cmdline = static_cast(malloc(strlen(ivlpp_string) + - strlen(path) + 4)); - strcpy(cmdline, ivlpp_string); - strcat(cmdline, " \""); - strcat(cmdline, path); - strcat(cmdline, "\""); + size_t cmdlen = strlen(ivlpp_string) + strlen(path) + 4; + char*cmdline = static_cast(malloc(cmdlen)); + snprintf(cmdline, cmdlen, "%s \"%s\"", ivlpp_string, path); if (verbose_flag) cerr << "Executing: " << cmdline << endl<< flush; - vl_input = popen(cmdline, "r"); + vl_input = ivl_run_cmd_pipe(cmdline); if (vl_input == 0) { cerr << "Unable to preprocess " << path << "." << endl; return 1; @@ -3526,7 +3524,7 @@ int pform_parse(const char*path) if (vl_input != stdin) { if (ivlpp_string) - pclose(vl_input); + ivl_close_cmd_pipe(vl_input); else fclose(vl_input); }