moved to codeberg

This commit is contained in:
2025-11-04 21:19:54 +01:00
parent 0f2bc83ca9
commit 738bc30d61
29 changed files with 3 additions and 1474 deletions

View File

@@ -1,69 +0,0 @@
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
- go mod tidy
gitea_urls:
api: https://codeberg.org/api/v1
download: https://codeberg.org
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
- freebsd
archives:
- formats: [tar.gz]
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}_{{ .Tag }}
# use zip for windows archives
format_overrides:
- goos: windows
formats: [zip]
- goos: linux
formats: [tar.gz,binary]
files:
- src: "*.md"
strip_parent: true
- src: "docs/*"
strip_parent: true
- src: Makefile.dist
dst: Makefile
wrap_in_directory: true
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
groups:
- title: Improved
regexp: '^.*?(feat|add|new)(\([[:word:]]+\))??!?:.+$'
order: 0
- title: Fixed
regexp: '^.*?(bug|fix)(\([[:word:]]+\))??!?:.+$'
order: 1
- title: Changed
order: 999
release:
header: "# Release Notes"
footer: >-
---
Full Changelog: [{{ .PreviousTag }}...{{ .Tag }}](https://codeberg.org/scip/epuppy/compare/{{ .PreviousTag }}...{{ .Tag }})

View File

@@ -1,36 +0,0 @@
matrix:
platform:
- linux/amd64
goversion:
- 1.24
labels:
platform: ${platform}
steps:
build:
when:
event: [push]
image: golang:${goversion}
commands:
- go get
- go build
linter:
when:
event: [push]
image: golang:${goversion}
commands:
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0
- golangci-lint --version
- golangci-lint run ./...
depends_on: [build]
test:
when:
event: [push]
image: golang:${goversion}
commands:
- go get
- go test -v -cover
depends_on: [build,linter]

View File

@@ -1,15 +0,0 @@
# build release
labels:
platform: linux/amd64
steps:
goreleaser:
image: goreleaser/goreleaser
when:
event: [tag]
environment:
GITEA_TOKEN:
from_secret: DEPLOY_TOKEN
commands:
- goreleaser release --clean --verbose

View File

@@ -1,142 +0,0 @@
# Introduction
Fantasy Name Generator has two interfaces that each strike its own
balance between ease of use and functionality. The Simple Interface is
extremely easy but only offers you a limited form of control over the
names that get generated. The Advanced Interface is not quite as easy
to learn -- see instructions for using it below -- but allows you
practically limitless control over the type of names that get
generated. Among other things, it allows you to generate names that
are variations on a name you already have in mind.
# Simple Interface
The Simple Interface provides a drop-down box to allow you to set what
kind of names you want to generate. The "Default Names" choice in this
list contains a diverse mix of generic name types, while the other
choices are more specialized.
To use the Simple Interface, simply select a name type from the
drop-down list and hit the Generate Names button next to it.
# Advanced Interface
The Advanced Interface offers a maximum of control over the names that
get generated. You can use it to generate names or words with
unfamiliar linguistic roots or to generate variations on a specific
name.
The Advanced Interface has a "Collapse Triples" checkbox, which is
checked by default. When on, names with three or more of the same
letter in a row will be reduced reduced to just two prior to being
printed out. So if the name "Purghinnn" was generated and the
"Collapse Triples" checkbox was checked, it would appear as just
"Purghinn." In addition, if the letter in question does not make sense
to have even doubled, it will be reduced to just one. So the name
"Leguiin" would be reduced to just "Leguin." If the "Collapse Triples"
checkbox is left unchecked, this reduction step will not take
place. It doesn't mean you will get unnatural double and triple letter
sequences; it just means no efforts will be made to prevent them. We
recommend you leave this option turned on unless you have a special
need for it to be off.
The heart of the Advanced Interface is the "Name Generation Template"
field. The name generation template describes how names should be
constructed. For instance, a template could dictate that all names be
a consonant followed by a vowel or vowel combination followed by a
consonant or consonant combination. This template would generate short
names. The template could also employ the use of random predefined
syllables (such as 'tor', 'ash', 'ald', and 'dar'); a very effective
template, one used frequently when default names are generated using
the Simple Interface, is one that dictates names should consist simply
of two predefined syllables.
Templates get more complicated than that, but they don't have
to. Simple and effective templates may do nothing but list how many
consonant combinations, vowel combinations, and syllables should
comprise a word, and in what order. At generation time, random
consonant combinations, random vowel combinations, and random
syllables will be placed together according to the template to form
each name. The randomizer used will be weighted so that more
frequently seen letters, such as 't' and 's', will appear more often
than less frequently seen letters, such as 'j' and 'z'. This goes a
long way toward generating good, pronouncable names.
A consonant combination is two or three consonants that tend to go
well together, such as 'ch', 'ck', 'tt', and 'str'. Examples of vowel
combinations are 'ou', 'ai', and 'ee'. In your template, you will be
able to specify whether you want only single consonants and single
vowels or whether you want to allow the possibility of combinations.
Each name type in the Simple Interface uses its own set of predefined
templates, hence the diversity of names you can get from it,
especially when using the "Default Names" choice. In the Advanced
Interface, you can define your own by entering it into the "Name
Generation Field."
In a template, a single case-sensitive character is used to represent
each component of the name. 's' means syllable; 'v' means single
vowel; 'V' means vowel or vowel combination. The consonants are a
little more complicated, because the template distinguishes between
consonant combinations that are appropriate for the beginning of
names, like 'wh' and 'chr', and consonant combinations that are
appropriate for the middle or ending of names, like 'nd' and 'ck'. To
avoid generating names that, for example, start with 'nd' or 'ck',
this distinction is necessary, although most consonant combinations
are members of both groups, as they can appear anywhere. In templates,
the character 'c' represents a single consonant and is appropriate
anywhere in a name. The character 'B' (note that it is capital)
represents a consonant or consonant combination appropriate for the
beginning of a name; the character 'C' represents a consonant or
consonant combination appropriate anywhere else. The apostrophe
character can be used to insert an apostrophe into the name; the
hyphen character can be used to insert a hyphen into the name. These
symbols and other, special-purpose symbols, are all documented in the
quick reference guide. Some example templates:
* `ss`
* `BVC`
* `s'vCv`
* `Bv-s`
Templates can be more complicated when you add parentheses into them. Any character in parentheses appears in the generated names literally. The template (str)VC, for example, will generate short names starting with 'str', like "Stript", "Stroagh", and "Strun". If you include an or bar, the '|' character, then either what's on the left or what's on the right will be chosen to be included in the name. The template (st|tr)VC will generate short names that start with either 'st' or 'tr'. Parentheses are useful when you already have an idea of what you want a name to look like but want to see variations on it. Here are some more sample templates:
* `ss(ien|ian)`
* `(d|t|p|w)(r)VCv`
* `ss(ly|ily|ish|ing)`
Angle brackets are another valid construct in templates. Angle
brackets can be used with `|` characters in the same way as
parentheses can, but what goes inside angle brackets isn't literal
text but template codes for syllables, consonants, vowels, and so
on. The template `<s|ss>v` generates names that consist either of one
or two syllables, followed by a vowel. Here are some examples:
* `<c|cvc>s`
* `(ma)<VC|s|Vs>`
* `<Bv|V>(ck|kk|k)v`
If there is nothing on one side of an or bar, it allows the
possibility of nothing. For example, the template `(s|)(mor)ss`
generates names that start with `mor` and names that start with
`smor`. The template ss<|v> generates two syllable names that may or
may not have an extra vowel appended. Also, parentheses and angle
brackets can be nested; the template `(ghoa|<s>)VC` generates names
that start with either `ghoa` or a predefined syllable. Here are more
examples:
* `(s||t)VC`
* `<|s|ss|sss>s`
* `<V|VCV|((a|e)i)>`
Nesting can influence the frequency certain choices are made. For
example, a template starting with (r|s|t) will generate names that
start with either 'r', 's', or 't', where each of the three letters
appear with approximately equal frequency. But if you use a template
starting with (r|(s|t)), roughly half the generated names will start
with 'r', and only a quarter will start with 's', and a quarter will
start with 't'.
Although the vast majority of the time, you won't need to use very
complex constructions with nested parentheses and angle brackets, once
in a while this functionality can be very helpful.

View File

@@ -1,87 +0,0 @@
# Copyright © 2024 Thomas von Dein
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# no need to modify anything below
tool = gfn
VERSION = $(shell grep VERSION config.go | head -1 | cut -d '"' -f2)
archs = darwin freebsd linux windows
PREFIX = /usr/local
UID = root
GID = 0
HAVE_POD := $(shell pod2text -h 2>/dev/null)
all: buildlocal
buildlocal:
CGO_LDFLAGS='-static' go build -tags osusergo,netgo -ldflags "-extldflags=-static" -o $(tool)
install: buildlocal
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/
clean:
rm -rf $(tool) coverage.out testdata t/out
test: clean
mkdir -p t/out
go test ./... $(ARGS)
testlint: test lint
lint:
golangci-lint run
lint-full:
golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest
testfuzzy: clean
go test -fuzz ./... $(ARGS)
singletest:
@echo "Call like this: make singletest TEST=TestPrepareColumns ARGS=-v"
go test -run $(TEST) $(ARGS)
cover-report:
go test ./... -cover -coverprofile=coverage.out
go tool cover -html=coverage.out
goupdate:
go get -t -u=patch ./...
buildall:
./mkrel.sh $(tool) $(VERSION)
release:
gh release create v$(VERSION) --generate-notes
show-versions: buildlocal
@echo "### gfn version:"
@./gfn -V
@echo
@echo "### go module versions:"
@go list -m all
@echo
@echo "### go version used for building:"
@grep -m 1 go go.mod
# lint:
# golangci-lint run -p bugs -p unused

View File

@@ -1,18 +0,0 @@
# -*-make-*-
.PHONY: install all
tool = rpn
PREFIX = /usr/local
UID = root
GID = 0
all:
@echo "Type 'sudo make install' to install the tool."
@echo "To change prefix, type 'sudo make install PREFIX=/opt'"
install:
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
install -d -o $(UID) -g $(GID) $(PREFIX)/share/doc
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
install -o $(UID) -g $(GID) -m 444 *.md $(PREFIX)/share/doc/

View File

@@ -6,6 +6,9 @@
[![License](https://img.shields.io/badge/license-GPL-blue.svg)](https://codeberg.org/scip/gfn/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/codeberg.org/scip/gfn)](https://goreportcard.com/report/codeberg.org/scip/gfn)
> [!IMPORTANT]
> This software is now being maintained on [Codeberg](https://codeberg.org/scip/gfn/).
Generate fantasy names for games and stories. It uses the fine
[fantasyname module](https://github.com/s0rg/fantasyname) by
[s0rg](https://github.com/s0rg/), which implements the code created by

View File

@@ -1,21 +0,0 @@
This is a quick reference guide to the syntax used in the "Name
Generation Template" of the Advanced Interface. If you don't know how
to use the information below, see the instructions first.
| Character | Meaning |
|-----------|-------------------------------------------------------------------------|
| s | generic syllable |
| v | single vowel |
| V | single vowel or vowel combination |
| c | single consonant |
| B | single consonant or consonant combination suitable for beginning a word |
| C | single consonant or consonant combination suitable anywhere in a word |
| i | unit for an insult |
| m | unit for a mushy name |
| M | unit for a mushy name ending |
| D | consonant or consonant suited for a stupid person's name |
| d | syllable suited for a stupid person's name (always begins with a vowel) |
| ' | literal apostrophe |
| ( ) | denotes that anything inside is literal |
| < > | denotes that anything inside is a special symbol |
| \| | logical OR operator for use in ( ) and < > |

275
config.go
View File

@@ -1,275 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/confmap"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/posflag"
"github.com/knadh/koanf/v2"
flag "github.com/spf13/pflag"
)
const (
MIDDLE_EARTH = "(bil|bal|ban|hil|ham|hal|hol|hob|wil|me|or|ol|od|gor|for|fos|tol|ar|fin|ere|leo|vi|bi|bren|thor)" +
"(|go|orbis|apol|adur|mos|ri|i|na|ole|n)" +
"(|tur|axia|and|bo|gil|bin|bras|las|mac|grim|wise|l|lo|fo|co|ra|via|" +
"da|ne|ta|y|wen|thiel|phin|dir|dor|tor|rod|on|rdo|dis)"
JAPANESE_NAMES_CONSTRAINED = "(aka|aki|bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|" +
"ichi|iwa|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|" +
"saka|saki|sawa|shita|shima|i|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|" +
"ao|gin|kin|ken|shiro|zaki|yuki|asa)(||||||||||bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|" +
"hon|hoshi|chi|wa|ka|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|" +
"ta|o|oo|oka|saka|saki|sawa|shita|shima|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|" +
"zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|sa)"
JAPANESE_NAMES_DIVERSE = "(a|i|u|e|o|||||)" +
"(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|" +
"no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|" +
"re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|" +
"chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|" +
"yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)" +
"(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|" +
"no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|" +
"ro|ro|ro|wa|wa|wa|wa|wo|wo)|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|" +
"tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|" +
"yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|" +
"shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|" +
"mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)))(|||n)"
CHINESE_NAMES = "(zh|x|q|sh|h)(ao|ian|uo|ou|ia)(|(l|w|c|p|b|m)(ao|ian|uo|ou|ia)" +
"(|n)|-(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|(d|j|q|l)(a|ai|iu|ao|i)))"
GREEK_NAMES = "<s<v|V>(tia)|s<v|V>(os)|B<v|V>c(ios)|B<v|V><c|C>v(ios|os)>"
HAWAIIAN_NAMES_1 = "((h|k|l|m|n|p|w)|)(a|e|i|o|u)((h|k|l|m|n|p|w)|)(a|e|i|o|u)(((h|k|l|m|n|p|w)|)" +
"(a|e|i|o|u)|)(((h|k|l|m|n|p|w)|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w)|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w)|)" +
"(a|e|i|o|u)|)"
HAWAIIAN_NAMES_2 = "((h|k|l|m|n|p|w|)(a|e|i|o|u|a|e|i|o|u|ae|ai|ao|au|oi|ou|eu|ei)" +
"(k|l|m|n|p|)|)(h|k|l|m|n|p|w|)(a|e|i|o|u|a|e|i|o|u|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)"
OLD_LATIN_PLACE_NAMES = "sv(nia|lia|cia|sia)"
DRAGONS_PERN = "<<s|ss>|<VC|vC|B|BVs|Vs>><v|V|v|<v(l|n|r)|vc>>(th)"
DRAGON_RIDERS = "c<s|cvc>"
POKEMON = "<i|s>v(mon|chu|zard|rtle)"
FANTASY_VOWELS_R = "(|(<B>|s|h|ty|ph|r))(i|ae|ya|ae|eu|ia|i|eo|ai|a)" +
"(lo|la|sri|da|dai|the|sty|lae|due|li|lly|ri|na|ral|sur|rith)(|(su|nu|sti|llo|ria|))" +
"(|(n|ra|p|m|lis|cal|deu|dil|suir|phos|ru|dru|rin|raap|rgue))"
FANTASY_S_A = "(cham|chan|jisk|lis|frich|isk|lass|mind|sond|sund|ass|chad|lirt|und|mar|lis|il|<BVC>)" +
"(jask|ast|ista|adar|irra|im|ossa|assa|osia|ilsa|<vCv>)(|(an|ya|la|sta|sda|sya|st|nya))"
FANTASY_H_L = "(ch|cht|sh|cal|val|ell|har|shar|shal|rel|laen|ral|jht|alr|ch|cht|av)" +
"(|(is|al|ow|ish|ul|el|ar|iel))" +
"(aren|aeish|aith|even|adur|ulash|alith|atar|aia|erin|aera|ael|ira|iel|ahur|ishul)"
FANTASY_N_L = "(ethr|qil|mal|er|eal|far|fil|fir|ing|ind|il|lam|quel|quar|quan|qar|pal|mal|yar|um|ard|enn|ey)" +
"(|(<vc>|on|us|un|ar|as|en|ir|ur|at|ol|al|an))" +
"(uard|wen|arn|on|il|ie|on|iel|rion|rian|an|ista|rion|rian|cil|mol|yon)"
FANTASY_K_N = "(taith|kach|chak|kank|kjar|rak|kan|kaj|tach|rskal|kjol|jok|jor|jad|kot|kon|" +
"knir|kror|kol|tul|rhaok|rhak|krol|jan|kag|ryr)(<vc>|in|or|an|ar|och|un|mar|yk|ja|arn|ir|ros|ror)" +
"(|(mund|ard|arn|karr|chim|kos|rir|arl|kni|var|an|in|ir|a|i|as))"
FANTASY_J_G_Z = "(aj|ch|etz|etzl|tz|kal|gahn|kab|aj|izl|ts|jaj|lan|kach|chaj|qaq|jol|ix|az|biq|nam)" +
"(|(<vc>|aw|al|yes|il|ay|en|tom||oj|im|ol|aj|an|as))" +
"(aj|am|al|aqa|ende|elja|ich|ak|ix|in|ak|al|il|ek|ij|os|al|im)"
FANTASY_K_J_Y = "(yi|shu|a|be|na|chi|cha|cho|ksa|yi|shu)" +
"(th|dd|jj|sh|rr|mk|n|rk|y|jj|th)" +
"(us|ash|eni|akra|nai|ral|ect|are|el|urru|aja|al|uz|ict|arja|ichi|ural|iru|aki|esh)"
FANTASY_S_E = "(syth|sith|srr|sen|yth|ssen|then|fen|ssth|kel|syn|est|bess|inth|nen|tin|cor|" +
"sv|iss|ith|sen|slar|ssil|sthen|svis|s|ss|s|ss)" +
"(|(tys|eus|yn|of|es|en|ath|elth|al|ell|ka|ith|yrrl|is|isl|yr|ast|iy))" +
"(us|yn|en|ens|ra|rg|le|en|ith|ast|zon|in|yn|ys)"
)
type Template struct {
Name string
Tmpl string
}
var Templates = map[string]string{
"MiddleEarth": MIDDLE_EARTH,
"JapaneseNamesConstrained": JAPANESE_NAMES_CONSTRAINED,
"JapaneseNamesDiverse": JAPANESE_NAMES_DIVERSE,
"ChineseNames": CHINESE_NAMES,
"GreekNames": GREEK_NAMES,
"HawaiianNames1": HAWAIIAN_NAMES_1,
"HawaiianNames2": HAWAIIAN_NAMES_2,
"OldLatinPlaceNames": OLD_LATIN_PLACE_NAMES,
"DragonsPern": DRAGONS_PERN,
"DragonRiders": DRAGON_RIDERS,
"Pokemon": POKEMON,
"FantasyR": FANTASY_VOWELS_R,
"FantasySA": FANTASY_S_A,
"FantasyHL": FANTASY_H_L,
"FantasyNL": FANTASY_N_L,
"FantasyKN": FANTASY_K_N,
"FantasyJGZ": FANTASY_J_G_Z,
"FantasyKJY": FANTASY_K_J_Y,
"FantasySE": FANTASY_S_E,
"Funny": "sdD",
"Idiots": "ii",
}
const (
VERSION string = "0.0.9"
DefaultCount int = 160 // number of words to generate if -c is omitted
DefaultColumns int = 10 // number of columns to print
MaxWidth int = 72 // max width of output, adjusts columns
Usage string = `This is gfn, a fantasy name generator cli.
Usage: gfn [-vld] [-c <config file>] [-n <number of names>] <name|code>
Options:
-c --config Config file to use (optional)
-n --number Number of names to generate
-l --list List pre-compiled shortcuts
-d --debug Show debugging output
-v --version Show program version
pattern syntax The letters s, v, V, c, B, C, i, m, M, D, and d
represent different types of random replacements:
s - generic syllable
v - vowel
V - vowel or vowel combination
c - consonant
B - consonant or consonant combination suitable for beginning a word
C - consonant or consonant combination suitable anywhere in a word
i - insult
m - mushy name
M - mushy name ending
D - consonant suited for a stupid person's name
d - syllable suited for a stupid person's name (begins with a vowel)
Everything else is emitted literally.
All characters between parenthesis () are emitted literally. For
example, the pattern s(dim), emits a random generic syllable followed
by dim.
Characters between angle brackets <> emit patterns from the table
above. Imagine the entire pattern is wrapped in one of these.
In both types of groupings, a vertical bar | denotes a random
choice. Empty groups are allowed. For example, (foo|bar) emits either
foo or bar. The pattern <c|v|> emits a constant, vowel, or nothing at
all.
An exclamation point ! means to capitalize the component that follows
it. For example, !(foo) will emit Foo and v!s will emit a lowercase
vowel followed by a capitalized syllable, like eRod.`
)
type Config struct {
Showversion bool `koanf:"version"` // -v
Debug bool `koanf:"debug"` // -d
Listshortcuts bool `koanf:"list"` // -l
Number int `koanf:"number"` // -c
Templates map[string]string `koanf:"templates"`
Config string `koanf:"config"`
Columns int `koanf:"columns"` // number of columns to use
Code string // arg
WordWidth int // max width of generated words
}
func InitConfig(output io.Writer) (*Config, error) {
var kloader = koanf.New(".")
// Load default values using the confmap provider.
if err := kloader.Load(confmap.Provider(map[string]interface{}{
"number": DefaultCount,
"columns": DefaultColumns,
}, "."), nil); err != nil {
return nil, fmt.Errorf("failed to load default values into koanf: %w", err)
}
// setup custom usage
flagset := flag.NewFlagSet("config", flag.ContinueOnError)
flagset.Usage = func() {
_, err := fmt.Fprintln(output, Usage)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
os.Exit(0)
}
// parse commandline flags
flagset.BoolP("list", "l", false, "show list of precompiled codes")
flagset.BoolP("version", "v", false, "show program version")
flagset.BoolP("debug", "d", false, "enable debug output")
flagset.IntP("number", "n", 1, "number of names to generate")
flagset.StringP("config", "c", "", "config file")
if err := flagset.Parse(os.Args[1:]); err != nil {
return nil, fmt.Errorf("failed to parse program arguments: %w", err)
}
// generate a list of config files to try to load, including the
// one provided via -c, if any
var configfiles []string
configfile, _ := flagset.GetString("config")
home, _ := os.UserHomeDir()
if configfile != "" {
configfiles = []string{configfile}
} else {
configfiles = []string{
"/etc/gfn.conf", "/usr/local/etc/gfn.conf", // unix variants
filepath.Join(home, ".config", "gfn", "config"),
filepath.Join(home, ".gfn"),
"gfn.conf",
}
}
// Load the config file[s]
for _, cfgfile := range configfiles {
if path, err := os.Stat(cfgfile); !os.IsNotExist(err) {
if !path.IsDir() {
if err := kloader.Load(file.Provider(cfgfile), toml.Parser()); err != nil {
return nil, fmt.Errorf("error loading config file: %w", err)
}
}
} // else: we ignore the file if it doesn't exists
}
// command line setup
if err := kloader.Load(posflag.Provider(flagset, ".", kloader), nil); err != nil {
return nil, fmt.Errorf("error loading flags: %w", err)
}
// fetch values
conf := &Config{}
if err := kloader.Unmarshal("", &conf); err != nil {
return nil, fmt.Errorf("error unmarshalling: %w", err)
}
// arg is the code
if len(flagset.Args()) > 0 {
conf.Code = flagset.Args()[0]
}
// merge configured and hardcoded templates
if conf.Templates == nil {
conf.Templates = Templates
} else {
for name, code := range Templates {
conf.Templates[name] = code
}
}
return conf, nil
}

View File

@@ -1,76 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"log/slog"
fn "github.com/s0rg/fantasyname"
)
// Actual fantasy name generation
func Generate(conf *Config) ([]string, error) {
// we register each generated word to avoid duplicates, which
// naturally happens every while
reg := map[string]int{}
// library call
gen, err := fn.Compile(conf.Code, fn.Collapse(true))
if err != nil {
return nil, fmt.Errorf("could not compile FN code: %w", err)
}
// fetch requested number of names
for i := 0; len(reg) < conf.Number; i++ {
name := gen.String()
if !Exists(reg, name) {
reg[name] = 1
if conf.WordWidth < len(name) {
conf.WordWidth = len(name)
}
}
// static codes (like 'akx', which is no FN code, just a
// literal) generates just 1 item
if i > conf.Number*2 {
break
}
}
slog.Debug("Generated fantasy names from code",
"code", conf.Code, "count-names", len(reg))
// adjust columns, if needed
if conf.WordWidth*conf.Columns > MaxWidth {
conf.Columns = MaxWidth / conf.WordWidth
}
// we just return a slice of names
names := make([]string, len(reg))
i := 0
for k := range reg {
names[i] = k
i++
}
return names, nil
}

View File

@@ -1,77 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"testing"
)
var tests = []struct {
name string
want bool
code string
}{
{
name: "code-ok",
want: true,
code: "!s(na|ha|ma|va)v",
},
{
name: "code-fail",
want: false,
code: "!s(na|ha|ma|vav",
},
}
var conf = &Config{
Number: 3,
}
func TestGenerate(t *testing.T) {
for _, tt := range tests {
testname := fmt.Sprintf("generate-%s", tt.name)
t.Run(testname, func(t *testing.T) {
conf.Code = tt.code
names, err := Generate(conf)
if err != nil {
if tt.want {
t.Errorf("Generate() returned an unexpected error: %s", err)
}
return
}
if len(names) != 3 {
t.Errorf("Generate() returned wrong number of results\nExp: %+v\nGot: %+v\n",
conf.Number, len(names))
return
}
for idx, name := range names {
if len(name) == 0 {
t.Errorf("Generate() returned empty results\nIndex: %d\nGot: <%s>\n",
idx, name)
return
}
}
})
}
}

View File

@@ -1,5 +0,0 @@
# example config file
[[Templates]]
morph = "!s(na|ha|ma|va)v"
morphium = "!s(na|ha|ma|va)v(ius|ium|aum|oum|eum)"

33
go.mod
View File

@@ -1,33 +0,0 @@
module codeberg.org/scip/gfn
go 1.24.0
toolchain go1.24.5
require (
github.com/knadh/koanf/parsers/toml v0.1.0
github.com/knadh/koanf/providers/confmap v1.0.0
github.com/knadh/koanf/providers/file v1.2.0
github.com/knadh/koanf/providers/posflag v1.0.1
github.com/knadh/koanf/v2 v2.3.0
github.com/rogpeppe/go-internal v1.14.1
github.com/s0rg/fantasyname v1.3.6
github.com/spf13/pflag v1.0.10
github.com/tlinden/yadu v0.1.3
)
require (
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.26.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

53
go.sum
View File

@@ -1,53 +0,0 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5zwp8Aebir+/EaRE=
github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A=
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
github.com/knadh/koanf/providers/posflag v1.0.1 h1:EnMxHSrPkYCFnKgBUl5KBgrjed8gVFrcXDzaW4l/C6Y=
github.com/knadh/koanf/providers/posflag v1.0.1/go.mod h1:3Wn3+YG3f4ljzRyCUgIwH7G0sZ1pMjCOsNBovrbKmAk=
github.com/knadh/koanf/v2 v2.3.0 h1:Qg076dDRFHvqnKG97ZEsi9TAg2/nFTa9hCdcSa1lvlM=
github.com/knadh/koanf/v2 v2.3.0/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/s0rg/fantasyname v1.3.6 h1:KOezDl0ijuuIDre06tqMz8idCEIdaM7KMkSnU/8bCxc=
github.com/s0rg/fantasyname v1.3.6/go.mod h1:9cZ037rckuT6N7DijPi2xpuuYFgJpLlcbpXMptYuEdY=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tlinden/yadu v0.1.3 h1:5cRCUmj+l5yvlM2irtpFBIJwVV2DPEgYSaWvF19FtcY=
github.com/tlinden/yadu v0.1.3/go.mod h1:l3bRmHKL9zGAR6pnBHY2HRPxBecf7L74BoBgOOpTcUA=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

115
main.go
View File

@@ -1,115 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"io"
"log"
"os"
"runtime/debug"
"log/slog"
"github.com/tlinden/yadu"
)
func main() {
os.Exit(Main(os.Stdout))
}
func TMain() int {
return Main(os.Stdout)
}
func Main(output io.Writer) int {
// parse config file and command line parameters, if any
conf, err := InitConfig(output)
if err != nil {
return Die(err)
}
if conf.Showversion {
_, err := fmt.Fprintf(output, "This is gfn version %s\n", VERSION)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
return 0
}
// enable debugging, if needed. We only use log/slog for
// debugging, so there's no need to configure it outside debugging
if conf.Debug {
logLevel := &slog.LevelVar{}
// we're using a more verbose logger in debug mode
buildInfo, _ := debug.ReadBuildInfo()
opts := &yadu.Options{
Level: logLevel,
AddSource: true,
}
logLevel.Set(slog.LevelDebug)
handler := yadu.NewHandler(output, opts)
debuglogger := slog.New(handler).With(
slog.Group("program_info",
slog.Int("pid", os.Getpid()),
slog.String("go_version", buildInfo.GoVersion),
),
)
slog.SetDefault(debuglogger)
}
// just show what we have
if conf.Listshortcuts {
ListTemplates(conf, output)
return 0
}
// code argument is mandatory
if len(conf.Code) == 0 {
_, err := fmt.Fprintln(output, Usage)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
return 1
}
// check if we can use a template, otherwise consider the argument
// to be FN code
if Exists(conf.Templates, conf.Code) {
slog.Debug("Argument resolves to template code",
"name", conf.Code, "code", conf.Templates[conf.Code])
conf.Code = conf.Templates[conf.Code]
}
// all prepared, run baby run
names, err := Generate(conf)
if err != nil {
return Die(err)
}
if err = PrintColumns(conf, names, output); err != nil {
return Die(err)
}
return 0
}

View File

@@ -1,38 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"testing"
"github.com/rogpeppe/go-internal/testscript"
)
// see https://bitfieldconsulting.com/golang/test-scripts
func TestMain(m *testing.M) {
testscript.Main(m, map[string]func(){
"gfn": main,
})
}
func TestGfn(t *testing.T) {
testscript.Run(t, testscript.Params{
Dir: "t",
})
}

View File

@@ -1,75 +0,0 @@
#!/bin/bash
# Copyright © 2024 Thomas von Dein
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# get list with: go tool dist list
DIST="darwin/amd64
freebsd/amd64
linux/amd64
netbsd/amd64
openbsd/amd64
windows/amd64
freebsd/arm64
linux/arm64
netbsd/arm64
openbsd/arm64
windows/arm64"
tool="$1"
version="$2"
if test -z "$version"; then
echo "Usage: $0 <tool name> <release version>"
exit 1
fi
rm -rf releases
mkdir -p releases
for D in $DIST; do
os=${D/\/*/}
arch=${D/*\//}
binfile="releases/${tool}-${os}-${arch}-${version}"
if test "$os" = "windows"; then
binfile="${binfile}.exe"
fi
tardir="${tool}-${os}-${arch}-${version}"
tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz"
set -x
GOOS=${os} GOARCH=${arch} go build -tags osusergo,netgo -ldflags "-extldflags=-static" -o ${binfile}
mkdir -p ${tardir}
cp ${binfile} README.md LICENSE ${tardir}/
echo 'tool = gfn
PREFIX = /usr/local
UID = root
GID = 0
install:
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/' > ${tardir}/Makefile
tar cpzf ${tarfile} ${tardir}
sha256sum ${binfile} | cut -d' ' -f1 > ${binfile}.sha256
sha256sum ${tarfile} | cut -d' ' -f1 > ${tarfile}.sha256
rm -rf ${tardir}
set +x
done

View File

@@ -1,118 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"io"
"log"
"log/slog"
"sort"
)
// Output a list of hardcoded FN code templates
func ListTemplates(conf *Config, output io.Writer) {
slog.Debug("Listing configured templates", "templates", conf.Templates)
names := []string{}
for name := range conf.Templates {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
_, err := fmt.Fprintln(output, name)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
}
}
// Columnar output
func PrintColumns(conf *Config, names []string, output io.Writer) error {
count := len(names)
// no need for the hassle to calculate columns
if count <= conf.Columns {
for _, name := range names {
_, err := fmt.Fprintln(output, name)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
}
return nil
}
// get a transposed list of columns
padlist, max := Getcolumns(names, conf.Columns)
// make sure there's enough spacing between the columns
format := fmt.Sprintf("%%-%ds", max+1)
for _, row := range padlist {
for _, word := range row {
_, err := fmt.Fprintf(output, format, word)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
}
_, err := fmt.Fprintln(output)
if err != nil {
log.Fatalf("failed to print to output: %s", err)
}
}
return nil
}
func Getcolumns(names []string, columns int) ([][]string, int) {
words := len(names)
max := 0
// we'll have a list of $columns columns
padlist := make([][]string, columns)
// initialize'em
for col := 0; col < columns; col++ {
padlist[col] = []string{}
}
// fill from input
for idx := 0; idx < words; idx += columns {
for col := 0; col < columns; col++ {
if idx+col >= words {
padlist[col] = append(padlist[col], "")
} else {
padlist[col] = append(padlist[col], names[idx+col])
length := len(names[idx+col])
if length > max {
max = length
}
}
}
}
// turn columns to rows
return Transpose(padlist), max
}

View File

@@ -1,2 +0,0 @@
exec gfn -d Vs
stdout 'gfn/generate.go:'

View File

@@ -1,2 +0,0 @@
exec gfn -h
stdout 'This is gfn'

View File

@@ -1,2 +0,0 @@
exec gfn -l
stdout 'JapaneseNamesDiverse'

View File

@@ -1 +0,0 @@
! exec gfn

View File

@@ -1,2 +0,0 @@
exec gfn akx
stdout 'akx'

View File

@@ -1,2 +0,0 @@
exec gfn akx
! stdout 'akx akx'

View File

@@ -1,2 +0,0 @@
exec gfn Idiots
stdout 'face'

View File

@@ -1,2 +0,0 @@
exec gfn sVdmi(usus|hehe)
stdout 'usus'

View File

@@ -1,2 +0,0 @@
exec gfn -v
stdout 'This is gfn version'

65
util.go
View File

@@ -1,65 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import "log"
func Exists[K comparable, V any](m map[K]V, v K) bool {
if _, ok := m[v]; ok {
return true
}
return false
}
func Die(err error) int {
log.Fatal("Error: ", err.Error())
return 1
}
// find an item in a list, generic variant
func Contains[E comparable](s []E, v E) bool {
for _, vs := range s {
if v == vs {
return true
}
}
return false
}
// Transpose a matrix, x=>y, y=>x
// via https://gist.github.com/tanaikech/5cb41424ff8be0fdf19e78d375b6adb8
func Transpose(slice [][]string) [][]string {
xl := len(slice[0])
yl := len(slice)
result := make([][]string, xl)
for i := range result {
result[i] = make([]string, yl)
}
for i := 0; i < xl; i++ {
for j := 0; j < yl; j++ {
result[i][j] = slice[j][i]
}
}
return result
}

View File

@@ -1,139 +0,0 @@
/*
Copyright © 2024 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"fmt"
"reflect"
"testing"
)
var testexists = []struct {
name string
want bool
key int
hash map[int]int
}{
{
name: "have-key",
want: true,
key: 1,
hash: map[int]int{1: 4, 2: 5},
},
{
name: "miss-key",
want: false,
key: 3,
hash: map[int]int{1: 4, 2: 5},
},
}
func TestExists(t *testing.T) {
for _, tt := range testexists {
testname := fmt.Sprintf("exists-%s", tt.name)
t.Run(testname, func(t *testing.T) {
got := Exists(tt.hash, tt.key)
if got != tt.want {
t.Errorf("Exists() returned wrong result\nExp: %+v\nGot: %+v\n",
tt.want, got)
}
})
}
}
var testcontains = []struct {
name string
want bool
key int
list []int
}{
{
name: "have-item",
want: true,
key: 1,
list: []int{1, 2},
},
{
name: "miss-item",
want: false,
key: 3,
list: []int{1, 2},
},
}
func TestContains(t *testing.T) {
for _, tt := range testcontains {
testname := fmt.Sprintf("contains-%s", tt.name)
t.Run(testname, func(t *testing.T) {
got := Contains(tt.list, tt.key)
if got != tt.want {
t.Errorf("Contains() returned wrong result\nExp: %+v\nGot: %+v\n",
tt.want, got)
}
})
}
}
var testtranspose = []struct {
name string
slice [][]string
expect [][]string
}{
{
name: "2x2-matrix",
slice: [][]string{
{"a1", "a2"},
{"b1", "b2"},
},
expect: [][]string{
{"a1", "b1"},
{"a2", "b2"},
},
},
{
name: "2x3-matrix",
slice: [][]string{
{"a1", "a2", "a3"},
{"b1", "b2", "b3"},
},
expect: [][]string{
{"a1", "b1"},
{"a2", "b2"},
{"a3", "b3"},
},
},
}
func TestTranspose(t *testing.T) {
for _, tt := range testtranspose {
testname := fmt.Sprintf("transpose-%s", tt.name)
t.Run(testname, func(t *testing.T) {
got := Transpose(tt.slice)
if !reflect.DeepEqual(tt.expect, got) {
t.Errorf("Transpose() returned wrong result\nExp: %+v\nGot: %+v\n",
tt.expect, got)
}
})
}
}