diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a13b28 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# emojisum + +:pray: :paperclip: Emoji that checksum! :tada: :poop: + +I attempted a curated list of 256 emojis that are not entirely similar. Using http://www.webpagefx.com/tools/emoji-cheat-sheet/ to compare them. I went with 256 as that is 8bit/1byte, and the hexadecimal output that is 2 hex characters. + +So 1 emoji is 2 hex positions. + +## build + +```bash +go get github.com/vbatts/emojisum +``` + +## usage + +This uses [`github.com/kyokomi/emoji`](https://github.com/kyokomi/emoji) to print to the console, but also gives the string ouptut for easy pasting to github/markdown. + +```bash +$> emojisum main.go +SHA1(main.go)= 14b09535217ca8f5f47f4665e2266e686f0728b4 +SHA1(main.go)= :bird::red_car::on::crystal_ball::calendar::lemon::pray::warning::violin::lollipop::facepunch::hearts::tm::children_crossing::hourglass::heavy_plus_sign::house::ant::clap::rocket: +SHA1(main.go)= 🐦 🚗 🔛 🔮 📆 🍋 🙏 ⚠️🎻 🍭 👊 ♥️™️🚸 ⌛️➕ 🏠 🐜 👏 🚀 +``` + +Like so! + +SHA1(main.go)= :bird::red_car::on::crystal_ball::calendar::lemon::pray::warning::violin::lollipop::facepunch::hearts::tm::children_crossing::hourglass::heavy_plus_sign::house::ant::clap::rocket: + + +### pass in the checksums + +Rather than relying on this simple tool to do the checksum itself, you will likely want to rely on OpenSSL or coreutils for checksumming. +`emojisum` can just take those formats on stdin: + +```bash +$> sha1sum main.go | emojisum -parse-coreutils +7656835947b4c6da272023c56b6f2529511bf88b main.go +:jp::gb::metal::goat::family::rocket::smiley_cat::swimmer::chocolate_bar::cactus::candy::smile::honeybee::house::cherries::cloud::fries::bow::wavy_dash::musical_score: main.go +🇯🇵 🇬🇧 🤘 🐐 👪 🚀 😺 🏊 🍫 🌵 🍬 😄 🐝 🏠 🍒 ☁️🍟 🙇 〰️ 🎼 main.go +``` + +Like so: + +🇯🇵 🇬🇧 🤘 🐐 👪 🚀 😺 🏊 🍫 🌵 🍬 😄 🐝 🏠 🍒 ☁️🍟 🙇 〰️ 🎼 main.go + + +```bash +$> openssl sha1 main.go |emojisum -parse-openssl +SHA1(main.go)= 7656835947b4c6da272023c56b6f2529511bf88b +SHA1(main.go)= :jp::gb::metal::goat::family::rocket::smiley_cat::swimmer::chocolate_bar::cactus::candy::smile::honeybee::house::cherries::cloud::fries::bow::wavy_dash::musical_score: +SHA1(main.go)= 🇯🇵 🇬🇧 🤘 🐐 👪 🚀 😺 🏊 🍫 🌵 🍬 😄 🐝 🏠 🍒 ☁️🍟 🙇 〰️ 🎼 +``` + +And like so: + +SHA1(main.go)= 🇯🇵 🇬🇧 🤘 🐐 👪 🚀 😺 🏊 🍫 🌵 🍬 😄 🐝 🏠 🍒 ☁️🍟 🙇 〰️ 🎼 diff --git a/main.go b/main.go new file mode 100644 index 0000000..a3e099a --- /dev/null +++ b/main.go @@ -0,0 +1,441 @@ +package main + +import ( + "bufio" + "crypto/sha1" + "encoding/hex" + "errors" + "flag" + "fmt" + "io" + "os" + "strings" + + "github.com/kyokomi/emoji" +) + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +var ( + flParseOpenSSL = flag.Bool("parse-openssl", false, "parse the output of OpenSSL style checksums on stdin (`openssl sha256 ./foo | emojisum -parse-openssl`)") + flParseCoreUtils = flag.Bool("parse-coreutils", false, "parse the output of CoreUtils style checksums on stdin (`sha256sum ./foo | emojisum -parse-coreutils`)") +) + +func run() error { + flag.Parse() + + if *flParseOpenSSL { + buf := bufio.NewReader(os.Stdin) + for { + line, err := buf.ReadString('\n') + if err != nil && err == io.EOF { + return nil + } else if err != nil { + return err + } + hash, name, sum, err := parseOpenSSL(line) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %q\n", err, line) + continue + } + str := emojiFromBytes(sum) + fmt.Printf("%s(%s)= %x\n", hash, name, sum) + fmt.Printf("%s(%s)= %s\n", hash, name, str) + fmt.Printf("%s(%s)= ", hash, name) + emoji.Println(str) + } + return nil + } + if *flParseCoreUtils { + buf := bufio.NewReader(os.Stdin) + for { + line, err := buf.ReadString('\n') + if err != nil && err == io.EOF { + return nil + } else if err != nil { + return err + } + name, sum, err := parseCoreUtils(line) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %q\n", err, line) + continue + } + str := emojiFromBytes(sum) + fmt.Printf("%x %s\n", sum, name) + fmt.Printf("%s %s\n", str, name) + emoji.Print(str) + fmt.Printf(" %s\n", name) + } + return nil + } + + // Otherwise do the checksum ourselves + + if flag.NArg() == 0 { + sum, err := Sum(os.Stdin) + if err != nil { + return err + } + str := emojiFromBytes(sum) + fmt.Printf("SHA1(-)=\t%x\n", sum) + fmt.Printf("SHA1(-)=\t%s\n", str) + fmt.Printf("SHA1(-)=\t") + emoji.Println(str) + return nil + } + + for _, arg := range flag.Args() { + fh, err := os.Open(arg) + if err != nil { + return err + } + defer fh.Close() + + sum, err := Sum(fh) + if err != nil { + return err + } + str := emojiFromBytes(sum) + fmt.Printf("SHA1(%s)=\t%x\n", arg, sum) + fmt.Printf("SHA1(%s)=\t%s\n", arg, str) + fmt.Printf("SHA1(%s)=\t", arg) + emoji.Println(str) + } + return nil +} + +/* +openssl sum: +``` +$> openssl sha256 tmp.efLuko +SHA256(tmp.efLuko)= f18bd8b680e834ab8097a66deb0255821195d9624e39da6b65903ff6a09a01bb +``` +*/ +func parseOpenSSL(line string) (hash, filename string, sum []byte, err error) { + if !strings.Contains(line, "(") { + return "", "", nil, ErrNotOpenSSLLine + } + chunks := strings.SplitN(strings.TrimRight(line, "\n"), ")= ", 2) + if len(chunks) != 2 { + return "", "", nil, ErrNotOpenSSLLine + } + chunksprime := strings.SplitN(chunks[0], "(", 2) + if len(chunks) != 2 { + return "", "", nil, ErrNotOpenSSLLine + } + sum, err = hex.DecodeString(chunks[1]) + if err != nil { + return "", "", nil, err + } + return chunksprime[0], chunksprime[1], sum, nil +} + +var ErrNotOpenSSLLine = errors.New("not an openssl checksum line") + +/* +coreutils output: +``` +$ sha256sum ./tmp.efLuko +f18bd8b680e834ab8097a66deb0255821195d9624e39da6b65903ff6a09a01bb ./tmp.efLuko +``` +*/ +func parseCoreUtils(line string) (filename string, sum []byte, err error) { + chunks := strings.SplitN(strings.TrimRight(line, "\n"), " ", 2) + if len(chunks) != 2 { + return "", nil, ErrNotCoreUtilsLine + } + sum, err = hex.DecodeString(chunks[0]) + if err != nil { + return "", nil, err + } + return chunks[1], sum, nil +} + +var ErrNotCoreUtilsLine = errors.New("not a coreutils checksum line") + +func Sum(r io.Reader) ([]byte, error) { + h := sha1.New() + if _, err := io.Copy(h, r); err != nil { + return nil, err + } + sum := h.Sum(nil) + return sum[:], nil +} + +func emojiFromBytes(buf []byte) string { + var ret string + for _, b := range buf { + ret = ret + emojiFromRune(b) + } + return ret +} + +func emojiFromRune(b byte) string { + //fmt.Printf("%#v\n", b) + return emojiMap[int(b)] +} + +// these are an ordered list, referened by a byte (each byte of a checksum digest) +var emojiMap = []string{ + ":+1:", + ":8ball:", + ":airplane:", + ":alien:", + ":anchor:", + ":angel:", + ":angry:", + ":ant:", + ":apple:", + ":art:", + ":baby:", + ":baby_bottle:", + ":back:", + ":bamboo:", + ":banana:", + ":barber:", + ":bathtub:", + ":beer:", + ":bell:", + ":bicyclist:", + ":bird:", + ":birthday:", + ":blossom:", + ":blue_car:", + ":boar:", + ":bomb:", + ":boom:", + ":bow:", + ":boy:", + ":broken_heart:", + ":bulb:", + ":bus:", + ":cactus:", + ":calendar:", + ":camera:", + ":candy:", + ":cat:", + ":cherries:", + ":children_crossing:", + ":chocolate_bar:", + ":clap:", + ":cloud:", + ":clubs:", + ":cn:", + ":coffee:", + ":construction:", + ":cookie:", + ":copyright:", + ":corn:", + ":cow:", + ":crescent_moon:", + ":crown:", + ":cry:", + ":crystal_ball:", + ":curly_loop:", + ":dancers:", + ":dash:", + ":de:", + ":diamonds:", + ":dog:", + ":doughnut:", + ":dragon:", + ":dvd:", + ":ear:", + ":eggplant:", + ":elephant:", + ":end:", + ":envelope:", + ":es:", + ":eyes:", + ":facepunch:", + ":family:", + ":ferris_wheel:", + ":finnadie:", + ":fire:", + ":fireworks:", + ":floppy_disk:", + ":football:", + ":fork_and_knife:", + ":four_leaf_clover:", + ":fr:", + ":fries:", + ":frog:", + ":fu:", + ":full_moon:", + ":game_die:", + ":gb:", + ":gem:", + ":girl:", + ":goat:", + ":grimacing:", + ":grin:", + ":guardsman:", + ":guitar:", + ":gun:", + ":hamburger:", + ":hammer:", + ":hamster:", + ":hear_no_evil:", + ":heart:", + ":heart_eyes_cat:", + ":hearts:", + ":heavy_check_mark:", + ":moyai:", + ":izakaya_lantern:", + ":helicopter:", + ":hocho:", + ":honeybee:", + ":horse:", + ":horse_racing:", + ":hourglass:", + ":house:", + ":hurtrealbad:", + ":icecream:", + ":imp:", + ":it:", + ":jack_o_lantern:", + ":japanese_goblin:", + ":jp:", + ":key:", + ":kiss:", + ":kissing_cat:", + ":koala:", + ":kr:", + ":lemon:", + ":lipstick:", + ":lock:", + ":lollipop:", + ":man:", + ":maple_leaf:", + ":mask:", + ":metal:", + ":microscope:", + ":moneybag:", + ":monkey:", + ":mount_fuji:", + ":muscle:", + ":mushroom:", + ":musical_keyboard:", + ":musical_score:", + ":nail_care:", + ":new_moon:", + ":no_entry:", + ":nose:", + ":notes:", + ":nut_and_bolt:", + ":o:", + ":ocean:", + ":ok_hand:", + ":on:", + ":package:", + ":palm_tree:", + ":panda_face:", + ":paperclip:", + ":partly_sunny:", + ":passport_control:", + ":paw_prints:", + ":peach:", + ":penguin:", + ":phone:", + ":pig:", + ":pill:", + ":pineapple:", + ":pizza:", + ":point_left:", + ":point_right:", + ":poop:", + ":poultry_leg:", + ":pray:", + ":princess:", + ":purse:", + ":pushpin:", + ":rabbit:", + ":rainbow:", + ":raised_hand:", + ":recycle:", + ":red_car:", + ":registered:", + ":ribbon:", + ":rice:", + ":rocket:", + ":roller_coaster:", + ":rooster:", + ":ru:", + ":sailboat:", + ":santa:", + ":satellite:", + ":satisfied:", + ":saxophone:", + ":scissors:", + ":see_no_evil:", + ":sheep:", + ":shell:", + ":shoe:", + ":ski:", + ":skull:", + ":sleepy:", + ":smile:", + ":smiley_cat:", + ":smirk:", + ":smoking:", + ":snail:", + ":snake:", + ":snowflake:", + ":soccer:", + ":soon:", + ":space_invader:", + ":spades:", + ":speak_no_evil:", + ":star:", + ":stars:", + ":statue_of_liberty:", + ":steam_locomotive:", + ":sunflower:", + ":sunglasses:", + ":sunny:", + ":sunrise:", + ":surfer:", + ":swimmer:", + ":syringe:", + ":tada:", + ":tangerine:", + ":taxi:", + ":tennis:", + ":tent:", + ":thought_balloon:", + ":tm:", + ":toilet:", + ":tongue:", + ":tophat:", + ":tractor:", + ":trolleybus:", + ":trollface:", + ":trophy:", + ":trumpet:", + ":turtle:", + ":two_men_holding_hands:", + ":two_women_holding_hands:", + ":uk:", + ":umbrella:", + ":unlock:", + ":us:", + ":v:", + ":vhs:", + ":violin:", + ":warning:", + ":watermelon:", + ":wave:", + ":wavy_dash:", + ":wc:", + ":wheelchair:", + ":woman:", + ":x:", + ":yum:", + ":zap:", + ":zzz:", +}