Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove code between #if 0 and #endif when exporting a C file to a new one

I want to remove all comments in a toy.c file. From Remove comments from C/C++ code I see that I could use

gcc -E -fpreprocessed -P -dD toy.c

But some of my code (say deprecated functions that I don't want to compile) are wrapped up between #if 0 and endif, as if they were commented out.

  • One one hand, the above command does not remove this type of "comment" because its removal is only possible during macro expansion, which -fpreprocessed prevents;
  • On the other hand, I have other macros I don't want to expand, so dropping -fpreprocessed is a bad idea.

I see a dilemma here. Is there a way out of this situation? Thanks.


The following toy example "toy.c" is sufficient to illustrate the problem.

#define foo 3  /* this is a macro */

// a toy function
int main (void) {
  return foo;
  }

// this is deprecated
#if 0
int main (void) {
  printf("%d\n", foo);
  return 0;
  }
#endif

gcc -E -fpreprocessed -P -dD toy.c gives

#define foo 3
int main (void) {
  return foo;
  }
#if 0
int main (void) {
  printf("%d\n", foo);
  return 0;
  }
#endif

while gcc -E -P toy.c gives

int main (void) {
  return 3;
  }
like image 415
Zheyuan Li Avatar asked Sep 09 '18 19:09

Zheyuan Li


2 Answers

There's a pair of programs, sunifdef ("Son of unifdef", which is available from unifdef) and coan, that can be used to do what you want. The question Is there a C pre-processor which eliminates #ifdef blocks based on values defined/undefined? has answers which discuss these programs.

For example, given "xyz37.c":

#define foo 3  /* this is a macro */

// a toy function
int main (void) {
  return foo;
  }

// this is deprecated
#if 0
int main (void) {
  printf("%d\n", foo);
  }
#endif

Using sunifdef

sunifdef -DDEFINED -ned < xyz37.c

gives

#define foo 3  /* this is a macro */

// a toy function
int main (void) {
  return foo;
  }

// this is deprecated

and given this file "xyz23.c":

#if 0
This is deleted
#else
This is not deleted
#endif

#if 0
Deleted
#endif

#if defined(XYZ)
XYZ is defined
#else
XYZ is not defined
#endif

#if 1
This is persistent
#else
This is inconsistent
#endif

The program

sunifdef -DDEFINE -ned < xyz23.c

gives

This is not deleted

#if defined(XYZ)
XYZ is defined
#else
XYZ is not defined
#endif

This is persistent

This is, I think, what you're after. The -DDEFINED options seems to be necessary; choose any name that you do not use in your code. You could use -UNEVER_DEFINE_THIS instead, if you prefer. The -ned option evaluates the constant terms and eliminates the relevant code. Without it, the constant terms like 0 and 1 are not eliminated.

I've used sunifdef happily for a number of years (encroaching on a decade). I've not yet found it to make a mistake, and I've used it to clean up some revoltingly abstruse sets of 'ifdeffery'. The program coan is a development of sunifdef with even more capabilities.

like image 182
Jonathan Leffler Avatar answered Nov 15 '22 13:11

Jonathan Leffler


The preprocessor doesn't make exceptions. You cannot use it here to do that.

A simple state machine using python can work. It even handles nesting (well, maybe not all cases are covered like nested #if 0 but you can compare the source before & after and manually validate). Also commented code isn't supported (but it seems that you have it covered)

the input (slightly more complex than yours for the demo):

#define foo 3
int main (void) {
  return foo;
  }
#if 0
int main (void) {
  #ifdef DDD
  printf("%d\n", foo);
  #endif
  }
#endif

void other_function()
{}

now the code, using regexes to detect #if & #endif.

import re
rif0 = re.compile("\s*#if\s+0")
rif = re.compile("\s*#(if|ifn?def)")
endif = re.compile("\s*#endif")

if_nesting = 0
if0_nesting = 0
suppress = False

with open("input.c") as fin, open("output.c","w") as fout:
    for l in fin:
        if rif.match(l):
            if_nesting += 1
            if rif0.match(l):
                suppress = True
                if0_nesting = if_nesting
        elif endif.match(l):
            if if0_nesting == if_nesting:
                suppress = False
            if_nesting -= 1
            continue  # don't write the #endif

        if not suppress:
            fout.write(l))

the output file contains:

#define foo 3
int main (void) {
  return foo;
  }

void other_function()
{}

so the nesting worked and the #if 0 part was successfully removed. Not something that sed "/#if 0/,/#endif/d can achieve.

like image 35
Jean-François Fabre Avatar answered Nov 15 '22 14:11

Jean-François Fabre