1#!/usr/bin/env python 2 3# Programme to convert all single-line or multi-line "/* ... */" 4# comments to "//" form in C or C++ code. Input source code file is 5# stdin, output file is stdout. 6import sys 7import re 8# Restore default signal handling for SIGPIPE as suggested by 9# http://www.velocityreviews.com/forums/t332662-ioerror-errno-32-broken-pipe.html 10# so that stdout can be sent (unix-only) to a pipelined process that terminates 11# early such as cmp. 12import signal 13signal.signal(signal.SIGPIPE, signal.SIG_DFL) 14 15ifsingleline = True 16previous_continue = False 17 18for line in sys.stdin.readlines(): 19 start_comment = line.find("/*") 20 # FIXME?: the simple rules below to ignore all special strings 21 # in a line where the first instance is quoted obviously 22 # need generalization. However, second instances 23 # of special strings are unlikely in practice so we will go with 24 # this simple rule until it is proved something more is required. 25 # Ignore all "/*" instances on a line where the first one is 26 # quoted. 27 if start_comment >=0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*/\*[^"]*"', line): 28 start_comment = -1 29 30 start_special = line.find("//*") 31 # FIXME? 32 # Ignore all "//*" instances on a line where the first one is 33 # quoted. 34 if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//\*[^"]*"', line): 35 start_special = -1 36 37 # if start_special corresponds to start_comment, then ignore start_comment 38 # to deal with issue of recursive changes to an original line of the 39 # form "/******..." 40 if start_special >= 0 and start_special == start_comment -1: 41 start_comment = -1 42 43 end_comment = line.find("*/") 44 # FIXME? 45 # Ignore all "*/" instances on a line where the first one is 46 # quoted. 47 if end_comment >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*\*/[^"]*"', line): 48 end_comment = -1 49 50 start_special = line.find("//") 51 # FIXME? 52 # Ignore all "//" instances on a line where the first one is 53 # quoted. 54 if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//[^"]*"', line): 55 start_special = -1 56 # If unquoted // before /* and not in the middle of a comment block, 57 # then ignore both /* and */ beyond //. 58 if ifsingleline and start_special >= 0 and start_special <= start_comment -1: 59 start_comment = -1 60 end_comment = -1 61 62 # Note trailing "\n" has not (yet) been removed from line so 63 # that the next to last character is at position len(line) - 3. 64 if end_comment >=0 and end_comment != len(line) - 3: 65 if ifsingleline and start_comment >=0: 66 # Skip most further processing for a line with embedded 67 # comments outside of multiline blocks of comments. 68 start_comment = -1 69 end_comment = -1 70 else: 71 sys.stderr.write(line) 72 raise RuntimeError, "Cannot interpret trailing character(s) after */ for this line" 73 74 # Note trailing "\n" has not (yet) been removed from line so 75 # that the next to last character is at position len(line) - 3. 76 # print "start_comment = ", start_comment 77 # print "end_comment = ", end_comment 78 # print "start_special = ", start_special 79 if ifsingleline: 80 if start_comment >=0 and start_comment < end_comment and end_comment == len(line) - 3: 81 # Single-line comment case. 82 # Strip trailing "\n" (Unix line endings, only) since that might 83 # cause trouble with stripping trailing white space below. 84 # This trailing "\n" will be added back at the end of this block. 85 line = re.sub(r'^(.*)\n$', "\\1", line) 86 # Convert single-line comment. 87 # \1 corresponds to zero or more leading characters before "/*". 88 # \3 corresponds to zero or more characters between "/*" and 89 # "*/". 90 # N.B. preserves indentation. 91 line = re.sub(r'^(.*)(/\*)(.*)\*/$', "\\1//\\3", line) 92 # strip trailing white space (if any). 93 line = line.rstrip() 94 # Add back (Unix-only) line ending. 95 line = line + "\n" 96 97 elif end_comment >=0: 98 sys.stderr.write(line) 99 raise RuntimeError, "Trailing */ for a line which is not a comment" 100 101 elif start_comment >=0: 102 # Convert first line of multiline comment. 103 # N.B. preserves indentation. 104 line = line.replace("/*", "//", 1) 105 ifsingleline = False 106 107 108 else: 109 # Case where dealing with multi-line comment. 110 if start_comment >=0: 111 sys.stderr.write(line) 112 raise RuntimeError, "/* in the middle of a comment block" 113 114 # strip trailing "\n" (Unix line endings, only) since that might 115 # cause trouble with stripping trailing white space below. 116 # This trailing "\n" will be added back at the end of this block. 117 line = re.sub(r'^(.*)\n$', "\\1", line) 118 if end_comment < 0: 119 # Convert multiline comment line that is not start line 120 # or end line of that comment. 121 # Replace " *" after zero or more blanks (the standard form 122 # produced by uncrustify) if it is there by "//". 123 # N.B. preserves indentation. 124 if re.search(r'^( *) \*', line): 125 line = re.sub(r'^( *) \*', "\\1//", line) 126 else: 127 # If standard indented form not found.... 128 line = "//" + line 129 130 131 else: 132 # Convert last line of multiline comment. 133 # Try converting vacuous form (initial blanks + " */") 134 # to initial blanks (if any) + "//". 135 # This consumes the blank in " */" that is customary as 136 # the last line of a multi-line block. 137 # N.B. preserves indentation. 138 # \1 contains the initial blanks 139 if re.search(r'^( *) \*/$', line): 140 line = re.sub(r'^( *) \*/$', "\\1//", line) 141 142 # Try converting non-vacuous standard form produced by 143 # uncrustify (initial blanks + " *" + any string + "*/") 144 # to initial blanks + "//" + any string. 145 # N.B. preserves indentation. 146 # \1 contains the initial blanks 147 # \2 contains the "any" string preceding "*/". 148 elif re.search(r'^( *) \*(.*)\*/$', line): 149 line = re.sub(r'^( *) \*(.*)\*/$', "\\1//\\2", line) 150 151 # If all previous conversion attempts failed.... 152 # N.B. Does not preserve indentation. 153 else: 154 line = re.sub(r'^(.*)\*/$', "//\\1", line) 155 156 # strip trailing white space (if any). 157 line = line.rstrip() 158 ifsingleline = True 159 160 # Add back (Unix-only) line ending. 161 line = line + "\n" 162 163 # If previous comment continuation exists, check whether it is 164 # valid, i.e., whether it is followed by another line consisting 165 # zero or more blanks followed by a comment. 166 if previous_continue: 167 if re.search(r'^ *//', line): 168 previous_continue = False 169 else: 170 sys.stderr.write(line_old) 171 sys.stderr.write(line) 172 raise RuntimeError, "Comment continuation not allowed unless next line is a comment" 173 # End with some special processing for all lines which previously 174 # or now include "//". 175 start_special = line.find("//") 176 if start_special >= 0: 177 # Special transforms to get rid of left-over "\*" and "*\" 178 # forms which have historically been used to frame 179 # multi-block comments by some PLplot developers. 180 # Replace leading "// \*" ==> "//" 181 line = re.sub(r'^// \\\*(.*)$', "//\\1", line) 182 # Remove "*\" from end of comment lines 183 line = re.sub(r'^//(.*)\*\\$', "//\\1", line) 184 # Convert long "horizontal line" comment forms to standard form. 185 line = re.sub(r'^// *[-*=/+_\\# ]{50,200}$', "//--------------------------------------------------------------------------", line) 186 # Look for trailing continuation after comment lines and 187 # complain if you find any. 188 start_special = line.rfind("\\") 189 # Note line has trailing "\n" so that the last character 190 # is at position len(line) - 2. 191 if start_special >=0 and start_special == len(line) -2: 192 # Strings are immutable so this is a copy not a reference. 193 line_old = line 194 previous_continue = True 195 196 sys.stdout.write(line) 197 198if not ifsingleline: 199 sys.stderr.write(line) 200 raise RuntimeError, "Last line of file leaves unterminated comment block." 201 202if previous_continue: 203 sys.stderr.write(line_old) 204 raise RuntimeError, "Last line of file is a continued comment." 205 206sys.exit() 207