2021-08-26 14:32:51 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
2021-08-26 16:00:10 +00:00
|
|
|
"io"
|
2021-08-26 14:32:51 +00:00
|
|
|
"log"
|
2021-08-26 15:31:20 +00:00
|
|
|
"os"
|
2021-08-26 14:32:51 +00:00
|
|
|
"os/exec"
|
2021-08-26 21:29:19 +00:00
|
|
|
"path/filepath"
|
2021-08-26 14:32:51 +00:00
|
|
|
"regexp"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
2021-08-26 21:29:19 +00:00
|
|
|
"strings"
|
2021-08-26 14:32:51 +00:00
|
|
|
)
|
|
|
|
|
2021-08-26 15:31:20 +00:00
|
|
|
const (
|
|
|
|
ErrNoCompiler = "none of the following latex compilers available: [latexmk, pdflatex]"
|
|
|
|
)
|
|
|
|
|
2021-08-26 14:32:51 +00:00
|
|
|
func main() {
|
|
|
|
fmt.Printf("Pdflatex command exists: %t\n", commandExists("pdflatex"))
|
2021-08-26 15:31:20 +00:00
|
|
|
fmt.Printf("LatexMk command exists: %t\n", commandExists("latexmk"))
|
|
|
|
fmt.Printf("Operation System: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
2021-08-26 14:32:51 +00:00
|
|
|
|
|
|
|
compileAndInstall()
|
|
|
|
}
|
|
|
|
|
|
|
|
func compileAndInstall() {
|
2021-08-26 20:40:56 +00:00
|
|
|
out, err := compileLatex()
|
2021-08-26 15:31:20 +00:00
|
|
|
|
2021-08-26 14:32:51 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("An error occured while compiling the document!")
|
|
|
|
|
2021-08-26 15:31:20 +00:00
|
|
|
if err.Error() == ErrNoCompiler {
|
|
|
|
log.Fatal(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if filename := parseMissingFile(out); filename != "" {
|
2021-08-26 14:32:51 +00:00
|
|
|
fmt.Printf("We need to download: %s\n", filename)
|
|
|
|
|
2021-08-26 15:31:20 +00:00
|
|
|
// now we need to perform a root check
|
2021-08-26 14:32:51 +00:00
|
|
|
if rootCheck() {
|
|
|
|
log.Println("Awesome! You are now running this program with root permissions!")
|
|
|
|
|
|
|
|
if installFile(filename) {
|
2021-08-26 15:31:20 +00:00
|
|
|
// we remove the main aux file to really trigger a rebuild!
|
|
|
|
os.Remove("main.aux")
|
2021-08-26 14:32:51 +00:00
|
|
|
// if successfully installed we try to compile again
|
|
|
|
compileAndInstall()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Fatal("This program must be run as root! (sudo)")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Println(*out)
|
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
fmt.Println("Another build error occured!")
|
2021-08-26 14:32:51 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-08-26 21:29:19 +00:00
|
|
|
fmt.Println("Document built successfully!")
|
2021-08-26 14:32:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseMissingFile(output *string) string {
|
|
|
|
matchfile := regexp.MustCompile("! LaTeX Error: File `([^`']*)' not found|! I can't find file `([^`']*)'.")
|
|
|
|
matches := matchfile.FindStringSubmatch(*output)
|
|
|
|
if matches != nil {
|
|
|
|
if matches[1] != "" {
|
|
|
|
return matches[1]
|
|
|
|
} else {
|
|
|
|
return matches[2]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ok now we try to find a font error
|
|
|
|
fontregex := regexp.MustCompile(`! Font \\[^=]*=([^\s]*)\s`)
|
|
|
|
fontmatch := fontregex.FindStringSubmatch(*output)
|
|
|
|
if fontmatch != nil {
|
|
|
|
if fontmatch[1] != "" {
|
|
|
|
return fontmatch[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now try babel errors
|
|
|
|
babelregex := regexp.MustCompile("Unknown option `([^`']*)'. Either you misspelled")
|
|
|
|
babelmatch := babelregex.FindStringSubmatch(*output)
|
|
|
|
if babelmatch != nil {
|
|
|
|
if babelmatch[1] != "" {
|
|
|
|
return babelmatch[1] + ".ldf"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if a specific system command is available
|
|
|
|
func commandExists(cmd string) bool {
|
|
|
|
_, err := exec.LookPath(cmd)
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:40:56 +00:00
|
|
|
// compile latex document with passed cmd args (or default ones if nothing passed) and return output or error
|
|
|
|
func compileLatex() (*string, error) {
|
2021-08-26 15:31:20 +00:00
|
|
|
app := ""
|
|
|
|
if commandExists("latexmk") {
|
|
|
|
app = "latexmk"
|
|
|
|
} else if commandExists("pdflatex") {
|
|
|
|
app = "pdflatex"
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf(ErrNoCompiler)
|
|
|
|
}
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 20:40:56 +00:00
|
|
|
argsWithoutProg := os.Args[1:]
|
|
|
|
filename := "main.tex"
|
|
|
|
if len(argsWithoutProg) > 0 {
|
|
|
|
filename = argsWithoutProg[len(argsWithoutProg)-1]
|
|
|
|
// cut last arg --> filename
|
|
|
|
argsWithoutProg = argsWithoutProg[:len(argsWithoutProg)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert default args
|
|
|
|
argsWithoutProg = append(argsWithoutProg, "-file-line-error",
|
2021-08-26 14:32:51 +00:00
|
|
|
"-interaction=nonstopmode",
|
|
|
|
"-synctex=1",
|
2021-08-26 20:40:56 +00:00
|
|
|
"-output-format=pdf", filename)
|
|
|
|
cmd := exec.Command(app, argsWithoutProg...)
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 16:00:10 +00:00
|
|
|
stdout, _ := cmd.StdoutPipe()
|
|
|
|
stderr, _ := cmd.StderrPipe()
|
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
fmt.Println("Building:")
|
2021-08-26 16:00:10 +00:00
|
|
|
cmd.Start()
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 16:00:10 +00:00
|
|
|
output := ""
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
scanner := bufio.NewScanner(stdout)
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
m := scanner.Text()
|
|
|
|
output += m + "\n"
|
|
|
|
printPoint()
|
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
scanner := bufio.NewScanner(stderr)
|
|
|
|
for scanner.Scan() {
|
|
|
|
scanner.Text()
|
|
|
|
printPoint()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
err := cmd.Wait()
|
2021-08-26 14:32:51 +00:00
|
|
|
|
|
|
|
return &output, err
|
|
|
|
}
|
|
|
|
|
2021-08-26 16:00:10 +00:00
|
|
|
var i = 0
|
|
|
|
|
2021-08-26 20:40:56 +00:00
|
|
|
// printPoint print a point with every 10th method call
|
2021-08-26 16:00:10 +00:00
|
|
|
func printPoint() {
|
|
|
|
if i%10 == 0 {
|
|
|
|
fmt.Printf(".")
|
|
|
|
}
|
|
|
|
|
|
|
|
i++
|
|
|
|
if i > 500 {
|
|
|
|
i = 0
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:40:56 +00:00
|
|
|
// rootCheck perform a root user check
|
2021-08-26 14:32:51 +00:00
|
|
|
func rootCheck() bool {
|
|
|
|
cmd := exec.Command("id", "-u")
|
|
|
|
output, err := cmd.Output()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// output has trailing \n
|
|
|
|
// need to remove the \n
|
|
|
|
// otherwise it will cause error for strconv.Atoi
|
|
|
|
// log.Println(output[:len(output)-1])
|
|
|
|
|
|
|
|
// 0 = root, 501 = non-root user
|
|
|
|
i, err := strconv.Atoi(string(output[:len(output)-1]))
|
|
|
|
|
|
|
|
if err != nil {
|
2021-08-26 15:31:20 +00:00
|
|
|
// maybe no unix system?
|
2021-08-26 14:32:51 +00:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2021-08-26 15:31:20 +00:00
|
|
|
return i == 0
|
2021-08-26 14:32:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func installFile(filename string) bool {
|
2021-08-26 21:29:19 +00:00
|
|
|
if commandExists("dnf") {
|
|
|
|
cmd := exec.Command("dnf", "-y", "install", fmt.Sprintf("tex(%s)", filename))
|
|
|
|
fmt.Println(cmd.String())
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
stdout, _ := cmd.StdoutPipe()
|
|
|
|
stderr, _ := cmd.StderrPipe()
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
fmt.Println("running dnf install now!")
|
|
|
|
cmd.Start()
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
printReadCloserToStdout(stdout)
|
|
|
|
printReadCloserToStdout(stderr)
|
2021-08-26 15:31:20 +00:00
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
err := cmd.Wait()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
} else if commandExists("tlmgr") {
|
|
|
|
fmt.Println("dnf not existing -> trying to install with tlmgr")
|
2021-08-26 14:32:51 +00:00
|
|
|
|
2021-08-26 21:29:19 +00:00
|
|
|
// tlmgr package name should be filename without suffix
|
|
|
|
cmd := exec.Command("tlmgr", "install", strings.TrimSuffix(filename, filepath.Ext(filename)))
|
|
|
|
fmt.Println(cmd.String())
|
|
|
|
|
|
|
|
stdout, _ := cmd.StdoutPipe()
|
|
|
|
stderr, _ := cmd.StderrPipe()
|
|
|
|
|
|
|
|
fmt.Println("running tlmgr install now!")
|
|
|
|
cmd.Start()
|
|
|
|
|
|
|
|
printReadCloserToStdout(stdout)
|
|
|
|
printReadCloserToStdout(stderr)
|
|
|
|
|
|
|
|
err := cmd.Wait()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
fmt.Println("There seems to be no tex distribution to be installed??")
|
2021-08-26 14:32:51 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2021-08-26 16:00:10 +00:00
|
|
|
|
|
|
|
func printReadCloserToStdout(reader io.ReadCloser) {
|
|
|
|
go func() {
|
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
for scanner.Scan() {
|
|
|
|
m := scanner.Text()
|
|
|
|
fmt.Println(m)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|