#!/usr/bin/env python # Programme to convert all single-line or multi-line "/* ... */" # comments to "//" form in C or C++ code. Input source code file is # stdin, output file is stdout. import sys import re # Restore default signal handling for SIGPIPE as suggested by # http://www.velocityreviews.com/forums/t332662-ioerror-errno-32-broken-pipe.html # so that stdout can be sent (unix-only) to a pipelined process that terminates # early such as cmp. import signal signal.signal(signal.SIGPIPE, signal.SIG_DFL) ifsingleline = True previous_continue = False for line in sys.stdin.readlines(): start_comment = line.find("/*") # FIXME?: the simple rules below to ignore all special strings # in a line where the first instance is quoted obviously # need generalization. However, second instances # of special strings are unlikely in practice so we will go with # this simple rule until it is proved something more is required. # Ignore all "/*" instances on a line where the first one is # quoted. if start_comment >=0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*/\*[^"]*"', line): start_comment = -1 start_special = line.find("//*") # FIXME? # Ignore all "//*" instances on a line where the first one is # quoted. if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//\*[^"]*"', line): start_special = -1 # if start_special corresponds to start_comment, then ignore start_comment # to deal with issue of recursive changes to an original line of the # form "/******..." if start_special >= 0 and start_special == start_comment -1: start_comment = -1 end_comment = line.find("*/") # FIXME? # Ignore all "*/" instances on a line where the first one is # quoted. if end_comment >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*\*/[^"]*"', line): end_comment = -1 start_special = line.find("//") # FIXME? # Ignore all "//" instances on a line where the first one is # quoted. if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//[^"]*"', line): start_special = -1 # If unquoted // before /* and not in the middle of a comment block, # then ignore both /* and */ beyond //. if ifsingleline and start_special >= 0 and start_special <= start_comment -1: start_comment = -1 end_comment = -1 # Note trailing "\n" has not (yet) been removed from line so # that the next to last character is at position len(line) - 3. if end_comment >=0 and end_comment != len(line) - 3: if ifsingleline and start_comment >=0: # Skip most further processing for a line with embedded # comments outside of multiline blocks of comments. start_comment = -1 end_comment = -1 else: sys.stderr.write(line) raise RuntimeError, "Cannot interpret trailing character(s) after */ for this line" # Note trailing "\n" has not (yet) been removed from line so # that the next to last character is at position len(line) - 3. # print "start_comment = ", start_comment # print "end_comment = ", end_comment # print "start_special = ", start_special if ifsingleline: if start_comment >=0 and start_comment < end_comment and end_comment == len(line) - 3: # Single-line comment case. # Strip trailing "\n" (Unix line endings, only) since that might # cause trouble with stripping trailing white space below. # This trailing "\n" will be added back at the end of this block. line = re.sub(r'^(.*)\n$', "\\1", line) # Convert single-line comment. # \1 corresponds to zero or more leading characters before "/*". # \3 corresponds to zero or more characters between "/*" and # "*/". # N.B. preserves indentation. line = re.sub(r'^(.*)(/\*)(.*)\*/$', "\\1//\\3", line) # strip trailing white space (if any). line = line.rstrip() # Add back (Unix-only) line ending. line = line + "\n" elif end_comment >=0: sys.stderr.write(line) raise RuntimeError, "Trailing */ for a line which is not a comment" elif start_comment >=0: # Convert first line of multiline comment. # N.B. preserves indentation. line = line.replace("/*", "//", 1) ifsingleline = False else: # Case where dealing with multi-line comment. if start_comment >=0: sys.stderr.write(line) raise RuntimeError, "/* in the middle of a comment block" # strip trailing "\n" (Unix line endings, only) since that might # cause trouble with stripping trailing white space below. # This trailing "\n" will be added back at the end of this block. line = re.sub(r'^(.*)\n$', "\\1", line) if end_comment < 0: # Convert multiline comment line that is not start line # or end line of that comment. # Replace " *" after zero or more blanks (the standard form # produced by uncrustify) if it is there by "//". # N.B. preserves indentation. if re.search(r'^( *) \*', line): line = re.sub(r'^( *) \*', "\\1//", line) else: # If standard indented form not found.... line = "//" + line else: # Convert last line of multiline comment. # Try converting vacuous form (initial blanks + " */") # to initial blanks (if any) + "//". # This consumes the blank in " */" that is customary as # the last line of a multi-line block. # N.B. preserves indentation. # \1 contains the initial blanks if re.search(r'^( *) \*/$', line): line = re.sub(r'^( *) \*/$', "\\1//", line) # Try converting non-vacuous standard form produced by # uncrustify (initial blanks + " *" + any string + "*/") # to initial blanks + "//" + any string. # N.B. preserves indentation. # \1 contains the initial blanks # \2 contains the "any" string preceding "*/". elif re.search(r'^( *) \*(.*)\*/$', line): line = re.sub(r'^( *) \*(.*)\*/$', "\\1//\\2", line) # If all previous conversion attempts failed.... # N.B. Does not preserve indentation. else: line = re.sub(r'^(.*)\*/$', "//\\1", line) # strip trailing white space (if any). line = line.rstrip() ifsingleline = True # Add back (Unix-only) line ending. line = line + "\n" # If previous comment continuation exists, check whether it is # valid, i.e., whether it is followed by another line consisting # zero or more blanks followed by a comment. if previous_continue: if re.search(r'^ *//', line): previous_continue = False else: sys.stderr.write(line_old) sys.stderr.write(line) raise RuntimeError, "Comment continuation not allowed unless next line is a comment" # End with some special processing for all lines which previously # or now include "//". start_special = line.find("//") if start_special >= 0: # Special transforms to get rid of left-over "\*" and "*\" # forms which have historically been used to frame # multi-block comments by some PLplot developers. # Replace leading "// \*" ==> "//" line = re.sub(r'^// \\\*(.*)$', "//\\1", line) # Remove "*\" from end of comment lines line = re.sub(r'^//(.*)\*\\$', "//\\1", line) # Convert long "horizontal line" comment forms to standard form. line = re.sub(r'^// *[-*=/+_\\# ]{50,200}$', "//--------------------------------------------------------------------------", line) # Look for trailing continuation after comment lines and # complain if you find any. start_special = line.rfind("\\") # Note line has trailing "\n" so that the last character # is at position len(line) - 2. if start_special >=0 and start_special == len(line) -2: # Strings are immutable so this is a copy not a reference. line_old = line previous_continue = True sys.stdout.write(line) if not ifsingleline: sys.stderr.write(line) raise RuntimeError, "Last line of file leaves unterminated comment block." if previous_continue: sys.stderr.write(line_old) raise RuntimeError, "Last line of file is a continued comment." sys.exit()