updated patch system
This commit is contained in:
parent
bb64cea202
commit
6c9c66bd7a
10 changed files with 434 additions and 208 deletions
98
README.md
98
README.md
|
@ -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
196
apply.go
|
@ -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
143
main.go
Executable 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)
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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
85
pref.go
Normal 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
13
run.sh
Executable 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
95
standard.go
Normal 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
|
||||
}
|
Loading…
Add table
Reference in a new issue