Search code examples
c++windowsprettier

fgetc doesn't return to process


I'm trying to do something like echo {a:1} | prettier --stdin-filepath index.js and return the value returned in stdout. But I also want to make this program platform independant.

Referencing this and this, I managed to write code like this:

#include <Windows.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <stdexcept>

HANDLE hChildStd_IN_Rd = NULL;
HANDLE hChildStd_IN_Wr = NULL;
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;

int main() {
  const char source_code[] = "{a:1}";

  FILE *fd_OUT = nullptr, *fd_IN = nullptr;
#ifdef _WIN32
  try {
    SECURITY_ATTRIBUTES saAttr{};
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0))
      throw std::runtime_error("StdoutRd CreatePipe");
    if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
      throw std::runtime_error("Stdout SetHandleInformation");
    if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0))
      throw std::runtime_error("Stdin CreatePipe");
    if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
      throw std::runtime_error("Stdin SetHandleInformation");

    TCHAR szCmdline[] = TEXT(
        "C:\\Users\\yuto0214w\\AppData\\Roaming\\npm\\prettier.cmd "
        "--stdin-filepath index.js");

    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));

    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = hChildStd_OUT_Wr;
    siStartInfo.hStdInput = hChildStd_IN_Rd;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL,
                       &siStartInfo, &piProcInfo))
      throw std::runtime_error("CreateProcess");

    CloseHandle(piProcInfo.hProcess);
    CloseHandle(piProcInfo.hThread);
    CloseHandle(hChildStd_OUT_Wr);
    CloseHandle(hChildStd_IN_Rd);

    fd_OUT = _fdopen(_open_osfhandle((intptr_t)hChildStd_OUT_Rd, _O_RDONLY), "r");
    fd_IN = _fdopen(_open_osfhandle((intptr_t)hChildStd_IN_Wr, _O_WRONLY), "w");
  } catch (std::exception& e) {
    std::cout << std::string("Encountered an error when running: ") + e.what()
              << std::endl;
    return 1;
  }
#else
  // pipe fork dup2 execlp fdopen
#endif
  fputs(source_code, fd_IN);

  std::string formatted_code;
  int c;
  while ((c = fgetc(fd_OUT)) != EOF) {
    formatted_code += c;
    std::cout << "ch: " << c << std::endl; // For testing
  }

  fclose(fd_OUT);
  fclose(fd_IN);

  std::cout << formatted_code << std::endl;

  return 0;
}

This will compile, but stops completely on fgetc. I mean stops completely by it doesn't show any ch:.

I built program using MSVC v143.

Thanks


Solution

  • The problem was I wasn't closing fd_IN before reading fd_OUT.

    This causes deadlock because prettier is awaiting input from program, and program is awaiting output from prettier.

    To fix this,

      fputs(source_code, fd_IN);
      fclose(fd_IN);
    
      std::string formatted_code;
      int c;
      while ((c = fgetc(fd_OUT)) != EOF) {
        formatted_code += c;
        std::cout << "ch: " << c << std::endl; // For testing
      }
    
      fclose(fd_OUT);