- In
pipe_parse
, the definition of prev_pipefd
is misplaced.
- Because it is loop scoped, it is being reset to -1 on each loop iteration.
- Thus, any changes to it from
exec_command
are lost.
To fix, move prev_pipefd
to function scope [similar to pipes_executed
].
For an example of a working shell pipe implementation, see my answer: fd leak, custom Shell
UPDATE:
I moved prev_pipefd (and pipefd after that as well) to function scope, no changes in the output when I enter a pipe command. Although I can see how the pipes creations could mess up when they was inside the loop. Is it possible I have unrelated other problem now? –
EladO O
Okay, with the full code, a number of issues ...
- In
pipe_parse
, there is only one pipe
call [done before the loop].
- This
pipe
call must be moved into the loop [just before the exec_command
].
- In
exec_command
, doing waitpid
before all pipe children have been forked/constructed is bad news.
- Installing the
SIGCHLD
[or any other] signal handler using signal
prevents multiple signals from occuring [AFAICT]. Use sigaction
instead.
- In
get_input
, getting a SIGCHLD
during fgets
will cause it to return NULL
with errno
set to EINTR
. The buffer will be unchanged and we'll loop infinitely on the same [stale] command. Must check for this and reloop.
- In
parse_input
, ampersand_flag
must be globa/file scope, so pipe_parse
can use it.
- Because
sig_child_handler
does a waitpid
, it can change errno
. This would be disruptive to the base level, so the handler should save/restore the original value of errno
.
- I/O redirection only works if not within a pipe. (e.g.)
cat /etc/passwd > /tmp/out
works but cat /etc/passwd | cat > /tmp/out
doesn't [not fixed below].
- Doing
for (int i = 0; i < strlen(str); i++)
is O(n^2) instead of O(n). Replace with for (int i = 0; str[i] != 0; i++)
- IMO, "sidebar" comments (e.g.
x = 5; // set the value
) are less readable except for struct
members.
- Various additional cleanups.
UPDATE #2:
Hey, first of all - thank you for your time and effort making changes to my program! Now, I cleaned the file with unifdef and ran it but I still can't use any pipe command. for example: "ps|wc" won't do anything besides printing "parent process" and giving me the prompt to next command input..
Okay, my bad. I didn't test this as thoroughly as I would have liked. The issue was the for
loop [in pipe_parse
]. Originally, I thought I had broken working code, but it turns out that your original loop had a bug.
Your original:
for (int i = 0, j = 0; i < strlen(str); ++i) {
The change I made:
for (int i = 0, j = 0; str[i] != 0; ++i) {
Your original was looping for one less than it should. In both cases above, exec_command
would never get the final pipe command.
Here is the [truly ;-)] fixed version:
int len = strlen(str);
for (int i = 0, j = 0; i <= len; ++i) {
I've added this change to the code below.
as for the rest of the changes you made - some of them aren't really relevant due to how our lecturer wanted us to write the assignment, but still, thank you! Am I missing something regarding the pipe feature? –
EladO O
Yes, I did some [style] cleanup. And, added some debug code. Aside from that, most of the changes were necessary. But, as far as that goes, just how did they conflict with how your lecturer wanted you to write the assignment?
Here is the corrected code. It is annotated with the bugs and fixes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if 1
#include <stdarg.h>
#include <errno.h>
#endif
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#define MAX_INPUT_LENGTH 510
#if 0
#define MAX_ARGS 10
#else
#define MAX_ARGS 30
#define MAX_INPUT_LENGTH_PLUS5 (MAX_INPUT_LENGTH + 5)
#endif
// Using LinkedList to store vars
typedef struct node {
char *name;
char *value;
struct node *next;
} Node;
typedef struct linked_list {
Node *head;
} LinkedList;
int get_input(char *input);
void parse_input(char *input, char **args, LinkedList *vars, int *stopped_process_pid);
void exec_command(char **args, int ampersand, char *file_name, int pipe_num, int pipe_amount, int *pipefd, int *prev_pipefd);
char *replace_vars(char *input, LinkedList *vars);
void quots_remover(char *str);
int is_echo(char *str);
int is_CD(char *str);
int is_BG(char *str);
int is_redirection(char *str);
int is_pipe(char *str);
void pipe_parse(char **args, char *str, int pipes_amount);
int have_illegal_spaces(char *str);
void spaces_remover(char *str);
void prompt_printer(int legCmds, int args);
void splitToEqual(const char *str, char *left_string, char *right_string);
int ampersand_finder_remover(char *str);
void initialize(LinkedList *list);
void insert(LinkedList *list, char *name, char *value);
Node *search(LinkedList *list, char *name);
void terminate(LinkedList *list);
void sig_child_handler(int n);
void father_handler(int n);
int pidd = 0;
#if 0
int last_pid = 0;
#else
volatile int last_pid = 0;
#endif
int redirect_flag = 0;
int legalArgs = 0,
legalCmds = 0;
#if 1
int ampersand_flag;
#endif
#if DEBUG
#define dbgprt(_fmt...) \
do { \
_dbgprt(_fmt); \
} while (0)
#else
#define dbgprt(_fmt...) \
do { \
} while (0)
#endif
#define dbgprtattr(_lvl) \
__attribute__((__format__(__printf__,_lvl,_lvl + 1)))
// signal-safe(?) debug printf
void dbgprtattr(1)
_dbgprt(const char *fmt,...)
{
va_list ap;
char buf[1000];
int sverr = errno;
va_start(ap,fmt);
int len = vsprintf(buf,fmt,ap);
va_end(ap);
write(2,buf,len);
errno = sverr;
}
void
xsignal(int signo,void *hdr)
{
#if 0
// NOTE/BUG: this is a one shot -- we need to catch _multiple_ signals
signal(signo,hdr);
#else
struct sigaction act;
sigaction(signo,NULL,&act);
act.sa_flags |= SA_SIGINFO;
act.sa_sigaction = hdr;
sigemptyset(&act.sa_mask);
sigaction(signo,&act,NULL);
#endif
}
int
main()
{
xsignal(SIGCHLD, sig_child_handler);
int noCmdCounter = 0,
inputStatus,
stopped_process_pid = 0;
#if 0
char input[MAX_INPUT_LENGTH + 5];
#else
char input[MAX_INPUT_LENGTH_PLUS5];
#endif
char *args[MAX_ARGS];
#if 1
setlinebuf(stderr);
#endif
for (int i = 0; i < MAX_ARGS; ++i) {
args[i] = "\0";
}
// int* pidd = malloc(sizeof(int));
LinkedList vars;
initialize(&vars);
while (1) {
#if 1
// reap detached jobs
// FIXME/CAE -- may not be necessary with fixed SIGCHLD handler
#if 1
while (1) {
pid_t pid = waitpid(-1,NULL,WNOHANG);
if (pid <= 0)
break;
dbgprt("main: WAIT pid=%d\n",pid);
}
#endif
#endif
// prompt
prompt_printer(legalCmds, legalArgs);
inputStatus = get_input(input);
// input len is greater than max
if (inputStatus == 0) {
printf("ERR\n");
continue;
}
// user pressed enter 3 times in a row
else if (inputStatus == -1) {
noCmdCounter++;
if (noCmdCounter == 3)
break;
continue;
}
// legal input
else {
noCmdCounter = 0;
parse_input(input, args, &vars, &stopped_process_pid);
}
}
terminate(&vars);
}
// function that gets command input from the user
int
get_input(char *input)
{
// printf("student@student-virtual-machine ~/ex1 $ ");
#if 1
fflush(stdout);
#endif
#if 0
// NOTE/BUG: with SIGCHLD handler, this can get EINTR and buffer is _not_
// changed, so we loop infinitely on last command
fgets(input, MAX_INPUT_LENGTH_PLUS5, stdin);
#else
while (1) {
if (fgets(input, MAX_INPUT_LENGTH + 5, stdin) != NULL)
break;
if (errno != EINTR)
return -1;
dbgprt("get_input: EINTR\n");
}
#endif
// replaces last char (new-line char) to NULL terminator
input[strcspn(input, "\n")] = '\0';
// if illegal input length
if (strlen(input) > MAX_INPUT_LENGTH)
return 0;
// if no cmd entered
else if (strlen(input) == 0)
return -1;
// if legal input length
return 1;
}
// function that divides the commands and their arguments
void
parse_input(char *input, char **args, LinkedList *vars,
int *stopped_process_pid)
{
#if 0
char temp_str[520], temp_arg[520];
#else
char temp_str[MAX_INPUT_LENGTH + 1], temp_arg[MAX_INPUT_LENGTH + 1];
#endif
char *token;
int input_len = strlen(input),
equal_val = '=',
idx,
quot_counter = 0,
#if 0
// NOTE/BUG: this needs to be global
ampersand_flag,
#endif
pipe_counter = 0;
dbgprt("parse_input: ENTER\n");
// ----------------------copying each command seperated by
// ';'-----------------------------------
// copying each command seperated by ';'
for (int i = 0, j = 0; i <= input_len; ++i) {
dbgprt("parse_input: CHAR i=%d j=%d chr='%c'\n",i,j,input[i]);
if (input[i] == '\"') {
if (quot_counter == 1)
quot_counter = 0;
else
quot_counter = 1;
}
// end of command
if ((input[i] == ';' || input[i] == '\0') && quot_counter == 0) {
char *file_name;
temp_str[j] = '\0';
redirect_flag = 0;
ampersand_flag = 0;
ampersand_flag = ampersand_finder_remover(temp_str);
quot_counter = 0;
dbgprt("command is : %s\n", temp_str);
j = 0;
// ******************working on each command***********************
pipe_counter = is_pipe(temp_str);
if (pipe_counter > 0) {
pipe_parse(args, temp_str, pipe_counter);
continue;
}
// redirection
if (is_redirection(temp_str) == 1) {
redirect_flag = 1;
// holds the cmd
char *command_to_redirect = strtok(temp_str, ">");
// holds the file name
file_name = strtok(NULL, ">");
spaces_remover(file_name);
dbgprt("@%s@\t@%s@\n", command_to_redirect, file_name);
dbgprt("--%s--\n", temp_str);
}
if (is_CD(temp_str) == 1) {
printf("CD not supported!\n");
continue;
}
// 'echo' cmd
if (is_echo(temp_str) == 1 && pipe_counter == 0) {
token = strtok(temp_str, " ");
// inserting "echo" to first exec array element
args[0] = token;
token = strtok(NULL, "\0");
// inserting echo's argument into second exec array element
args[1] = token;
args[2] = "\0";
for (int k = 0; k < MAX_ARGS; ++k) {
if (k != 0 && k != 1)
args[k] = '\0';
if (k == 1) {
strcpy(temp_arg, replace_vars(args[1], vars));
quots_remover(temp_arg);
args[1] = temp_arg;
}
}
}
// if cmd has '=' then it's a variable cmd
else if (strchr(temp_str, equal_val) != NULL && pipe_counter == 0) {
char temp_str2[520];
strcpy(temp_str2, temp_str);
if (have_illegal_spaces(temp_str2) == 0) {
char left[520],
right[520];
// left/right holds the two parts of the variable
splitToEqual(temp_str, left, right);
char var_name[520];
strcpy(var_name, left);
spaces_remover(var_name);
char var_value[520];
strcpy(var_value, right);
quots_remover(var_value);
insert(vars, var_name, var_value);
continue;
}
}
else if (is_BG(temp_str) == 1 && pipe_counter == 0) {
kill(last_pid, SIGCONT);
continue;
}
// regular command
else {
idx = 0;
char *p = (char *) malloc(sizeof(char *));
p = replace_vars(temp_str, vars);
strcpy(temp_arg, p);
token = strtok(temp_arg, " ");
while (token != NULL) {
spaces_remover(token);
args[idx++] = token;
token = strtok(NULL, " ");
}
while (idx < MAX_ARGS) {
args[idx++] = NULL;
}
}
if (redirect_flag == 1) {
exec_command(args, ampersand_flag, file_name, 0, 0, NULL, NULL);
continue;
}
exec_command(args, ampersand_flag, NULL, 0, 0, NULL, NULL);
}
// ***************************************************************
// copying next letter
else {
temp_str[j] = input[i];
j++;
}
}
dbgprt("parse_input: EXIT\n");
// ------------------------------------------------------------------------
}
void
printArr(char **arr)
{
dbgprt("printArr:\n");
for (int i = 0; arr[i] != NULL; i++)
dbgprt("%s\n", arr[i]);
}
// function that creates a son process and sends the command to execvp
void
exec_command(char **args, int ampersand, char *file_name,
int pipe_num, int pipe_amount, int *pipefd, int *prev_pipefd)
{
int status;
dbgprt("exec_command: ENTER ampersand=%d file_name='%s' pipe_num=%d pipe_amount=%d pipefd=%p prev_pipefd=%p\n",
ampersand,file_name,pipe_num,pipe_amount,pipefd,prev_pipefd);
for (int i = 0; i < MAX_ARGS; ++i) {
if (args[i] == NULL)
continue;
dbgprt("exec_command: ARGV/%d args='%s'\n",i,args[i]);
}
pidd = fork();
// child
if (pidd == 0) {
xsignal(SIGTSTP, SIG_DFL);
if (redirect_flag == 1) {
int fd = open(file_name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO);
if (fd == -1)
perror("ERR file");
}
if (pipe_amount != 0) {
// first pipe
if (pipe_num == 0) {
dbgprt("first pipe\n");
// close read
close(pipefd[0]);
// redirect stdout to write
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
perror("pipe 1 dup");
exit(EXIT_FAILURE);
}
}
// last pipe
else if (pipe_num == pipe_amount) {
dbgprt("last pipe\n");
// close unused write end of previous pipe
close(prev_pipefd[1]);
// redirect stdin to read end of previous pipe
if (dup2(prev_pipefd[0], STDIN_FILENO) == -1) {
perror("pipe 3 dup");
exit(EXIT_FAILURE);
}
// close unused read end of pipe
close(pipefd[0]);
}
// mid pipes
else {
dbgprt("mid pipe\n");
// close unused write end of previous pipe
close(prev_pipefd[1]);
// redirect stdin to read end of previous pipe
dup2(prev_pipefd[0], STDIN_FILENO);
// close unused read end of pipe
close(pipefd[0]);
// redirect stdout to write end of pipe
dup2(pipefd[1], STDOUT_FILENO);
}
}
execvp(args[0], args);
printf("after exec\n");
perror("ERR0");
exit(1);
}
// error
else if (pidd < 0) {
perror("ERR1\n");
exit(2);
}
// parent
else {
if (pipe_amount != 0) {
if (prev_pipefd[0] != -1)
close(prev_pipefd[0]);
if (prev_pipefd[1] != -1)
close(prev_pipefd[1]);
prev_pipefd[0] = pipefd[0];
prev_pipefd[1] = pipefd[1];
printf("parent process\n");
}
xsignal(SIGTSTP, father_handler);
#if 0
// NOTE/BUG: parent must _not_ wait until all pipe sections are created
if (ampersand == 0)
waitpid(pidd, &status, WUNTRACED);
#endif
// inc cmds counter
legalCmds += 1;
// inc args counter
for (int i = 0; i < MAX_ARGS; ++i)
if (args[i] != NULL)
legalArgs += 1;
}
dbgprt("exec_command: EXIT\n");
// if (prev_pipefd[0] != -1) close(prev_pipefd[0]);
// if (prev_pipefd[1] != -1) close(prev_pipefd[1]);
}
// function that removes quots from a string
void
quots_remover(char *str)
{
int len = strlen(str);
int i, j;
for (i = 0, j = 0; i < len; i++)
if (str[i] != '\"')
str[j++] = str[i];
str[j] = '\0';
}
// function that checks if it's an echo command
int
is_echo(char *str)
{
int counter = 0;
for (int i = 0; str[i] != 0; ++i) {
if (str[i] == 'e') {
counter++;
continue;
}
if (str[i] == 'c' && counter == 1) {
counter++;
continue;
}
if (str[i] == 'h' && counter == 2) {
counter++;
continue;
}
if (str[i] == 'o' && counter == 3) {
counter++;
break;
}
counter = 0;
}
if (counter == 4)
return 1;
return 0;
}
int
is_CD(char *str)
{
int counter = 0;
for (int i = 0; str[i] != 0; ++i) {
if (str[i] == 'c') {
counter++;
continue;
}
if (str[i] == 'd' && counter == 1) {
counter++;
break;
}
counter = 0;
}
if (counter == 2)
return 1;
return 0;
}
int
is_BG(char *str)
{
int counter = 0;
for (int i = 0; str[i] != 0; ++i) {
if (str[i] == 'b') {
counter++;
continue;
}
if (str[i] == 'g' && counter == 1) {
counter++;
break;
}
counter = 0;
}
if (counter == 2)
return 1;
return 0;
}
// function that determines how many pipes the command have, if any
int
is_pipe(char *str)
{
int counter = 0;
for (int i = 0; str[i] != 0; ++i)
if (str[i] == '|')
counter++;
return counter;
}
// function that parses the command by pipes
void
pipe_parse(char **args, char *str, int pipes_amount)
{
char curr_cmd[MAX_INPUT_LENGTH];
for (int i = 0; i < MAX_INPUT_LENGTH; ++i)
curr_cmd[i] = '\0';
int pipes_executed = 0;
char *token;
int pipefd[2];
int prev_pipefd[2] = { -1, -1 };
dbgprt("pipe_parse: ENTER\n");
#if 0
// NOTE/BUG: we need a new pipe for each pipe stage
if (pipe(pipefd) == -1) {
perror("ERR pipe");
exit(1);
}
#endif
#if 0
// Elad's original code
// NOTE/BUG: this is looping one too few
for (int i = 0, j = 0; i < strlen(str); ++i) {
#endif
#if 0
// Craig's original change
for (int i = 0, j = 0; str[i] != 0; ++i) {
#endif
#if 1
// corrected loop
int len = strlen(str);
for (int i = 0, j = 0; i <= len; ++i) {
#endif
if (str[i] == '|' || str[i] == '\0') {
curr_cmd[j] = '\0';
j = 0;
dbgprt("pipe_parse: CURR curr_cmd='%s'\n",curr_cmd);
// printf("@%s@\n", curr_cmd);
token = strtok(curr_cmd, " ");
args[0] = token;
int k = 0;
for (; token != NULL; k++, token = strtok(NULL, " "))
args[k] = token;
for (; k < MAX_ARGS; k++)
args[k] = NULL;
#if 1
// NOTE/FIX: we need a new pipe for each pipe stage
if (pipe(pipefd) == -1) {
perror("ERR pipe");
exit(1);
}
#endif
exec_command(args, 0, NULL, pipes_executed, pipes_amount, pipefd, prev_pipefd);
pipes_executed++;
}
else {
curr_cmd[j++] = str[i];
}
}
#if 1
// reap foreground pipe jobs
if (ampersand_flag == 0) {
while (1) {
pid_t pid = waitpid(-1, NULL, WUNTRACED);
if (pid <= 0)
break;
dbgprt("pipe_parse: WAIT pid=%d\n",pid);
}
}
#endif
dbgprt("pipe_parse: EXIT\n");
}
// function that checks if an environmental variable command has illegal spaces
int
have_illegal_spaces(char *str)
{
char *temp = str;
char *toke = strtok(temp, "=");
int flag = 0;
for (int i = 0; toke[i] != 0; ++i) {
if (temp[i] != ' ') {
flag = 1;
}
if (temp[i] == ' ' && flag != 0)
return 1;
}
return 0;
}
// function that removes spaces
void
spaces_remover(char *str)
{
int len = strlen(str);
int i,
j;
for (i = 0, j = 0; i < len; i++)
if (str[i] != ' ')
str[j++] = str[i];
str[j] = '\0';
}
// function that prints the prompt line
void
prompt_printer(int legCmds, int args)
{
char cwd[256];
if (getcwd(cwd, sizeof(cwd)) == NULL)
// TODO: CHANGE TO WHAT?
printf("ERR getcwd");
else
printf("#cmd:%d|#args:%d@%s ", legCmds, args, cwd);
#if 1
fflush(stdout);
#endif
}
// function that replaces all variables calls with their values
char *
replace_vars(char *input, LinkedList *vars)
{
char *temp = (char *) malloc(520);
// TODO: add * before temp inside sizeof ??
memset(temp, '\0', sizeof(*temp));
char name_temp[520];
memset(name_temp, '\0', sizeof(name_temp));
int flag = 0,
tempIdx = 0,
nameIdx = 0;
char c[2];
c[1] = 0;
#if 0
for (int i = 0; i < strlen(input) + 1; ++i) {
#else
int len = strlen(input);
for (int i = 0; i < len + 1; ++i) {
#endif
if (input[i] == '$')
flag = 1;
// check for var in list
else if (input[i] == ' ' || input[i] == '\0' || input[i] == '\"') {
flag = 0;
Node *temp_node = search(vars, name_temp);
// var exist
if (temp_node != NULL) {
strcat(temp, temp_node->value);
}
// var doesn't exist
else {
strcat(temp, " ");
}
memset(name_temp, '\0', sizeof(name_temp));
if (input[i] == ' ') {
strcat(temp, " ");
}
}
// copy char
else if (input[i] != ' ') {
sprintf(c, "%c", input[i]);
// printf("%c\n", c[0]);
// copy to name
if (flag == 1) {
strcat(name_temp, c);
}
// copy to temp
else {
strcat(temp, c);
}
}
}
return temp;
}
// function that splits the environmental variable
void
splitToEqual(const char *str, char *left, char *right)
{
memset(left, '\0', MAX_INPUT_LENGTH);
memset(right, '\0', MAX_INPUT_LENGTH);
int flag = 0,
j = 0;
for (int i = 0; str[i] != 0; ++i) {
if (str[i] == '=') {
flag = 1, j = 0;
}
else {
if (flag == 0)
left[j++] = str[i];
else
right[j++] = str[i];
}
}
}
void
sig_child_handler(int n)
{
#if 0
// NOTE/BUG: try to reap more
waitpid(-1, NULL, WNOHANG);
#else
int sverr = errno;
while (1) {
pid_t pid = waitpid(-1, NULL, WNOHANG);
if (pid <= 0)
break;
dbgprt("sig_child_handler: pid=%d\n",pid);
}
errno = sverr;
#endif
}
void
father_handler(int n)
{
last_pid = pidd;
}
// function that looks if command has ampersand, if it does it'll delete it
int
ampersand_finder_remover(char *str)
{
int i,
j;
int insideQuotes = 0;
int deleted = 0;
for (i = 0, j = 0; str[i]; i++) {
if (str[i] == '\"') {
// toggle flag
insideQuotes = !insideQuotes;
}
else if (str[i] == '&' && !insideQuotes) {
deleted = 1;
// skip ampersand if not inside quotes
continue;
}
else {
str[j++] = str[i];
}
}
str[j] = '\0';
return deleted;
}
// function that checks for '>' char and determines if the cmd is a concat cmd
int
is_redirection(char *str)
{
for (int i = 0; str[i] != 0; ++i)
if (str[i] == '>')
return 1;
return 0;
}
// Function to initialize the linked list
void
initialize(LinkedList *list)
{
list->head = NULL;
}
// function to insert a new node into the linked list
void
insert(LinkedList *list, char *name, char *value)
{
Node *new_node = malloc(sizeof(Node));
if (new_node == NULL) {
printf("ERR");
exit(1);
}
new_node->name = strdup(name);
new_node->value = strdup(value);
new_node->next = NULL;
// check if list is empty
if (list->head == NULL) {
list->head = new_node;
}
// search the node with the same name
else {
Node *current_node = list->head;
while (current_node != NULL) {
if (strcmp(current_node->name, name) == 0) {
// replace the existing node with the new node
free(current_node->value);
current_node->value = strdup(value);
free(new_node->name);
free(new_node->value);
free(new_node);
return;
}
current_node = current_node->next;
}
// Insert the new node at the end of the list
current_node = list->head;
while (current_node->next != NULL) {
current_node = current_node->next;
}
current_node->next = new_node;
}
}
// Function to search for a node with a given name
Node *
search(LinkedList *list, char *name)
{
Node *current_node = list->head;
while (current_node != NULL) {
if (strcmp(current_node->name, name) == 0) {
return current_node;
}
current_node = current_node->next;
}
return NULL;
}
// function to terminate the linked list and free all memory
void
terminate(LinkedList *list)
{
Node *current_node = list->head;
while (current_node != NULL) {
Node *next_node = current_node->next;
free(current_node->name);
free(current_node->value);
free(current_node);
current_node = next_node;
}
list->head = NULL;
}
In the code above, I've used cpp
conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Note: this can be cleaned up by running the file through unifdef -k