fped/cpp.c

218 lines
3.8 KiB
C

/*
* cpp.c - CPP subprocess
*
* Written 2002-2004, 2006, 2008 by Werner Almesberger
* Copyright 2002, 2003 California Institute of Technology
* Copyright 2004, 2006 Werner Almesberger
* Copyright 2008 by OpenMoko, Inc.
*
* 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "cpp.h"
const char *cpp_command = CPP;
static pid_t cpp_pid;
static int cpp_argc = 0;
static const char **cpp_argv = NULL;
static int real_stdin = -1;
void add_cpp_arg(const char *arg)
{
if (!cpp_argc)
cpp_argc = 1;
cpp_argv = realloc(cpp_argv,sizeof(const char *)*(cpp_argc+1));
if (!cpp_argv) {
perror("realloc");
exit(1);
}
if (cpp_argc == 1)
cpp_argv[0] = cpp_command;
if (arg) {
arg = strdup(arg);
if (!arg) {
perror("strdup");
exit(1);
}
}
cpp_argv[cpp_argc++] = arg;
}
void add_cpp_Wp(const char *arg)
{
char *tmp = strdup(arg);
char *curr,*end;
if (!tmp) {
perror("strdup");
exit(1);
}
curr = tmp;
do {
end = strchr(curr,',');
if (end)
*end++ = 0;
add_cpp_arg(curr);
curr = end;
}
while (end);
free(tmp);
}
static void kill_cpp(void)
{
if (cpp_pid)
(void) kill(cpp_pid,SIGTERM);
}
static void run_cpp(const char *name,int fd,int close_fd)
{
char **arg;
int fds[2];
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
if (name)
add_cpp_arg(name);
add_cpp_arg(NULL);
cpp_pid = fork();
if (cpp_pid < 0) {
perror("fork");
exit(1);
}
if (!cpp_pid) {
if (close(fds[0]) < 0) {
perror("close");
exit(1);
}
if (close_fd != -1 && close(close_fd) < 0) {
perror("close");
exit(1);
}
if (fd != -1 && dup2(fd,0) < 0) {
perror("dup2");
exit(1);
}
if (dup2(fds[1],1) < 0) {
perror("dup2");
exit(1);
}
if (execvp(cpp_command,(char **) cpp_argv) < 0) {
/* prototype is weird */
perror(cpp_command);
exit(1);
}
/* not reached */
}
if (close(fds[1]) < 0) {
perror("close");
exit(1);
}
real_stdin = dup(0);
if (real_stdin < 0) {
perror("dup");
exit(1);
}
if (fd != -1 && close(fd) < 0) {
perror("close");
exit(1);
}
if (dup2(fds[0],0) < 0) {
perror("dup2");
exit(1);
}
for (arg = (char **) cpp_argv+1; *arg; arg++)
free(*arg);
free(cpp_argv);
cpp_argv = NULL;
cpp_argc = 0;
}
void run_cpp_on_file(const char *name)
{
run_cpp(name,name ? -1 : 0,-1);
atexit(kill_cpp);
}
void run_cpp_on_string(const char *str)
{
int fds[2];
pid_t pid;
int left,wrote;
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
run_cpp(NULL,fds[0],fds[1]);
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (!pid) {
for (left = strlen(str); left; left -= wrote) {
wrote = write(fds[1],str,left);
if (wrote < 0)
break; /* die silently */
str += wrote;
}
exit(0);
}
if (close(fds[1]) < 0) {
perror("close");
exit(1);
}
atexit(kill_cpp);
}
void reap_cpp(void)
{
int status;
cpp_pid = 0;
if (waitpid(cpp_pid,&status,0) < 0) {
perror("waitpid");
exit(1);
}
if (!status) {
if (dup2(real_stdin,0) < 0) {
perror("dup2");
exit(1);
}
return;
}
if (WIFEXITED(status))
exit(WEXITSTATUS(status));
if (WIFSIGNALED(status))
fprintf(stderr,"cpp terminated with signal %d\n",WTERMSIG(status));
else
fprintf(stderr,"cpp terminated with incomprehensible status %d\n",
status);
exit(1);
}