package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/middleware/cors"
)

const defaultConfigPath = "alosgarble.json"

var pageTemplate = template.Must(template.New("ui").Parse(`<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ALOS Garble Builder</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4/dist/full.min.css" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: {} } }
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap');
body{font-family:'Inter',sans-serif;}
.mono{font-family:'JetBrains Mono',monospace;}
.glass{background:rgba(255,255,255,0.03);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.06);}
.glow{box-shadow:0 0 40px rgba(99,102,241,0.15);}
.animate-in{animation:slideIn .5s ease-out both;}
@keyframes slideIn{from{opacity:0;transform:translateY(18px);}to{opacity:1;transform:translateY(0);}}
.command-preview{background:#0f0f13;border:1px solid #27272a;border-radius:.75rem;padding:1rem;position:relative;overflow:hidden;}
.command-preview::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,#6366f1,#a855f7,#ec4899);}
.cmd-text{color:#e4e4e7;font-size:.875rem;white-space:pre-wrap;word-break:break-all;}
.switch-huge{transform:scale(1.15);}
.btn-shine{position:relative;overflow:hidden;}
.btn-shine::after{content:'';position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:linear-gradient(45deg,transparent,rgba(255,255,255,0.1),transparent);transform:rotate(30deg);transition:.6s;}
.btn-shine:hover::after{left:100%;}
.tag-pill{background:rgba(99,102,241,0.12);color:#a5b4fc;border:1px solid rgba(99,102,241,0.25);padding:.25rem .6rem;border-radius:9999px;font-size:.75rem;font-weight:600;letter-spacing:.02em;}
</style>
</head>
<body class="bg-[#0a0a0f] text-gray-100 min-h-screen">
<div class="max-w-6xl mx-auto px-6 py-10">

<div class="flex items-center justify-between mb-10 animate-in">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-xl font-bold shadow-lg shadow-indigo-500/20">AG</div>
<div>
<h1 class="text-3xl font-extrabold tracking-tight bg-gradient-to-r from-indigo-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">ALOS Garble Builder</h1>
<p class="text-sm text-gray-500 mt-0.5">Visual command builder &nbsp;\u00b7&nbsp; v3</p>
</div>
</div>
<div class="flex gap-2">
<span class="tag-pill">Fiber v3</span>
<span class="tag-pill">DaisyUI</span>
<span class="tag-pill">No Prefork</span>
</div>
</div>

<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">

<div class="lg:col-span-7 space-y-5">
<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.05s">
<h2 class="text-lg font-bold mb-4 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-indigo-400"></span>Build Command</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Command</span></label>
<select id="cmd" class="select select-bordered w-full bg-[#12121a] border-gray-700 text-gray-100" onchange="updatePreview()">
<option value="build" selected>build</option>
<option value="test">test</option>
<option value="run">run</option>
</select>
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Package Path</span></label>
<input id="pkg" type="text" value="." class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
</div>
</div>

<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.1s">
<h2 class="text-lg font-bold mb-4 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-purple-400"></span>Obfuscation Options</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input id="lit" type="checkbox" class="toggle toggle-primary switch-huge" checked onchange="updatePreview()">
<span class="label-text font-medium">Literals</span>
</label>
<p class="text-xs text-gray-500 ml-12">Encrypt string & numeric literals</p>
</div>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input id="tiny" type="checkbox" class="toggle toggle-secondary switch-huge" checked onchange="updatePreview()">
<span class="label-text font-medium">Tiny</span>
</label>
<p class="text-xs text-gray-500 ml-12">Optimize for smallest binary size</p>
</div>
<div class="form-control">
<label class="label cursor-pointer justify-start gap-3">
<input id="dbg" type="checkbox" class="toggle toggle-accent switch-huge" onchange="updatePreview()">
<span class="label-text font-medium">Debug</span>
</label>
<p class="text-xs text-gray-500 ml-12">Write debug logs on run</p>
</div>
</div>
</div>

<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.15s">
<h2 class="text-lg font-bold mb-4 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-pink-400"></span>Advanced</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Seed</span></label>
<input id="seed" type="text" placeholder="random" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
<p class="text-xs text-gray-500 mt-1">Leave empty for random seed per build</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Debug Password</span></label>
<input id="dbgpw" type="password" placeholder="" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Output (-o)</span></label>
<input id="out" type="text" placeholder="" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Debug Dir</span></label>
<input id="dbgdir" type="text" placeholder="" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
<div class="form-control sm:col-span-2">
<label class="label"><span class="label-text text-gray-400">Extra Flags</span></label>
<input id="extra" type="text" placeholder="-ldflags=-s -w -tags=prod" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
</div>
</div>

<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.18s">
<h2 class="text-lg font-bold mb-4 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-rose-400"></span>Ignore Rules</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Ignore Func Substrings</span></label>
<input id="ignfunc" type="text" placeholder="handler,endpoint,api" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
<p class="text-xs text-gray-500 mt-1">Comma-separated substrings; matching func names are preserved</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Ignore Func Exact</span></label>
<input id="ignfuncex" type="text" placeholder="main,init" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
<p class="text-xs text-gray-500 mt-1">Comma-separated exact function names to preserve</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Ignore String Substrings</span></label>
<input id="ignstr" type="text" placeholder="Bearer,apiKey,secret" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
<p class="text-xs text-gray-500 mt-1">Comma-separated substrings; matching strings + their struct are preserved</p>
</div>
<div class="form-control">
<label class="label"><span class="label-text text-gray-400">Ignore String Exact</span></label>
<input id="ignstrex" type="text" placeholder="GET,POST" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
<p class="text-xs text-gray-500 mt-1">Comma-separated exact string literals to preserve</p>
</div>
</div>
</div>
</div>

<div class="lg:col-span-5 space-y-5">
<div class="glass rounded-2xl p-6 glow animate-in" style="animation-delay:.2s">
<h2 class="text-lg font-bold mb-3 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-emerald-400"></span>Generated Command</h2>
<div class="command-preview mono">
<div class="cmd-text" id="preview">alosgarble build .</div>
</div>
<div class="flex gap-3 mt-4">
<button onclick="doBuild()" class="btn btn-primary flex-1 btn-shine shadow-lg shadow-indigo-500/20">
<svg class="w-5 h-5 mr-1" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
Run Build
</button>
<button onclick="copyCmd()" class="btn btn-outline border-gray-600 text-gray-300 hover:bg-gray-800 hover:border-gray-500">
Copy
</button>
</div>
</div>

<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.25s">
<h2 class="text-lg font-bold mb-3 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-amber-400"></span>Config</h2>
<div class="form-control mb-3">
<label class="label"><span class="label-text text-gray-400">Save / Load Path</span></label>
<input id="cfgPath" type="text" value="alosgarble.json" class="input input-bordered w-full bg-[#12121a] border-gray-700 text-gray-100 mono" oninput="updatePreview()">
</div>
<div class="flex gap-3">
<button onclick="saveConfig()" class="btn btn-warning flex-1 btn-shine shadow-lg shadow-amber-500/20">
<svg class="w-5 h-5 mr-1" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
Save Config
</button>
<button onclick="loadConfig()" class="btn btn-outline border-gray-600 text-gray-300 hover:bg-gray-800 hover:border-gray-500">
Load
</button>
</div>
<p id="cfgMsg" class="text-xs mt-2 text-gray-500 min-h-[1.25rem]"></p>
</div>

<div class="glass rounded-2xl p-6 animate-in" style="animation-delay:.3s">
<h2 class="text-lg font-bold mb-3 flex items-center gap-2"><span class="w-2 h-2 rounded-full bg-blue-400"></span>Build Output</h2>
<div id="outBox" class="bg-[#0f0f13] border border-gray-800 rounded-xl p-4 h-64 overflow-auto mono text-xs text-gray-400">
<p class="text-gray-600 italic">Ready to build...</p>
</div>
</div>
</div>

</div>
</div>

<script>
function updatePreview(){
const cmd=document.getElementById('cmd').value;
const pkg=document.getElementById('pkg').value||'.';
const lit=document.getElementById('lit').checked;
const tiny=document.getElementById('tiny').checked;
const dbg=document.getElementById('dbg').checked;
const seed=document.getElementById('seed').value.trim();
const dbgpw=document.getElementById('dbgpw').value.trim();
const out=document.getElementById('out').value.trim();
const dbgdir=document.getElementById('dbgdir').value.trim();
const extra=document.getElementById('extra').value.trim();
const ignfunc=document.getElementById('ignfunc').value.trim();
const ignfuncex=document.getElementById('ignfuncex').value.trim();
const ignstr=document.getElementById('ignstr').value.trim();
const ignstrex=document.getElementById('ignstrex').value.trim();
let parts=['alosgarble'];
if(!lit)parts.push('-literals=false');
if(!tiny)parts.push('-tiny=false');
if(dbg)parts.push('-debug');
if(dbgpw)parts.push('-debugpassword='+dbgpw);
if(seed)parts.push('-seed='+seed);
if(dbgdir)parts.push('-debugdir='+dbgdir);
if(ignfunc)parts.push('-ignorefunc='+ignfunc);
if(ignfuncex)parts.push('-ignorefunc-exact='+ignfuncex);
if(ignstr)parts.push('-ignorestring='+ignstr);
if(ignstrex)parts.push('-ignorestring-exact='+ignstrex);
parts.push(cmd);
if(out)parts.push('-o',out);
if(extra)parts.push(...extra.split(/\s+/));
parts.push(pkg);
document.getElementById('preview').textContent=parts.join(' ');
}
function copyCmd(){
const t=document.getElementById('preview').textContent;
navigator.clipboard.writeText(t).then(()=>{
const b=event.target.closest('button');const old=b.textContent;b.textContent='Copied!';setTimeout(()=>b.textContent=old,1200);
});
}
async function doBuild(){
const box=document.getElementById('outBox');
box.innerHTML='<p class="text-indigo-400 animate-pulse">Building...</p>';
try{
const body={
cmd:document.getElementById('cmd').value,
pkg:document.getElementById('pkg').value||'.',
lit:document.getElementById('lit').checked,
tiny:document.getElementById('tiny').checked,
debug:document.getElementById('dbg').checked,
seed:document.getElementById('seed').value.trim(),
debugpassword:document.getElementById('dbgpw').value.trim(),
output:document.getElementById('out').value.trim(),
debugdir:document.getElementById('dbgdir').value.trim(),
extra:document.getElementById('extra').value.trim(),
ignorefunc:document.getElementById('ignfunc').value.trim(),
ignorefuncexact:document.getElementById('ignfuncex').value.trim(),
ignorestring:document.getElementById('ignstr').value.trim(),
ignorestringexact:document.getElementById('ignstrex').value.trim()
};
const r=await fetch('/api/build',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
const j=await r.json();
if(j.error){box.innerHTML='<p class="text-red-400">'+esc(j.error)+'</p>';return;}
box.innerHTML='<p class="text-emerald-400 mb-2">'+esc(j.message)+'</p>';
if(j.stdout)box.innerHTML+='<pre class="text-gray-300 whitespace-pre-wrap">'+esc(j.stdout)+'</pre>';
if(j.stderr)box.innerHTML+='<pre class="text-amber-300 whitespace-pre-wrap mt-2">'+esc(j.stderr)+'</pre>';
}catch(e){box.innerHTML='<p class="text-red-400">'+esc(e.message)+'</p>';}
}
async function saveConfig(){
const body={
cmd:document.getElementById('cmd').value,
pkg:document.getElementById('pkg').value||'.',
lit:document.getElementById('lit').checked,
tiny:document.getElementById('tiny').checked,
debug:document.getElementById('dbg').checked,
seed:document.getElementById('seed').value.trim(),
debugpassword:document.getElementById('dbgpw').value.trim(),
output:document.getElementById('out').value.trim(),
debugdir:document.getElementById('dbgdir').value.trim(),
extra:document.getElementById('extra').value.trim(),
ignorefunc:document.getElementById('ignfunc').value.trim(),
ignorefuncexact:document.getElementById('ignfuncex').value.trim(),
ignorestring:document.getElementById('ignstr').value.trim(),
ignorestringexact:document.getElementById('ignstrex').value.trim(),
path:document.getElementById('cfgPath').value.trim()||'alosgarble.json'
};
try{
const r=await fetch('/api/save-config',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
const j=await r.json();
document.getElementById('cfgMsg').textContent=j.message||j.error;
document.getElementById('cfgMsg').className='text-xs mt-2 '+(j.error?'text-red-400':'text-emerald-400');
}catch(e){
document.getElementById('cfgMsg').textContent=e.message;
document.getElementById('cfgMsg').className='text-xs mt-2 text-red-400';
}
}
async function loadConfig(){
const p=document.getElementById('cfgPath').value.trim()||'alosgarble.json';
try{
const r=await fetch('/api/load-config?path='+encodeURIComponent(p));
const j=await r.json();
if(j.error){document.getElementById('cfgMsg').textContent=j.error;document.getElementById('cfgMsg').className='text-xs mt-2 text-red-400';return;}
if(j.cmd!==undefined)document.getElementById('cmd').value=j.cmd;
if(j.pkg!==undefined)document.getElementById('pkg').value=j.pkg;
if(j.lit!==undefined)document.getElementById('lit').checked=j.lit;
if(j.tiny!==undefined)document.getElementById('tiny').checked=j.tiny;
if(j.debug!==undefined)document.getElementById('dbg').checked=j.debug;
if(j.seed!==undefined)document.getElementById('seed').value=j.seed;
if(j.debugpassword!==undefined)document.getElementById('dbgpw').value=j.debugpassword;
if(j.output!==undefined)document.getElementById('out').value=j.output;
if(j.debugdir!==undefined)document.getElementById('dbgdir').value=j.debugdir;
if(j.extra!==undefined)document.getElementById('extra').value=j.extra;
if(j.ignorefunc!==undefined)document.getElementById('ignfunc').value=j.ignorefunc;
if(j.ignorefuncexact!==undefined)document.getElementById('ignfuncex').value=j.ignorefuncexact;
if(j.ignorestring!==undefined)document.getElementById('ignstr').value=j.ignorestring;
if(j.ignorestringexact!==undefined)document.getElementById('ignstrex').value=j.ignorestringexact;
updatePreview();
document.getElementById('cfgMsg').textContent='Config loaded from '+p;
document.getElementById('cfgMsg').className='text-xs mt-2 text-emerald-400';
}catch(e){
document.getElementById('cfgMsg').textContent=e.message;
document.getElementById('cfgMsg').className='text-xs mt-2 text-red-400';
}
}
function esc(s){return(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
updatePreview();
</script>
</body>
</html>`))

type buildRequest struct {
	Cmd                string `json:"cmd"`
	Pkg                string `json:"pkg"`
	Lit                bool   `json:"lit"`
	Tiny               bool   `json:"tiny"`
	Debug              bool   `json:"debug"`
	Seed               string `json:"seed"`
	DebugPassword      string `json:"debugpassword"`
	Output             string `json:"output"`
	DebugDir           string `json:"debugdir"`
	Extra              string `json:"extra"`
	IgnoreFunc         string `json:"ignorefunc"`
	IgnoreFuncExact    string `json:"ignorefuncexact"`
	IgnoreString       string `json:"ignorestring"`
	IgnoreStringExact  string `json:"ignorestringexact"`
}

type configRequest struct {
	buildRequest
	Path string `json:"path"`
}

type apiResponse struct {
	Error   string `json:"error,omitempty"`
	Message string `json:"message,omitempty"`
	Stdout  string `json:"stdout,omitempty"`
	Stderr  string `json:"stderr,omitempty"`
}

func commandWeb(args []string) error {
	app := fiber.New()
	app.Use(cors.New())

	app.Get("/", func(c fiber.Ctx) error {
		c.Set("Content-Type", "text/html; charset=utf-8")
		return pageTemplate.Execute(c.Response().BodyWriter(), nil)
	})

	app.Post("/api/build", func(c fiber.Ctx) error {
		var req buildRequest
		if err := json.Unmarshal(c.Body(), &req); err != nil {
			return c.JSON(apiResponse{Error: "invalid JSON: " + err.Error()})
		}

		cmdArgs := buildCmdArgs(req)
		cmd := exec.Command("alosgarble", cmdArgs...)
		cmd.Dir = req.Pkg
		if req.Pkg == "." {
			cmd.Dir = ""
		}
		if cmd.Dir == "" {
			var err error
			cmd.Dir, err = os.Getwd()
			if err != nil {
				cmd.Dir = "."
			}
		}

		out, err := cmd.CombinedOutput()
		resp := apiResponse{
			Stdout: string(out),
		}
		if err != nil {
			resp.Error = err.Error()
			resp.Stderr = string(out)
		} else {
			resp.Message = "Build completed successfully"
		}
		return c.JSON(resp)
	})

	app.Post("/api/save-config", func(c fiber.Ctx) error {
		var req configRequest
		if err := json.Unmarshal(c.Body(), &req); err != nil {
			return c.JSON(apiResponse{Error: "invalid JSON: " + err.Error()})
		}
		path := req.Path
		if path == "" {
			path = defaultConfigPath
		}
		if !filepath.IsAbs(path) {
			wd, _ := os.Getwd()
			path = filepath.Join(wd, path)
		}
		data, err := json.MarshalIndent(req.buildRequest, "", "  ")
		if err != nil {
			return c.JSON(apiResponse{Error: "marshal error: " + err.Error()})
		}
		if err := os.WriteFile(path, data, 0o644); err != nil {
			return c.JSON(apiResponse{Error: "write error: " + err.Error()})
		}
		return c.JSON(apiResponse{Message: "Config saved to " + path})
	})

	app.Get("/api/load-config", func(c fiber.Ctx) error {
		path := c.Query("path", defaultConfigPath)
		if !filepath.IsAbs(path) {
			wd, _ := os.Getwd()
			path = filepath.Join(wd, path)
		}
		data, err := os.ReadFile(path)
		if err != nil {
			return c.JSON(apiResponse{Error: "read error: " + err.Error()})
		}
		var cfg buildRequest
		if err := json.Unmarshal(data, &cfg); err != nil {
			return c.JSON(apiResponse{Error: "parse error: " + err.Error()})
		}
		return c.JSON(cfg)
	})

	port := "8080"
	if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
		port = args[0]
	}
	fmt.Printf("ALOS Garble Web UI running on http://localhost:%s\n", port)
	return app.Listen(":" + port)
}

func buildCmdArgs(req buildRequest) []string {
	var args []string
	if !req.Lit {
		args = append(args, "-literals=false")
	}
	if !req.Tiny {
		args = append(args, "-tiny=false")
	}
	if req.Debug {
		args = append(args, "-debug")
	}
	if req.DebugPassword != "" {
		args = append(args, "-debugpassword="+req.DebugPassword)
	}
	if req.Seed != "" {
		args = append(args, "-seed="+req.Seed)
	}
	if req.DebugDir != "" {
		args = append(args, "-debugdir="+req.DebugDir)
	}
	if req.IgnoreFunc != "" {
		args = append(args, "-ignorefunc="+req.IgnoreFunc)
	}
	if req.IgnoreFuncExact != "" {
		args = append(args, "-ignorefunc-exact="+req.IgnoreFuncExact)
	}
	if req.IgnoreString != "" {
		args = append(args, "-ignorestring="+req.IgnoreString)
	}
	if req.IgnoreStringExact != "" {
		args = append(args, "-ignorestring-exact="+req.IgnoreStringExact)
	}
	args = append(args, req.Cmd)
	if req.Output != "" {
		args = append(args, "-o", req.Output)
	}
	if req.Extra != "" {
		args = append(args, strings.Fields(req.Extra)...)
	}
	args = append(args, req.Pkg)
	return args
}
