chikuchikugonzalezの雑記帳

趣味とか日記とかメモとか(∩゚д゚)

ssh-keygenを使わずにOpenSSHキーペアを作りたかった

結果

Goで作りました。
bitbucket.org

目的とか

もともとはWindowsでOpenSSHキーを作るのに、わざわざPuTTYGenを使うのはちょっとなー、でもCygwin入れたくないしなー、みたいなことを考えてました。*1
そんなときに、Goの非標準パッケージに golang.org/x/crypto/ssh があるのに気がついてしまいまして。

そうだ、Goで作ればWindowsでも動くぞ…!

とか考えて作りました。

使い方

  1. go get する*2
  2. makey
  3. ✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌
C:>go get bitbucket.org/chiku2gonzalez/makey
C:>makey -output hello
C:>dir
2016/05/26  23:08             1,766 hello
2016/05/26  23:08               380 hello.pub

makey -h とかするとオプション出てきます。-outputでファイル名を指定ない場合は id_rsa になるので注意してください。
というのも、保存処理は上書き (確認なし) なので。

やり残し

  • フィンガープリントしたい
  • Randomart imageとか出したい
  • DSAって対応するべきだろか
  • ssh-keygenにある以下の機能はあったほうがいいよね
    • ファイル名が指定されなかった場合に対話式で聞く
    • 上書き確認
    • ログ出さなくていいよモード (-quiet)

*1:特にMercurialSSH接続するときに、ssh-keygenがなくて困ったというのが発端。Gitの場合はGit Bash経由でssh-keygenが使えるから問題にならないはず

*2:ロスコンパイル環境がないのでバイナリは用意してないです

適当なGo言語用ログライブラリのようなものを作った

LOGO is LOGger for GO

chiku2gonzalez / logo — Bitbucket

動機

モンハンやってる合間に細々とツール作成しているわけですが、そんな中でログライブラリほしいなー、って思うことがあるんです。
ただ最近は、どのロガーもちょっとしたツールには機能過剰だなーと感じてました。
とはいえ、標準のロガーだとそれはそれでデバッグログとか出し分けができないやつなので、どうしたもんかと思ってたんです。

で、仕方なく作りました。
機能的には標準のロガーにデバッグログの出し分けを増やした程度のものです。

使い方

標準のロガーと併用する前提で、こんな風になります。

package main

import "fmt"
import "log"
import "os"
import "bitbucket.org/chiku2gonzalez/logo"

func main() {
	logger := Wrap(log.New(os.Stdout, "[LOGO]", 0))

	fmt.Fprintf(os.Stdout, "<<BASIC>>\n")
	logger.SetMode(BASIC)
	logger.Println("(・∀・)")
	logger.Debugln("(`・ω・´)")
	fmt.Fprintf(os.Stdout, "\n")

	fmt.Fprintf(os.Stdout, "<<DEBUG>>\n")
	logger.SetMode(DEBUG)
	logger.Println("( ˘ω˘ )")
	logger.Debugln("( ゚Д゚)")
	fmt.Fprintf(os.Stdout, "\n")

	fmt.Fprintf(os.Stdout, "<<QUIET>>\n")
	logger.SetMode(QUIET)
	logger.Println("('ω')")
	logger.Debugln("(・ω・)")
	fmt.Fprintf(os.Stdout, "\n")

	fmt.Fprintf(os.Stdout, "<<END>>\n")
}

2016年始まりましたね

あけましておめでとうございます。今年もchikuchikugonzalezをよろしくお願いします。

などとあいさつを済ませたところで本題です。
昨年の最後のエントリで適当に作ったライブラリを紹介しましたが、その片方の cfgo でこんなことを言ってました。

そのうち一度読み込んだファイルはは読み込まないっていう機能を持った構造体を追加しようと思ってます。

http://chiku2gonzalez.hatenablog.com/entry/2015/12/28/235253

で、それを作ってるところで、さぁテスト書くかって思ったときに一つ問題にぶち当たりました。っていうか前にもぶち当たったのを忘れてました。
それは

サブパッケージ内から親パッケージを見ている場合に、サブパッケージを利用するテストを親パッケージに置けない

です。いや当たり前なんですが今回はまともなテストが書けないっていう感じになりました。

なので、サブパッケージのリポジトリを分割しました。

chiku2gonzalez / cfgo-driver — Bitbucket

ただ分けるのもあれだったので TOML 対応もしておきました。
✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌

適当に作ったGo用ライブラリ置いておきますね

chiku2gonzalez / xdgo — Bitbucket

XDG Base Directory Specification のディレクトリから設定を読み込むためのライブラリ。
ってもパスしか返さないので読み込みは使う側でどうにかしてください。

動機としては、ひっそりと作ってるツールの設定ファイルをどこに置くか悩んでるときに NeoVim が上の仕様で読み込むようにしたよー、って言う記事を見たのが発端です。
その中で Windows は AppData から、ていうのが書いてあったので「じゃぁそれで対応するか」ってなりました。

chiku2gonzalez / cfgo — Bitbucket

拡張子から適当にフォーマットを判別して Unmarshal をするためのライブラリ。
いやね、ひっそり作ってるツールだとルート設定を XML にしておいて、その下にぶら下がる設定は XML/YAML/JSON/TOML を選べるようにしよう、とか張り切ったせいで設定の読み込みがつらくなりました。
なのでせめてフォーマット識別くらいは外出ししようとしたのがこちらになります。

そのうち一度読み込んだファイルはは読み込まないっていう機能を持った構造体を追加しようと思ってます。

Goで構造体スライスをXMLにして ( ;゚皿゚)ノシΣバンバン!! ってなった話

結論から書くと、ちゃんと構造体でラップしましょう、ということです。
出ないと書き出したXMLがちゃんと読み込まれなくて ( ;゚皿゚)ノシΣバンバン!! ってすることになります。

やらかしたこと

次のように、構造体とそのスライスを用意して、XMLに変換したわけです。

package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items []Item

func main() {
	items := Items{
		Item{
			ID:    "001",
			Name:  "AA1",
			Value: "|ω・`)ノ ヤァ",
		},
		Item{
			ID:    "002",
			Name:  "AA2",
			Value: "|д゚)チラッ",
		},
	}

	if buf, err := xml.MarshalIndent(items, "", " "); err != nil {
		panic(err)
	} else {
		fmt.Print(xml.Header)
		fmt.Println(string(buf))
	}
}

実行すると次のようなXMLを得られます。

<?xml version="1.0" encoding="UTF-8"?>
<item id="001">
 <name>AA1</name>
 <value>|ω・`)ノ ヤァ</value>
</item>
<item id="002">
 <name>AA2</name>
 <value>|д゚)チラッ</value>
</item>

この時点ですでになんか怪しいことにお気づきでしょうか。そう、ルート要素が複数あるということに。

さて、これを以下のコードで読み込むと( ゚д゚)します。

package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items []Item

func main() {
	items := make(Items, 0)
	doc := `<?xml version="1.0" encoding="UTF-8"?>
<item id="001">
 <name>AA1</name>
 <value>|ω・`)ノ ヤァ</value>
</item>
<item id="002">
 <name>AA2</name>
 <value>|д゚)チラッ</value>
</item>
`
	if err := xml.Unmarshal([]byte(doc), &items); err != nil {
		panic(err)
	} else {
		for _, item := range items {
			fmt.Println(item)
		}
	}
}

以下出力結果。

{{ item} 001 AA1 |ω・`)ノ ヤァ}

二要素目が闇に消えましたね ( ・´ー・`)

原因

たぶんルート要素がいっぱいあるせい。つまり正しいXMLになってない。

正しい解決法

なので、ちゃんと構造体でラップしましょう、そうしましょう。

エンコードする方のコード
package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items struct {
	XMLName xml.Name `xml:"items"`
	Items   []Item   `xml:"item"`
}

func main() {
	items := &Items{
		Items: []Item{
			Item{
				ID:    "001",
				Name:  "AA1",
				Value: "|ω・`)ノ ヤァ",
			},
			Item{
				ID:    "002",
				Name:  "AA2",
				Value: "|д゚)チラッ",
			},
		},
	}

	if buf, err := xml.MarshalIndent(items, "", " "); err != nil {
		panic(err)
	} else {
		fmt.Print(xml.Header)
		fmt.Println(string(buf))
	}
}
エンコード結果
<?xml version="1.0" encoding="UTF-8"?>
<items>
 <item id="001">
  <name>AA1</name>
  <value>|ω・`)ノ ヤァ</value>
 </item>
 <item id="002">
  <name>AA2</name>
  <value>|д゚)チラッ</value>
 </item>
</items>
デコードする方のコード
package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items struct {
	XMLName xml.Name `xml:"items"`
	Items   []Item   `xml:"item"`
}

func main() {
	items := Items{}
	doc := `<?xml version="1.0" encoding="UTF-8"?>
<items>
 <item id="001">
  <name>AA1</name>
  <value>|ω・`)ノ ヤァ</value>
 </item>
 <item id="002">
  <name>AA2</name>
  <value>|д゚)チラッ</value>
 </item>
</items>
`
	if err := xml.Unmarshal([]byte(doc), &items); err != nil {
		panic(err)
	} else {
		for _, item := range items.Items {
			fmt.Println(item)
		}
	}
}
デコード結果
{{ item} 001 AA1 |ω・`)ノ ヤァ}
{{ item} 002 AA2 |д゚)チラッ}

ダメな解決方法

構造体作りたくないでござる、スライスはスライスのままにしたいでござる、みたいな人 *1 のためにたぶん間違った解決方法を。
その方法は xml.Marshaler / xml.Unmarshalerを実装して、ルート要素を無理やり作る、というのもですが。

エンコード
package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items []Item

func (self Items) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	root := struct {
		Items []Item `xml:"item"`
	}{
		Items: self,
	}

	start.Name.Local = "items"
	return e.EncodeElement(&root, start)
}

func main() {
	items := Items{
		Item{
			ID:    "001",
			Name:  "AA1",
			Value: "|ω・`)ノ ヤァ",
		},
		Item{
			ID:    "002",
			Name:  "AA2",
			Value: "|д゚)チラッ",
		},
	}

	if buf, err := xml.MarshalIndent(items, "", " "); err != nil {
		panic(err)
	} else {
		fmt.Print(xml.Header)
		fmt.Println(string(buf))
	}
}
デコード側
package main

import "encoding/xml"
import "fmt"

type Item struct {
	XMLName xml.Name `xml:"item"`
	ID      string   `xml:"id,attr"`
	Name    string   `xml:"name"`
	Value   string   `xml:"value"`
}

type Items []Item

func (self *Items) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	root := struct {
		Items []Item `xml:"item"`
	}{}

	if err := d.DecodeElement(&root, &start); err != nil {
		return err
	} else {
		*self = root.Items
		return nil
	}
}

func main() {
	items := Items{}
	doc := `<?xml version="1.0" encoding="UTF-8"?>
<items>
 <item id="001">
  <name>AA1</name>
  <value>|ω・`)ノ ヤァ</value>
 </item>
 <item id="002">
  <name>AA2</name>
  <value>|д゚)チラッ</value>
 </item>
</items>
`
	if err := xml.Unmarshal([]byte(doc), &items); err != nil {
		panic(err)
	} else {
		for _, item := range items {
			fmt.Println(item)
		}
	}
}

自分自身のスライスの参照先を書き換えるとか ((((;゚Д゚))))ガクガクブルブル しますね!!

*1:

Go言語用PCXライブラリを作り直しました

ここで公開してあります。
chiku2gonzalez / PCX.go — Bitbucket*1

もともとは
chiku2gonzalez / Goobers — Bitbucket
に含んでたんですが、思うところがあって分離しました

制約とか
  1. 8bit画像だけ対応しました
    1. Rubyで実装したときはなんちゃってモノクロ/2bit/4bit実装した気がするけど、需要無さそうだしやめておきました
  2. パレット形式も256色を強制します
    1. 開くと256色のPalettedに
    2. 256未満のパレット形式も256色に拡張して保存
謎挙動について

なぜかJPEGを保存すると容量が増えることがある( ゚д゚)
というか、BMPPNGからPCXに変換したときはIrfanViewと同じサイズになるんだけど、JPEGだけは違う結果になって謎い

今後

いい加減
chiku2gonzalez / MUGON — Bitbucket
をどうにかしなきゃと思ってます

*1:前は埋め込みできたと思ったんだけど、最近埋め込めないんだよね