手元で urlencode
/urldecode
するのに毎回rubyとかでワンライナー書かなきゃならないのがめんどくさくて、
どうせ libcurl
入ってるんだからもう標準っぽいコマンドが欲しいなー、とかずっと思ってたけど、
じゃあもう簡易ラッパ書くか、っていう話。
-
cgo
触りたかったのと、 - 中身は
libcurl
の共有ライブラリを参照して、ガワの部分はクロスコンパイルしたい目論見で、
golang
で書いてみたはいいものの…
後述する問題のためにクロスコンパイルが実現できず、そしたらもう全部cでいいじゃん状態。
gccのFLAG類がコメントに含められるので Makefile
書かなくていい分ちょっと楽かも。
curl のヘッダが要るので、カレントディレクトリにcloneしておく。
あと、 LDFLAGS
は libcurl
のあるパスに書き換えが必要。
urlencode
package main
/*
#cgo CFLAGS: -I./curl/include/
#cgo LDFLAGS: -L/usr/local/Cellar/curl/7.54.1/lib/ -lcurl
#include <string.h>
#include <stdio.h>
#include <curl/curl.h>
void escape(const char* buf) {
CURL* curl = curl_easy_init();
if(curl) {
char* output = curl_easy_escape(curl, buf, strlen(buf));
if(output) {
fprintf(stdout, "%s", output);
fflush(stdout);
curl_free(output);
}
curl_easy_cleanup(curl);
}
}
*/
import "C"
import (
"io/ioutil"
"os"
)
func main() {
buf, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
s2 := C.CString(string(buf))
C.escape(s2)
}
urldecode
package main
/*
#cgo CFLAGS: -I./curl/include/
#cgo LDFLAGS: -L/usr/local/Cellar/curl/7.54.1/lib/ -lcurl
#include <string.h>
#include <stdio.h>
#include <curl/curl.h>
void unescape(const char* buf) {
CURL* curl = curl_easy_init();
if(curl) {
int outlength;
char* output = curl_easy_unescape(curl, buf, strlen(buf), &outlength);
if(output) {
fprintf(stdout, "%s", output);
fflush(stdout);
curl_free(output);
}
curl_easy_cleanup(curl);
}
}
*/
import "C"
import (
"io/ioutil"
"os"
)
func main() {
buf, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(err)
}
s2 := C.CString(string(buf))
C.unescape(s2)
}
ビルドと実行
go build urlencode.go
go build urldecode.go
echo 'Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。' | ./urlencode | ./urldecode
余談
超付け焼き刃なので cgo
の理解が追いついてないのだけど、
go build
すると静的リンクになってしまうけど、本体だけのビルドってできるのかな?
デバッグ用シンボルっぽいいろいろ余計なものが入るからサイズがでかくなってるけど、共有ライブラリ使用のビルド自体はできている様子。
というかmacのgccはそもそも -static
リンクができないっぽい。
$ export PKG_CONFIG_PATH=/usr/local/Cellar/curl/7.54.1/lib/pkgconfig
$ g++ -static urlencode.cpp `pkg-config --static --cflags libcurl` `pkg-config --static --libs libcurl`
ld: library not found for -lcrt0.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ただし、
can't load package: package main: build constraints exclude all Go files in /path/to
とか出てクロスコンパイルはできない。
【おまけ】 c++ で libcurl.so
版
そしたらもう全部cでいいじゃん版。
考えてみたらコマンド分ける意味なかったので、base64
みたいにdecodeオプションにした。
あと、 stdin
一気読みがcだと面倒なのでc++にした。
#include <string>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <curl/curl.h>
void escape(const std::string& buf) {
CURL* curl = curl_easy_init();
if(curl) {
char* output = curl_easy_escape(curl, buf.c_str(), buf.size());
if(output) {
std::cout << output << std::flush;
curl_free(output);
}
curl_easy_cleanup(curl);
}
}
void unescape(const std::string& buf) {
CURL* curl = curl_easy_init();
if(curl) {
int outlength;
char* output = curl_easy_unescape(curl, buf.c_str(), buf.size(), &outlength);
if(output) {
std::cout << output << std::flush;
curl_free(output);
}
curl_easy_cleanup(curl);
}
}
int main(int argc, char* argv[]) {
int opt;
bool decode = false;
while ((opt = getopt(argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
decode = true;
break;
}
}
std::stringstream sstream;
sstream << std::cin.rdbuf();
if(decode) {
unescape(sstream.str());
} else {
escape(sstream.str());
}
return 0;
}
ビルドと実行
CFLAGS=-I./curl/include/
LDFLAGS=/usr/local/Cellar/curl/7.54.1/lib/
g++ urlencode.cpp -o urlencode -lcurl
echo 'Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。' | ./urlencode | ./urlencode -d
確認
手元のはmacなので、こう。
otool -l urlencode | grep --before 3 --after 3 libcurl
linuxならlddで。