[C++] 166 FLTK : SFTP補助アプリの製作 日本語ファイル名への対応

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

前回の続きです。

SFTP補助アプリですが、ファイル名に日本語が含まれる場合はクリップボードへコピーされないことが判明しました。毎度ながら実行ファイルでは問題なく、appファイルでおかしくなります。

解決方法はpbcopyの際のロケール設定です。日本語を全く受け付けないのではなく、文字化けでも構わないのでコピーして欲しかったです。解決の手掛かりになりますので。

AppleScriptで同じトラブルに見舞われた方のブログ記事を参考にしました。まさかpbcopyに変なクセがあるとは思いもよりませんでした。

void cmd_cb(Fl_Widget*, void*) {
    string cmd0;
    string cmd1;
    string cmd;

    int onoff_put = put_rbtn->value();
    int onoff_dir = dir_rbtn->value();

    if (onoff_put == 1){
        cmd0 = "put";
    } else {
        cmd0 = "get";
    }

    if (onoff_dir == 1){
        cmd1 = "-r";
    } else {
        cmd1 = "";
    }

    const char* fileName = file_input -> value();
    const char* toName = to_input -> value();

    cmd = cmd0 + " " + cmd1 + " \"" + string(fileName) + "\" " + string(toName);
    cout << "cmd " << cmd << endl;

    cmd_input -> value("");
    cmd_input -> value(cmd.c_str());

    string cmdCopy = "echo '" + cmd + "' | LANG=ja_JP.UTF-8 pbcopy"; // 対応箇所
    output_line -> insert(cmdCopy.c_str());
    output_line -> insert("\n");
    system(cmdCopy.c_str());
}

参考記事

[C++] 165 FLTK : SFTP補助アプリの製作

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

ローカル環境のPC間でファイルを転送するためのコマンドを生成するSFTP補助アプリを製作しました。

CONNボタンを押すとSFTPコマンド、CMDボタンを押すとPUTあるいはGETコマンドがクリップボードにコピーされます。

これでMacOS、Windows、Linux間のファイル転送が楽になりそうです。

#include <SFTPAssist.h>
#include <FileChooser.h>

Fl_Window *window;
Fl_Group *radio_btns, *radio_btns2;
Fl_Radio_Round_Button *get_rbtn, *put_rbtn, *file_rbtn, *dir_rbtn;
Fl_Box *pc_label, *file_label, *to_label, *conn_label, *cmd_label;
Fl_Input *file_input, *to_input, *conn_input, *cmd_input;
Fl_Button *conn_btn, *cmd_btn, *clear_btn, *fileFcBtn, *toFcBtn;
Fl_Choice *choice;
FileChooser* chooser;

void conn_cb(Fl_Widget*, void*) {
    string cmd_conn;
    string pc;

    int choice_num = choice -> value();
    switch(choice_num){
        case 0:{
            pc = "aaa.local";
            break;
        }
        case 1:{
            pc = "bbb.local";
            break;
        }
        case 2:{
            pc = "ccc.local";
            break;
        }
    }

    string cmd_pwd = "pwd";
    system(cmd_pwd.c_str());

    cmd_conn = "sftp " + pc;
    cout << "cmd_conn " << cmd_conn << endl;

    conn_input -> value("");
    conn_input -> value(cmd_conn.c_str());

    string cmdConnCopy = "printf '" + cmd_conn + "' | pbcopy";
    system(cmdConnCopy.c_str());

}

void cmd_cb(Fl_Widget*, void*) {
    string cmd0;
    string cmd1;
    string cmd;

    int onoff_put = put_rbtn->value();
    int onoff_dir = dir_rbtn->value();

    if (onoff_put == 1){
        cmd0 = "put";
    } else {
        cmd0 = "get";
    }

    if (onoff_dir == 1){
        cmd1 = "-r";
    } else {
        cmd1 = "";
    }

    const char* fileName = file_input -> value();
    const char* toName = to_input -> value();

    cmd = cmd0 + " " + cmd1 + " \"" + string(fileName) + "\" " + string(toName);
    cout << "cmd " << cmd << endl;

    cmd_input -> value("");
    cmd_input -> value(cmd.c_str());

    string cmdCopy = "printf '" + cmd + "' | pbcopy";
    system(cmdCopy.c_str());

}

void clear_cb(Fl_Widget*, void*) {
    file_input->value("");
    to_input->value("");
    conn_input->value("");
    cmd_input->value("");
}

void FileChooserCB(Fl_Widget*, void*) {
    int fc;
    string appdir = "/Volumes/Macintosh HD/Users/xxxxx/";
    int onoff_dir = dir_rbtn->value();

    if (onoff_dir == 1){
        fc = 4; // FileChooser::DIRECTORY
    } else {
        fc = 0; // FileChooser::SINGLE
    }

    int x_win = window->x_root();
    int y_win = window->y_root();
    cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;

    chooser = new FileChooser(appdir.c_str(), 
                        "*.*",
                        fc,
                        "File/Dir Chooser",490,380
                        );
    
    chooser->resize(x_win+85,y_win+50,490,380);
    chooser->set_modal();
    chooser->Fl_Window::show();

    while(chooser->Fl_Window::shown()){
        Fl::wait();
    }
}

int main(int argc, char **argv) {
    Fl::set_font(FL_HELVETICA, "Osaka");
    window = new Fl_Window(100,100,360,245,"SFTP Assist");
    window->color(fl_rgb_color(147,184,129)); // やなぎぞめ
    
    // file
    pc_label = new Fl_Box(18,15,35,16,"PC");
    window->add(pc_label);
    pc_label->labelsize(14);
    pc_label->labelcolor(fl_rgb_color(255,239,213));
    pc_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    // PC選択項目
    Fl_Menu_Item pc_type[4] = {
        {"Mac mini Intel"},
        {"WindowsPC"},
        {"LinuxPC"}
    };

    // PC選択メニュー
    choice = new Fl_Choice(55, 10, 220, 25, nullptr);
    choice->menu(pc_type);

    radio_btns = new Fl_Group(75,45,185,20,"");{
        radio_btns->labelsize(12);

        // put_rbtn
        put_rbtn = new Fl_Radio_Round_Button(75,45,90,20,"PUT");
        put_rbtn->labelcolor(fl_rgb_color(255,239,213));
        put_rbtn->setonly();
        
        // get_rbtn
        get_rbtn = new Fl_Radio_Round_Button(170,45,90,20,"GET");
        get_rbtn->labelcolor(fl_rgb_color(255,239,213));
            
    }
    radio_btns->end();

    radio_btns2 = new Fl_Group(75,75,185,20,"");{
        radio_btns2->labelsize(12);

        // file_rbtn
        file_rbtn = new Fl_Radio_Round_Button(75,75,90,20,"FILE");
        file_rbtn->labelcolor(fl_rgb_color(255,239,213));
        file_rbtn->setonly();
        
        // dir_rbtn
        dir_rbtn = new Fl_Radio_Round_Button(170,75,90,20,"DIR");
        dir_rbtn->labelcolor(fl_rgb_color(255,239,213));
            
    }
    radio_btns2->end();

    // FILE/DIR
    file_label = new Fl_Box(18,112,52,13,"FILE/DIR");
    file_label->labelcolor(fl_rgb_color(255,239,213));
    file_label->labelsize(12);
    file_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    file_input = new Fl_Input(75,105,165,25,"");
    file_input->textsize(12);

    // fileFcBtn
    fileFcBtn = new Fl_Button(245,108,30,20,"...");
    fileFcBtn->color(fl_rgb_color(112,128,144));
    fileFcBtn->labelcolor(fl_rgb_color(255,239,213));
    fileFcBtn->labelsize(14);
    fileFcBtn->callback(FileChooserCB);

    // TO
    to_label = new Fl_Box(18,146,20,14,"TO");
    to_label->labelcolor(fl_rgb_color(255,239,213));
    to_label->labelsize(14);
    to_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    to_input = new Fl_Input(75,140,165,25,"");
    to_input->textsize(12);

    // toFcBtn
    toFcBtn = new Fl_Button(245,143,30,20,"...");
    toFcBtn->color(fl_rgb_color(112,128,144));
    toFcBtn->labelcolor(fl_rgb_color(255,239,213));
    toFcBtn->labelsize(14);
    toFcBtn->callback(FileChooserCB);

    // CONN
    conn_label = new Fl_Box(18,181,44,14,"CONN");
    conn_label->labelcolor(fl_rgb_color(255,239,213));
    conn_label->labelsize(14);
    conn_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    conn_input = new Fl_Input(75,175,270,25,"");
    conn_input->color(fl_rgb_color(187,200,230)); // あわふじいろ
    conn_input->textsize(12);

    // CMD
    cmd_label = new Fl_Box(18,216,34,14,"CMD");
    cmd_label->labelcolor(fl_rgb_color(255,239,213));
    cmd_label->labelsize(14);
    cmd_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    cmd_input = new Fl_Input(75,210,270,25,"");
    cmd_input->color(fl_rgb_color(187,200,230));
    cmd_input->textsize(12);

    // CONN_BTN
    conn_btn = new Fl_Button(295,10,50,40,"CONN");
    conn_btn->color(fl_rgb_color(112,128,144));
    conn_btn->labelcolor(fl_rgb_color(255,239,213));
    conn_btn->labelsize(14);
    conn_btn->callback(conn_cb);

    // CMD_BTN
    cmd_btn = new Fl_Button(295,60,50,40,"CMD");
    cmd_btn->color(fl_rgb_color(112,128,144));
    cmd_btn->labelcolor(fl_rgb_color(255,239,213));
    cmd_btn->labelsize(14);
    cmd_btn->callback(cmd_cb);

    clear_btn = new Fl_Button(295,110,50,25,"CLR");
    clear_btn->color(fl_rgb_color(112,128,144));
    clear_btn->labelcolor(fl_rgb_color(255,239,213));
    clear_btn->labelsize(14);
    clear_btn->callback(clear_cb);

    window->end();
    window->show(argc, argv);
    return Fl::run();
}

[MacOS] Windows機とのファイル送受信 SFTPコマンド例

[M1 Mac, Big Sur 11.6.8]
[DeskMini B660, Windows11]

MacとWindows機との間でファイルのやりとりができるようにしました。

Macをサーバーとする送受信はOpenSSHクライアントがデフォルトで有効になっているため、何も設定しなくてもWindows機コマンドプロンプト(あるいはPowerShell)からSFTPコマンドで可能です。

今回の設定はWindows機がサーバー、MacがクライアントのSFTPによるファイル送受信になります。

手順は以下の通りです。

1.Windows11のオプション機能 OpenSSHサーバーを追加する。

2.”コンピューターの管理”でOpenSSHサーバーを有効にする。

`3.コマンドプロンプトにhostnameを入力してホスト名を確認する。

4.MacのターミナルからWindows機にアクセスする。

sftp ホスト名.local

5.getコマンド、putコマンドでファイルをコピーする。

# Windows11のホームにあるtest.txtをMacのdirへコピーする。
get test.txt [to mac dir]

# Windows11のホームにあるtestディレクトリをMacのホームへコピーする。
get -r test

# Windows11のホームにあるtestディレクトリ内のファイル全てをMacのdirへコピーする。
get -r test [to mac dir]

# Macのホームにあるtest2.txtをWindows11のdirへコピーする。
put test2.txt [to win dir]

[MacOS] Mac間のファイル転送 SFTP

[M1 Mac, Big Sur 11.6.8]

ローカル環境のMac間でファイルをコピーする時は、iCloudやFinderのSMB接続を使っていましたが、iCloudは同期が異様に遅い、SMB接続はトラブルが多いため、結局USBストレージでコピーファイルを移すことが多かったです。

最近、通信プロトコルを学習していて、SFTPコマンドを使えば簡単にファイル転送できることを知りました。SFTPはFTPのセキュリティ強化版です。なおAirDropはWi-Fi接続が必須なので今回は除外しています。

これまでGUIでSMBプロトコルを使うとトラブルに見舞われることが多かったので、これを機会にSFTPへシフトするつもりです。

これで大分作業効率がアップしそうです。Macユーザーでどれくらいの方がSFTPを使っているか知りたいです。

次はWindows機との効率的なファイル送受信を検討します。

SMBのトラブル例(他にログインがなくてもこうなる)
SFTPによるファイル転送(ディレクトリ転送も可能)

[Python] 338 ディレクトリ内ファイルの文字コード一括変換 上書きの場合

[M1 Mac, Big Sur 11.6.8, Python 3.10.4]

Pythonに関する記事は約1ヶ月ぶりです。

最近TCP/IPに関する書籍を購入したのですが、サンプルコードの文字コードがEUC-JPなので以下のコードでUTF-8に一括変換しました。

サンプルコードのディレクトリをコピーして上書き変換させています。過去に類似の記事がありますが、別ファイルへ変換したケースです。

import glob,subprocess

dir = "/sample_code/CSockets_copy"
paths_c = glob.glob(dir + "/**/*.c", recursive=True)
paths_h = glob.glob(dir + "/**/*.h", recursive=True)

paths_c.extend(paths_h)

print(paths_c)
print(len(paths_c))

for path in paths_c:
    cmd = "nkf -w --overwrite %s" %path
    subprocess.call(cmd, shell=True)

[C++] 164 POP3プロトコル POP3サーバの仮設置 Dovecot

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年) 第3章はPOP3プロトコルです。Intel MacからM1 Macに戻ってきました。

DovecotというIMAP/POP3サーバを導入し、プログラムで接続するところまで進めました。これはかなり難易度が高かったです。朝から深夜まで休憩をはさみつつ丸一日掛かっています。実験環境構築に没頭するあまり、目指しているわけでもないブログの連続投稿が途切れました。

まずHomebrewからインストールしたDovecotは起動してもポートを開けられませんでした。次にソースコード(v 2.3.19.1)をビルドしようとしたところmakeで失敗。Intel Macでもダメでした。エラー内容を確認するとコンパイルオプション-std=gnu99が悪さをしているようです。

2019年8月リリースの安定版 v 2.2.36.4をビルドするとこれが上手くいき、そのまま接続までスムーズでした。M1 Mac発売から1年以上前のコードが動くとは正直思いませんでした。

こちらのMakefileには-std=gnu99が入っていません。最新版は古いコード仕様も使えるようにしてバグが発生したということでしょうか。

話は変わりますが、Homebrewはこれまでの経験から今一つ信用できないところがあります。Homebrew版が使えなくて、GitHubから入手したコードで成功した例が結構あります。意地でもやらないでしょうが、RubyにこだわらずPythonも開発言語に加えてはいかがでしょうか。

とにかく接続はできたので、あとはコードの詳細を詰めていきます。

DovecotによりPOP3のポート番号110開放(netstat -natコマンドで確認)
poptestコマンドで接続成功

[C++] 163 SMTPプロトコル 仮想SMTPサーバーとの通信

[Intel Mac, Big Sur 11.6.5, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年)第2章 SMTPプロトコルの所を学習しています。

仮想SMTPサーバー FakeSMTPにメールを送信しデータのやり取りをさせました。

サンプルコードにあったEUC-JP→ISO-2022-JP変換関数とBase64変換関数は大幅に変更しました。EUC-JP→ISO-2022-JP変換(MacはUTF-8から変換)はネットにあったコードを丸々拝借しました。libiconvライブラリを使用します。Base64変換についてはclxライブラリをダウンロードして使っています。これによりcppファイル2つを削減しました。

FakeSMTPのソースコードを入手しM1 Macでビルドしたものの、残念ながら起動しませんでした。

Apple Silicon版仮想SMTPサーバーを自製できれば良いのですが。

#include <mailtest.h>
#include <clx/base64.h>

int main(int argc,char *argv[])
{
	struct passwd	*pwd;
	char	hostname[MAXHOSTNAMELEN+1];
	time_t	t;
	char	from[65];
	char	b_subject[1024];
	char	j_data[1024*2];
	char	*ptr;
	char	buf[1024];
	char	boundary[80];
	char	*f_buf,*b_buf;
	FILE	*fpd;
	struct stat	st;
	int	i;
	int	soc;

	if(argc <= 1){
		fprintf(stderr,"mailtest recpient [file] [file] ...\n");
		return 0;
	}

	 // JIS:Base64のサブジェクトを標準入力から取得
	GetSubject(b_subject);

	 // 本文データを標準入力から取得
	GetData(j_data);

	cout << "GetData pass" << endl;

	 // 送信者設定
	pwd = getpwuid(getuid());
	gethostname(hostname,MAXHOSTNAMELEN);
	sprintf(from,"%s@%s",pwd->pw_name,hostname);

	cout << "from: " << from << endl;

	 // ローカルホストのSMTPに接続
	if((soc = ConnectHost("localhost","smtp",25)) == -1){
		fprintf(stderr,"Cannot connect to local smtp.\n");
		return 0;
	}

	 // SMTPとの通信
	SOCprintf(soc,"HELO %s\r\n",hostname);
	SOCrecv(soc,buf);
	SOCprintf(soc,"MAIL FROM:%s\r\n",from);
	SOCrecv(soc,buf);
	SOCprintf(soc,"RCPT TO:%s\r\n",argv[1]);
	SOCrecv(soc,buf);

	 // データ送信開始
	SOCprintf(soc,"DATA\r\n");
	SOCrecv(soc,buf);

	 // ヘッダ部送信
	t=time(&t);
	SOCprintf(soc,"Date: %s",ctime(&t));
	SOCprintf(soc,"From: %s\r\n",from);
	SOCprintf(soc,"Subject: =?ISO-2022-JP?B?%s?=\r\n",b_subject);
	SOCprintf(soc,"To: %s\r\n",argv[1]);
	SOCprintf(soc,"MIME-Version: 1.0\r\n");
	sprintf(boundary,"%010d_%09d",t,getpid()); // プロセスID取得
	SOCprintf(soc,"Content-Type: MULTIPART/mixed; BOUNDARY=%s\r\n",boundary);
	SOCprintf(soc,"\r\n");

	 // データ部送信
	SOCprintf(soc,"--%s\r\n",boundary);
	SOCprintf(soc,"Content-Type: TEXT/plain; charset=ISO-2022-JP\r\n");
	SOCprintf(soc,"\r\n");

	send(soc,j_data,strlen(j_data),0);

	SOCprintf(soc,"\r\n");

	 // 添付ファイル送信
	for(i = 2; i < argc; i++){
		if((fpd = fopen(argv[i], "r"))!= NULL){
			printf("argv[%d] %s\n",i,argv[i]);

			fstat(fileno(fpd), &st); // ファイル状態を取得
			f_buf=(char *)malloc(st.st_size);
			b_buf=(char *)malloc(st.st_size * 2);
			fread(f_buf, 1, st.st_size, fpd); // ファイルデータを1バイトずつ全サイズ分をf_bufに格納
			fclose(fpd);
			SOCprintf(soc,"--%s\r\n",boundary);
			SOCprintf(soc,"Content-Type: application; name=%s; x-unix-mode=0777\r\n",argv[i]);
			SOCprintf(soc,"Content-Transfer-Encoding: BASE64\r\n");
			SOCprintf(soc,"Content-Description: %s\r\n",argv[i]);
			SOCprintf(soc,"\r\n");
			
			// Base64変換
			string f_buf_str = string(f_buf);
			string f_buf_64 = clx::base64::encode(f_buf_str);
			f_buf_64.copy(b_buf, st.st_size *2 -1);

			// base64(f_buf,st.st_size,b_buf); // base64関数は廃止

			send(soc,b_buf,strlen(b_buf),0);
			SOCprintf(soc,"\r\n");
			SOCprintf(soc,"\r\n");
			free(f_buf);
			free(b_buf);
		}
		else{
			fprintf(stderr,"%s cannot read.\n",argv[i]);
		}
	}

	SOCprintf(soc,"--%s--\r\n",boundary);

	 // データ送信完了 
	SOCprintf(soc,".\r\n");
	SOCrecv(soc,buf);

	 // 終了 
	SOCprintf(soc,"QUIT\r\n");
	SOCrecv(soc,buf);

	 // ソケットクローズ 
	SocketClose(soc);
}

参考サイト

[C++] 162 SMTPプロトコル 仮想SMTPサーバーの設置 Intel Macへの回帰

[Intel Mac, Big Sur 11.6.5, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年)第2章 SMTPプロトコルのサンプルコードを参考に仮想SMTPサーバーへメールを送る実験に取り組んでいます。

Java製の仮想SMTPサーバー FakeSMTPがM1 Macでは起動できなかったため、Intel Macでの開発に切り替えました。

メールタイトルはUTF-8からISO-2022-JPを経て添付ファイルとともにbase64に変換しました。試行錯誤の末、やっとメールの送信に成功しました。

開発環境は整ったので、これから本腰を入れます。やはりアプリ開発・プログラミング学習をする上でIntel Macはまだまだ必要ですね。

送信したメールの内容
mailtestコマンド(仮送信先と添付ファイル)
--------------------------------------------------
mailtest test_recv@example.com mailtest.h
--------------------------------------------------
出力
--------------------------------------------------
Subject: 日本語
instr 日本語
outstr BF|K\8l // ISO-2022-JP
sub_jis: BF|K\8l
sub_64: GyRCRnxLXDhs // Base64
b_subject: GyRCRnxLXDhs
GetData開始
test
.
instr test

outstr test

GetData pass
from: noMac-mini.local
soc: 3
sockaddr: 0x7ffee0f0b0c8
IP adrress: 0x7ffee0f0b0cc
HELO 
>>>>>220 localhost ESMTP SubEthaSMTP null

MAIL FROM:
>>>>>250 localhost

RCPT TO:
>>>>>250 Ok

DATA
>>>>>250 Ok

Date: From: 
Subject: =?ISO-2022-JP?B??=
To: 
MIME-Version: 1.0
Content-Type: MULTIPART/mixed; BOUNDARY=

--
Content-Type: TEXT/plain; charset=ISO-2022-JP

--
Content-Type: application; name=; x-unix-mode=0777
Content-Transfer-Encoding: BASE64
Content-Description: 

----
.
>>>>>354 End data with <CR><LF>.<CR><LF>

QUIT
>>>>>250 Ok

[C++] 161 SMTPプロトコル ソースファイルの再構成

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

ようやく『C言語によるTCP/IPネットワークプログラミング』の第2章 SMTPプロトコルに進みます。

まずは第1章と同様に2001年作成のソースコードがビルドできるようにしました。cppファイルが3つに増えたので、srcディレクトリなどを作成し、オブジェクトファイルやヘッダファイルを別々に管理するようにしています。

MacOSの場合はそのままではgetuid関数やmalloc関数がないというエラーが出ますが、それぞれunistd.h、stdlib.hをインクルードすれば解決します。

骨が折れる作業ですが、以前手がけたゲームプログラミングの本に比べたら大分楽です。汎用性が低かったゲームのコードに比べ、得られた知識を積み上げてメーラーやサーバーアプリ製作などの成果を上げられそうなのでモチベーションは高いです。

これから動作確認を行います。

getuidに関するMacOSマニュアルの説明
COMPILER = clang++
CPPFLAGS = -g -w -std=c++98

INCLUDE = -I./include
LDLIBS = 

# ソースファイル
SRCDIR = ./src
SRCS = $(shell find $(SRCDIR) -type f)

# オブジェクトファイル
OBJDIR = ./obj
OBJS = $(addprefix $(OBJDIR), $(patsubst ./src/%.cpp,/%.o,$(SRCS)))

# 実行ファイル
TARGETDIR = ./bin
TARGET = mailtest

# cppファイルからoファイル作成 $<:依存ファイル
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
	$(COMPILER) $(CPPFLAGS) $(INCLUDE) $(DEBUG) -o $@ -c $<

# oファイルから実行ファイル作成
$(TARGET):$(OBJS)
	$(COMPILER) -o $(TARGETDIR)/$@ $(OBJS) $(LDLIBS)

# 全ソース・コンパイル&ビルド
.PHONY:all
all: clean $(OBJS) $(TARGET)

# oファイル・実行ファイル削除
.PHONY:clean
clean:
	rm -rf $(OBJS) $(TARGETDIR)/$(TARGET)
Smtp $ make all
rm -rf ./obj/base64.o ./obj/mailtest.o ./obj/ujtoj.o ./bin/mailtest
clang++ -g -std=c++98 -w -I./include  -o obj/base64.o -c src/base64.cpp
clang++ -g -std=c++98 -w -I./include  -o obj/mailtest.o -c src/mailtest.cpp
clang++ -g -std=c++98 -w -I./include  -o obj/ujtoj.o -c src/ujtoj.cpp
clang++ -o ./bin/mailtest ./obj/base64.o ./obj/mailtest.o ./obj/ujtoj.o