From 8cfdb0713122a9b8f2ea236b9505ae36bb48d39b Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Mon, 27 Nov 2017 17:22:02 +0100 Subject: pkgs/profpatsch/nman: rewrite in golang Apart from using the go compiler, switch from `nix-shell -p`, which only worked in certain cases, to a `nix-build` with fallback for `man` and default outputs. Thanks to @muesli for the golang intro. --- pkgs/profpatsch/nman/default.nix | 6 +- pkgs/profpatsch/nman/nman.go | 137 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 pkgs/profpatsch/nman/nman.go (limited to 'pkgs/profpatsch/nman') diff --git a/pkgs/profpatsch/nman/default.nix b/pkgs/profpatsch/nman/default.nix index 10e5417f..b9b967cf 100644 --- a/pkgs/profpatsch/nman/default.nix +++ b/pkgs/profpatsch/nman/default.nix @@ -1,12 +1,12 @@ -{ lib, runCommandCC }: +{ lib, runCommandNoCC, go }: -runCommandCC "nman" { +runCommandNoCC "nman" { meta = with lib; { description = "Invoke manpage in temporary nix-shell"; license = licenses.gpl3; }; } '' - cc -o nman ${./nman.c} + ${lib.getBin go}/bin/go build -o nman ${./nman.go} install -D nman $out/bin/nman '' diff --git a/pkgs/profpatsch/nman/nman.go b/pkgs/profpatsch/nman/nman.go new file mode 100644 index 00000000..e6b6247d --- /dev/null +++ b/pkgs/profpatsch/nman/nman.go @@ -0,0 +1,137 @@ +package main + +import ( + "fmt" + "os" + "strconv" + "log" + "bytes" + "os/exec" + "io/ioutil" + "syscall" +) + +var usage = `nman drvAttr [section|page] [page] + +Open man pages in a temporary nix-shell. +1 If one argument is given, the drvAttr & page have the same name. +2 If two arguments are given and the second arg is + a ) like 1, but in the man section + a ) like 1, but open the page +3 If three arguments are given, the order is +` + +func lookupManPage(manpath string, manSection int64, manPage string) (int, error) { + if manSection < -1 { + panic("manSection must not be < -1") + } + // holy debug printf + // fmt.Printf("attr: %s, sect: %d, page: %s\n", drvAttr, manSection, manPage) + + manBin, err := exec.LookPath("man"); + if err != nil { + return 0, fmt.Errorf("man executable not in PATH") + } + + var manArgs []string + if (manSection == -1) { + manArgs = []string{manPage} + } else { + manArgs = []string{strconv.FormatInt(manSection, 10), manPage} + } + + man := exec.Command(manBin, manArgs...) + + man.Env = append( + os.Environ(), + fmt.Sprintf("MANPATH=%s", manpath)) + man.Stderr = os.Stderr + man.Stdout = os.Stdout + err = man.Run() + if exiterr, ok := err.(*exec.ExitError); ok { + ws := exiterr.Sys().(syscall.WaitStatus) + return ws.ExitStatus(), nil + } else { + return 0, err + } + +} + +func buildManOutput(drvAttr string) (string, error) { + nixExpr := fmt.Sprintf( + `with import {}; %s.man or %s.doc or %s.out or %s`, + drvAttr, drvAttr, drvAttr, drvAttr) + + nixBuild := exec.Command("nix-build", "-E", nixExpr) + nixBuild.Stderr = os.Stderr + pipe, err := nixBuild.StdoutPipe() + if err != nil { return "", fmt.Errorf("could not access stdout of nix-build: %s", err) } + + err = nixBuild.Start() + if err != nil { return "", fmt.Errorf("could not start nix-build: %s", err) } + + out, err := ioutil.ReadAll(pipe) + if err != nil { return "", fmt.Errorf("could not read from nix-build: %s", err) } + + lines := bytes.Split(bytes.TrimSpace(out), []byte("\n")) + err = nixBuild.Wait() + if err != nil { return "", fmt.Errorf("nix-build process died: %s", err) } + + // last line ouf output looks like: /nix/store/abc…-foobar.drv!output + drvPath := bytes.Split(lines[len(lines)-1], []byte("!"))[0] + if _, err := os.Stat(string(drvPath)); err != nil { + return "", fmt.Errorf("%s doesn’t look like an output path: %s", drvPath, err) + } + + return string(drvPath), nil +} + +func main() { + var manPage string + var drvAttr string + + args := os.Args + + // man section or -1 if no man section + var manSection int64 = -1 + if (len(args) >= 3) { + i, err := strconv.ParseUint(args[2], 10, 64) + if err == nil && i >= 0 { + manSection = int64(i) + } + } + + // the first argument is always a derivation attribute + switch len(args) { + case 2: + // arg is package and drv attr + manPage = args[1] + case 3: + if (manSection == -1) { + // like 2, but arg 2 is package + manPage = args[2] + } else { + // man section given, page is arg 1 + manPage = args[1] + } + case 4: + // arg 2 is manSection, arg 3 is package + manPage = args[3] + default: + fmt.Print(usage) + os.Exit(-1) + } + + drvAttr = args[1] + drvPath, err := buildManOutput(drvAttr) + if err != nil { + log.Fatal(err) + } + exitStatus, err := lookupManPage(drvPath + "/share/man", manSection, manPage) + if err != nil { + log.Fatal(err) + } + + os.Exit(exitStatus) + +} -- cgit 1.4.1