1*8fb009dcSAndroid Build Coastguard Worker// Copyright (c) 2017, Google Inc. 2*8fb009dcSAndroid Build Coastguard Worker// 3*8fb009dcSAndroid Build Coastguard Worker// Permission to use, copy, modify, and/or distribute this software for any 4*8fb009dcSAndroid Build Coastguard Worker// purpose with or without fee is hereby granted, provided that the above 5*8fb009dcSAndroid Build Coastguard Worker// copyright notice and this permission notice appear in all copies. 6*8fb009dcSAndroid Build Coastguard Worker// 7*8fb009dcSAndroid Build Coastguard Worker// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8*8fb009dcSAndroid Build Coastguard Worker// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9*8fb009dcSAndroid Build Coastguard Worker// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10*8fb009dcSAndroid Build Coastguard Worker// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11*8fb009dcSAndroid Build Coastguard Worker// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12*8fb009dcSAndroid Build Coastguard Worker// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13*8fb009dcSAndroid Build Coastguard Worker// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14*8fb009dcSAndroid Build Coastguard Worker 15*8fb009dcSAndroid Build Coastguard Worker//go:build ignore 16*8fb009dcSAndroid Build Coastguard Worker 17*8fb009dcSAndroid Build Coastguard Workerpackage main 18*8fb009dcSAndroid Build Coastguard Worker 19*8fb009dcSAndroid Build Coastguard Workerimport ( 20*8fb009dcSAndroid Build Coastguard Worker "bytes" 21*8fb009dcSAndroid Build Coastguard Worker "fmt" 22*8fb009dcSAndroid Build Coastguard Worker "os" 23*8fb009dcSAndroid Build Coastguard Worker "strings" 24*8fb009dcSAndroid Build Coastguard Worker) 25*8fb009dcSAndroid Build Coastguard Worker 26*8fb009dcSAndroid Build Coastguard Worker// convert_comments.go converts C-style block comments to C++-style line 27*8fb009dcSAndroid Build Coastguard Worker// comments. A block comment is converted if all of the following are true: 28*8fb009dcSAndroid Build Coastguard Worker// 29*8fb009dcSAndroid Build Coastguard Worker// * The comment begins after the first blank line, to leave the license 30*8fb009dcSAndroid Build Coastguard Worker// blocks alone. 31*8fb009dcSAndroid Build Coastguard Worker// 32*8fb009dcSAndroid Build Coastguard Worker// * There are no characters between the '*/' and the end of the line. 33*8fb009dcSAndroid Build Coastguard Worker// 34*8fb009dcSAndroid Build Coastguard Worker// * Either one of the following are true: 35*8fb009dcSAndroid Build Coastguard Worker// 36*8fb009dcSAndroid Build Coastguard Worker// - The comment fits on one line. 37*8fb009dcSAndroid Build Coastguard Worker// 38*8fb009dcSAndroid Build Coastguard Worker// - Each line the comment spans begins with N spaces, followed by '/*' for 39*8fb009dcSAndroid Build Coastguard Worker// the initial line or ' *' for subsequent lines, where N is the same for 40*8fb009dcSAndroid Build Coastguard Worker// each line. 41*8fb009dcSAndroid Build Coastguard Worker// 42*8fb009dcSAndroid Build Coastguard Worker// This tool is a heuristic. While it gets almost all cases correct, the final 43*8fb009dcSAndroid Build Coastguard Worker// output should still be looked over and fixed up as needed. 44*8fb009dcSAndroid Build Coastguard Worker 45*8fb009dcSAndroid Build Coastguard Worker// allSpaces returns true if |s| consists entirely of spaces. 46*8fb009dcSAndroid Build Coastguard Workerfunc allSpaces(s string) bool { 47*8fb009dcSAndroid Build Coastguard Worker return strings.IndexFunc(s, func(r rune) bool { return r != ' ' }) == -1 48*8fb009dcSAndroid Build Coastguard Worker} 49*8fb009dcSAndroid Build Coastguard Worker 50*8fb009dcSAndroid Build Coastguard Worker// isContinuation returns true if |s| is a continuation line for a multi-line 51*8fb009dcSAndroid Build Coastguard Worker// comment indented to the specified column. 52*8fb009dcSAndroid Build Coastguard Workerfunc isContinuation(s string, column int) bool { 53*8fb009dcSAndroid Build Coastguard Worker if len(s) < column+2 { 54*8fb009dcSAndroid Build Coastguard Worker return false 55*8fb009dcSAndroid Build Coastguard Worker } 56*8fb009dcSAndroid Build Coastguard Worker if !allSpaces(s[:column]) { 57*8fb009dcSAndroid Build Coastguard Worker return false 58*8fb009dcSAndroid Build Coastguard Worker } 59*8fb009dcSAndroid Build Coastguard Worker return s[column:column+2] == " *" 60*8fb009dcSAndroid Build Coastguard Worker} 61*8fb009dcSAndroid Build Coastguard Worker 62*8fb009dcSAndroid Build Coastguard Worker// indexFrom behaves like strings.Index but only reports matches starting at 63*8fb009dcSAndroid Build Coastguard Worker// |idx|. 64*8fb009dcSAndroid Build Coastguard Workerfunc indexFrom(s, sep string, idx int) int { 65*8fb009dcSAndroid Build Coastguard Worker ret := strings.Index(s[idx:], sep) 66*8fb009dcSAndroid Build Coastguard Worker if ret < 0 { 67*8fb009dcSAndroid Build Coastguard Worker return -1 68*8fb009dcSAndroid Build Coastguard Worker } 69*8fb009dcSAndroid Build Coastguard Worker return idx + ret 70*8fb009dcSAndroid Build Coastguard Worker} 71*8fb009dcSAndroid Build Coastguard Worker 72*8fb009dcSAndroid Build Coastguard Worker// A lineGroup is a contiguous group of lines with an eligible comment at the 73*8fb009dcSAndroid Build Coastguard Worker// same column. Any trailing '*/'s will already be removed. 74*8fb009dcSAndroid Build Coastguard Workertype lineGroup struct { 75*8fb009dcSAndroid Build Coastguard Worker // column is the column where the eligible comment begins. line[column] 76*8fb009dcSAndroid Build Coastguard Worker // and line[column+1] will both be replaced with '/'. It is -1 if this 77*8fb009dcSAndroid Build Coastguard Worker // group is not to be converted. 78*8fb009dcSAndroid Build Coastguard Worker column int 79*8fb009dcSAndroid Build Coastguard Worker lines []string 80*8fb009dcSAndroid Build Coastguard Worker} 81*8fb009dcSAndroid Build Coastguard Worker 82*8fb009dcSAndroid Build Coastguard Workerfunc addLine(groups *[]lineGroup, line string, column int) { 83*8fb009dcSAndroid Build Coastguard Worker if len(*groups) == 0 || (*groups)[len(*groups)-1].column != column { 84*8fb009dcSAndroid Build Coastguard Worker *groups = append(*groups, lineGroup{column, nil}) 85*8fb009dcSAndroid Build Coastguard Worker } 86*8fb009dcSAndroid Build Coastguard Worker (*groups)[len(*groups)-1].lines = append((*groups)[len(*groups)-1].lines, line) 87*8fb009dcSAndroid Build Coastguard Worker} 88*8fb009dcSAndroid Build Coastguard Worker 89*8fb009dcSAndroid Build Coastguard Worker// writeLine writes |line| to |out|, followed by a newline. 90*8fb009dcSAndroid Build Coastguard Workerfunc writeLine(out *bytes.Buffer, line string) { 91*8fb009dcSAndroid Build Coastguard Worker out.WriteString(line) 92*8fb009dcSAndroid Build Coastguard Worker out.WriteByte('\n') 93*8fb009dcSAndroid Build Coastguard Worker} 94*8fb009dcSAndroid Build Coastguard Worker 95*8fb009dcSAndroid Build Coastguard Workerfunc convertComments(path string, in []byte) []byte { 96*8fb009dcSAndroid Build Coastguard Worker lines := strings.Split(string(in), "\n") 97*8fb009dcSAndroid Build Coastguard Worker 98*8fb009dcSAndroid Build Coastguard Worker // Account for the trailing newline. 99*8fb009dcSAndroid Build Coastguard Worker if len(lines) > 0 && len(lines[len(lines)-1]) == 0 { 100*8fb009dcSAndroid Build Coastguard Worker lines = lines[:len(lines)-1] 101*8fb009dcSAndroid Build Coastguard Worker } 102*8fb009dcSAndroid Build Coastguard Worker 103*8fb009dcSAndroid Build Coastguard Worker // First pass: identify all comments to be converted. Group them into 104*8fb009dcSAndroid Build Coastguard Worker // lineGroups with the same column. 105*8fb009dcSAndroid Build Coastguard Worker var groups []lineGroup 106*8fb009dcSAndroid Build Coastguard Worker 107*8fb009dcSAndroid Build Coastguard Worker // Find the license block separator. 108*8fb009dcSAndroid Build Coastguard Worker for len(lines) > 0 { 109*8fb009dcSAndroid Build Coastguard Worker line := lines[0] 110*8fb009dcSAndroid Build Coastguard Worker lines = lines[1:] 111*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, line, -1) 112*8fb009dcSAndroid Build Coastguard Worker if len(line) == 0 { 113*8fb009dcSAndroid Build Coastguard Worker break 114*8fb009dcSAndroid Build Coastguard Worker } 115*8fb009dcSAndroid Build Coastguard Worker } 116*8fb009dcSAndroid Build Coastguard Worker 117*8fb009dcSAndroid Build Coastguard Worker // inComment is true if we are in the middle of a comment. 118*8fb009dcSAndroid Build Coastguard Worker var inComment bool 119*8fb009dcSAndroid Build Coastguard Worker // comment is the currently buffered multi-line comment to convert. If 120*8fb009dcSAndroid Build Coastguard Worker // |inComment| is true and it is nil, the current multi-line comment is 121*8fb009dcSAndroid Build Coastguard Worker // not convertable and we copy lines to |out| as-is. 122*8fb009dcSAndroid Build Coastguard Worker var comment []string 123*8fb009dcSAndroid Build Coastguard Worker // column is the column offset of |comment|. 124*8fb009dcSAndroid Build Coastguard Worker var column int 125*8fb009dcSAndroid Build Coastguard Worker for len(lines) > 0 { 126*8fb009dcSAndroid Build Coastguard Worker line := lines[0] 127*8fb009dcSAndroid Build Coastguard Worker lines = lines[1:] 128*8fb009dcSAndroid Build Coastguard Worker 129*8fb009dcSAndroid Build Coastguard Worker var idx int 130*8fb009dcSAndroid Build Coastguard Worker if inComment { 131*8fb009dcSAndroid Build Coastguard Worker // Stop buffering if this comment isn't eligible. 132*8fb009dcSAndroid Build Coastguard Worker if comment != nil && !isContinuation(line, column) { 133*8fb009dcSAndroid Build Coastguard Worker for _, l := range comment { 134*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, l, -1) 135*8fb009dcSAndroid Build Coastguard Worker } 136*8fb009dcSAndroid Build Coastguard Worker comment = nil 137*8fb009dcSAndroid Build Coastguard Worker } 138*8fb009dcSAndroid Build Coastguard Worker 139*8fb009dcSAndroid Build Coastguard Worker // Look for the end of the current comment. 140*8fb009dcSAndroid Build Coastguard Worker idx = strings.Index(line, "*/") 141*8fb009dcSAndroid Build Coastguard Worker if idx < 0 { 142*8fb009dcSAndroid Build Coastguard Worker if comment != nil { 143*8fb009dcSAndroid Build Coastguard Worker comment = append(comment, line) 144*8fb009dcSAndroid Build Coastguard Worker } else { 145*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, line, -1) 146*8fb009dcSAndroid Build Coastguard Worker } 147*8fb009dcSAndroid Build Coastguard Worker continue 148*8fb009dcSAndroid Build Coastguard Worker } 149*8fb009dcSAndroid Build Coastguard Worker 150*8fb009dcSAndroid Build Coastguard Worker inComment = false 151*8fb009dcSAndroid Build Coastguard Worker if comment != nil { 152*8fb009dcSAndroid Build Coastguard Worker if idx == len(line)-2 { 153*8fb009dcSAndroid Build Coastguard Worker // This is a convertable multi-line comment. 154*8fb009dcSAndroid Build Coastguard Worker if idx >= column+2 { 155*8fb009dcSAndroid Build Coastguard Worker // |idx| may be equal to 156*8fb009dcSAndroid Build Coastguard Worker // |column| + 1, if the line is 157*8fb009dcSAndroid Build Coastguard Worker // a '*/' on its own. In that 158*8fb009dcSAndroid Build Coastguard Worker // case, we discard the line. 159*8fb009dcSAndroid Build Coastguard Worker comment = append(comment, line[:idx]) 160*8fb009dcSAndroid Build Coastguard Worker } 161*8fb009dcSAndroid Build Coastguard Worker for _, l := range comment { 162*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, l, column) 163*8fb009dcSAndroid Build Coastguard Worker } 164*8fb009dcSAndroid Build Coastguard Worker comment = nil 165*8fb009dcSAndroid Build Coastguard Worker continue 166*8fb009dcSAndroid Build Coastguard Worker } 167*8fb009dcSAndroid Build Coastguard Worker 168*8fb009dcSAndroid Build Coastguard Worker // Flush the buffered comment unmodified. 169*8fb009dcSAndroid Build Coastguard Worker for _, l := range comment { 170*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, l, -1) 171*8fb009dcSAndroid Build Coastguard Worker } 172*8fb009dcSAndroid Build Coastguard Worker comment = nil 173*8fb009dcSAndroid Build Coastguard Worker } 174*8fb009dcSAndroid Build Coastguard Worker idx += 2 175*8fb009dcSAndroid Build Coastguard Worker } 176*8fb009dcSAndroid Build Coastguard Worker 177*8fb009dcSAndroid Build Coastguard Worker // Parse starting from |idx|, looking for either a convertable 178*8fb009dcSAndroid Build Coastguard Worker // line comment or a multi-line comment. 179*8fb009dcSAndroid Build Coastguard Worker for { 180*8fb009dcSAndroid Build Coastguard Worker idx = indexFrom(line, "/*", idx) 181*8fb009dcSAndroid Build Coastguard Worker if idx < 0 { 182*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, line, -1) 183*8fb009dcSAndroid Build Coastguard Worker break 184*8fb009dcSAndroid Build Coastguard Worker } 185*8fb009dcSAndroid Build Coastguard Worker 186*8fb009dcSAndroid Build Coastguard Worker endIdx := indexFrom(line, "*/", idx) 187*8fb009dcSAndroid Build Coastguard Worker if endIdx < 0 { 188*8fb009dcSAndroid Build Coastguard Worker // The comment is, so far, eligible for conversion. 189*8fb009dcSAndroid Build Coastguard Worker inComment = true 190*8fb009dcSAndroid Build Coastguard Worker column = idx 191*8fb009dcSAndroid Build Coastguard Worker comment = []string{line} 192*8fb009dcSAndroid Build Coastguard Worker break 193*8fb009dcSAndroid Build Coastguard Worker } 194*8fb009dcSAndroid Build Coastguard Worker 195*8fb009dcSAndroid Build Coastguard Worker if endIdx != len(line)-2 { 196*8fb009dcSAndroid Build Coastguard Worker // Continue parsing for more comments in this line. 197*8fb009dcSAndroid Build Coastguard Worker idx = endIdx + 2 198*8fb009dcSAndroid Build Coastguard Worker continue 199*8fb009dcSAndroid Build Coastguard Worker } 200*8fb009dcSAndroid Build Coastguard Worker 201*8fb009dcSAndroid Build Coastguard Worker addLine(&groups, line[:endIdx], idx) 202*8fb009dcSAndroid Build Coastguard Worker break 203*8fb009dcSAndroid Build Coastguard Worker } 204*8fb009dcSAndroid Build Coastguard Worker } 205*8fb009dcSAndroid Build Coastguard Worker 206*8fb009dcSAndroid Build Coastguard Worker // Second pass: convert the lineGroups, adjusting spacing as needed. 207*8fb009dcSAndroid Build Coastguard Worker var out bytes.Buffer 208*8fb009dcSAndroid Build Coastguard Worker var lineNo int 209*8fb009dcSAndroid Build Coastguard Worker for _, group := range groups { 210*8fb009dcSAndroid Build Coastguard Worker if group.column < 0 { 211*8fb009dcSAndroid Build Coastguard Worker for _, line := range group.lines { 212*8fb009dcSAndroid Build Coastguard Worker writeLine(&out, line) 213*8fb009dcSAndroid Build Coastguard Worker } 214*8fb009dcSAndroid Build Coastguard Worker } else { 215*8fb009dcSAndroid Build Coastguard Worker // Google C++ style prefers two spaces before a comment 216*8fb009dcSAndroid Build Coastguard Worker // if it is on the same line as code, but clang-format 217*8fb009dcSAndroid Build Coastguard Worker // has been placing one space for block comments. All 218*8fb009dcSAndroid Build Coastguard Worker // comments within a group should be adjusted by the 219*8fb009dcSAndroid Build Coastguard Worker // same amount. 220*8fb009dcSAndroid Build Coastguard Worker var adjust string 221*8fb009dcSAndroid Build Coastguard Worker for _, line := range group.lines { 222*8fb009dcSAndroid Build Coastguard Worker if !allSpaces(line[:group.column]) && line[group.column-1] != '(' { 223*8fb009dcSAndroid Build Coastguard Worker if line[group.column-1] != ' ' { 224*8fb009dcSAndroid Build Coastguard Worker if len(adjust) < 2 { 225*8fb009dcSAndroid Build Coastguard Worker adjust = " " 226*8fb009dcSAndroid Build Coastguard Worker } 227*8fb009dcSAndroid Build Coastguard Worker } else if line[group.column-2] != ' ' { 228*8fb009dcSAndroid Build Coastguard Worker if len(adjust) < 1 { 229*8fb009dcSAndroid Build Coastguard Worker adjust = " " 230*8fb009dcSAndroid Build Coastguard Worker } 231*8fb009dcSAndroid Build Coastguard Worker } 232*8fb009dcSAndroid Build Coastguard Worker } 233*8fb009dcSAndroid Build Coastguard Worker } 234*8fb009dcSAndroid Build Coastguard Worker 235*8fb009dcSAndroid Build Coastguard Worker for i, line := range group.lines { 236*8fb009dcSAndroid Build Coastguard Worker // The OpenSSL style writes multiline block comments with a 237*8fb009dcSAndroid Build Coastguard Worker // blank line at the top and bottom, like so: 238*8fb009dcSAndroid Build Coastguard Worker // 239*8fb009dcSAndroid Build Coastguard Worker // /* 240*8fb009dcSAndroid Build Coastguard Worker // * Some multi-line 241*8fb009dcSAndroid Build Coastguard Worker // * comment 242*8fb009dcSAndroid Build Coastguard Worker // */ 243*8fb009dcSAndroid Build Coastguard Worker // 244*8fb009dcSAndroid Build Coastguard Worker // The trailing lines are already removed above, when buffering. 245*8fb009dcSAndroid Build Coastguard Worker // Remove the leading lines here. (The leading lines cannot be 246*8fb009dcSAndroid Build Coastguard Worker // removed when buffering because we may discover the comment is 247*8fb009dcSAndroid Build Coastguard Worker // not convertible in later lines.) 248*8fb009dcSAndroid Build Coastguard Worker // 249*8fb009dcSAndroid Build Coastguard Worker // Note the leading line cannot be easily removed if there is 250*8fb009dcSAndroid Build Coastguard Worker // code before it, such as the following. Skip those cases. 251*8fb009dcSAndroid Build Coastguard Worker // 252*8fb009dcSAndroid Build Coastguard Worker // foo(); /* 253*8fb009dcSAndroid Build Coastguard Worker // * Some multi-line 254*8fb009dcSAndroid Build Coastguard Worker // * comment 255*8fb009dcSAndroid Build Coastguard Worker // */ 256*8fb009dcSAndroid Build Coastguard Worker if i == 0 && allSpaces(line[:group.column]) && len(line) == group.column+2 { 257*8fb009dcSAndroid Build Coastguard Worker continue 258*8fb009dcSAndroid Build Coastguard Worker } 259*8fb009dcSAndroid Build Coastguard Worker newLine := fmt.Sprintf("%s%s//%s", line[:group.column], adjust, strings.TrimRight(line[group.column+2:], " ")) 260*8fb009dcSAndroid Build Coastguard Worker if len(newLine) > 80 { 261*8fb009dcSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "%s:%d: Line is now longer than 80 characters\n", path, lineNo+i+1) 262*8fb009dcSAndroid Build Coastguard Worker } 263*8fb009dcSAndroid Build Coastguard Worker writeLine(&out, newLine) 264*8fb009dcSAndroid Build Coastguard Worker } 265*8fb009dcSAndroid Build Coastguard Worker 266*8fb009dcSAndroid Build Coastguard Worker } 267*8fb009dcSAndroid Build Coastguard Worker lineNo += len(group.lines) 268*8fb009dcSAndroid Build Coastguard Worker } 269*8fb009dcSAndroid Build Coastguard Worker return out.Bytes() 270*8fb009dcSAndroid Build Coastguard Worker} 271*8fb009dcSAndroid Build Coastguard Worker 272*8fb009dcSAndroid Build Coastguard Workerfunc main() { 273*8fb009dcSAndroid Build Coastguard Worker for _, arg := range os.Args[1:] { 274*8fb009dcSAndroid Build Coastguard Worker in, err := os.ReadFile(arg) 275*8fb009dcSAndroid Build Coastguard Worker if err != nil { 276*8fb009dcSAndroid Build Coastguard Worker panic(err) 277*8fb009dcSAndroid Build Coastguard Worker } 278*8fb009dcSAndroid Build Coastguard Worker if err := os.WriteFile(arg, convertComments(arg, in), 0666); err != nil { 279*8fb009dcSAndroid Build Coastguard Worker panic(err) 280*8fb009dcSAndroid Build Coastguard Worker } 281*8fb009dcSAndroid Build Coastguard Worker } 282*8fb009dcSAndroid Build Coastguard Worker} 283