aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/extpo/main.go305
-rw-r--r--cmd/exttmpl/main.go224
-rw-r--r--cmd/extwiki/main.go151
-rw-r--r--cmd/mfmt/main.go189
-rw-r--r--cmd/mfmt/main_test.go140
-rw-r--r--cmd/mfmt/util_test.go35
6 files changed, 456 insertions, 588 deletions
diff --git a/cmd/extpo/main.go b/cmd/extpo/main.go
new file mode 100644
index 0000000..6aa1503
--- /dev/null
+++ b/cmd/extpo/main.go
@@ -0,0 +1,305 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
+ "text/template/parse"
+ "time"
+)
+
+type config struct {
+ arg int
+ plural int
+ context int
+ domain int
+}
+
+type translation struct {
+ msgid string
+ msgidPlural string
+ msgctxt string
+ domain string
+}
+
+type transinfo struct {
+ comment string
+ locs []loc
+}
+
+type loc struct {
+ file string
+ line int
+}
+
+func (l loc) String() string {
+ return fmt.Sprintf("%s:%d", l.file, l.line)
+}
+
+var (
+ currentFile []byte
+ currentPath string
+ lastComment string
+ translations = make(map[translation]transinfo)
+ configs = map[string]config{
+ "Get": {1, -1, -1, -1},
+ "GetC": {1, -1, 2, -1},
+ "GetD": {2, -1, -1, 1},
+ "GetDC": {2, -1, 3, 1},
+ "GetN": {1, 2, -1, -1},
+ "GetNC": {1, 2, 4, -1},
+ "GetND": {2, 3, -1, 1},
+ "GetNDC": {2, 3, 5, 1},
+ }
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n",
+ filepath.Base(os.Args[0]))
+ flag.PrintDefaults()
+}
+
+func main() {
+ try(os.Chdir(filepath.Dir(os.Args[0])))
+
+ outpath := flag.String("out", "-", "output file")
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() < 1 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ var outfile *os.File
+ if *outpath == "-" {
+ outfile = os.Stdout
+ } else {
+ outfile = try2(os.Create(*outpath))
+ }
+
+ for _, f := range flag.Args() {
+ process(f)
+ }
+
+ fmt.Fprintf(outfile, `# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: %s\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+`, time.Now().Format("2006-01-02 15:04-0700"))
+
+ for tl, ti := range translations {
+ if ti.comment != "" {
+ fmt.Fprintln(outfile, "#.", ti.comment)
+ }
+
+ slices.SortFunc(ti.locs, func(a, b loc) int {
+ if x := strings.Compare(a.file, b.file); x != 0 {
+ return x
+ }
+ return a.line - b.line
+ })
+ for _, x := range ti.locs {
+ fmt.Fprintln(outfile, "#:", x)
+ }
+
+ if tl.msgctxt != "" {
+ writeField(outfile, "msgctxt", tl.msgctxt)
+ }
+ writeField(outfile, "msgid", tl.msgid)
+ if tl.msgidPlural != "" {
+ writeField(outfile, "msgid_plural", tl.msgidPlural)
+ writeField(outfile, "msgstr[0]", "")
+ writeField(outfile, "msgstr[1]", "")
+ } else {
+ writeField(outfile, "msgstr", "")
+ }
+ fmt.Fprint(outfile, "\n")
+ }
+}
+
+func process(path string) {
+ currentPath = path
+ currentFile = try2(os.ReadFile(path))
+ trees := make(map[string]*parse.Tree)
+ t := parse.New(path)
+ t.Mode |= parse.ParseComments | parse.SkipFuncCheck
+ try2(t.Parse(string(currentFile), "", "", trees))
+ for _, t := range trees {
+ processNode(t.Root)
+ }
+}
+
+func processNode(node parse.Node) {
+ switch n := node.(type) {
+ case *parse.ListNode:
+ for _, m := range n.Nodes {
+ processNode(m)
+ }
+ case *parse.PipeNode:
+ for _, m := range n.Cmds {
+ processNode(m)
+ }
+ case *parse.TemplateNode:
+ processNode(n.Pipe)
+ case *parse.IfNode:
+ processBranch(n.BranchNode)
+ case *parse.RangeNode:
+ processBranch(n.BranchNode)
+ case *parse.WithNode:
+ processBranch(n.BranchNode)
+ case *parse.BranchNode:
+ processBranch(*n)
+ case *parse.ActionNode:
+ processNode(n.Pipe)
+ case *parse.CommandNode:
+ if len(n.Args) == 0 {
+ break
+ }
+
+ var funcname string
+ f, ok := n.Args[0].(*parse.FieldNode)
+ if !ok || len(f.Ident) == 0 {
+ ff, ok := n.Args[0].(*parse.VariableNode)
+ if !ok || len(ff.Ident) == 0 {
+ fff, ok := n.Args[0].(*parse.IdentifierNode)
+ if !ok {
+ for _, pipe := range n.Args {
+ processNode(pipe)
+ }
+ break
+ }
+ funcname = fff.Ident
+ } else {
+ funcname = ff.Ident[len(ff.Ident)-1]
+ }
+ } else {
+ funcname = f.Ident[len(f.Ident)-1]
+ }
+
+ cfg, ok := configs[funcname]
+ if !ok {
+ for _, pipe := range n.Args {
+ processNode(pipe)
+ }
+ break
+ }
+
+ var (
+ tl translation
+ linenr int
+ )
+
+ if sn, ok := n.Args[cfg.arg].(*parse.StringNode); ok {
+ tl.msgid = sn.Text
+ linenr = getlinenr(sn.Pos.Position())
+ } else {
+ break
+ }
+ if cfg.plural != -1 {
+ if sn, ok := n.Args[cfg.plural].(*parse.StringNode); ok {
+ tl.msgidPlural = sn.Text
+ }
+ }
+ if cfg.context != -1 {
+ if sn, ok := n.Args[cfg.context].(*parse.StringNode); ok {
+ tl.msgctxt = sn.Text
+ }
+ }
+
+ ti := translations[tl]
+ if lastComment != "" {
+ ti.comment = lastComment
+ lastComment = ""
+ }
+ ti.locs = append(ti.locs, loc{currentPath, linenr})
+ translations[tl] = ti
+ case *parse.CommentNode:
+ if strings.HasPrefix(n.Text, "/* TRANSLATORS:") {
+ lastComment = strings.TrimSpace(n.Text[2 : len(n.Text)-2])
+ }
+ }
+}
+
+func processBranch(n parse.BranchNode) {
+ processNode(n.List)
+ if n.ElseList != nil {
+ processNode(n.ElseList)
+ }
+}
+
+func writeField(w io.Writer, pfx, s string) {
+ fmt.Fprintf(w, "%s ", pfx)
+ if strings.ContainsRune(s, '\n') {
+ fmt.Fprintln(w, "\"\"")
+ lines := strings.SplitAfter(s, "\n")
+ n := len(lines)
+ if n > 1 && lines[n-1] == "" {
+ lines = lines[:n-1]
+ }
+ for _, ss := range lines {
+ writeLine(w, ss)
+ }
+ fmt.Fprintln(w, "\"\"")
+ } else {
+ writeLine(w, s)
+ }
+}
+
+func writeLine(w io.Writer, s string) {
+ fmt.Fprint(w, "\"")
+ for _, c := range s {
+ switch c {
+ case '\\', '"':
+ fmt.Fprintf(w, "\\%c", c)
+ case '\n':
+ fmt.Fprint(w, "\\n")
+ default:
+ fmt.Fprintf(w, "%c", c)
+ }
+ }
+ fmt.Fprintln(w, "\"")
+}
+
+func getlinenr(p parse.Pos) int {
+ return bytes.Count(currentFile[:p], []byte{'\n'}) + 1
+}
+
+func try(err error) {
+ if err != nil {
+ die(err)
+ }
+}
+
+func try2[T any](val T, err error) T {
+ try(err)
+ return val
+}
+
+func warn(err any) {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
+}
+
+func die(err any) {
+ warn(err)
+ os.Exit(1)
+}
diff --git a/cmd/exttmpl/main.go b/cmd/exttmpl/main.go
deleted file mode 100644
index 299bf2a..0000000
--- a/cmd/exttmpl/main.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "slices"
- "strings"
- "text/template/parse"
-
- "golang.org/x/text/language"
- "golang.org/x/text/message/pipeline"
- "golang.org/x/tools/go/packages"
-)
-
-const (
- pkgbase = "git.thomasvoss.com/euro-cash.eu"
- srclang = "en"
- srcdir = "./src"
- transdir = srcdir + "/rosetta"
- outfile = "catalog.gen.go"
- transfn = "T"
-)
-
-func main() {
- /* cd to the project root directory */
- try(os.Chdir(filepath.Dir(os.Args[0])))
-
- pkgnames := packageList(".")
-
- var paths []string
- pkgs := try2(packages.Load(&packages.Config{
- Mode: packages.NeedFiles | packages.NeedEmbedFiles,
- }, pkgnames...))
-
- for _, pkg := range pkgs {
- if len(pkg.Errors) != 0 {
- for _, err := range pkg.Errors {
- warn(err.Msg)
- }
- os.Exit(1)
- }
- for _, f := range pkg.EmbedFiles {
- if filepath.Ext(f) == ".tmpl" {
- paths = append(paths, f)
- }
- }
- }
-
- msgs := make([]pipeline.Message, 0, 1024)
- for _, path := range paths {
- f := try2(os.ReadFile(path))
- trees := make(map[string]*parse.Tree)
- t := parse.New("name")
- t.Mode |= parse.SkipFuncCheck
- try2(t.Parse(string(f), "", "", trees))
- for _, t := range trees {
- process(&msgs, t.Root)
- }
- }
-
- pconf := &pipeline.Config{
- Supported: languages(),
- SourceLanguage: language.Make(srclang),
- Packages: pkgnames,
- Dir: transdir,
- GenFile: outfile,
- GenPackage: srcdir,
- }
-
- state := try2(pipeline.Extract(pconf))
- state.Extracted.Messages = append(state.Extracted.Messages, msgs...)
-
- try(state.Import())
- try(state.Merge())
- try(state.Export())
- try(state.Generate())
-}
-
-func process(tmplMsgs *[]pipeline.Message, node parse.Node) {
- switch node.Type() {
- case parse.NodeList:
- if ln, ok := node.(*parse.ListNode); ok {
- for _, n := range ln.Nodes {
- process(tmplMsgs, n)
- }
- }
- case parse.NodeIf:
- if in, ok := node.(*parse.IfNode); ok {
- process(tmplMsgs, in.List)
- if in.ElseList != nil {
- process(tmplMsgs, in.ElseList)
- }
- }
- case parse.NodeWith:
- if wn, ok := node.(*parse.WithNode); ok {
- process(tmplMsgs, wn.List)
- if wn.ElseList != nil {
- process(tmplMsgs, wn.ElseList)
- }
- }
- case parse.NodeRange:
- if rn, ok := node.(*parse.RangeNode); ok {
- process(tmplMsgs, rn.List)
- if rn.ElseList != nil {
- process(tmplMsgs, rn.ElseList)
- }
- }
- case parse.NodeAction:
- an, ok := node.(*parse.ActionNode)
- if !ok {
- break
- }
-
- for _, cmd := range an.Pipe.Cmds {
- if !hasIndent(cmd, transfn) {
- continue
- }
- for _, arg := range cmd.Args {
- if arg.Type() != parse.NodeString {
- continue
- }
- if sn, ok := arg.(*parse.StringNode); ok {
- txt := collapse(sn.Text)
- *tmplMsgs = append(*tmplMsgs, pipeline.Message{
- ID: pipeline.IDList{txt},
- Key: txt,
- Message: pipeline.Text{Msg: txt},
- })
- break
- }
- }
- }
- }
-}
-
-func hasIndent(cmd *parse.CommandNode, s string) bool {
- if len(cmd.Args) == 0 {
- return false
- }
- arg := cmd.Args[0]
- var idents []string
- switch arg.Type() {
- case parse.NodeField:
- idents = arg.(*parse.FieldNode).Ident
- case parse.NodeVariable:
- idents = arg.(*parse.VariableNode).Ident
- }
- return slices.Contains(idents, s)
-}
-
-func packageList(path string) []string {
- ents := try2(os.ReadDir(path))
- xs := make([]string, 0, len(ents))
- foundOne := false
- for _, ent := range ents {
- switch {
- case filepath.Ext(ent.Name()) == ".go":
- if !foundOne {
- xs = append(xs, pkgbase+"/"+path)
- foundOne = true
- }
- case !ent.IsDir(), ent.Name() == "cmd", ent.Name() == "vendor":
- continue
- default:
- xs = append(xs, packageList(path+"/"+ent.Name())...)
- }
- }
- return xs
-}
-
-func languages() []language.Tag {
- ents := try2(os.ReadDir(transdir))
- tags := make([]language.Tag, len(ents))
- for i, e := range ents {
- tags[i] = language.MustParse(e.Name())
- }
- return tags
-}
-
-func collapse(s string) string {
- var (
- sb strings.Builder
- prev bool
- )
- const spc = " \t\n"
-
- for _, ch := range strings.Trim(s, spc) {
- if strings.ContainsRune(spc, ch) {
- if prev {
- continue
- }
- ch = ' '
- prev = true
- } else {
- prev = false
- }
- sb.WriteRune(ch)
- }
-
- return sb.String()
-}
-
-func try(err error) {
- if err != nil {
- die(err)
- }
-}
-
-func try2[T any](val T, err error) T {
- if err != nil {
- die(err)
- }
- return val
-}
-
-func warn(err any) {
- fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
-}
-
-func die(err any) {
- warn(err)
- os.Exit(1)
-}
diff --git a/cmd/extwiki/main.go b/cmd/extwiki/main.go
new file mode 100644
index 0000000..76e0f3a
--- /dev/null
+++ b/cmd/extwiki/main.go
@@ -0,0 +1,151 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "maps"
+ "os"
+ "path/filepath"
+ "slices"
+ "sort"
+ "text/template/parse"
+)
+
+var (
+ titles = make(map[string]struct{})
+ configs = map[string]int{"Wikipedia": 1}
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n",
+ filepath.Base(os.Args[0]))
+ flag.PrintDefaults()
+}
+
+func main() {
+ try(os.Chdir(filepath.Dir(os.Args[0])))
+
+ outpath := flag.String("out", "-", "output file")
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() < 1 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ var outfile *os.File
+ if *outpath == "-" {
+ outfile = os.Stdout
+ } else {
+ outfile = try2(os.Create(*outpath))
+ }
+
+ for _, f := range flag.Args() {
+ process(f)
+ }
+
+ fmt.Fprint(outfile, `// Code generated by extwiki. DO NOT EDIT.
+
+package wikipedia
+
+var extractedTitles = [...]string{
+`)
+ xs := slices.Collect(maps.Keys(titles))
+ sort.Strings(xs)
+ for _, t := range xs {
+ fmt.Fprintf(outfile, "%q,\n", t)
+ }
+ fmt.Fprint(outfile, "}\n")
+}
+
+func process(path string) {
+ currentFile := try2(os.ReadFile(path))
+ trees := make(map[string]*parse.Tree)
+ t := parse.New(path)
+ t.Mode |= parse.ParseComments | parse.SkipFuncCheck
+ try2(t.Parse(string(currentFile), "", "", trees))
+ for _, t := range trees {
+ processNode(t.Root)
+ }
+}
+
+func processNode(node parse.Node) {
+ switch n := node.(type) {
+ case *parse.ListNode:
+ for _, m := range n.Nodes {
+ processNode(m)
+ }
+ case *parse.PipeNode:
+ for _, m := range n.Cmds {
+ processNode(m)
+ }
+ case *parse.TemplateNode:
+ processNode(n.Pipe)
+ case *parse.IfNode:
+ processBranch(n.BranchNode)
+ case *parse.RangeNode:
+ processBranch(n.BranchNode)
+ case *parse.WithNode:
+ processBranch(n.BranchNode)
+ case *parse.BranchNode:
+ processBranch(*n)
+ case *parse.ActionNode:
+ processNode(n.Pipe)
+ case *parse.CommandNode:
+ if len(n.Args) == 0 {
+ break
+ }
+
+ var funcname string
+ f, ok := n.Args[0].(*parse.FieldNode)
+ if !ok || len(f.Ident) == 0 {
+ ff, ok := n.Args[0].(*parse.VariableNode)
+ if !ok || len(ff.Ident) == 0 {
+ fff, ok := n.Args[0].(*parse.IdentifierNode)
+ if !ok {
+ for _, pipe := range n.Args {
+ processNode(pipe)
+ }
+ break
+ }
+ funcname = fff.Ident
+ } else {
+ funcname = ff.Ident[len(ff.Ident)-1]
+ }
+ } else {
+ funcname = f.Ident[len(f.Ident)-1]
+ }
+
+ i, ok := configs[funcname]
+ if !ok {
+ for _, pipe := range n.Args {
+ processNode(pipe)
+ }
+ break
+ }
+
+ if sn, ok := n.Args[i].(*parse.StringNode); ok {
+ titles[sn.Text] = struct{}{}
+ }
+ }
+}
+
+func processBranch(n parse.BranchNode) {
+ processNode(n.List)
+ if n.ElseList != nil {
+ processNode(n.ElseList)
+ }
+}
+
+func try(err error) {
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
+ os.Exit(1)
+ }
+}
+
+func try2[T any](val T, err error) T {
+ try(err)
+ return val
+}
diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go
deleted file mode 100644
index c4e2277..0000000
--- a/cmd/mfmt/main.go
+++ /dev/null
@@ -1,189 +0,0 @@
-/* Simple formatter for mintage files. This is not perfect and doesn’t
- check for syntactic correctness, it’s just to get stuff aligned
- nicely. Maybe in the future I will construct a military-grade
- formatter. */
-
-package main
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "regexp"
- "strings"
-)
-
-const ws = " \t"
-
-var (
- rv int
-
- reMintageYear = regexp.MustCompile(`^\d{4}(-[^ \t]+)?`)
- reMintageRowC = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){2}([0-9.]+|\?)$`)
- reMintageRowS = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){7}([0-9.]+|\?)$`)
-)
-
-func main() {
- if len(os.Args) == 1 {
- mfmt("-", os.Stdin, os.Stdout)
- }
- for _, arg := range os.Args[1:] {
- f, err := os.OpenFile(arg, os.O_RDWR, 0)
- if err != nil {
- warn(err)
- continue
- }
- defer f.Close()
-
- buf := bytes.NewBuffer(make([]byte, 0, 8192))
- mfmt(arg, f, buf)
-
- if _, err = f.Seek(0, io.SeekStart); err != nil {
- warn(err)
- continue
- }
-
- if _, err = f.Write(buf.Bytes()); err != nil {
- warn(err)
- continue
- }
-
- if err = f.Truncate(int64(buf.Len())); err != nil {
- warn(err)
- }
- }
- os.Exit(rv)
-}
-
-func mfmt(file string, r io.Reader, w io.Writer) {
- var (
- buf [3]string
- bufsz int
- longestNums [8]int
- )
-
- scanner := bufio.NewScanner(r)
- for linenr := 1; scanner.Scan(); linenr++ {
- line := strings.Trim(scanner.Text(), ws)
-
- switch {
- case len(line) == 0, line[0] == '#':
- fmt.Fprintln(w, line)
- case reMintageRowS.MatchString(line):
- switch bufsz {
- case len(buf):
- bufsz = 0
- clear(longestNums[:])
- fallthrough
- default:
- setLongestNum(longestNums[:], line)
- buf[bufsz] = line
- if bufsz++; bufsz == len(buf) {
- fmtMintageRow(buf[0], longestNums[:], w)
- fmtMintageRow(buf[1], longestNums[:], w)
- fmtMintageRow(buf[2], longestNums[:], w)
- }
- }
- case reMintageRowC.MatchString(line):
- var ns [3]int
- setLongestNum(ns[:], line)
- fmtMintageRow(line, ns[:], w)
- case reMintageYear.MatchString(line):
- fmtMintageYear(line, w)
- default:
- warn(fmt.Sprintf("%s:%d: potential syntax error", file, linenr))
- fmt.Fprintln(w, line)
- }
- }
-}
-
-func setLongestNum(longest []int, line string) {
- for i, x := range strings.FieldsFunc(line, func(r rune) bool {
- return strings.ContainsRune(ws, r)
- }) {
- n := len(strings.ReplaceAll(x, ".", ""))
- n += (n - 1) / 3 // Thousands separators
- longest[i] = max(longest[i], n)
- }
-}
-
-func fmtMintageYear(line string, w io.Writer) {
- switch i := strings.IndexAny(line, ws); i {
- case -1:
- fmt.Fprintln(w, line)
- default:
- fmt.Fprintf(w, "%s %s\n", line[:i], strings.TrimLeft(line[i:], ws))
- }
-}
-
-func fmtMintageRow(line string, longest []int, w io.Writer) {
- tokens := strings.FieldsFunc(line, func(r rune) bool {
- return strings.ContainsRune(ws, r)
- })
-
- for i, tok := range tokens {
- s := formatMintage(tok)
-
- if i == 0 {
- fmt.Fprintf(w, "\t%*s", longest[i], s)
- } else {
- fmt.Fprintf(w, "%*s", longest[i]+1, s)
- }
- }
-
- fmt.Fprintln(w)
-}
-
-func formatMintage(s string) string {
- if s == "?" {
- return s
- }
-
- n := atoiWithDots(s)
- digits := intlen(n)
- dots := (digits - 1) / 3
- out := make([]byte, digits+dots)
-
- for i, j := len(out)-1, 0; ; i-- {
- out[i] = byte(n%10) + 48
- if n /= 10; n == 0 {
- return string(out)
- }
- if j++; j == 3 {
- i, j = i-1, 0
- out[i] = '.'
- }
- }
-}
-
-func intlen(v int) int {
- switch {
- case v == 0:
- return 1
- default:
- n := 0
- for x := v; x != 0; x /= 10 {
- n++
- }
- return n
- }
-}
-
-func atoiWithDots(s string) int {
- n := 0
- for _, ch := range s {
- if ch == '.' {
- continue
- }
- n = n*10 + int(ch) - '0'
- }
- return n
-}
-
-func warn(e any) {
- fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), e)
- rv = 1
-}
diff --git a/cmd/mfmt/main_test.go b/cmd/mfmt/main_test.go
deleted file mode 100644
index ef91473..0000000
--- a/cmd/mfmt/main_test.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package main
-
-import (
- "bytes"
- "testing"
-)
-
-func runTest(t *testing.T, in, out string, nilErr bool) {
-}
-
-func TestComplete(t *testing.T) {
- in := `
-2014
-60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000
-70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000
-3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000
-
-# Ref: https://example.com
-2015
- 0 0 0 0 0 0 0 1.072.400
-40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000
- ? ? ? ? ? ? ? ?
-
-2016
- 0 0 0 0 0 0 2.339.200 0
-35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000
- ? ? ? ? ? ? ? ?
-
-2017
-2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588
-22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000
- ? ? ? ? ? ? ? ?
-
-2018
-2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000
-20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000
- ? ? ? ? ? ? ? ?
-
-2019
-2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310
-15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000
- ? ? ? ? ? ? ? ?
-
-2020
- 0 0 0 860.000 175.000 740.000 0 1.500.000
-12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000
- ? ? ? ? ? ? ? ?
-
-2021
- 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500
-10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2022
- 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000
-10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2023
- 0 0 0 0 0 0 0 2.075.250
-10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2024
- ? ? ? ? ? ? ? ?
- ? ? ? ? ? ? ? ?
- ? ? ? ? ? ? ? ?
-
- 2014 "Council of Europe"
- ? ? ?
-`
- out := `
-2014
- 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000
- 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000
- 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000
-
-# Ref: https://example.com
-2015
- 0 0 0 0 0 0 0 1.072.400
- 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000
- ? ? ? ? ? ? ? ?
-
-2016
- 0 0 0 0 0 0 2.339.200 0
- 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000
- ? ? ? ? ? ? ? ?
-
-2017
- 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588
- 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000
- ? ? ? ? ? ? ? ?
-
-2018
- 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000
- 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000
- ? ? ? ? ? ? ? ?
-
-2019
- 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310
- 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000
- ? ? ? ? ? ? ? ?
-
-2020
- 0 0 0 860.000 175.000 740.000 0 1.500.000
- 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000
- ? ? ? ? ? ? ? ?
-
-2021
- 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500
- 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2022
- 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000
- 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2023
- 0 0 0 0 0 0 0 2.075.250
- 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500
- ? ? ? ? ? ? ? ?
-
-2024
- ? ? ? ? ? ? ? ?
- ? ? ? ? ? ? ? ?
- ? ? ? ? ? ? ? ?
-
-2014 "Council of Europe"
- ? ? ?
-`
-
- r := bytes.NewReader([]byte(in))
- w := new(bytes.Buffer)
-
- mfmt("-", r, w)
- if w.String() != out {
- t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String())
- }
-}
diff --git a/cmd/mfmt/util_test.go b/cmd/mfmt/util_test.go
deleted file mode 100644
index 8366a50..0000000
--- a/cmd/mfmt/util_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This file contains tests for utility functions used in the mfmt
- binary. Tests of the actual application are in main_test.go. */
-
-package main
-
-import "testing"
-
-func TestFormatInt(t *testing.T) {
- for _, x := range [...]struct{ x, y string }{
- {"?", "?"},
- {"0", "0"},
- {"123", "123"},
- {"81758", "81.758"},
- {"752759237528", "752.759.237.528"},
- } {
- s := formatMintage(x.x)
- if s != x.y {
- t.Fatalf(`Expected s="%s"; got "%s"`, x.y, s)
- }
- }
-}
-
-func TestIntLen(t *testing.T) {
- for _, x := range [...]struct{ x, y int }{
- {0, len("0")},
- {123, len("123")},
- {81758, len("81758")},
- {752759237528, len("752759237528")},
- } {
- n := intlen(x.x)
- if n != x.y {
- t.Fatalf("Expected n=%d; got %d", x.y, n)
- }
- }
-}