loading up the forgejo repo on tangled to test page performance
fork

Configure Feed

Select the types of activity you want to include in your feed.

at forgejo 101 lines 3.6 kB view raw
1// Copyright 2020 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package util 5 6import "strings" 7 8// Bash has the definition of a metacharacter: 9// * A character that, when unquoted, separates words. 10// A metacharacter is one of: " \t\n|&;()<>" 11// 12// The following characters also have addition special meaning when unescaped: 13// * ‘${[*?!"'`\’ 14// 15// Double Quotes preserve the literal value of all characters with then quotes 16// excepting: ‘$’, ‘`’, ‘\’, and, when history expansion is enabled, ‘!’. 17// The backslash retains its special meaning only when followed by one of the 18// following characters: ‘$’, ‘`’, ‘"’, ‘\’, or newline. 19// Backslashes preceding characters without a special meaning are left 20// unmodified. A double quote may be quoted within double quotes by preceding 21// it with a backslash. If enabled, history expansion will be performed unless 22// an ‘!’ appearing in double quotes is escaped using a backslash. The 23// backslash preceding the ‘!’ is not removed. 24// 25// -> This means that `!\n` cannot be safely expressed in `"`. 26// 27// Looking at the man page for Dash and ash the situation is similar. 28// 29// Now zsh requires that ‘}’, and ‘]’ are also enclosed in doublequotes or escaped 30// 31// Single quotes escape everything except a ‘'’ 32// 33// There's one other gotcha - ‘~’ at the start of a string needs to be expanded 34// because people always expect that - of course if there is a special character before '/' 35// this is not going to work 36 37const ( 38 tildePrefix = '~' 39 needsEscape = " \t\n|&;()<>${}[]*?!\"'`\\" 40 needsSingleQuote = "!\n" 41) 42 43var ( 44 doubleQuoteEscaper = strings.NewReplacer(`$`, `\$`, "`", "\\`", `"`, `\"`, `\`, `\\`) 45 singleQuoteEscaper = strings.NewReplacer(`'`, `'\''`) 46 singleQuoteCoalescer = strings.NewReplacer(`''\'`, `\'`, `\'''`, `\'`) 47) 48 49// ShellEscape will escape the provided string. 50// We can't just use go-shellquote here because our preferences for escaping differ from those in that we want: 51// 52// * If the string doesn't require any escaping just leave it as it is. 53// * If the string requires any escaping prefer double quote escaping 54// * If we have ! or newlines then we need to use single quote escaping 55func ShellEscape(toEscape string) string { 56 if len(toEscape) == 0 { 57 return toEscape 58 } 59 60 start := 0 61 62 if toEscape[0] == tildePrefix { 63 // We're in the forcibly non-escaped section... 64 idx := strings.IndexRune(toEscape, '/') 65 if idx < 0 { 66 idx = len(toEscape) 67 } else { 68 idx++ 69 } 70 if !strings.ContainsAny(toEscape[:idx], needsEscape) { 71 // We'll assume that they intend ~ expansion to occur 72 start = idx 73 } 74 } 75 76 // Now for simplicity we'll look at the rest of the string 77 if !strings.ContainsAny(toEscape[start:], needsEscape) { 78 return toEscape 79 } 80 81 // OK we have to do some escaping 82 sb := &strings.Builder{} 83 _, _ = sb.WriteString(toEscape[:start]) 84 85 // Do we have any characters which absolutely need to be within single quotes - that is simply ! or \n? 86 if strings.ContainsAny(toEscape[start:], needsSingleQuote) { 87 // We need to single quote escape. 88 sb2 := &strings.Builder{} 89 _, _ = sb2.WriteRune('\'') 90 _, _ = singleQuoteEscaper.WriteString(sb2, toEscape[start:]) 91 _, _ = sb2.WriteRune('\'') 92 _, _ = singleQuoteCoalescer.WriteString(sb, sb2.String()) 93 return sb.String() 94 } 95 96 // OK we can just use " just escape the things that need escaping 97 _, _ = sb.WriteRune('"') 98 _, _ = doubleQuoteEscaper.WriteString(sb, toEscape[start:]) 99 _, _ = sb.WriteRune('"') 100 return sb.String() 101}