Internet Kill Switch by Firewall (OpenVPN + iptables)
タイトルの通り. このPythonプログラムはopenvpnの設定ファイルの
remote <hostname> <port>
を
remote <ip address> <port>
に書き換える.
また, 書き換えたVPNサーバのIPアドレスへのアクセスを許可するiptablesのOUTPUTチェインの追加ルールを./rules.txt
に保存する.
iptables-restoreで読み込める形にしてるので, このルールを追加しその他のルールを適切に設定すれば, ファイアウォールでキルスイッチを実現できる.
プログラム
使用は自己責任で
何をやってるかわからない人は実行しないほうがよいです.
Use at your own risk
Don't execute it unless you understand what it does.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys import socket import ipaddress def main(): from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('-d', '--dry-run', action='store_true') args = parser.parse_args() path = '/etc/openvpn/client/' filenames = [f for f in os.listdir(path) if os.path.isfile(path + f) and (f.endswith('.conf') or f.endswith('.ovpn'))] targets = [] for filename in filenames: lines = None with open(path + filename, 'r') as f: lines = [line.strip() for line in f.readlines()] found = False port = 0 addr = '' proto = '' for i, line in enumerate(lines): if not line: continue linesplit = line.split() if linesplit[0] == 'remote': port = linesplit[2] try: addr = socket.gethostbyname(linesplit[1]) ipaddress.ip_address(addr) except: print('cannot get ip address of {} in {}'.format(linesplit[1], filename)) break found = True lines[i] = 'remote {} {}'.format(addr, port) elif linesplit[0] == 'proto': proto = linesplit[1] if found: if not args.dry_run: with open(path + filename, 'w') as f: f.writelines('{}\n'.format('\n'.join(lines))) targets.append({ 'addr': addr, 'port': port, 'proto': proto, 'comment': 'VPN {}'.format(' '.join(filename.rsplit('.', 1)[0].split('_'))), }) rules = [] # iptables rules for target in targets: rules.append('# {}'.format(target['comment'])) rules.append('-A OUTPUT -p {} -m {} -d {}/32 --dport {} -j ACCEPT'.format(target['proto'], target['proto'], target['addr'], target['port'])) f = None if not args.dry_run: try: f = open('rules.txt', 'w') except OSError: print('failed to create a rule file.') print('write to stdout') try: print(*rules, sep='\n', file=f) except IOError: print('failed to write to the rule file') if f is not None and not args.dry_run: f.close() if __name__ == '__main__': main()
おまけ
動作確認はしていないので注意. 統合する気はありません.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys def main(): with open('./rules.txt') as f: vpn_rules = f.readlines() fw = '/etc/iptables/iptables.rules' with open(fw) as f: rules = f.readlines() start = -1 end = -1 for i, rule in enumerate(rules): if rule.startswith('## BEGIN VPN'): start = i elif rule.startswith('## END VPN'): end = i break if start >= end or end == -1: sys.exit(1) rules = rules[:start + 1] + vpn_rules + rules[end:] print(*rules, sep='') yn = input('write to file?[y/N]: ') if yn == 'y': with open(fw, 'w') as f: f.writelines(rules) if __name__ == '__main__': main()
安全でないストリーム暗号ARCFOUR
はじめに
Wikipediaを見てみたらARCFOURのアルゴリズムが単純だったので実装してみた. 一応言っておくが, 暗号アルゴリズムの実装は素人がやるものではない.
参考記事: https://en.wikipedia.org/wiki/RC4
記事に書いてある通りに実装した.
ソースコード
/* This program is in the public domain */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <stdint.h> #define MAX_KEY_LEN 256 #define S_LEN 256 uint8_t key[MAX_KEY_LEN + 1]; uint8_t S[S_LEN]; void swap(uint8_t *a, uint8_t *b) { uint8_t t = *a; *a = *b; *b = t; } char *readline(char *s, size_t size) { if (fgets(s, size, stdin) == NULL) return NULL; if (strchr(s, '\n') != NULL) s[strlen(s) - 1] = '\0'; else while (getchar() != '\n') ; return s; } /* key-scheduling algorithm */ void KSA(size_t key_len) { size_t i, j; for (i = 0; i < S_LEN; i++) S[i] = i; j = 0; for (i = 0; i < S_LEN; i++) { j = (j + S[i] + key[i % key_len]) % S_LEN; swap(&S[i], &S[j]); } } /* pseudo-random generation algorithm (PRGA) */ uint8_t PRGA(void) { /* static variables! */ static size_t i, j; i = (i + 1) % S_LEN; j = (j + S[i]) % S_LEN; swap(&S[i], &S[j]); return S[(S[i] + S[j]) % S_LEN]; } int main(int argc, char **argv) { FILE *fr, *fw; uint8_t buf; size_t key_len; if (argc != 3) { fprintf(stderr, "specify files\n"); return 1; } if (!readline((char *)key, MAX_KEY_LEN + 1)) { fprintf(stderr, "failed to read key"); return 1; } key_len = strlen((char *)key); KSA(key_len); if ((fr = fopen(argv[1], "rb")) == NULL) { fprintf(stderr, "failed to open file"); return 1; } if ((fw = fopen(argv[2], "wb")) == NULL) { fprintf(stderr, "failed to open file"); fclose(fr); return 1; } for (;;) { fread(&buf, sizeof(buf), 1, fr); if (feof(fr)) break; if (ferror(fr)) { fprintf(stderr, "failed to read\n"); break; } buf ^= PRGA(); if (fwrite(&buf, sizeof(buf), 1, fw) < 1) { fprintf(stderr, "failed to write\n"); break; } } fclose(fr); fclose(fw); return 0; }
動作テスト
テキストファイルtest.txt
を暗号化する.
暗号化すると文字化けするので16進数で表示する.
$ hexdump test.txt 0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab 0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6 0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981 0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882 0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393 0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70 0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354 0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5 0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e 0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3 00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781 00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182 00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af 00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881 00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81 00000f0 81e3 ef99 81bc 000a 00000f7
これをキーVirtual YouTuber
で暗号化してtest.txt.enc
に保存.
$ ./arc4 test.txt test.txt.enc Virtual YouTuber $ hexdump test.txt.enc 0000000 ee83 d240 22d7 820f afa8 e854 2757 303f 0000010 bfed 3ebb f9a3 b055 29da b054 e975 17c2 0000020 1211 d87e 97b4 bae8 a525 3d65 7256 05e6 0000030 138a 697d c071 a7a5 e715 75be 49de 8e4c 0000040 bf07 bfff b0a4 cc27 8498 0dbd 77ea 67b1 0000050 670a 2daa a470 ab84 251f 8129 9885 41c2 0000060 afd8 57e4 2552 39ff 18db 327e 0745 399f 0000070 5c34 b6e2 c6d2 93ef c14b d111 da67 b225 0000080 fb26 fc24 d760 15fa 71e7 dce4 b55f 6d71 0000090 3355 874c e3ef 105b 423d ca88 4d9b 58ec 00000a0 f162 ef70 510a 8faa 6ef5 d474 0a03 1598 00000b0 4a35 7574 494e 2117 b50c 9898 7e58 1a4a 00000c0 93b2 4c73 4116 2b17 b2ab 4290 9d2b 107f 00000d0 1f7e d8be e6ef 0bfe 2012 f740 4595 8a8d 00000e0 ff29 84a3 a444 7c0e fbae 2515 f988 2ce9 00000f0 dcd7 7249 e9ec 002a 00000f7
test.txt.enc
を復号してtest.txt.enc.dec
に保存.
$ ./arc4 test.txt.enc test.txt.enc.dec Virtual YouTuber $ hexdump test.txt.enc.dec 0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab 0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6 0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981 0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882 0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393 0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70 0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354 0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5 0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e 0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3 00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781 00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182 00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af 00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881 00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81 00000f0 81e3 ef99 81bc 000a 00000f7
もとに戻った.
$ sha256sum test.txt test.txt.enc.dec 0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6 test.txt 0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6 test.txt.enc.dec
おまけ
Rustの練習で書いてみた. まったく感覚がわからない.
use std::env; use std::fs::File; use std::io::{self, BufReader, BufWriter, Read, Write}; use std::process; const MAX_KEY_LEN: usize = 256; const S_LEN: usize = 256; struct Arc4 { s: [u8; S_LEN], i: usize, j: usize, } // key-scheduling algorithm fn ksa(key: &[u8]) -> Arc4 { let mut a = Arc4 { s: [0; S_LEN], i: 0, j: 0, }; for i in 0..S_LEN { a.s[i] = i as u8; } let mut j: usize = 0; let key_len = key.len(); for i in 0..S_LEN { j = (j + usize::from(a.s[i]) + usize::from(key[i % key_len])) % S_LEN; a.s.swap(i, j); } a } // pseudo-random generation algorithm (PRGA) fn prga(a: &mut Arc4) -> u8 { a.i = (a.i + 1) % S_LEN; a.j = (a.j + usize::from(a.s[a.i])) % S_LEN; a.s.swap(a.i, a.j); a.s[(usize::from(a.s[a.i]) + usize::from(a.s[a.j])) % S_LEN] } fn main() { if env::args().len() != 3 { println!("specify files"); process::exit(1); } let mut key_input = String::new(); io::stdin().read_line(&mut key_input).expect("failed to read input from stdin"); let key_input_trimmed = key_input.trim(); match key_input_trimmed.len() { 1...MAX_KEY_LEN => {}, _ => { println!("the key length must be 1-{} bytes (8 - {} bits)", MAX_KEY_LEN, MAX_KEY_LEN * 8); process::exit(1); } } let mut a = ksa(key_input_trimmed.as_bytes()); let args: Vec<String> = env::args().skip(1).collect(); let fr = File::open(&args[0]).expect("failed to open a file"); let mut reader = BufReader::new(fr); let fw = File::create(&args[1]).expect("failed to create a file"); let mut writer = BufWriter::new(fw); let mut buf = vec![]; reader.read_to_end(&mut buf).expect("failed to read a file"); for b in buf.iter_mut() { *b ^= prga(&mut a); } writer.write_all(&buf).expect("failed to write to a file"); }
TweetDeckのサムネイルを小さいサイズの画像にするブックマークレット
通信速度制限を喰らっているときにTweetDeckの画像抽出カラムの流速が速いととてもつらいのでそれを若干緩和させるブックマークレットを書いた. JavaScriptをまともに触るのは初めてなので苦戦. Web系まったくわからん.
Content Security PolicyのアレでこれをFirefoxで実行するにはabout:config
からsecurity.csp.enable
をfalse
にする必要がある.
常用する場合は*monkeyとかを使えばよさそう.
javascript:(()=>Array.from(document.getElementsByClassName('chirp-container')).forEach(e=>(new MutationObserver((r,o)=>r.forEach(s=>Array.from(s.addedNodes).forEach(n=>Array.from(n.getElementsByTagName('a')).filter(a=>a.hasAttribute('rel')&&a.getAttribute('rel')==='mediaPreview').forEach(a=>a.style.backgroundImage=a.style.backgroundImage.replace(/name=.*$/,'name=tiny")')))))).observe(e,{childList:true})))();
How to Play an Encrypted Video File Directly with openssl and mpv
openssl
と mpv
を使用して暗号化された動画ファイルを直接再生する方法
やむを得ない事情により, 復号したファイルを保存できない場合があるかもしれない.
openssl
と mpv
を使用することで, 暗号化された動画を直接再生することができる.
Example
Assume that your file vvv.enc
is encrypted with AES256-CBC with salt, and the passphrase is wholesome
.
You can decrypt it and play the movie with the following command:
$ openssl enc -d -aes-256-cbc -salt -pass pass:wholesome -in vvv.enc | mpv --force-seekable -
Linux Kernel の黒魔術マクロ: __is_constexpr(x)
__is_constexpr(x)
は Linux kernel 4.17 で導入されたマクロで、与えられた式が定数式であるかを 式を評価せずに 判定する。
/* * This returns a constant expression while determining if an argument is * a constant expression, most importantly without evaluating the argument. * Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de> */ #define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
kernel/git/torvalds/linux.git - Linux kernel source tree
Linus さんも「それはアイデアとは言わねえ。天才かマジで病んだ頭脳のどっちかだわ」(適当訳) とべた褒めしている。
LKML: Linus Torvalds: Re: detecting integer constant expressions in macros
サンプルコード:
#include <stdio.h> #define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) #define sq(x) ((x) * (x)) int main(void) { int a = 2; if (__is_constexpr(sq(2))) puts("Yes"); else puts("No"); if (__is_constexpr(sq(a))) puts("Yes"); else puts("No"); return 0; }
実行結果:
Yes No
はい天才。
解説は暇があったら書くが、既にある:
漢字変換を雑に実装してみる
一発変換と変換候補を出すだけ。 たぶん一番基本的なやつ。
真面目に日本語入力を実現するには形態素解析とかいろいろしなきゃいけないので自分には MURI。 ライブラリもあるけど興味もそこまでない。
Mozc 更新して。
コメント書けないのはアレだけど、Python だと JSON を標準ライブラリでサポートしてるので便利。
Wikipedia の Trie の記事を参考にした: https://en.wikipedia.org/wiki/Trie
ソースコード
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright (C) 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from collections import deque class Node(object): def __init__(self): self.values = [] self.children = {} class Trie(object): def __init__(self, vocabulary=None): self.__root = Node() if vocabulary is not None: for key, values in vocabulary.items(): self.insert(key, values) def insert(self, key, values): node = self.__root i = 0 while i < len(key): if key[i] in node.children: node = node.children[key[i]] i += 1 else: break while i < len(key): node.children[key[i]] = Node() node = node.children[key[i]] i += 1 node.values.extend(values) def query(self, key): node = self.__root for char in key: if char in node.children: node = node.children[char] else: return list() return node.values def suggest(self, key, n=10): suggestions = [] node = self.__root for char in key: if char in node.children: node = node.children[char] else: break if node is not self.__root: suggestions.extend(node.values) que = deque() que.append(node.children) while len(que) > 0: for child in que.popleft().values(): que.append(child.children) suggestions.extend(child.values) if len(suggestions) > n: return suggestions return suggestions
vocabulary.json
{ "い": ["胃", "異", "煎"], "いい": ["良い", "謂", "易々"], "いいん": ["委員", "医院"], "いいんちょう": ["委員長"] }
使用例
$ python3 Python 3.5.3 (default, Jan 19 2017, 14:11:04) [GCC 6.3.0 20170118] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from trie import Trie >>> import json >>> with open('vocabulary.json', 'r') as f: ... vocabulary = json.load(f) ... >>> trie = Trie(vocabulary) >>> trie.query('いいんちょう') ['委員長'] >>> trie.query('い') ['胃', '異', '煎'] >>> trie.query('がっきゅういいん') [] >>> trie.suggest('いい') ['良い', '謂', '易々', '委員', '医院', '委員長'] >>>
Uniqfuck: Brainfuck Translator
Uniqfuck
Brainfuck プログラムと Brainfuck 派生言語プログラムを相互変換するプログラムを作った。 名前は Uniqfuck とした。 ソースコードは記事の最後に載せる。 ライセンスは MIT License とする。
使い方
Uniqfuck の使い方を簡単に説明する。 Python が読める方はソースを読んだほうがはやい。
必須入力ファイルはソースファイルと文法定義ファイルである。 文法定義は JSON で記述する。
例: Mukadefuck
使い方の例として新たな Brainfuck 派生言語を作った。 言語名は Mukadefuck とした。
文法定義: mukade.json
{ "commands": { ">": "起立! 気を付け!", "<": "これがバーチャルYouTuberなんだよなあ……。", "+": "よいしょ。", "-": "わたくしで隠さなきゃ……!", ",": "クソ雑魚パンチやめてくださいwww", ".": "僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!", "[": "ぜろ……。ぜろ……。ぜろ……。", "]": "おるやんけ! おるやんけ! どなたかおるやんけ!" }, "special_characters": { "delimiter": " ", "r_delimiter": "" } }
Brainfuck -> Mukadefuck
"Hello, world!" を出力する Brainfuck プログラムを Mukadefuck に変換してみる。 ソースコードはニコニコ大百科から引用した。
helloworld.brainfuck
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+ ++.>-.------------.<++++++++.--------.+++.------.--------.>+.
出典: http://dic.nicovideo.jp/a/brainfuck
次のコマンドを実行すると
$ ./uniqfuck.py -o helloworld.mukadefuck mukade.json helloworld.brainfuck
次のファイルが得られる。
helloworld.mukadefuck
よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 ぜろ……。ぜろ……。ぜろ……。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 これがバーチャルYouTuberなんだよなあ……。 これがバーチャルYouTuberなんだよなあ……。 これがバーチャルYouTuberなんだよなあ……。 わたくしで隠さなきゃ……! おるやんけ! おるやんけ! どなたかおるやんけ! 起立! 気を付け! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! これがバーチャルYouTuberなんだよなあ……。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!
Mukadefuck -> Brainfuck
'X' を 8 個出力する Mukadefuck プログラムを Brainfuck に変換してみる。
x8.mukadefuck
# 'X' を 8 個出力するプログラムを作りましょう。 # ASCII コードの 'X' に対応する数値 88 を 3 番地に用意します。 # まずカウントダウン用に 0 番地に 8 を用意します。 「よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。」 # ここから 0 番地の値が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 2 番地に移動します。 「起立! 気を付け! 起立! 気を付け!」 # カウントダウン用 2 番地に 11 を用意します (8 * 11 = 88)。 「よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。」 # 2 番地が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 3 番地に移動し、1 加えます。 # ここに 88 が用意されます。 「起立! 気を付け! よいしょ。」 # 2 番地に移動し、1 引きます。 「これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 2 番地が 0 であれば、次に進みます。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # 1 番地に移動し 1 加えます。 # なぜかというと、'X' を 8 回表示するために、 # カウントダウン用の 8 を用意する必要があるからです # (「僕はね音楽なんて(略)」を 8 回書くと大変)。 # ここのループは 8 回繰り返されるので 8 が得られます。 「これがバーチャルYouTuberなんだよなあ……。よいしょ。」 # 0 番地に移動して 1 引きます。 「これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 0 番地が 0 であれば、次に進みます。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # 1 番地に移動します (1 番地の値は 8 です)。 「起立! 気を付け!」 # ここから 1 番地の値が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 3 番地まで移動します。 「起立! 気を付け!起立! 気を付け!」 # 3 番地の値 88、つまり 'X' を出力します。 「僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!」 # 1 番地に移動して、1 引きます。 「これがバーチャルYouTuberなんだよなあ……。これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 1 番地が 0 であればプログラムは終了します。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # これでプログラムは終了です。楽しんでいただけましたか? # 気を付け! 着席! # 以上、Mukadefuck のプログラムの例をお送りしました。
'-r' オプションを付けて実行すると
$ ./uniqfuck.py -r -o x8.brainfuck mukade.json x8.mukadefuck
次のファイルが得られる。
x8.brainfuck
++++++++[>>+++++++++++[>+<-]<+<-]>[>>.<<-]
ソースコード
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright (C) 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import sys import argparse import json def is_uniquely_decodable(codewords): def get_residual_words(prefixes, words): residual_words = set() if len(prefixes) == 0: return words for prefix in prefixes: for word in words: if word.find(prefix) == 0: residual_words.add(word[len(prefix):]) return residual_words s = [set()] for word in get_residual_words(codewords, codewords): s[0].add(word) s[0].remove('') i = 0 while True: if len(s[i]) == 0: return True for word in s[i]: if word == '' or word in codewords: return False for t in s[:i]: if t == s[i]: return True s.append(set()) for word in get_residual_words(codewords, s[i]): s[i + 1].add(word) for word in get_residual_words(s[i], codewords): s[i + 1].add(word) i += 1 def main(): parser = argparse.ArgumentParser(description='Uniqfuck: Meta(?) Brainfuck') parser.add_argument('-r', '--reverse', action='store_true', help='convert source code to Brainfuck code') parser.add_argument('-s', '--stdout', action='store_true', help='set output to stdout') parser.add_argument('-i', '--stdin', action='store_true', help='read source code from stdin') parser.add_argument('-o', '--output', nargs='?', default='a.out.txt', help='set output file name') parser.add_argument('grammar', metavar='grammar_file', nargs='?', help='grammar definition file') parser.add_argument('source', metavar='src_file', nargs='?', help='source file') args = parser.parse_args() reverse = args.reverse stdout = args.stdout stdin = args.stdin output = args.output if args.grammar is None or stdin == False and args.source is None: print('must specify files') sys.exit() grammar = args.grammar source = args.source commands = None special_characters = None with open(grammar, 'r') as f: grammar_dict = json.load(f) commands = grammar_dict['commands'] special_characters = grammar_dict['special_characters'] if is_uniquely_decodable(set(v for v in commands.values())) == False: print('not uniquely decodable') sys.exit() if reverse == True: commands = dict(((v, k) for k, v in commands.items())) source_code = None if stdin is True: source_code = ''.join(sys.stdin.readlines()) else: with open(source, 'r') as f: source_code = ''.join(f.readlines()) compiled_code = None if reverse == True: commands_lengths = sorted([len(k) for k in commands.keys()], reverse=True) compiled_commands = [] i = 0 while i + commands_lengths[-1] < len(source_code): for length in commands_lengths: if i + length > len(source_code): continue command = source_code[i:i + length] if command in commands: i += length compiled_commands.append(commands[command]) break else: i += 1 compiled_code = special_characters['r_delimiter'].join(compiled_commands) else: compiled_code = special_characters['delimiter'].join(commands[command] for command in source_code if command in commands) if stdout == True: print(compiled_code) else: with open(output, 'w') as f: f.write(''.join([compiled_code, '\n'])) if __name__ == '__main__': main()