================ @@ -0,0 +1,203 @@ +//===-- runtime/execute.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang/Runtime/execute.h" +#include "environment.h" +#include "stat.h" +#include "terminator.h" +#include "tools.h" +#include "flang/Runtime/descriptor.h" +#include <cstdlib> +#include <future> +#include <limits> +#ifdef _WIN32 +#define LEAN_AND_MEAN +#define NOMINMAX +#include <stdio.h> +#include <windows.h> +#else +#include <signal.h> +#include <unistd.h> +#endif + +namespace Fortran::runtime { + +// cmdstat specified in 16.9.73 +// −1 if the processor does not support command line execution, +// a processor-dependent positive value if an error condition occurs +// −2 if no error condition occurs but WAIT is present with the value false +// and the processor does not support asynchronous execution. Otherwise it is +// assigned the value 0 +enum CMD_STAT { + ASYNC_NO_SUPPORT_ERR = -2, + NO_SUPPORT_ERR = -1, + CMD_EXECUTED = 0, + FORK_ERR = 1, + EXECL_ERR = 2, + INVALID_CL_ERR = 3, + SIGNAL_ERR = 4 +}; + +// Override CopyCharsToDescriptor in tools.h, pass string directly +void CopyCharsToDescriptor(const Descriptor &value, const char *rawValue) { + CopyCharsToDescriptor(value, rawValue, std::strlen(rawValue)); +} + +void CheckAndCopyCharsToDescriptor( + const Descriptor *value, const char *rawValue) { + if (value) { + CopyCharsToDescriptor(*value, rawValue); + } +} + +void CheckAndStoreIntToDescriptor( + const Descriptor *intVal, std::int64_t value, Terminator &terminator) { + if (intVal) { + StoreIntToDescriptor(intVal, value, terminator); + } +} + +// If a condition occurs that would assign a nonzero value to CMDSTAT but +// the CMDSTAT variable is not present, error termination is initiated. +int TerminationCheck(int status, const Descriptor *cmdstat, + const Descriptor *cmdmsg, Terminator &terminator) { + if (status == -1) { + if (!cmdstat) { + terminator.Crash("Execution error with system status code: %d", status); + } else { + CheckAndStoreIntToDescriptor(cmdstat, EXECL_ERR, terminator); + CopyCharsToDescriptor(*cmdmsg, "Execution error"); + } + } +#ifdef _WIN32 + // On WIN32 API std::system returns exit status directly + int exitStatusVal{status}; + if (exitStatusVal == 1) { +#else + int exitStatusVal{WEXITSTATUS(status)}; + if (exitStatusVal == 127 || exitStatusVal == 126) { +#endif + if (!cmdstat) { + terminator.Crash( + "Invalid command quit with exit status code: %d", exitStatusVal); + } else { + CheckAndStoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator); + CopyCharsToDescriptor(*cmdmsg, "Invalid command line"); + } + } +#if defined(WIFSIGNALED) && defined(WTERMSIG) + if (WIFSIGNALED(status)) { + if (!cmdstat) { + terminator.Crash("killed by signal: %d", WTERMSIG(status)); + } else { + CheckAndStoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator); + CopyCharsToDescriptor(*cmdmsg, "killed by signal"); + } + } +#endif +#if defined(WIFSTOPPED) && defined(WSTOPSIG) + if (WIFSTOPPED(status)) { + if (!cmdstat) { + terminator.Crash("stopped by signal: %d", WSTOPSIG(status)); + } else { + CheckAndStoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator); + CopyCharsToDescriptor(*cmdmsg, "stopped by signal"); + } + } +#endif + return exitStatusVal; +} + +void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait, + const Descriptor *exitstat, const Descriptor *cmdstat, + const Descriptor *cmdmsg, const char *sourceFile, int line) { + Terminator terminator{sourceFile, line}; + const char *newCmd{EnsureNullTerminated( + command.OffsetElement(), command.ElementBytes(), terminator)}; + + if (exitstat) { + RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat)); + } + + if (cmdstat) { + RUNTIME_CHECK(terminator, IsValidIntDescriptor(cmdstat)); + // Assigned 0 as specifed in standard, if error then overwrite + StoreIntToDescriptor(cmdstat, CMD_EXECUTED, terminator); + } + + if (cmdmsg) { + RUNTIME_CHECK(terminator, IsValidCharDescriptor(cmdmsg)); + } + + if (wait) { + // either wait is not specified or wait is true: synchronous mode + int status{std::system(newCmd)}; + int exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)}; + // If sync, assigned processor-dependent exit status. Otherwise unchanged + CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator); + } else { +// Asynchronous mode +#ifdef _WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + // append "cmd.exe /c " to the beginning of command + const char *prefix{"cmd.exe /c "}; + char *newCmdWin{(char *)AllocateMemoryOrCrash( + terminator, std::strlen(prefix) + std::strlen(newCmd) + 1)}; + std::strcpy(newCmdWin, prefix); + std::strcat(newCmdWin, newCmd); + + // Convert the char to wide char + const size_t sizeNeeded{mbstowcs(NULL, newCmdWin, 0) + 1}; + wchar_t *wcmd{new wchar_t[sizeNeeded]}; ---------------- yi-wu-arm wrote:
used `AllocateMemoryOrCrash()` to allocate memory https://github.com/llvm/llvm-project/pull/74077 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits