commit 17eda7062b36097f20280a2b79e389351540e698 Author: 諏訪子 Date: Tue Jan 6 21:17:47 2026 +0900 SVNからのミラー diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d934d7f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# 0.3.1 (2024/05/??) +* Minix、Haiku、とmacOSでコンパイル出来る様に + +# 0.3.0 (2024/05/31) +* CDNの回避 +* Pixivからダウンロード出来る様に +* NetBSDは「/usr/pkg」→「/usr/local」 +* HTTPステータス200だけの場合をダウンロードする様に +* ダウンロードに失敗する場合、ファイルを消す +* 1つ以上のファイルがダウンロードに失敗する場合、正しく状況を知らせる様に +* 予行演習モード +* `make release-freebsd`の修正 +* 使い方の修正 +* 「ダウンロード中」や「ダウンロード済み」状況を表示する事が不要 + +# 0.2.0 (2024/04/26) +* ファイル名はパラメートルを付いたら、ファイル名は拡張子に +* 名前として保存オプション +* manpageの追加 +* ファイルが既に存在したら、ダウンロードに終了するオプション + +# 0.1.0 (2024/04/22) +* 最初リリース diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..777a97f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright © 2018-2024 by 076.moe + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4a57f15 --- /dev/null +++ b/Makefile @@ -0,0 +1,106 @@ +UNAME_S != uname -s +UNAME_M != uname -m +OS = ${UNAME_S} + +.if ${UNAME_S} == "OpenBSD" +OS = openbsd +.elif ${UNAME_S} == "NetBSD" +OS = netbsd +.elif ${UNAME_S} == "FreeBSD" +OS = freebsd +.elif ${UNAME_S} == "Linux" +OS = linux +.endif + +NAME != cat main.c | grep "const char\* sofname" | awk '{print $$5}' | \ + sed "s/\"//g" | sed "s/;//" +VERSION != cat main.c | grep "const char\* version" | awk '{print $$5}' | \ + sed "s/\"//g" | sed "s/;//" +PREFIX = /usr/local +.if ${UNAME_S} == "Linux" +PREFIX = /usr +.elif ${UNAME_S} == "Haiku" +PREFIX = /boot/home/config/non-packaged +.elif ${UNAME_S} == "Darwin" +.endif + +MANPREFIX = ${PREFIX}/share/man +.if ${UNAME_S} == "OpenBSD" +MANPREFIX = ${PREFIX}/man +.elif ${UNAME_S} == "Haiku" +MANPREFIX = ${PREFIX}/documentation/man +.endif + +CFLAGS = -Wall -Wextra -g -I/usr/include -I/usr/local/include +LDFLAGS = -L/usr/lib -L/usr/local/lib + +.if ${UNAME_S} == "NetBSD" || ${UNAME_S} == "Minix" +CFLAGS += -I/usr/pkg/include +LDFLAGS += -L/usr/pkg/lib +.elif ${UNAME_S} == "Haiku" +CFLAGS += -I/boot/system/develop/headers +LDFLAGS += -L/boot/system/develop/lib +.elif ${UNAME_S} == "Darwin" +CFLAGS += -I/opt/local/include +LDFLAGS += -L/opt/local/lib +.endif + +CC = cc +.if ${UNAME_S} == "Minix" +CC = clang +.endif + +FILES = main.c +LIBS = -lcurl + +STATIC = -static ${LIBS} +.if ${UNAME_S} == "OpenBSD" +STATIC += -lc -lnghttp3 -lngtcp2_crypto_quictls -lngtcp2 -lssl -lcrypto -lnghttp2\ + -lz -lpthread +.elif ${UNAME_S} == "FreeBSD" +STATIC += -liconv -lc -lnghttp2 -lssh2 -lpsl -lssl -lheimntlm -lhx509 -lcom_err\ + -lcrypto -lasn1 -lwind -lheimbase -lroken -lcrypt -lz -lkrb5 -lgssapi\ + -lgssapi_krb5 -lthr -lidn2 -lunistring -lprivateheimipcc +.elif ${UNAME_S} == "NetBSD" +STATIC += -lnghttp2 -lc -lidn2 -lunistring -lgssapi -lkrb5 -lhx509 -lintl -lssl\ + -lcrypto -lcrypt -lasn1 -lcom_err -lroken -lutil -lwind -lheimbase\ + -lheimntlm -lz -lpthread +.elif ${UNAME_S} == "Linux" +STATIC += -lc -lnghttp2 -lidn2 -lssh2 -lpsl -lssl -lcrypto -lzstd -lz -lunistring +.endif + +all: + ${CC} ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} ${LIBS} + strip ${NAME} + +clean: + rm -f ${NAME} + +dist: + mkdir -p ${NAME}-${VERSION} release/src + cp -R LICENSE.txt Makefile README.md CHANGELOG.md\ + ${NAME}.1 ${FILES} ${NAME}-${VERSION} + tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION} + rm -rf ${NAME}-${VERSION} + +man: + mkdir -p release/man/${VERSION} + sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > release/man/${VERSION}/${NAME}.1 + +release: + mkdir -p release/bin/${VERSION}/${OS}/${UNAME_M} + ${CC} ${CFLAGS} -o release/bin/${VERSION}/${OS}/${UNAME_M}/${NAME}\ + ${FILES} ${LDFLAGS} ${STATIC} + strip release/bin/${VERSION}/${OS}/${UNAME_M}/${NAME} + +install: + mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDOR}${MANPREFIX}/man1 + cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + sed "s/VERSION/${VERSION}/g" < ${NAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${NAME}.1 + chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + +uninstall: + rm -f ${DESTDIR}${MANPREFIX}/man1/${NAME}.1 + rm -f ${DESTDIR}${PREFIX}/bin/${NAME} + +.PHONY: all clean dist man release install uninstall diff --git a/README.md b/README.md new file mode 100644 index 0000000..092f9d9 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# odl - オンライン・ダウンロード + +## インストールする方法 +### OpenBSD +```sh +doas pkg_add curl +make +doas make install +``` + +### Void Linux +```sh +doas xbps-install curl bmake +bmake +doas bmake install +``` + +### FreeBSD +```sh +doas pkg install curl +make +doas make install +``` + +### NetBSD +```sh +doas pkgin install curl +make +doas make install +``` + +### CRUX Linux +```sh +doas prt-get depinst curl bmake +bmake +doas bmake install +``` + +### Minix +```sh +su +pkgin install curl clang bmake +bmake +bmake install +``` + +### Haiku +```sh +pkgman install curl curl_devel bmake +bmake +bmake install +``` + +### macOS +```sh +brew install curl bmake +bmake +doas bmake install +``` + +![](scrot1.png) +![](scrot2.png) diff --git a/main.c b/main.c new file mode 100644 index 0000000..ba1da2b --- /dev/null +++ b/main.c @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include + +#include + +const char* sofname = "odl"; +const char* version = "0.3.1"; +const char* avalopt = "nopv"; +char* filename; + +int opt; +int optind; +int yokou_flag = 0; +int output_flag = 0; +int version_flag = 0; +int already_flag = 0; +int err_flag = 0; +int dlstat = 0; +int onedlsuc = 0; + +char* get_filename(const char* url) { + char* fn_start = strrchr(url, '/'); + if (fn_start == NULL) { + return NULL; + } + fn_start++; + + char* query = strchr(fn_start, '?'); + char* anchor = strchr(fn_start, '#'); + char* fn_end = NULL; + + if (query != NULL && anchor != NULL) { + fn_end = (query < anchor) ? query : anchor; + } else if (query != NULL) { + fn_end = query; + } else if (anchor != NULL) { + fn_end = anchor; + } + + // URLでパラメートルがなければ、そのままファイル名をコピーして + if (fn_end == NULL) { + fn_end = strchr(fn_start, '\0'); + } + + size_t length = fn_end - fn_start; + + char* extfn = malloc(length + 1); + if (extfn == NULL) { + return NULL; + } + + strncpy(extfn, fn_start, length); + extfn[length] = '\0'; + + return extfn; +} + +int progress_callback(void *cp, double dt, double dn, double ut, double un) { + (void)cp; + (void)ut; + (void)un; + + double progress = (dn / dt) * 100.0; + + printf("\r["); + int barw = 50; + int pos = (int)(progress * barw / 100.0); + + for (int i = 0; i < barw; ++i) { + if (i < pos) printf("="); + else if (i == pos) printf(">"); + else printf(" "); + } + + printf("] %.2f%% %s", progress, filename); + fflush(stdout); + + return 0; +} + +void handle_o(int argc, char* argv[]) { + output_flag = 1; + if (optind < argc) { + if (filename != NULL) { + fprintf( + stderr, + "-oをご利用する場合、1つのファイルだけをダウンロード出来ます。\n" + ); + } + filename = argv[optind]; + } else { + fprintf(stderr, "-oの後でファイル名がありません。"); + err_flag = 1; + } +} + +void dlsucmsg() { + if (yokou_flag == 0) printf("\nダウンロードに完了しました。\n"); + else { + printf("\nダウンロードに完了しました。\n"); + printf("予行演習モードですので、ファイルを保存していません。\n"); + printf("保存するには、「-n」フラグを消して下さい。\n"); + } +} + +void usage() { + printf("%s-%s\nusage: %s [-%s] [url ...]\n", sofname, version, sofname, avalopt); +} + +void flags(int opt, int argc, char* argv[]) { + switch (opt) { + case 'n': + yokou_flag = 1; + break; + case 'o': + handle_o(argc, argv); + break; + case 'p': + already_flag = 1; + break; + case 'v': + version_flag = 1; + break; + default: + err_flag = 1; + usage(); + break; + } +} + +int downloader(CURL* curl, char* filename, const char* url) { + if (already_flag == 1 && access(filename, F_OK) != -1) { + printf("ファイルが既に存在しますので、ダウンロードしません。\n"); + return 1; + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + + // Clownflareは面倒くさいわね・・・ + curl_easy_setopt( + curl, + CURLOPT_USERAGENT, + "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0" + ); + // Pixivも結構面倒くさい + if ( + strstr("s.pixiv.net", url) == 0 || + strstr("i.pixiv.net", url) == 0 || + strstr("s.pximg.net", url) == 0 || + strstr("i.pximg.net", url) == 0 + ) { + curl_easy_setopt(curl, CURLOPT_REFERER, "https://www.pixiv.net/"); + } + + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + + FILE* file = NULL; + + if (yokou_flag == 0) { + file = fopen(filename, "wb"); + } else { + file = fopen("/tmp/odl_test", "wb"); + } + + if (!file) { + perror("ファイルを開けません。"); + curl_easy_cleanup(curl); + return -1; + } + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + + long httpcode = 0; + CURLcode res = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode); + + fclose(file); + if (yokou_flag == 1) unlink("/tmp/odl_test"); + + if (res != CURLE_OK) { + if (yokou_flag == 0) unlink(filename); + fprintf(stderr, "\nダウンロードに失敗: %s\n", curl_easy_strerror(res)); + return -1; + } + + if (res == CURLE_ABORTED_BY_CALLBACK || httpcode != 200) { + if (yokou_flag == 0) unlink(filename); + fprintf(stderr, "\n%sをダウンロードに失敗: HTTP CODE: %ld\n", filename, httpcode); + return -1; + } + + printf("\n"); + + return 0; +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + usage(); + return 1; + } + + while ((opt = getopt(argc, argv, avalopt)) != -1) { + flags(opt, argc, argv); + } + + if (err_flag == 1) { + return 1; + } + + if (version_flag == 1) { + printf("%s-%s\n", sofname, version); + return 0; + } + + CURL* curl = curl_easy_init(); + if (!curl) { + perror("curlを初期設置に失敗。"); + return -1; + } + + // 一つのファイルだけが可能 + if (output_flag == 1) { + if (optind >= argc) { + fprintf(stderr, "-oの後でURLがありません。"); + return 1; + } + + if (filename == NULL) { + fprintf(stderr, "-oの後でファイル名がありません。"); + return 1; + } + + filename = argv[optind]; + const char* url = argv[optind+1]; + dlstat = downloader(curl, filename, url); + if (dlstat == 0) onedlsuc = 1; + + curl_easy_cleanup(curl); + + if (onedlsuc == 1) { + dlsucmsg(); + return 0; + } + + return 1; + } + + // 複数ファイル名可能 + for (int i = optind; i < argc; i++) { + const char* url = argv[i]; + filename = get_filename(url); + if (filename == NULL) { + fprintf(stderr, "URLからファイル名を抽出出来ませんでした。\n"); + continue; + } + + dlstat = downloader(curl, filename, url); + free(filename); + if (dlstat == 0) onedlsuc = 1; + } + + curl_easy_cleanup(curl); + if (onedlsuc == 1) { + dlsucmsg(); + return 0; + } + + return 1; +} diff --git a/odl.1 b/odl.1 new file mode 100644 index 0000000..f2ec362 --- /dev/null +++ b/odl.1 @@ -0,0 +1,25 @@ +.TH ODL 1 VERSION +.SH NAME +odl - onrain downloader +.SH SYNOPSIS +.B odl +[-v] [-o \fI\,name\fR \fI\,file\fR] [-pn] \fI\,file\fR [\fI\,...\fR] +.SH DESCRIPTION +.PP +ネットから簡単にファイルをダウンロード出来るツールです。 +.TP +-\fB\,n\fR \fI\,file\fR \fI\,...\fR +ファイル保存せずにダウンロードが可能かどうか確認出来る。 +.TP +-\fB\,o\fR \fI\,name\fR \fI\,file\fR +ファイル名を付いてダウンロードします。 +それで1回1つのファイルのみダウンロード可能です。 +.TP +-\fB\,p\fR \fI\,file\fR \fI\,...\fR +ファイルは既に存在する場合、ダウンロードしない様にします。 +.TP +-\fB\,v\fR +バージョンを表示 +.SH AUTHORS +.PP +テクニカル諏訪子 diff --git a/scrot1.png b/scrot1.png new file mode 100755 index 0000000..9b1412f Binary files /dev/null and b/scrot1.png differ diff --git a/scrot2.png b/scrot2.png new file mode 100755 index 0000000..1abdc23 Binary files /dev/null and b/scrot2.png differ