diff --git a/cmd/texd/main.go b/cmd/texd/main.go index 548e092..609f15f 100644 --- a/cmd/texd/main.go +++ b/cmd/texd/main.go @@ -27,12 +27,23 @@ import ( "go.uber.org/zap/zapcore" ) +const ( + defaultQueueTimeout = 10 * time.Second + defaultMaxJobSize = 50 * units.MiB + defaultCompileTimeout = time.Minute + defaultRetentionPoolSize = 100 * units.MiB + + exitSuccess = 0 + exitFlagErr = 2 + exitTimeout = 10 * time.Second +) + var opts = service.Options{ Addr: ":2201", QueueLength: runtime.GOMAXPROCS(0), - QueueTimeout: 10 * time.Second, - MaxJobSize: 50 * units.MiB, - CompileTimeout: time.Minute, + QueueTimeout: defaultQueueTimeout, + MaxJobSize: defaultMaxJobSize, + CompileTimeout: defaultCompileTimeout, Mode: "local", Executor: exec.LocalExec, KeepJobs: service.KeepJobsNever, @@ -59,8 +70,8 @@ var ( 1: {"purge", "purge-on-start"}, 2: {"access"}, } - retPolItems = 1000 // number of items in refstore - retPolSize = units.BytesSize(float64(100 * units.MiB)) // max total file size + retPolItems = 1000 // number of items in refstore + retPolSize = units.BytesSize(float64(defaultRetentionPoolSize)) // max total file size ) func retentionPolicy() (refstore.RetentionPolicy, error) { @@ -69,7 +80,7 @@ func retentionPolicy() (refstore.RetentionPolicy, error) { return &refstore.KeepForever{}, nil case 1: return &refstore.PurgeOnStart{}, nil - case 2: + case 2: //nolint:mnd sz, err := units.FromHumanSize(retPolSize) if err != nil { return nil, err @@ -83,10 +94,10 @@ func retentionPolicy() (refstore.RetentionPolicy, error) { panic("not reached") } -func parseFlags() []string { - fs := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) +func parseFlags(progname string, args ...string) []string { + fs := pflag.NewFlagSet(progname, pflag.ContinueOnError) fs.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage of %s:\n", progname) fs.PrintDefaults() } @@ -122,21 +133,21 @@ func parseFlags() []string { fs.StringVar(&retPolSize, "rp-access-size", retPolSize, "for retention-policy=access: maximum total size of items in access list, before evicting files") - switch err := fs.Parse(os.Args[1:]); { + switch err := fs.Parse(args); { case errors.Is(err, pflag.ErrHelp): // pflag already has called fs.Usage - os.Exit(0) + os.Exit(exitSuccess) case err != nil: fmt.Fprintf(os.Stderr, "Error parsing flags:\n\t%v\n", err) - os.Exit(2) + os.Exit(exitFlagErr) } return fs.Args() } -func main() { +func main() { //nolint:funlen texd.PrintBanner(os.Stdout) - images := parseFlags() //nolint:ifshort // func has sideeffects + images := parseFlags(os.Args[0], os.Args[1:]...) log, sync := setupLogger() defer sync() @@ -155,12 +166,12 @@ func main() { zap.String("flag", "--tex-engine"), zap.Error(err)) } - if max, err := units.FromHumanSize(maxJobSize); err != nil { + if maxsz, err := units.FromHumanSize(maxJobSize); err != nil { log.Fatal("error parsing maximum job size", zap.String("flag", "--max-job-size"), zap.Error(err)) } else { - opts.MaxJobSize = max + opts.MaxJobSize = maxsz } if storageDSN != "" { rp, err := retentionPolicy() @@ -202,12 +213,10 @@ func main() { onExit(log, stop) } -const exitTimeout = 10 * time.Second - type stopFun func(context.Context) error func onExit(log *zap.Logger, stopper ...stopFun) { - exitCh := make(chan os.Signal, 2) + exitCh := make(chan os.Signal, 2) //nolint:mnd // idiomatic signal.Notify(exitCh, syscall.SIGINT, syscall.SIGTERM) sig := <-exitCh diff --git a/tex/document.go b/tex/document.go index 87943e6..4f2a73c 100644 --- a/tex/document.go +++ b/tex/document.go @@ -18,6 +18,16 @@ import ( // should specify main files explicitly. const Mark = "%!texd" +// When no main input file is given, the first KiB of each TeX file is +// searched for a `\documentclass` macro. This is another last resort +// measurement. +const guessLimit = 1024 // first KiB + +const ( + o_rwx = 0o700 // read, write, execute permissions for owner + o_rw = 0o600 // read and write permissions for owner +) + // texFs can be overridden in tests. var texFs = afero.NewOsFs() @@ -57,8 +67,8 @@ type fileWriter struct { func (w *fileWriter) Write(p []byte) (int, error) { if w.file.isCandidate() { // fill buf, if buf has capacity - if max := len(w.buf); w.off < max { - if n := max - w.off; n > len(p) { + if pos := len(w.buf); w.off < pos { + if n := pos - w.off; n > len(p) { // p fits completely into buf's rest capacity copy(w.buf[w.off:], p) w.off += len(p) @@ -271,12 +281,12 @@ func (doc *document) NewWriter(name string) (wc io.WriteCloser, err error) { var ok bool if file.name, ok = cleanpath(name); !ok { err = InputError("invalid file name", nil, nil) - return + return wc, err } if _, exists := doc.files[name]; exists { err = InputError("duplicate file name", nil, nil) - return + return wc, err } else { doc.files[name] = file } @@ -284,20 +294,20 @@ func (doc *document) NewWriter(name string) (wc io.WriteCloser, err error) { var wd string wd, err = doc.WorkingDirectory() if err != nil { - return // err is already an errWithCategory + return wc, err // err is already an errWithCategory } if dir := path.Dir(name); dir != "" { - if osErr := doc.fs.MkdirAll(path.Join(wd, dir), 0o700); osErr != nil { + if osErr := doc.fs.MkdirAll(path.Join(wd, dir), o_rwx); osErr != nil { err = InputError("cannot create directory", osErr, nil) - return + return wc, err } } - f, osErr := doc.fs.OpenFile(path.Join(wd, name), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) + f, osErr := doc.fs.OpenFile(path.Join(wd, name), os.O_CREATE|os.O_APPEND|os.O_WRONLY, o_rw) if osErr != nil { err = InputError("cannot create file", osErr, nil) - return + return wc, err } log := doc.log.With(zap.String("filename", file.name)) @@ -310,7 +320,7 @@ func (doc *document) NewWriter(name string) (wc io.WriteCloser, err error) { log: log, file: file, wc: f, - buf: make([]byte, 1024), + buf: make([]byte, guessLimit), }, nil }