updated patch system

This commit is contained in:
partisan 2024-12-08 17:51:31 +01:00
parent bb64cea202
commit 6c9c66bd7a
10 changed files with 434 additions and 208 deletions

View file

@ -15,17 +15,101 @@ This is a custom patcher for applying modifications to firefox source code. It p
1. Place your patch files in the `./patches` directory. Example of patch file:
```patch
--- /browser/branding/official/configure.sh
+++ /browser/branding/official/configure.sh
i:/browser/branding/official/configure.sh
o:/browser/branding/official/configure.sh
-MOZ_APP_DISPLAYNAME=Firefox
+MOZ_APP_DISPLAYNAME=Spitfire
```
2. Run the patcher with the `--path` flag:
```sh
./run.sh --path ./mozilla-central
```
*Root path will be resolved to an absolute path, and patches will be applied relative to this path.*
## Parameters Explained
Here are the details of each parameter used in the patching system:
### **`i:` (Input)**
- Specifies the input file to which the patch should be applied.
- This is the file that will be read and modified based on the patch instructions.
- **Example:**
```
i:/browser/branding/official/configure.sh
```
- This means the input file is located at `/browser/branding/official/configure.sh`.
### **`o:` (Output)**
- Specifies the output file where the modified content will be saved.
- If the input and output paths are the same, the original file will be overwritten.
- **Example:**
```
o:/browser/branding/official/configure.sh
```
- This means the output file is `/browser/branding/official/configure.sh`.
### **`t:` (Type)**
- Defines the type of patch being applied. This parameter can be skipped if not applicable.
- Common types:
1. **`pref`:** Indicates that the patch is modifying preference settings (e.g., Firefox `prefs.js`).
- When `t:pref` is specified, the script will search for existing preferences in the input file and replace them with the new ones provided in the patch. If not found, it will add the new preference.
- **Example:**
```
t:pref
+pref("extensions.getAddons.showPane", false); // HIDDEN
```
2. **`standard`:** Standard patching type that follows the `+` (add) and `-` (remove) syntax.
- Used for general file modifications.
- **Example:**
```
-MOZ_APP_DISPLAYNAME=Firefox
+MOZ_APP_DISPLAYNAME=Spitfire
```
- **When to Use:**
- **Required for specialized processing:** If the patch involves specific logic, like handling `pref` files, include this parameter.
- **Optional for standard patches:** For simple addition or removal of lines, you can skip this parameter.
### Example Patch File
```
t:pref
i:/browser/branding/official/configure.sh
o:/browser/branding/official/configure.sh
-pref("browser.privatebrowsing.autostart", false);
+pref("browser.privatebrowsing.autostart", true);
t:standard
i:/browser/app/profile/firefox.js
o:/browser/app/profile/firefox.js
-MOZ_APP_DISPLAYNAME=Firefox
+MOZ_APP_DISPLAYNAME=Spitfire
```
2. Run the patcher with the `--path` flag:
### Summary of Parameters
```sh
go run patch.go --path=./mozilla-central
```
*Root path will be resolved to an absolute path, and patches will be applied relative to this path.*
| Parameter | Required | Purpose | Example |
| ----------- | ---------- | ------------------------------------- | ------------------------------------- |
| `i:` | Yes | Specifies the input file path. | `i:/browser/app/profile/firefox.js` |
| `o:` | Yes | Specifies the output file path. | `o:/browser/app/profile/firefox.js` |
| `t:` | No | Defines the type of patch. | `t:pref` or `t:standard` |
| `+` | Yes | Adds a line to the output file. | `+MOZ_APP_DISPLAYNAME=Spitfire` |
| `-` | Yes | Removes a line from the input file. | `-MOZ_APP_DISPLAYNAME=Firefox` |
### Workflow
1. **Define Input and Output:** Use `i:` and `o:` to specify which files to read and write.
2. **Specify Patch Type:** Use `t:` for special handling like preference modifications.
3. **Apply Modifications:** Use `+` to add lines and `-` to remove lines.

196
apply.go
View file

@ -1,196 +0,0 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
// Define the --path flag
rootPath := flag.String("path", ".", "Root path for patch application")
flag.Parse()
// Convert root path to an absolute path
absoluteRootPath, err := filepath.Abs(*rootPath)
if err != nil {
log.Fatalf("Failed to resolve absolute path for root: %v", err)
}
fmt.Printf("Starting custom patcher...\nRoot path: %s\n", absoluteRootPath)
patchDir := "./patches"
var successfulPatches, failedPatches []string
err = filepath.Walk(patchDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), ".patch") {
fmt.Printf("Applying patch: %s\n", path)
err := applyPatch(path, absoluteRootPath)
if err != nil {
fmt.Printf("Failed to apply patch %s: %v\n", path, err)
failedPatches = append(failedPatches, path)
} else {
fmt.Printf("Successfully applied patch: %s\n", path)
successfulPatches = append(successfulPatches, path)
}
}
return nil
})
if err != nil {
log.Fatalf("Error reading patches: %v", err)
}
// Print the summary
fmt.Println("\nSummary:")
fmt.Printf("Successful patches (%d):\n", len(successfulPatches))
for _, patch := range successfulPatches {
fmt.Printf(" - %s\n", patch)
}
fmt.Printf("Failed patches (%d):\n", len(failedPatches))
for _, patch := range failedPatches {
fmt.Printf(" - %s\n", patch)
}
// Exit with appropriate status for Forgejo/Gitea runners
if len(failedPatches) > 0 {
fmt.Println("\nPatch process completed with failures.")
os.Exit(1)
} else {
fmt.Println("\nAll patches applied successfully.")
os.Exit(0)
}
}
// applyPatch processes a single patch file
func applyPatch(patchPath, rootPath string) error {
// Open the patch file
file, err := os.Open(patchPath)
if err != nil {
return fmt.Errorf("failed to open patch file: %v", err)
}
defer file.Close()
var targetFilePath, tempFilePath string
var modifications []string
scanner := bufio.NewScanner(file)
isReadingChanges := false
// Parse the patch file
for scanner.Scan() {
line := scanner.Text()
// Ignore lines starting with //
if strings.HasPrefix(strings.TrimSpace(line), "//") {
continue
}
// Detect the target file
if strings.HasPrefix(line, "--- ") {
relativePath := strings.TrimSpace(strings.TrimPrefix(line, "--- "))
targetFilePath = filepath.Join(rootPath, relativePath)
isReadingChanges = true
continue
}
// Start processing modifications
if isReadingChanges {
modifications = append(modifications, line)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read patch file: %v", err)
}
// Open the target file for reading
targetFile, err := os.Open(targetFilePath)
if err != nil {
return fmt.Errorf("failed to open target file: %v", err)
}
defer targetFile.Close()
// Prepare a temporary file for output
tempFilePath = targetFilePath + ".tmp"
tempFile, err := os.Create(tempFilePath)
if err != nil {
return fmt.Errorf("failed to create temp file: %v", err)
}
defer tempFile.Close()
scanner = bufio.NewScanner(targetFile)
var targetLines []string
for scanner.Scan() {
targetLines = append(targetLines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read target file: %v", err)
}
// Apply the modifications
updatedLines, err := applyModifications(targetLines, modifications)
if err != nil {
return err
}
// Write the updated lines to the temporary file
for _, line := range updatedLines {
_, err := fmt.Fprintln(tempFile, line)
if err != nil {
return fmt.Errorf("failed to write to temp file: %v", err)
}
}
// Replace the original file with the updated file
err = os.Rename(tempFilePath, targetFilePath)
if err != nil {
return fmt.Errorf("failed to replace target file: %v", err)
}
return nil
}
// applyModifications applies the changes from the patch file to the target lines
func applyModifications(targetLines, modifications []string) ([]string, error) {
var result []string
i := 0
for _, mod := range modifications {
switch {
case strings.HasPrefix(mod, "-"): // Delete or replace
mod = strings.TrimPrefix(mod, "-")
for i < len(targetLines) {
if strings.TrimSpace(targetLines[i]) == strings.TrimSpace(mod) {
i++ // Skip this line (delete)
break
}
result = append(result, targetLines[i])
i++
}
case strings.HasPrefix(mod, "+"): // Add or replace
mod = strings.TrimPrefix(mod, "+")
result = append(result, mod)
default: // Keep existing lines
if i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
}
}
// Append remaining lines from the original file
for i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
return result, nil
}

143
main.go Executable file
View file

@ -0,0 +1,143 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
// Define the --path flag
rootPath := flag.String("path", ".", "Root path for patch application")
flag.Parse()
// Convert root path to an absolute path
absoluteRootPath, err := filepath.Abs(*rootPath)
if err != nil {
log.Fatalf("Failed to resolve absolute path for root: %v", err)
}
fmt.Printf("Starting custom patcher...\nRoot path: %s\n", absoluteRootPath)
patchDir := "./patches"
var successfulPatches, failedPatches []string
err = filepath.Walk(patchDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), ".patch") {
fmt.Printf("Applying patch: %s\n", path)
err := applyPatch(path, absoluteRootPath)
if err != nil {
fmt.Printf("Failed to apply patch %s: %v\n", path, err)
failedPatches = append(failedPatches, path)
} else {
fmt.Printf("Successfully applied patch: %s\n", path)
successfulPatches = append(successfulPatches, path)
}
}
return nil
})
if err != nil {
log.Fatalf("Error reading patches: %v", err)
}
// Print the summary
fmt.Println("\nSummary:")
fmt.Printf("Successful patches (%d):\n", len(successfulPatches))
for _, patch := range successfulPatches {
fmt.Printf(" - %s\n", patch)
}
fmt.Printf("Failed patches (%d):\n", len(failedPatches))
for _, patch := range failedPatches {
fmt.Printf(" - %s\n", patch)
}
// Exit with appropriate status
if len(failedPatches) > 0 {
fmt.Println("\nPatch process completed with failures.")
os.Exit(1)
} else {
fmt.Println("\nAll patches applied successfully.")
os.Exit(0)
}
}
// applyPatch processes a single patch file
func applyPatch(patchPath, rootPath string) error {
file, err := os.Open(patchPath)
if err != nil {
return fmt.Errorf("failed to open patch file: %v", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
var inputFilePath, outputFilePath string
var modifications []string
var patchType string
// Parse the patch file
for scanner.Scan() {
line := scanner.Text()
// Check for header type
if strings.HasPrefix(line, "t:") && patchType == "" {
patchType = strings.TrimSpace(strings.TrimPrefix(line, "t:"))
fmt.Printf("Detected patch type: %s\n", patchType)
continue
}
// Detect the input file
if strings.HasPrefix(line, "i:") {
relativePath := strings.TrimSpace(strings.TrimPrefix(line, "i:"))
inputFilePath = filepath.Join(rootPath, relativePath)
continue
}
// Detect the output file
if strings.HasPrefix(line, "o:") {
relativePath := strings.TrimSpace(strings.TrimPrefix(line, "o:"))
outputFilePath = filepath.Join(rootPath, relativePath)
continue
}
// Ignore comment lines
if strings.HasPrefix(strings.TrimSpace(line), "//") {
continue
}
// Collect modifications
modifications = append(modifications, line)
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read patch file: %v", err)
}
if inputFilePath == "" || outputFilePath == "" {
return fmt.Errorf("patch file must specify both input (i:) and output (o:) files")
}
// Replace the original file with the temporary file
err = os.Rename(inputFilePath, outputFilePath)
if err != nil {
return fmt.Errorf("failed to replace output file: %v", err)
}
// Process based on patch type
switch patchType {
case "pref":
return applyPrefModifications(outputFilePath, modifications)
case "standard":
return applyStandardModifications(outputFilePath, modifications)
default:
fmt.Printf("Type not specified defaulting to standard")
return applyStandardModifications(outputFilePath, modifications)
}
}

View file

@ -1,5 +1,5 @@
--- /browser/branding/nightly/configure.sh
+++ /browser/branding/nightly/configure.sh
i: /browser/branding/nightly/configure.sh
o: /browser/branding/nightly/configure.sh
-MOZ_APP_DISPLAYNAME="Firefox Nightly"
+MOZ_APP_DISPLAYNAME="Spitfire Nightly"

View file

@ -1,5 +1,5 @@
--- /browser/branding/official/configure.sh
+++ /browser/branding/official/configure.sh
i: /browser/branding/official/configure.sh
o: /browser/branding/official/configure.sh
-MOZ_APP_DISPLAYNAME=Firefox
+MOZ_APP_DISPLAYNAME=Spitfire

View file

@ -1,5 +1,6 @@
--- /browser/app/profile/firefox.js
+++ /browser/app/profile/firefox.js
t:pref
i: /browser/app/profile/firefox.js
o: /browser/app/profile/firefox.js
-pref("browser.startup.homepage", "about:home");
+pref("browser.startup.homepage", "https://qgato.xyz");

View file

@ -1,5 +1,6 @@
--- /browser/app/profile/firefox.js
+++ /browser/app/profile/firefox.js
t:pref
i: /browser/app/profile/firefox.js
o: /browser/app/profile/firefox.js
+pref("browser.privatebrowsing.vpnpromourl", "");
+pref("extensions.getAddons.showPane", false); // HIDDEN

85
pref.go Normal file
View file

@ -0,0 +1,85 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// applyPrefModifications handles `t:pref` type patches
func applyPrefModifications(targetFilePath string, modifications []string) error {
// Open the target file
targetFile, err := os.Open(targetFilePath)
if err != nil {
return fmt.Errorf("failed to open target file: %v", err)
}
defer targetFile.Close()
tempFilePath := targetFilePath + ".tmp"
tempFile, err := os.Create(tempFilePath)
if err != nil {
return fmt.Errorf("failed to create temp file: %v", err)
}
defer tempFile.Close()
scanner := bufio.NewScanner(targetFile)
modMap := map[string]string{}
// Prepare the modification map
for _, mod := range modifications {
if strings.HasPrefix(mod, "+pref") {
parts := strings.SplitN(mod, "(", 2)
if len(parts) > 1 {
key := strings.TrimSpace(parts[0][1:])
modMap[key] = strings.TrimSpace(mod)
}
}
}
modifiedLines := map[string]bool{}
for scanner.Scan() {
line := scanner.Text()
// Check if the line matches any modification key
lineModified := false
for key, replacement := range modMap {
if strings.HasPrefix(line, key) && !modifiedLines[key] {
fmt.Printf("Replacing line: %s\n", line)
if _, err := tempFile.WriteString(replacement + "\n"); err != nil {
return fmt.Errorf("failed to write to temp file: %v", err)
}
modifiedLines[key] = true
lineModified = true
break
}
}
// Write the original line if no modification matches
if !lineModified {
if _, err := tempFile.WriteString(line + "\n"); err != nil {
return fmt.Errorf("failed to write to temp file: %v", err)
}
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read target file: %v", err)
}
// Write remaining modifications (new preferences)
for key, replacement := range modMap {
if !modifiedLines[key] {
if _, err := tempFile.WriteString(replacement + "\n"); err != nil {
return fmt.Errorf("failed to write new preference to temp file: %v", err)
}
}
}
// Replace the original file with the temporary file
if err := os.Rename(tempFilePath, targetFilePath); err != nil {
return fmt.Errorf("failed to replace target file: %v", err)
}
return nil
}

13
run.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
# Check if --path argument is provided
if [[ "$#" -lt 2 ]] || [[ "$1" != "--path" ]]; then
echo "Usage: $0 --path <path-to-apply>"
exit 1
fi
# Extract the --path value
ROOT_PATH=$2
# Run the Go application with the specified path
go run main.go pref.go standard.go --path "$ROOT_PATH"

95
standard.go Normal file
View file

@ -0,0 +1,95 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// applyStandardModifications applies standard modifications
func applyStandardModifications(targetFilePath string, modifications []string) error {
// Open the target file
targetFile, err := os.Open(targetFilePath)
if err != nil {
return fmt.Errorf("failed to open target file: %v", err)
}
defer targetFile.Close()
// Prepare a temporary file for output
tempFilePath := targetFilePath + ".tmp"
tempFile, err := os.Create(tempFilePath)
if err != nil {
return fmt.Errorf("failed to create temp file: %v", err)
}
defer tempFile.Close()
scanner := bufio.NewScanner(targetFile)
var targetLines []string
for scanner.Scan() {
targetLines = append(targetLines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to read target file: %v", err)
}
// Apply the modifications
updatedLines, err := applyModifications(targetLines, modifications)
if err != nil {
return err
}
// Write the updated lines to the temporary file
for _, line := range updatedLines {
_, err := fmt.Fprintln(tempFile, line)
if err != nil {
return fmt.Errorf("failed to write to temp file: %v", err)
}
}
// Replace the original file with the updated file
err = os.Rename(tempFilePath, targetFilePath)
if err != nil {
return fmt.Errorf("failed to replace target file: %v", err)
}
return nil
}
// applyModifications applies the changes from the patch file to the target lines
func applyModifications(targetLines, modifications []string) ([]string, error) {
var result []string
i := 0
for _, mod := range modifications {
switch {
case strings.HasPrefix(mod, "-"): // Delete or replace
mod = strings.TrimPrefix(mod, "-")
for i < len(targetLines) {
if strings.TrimSpace(targetLines[i]) == strings.TrimSpace(mod) {
i++ // Skip this line (delete)
break
}
result = append(result, targetLines[i])
i++
}
case strings.HasPrefix(mod, "+"): // Add or replace
mod = strings.TrimPrefix(mod, "+")
result = append(result, mod)
default: // Keep existing lines
if i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
}
}
// Append remaining lines from the original file
for i < len(targetLines) {
result = append(result, targetLines[i])
i++
}
return result, nil
}