OS2/emx/libc/popen/full_duplex_popen
OS2/emx/libc/popen/full_duplex_popen
双方向リダイレクト可能な popen(もどき)をためしに作成。
/* 2006-01-04 emx libc の popen あたりを参考に試作 2006-01-11 けっこうバグってたので直した */ #if defined(__EMX__) && !defined(__ST_MT_ERRNO__) #define __ST_MT_ERRNO__ #endif #include <errno.h> #include <fcntl.h> #include <process.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static char * my_getshell(char **optstr) { char *s = NULL; char *bn, *ops; #if 0 s = getenv("RUBYSHELL"); #endif #if defined(__EMX__) if (!s) s = getenv("EMXSHELL"); #endif if (!s) s = getenv("SHELL"); if (!s) s = getenv("COMSPEC"); if (!s) s = "cmd.exe"; bn = _getname(s); if (stricmp(bn, "cmd.exe") == 0 #if defined(OS2) || defined(__OS2__) || stricmp(bn, "4os2.exe") == 0 #endif || stricmp(bn, "command.com") == 0 ) ops = "/c"; else ops = "-c"; if (optstr) *optstr = ops; return s; } int emx_duplex_popen(const char *cmd, const char *fmode, FILE **fprd, FILE **fpwr) { FILE *fr = NULL, *fw = NULL; char fmr[4], fmw[4]; int pdr[2], pdw[2]; int pid_child = -1; int ph_stdin = -1, ph_stdout = -1; int pm_stdin = -1, pm_stdout = -1; int prev_errno; char *sh, *shopt; enum { MY_FMODE_DEFAULT = 0, MY_FMODE_TEXT, MY_FMODE_BINARY } my_fmode = MY_FMODE_DEFAULT; char c; sh = my_getshell(&shopt); fmr[0] = fmr[1] = fmw[0] = fmw[1] = '\0'; if (!fmode || !(fmode[0] == 'r' || fmode[0] == 'w')) { errno = EINVAL; return -1; } while((c = *fmode++) != '\0') { switch(c) { case 'r': fmr[0] = c; break; case 'w': fmw[0] = c; break; case 'b': my_fmode = MY_FMODE_BINARY; break; case 't': my_fmode = MY_FMODE_TEXT; break; case '+': fmr[0] = 'r'; fmw[0] = 'w'; break; } } if (my_fmode != MY_FMODE_DEFAULT) { fmr[1] = fmw[1] = (my_fmode == MY_FMODE_TEXT) ? 't' : 'b'; fmr[2] = fmw[2] = '\0'; } if (*fmr) { if (pipe(pdr) < 0) return -1; if (fcntl(pdr[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(pdr[1], F_SETFD, FD_CLOEXEC) == -1) { return -1; } } if (*fmw) { if (pipe(pdw) < 0) return -1; if (fcntl(pdw[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(pdw[1], F_SETFD, FD_CLOEXEC) == -1) { return -1; } } /* ATOMIC 制御いれるならこのへんあたりから? */ if (*fmr) { /* parent pipe-in <- child stdout */ pm_stdout = fcntl(STDOUT_FILENO, F_GETFD, 0); ph_stdout = dup(STDOUT_FILENO); fcntl(ph_stdout, F_SETFD, FD_CLOEXEC); dup2(pdr[1], STDOUT_FILENO); close(pdr[1]); fr = fdopen(pdr[0], fmr); if (fr) setbuf(fr, NULL); } if (*fmw) { /* parent pipe-out -> child stdin */ pm_stdin = fcntl(STDIN_FILENO, F_GETFD, 0); ph_stdin = dup(STDIN_FILENO); fcntl(ph_stdin, F_SETFD, FD_CLOEXEC); dup2(pdw[0], STDIN_FILENO); close(pdw[0]); fw = fdopen(pdw[1], fmw); if (fw) setbuf(fw, NULL); } if (fr || fw) { pid_child = spawnlp(P_NOWAIT, sh, sh, shopt, cmd, (char *)NULL); } if (pid_child == -1) { if (fr) { fclose(fr); fr = NULL; } if (fw) { fclose(fw); fw = NULL; } } else { if (fr) fr->_pid = pid_child; if (fw) fw->_pid = pid_child; } prev_errno = errno; if (ph_stdin != -1) { dup2(ph_stdin, STDIN_FILENO); close(ph_stdin); if (pm_stdin != -1) fcntl(STDIN_FILENO, F_SETFD, pm_stdin); } if (ph_stdout != -1) { dup2(ph_stdout, STDOUT_FILENO); close(ph_stdout); if (pm_stdout != -1) fcntl(STDOUT_FILENO, F_SETFD, pm_stdout); } errno = prev_errno; /* このへんで ATOMIC 解除? */ if (fprd) *fprd = fr; if (fpwr) *fpwr = fw; return pid_child; } int emx_duplex_pclose(FILE *fr, FILE *fw) { FILE *f = fr; if (fr && fw) { if (fr->_pid != fw->_pid) { errno = EBADF; return -1; } f = fr; if (fclose(fw) == -1) return -1; } else if (fr != NULL) f = fr; else if (fw != NULL) f = fw; else { errno = EBADF; return -1; } return pclose(f); } #if defined(TEST) int main(int argc, char *argv[]) { int i, n; char *s; char buf[1024]; FILE *fr = NULL, *fw = NULL; if (argc <= 1) { fprintf(stderr, "dupopen cmd args...\n"); return 1; } n = 0; for(i=1; i<argc; i++) { n += strlen(argv[i]) + 1; } s = (char *)malloc(n); *s = '\0'; for(i=1; i<argc; i++) { strcat(s, argv[i]); strcat(s, " "); } fprintf(stderr, "dupopen... %s\n", s); if (emx_duplex_popen(s, "r+", &fr, &fw) == -1) { fprintf(stderr, "err.\n"); return 1; } fprintf(stderr, "put\n"); fprintf(fw, "AAAAA\n"); fprintf(stderr, "get\n"); fgets(buf, sizeof(buf), fr); printf("%s\n", buf); emx_duplex_pclose(fr, fw); return 0; } #endif
エラーチェックが微妙に甘めか?(pipe 作成のあたりとか)
(2006-01-19) emx の libc だとリードモードの pclose は waitpid してから fclose しているが、fclose してから waitpid のほうがいいかもしれない。 (APUE に載ってる実装――プログラム 14.5 だとリードの場合もライトの場合も fclose → waitpid の順番になってる) というか順番変えたら ruby の test-all 時に openssl のとこではまらなくなった。うひー。
(2006-01-19 その2) あんまり関係ないような気もする。 というかやはりはまった。ひぎぃ。