Last active
April 26, 2023 22:20
-
-
Save gvanem/6e574d133106e5d99f6dba1d80e3702b to your computer and use it in GitHub Desktop.
Wget2 stuff for Windows (clang-cl): added 'libwget/mswindows.[ch]' and 'Makefile.Windows'.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Check for unused libraries in a MSVC link .map file. | |
# Prints with colours using 'colorama' (if available). | |
# | |
import os, sys | |
class Color(): | |
RESET = RED = WHITE = "" | |
try: | |
from colorama import init, Fore, Style | |
init() | |
Color.RESET = Style.RESET_ALL | |
Color.RED = Fore.RED + Style.BRIGHT | |
Color.WHITE = Fore.WHITE + Style.BRIGHT | |
except: | |
pass | |
class State(): | |
IDLE = 0 | |
UNUSED = 1 | |
ignore_libs = [ "oldnames.lib" ] | |
def report (map_file, unused): | |
num = len(unused) | |
if num == 0: | |
return | |
print ("%s%d unused %s in %s:%s" % (Color.RED, num, ["library", "libraries"][num > 1], map_file, Color.RESET)) | |
for u in unused: | |
print (" " + u) | |
def process_map (file, state): | |
unused_libs = [] | |
f = open (file, "rt") | |
lines = f.readlines() | |
f.close() | |
for l in lines: | |
l = l.strip() | |
if l == "Unused libraries:": | |
state = State.UNUSED | |
continue | |
if state == State.UNUSED: | |
if l == "": | |
break | |
if os.path.basename (l).lower() not in ignore_libs: | |
unused_libs.append (l) | |
return unused_libs | |
map_file = sys.argv[1] | |
report (map_file, process_map (map_file, State.IDLE)) | |
print ("%sDone.%s\n" % (Color.WHITE, Color.RESET)) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# GNU Makefile for Wget2 (MSVC and clang-cl). | |
# 32-bit only. | |
# | |
# By <[email protected]> 2015 - 2021. | |
# | |
THIS_FILE = Makefile.Windows | |
THIS_DIR = $(realpath .) | |
comma := , | |
DATE := $(shell date +%d-%B-%Y) | |
# | |
# Only 'x86' supported at the moment. | |
# | |
CPU ?= x86 | |
# | |
# Comment away this to avoid rebuilding everything when | |
# $(THIS_FILE) changes. | |
# | |
MDEPEND = # $(THIS_FILE) | |
# | |
# Choose your weapons. | |
# | |
USE_BROTLI ?= 1 | |
USE_BZIP2 ?= 0 | |
USE_LIBPSL ?= 0 | |
USE_LZMA ?= 0 | |
USE_ZLIB ?= 1 | |
USE_LZIP ?= 0 | |
USE_NGHTTP2 ?= 0 | |
USE_GPGME ?= 0 | |
USE_MHD_STATIC ?= 0 | |
USE_LIBIDN ?= 0 | |
USE_LIBIDN2 ?= 1 | |
USE_MP_COMPILE ?= 0 | |
USE_DOXYGEN ?= 0 | |
USE_FUZZ ?= 0 | |
USE_PANDOC ?= 1 | |
USE_ASTYLE_FORMAT ?= 1 | |
USE_CLANG_FORMAT ?= 1 | |
# | |
# Use one of these TLS libraries: | |
# | |
USE_GNUTLS ?= 1 | |
USE_GNUTLS_STATIC ?= 1 | |
USE_OPENSSL ?= 0 | |
USE_OPENSSL_STATIC ?= 1 | |
USE_WOLFSSL ?= 0 | |
USE_WOLFSSL_STATIC ?= 0 | |
# | |
# What library to link to for wget2.exe and test-programs. | |
# If 1: Use libwget2.dll | |
# If 0: Use libwget2.lib + other needed libraries. | |
# | |
USE_LIBWGET2_STATIC ?= 1 | |
# | |
# Use GnuLib as a DLL or a static library. | |
# | |
USE_GNULIB_STATIC ?= 1 | |
# | |
# Use the tracing Wsock_trace lib (and not ws2_32.lib). | |
# Currently works best for MSVC/clang-cl. | |
# Ref: https://github.com/gvanem/wsock-trace/ | |
# | |
USE_WSOCK_TRACE ?= 1 | |
ifeq ($(CPU),x86) | |
BITS = 32 | |
else ifeq ($(CPU),x64) | |
BITS = 64 | |
else | |
$(error Unsupported CPU: $(CPU).) | |
endif | |
ifeq ($(USE_GPGME),1) | |
$(warning Not possible to use 'GPGME' on MSVC/clang-cl) | |
USE_GPGME = 0 | |
endif | |
ifeq ($(USE_LIBIDN2),1) | |
ifeq ($(USE_GNULIB_STATIC),0) | |
# | |
# Since GnuLib's <uninorm.h> doesn't have the appropriate '__declspec(dllimport)' | |
# on 'uninorm_nfc', 'UC_CATEGORY_M' needed by idn2.lib, we're forced to use GnuLib | |
# as a static libraray. | |
# | |
$(error Not possible to use GnuLib as a DLL with 'USE_LIBIDN2=1' on MSVC/clang-cl) | |
endif | |
endif | |
ifeq ($(CC),clang-cl) | |
# | |
# No point using multi-compile ('-MP') with clang-cl. | |
# | |
USE_MP_COMPILE = 0 | |
endif | |
# | |
# Change paths to suite your setup: | |
# | |
MINGW_ROOT = $(realpath $(MINGW32)) | |
VC_ROOT = $(realpath $(VSINSTALLDIR)) | |
MAN_PAGE_ROOT ?= $(MINGW_ROOT)/share | |
PLUGINS = $(addprefix .libs/, \ | |
libplugindb.dll \ | |
libpluginname.dll \ | |
libpluginexit.dll \ | |
libpluginapi.dll \ | |
libpluginoption.dll \ | |
libalpha.dll \ | |
libbeta.dll) | |
# | |
# URL used to generate '$(OBJ_DIR)/ansi-test-1.h' which gets included | |
# into 'libwget/mswindows.c' when '-DANSI_TEST' is set. | |
# Ref: | |
# the rule for '$(OBJ_DIR)/ansi-test.obj' and 'unit-tests/ansi-test.exe' below. | |
# | |
ANSI_TEST_1_URL = 'http://adoxa.altervista.org/ansicon/ANSI%20Prompt%20Colours.txt' | |
ANSI_TEST_2_URL = 'http://wttr.in/' | |
# | |
# Extract each digit from configure.ac: | |
# AC_INIT([wget2], [1.99.0], [[email protected]], [wget2], [https://savannah.gnu.org/projects/wget]) | |
# word 1 ^ 2 ^ | |
# | |
get_ver_digit = $(word $(1), $(shell grep -m1 AC_INIT configure.ac | grep -o '[0-9*]')) | |
VER_MAJOR := $(call get_ver_digit, 2) | |
VER_MINOR := $(call get_ver_digit, 3) | |
VER_PATCH := $(call get_ver_digit, 4) | |
VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) | |
# | |
# Change paths to suite your setup: | |
# | |
BROTLI_ROOT ?= $(MINGW_ROOT)/src/Compression/Brotli | |
BZIP2_ROOT ?= $(MINGW_ROOT)/src/Compression/bzip2-1.0.2 | |
LZMA_ROOT ?= $(MINGW_ROOT)/src/Compression/xz | |
ZLIB_ROOT ?= $(MINGW_ROOT)/src/Compression/zlib-1.2.8 | |
LZIP_ROOT ?= $(MINGW_ROOT)/src/Compression/lzip | |
GMP_ROOT ?= $(MINGW_ROOT)/src/Math/gmp | |
GNULIB_ROOT ?= $(MINGW_ROOT)/src/Gnu/GnuLib | |
GNUTLS_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/GnuTLS | |
GPGME_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/gpgme-1.1.6 | |
GPGERROR_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/libgpg-error-1.6 | |
OPENSSL_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/OpenSSL | |
WOLFSSL_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/WolfSSL | |
MICRO_HTTPD_ROOT ?= $(MINGW_ROOT)/src/inet/Web/libmicrohttpd | |
NETTLE_ROOT ?= $(MINGW_ROOT)/src/inet/Crypto/nettle | |
IDN_ROOT ?= $(MINGW_ROOT)/src/inet/IDN/libidn | |
IDN2_ROOT ?= $(MINGW_ROOT)/src/inet/IDN/libidn2 | |
NGHTTP2_ROOT ?= $(MINGW_ROOT)/src/inet/Web/nghttp2 | |
LIBPSL_ROOT ?= $(MINGW_ROOT)/src/inet/Web/libpsl | |
PYTHON ?= py -3 | |
VPATH = libwget src tests unit-tests examples | |
# | |
# The 'root directory' for 'make install'; | |
# i.e. where to copy '$(TARGETS)' to. | |
# | |
INSTALL_ROOT = $(VC_ROOT) | |
define Usage | |
Usage: "make -f $(THIS_FILE) CC=[cl | clang-cl] [all | clean | vclean | realclean | depend | install]" | |
Specify CC=cl - use MSVC | |
Specify CC=clang-cl - use clang-cl | |
endef | |
# | |
# The following ESC codes assumes you have MSys/Cygwin's echo with colour support. | |
# | |
BRIGHT_GREEN = \e[1;32m | |
BRIGHT_YELLOW = \e[1;33m | |
BRIGHT_WHITE = \e[1;37m | |
colour_msg = @echo -e '$(1)\e[0m' | |
green_msg = $(call colour_msg,$(BRIGHT_GREEN)$(strip $(1))) | |
yellow_msg = $(call colour_msg,$(BRIGHT_YELLOW)$(strip $(1))) | |
white_msg = $(call colour_msg,$(BRIGHT_WHITE)$(strip $(1))) | |
# | |
# Create a list of .obj-files from .c-files. | |
# | |
c_to_obj = $(addprefix $(OBJ_DIR)/$(strip $(2)), $(notdir $(1:.c=.obj))) | |
CFLAGS = -nologo -W3 -Z7 -DWIN32 \ | |
-D_CRT_SECURE_NO_WARNINGS \ | |
-D_CRT_NO_POSIX_ERROR_CODES \ | |
-MD -GS- -Gy -GF -Ob2 -Ox -Oy -Ot | |
LDFLAGS = -nologo -verbose -incremental:no \ | |
-version:$(VER_MAJOR).$(VER_MINOR)$(VER_PATCH) | |
# | |
# Enable .DLL and .EXEs to be used on Win-Vista (6.1) in case | |
# it was built under > Win-Vistas. | |
# | |
LDFLAGS += -subsystem:console,6.01 | |
OBJ_DIR = objects | |
# | |
# clang-cl: | |
# Undefine any '%CL' in the environment. It is NOT supported by 'clang-cl'. | |
# | |
export CL= | |
ifeq ($(CC),cl) | |
CFLAGS += -wd4005 -wd4068 -wd4090 -wd4244 | |
RCFLAGS = -nologo -D_MSC_VER | |
LINK = link | |
$(error Not yet possible to use MSVC due to lack of C99-features) | |
else ifeq ($(CC),clang-cl) | |
RCFLAGS = -nologo -D__clang__ | |
LINK = link | |
# | |
# (We MUST use 'clang-cl' as a drop-in replacement for the non-C99 compliant 'cl.exe'). | |
# But 'link.exe' is the same. | |
# | |
CFLAGS += -Wall \ | |
-Wno-unused-local-typedef \ | |
-Wno-unused-function \ | |
-Wno-ignored-attributes \ | |
-Wno-unknown-pragmas \ | |
-Wno-unused-parameter \ | |
-Wno-sign-compare \ | |
-Wno-pointer-sign \ | |
-Wno-unused-variable \ | |
-Wno-expansion-to-defined \ | |
-Wno-unused-label \ | |
-Wno-reserved-id-macro -Wno-undef \ | |
-Wno-language-extension-token \ | |
-Wno-nonportable-system-include-path \ | |
-Wno-disabled-macro-expansion \ | |
-Wno-documentation-unknown-command \ | |
-Wno-keyword-macro \ | |
-Wno-sign-conversion \ | |
-Wno-conversion \ | |
-Wno-shorten-64-to-32 \ | |
-Wno-vla \ | |
-Wno-pedantic \ | |
-Wno-unused-macros \ | |
-Wno-documentation \ | |
-Wno-documentation-pedantic \ | |
-Wno-cast-qual \ | |
-Wno-cast-align \ | |
-Wno-visibility \ | |
-Wno-missing-noreturn \ | |
-Wno-covered-switch-default \ | |
-Wno-embedded-directive \ | |
-Wno-format-nonliteral \ | |
-Wno-gnu-include-next \ | |
-Wno-gnu-zero-variadic-macro-arguments | |
# | |
# Warnings like: | |
# $(GNULIB_ROOT)/lib/stdlib.h(150,19): warning: '_Exit' redeclared without 'dllimport' attribute: | |
# previous 'dllimport' ignored [-Winconsistent-dllimport] | |
# _GL_FUNCDECL_SYS (_Exit, _Noreturn void, (int status)); | |
# ^ | |
# are pretty serious. But Gnulib's functions have no 'dllexport' attribute on it's functions. | |
# | |
CFLAGS += -Wno-inconsistent-dllimport | |
# | |
# Squelch some new annoying warnings in 'clang-cl' v.13: | |
# | |
# warning: identifier '_Stream' is reserved because it starts with '_' followed by a | |
# capital letter [-Wreserved-identifier] | |
# _Inout_ FILE* const _Stream, | |
# ^ | |
# and: | |
# libwget/ssl_gnutls.c(1867,31): warning: cast from function call of type 'gnutls_transport_ptr_t' (aka 'void *') to | |
# non-matching type 'ptrdiff_t' (aka 'int') [-Wbad-function-cast] | |
# int sockfd = (int)(ptrdiff_t)gnutls_transport_get_ptr(session); | |
# ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
# | |
ifeq ($(CLANG_MAJOR_VER),13) | |
CFLAGS += -Wno-reserved-identifier \ | |
-Wno-bad-function-cast \ | |
-Wno-cast-function-type | |
endif | |
else | |
$(error $(usage)) | |
endif | |
# CFLAGS += -DUSE_GNULIB_POLL -DUSE_WSAPOLL | |
LDFLAGS += -map -debug #:fastlink | |
# | |
# Static + import library names we produce and use: | |
# | |
WGET2_STAT_LIB = libwget2.lib | |
WGET2_IMP_LIB = libwget2_imp.lib | |
# | |
# GnuLib library names we need: | |
# | |
GNULIB_STAT_LIB = $(GNULIB_ROOT)/lib/gnulib-vc$(BITS).lib | |
GNULIB_IMP_LIB = $(GNULIB_ROOT)/lib/gnulib-vc$(BITS)_imp.lib | |
# | |
# The needed libraries for 'libwget2.dll' and the plugin DLLs. | |
# | |
DLL_LIBS = | |
# | |
# Man-pages generated by Doxygen if 'USE_DOXYGEN = 1'. | |
# | |
MAN3_PAGES = $(addprefix docs/man/man3/, \ | |
libwget-base64.3 \ | |
libwget-buffer.3 \ | |
libwget-console.3 \ | |
libwget-dns-caching.3 \ | |
libwget-hash.3 \ | |
libwget-hashmap.3 \ | |
libwget-hpkp.3 \ | |
libwget-hsts.3 \ | |
libwget-io.3 \ | |
libwget-ip.3 \ | |
libwget-iri.3 \ | |
libwget-list.3 \ | |
libwget-mem.3 \ | |
libwget-parse_atom.3 \ | |
libwget-progress.3) | |
# | |
# The needed libraries for 'wget2.exe' and test '*.exe' files. | |
# Possibly far too many. | |
# | |
# Note: if 'USE_LIBWGET2_STATIC = 1', the libraries in 'DLL_LIBS' will | |
# be added to 'EX_LIBS' when used to link 'wget2.exe'. | |
# | |
EX_LIBS = | |
TARGETS = $(WGET2_STAT_LIB) $(WGET2_IMP_LIB) libwget2.dll wget2.exe $(PLUGINS) | |
ifeq ($(USE_DOXYGEN),1) | |
TARGETS += wget2.chm | |
# | |
# Let Doxygen generate man-pages too (docs/man/man3/libwget*.3) | |
# | |
TARGETS += $(MAN3_PAGES) | |
endif | |
ifeq ($(USE_PANDOC),1) | |
TARGETS += docs/wget2.pdf | |
endif | |
CFLAGS += -I. -I./libwget -I./src \ | |
-I$(GNULIB_ROOT)/lib \ | |
-D_WIN32_WINNT=0x0601 | |
LIB_SRC = $(addprefix libwget/, \ | |
atom_url.c \ | |
bar.c \ | |
base64.c \ | |
bitmap.c \ | |
buffer.c \ | |
buffer_printf.c \ | |
console.c \ | |
cookie.c \ | |
cookie_parse.c \ | |
css.c \ | |
css_tokenizer.c \ | |
css_url.c \ | |
decompressor.c \ | |
dns.c \ | |
dns_cache.c \ | |
encoding.c \ | |
error.c \ | |
hashfile.c \ | |
hashmap.c \ | |
hash_printf.c \ | |
hpkp.c \ | |
hpkp_db.c \ | |
hsts.c \ | |
html_url.c \ | |
http.c \ | |
http_highlevel.c \ | |
http_parse.c \ | |
init.c \ | |
io.c \ | |
ip.c \ | |
iri.c \ | |
list.c \ | |
log.c \ | |
logger.c \ | |
mem.c \ | |
metalink.c \ | |
mswindows.c \ | |
net.c \ | |
netrc.c \ | |
ocsp.c \ | |
plugin.c \ | |
printf.c \ | |
random.c \ | |
robots.c \ | |
rss_url.c \ | |
sitemap_url.c \ | |
stringmap.c \ | |
strlcpy.c \ | |
strscpy.c \ | |
thread.c \ | |
tls_session.c \ | |
utils.c \ | |
vector.c \ | |
xalloc.c \ | |
xml.c) | |
WGET2_SRC = $(addprefix src/, \ | |
bar.c \ | |
blacklist.c \ | |
dl.c \ | |
gpgme.c \ | |
host.c \ | |
job.c \ | |
log.c \ | |
options.c \ | |
plugin.c \ | |
stats_server.c \ | |
stats_site.c \ | |
testing.c \ | |
utils.c \ | |
wget.c) | |
# | |
# to-do | |
# | |
# WGET2_SRC += src/gmpme.c | |
WGET_OBJ = $(call c_to_obj, $(WGET2_SRC), wget_) | |
UNIT_TEST_SRC = $(addprefix unit-tests/, \ | |
buffer_printf_perf.c \ | |
stringmap_perf.c \ | |
test-cond.c \ | |
test-decompress.c \ | |
test-dl.c \ | |
test-cookies-http_state.c \ | |
test-parse-html.c \ | |
test.c) | |
TEST_SRC = $(addprefix tests/, \ | |
test--accept.c \ | |
test--filter-mime-type.c \ | |
test--follow-tags.c \ | |
test--https-enforce-hard1.c \ | |
test--https-enforce-hard2.c \ | |
test--https-enforce-hard3.c \ | |
test--https-enforce-soft1.c \ | |
test--https-enforce-soft2.c \ | |
test--https-enforce-soft3.c \ | |
test--page-requisites.c \ | |
test--save-content-on.c \ | |
test--spider-r.c \ | |
test-auth-basic.c \ | |
test-auth-digest.c \ | |
test-base.c \ | |
test-c-r.c \ | |
test-convert-file-only.c \ | |
test-cookies.c \ | |
test-chunked.c \ | |
test-cookies-http_state.c \ | |
test-compression.c \ | |
test-cut-dirs.c \ | |
test-directory-clash.c \ | |
test-download-attr.c \ | |
test-i-http.c \ | |
test-i-https.c \ | |
test-idn-cmd.c \ | |
test-idn-meta.c \ | |
test-idn-robots.c \ | |
test-ignore-length.c \ | |
test-include-and-exclude-directories.c \ | |
test-interrupt-response.c \ | |
test-iri-disabled.c \ | |
test-iri-forced-remote.c \ | |
test-iri-list.c \ | |
test-iri-percent.c \ | |
test-iri-subdir.c \ | |
test-iri.c \ | |
test-gzip.c \ | |
test-k.c \ | |
test-limit-rate.c \ | |
test-limit-rate-http2.c \ | |
test-meta-robots.c \ | |
test-metalink.c \ | |
test-np.c \ | |
test-ocsp-server.c \ | |
test-p-nc.c \ | |
test-parse-css.c \ | |
test-parse-html-css.c \ | |
test-parse-rss.c \ | |
test-plugin.c \ | |
test-plugin-failure.c \ | |
test-plugin-interception.c \ | |
test-plugin-nonexistance.c \ | |
test-post-handshake-auth.c \ | |
test-redirection.c \ | |
test-restrict-ascii.c \ | |
test-robots.c \ | |
test-robots-off.c \ | |
test-stats-dns.c \ | |
test-stats-ocsp.c \ | |
test-stats-server.c \ | |
test-stats-site.c \ | |
test-stats-tls.c \ | |
test-timestamping.c \ | |
test-unlink.c \ | |
test-wget-1.c) | |
ifeq ($(USE_GPGME),1) | |
TEST_SRC += $(addprefix tests/, \ | |
test-gpg-save-failed.c \ | |
test-gpg-verify-no-file.c) | |
endif | |
# | |
# Not finished? | |
# | |
# TEST_SRC += tests/test--post-file.c \ | |
# tests/test-E-k.c | |
# | |
TEST_PROGRAMS = $(TEST_SRC:.c=.exe) | |
TEST_OBJ = $(call c_to_obj, $(TEST_SRC)) | |
TEST_I = $(notdir $(TEST_SRC:.c=.i)) | |
UNIT_TEST_OBJ = $(call c_to_obj, $(UNIT_TEST_SRC)) | |
UNIT_TEST_I = $(notdir $(UNIT_TEST_SRC:.c=.i)) | |
UNIT_TEST_PROGRAMS = unit-tests/test.exe \ | |
unit-tests/test-cond.exe \ | |
unit-tests/test-cookies-http_state.exe \ | |
unit-tests/test-decompress.exe \ | |
unit-tests/test-dl.exe \ | |
unit-tests/test-parse-html.exe \ | |
unit-tests/ansi-test.exe \ | |
libwget/test_linking.exe \ | |
libwget/test_linking_robots.exe | |
FUZZ_SRC = $(addprefix fuzz/, \ | |
libwget_atom_url_fuzzer.c \ | |
libwget_bar_fuzzer.c \ | |
libwget_base64_fuzzer.c \ | |
libwget_cookie_fuzzer.c \ | |
libwget_css_url_fuzzer.c \ | |
libwget_html_url_fuzzer.c \ | |
libwget_http_client_fuzzer.c \ | |
libwget_iri_fuzzer.c \ | |
libwget_metalink_parse_fuzzer.c \ | |
libwget_robots_parse_fuzzer.c \ | |
libwget_sitemap_url_fuzzer.c \ | |
libwget_utils_fuzzer.c \ | |
libwget_xml_parse_buffer_fuzzer.c \ | |
main.c) | |
FUZZ_PROGRAMS = $(filter fuzz/libwget_%, $(FUZZ_SRC:.c=.exe)) | |
FUZZ_OBJ = $(call c_to_obj, $(FUZZ_SRC)) | |
FUZZ_I = $(notdir $(FUZZ_SRC:.c=.i)) | |
$(FUZZ_OBJ) $(FUZZ_I): EXTRA_CFLAGS += -DTEST_RUN | |
$(UNIT_TEST_OBJ) $(UNIT_TEST_I): EXTRA_CFLAGS += -DUNIT_TEST_RUN | |
ifeq ($(USE_FUZZ),1) | |
VPATH += fuzz | |
TARGETS += $(FUZZ_PROGRAMS) | |
endif | |
ifeq ($(USE_GPGME),1) | |
CFLAGS += -DWITH_GPGME -I$(GPGME_ROOT)/gpgme -I$(GPGERROR_ROOT)/src | |
DLL_LIBS += $(GPGME_ROOT)/libgpgme.a \ | |
$(GPGERROR_ROOT)/libgpg-error.a | |
endif | |
ifeq ($(USE_ZLIB),1) | |
CFLAGS += -DWITH_ZLIB=1 -I$(ZLIB_ROOT) | |
DLL_LIBS += $(ZLIB_ROOT)/lib/$(CPU)/zlib.lib | |
endif | |
# | |
# Untested | |
# | |
ifeq ($(USE_LZIP),1) | |
CFLAGS += -DWITH_LZIP=1 -I$(LZIP_ROOT) | |
DLL_LIBS += $(LZIP_ROOT)/lib/$(CPU)/lzip.lib | |
endif | |
# | |
# Not finished | |
# | |
ifeq ($(USE_LIBPSL),1) | |
CFLAGS += -DWITH_LIBPSL=1 -I$(LIBPSL_ROOT)/include | |
DLL_LIBS += $(LIBPSL_ROOT)/lib/psl.lib | |
endif | |
ifeq ($(USE_NGHTTP2),1) | |
CFLAGS += -DWITH_LIBNGHTTP2=1 -DNGHTTP2_STATICLIB -I$(NGHTTP2_ROOT)/lib/includes | |
DLL_LIBS += $(NGHTTP2_ROOT)/lib/nghttp2.lib | |
endif | |
ifeq ($(USE_BROTLI),1) | |
CFLAGS += -DWITH_BROTLIDEC=1 -I$(BROTLI_ROOT)/c/include | |
DLL_LIBS += $(BROTLI_ROOT)/brotli.lib | |
endif | |
ifeq ($(USE_BZIP2),1) | |
CFLAGS += -DWITH_BZIP2=1 -I$(BZIP2_ROOT) | |
DLL_LIBS += $(BZIP2_ROOT)/libbz2.lib | |
endif | |
ifeq ($(USE_LZMA),1) | |
CFLAGS += -DWITH_LZMA=1 -DLZMA_API_STATIC -I$(LZMA_ROOT)/include | |
DLL_LIBS += $(LZMA_ROOT)/lib/$(CPU)/lzma.lib | |
endif | |
# | |
# to-do .. | |
# | |
ifeq ($(USE_LIBPSL),1) | |
CFLAGS += -DWITH_LIBPSL=1 | |
endif | |
ifeq ($(USE_OPENSSL),1) | |
LIB_SRC += libwget/ssl_openssl.c | |
CFLAGS += -DWITH_OPENSSL=1 -DWITH_TLS=1 -I$(OPENSSL_ROOT)/include | |
ifeq ($(USE_OPENSSL_STATIC),1) | |
DLL_LIBS += $(OPENSSL_ROOT)/lib32/libcommon.lib \ | |
$(OPENSSL_ROOT)/lib32/libcrypto.lib \ | |
$(OPENSSL_ROOT)/lib32/libssl.lib | |
else | |
DLL_LIBS += $(OPENSSL_ROOT)/lib32/libcrypto_imp.lib \ | |
$(OPENSSL_ROOT)/lib32/libssl_imp.lib | |
endif | |
else ifeq ($(USE_GNUTLS),1) | |
LIB_SRC += libwget/ssl_gnutls.c | |
CFLAGS += -DWITH_GNUTLS=1 -DWITH_TLS=1 -I$(GNUTLS_ROOT)/lib/includes | |
ifeq ($(USE_GNUTLS_STATIC),1) | |
# $(error 'USE_GNUTLS_STATIC=1' not possible with 'CC=$(CC)'.) | |
ifeq ($(USE_LIBIDN2),0) | |
$(error 'USE_GNUTLS_STATIC=1' needs 'USE_LIBIDN2=1'.) | |
endif | |
# | |
# Adding 'GNUTLS_INTERNAL_BUILD' will cause functions in | |
# 'gnutls.h' to NOT be marked with with '__declspec(dllexport)'. | |
# | |
CFLAGS += -DUSE_GNUTLS_STATIC -DGNUTLS_INTERNAL_BUILD -DASN1_STATIC | |
DLL_LIBS += $(GNUTLS_ROOT)/lib/gnutls-gv.lib \ | |
$(GNUTLS_ROOT)/lib/libtasn1.lib \ | |
$(NETTLE_ROOT)/nettle.lib \ | |
$(GMP_ROOT)/gmp.lib | |
DLL_LIBS += advapi32.lib crypt32.lib ncrypt.lib | |
else | |
DLL_LIBS += $(GNUTLS_ROOT)/lib/gnutls-gv_imp.lib | |
endif | |
else ifeq ($(USE_WOLFSSL),1) | |
LIB_SRC += libwget/ssl_wolfssl.c | |
CFLAGS += -DWITH_WOLFSSL=1 -DWITH_TLS=1 -DHAVE_ALPN -DBIO_NOCLOSE=0 -I$(WOLFSSL_ROOT) | |
ifeq ($(USE_WOLFSSL_STATIC),1) | |
DLL_LIBS += $(WOLFSSL_ROOT)/lib/WolfSSL.lib | |
else | |
DLL_LIBS += $(WOLFSSL_ROOT)/lib/WolfSSL_imp.lib | |
endif | |
else | |
LIB_SRC += libwget/ssl_none.c | |
endif | |
# | |
# libidn2.a MUST be listed after libgnutls.a in | |
# case 'USE_GNUTLS_STATIC = 1'. | |
# | |
ifeq ($(USE_LIBIDN),1) | |
ifeq ($(USE_LIBIDN2),1) | |
$(error Cannot have both 'USE_LIBIDN=1' and 'USE_LIBIDN2=1') | |
endif | |
CFLAGS += -DWITH_LIBIDN=1 \ | |
-DHAVE_IDNA_H=1 \ | |
-DLIBIDN_STATIC \ | |
-I$(IDN_ROOT)/lib | |
DLL_LIBS += $(IDN_ROOT)/lib/libidn.a | |
endif | |
ifeq ($(USE_LIBIDN2),1) | |
ifeq ($(USE_LIBIDN),1) | |
$(error Cannot have both 'USE_LIBIDN=1' and 'USE_LIBIDN2=1') | |
endif | |
CFLAGS += -DWITH_LIBIDN2=1 \ | |
-DHAVE_IDN2_H=1 \ | |
-DHAVE_UNICASE_H=1 \ | |
-DWITH_LIBUNISTRING=1 \ | |
-DIDN2_STATIC \ | |
-I$(IDN2_ROOT)/lib | |
DLL_LIBS += $(IDN2_ROOT)/idn2.lib | |
endif | |
# | |
# Libraries for wget2.exe | |
# | |
ifeq ($(USE_LIBWGET2_STATIC),0) | |
WGET2_LIBS = $(WGET2_IMP_LIB) | |
else | |
WGET2_LIBS = $(WGET2_STAT_LIB) | |
endif | |
# | |
# We always need GnuLib. Either as an import-lib or a static library. | |
# | |
ifeq ($(USE_GNULIB_STATIC),1) | |
WGET2_LIBS += $(GNULIB_STAT_LIB) | |
DLL_LIBS += $(GNULIB_STAT_LIB) | |
else | |
WGET2_LIBS += $(GNULIB_IMP_LIB) | |
DLL_LIBS += $(GNULIB_IMP_LIB) | |
endif | |
ifeq ($(USE_WSOCK_TRACE),1) | |
WS2_LIB = wsock_trace.lib | |
else | |
WS2_LIB = ws2_32.lib | |
endif | |
EX_LIBS += $(WS2_LIB) | |
DLL_LIBS += $(WS2_LIB) kernel32.lib user32.lib | |
# | |
# Now we have the list of .c-files for '$(WGET2_STAT_LIB)'. | |
# | |
LIB_OBJ = $(call c_to_obj, $(LIB_SRC)) | |
LIB_I = $(notdir $(LIB_OBJ:.obj=.i)) | |
$(LIB_OBJ) $(LIB_I): EXTRA_CFLAGS = -DBUILDING_LIBWGET # -DLIBWGET_STATIC | |
CFLAGS += -I./include/wget \ | |
-I$(MICRO_HTTPD_ROOT)/src/include \ | |
-DWITH_MICROHTTPD | |
GENERATED = $(CC).args \ | |
config.h \ | |
include/wget/wgetver.h \ | |
libwget/css_tokenizer.c | |
EXAMPLE_SRC = $(addprefix examples/, \ | |
batch_loader.c \ | |
check_url_types.c \ | |
getstream.c \ | |
http_get.c \ | |
http_get2.c \ | |
http_multi_get.c \ | |
print_css_urls.c \ | |
print_css_urls2.c \ | |
print_css_urls3.c \ | |
print_html_urls.c \ | |
relative_to_absolute_url.c \ | |
websequencediagram.c \ | |
websequencediagram_high.c) | |
EXAMPLE_OBJ = $(call c_to_obj, $(EXAMPLE_SRC)) | |
EXAMPLES = $(EXAMPLE_SRC:.c=.exe) | |
.SECONDARY: $(EXAMPLE_OBJ) | |
# | |
# If 'wget2.exe' shall use the static 'libwget2.lib', 'wget2.exe' | |
# MUST off-cource link to the libraries 'libwget2.dll' would need. | |
# | |
ifeq ($(USE_LIBWGET2_STATIC),1) | |
EX_LIBS += $(DLL_LIBS) | |
endif | |
all: $(OBJ_DIR) $(GENERATED) $(TARGETS) epilogue | |
epilogue: | |
$(call green_msg, Welcome to wget2.) | |
$(call green_msg, Do a $(BRIGHT_WHITE)make CC=$(CC) -f $(THIS_FILE) install$(BRIGHT_GREEN) at own risk. Or) | |
$(call green_msg, Do a $(BRIGHT_WHITE)make CC=$(CC) -f $(THIS_FILE) tests runtests$(BRIGHT_GREEN) to test.) | |
tests: all $(TEST_OBJ) $(TEST_PROGRAMS) $(UNIT_TEST_PROGRAMS) plugins tests_epilogue | |
tests_epilogue: | |
$(call green_msg, Welcome to $$(TEST_PROGRAMS)$(comma) and plugins.) | |
plugins: all $(PLUGINS) | |
# | |
# Unfinished | |
# | |
examples: all $(EXAMPLES) | |
$(PLUGINS): | .libs | |
.libs/libplugindb.dll: $(OBJ_DIR)/plugin-db.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^) | |
.libs/libpluginname.dll: $(OBJ_DIR)/plugin-name.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^ $(GNULIB_STAT_LIB)) | |
.libs/libpluginexit.dll: $(OBJ_DIR)/plugin-exit.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^ $(GNULIB_STAT_LIB)) | |
.libs/libpluginoption.dll: $(OBJ_DIR)/plugin-option.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^ $(GNULIB_STAT_LIB)) | |
.libs/libpluginapi.dll: $(OBJ_DIR)/plugin-api.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^ $(GNULIB_STAT_LIB)) | |
.libs/libalpha.dll: $(OBJ_DIR)/plugin-alpha.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^) | |
.libs/libbeta.dll: $(OBJ_DIR)/plugin-beta.obj $(WGET2_IMP_LIB) | |
$(call link_plugin, $@, $^) | |
$(OBJ_DIR)/plugin-alpha.obj: unit-tests/test-dl-dummy.c | |
$(call C_compile, $< -DPARAM=alpha, $@) | |
$(OBJ_DIR)/plugin-api.obj: tests/test-plugin-dummy.c | |
$(call C_compile, $< -DTEST_SELECT_API, $@) | |
$(OBJ_DIR)/plugin-beta.obj: unit-tests/test-dl-dummy.c | |
$(call C_compile, $< -DPARAM=beta, $@) | |
$(OBJ_DIR)/plugin-db.obj: tests/test-plugin-dummy.c | |
$(call C_compile, $< -DTEST_SELECT_DATABASE, $@) | |
$(OBJ_DIR)/plugin-exit.obj: tests/test-plugin-dummy.c | |
$(call C_compile, $< -DTEST_SELECT_EXITSTATUS, $@) | |
$(OBJ_DIR)/plugin-name.obj: tests/test-plugin-dummy.c | |
$(call C_compile, $< -DTEST_SELECT_NAME, $@) | |
$(OBJ_DIR)/plugin-option.obj: tests/test-plugin-dummy.c | |
$(call C_compile, $< -DTEST_SELECT_OPTIONS, $@) | |
runtests run_tests: $(TEST_PROGRAMS) | |
export WGET2_TRACE= ; \ | |
cd ./tests ; \ | |
for p in $(notdir $(TEST_PROGRAMS)) ; do \ | |
echo "$$p --------------------" ; \ | |
$$p ; \ | |
if [ $$? -ne 0 ]; then \ | |
echo "$$p failed" ; \ | |
exit 1 ; \ | |
fi ; \ | |
done | |
runfuzz run_fuzz: check_fuzz_$(USE_FUZZ) $(FUZZ_PROGRAMS) | |
export WGET2_TRACE= ; \ | |
for p in $(FUZZ_PROGRAMS) ; do \ | |
echo -e "\n$$p --------------------------" ; \ | |
$$p ; \ | |
if [ $$? -ne 0 ]; then \ | |
echo "$$p failed" ; \ | |
exit 1 ; \ | |
fi ; \ | |
done | |
check_fuzz_0: | |
$(error 'USE_FUZZ=1' required for the 'runfuzz' target.) | |
check_fuzz_1: | |
install: $(TARGETS) | |
cp --update $(WGET2_STAT_LIB) $(WGET2_IMP_LIB) $(INSTALL_ROOT)/lib | |
cp --update libwget2.dll wget2.exe $(PLUGINS) $(INSTALL_ROOT)/bin | |
cp --update libwget2.pdb wget2.pdb $(PLUGINS:.dll=.pdb) $(INSTALL_ROOT)/bin | |
ifeq ($(USE_DOXYGEN),1) | |
cp --update wget2.chm $(INSTALL_ROOT)/bin | |
cp --update $(MAN3_PAGES) $(MAN_PAGE_ROOT)/man/man3 | |
endif | |
$(OBJ_DIR) .libs: | |
- mkdir $@ | |
# | |
# Generate a .def-file for exports in libwget2.dll | |
# | |
GET_CODE_SYMS = grep -e ' T _wget_' | |
GET_DATA_SYMS = grep -e ' [RD] _wget_' | |
FILTER_SYMS = sed 's/^.* _\([_a-zA-Z0-9]*\)/ \1 $(1) /' | |
EXTRACT_CODE = nm --defined-only $(1) | $(GET_CODE_SYMS) | $(call FILTER_SYMS) | |
EXTRACT_DATA = nm --defined-only $(1) | $(GET_DATA_SYMS) | $(call FILTER_SYMS, DATA) | |
$(OBJ_DIR)/libwget2.def: $(WGET2_STAT_LIB) $(THIS_FILE) | |
$(call Generate, $@,;) | |
@echo -en ";\nLIBRARY libwget2.dll\nEXPORTS\n" >> $@ | |
$(call EXTRACT_CODE, $<) | sort >> $@ | |
$(call EXTRACT_DATA, $<) | sort >> $@ | |
$(WGET2_IMP_LIB): libwget2.dll | |
libwget2.dll: $(OBJ_DIR)/libwget2.def $(LIB_OBJ) $(OBJ_DIR)/libwget2.res | |
$(call link_DLL, $@, $(LIB_OBJ) $(OBJ_DIR)/libwget2.res $(DLL_LIBS), $(WGET2_IMP_LIB), $(OBJ_DIR)/libwget2.def) | |
$(OBJ_DIR)/libwget2.res: $(OBJ_DIR)/libwget2.rc | |
$(call make_res, $<, $@) | |
$(OBJ_DIR)/wget2.res: $(OBJ_DIR)/wget2.rc | |
$(call make_res, $<, $@) | |
libwget/test_linking.exe: $(OBJ_DIR)/test_linking.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
libwget/test_linking_robots.exe: $(OBJ_DIR)/test_linking_robots.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
# | |
# This option does not work so well. Use the '$(MICRO_HTTPD_ROOT)/libmicrohttpd.dll' instead. | |
# make sure you did a 'make install' in '$(MICRO_HTTPD_ROOT)' so this .DLL is on 'PATH'. | |
# | |
ifeq ($(USE_MHD_STATIC),1) | |
MICRO_HTTPD_LIB = $(MICRO_HTTPD_ROOT)/libmicrohttpd.lib \ | |
$(GNUTLS_ROOT)/lib/gnutls-gv_imp.lib | |
else | |
CFLAGS += -DMHD_W32DLL | |
MICRO_HTTPD_LIB = $(MICRO_HTTPD_ROOT)/libmicrohttpd_imp.lib | |
endif | |
# | |
# Objects from ./src/*.c needed in some test*.exe programs. | |
# | |
TEST_WGET2_SRC_OBJ = $(addprefix $(OBJ_DIR)/, \ | |
wget_dl.obj \ | |
wget_options.obj \ | |
wget_log.obj \ | |
wget_plugin.obj \ | |
wget_testing.obj \ | |
wget_stats_server.obj \ | |
wget_stats_site.obj \ | |
wget_utils.obj) | |
TEST_WGET2_SRC_I = $(notdir $(TEST_WGET2_SRC_OBJ:obj=.i)) | |
# | |
# If these objects uses functions from the static 'libwget2.lib', | |
# we MUST add '-DLIBWGET_STATIC' to the CFLAGS when compiling these. | |
# | |
ifeq ($(USE_LIBWGET2_STATIC),1) | |
$(WGET_OBJ): EXTRA_CFLAGS += -DLIBWGET_STATIC | |
endif | |
# $(OBJ_DIR)/libtest.obj: config.h | |
# | |
# These are always compiled with '-DLIBWGET_STATIC' | |
# | |
$(TEST_OBJ) \ | |
$(TEST_I) \ | |
$(UNIT_TEST_OBJ) \ | |
$(UNIT_TEST_I) \ | |
$(FUZZ_OBJ) \ | |
$(FUZZ_I) \ | |
$(TEST_WGET2_SRC_OBJ) \ | |
$(TEST_WGET2_SRC_I) \ | |
$(OBJ_DIR)/libtest.obj \ | |
$(OBJ_DIR)/test_linking.obj \ | |
$(OBJ_DIR)/test_linking_robots.obj: EXTRA_CFLAGS += -DLIBWGET_STATIC | |
# | |
# The order of libraries is important for 'lld-link'. Yeah! | |
# | |
wget2.exe: $(WGET_OBJ) $(WGET2_LIBS) $(OBJ_DIR)/wget2.res | |
$(call link_EXE, $@, $(WGET_OBJ) $(OBJ_DIR)/wget2.res $(EX_LIBS) $(WGET2_LIBS)) | |
cp --update $@ src/wget2_noinstall.exe | |
cp --update wget2.pdb src/wget2_noinstall.pdb | |
unit-tests/test.exe: $(OBJ_DIR)/test.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-tests/test-cookies-http_state.exe: $(OBJ_DIR)/test-cookies-http_state.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-tests/test-cond.exe: $(OBJ_DIR)/test-cond.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-tests/test-decompress.exe: $(OBJ_DIR)/test-decompress.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-tests/test-dl.exe: $(OBJ_DIR)/test-dl.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-tests/test-parse-html.exe: $(OBJ_DIR)/test-parse-html.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
tests/test%.exe: $(OBJ_DIR)/test%.obj $(OBJ_DIR)/libtest.obj $(TEST_WGET2_SRC_OBJ) $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS) $(WS2_LIB) $(MICRO_HTTPD_LIB)) | |
tests/buffer_printf_perf.exe: $(OBJ_DIR)/buffer_printf_perf.obj $(OBJ_DIR)/libtest.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
unit-test/stringmap_perf.exe: $(OBJ_DIR)/stringmap_perf.obj $(OBJ_DIR)/libtest.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
$(OBJ_DIR)/ansi-test.obj: libwget/mswindows.c $(OBJ_DIR)/ansi-test-1.h # $(OBJ_DIR)/ansi-test-2.h | |
$(call C_compile, $< -I$(OBJ_DIR) -DANSI_TEST -DLIBWGET_STATIC, $@) | |
unit-tests/ansi-test.exe: $(OBJ_DIR)/ansi-test.obj $(OBJ_DIR)/console.obj | |
$(call link_EXE, $@, $^ $(GNULIB_STAT_LIB)) | |
$(OBJ_DIR)/ansi-test-1.h: $(MDEPEND) | |
$(call URL_to_H_file, $@, $(ANSI_TEST_1_URL), ansi_test_1) | |
# | |
# Rules for 'fuzz/*.exe' programs: | |
# | |
fuzz/%.exe: $(OBJ_DIR)/%.obj $(OBJ_DIR)/main.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
# | |
# Rules for 'examples/*.exe' programs: | |
# | |
examples/%.exe: $(OBJ_DIR)/%.obj $(WGET2_LIBS) | |
$(call link_EXE, $@, $^ $(EX_LIBS)) | |
# | |
# Since some .c-files in 'src' overlap with some 'libwget/*.c' files, | |
# this rule is used for objects of 'wget2.exe' and 'tests/*.exe'. | |
# | |
$(OBJ_DIR)/wget_%.obj: src/%.c | |
$(call C_compile, $<, $@) | |
# | |
# For 'cl' only (one day ...): | |
# | |
ifeq ($(USE_MP_COMPILE),1) | |
$(LIB_OBJ): $(LIB_SRC) | |
$(call yellow_msg, Compiling all LIB_SRC in one go...) | |
$(call C_compile, $(LIB_SRC), $(OBJ_DIR)\\) | |
$(TEST_OBJ): $(TEST_SRC) | |
$(call yellow_msg, Compiling all TEST_SRC in one go...) | |
$(call C_compile, $(TEST_SRC), $(OBJ_DIR)\\) | |
endif | |
$(OBJ_DIR)/%.obj: %.c | |
$(call C_compile, $<, $@) | |
$(WGET2_STAT_LIB): $(LIB_OBJ) | |
$(call make_stat_lib, $@, $^) | |
wget2.chm: docs/libwget2.doxy doxy_epilogue | |
cd docs ; doxygen libwget2.doxy 2> wget2-doxygen.log | |
cd docs/html ; hhc index.hhp ; mv --force wget2.chm ../.. | |
doxy_epilogue: | |
$(call green_msg, Doxygen warnings are in 'docs/wget2-doxygen.log'.) | |
@echo | |
# | |
# Simply replace libwget2.doxy's 'INPUT' with this: | |
# | |
DOXY_INPUT = $(THIS_DIR)/README.md \ | |
$(THIS_DIR)/docs \ | |
$(THIS_DIR)/include/wget/wget.h \ | |
$(THIS_DIR)/include/wget/wgetver.h \ | |
$(THIS_DIR)/libwget \ | |
$(THIS_DIR)/libwget/msvc \ | |
$(THIS_DIR)/tests/libtest.c | |
DOXY_REPLACE = -e 's|@PACKAGE_NAME@|Wget2|g' \ | |
-e 's|@PACKAGE_VERSION@|$(VERSION)|g' \ | |
-e 's|^INPUT.*=*|INPUT=$(DOXY_INPUT)|g' \ | |
-e 's|@srcdir@|$(THIS_DIR)/docs|g' \ | |
-e 's|@top_srcdir@|$(THIS_DIR)|g' \ | |
-e 's|^OUTPUT_DIRECTORY.*|OUTPUT_DIRECTORY=$(THIS_DIR)/docs|g' \ | |
-e 's|^CLANG_.*||g' \ | |
-e 's|^FILE_PATTERNS.*|FILE_PATTERNS=*.c *.h|g' \ | |
-e 's|^RECURSIVE.*|RECURSIVE=NO|g' \ | |
-e 's|^SEARCHENGINE.*|SEARCHENGINE=NO|g' \ | |
-e 's|^GENERATE_MAN.*|GENERATE_MAN=YES|g' \ | |
-e 's|^GENERATE_TREEVIEW.*|GENERATE_TREEVIEW=NO|g' \ | |
-e 's|^GENERATE_HTMLHELP.*|GENERATE_HTMLHELP=YES|g' \ | |
-e 's|^BINARY_TOC.*|BINARY_TOC=YES|g' \ | |
-e 's|^FILE_VERSION_FILTER.*||g' \ | |
-e 's|^EXCLUDE_PATTERNS.*=*|EXCLUDE_PATTERNS=*/libwget/*.h $(GNULIB_ROOT)/lib/*|g' \ | |
-e 's|^CHM_FILE.*|CHM_FILE=wget2.chm|g' | |
# DOXY_REPLACE += -e 's|^WARN_LOGFILE.*|WARN_LOGFILE=$(THIS_DIR)/docs/wget2-doxygen.log|g' | |
docs/libwget2.doxy: docs/libwget.doxy.in $(THIS_FILE) | |
$(call Generate, $@,#) | |
sed $(DOXY_REPLACE) < $< >> $@ | |
docs/wget2.pdf: docs/wget2.md | |
$(call white_msg, Running 'pandoc' to create $@...) | |
pandoc --standalone --from markdown --to pdf --out $@ $< | |
@echo | |
MAP_FILES = $(TARGETS:.dll=.map) \ | |
$(TARGETS:.exe=.map) \ | |
$(TEST_PROGRAMS:.exe=.map) \ | |
$(UNIT_TEST_PROGRAMS:.exe=.map) \ | |
$(EXAMPLES:.exe=.map) | |
PDB_FILES = $(TARGETS:.dll=.pdb) \ | |
$(TARGETS:.exe=.pdb) \ | |
$(TEST_PROGRAMS:.exe=.pdb) \ | |
$(UNIT_TEST_PROGRAMS:.exe=.pdb) \ | |
$(EXAMPLES:.exe=.pdb) \ | |
src/wget2_noinstall.pdb vc1*.pdb | |
# | |
# '$(sort ...)' creates a unique list. | |
# | |
link_JUNK = $(sort $(MAP_FILES) $(PDB_FILES)) | |
clean: | |
rm -f $(OBJ_DIR)/* $(GENERATED) $(link_JUNK) \ | |
docs/libwget2.doxy docs/wget2-doxygen.log cpp_filter.py \ | |
cl.args clang-cl.args link.args depend.args .depend.tmp \ | |
src/wget2_noinstall.exe wget2.lib wget2.exp \ | |
libwget2_imp.exp link.tmp | |
- rmdir $(OBJ_DIR) | |
realclean vclean: clean fuzz_vclean docs_vclean | |
rm -f $(TARGETS) $(TEST_PROGRAMS) $(UNIT_TEST_PROGRAMS) .depend.Windows | |
- rmdir .libs | |
fuzz_vclean: | |
- rm -f $(FUZZ_PROGRAMS) $(FUZZ_PROGRAMS:.exe=.map) $(FUZZ_PROGRAMS:.exe=.pdb) | |
docs_vclean: | |
- rm -f docs/man/man3/* docs/html/search/* docs/html/* docs/latex/* wget2.chm | |
- rmdir docs/man/man3 docs/man docs/html/search docs/html docs/latex | |
# | |
# This stuff is supposed to generate './include/wget/wgetver.h' | |
# | |
define WGETVER_H | |
/**\\file wgetver.h | |
* \ingroup libwget-utils | |
*/ | |
#define LIBWGET_VERSION "$(VERSION)" | |
#define LIBWGET_VERSION_MAJOR $(VER_MAJOR) | |
#define LIBWGET_VERSION_MINOR $(VER_MINOR) | |
#define LIBWGET_VERSION_PATCH $(VER_PATCH) | |
#define LIBWGET_VERSION_NUMBER $(VERSION) | |
endef | |
# | |
# This stuff is supposed to generate ./config.h | |
# | |
define CONFIG_H | |
/* This header is from GnuLib and MUST come | |
* before any other headers pulled in by GnuLib. | |
*/ | |
#include "$(GNULIB_ROOT)/lib/config.h" | |
#include <string.h> | |
#include <strings.h> /* For strcasecmp() */ | |
#include <errno.h> | |
#include <memory.h> | |
#include <malloc.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> /* This will include <winsock2.h> ! */ | |
#include <time.h> | |
#include <assert.h> | |
#include <fcntl.h> | |
#include <process.h> /* for 'getpid()' in tests/libtest.c */ | |
#include <sys/stat.h> | |
#include <dirname.h> | |
#define __STDC_NO_THREADS__ /* We have no '<threads.h>' header. */ | |
#ifndef __THROWNL | |
#define __THROWNL | |
#endif | |
#if defined(_MSC_VER) && (!defined(USE_WINDOWS_THREADS) || USE_WINDOWS_THREADS == 0) | |
#error "_MSC_VER / __clang__ MUST have 'USE_WINDOWS_THREADS' defined." | |
#endif | |
/* For some strange reason, the preprocessor doesn't see | |
* rpl_stat() -- included from GnuLib's <sys/stat.h>. | |
* and base_name() -- included from GnuLib's <dirname.h>. | |
* | |
* So just add the protypes here: | |
*/ | |
_GL_EXTERN_C int rpl_stat (const char *name, struct rpl_stat *buf) _GL_ARG_NONNULL ((1, 2)); | |
_GL_EXTERN_C char *base_name (char const *file); | |
#if defined(_VCRUNTIME_H) | |
#include <sys/utime.h> | |
#undef HAVE_STRUCT_TIMESPEC | |
#define HAVE_STRUCT_TIMESPEC 1 | |
#undef HAVE_STRUCT_UTIMBUF | |
#define HAVE_STRUCT_UTIMBUF 1 | |
#endif | |
#if defined(BUILDING_LIBWGET) && !defined(LIBWGET_STATIC) | |
#define LIBWGET_STATIC | |
#endif | |
#undef _GL_INLINE | |
#if defined(__GNUC__) | |
#define _GL_INLINE inline | |
#else | |
#define _GL_INLINE __inline | |
#endif | |
#if !defined(__attribute__) && !defined(__clang__) | |
#define __attribute__(x) | |
#endif | |
#if defined(WITH_GNUTLS) | |
/* | |
* Needed in libwget/ssl_gnutls.c: | |
*/ | |
#define WITH_GNUTLS_OCSP 1 | |
#define HAVE_GNUTLS_OCSP_H 1 | |
#define HAVE_GNUTLS_SRP_SERVER_GET_USERNAME 1 | |
// #define HAVE_GNUTLS_TRANSPORT_GET_INT 1 | |
/* Needed in libwget/hashfile.c: | |
*/ | |
#define HAVE_GNUTLS_CRYPTO_H 1 | |
#endif | |
/* Needed in src/options.c: | |
*/ | |
#define SYSCONFDIR "." | |
#define SIZEOF_OFF_T 8 | |
#if defined(TEST_RUN) | |
/* | |
* Needed in fuzz/main.c. | |
* All $$(FUZZ_PROGAMS) *must* be run from './fuzz'. | |
*/ | |
#define SRCDIR "." | |
#elif defined(UNIT_TEST_RUN) | |
/* Needed in unit-tests/ | |
*/ | |
#define SRCDIR "$(THIS_DIR)/unit-tests" | |
#else | |
/* Needed in tests/libtest.c: | |
* (due to GNUmake, the back-slashes must be doubled up). | |
*/ | |
#define SRCDIR "$(THIS_DIR)/tests" | |
#define BUILDDIR "$(subst /,\\\\,$(THIS_DIR))\\\\tests" | |
#endif | |
#define WGET_DATADIR "$(THIS_DIR)/data" | |
//#define WGETVER_FILE "./include/wget/wgetver.h" | |
#define EXEEXT ".exe" | |
/* Stuff for plugins: | |
*/ | |
#define WGET_PLUGIN_DIR ".libs" | |
#define PLUGIN_SUPPORT 1 | |
#define PLUGIN_SUPPORT_WINDOWS 1 | |
#define HAVE_ERRNO_H 1 | |
#define HAVE_FCNTL_H 1 | |
#define HAVE_GETHOSTBYNAME 1 | |
#define HAVE_INTTYPES_H 1 | |
#define HAVE_LIMITS_H 1 | |
#define HAVE_MALLOC 1 | |
#define HAVE_MEMORY_H 1 | |
#define HAVE_MEMSET 1 | |
#define HAVE_REALLOC 1 | |
#define HAVE_SOCKET 1 | |
#define HAVE_STDDEF_H 1 | |
#define HAVE_STDINT_H 1 | |
#define HAVE_STDLIB_H 1 | |
#define HAVE_STRING_H 1 | |
#define HAVE_SYS_STAT_H 1 | |
#define HAVE_SYS_TYPES_H 1 | |
#define HAVE_MHD_FREE 1 | |
#undef PACKAGE | |
#define PACKAGE "wget2" | |
#undef PACKAGE_NAME | |
#define PACKAGE_NAME PACKAGE | |
#define PACKAGE_STRING PACKAGE ## $(VERSION) | |
#define PACKAGE_VERSION "$(VERSION)" | |
#undef VERSION | |
#define VERSION PACKAGE_VERSION | |
#define SIZEOF_LONG 4 | |
#define SIZEOF_LONG_LONG 8 | |
#define STDC_HEADERS 1 | |
#if !defined(HAVE_STRUCT_UTIMBUF) | |
#define HAVE_STRUCT_UTIMBUF 1 | |
struct utimbuf { | |
long actime; | |
long modtime; | |
}; | |
#endif | |
/* Use Gnulibs' 'vasnprintf()' in 'wget_ANSI_vprintf()'? | |
*/ | |
#ifndef USE_VASNPRINTF | |
#define USE_VASNPRINTF 1 | |
#endif | |
/* Use ANSI-sequences in 'wget_trace()' in libwget/mswindows.c. | |
*/ | |
#ifndef USE_ANSI_TRACE | |
#define USE_ANSI_TRACE 1 | |
#endif | |
/* Shoe-horn things for Windows. Like a '#undef HAVE_POLL' | |
*/ | |
#include "mswindows.h" | |
#if !defined(USE_GNULIB_POLL) | |
/* | |
* For 'libwget/io.c': | |
* Force use of Winsock2's 'select()' and not 'rpl_select()' from GnuLib. | |
* Since GnuLib's 'rpl_poll()' works badly for Winsock2. | |
*/ | |
#undef HAVE_POLL | |
#endif | |
endef | |
define LIBWGET2_RC | |
#define RC_FILETYPE VFT_DLL | |
#define RC_INTERNAL_NAME "libwget2.dll" | |
endef | |
define WGET2_RC | |
#define RC_FILETYPE VFT_APP | |
#define RC_INTERNAL_NAME "wget2.exe" | |
endef | |
# | |
# Both libwget2.dll and wget2.exe have the same version and bitness. | |
# | |
define COMMON_RC_STUFF | |
#include <winver.h> | |
#if defined(__clang__) | |
#define RC_HOST "clang-cl" | |
#elif defined(_MSC_VER) | |
#define RC_HOST "MSVC" | |
#else | |
#error Who are you? | |
#endif | |
#define RC_VERSION $(VER_MAJOR),$(VER_MINOR),$(VER_PATCH),0 | |
LANGUAGE 0x09,0x01 | |
VS_VERSION_INFO VERSIONINFO | |
FILEVERSION RC_VERSION | |
PRODUCTVERSION RC_VERSION | |
FILEFLAGSMASK 0x3fL | |
FILEOS VOS__WINDOWS32 | |
FILETYPE RC_FILETYPE | |
FILESUBTYPE 0x0L | |
FILEFLAGS 0 | |
BEGIN | |
BLOCK "StringFileInfo" | |
BEGIN | |
BLOCK "040904b0" | |
BEGIN | |
VALUE "CompanyName", "https://gitlab.com/gnuwget/wget2" | |
VALUE "FileDescription", RC_INTERNAL_NAME " (" RC_HOST ", $(BITS)-bit)." | |
VALUE "FileVersion", "$(VERSION)." | |
VALUE "InternalName", RC_INTERNAL_NAME "." | |
VALUE "LegalCopyright", "GNU GENERAL PUBLIC LICENSE v2 or commercial licence." | |
VALUE "Comments", "Built on $(DATE) by <[email protected]>." | |
END | |
END | |
BLOCK "VarFileInfo" | |
BEGIN | |
VALUE "Translation", 0x409, 1200 | |
END | |
END | |
endef | |
export WGETVER_H CONFIG_H LIBWGET2_RC WGET2_RC COMMON_RC_STUFF | |
# | |
# Generate a hexified .h-file from an URL-resource: | |
# arg1: $(1): the .h-file | |
# arg1: $(2): the URL | |
# arg3: $(3): the name of the 'const char array[]' to generate. | |
# | |
# Needs 'curl' and 'xxd' on PATH. | |
# | |
define URL_to_H_file | |
$(call Generate, $(1),//) | |
@echo -ne "// Generated by:\n" \ | |
"// curl -s $(strip $(2)) | xxd -c 15 -include >> $(1)\n" \ | |
"static const char $(strip $(3))[] = {\n" >> $(1) | |
curl -s $(2) | xxd -c 15 -include >> $(1) | |
@echo "};" >> $(1) | |
endef | |
include/wget/wgetver.h: $(THIS_FILE) | |
$(call Generate, $@,//) | |
@echo -e "#ifndef _wget2_VER_H\n#define _wget2_VER_H\n" \ | |
"$$WGETVER_H\n" \ | |
"#endif /* _wget2_VER_H */" >> $@ | |
$(CC).args: $(THIS_FILE) | |
$(call white_msg, All CFLAGS are in $@) | |
$(call create_resp_file, $@, -c $(CFLAGS)) | |
config.h: $(THIS_FILE) | |
$(call Generate, $@,//) | |
@echo -ne "#ifndef _wget2_CONFIG_H\n#define _wget2_CONFIG_H\n" \ | |
"$$CONFIG_H\n" \ | |
"#endif /* _wget2_CONFIG_H */\n" >> $@ | |
libwget/css_tokenizer.c: | config.h libwget/css_tokenizer.lex | |
$(call Generate, $@,//) | |
flex --8bit --nowarn --stdout libwget/css_tokenizer.lex | sed -e 's/fprintf/wget_fprintf/g' >> $@ | |
@echo | |
$(OBJ_DIR)/libwget2.rc: $(THIS_FILE) | |
$(call Generate, $@,//) | |
@echo -e "$$LIBWGET2_RC\n" \ | |
"$$COMMON_RC_STUFF" >> $@ | |
$(OBJ_DIR)/wget2.rc: $(THIS_FILE) | |
$(call Generate, $@,//) | |
@echo -e "$$WGET2_RC\n" \ | |
"$$COMMON_RC_STUFF" >> $@ | |
@echo | |
ver_test: | |
$(call green_msg, VER_MAJOR: '$(VER_MAJOR)'.) | |
$(call green_msg, VER_MINOR: '$(VER_MINOR)'.) | |
$(call green_msg, VER_PATCH: '$(VER_PATCH)'.) | |
# | |
# GNU-make macros: | |
# | |
# .c compile macro: | |
# arg1: $(1): the .c-file to compile (+ extra CFLAGS). | |
# arg2: $(2): the object file. | |
# | |
C_compile = $(CC) @$(CC).args $(1) -Fo./$(strip $(2)) $(EXTRA_CFLAGS) | |
# | |
# Check for unused libraries in $(1). | |
# | |
check_unused = $(PYTHON) check-for-unused-libraries.py $(1) | |
# | |
# .exe link macro: | |
# arg1: $(1): the .exe to create. | |
# arg2: $(2): the rest; objects + libs. | |
# | |
define link_EXE | |
$(call green_msg, Linking $(strip $(1))...) | |
$(call create_resp_file, link.args, $(LDFLAGS) $(2)) | |
$(LINK) @link.args -out:$(strip $(1)) > link.tmp | |
@cat link.tmp >> $(1:.exe=.map) | |
@rm -f $(1:.exe=.lib) $(1:.exe=.exp) | |
@$(call check_unused, link.tmp) | |
endef | |
# | |
# .dll link macro: | |
# arg1: $(1): the .dll to create. | |
# arg2: $(2): the .obj files and libraries to link with. | |
# arg3: $(3): the import library for the .dll. | |
# arg4: $(4): the .def file with the EXPORTs of functions and data. | |
# | |
define link_DLL | |
$(call green_msg, Linking $(strip $(1))...) | |
$(call create_resp_file, link.args, -dll $(LDFLAGS) -implib:$(strip $(3)) -def:$(strip $(4)) $(2)) | |
$(LINK) @link.args -out:$(strip $(1)) > link.tmp | |
@cat link.tmp >> $(1:.dll=.map) | |
@rm -f $(3:.lib=.exp) | |
@$(call check_unused, link.tmp) | |
endef | |
# | |
# plugin.dll link macro: | |
# arg1: $(1): the .dll to create. | |
# arg2: $(2): the .obj files and libraries to link with. | |
# | |
define link_plugin | |
$(call green_msg, Linking $(strip $(1))...) | |
$(call create_resp_file, link.args, -dll $(LDFLAGS) $(filter-out .libs, $(2))) | |
$(LINK) @link.args -out:$(strip $(1)) > link.tmp | |
@cat link.tmp >> $(1:.dll=.map) | |
@rm -f $(1:.dll=.exp) $(1:.dll=.lib) | |
@$(call check_unused, link.tmp) | |
endef | |
# | |
# static lib creation macro: | |
# arg1: $(1): the .lib to create. | |
# arg2: $(2): the objects. | |
# | |
define make_stat_lib | |
lib -nologo -out:$(strip $(1)) $(2) | |
@echo | |
endef | |
# | |
# .res file macro: | |
# arg1: $(1): the .rc file. | |
# arg2: $(2): the .res file. | |
# | |
define make_res | |
rc $(RCFLAGS) -fo $(2) $(1) | |
@echo | |
endef | |
define Warning | |
$(1) | |
$(1) This file was generated by $(THIS_FILE) at $(DATE). | |
$(1) DO NOT EDIT. YOUR CHANGED WILL BE LOST. | |
$(1) | |
endef | |
define Generate | |
$(call green_msg, Generating $(strip $(1))) | |
$(file > $(1),$(call Warning,$(2))) | |
endef | |
# | |
# Make a response file using this make-macro instead of relying on | |
# various 'echo' programs. Depending on Cygwin or Msys version, the cmdline | |
# limit can be very low. Besides this macro avoids spawning 'sh'. | |
# | |
# arg1, $(1): The name of the response file | |
# arg2, $(2): The content for the response file. | |
# | |
define create_resp_file | |
$(file > $(1)) | |
$(foreach l, $(2), $(file >> $(1),$(strip $(l)))) | |
endef | |
DEP_REPLACE = -e 's@\(.*\)\.o: @\n$$(OBJ_DIR)\/\1.$obj: @' \ | |
-e 's@$(OBJ_DIR)@$$(OBJ_DIR)@' \ | |
-e 's@$(BROTLI_ROOT)@$$(BROTLI_ROOT)@g' \ | |
-e 's@$(BZIP2_ROOT)@$$(BZIP2_ROOT)@g' \ | |
-e 's@$(IDN_ROOT)@$$(IDN_ROOT)@g' \ | |
-e 's@$(IDN2_ROOT)@$$(IDN2_ROOT)@g' \ | |
-e 's@$(GNULIB_ROOT)@$$(GNULIB_ROOT)@g' \ | |
-e 's@$(GNUTLS_ROOT)@$$(GNUTLS_ROOT)@g' \ | |
-e 's@$(MICRO_HTTPD_ROOT)@$$(MICRO_HTTPD_ROOT)@g' \ | |
-e 's@$(NGHTTP2_ROOT)@$$(NGHTTP2_ROOT)@g' \ | |
-e 's@$(OPENSSL_ROOT)@$$(OPENSSL_ROOT)@g' \ | |
-e 's@$(LIBPSL_ROOT)@$$(LIBPSL_ROOT)@g' \ | |
-e 's@$(LZMA_ROOT)@$$(LZMA_ROOT)@g' \ | |
-e 's@$(ZLIB_ROOT)@$$(ZLIB_ROOT)@g' \ | |
-e 's@$(WOLFSSL_ROOT)@$$(WOLFSSL_ROOT)@g' | |
DEP_CFLAGS = -MM $(filter -D% -I%, $(CFLAGS)) -D_GL_INLINE_HEADER_BEGIN -DGCC_DEPEND_RUN | |
ALL_SRC = $(LIB_SRC) $(TEST_SRC) $(UNIT_TEST_SRC) $(EXAMPLE_SRC) \ | |
libwget/test_linking.c libwget/test_linking_robots.c tests/libtest.c | |
depend: $(OBJ_DIR) $(GENERATED) | |
$(call Generate, .depend.Windows,#) | |
$(call create_resp_file, depend.args, $(DEP_CFLAGS) $(ALL_SRC)) | |
gcc @depend.args | sed $(DEP_REPLACE) >> .depend.Windows | |
# | |
# Command to generate a nicer C preprocessed output | |
# with the help of 'cpp_filter.py' and optionally 'Astyle' or 'clang-format'. | |
# | |
ifeq ($(USE_ASTYLE_FORMAT),1) | |
C_FORMATER = | astyle | |
else ifeq ($(USE_CLANG_FORMAT),1) | |
C_FORMATER = | clang-format -style=Mozilla -assume-filename=c | |
endif | |
define C_preprocess | |
$(CC) -E @$(CC).args $(1) | $(PYTHON) cpp_filter.py $(C_FORMATER) > $(2) | |
@echo | |
endef | |
preprocess_DEPS = FORCE cpp_filter.py $(GENERATED) | |
%.i: %.c $(preprocess_DEPS) | |
$(call C_preprocess, $< $(EXTRA_CFLAGS), $@) | |
plugin-api.i: tests/test-plugin-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DTEST_SELECT_API, $@) | |
plugin-db.i: tests/test-plugin-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DTEST_SELECT_DATABASE, $@) | |
plugin-exit.i: tests/test-plugin-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DTEST_SELECT_EXITSTATUS, $@) | |
plugin-name.i: tests/test-plugin-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DTEST_SELECT_NAME, $@) | |
plugin-option.i: tests/test-plugin-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DTEST_SELECT_OPTIONS, $@) | |
plugin-alpha.i: unit-tests/test-dl-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DPARAM=alpha, $@) | |
plugin-beta.i: unit-tests/test-dl-dummy.c $(preprocess_DEPS) | |
$(call C_preprocess, $< -DPARAM=beta, $@) | |
FORCE: | |
cpp_filter.py: $(THIS_FILE) | |
$(call Generate, $@, #) | |
$(file >> $@,#!/usr/env/python) | |
$(file >> $@,from __future__ import print_function) | |
$(file >> $@,if 1:) | |
$(file >> $@,$(CPP_FILTER_PY)) | |
define CPP_FILTER_PY | |
import sys, os | |
try: | |
import ntpath | |
except ImportError as e: | |
print ("Failed to import ntpath: %s" % e) | |
sys.exit(1) | |
def _win32_abspath (path): | |
path = ntpath.abspath (path) | |
return path.replace ('\\', '/') | |
cwd = _win32_abspath (os.getcwd()) + '/' | |
last_line = '??' | |
last_fname = '??' | |
empty_lines = 0 | |
while True: | |
line = sys.stdin.readline() | |
if not line: | |
break | |
if line.startswith('\n') or line.startswith('\r'): | |
empty_lines += 1 | |
continue | |
if 0: | |
line = line.replace ("\\\\", "/") | |
fname = None | |
quote = line.find ('\"') | |
if line.startswith("#line ") and quote > 0: | |
fname = _win32_abspath (line[quote:]) | |
last_fname = fname | |
if line.strip() != '' and last_line != '': | |
if fname is None or fname != last_fname: | |
print (line, end="") | |
last_line = line | |
if empty_lines > 0: | |
sys.stderr.write ("Removed %d empty lines.\n" % empty_lines) | |
endef | |
ifeq ($(MAKECMDGOALS),depend) | |
$(warning Not reading .depend.Windows) | |
else | |
-include .depend.Windows | |
endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright(c) 2016-2021 Free Software Foundation, Inc. | |
* | |
* This file is part of libwget. | |
* | |
* Libwget is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Lesser General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* Libwget is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public License | |
* along with libwget. If not, see <https://www.gnu.org/licenses/>. | |
* | |
* Win32 console + ANSI-decoder functions. | |
*/ | |
#define INSIDE_MSWINDOWS_C 1 | |
#include <config.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <getopt.h> | |
#undef option /* Because of '#define option gnulib_option' in <config.h> */ | |
#include <wget.h> | |
#include "libwget/private.h" | |
#include "libwget/mswindows.h" | |
static HANDLE g_semaphore = NULL; | |
static BOOL g_sema_inherited = FALSE; | |
static FILE *g_file_out = NULL; | |
static int g_stdout_fd = -1; /* Opened in TEXT-mode */ | |
static int ANSI_write (const char *buf, size_t len, FILE *stream); | |
#ifdef ANSI_TEST | |
#define ALWAYS_BOLD_AND_CURRENT_BACKGROUND 0 | |
#endif | |
/* | |
* The console colours looks very ugly without a 'ALWAYS_BOLD_AND_CURRENT_BACKGROUND=1' | |
* if using e.g. the ANSI codes in src/log.c: | |
* _write_out(fp, data, len, 0, "\033[31m"); // red text | |
* | |
* With e.g. a default blue WinConsole background, the above would give a dark red | |
* on a black background which is very hard to read. | |
*/ | |
#ifndef ALWAYS_BOLD_AND_CURRENT_BACKGROUND | |
#define ALWAYS_BOLD_AND_CURRENT_BACKGROUND 1 | |
#endif | |
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | |
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 | |
#endif | |
/* | |
* Minumum level to enable the ANSI state-machine tracing. | |
* Does *not* print the CSI-sequences. | |
*/ | |
#ifndef ANSI_MIN_DEBUG_LEVEL | |
#define ANSI_MIN_DEBUG_LEVEL 3 | |
#endif | |
/* | |
* Windows-10 "Console Virtual Terminal Sequences": | |
* https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 | |
* | |
* Underlined fonts etc.: | |
* https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx | |
* | |
* Console Improvements in the Windows 10 Technical Preview | |
* https://blogs.windows.com/buildingapps/2014/10/07/console-improvements-in-the-windows-10-technical-preview/#ix0cicvpe6vf1ugW.97 | |
*/ | |
static bool use_vts_mode = false; /* If '$WGET2_TRACE=N,vts' */ | |
static bool use_ansicon = false; /* If running under ANSICON driver. */ | |
static bool have_csi_ex = false; /* If 'GetConsoleScreenBufferInfoEx()' is available */ | |
static bool ANSI_raw = false; | |
static int trace_level = -1; | |
static DWORD ansicon_pid = 0; | |
static void colour_set_underline (int on); | |
static void colour_set_inverse (int on); | |
static void wget_trace_init (void); | |
static void wget_trace_colour (unsigned short colour); | |
static void wget_set_colour (unsigned short colour); | |
/* In Win-Vista or above. | |
*/ | |
typedef BOOL (WINAPI *func_GetConsoleScreenBufferInfoEx) (HANDLE console, | |
CONSOLE_SCREEN_BUFFER_INFOEX *csi_ex); | |
typedef BOOL (WINAPI *func_SetConsoleScreenBufferInfoEx) (HANDLE console, | |
CONSOLE_SCREEN_BUFFER_INFOEX *csi_ex); | |
typedef BOOL (WINAPI *func_GetCurrentConsoleFontEx) (HANDLE console, | |
BOOL maximum_win, | |
CONSOLE_FONT_INFOEX *font_ex); | |
typedef BOOL (WINAPI *func_SetCurrentConsoleFontEx) (HANDLE console, | |
BOOL maximum_win, | |
CONSOLE_FONT_INFOEX *font_ex); | |
static func_GetConsoleScreenBufferInfoEx p_GetConsoleScreenBufferInfoEx; | |
static func_SetConsoleScreenBufferInfoEx p_SetConsoleScreenBufferInfoEx; | |
static func_GetCurrentConsoleFontEx p_GetCurrentConsoleFontEx; | |
static func_SetCurrentConsoleFontEx p_SetCurrentConsoleFontEx; | |
static CONSOLE_SCREEN_BUFFER_INFOEX g_console_info_ex; | |
static CONSOLE_SCREEN_BUFFER_INFO g_orig_info; | |
/* | |
* For some mysterious reason, the GnuLib's 'rpl_gmtime_r()' and | |
* 'localtime_r()' gets redefined in MinGW-w64's <time.h> with a | |
* '__forceinline' which never gets into the 'http.o' file. | |
* So just put it here. | |
*/ | |
#undef gmtime | |
struct tm *wget_gmtime_r (const time_t *_Time, struct tm *_Tm) | |
{ | |
struct tm *tm = gmtime (_Time); | |
if (tm) | |
return memcpy (_Tm, tm, sizeof(*_Tm)); | |
return (NULL); | |
} | |
struct tm *wget_localtime_r (const time_t *_Time, struct tm *_Tm) | |
{ | |
struct tm *tm = localtime (_Time); | |
if (tm) | |
return memcpy (_Tm, tm, sizeof(*_Tm)); | |
return (NULL); | |
} | |
int wget_sema_wait (void) | |
{ | |
int rc = 0; | |
while (g_semaphore) | |
{ | |
if (WaitForSingleObject(g_semaphore, 0) == WAIT_OBJECT_0) | |
break; | |
rc++; | |
Sleep (1); | |
} | |
return (rc); | |
} | |
static LONG last_sema = 0; | |
int wget_sema_release (void) | |
{ | |
LONG rc = 0; | |
if (g_semaphore && ReleaseSemaphore(g_semaphore, 1, &rc)) | |
last_sema = rc; | |
return (int) rc; | |
} | |
static int wget_trace_level (void) | |
{ | |
if (trace_level == -1) | |
{ | |
trace_level = 0; /* Don't call here again from 'wget_console_init2()' */ | |
wget_console_init2(); | |
wget_trace_init(); | |
WGET2_TRACE (2, "wget_trace_init() done. ANSI_raw: %d, use_vts_mode: %d, use_ansicon: %d, ansicon_pid: %lu\n", | |
ANSI_raw, use_vts_mode, use_ansicon, ansicon_pid); | |
if (_gl_win32_winnt != _WIN32_WINNT) | |
WGET2_TRACE (1, "_gl_win32_winnt != the '_WIN32_WINNT' value libwget was built with. _gl_win32_winnt=0x%04X.\n", _gl_win32_winnt); | |
} | |
return (trace_level); | |
} | |
void wget_trace (int level, const char *file, unsigned line, const char *fmt, ...) | |
{ | |
if (g_file_out && wget_trace_level() >= level) | |
{ | |
va_list args; | |
#if (USE_ANSI_TRACE) | |
wget_ANSI_fprintf (g_file_out, "\033[32;1m[%d/%ld] %s(%u): \033[33;1m", | |
g_sema_inherited ? 1 : 0, last_sema, file, line); | |
va_start (args, fmt); | |
vfprintf (g_file_out, fmt, args); | |
ANSI_write ("\033[m", 4, g_file_out); /* restore to default colours */ | |
#else | |
wget_sema_wait(); | |
wget_trace_colour (TRACE_COLOR_START); | |
fprintf (g_file_out, "[%d/%ld] %s(%u): ", g_sema_inherited ? 1 : 0, last_sema, file, line); | |
wget_trace_colour (TRACE_COLOR_ARGS); | |
va_start (args, fmt); | |
vfprintf (g_file_out, fmt, args); | |
va_end (args); | |
wget_trace_colour (0); | |
wget_sema_release(); | |
#endif | |
} | |
} | |
/* | |
* Check if we've already got an instance of ourself. | |
* If we are the top-level libwget, we want the handle to be inherited | |
* by child processes. | |
*/ | |
static void wget_sema_init (void) | |
{ | |
SECURITY_ATTRIBUTES sec; | |
sec.nLength = sizeof (sec); | |
sec.lpSecurityDescriptor = NULL; | |
sec.bInheritHandle = TRUE; | |
SetLastError (0); | |
g_semaphore = CreateSemaphore (&sec, 1, 10, "Global\\libwget-semaphore"); | |
g_sema_inherited = (GetLastError() == ERROR_ALREADY_EXISTS); | |
} | |
static void wget_sema_exit (void) | |
{ | |
if (g_semaphore) | |
CloseHandle (g_semaphore); | |
g_semaphore = NULL; | |
} | |
static void wget_trace_init (void) | |
{ | |
const char *env; | |
/* $WGET2_TRACE=trace_level[,raw][,vts] | |
*/ | |
env = getenv ("WGET2_TRACE"); | |
if (env) | |
{ | |
if (isdigit(*env)) | |
{ | |
trace_level = *env - '0'; | |
env++; | |
} | |
if (*env && !strnicmp(env, ",raw", 4)) | |
{ | |
ANSI_raw = true; | |
env += 4; | |
} | |
if (*env && !strnicmp(env, ",vts", 4)) | |
use_vts_mode = true; | |
} | |
else | |
trace_level = 0; | |
/* Check if running under ANSICON driver. | |
*/ | |
env = getenv ("ANSICON_DEF"); | |
if (env && atoi(env) > 0) | |
{ | |
use_ansicon = true; | |
/* to-do: find the process-id of ansicon.exe | |
*/ | |
} | |
} | |
static void wget_console_deinit2 (void) | |
{ | |
if (g_stdout_hnd != INVALID_HANDLE_VALUE) | |
{ | |
colour_set_underline (0); | |
colour_set_inverse (0); | |
wget_set_colour (g_orig_info.wAttributes); /* calls 'fflush(g_file_out)' */ | |
if (g_stdout_fd > -1) | |
_close (g_stdout_fd); | |
g_stdout_fd = -1; | |
g_file_out = NULL; | |
g_stdout_hnd = INVALID_HANDLE_VALUE; | |
} | |
wget_sema_exit(); | |
} | |
/* | |
* This function is called from 'wget_console_init()' only. | |
* It completely replaces it; trying to compensate for redirected | |
* 'stdout' or 'stderr' output. | |
*/ | |
int wget_console_init2 (void) | |
{ | |
static bool is_init = false; | |
HANDLE hnd; | |
DWORD csi_ex_err = 0; | |
if (is_init) | |
return (g_stdout_hnd != INVALID_HANDLE_VALUE); | |
wget_sema_init(); | |
WGET2_TRACE (2, "wget_console_init2() called. g_semaphore: %p, g_sema_inherited: %d\n", | |
g_semaphore, g_sema_inherited); | |
hnd = LoadLibrary ("kernel32.dll"); | |
if (hnd && hnd != INVALID_HANDLE_VALUE) | |
{ | |
#define GET_FUNC(f) p_##f = (func_##f) GetProcAddress (hnd, #f) | |
GET_FUNC (GetConsoleScreenBufferInfoEx); | |
GET_FUNC (SetConsoleScreenBufferInfoEx); | |
GET_FUNC (GetCurrentConsoleFontEx); | |
GET_FUNC (SetCurrentConsoleFontEx); | |
#undef GET_FUNC | |
} | |
hnd = GetStdHandle (STD_OUTPUT_HANDLE); | |
if (hnd != INVALID_HANDLE_VALUE) | |
{ | |
if (p_GetConsoleScreenBufferInfoEx) | |
have_csi_ex = (*p_GetConsoleScreenBufferInfoEx) (hnd, &g_console_info_ex); | |
else csi_ex_err = GetLastError(); | |
GetConsoleScreenBufferInfo (hnd, &g_console_info); | |
memcpy (&g_orig_info, &g_console_info, sizeof(g_orig_info)); | |
if (GetFileType(hnd) != FILE_TYPE_CHAR) /* 'stdout' is redirected */ | |
hnd = INVALID_HANDLE_VALUE; | |
} | |
#if !defined(ANSI_TEST) && 0 | |
/* | |
* If the above failed for 'STD_OUTPUT_HANDLE', we have a redirected 'stdout'. | |
* Hence try the same for 'STD_ERROR_HANDLE' instead. If that fails too, | |
* do not output anything. | |
*/ | |
if (hnd == INVALID_HANDLE_VALUE) | |
{ | |
hnd = GetStdHandle (STD_ERROR_HANDLE); | |
if (hnd != INVALID_HANDLE_VALUE) | |
{ | |
if (p_GetConsoleScreenBufferInfoEx) | |
have_csi_ex = (*p_GetConsoleScreenBufferInfoEx) (hnd, &g_console_info_ex); | |
else csi_ex_err = GetLastError(); | |
if (GetFileType(hnd) != FILE_TYPE_CHAR) /* 'stderr' is redirected */ | |
hnd = INVALID_HANDLE_VALUE; | |
} | |
} | |
#endif | |
g_stdout_hnd = hnd; | |
g_file_out = stdout; | |
if (g_stdout_hnd != INVALID_HANDLE_VALUE) | |
{ | |
g_stdout_fd = _open_osfhandle ((intptr_t)hnd, _O_RDONLY | _O_TEXT); | |
g_file_out = _fdopen (g_stdout_fd, "wt"); | |
} | |
is_init = true; | |
atexit (wget_console_deinit2); | |
WGET2_TRACE (2, "%-30s: 0x%p\n", "GetConsoleScreenBufferInfoEx()", (const void*)p_GetConsoleScreenBufferInfoEx); | |
WGET2_TRACE (2, "%-30s: 0x%p\n", "SetConsoleScreenBufferInfoEx()", (const void*)p_SetConsoleScreenBufferInfoEx); | |
WGET2_TRACE (2, "%-30s: 0x%p\n", "GetCurrentConsoleFontEx()", (const void*)p_GetCurrentConsoleFontEx); | |
WGET2_TRACE (2, "%-30s: 0x%p\n", "SetCurrentConsoleFontEx()", (const void*)p_SetCurrentConsoleFontEx); | |
if (csi_ex_err) | |
WGET2_TRACE (2, "GetConsoleScreenBufferInfoEx() failed: %lu\n", csi_ex_err); | |
if (use_vts_mode && hnd != INVALID_HANDLE_VALUE) | |
{ | |
DWORD mode = 0; | |
bool okay; | |
if (GetConsoleMode(hnd, &mode)) | |
{ | |
WGET2_TRACE (2, "GetConsoleMode(): mode: 0x%04lX.\n", mode); | |
if (!SetConsoleMode(hnd, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) | |
WGET2_TRACE (3, "SetConsoleMode(): err: %lu.\n", GetLastError()); | |
/* Just check if the VTS mode is in effect now. | |
*/ | |
mode = 0; | |
GetConsoleMode (hnd, &mode); | |
okay = (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); | |
WGET2_TRACE (2, "GetConsoleMode(): 0x%04lX, %sokay.\n", mode, okay ? "" : "not "); | |
if (!okay) | |
use_vts_mode = false; | |
} | |
} | |
WGET2_TRACE (2, "wget_console_init2() done, g_stdout_hnd: 0x%p, " | |
"g_sema_inherited: %d\n", g_stdout_hnd, g_sema_inherited); | |
if (have_csi_ex) | |
{ | |
COLORREF cr; | |
int i; | |
WGET2_TRACE (2, "csi_ex: wPopupAttributes: 0x%04X\n" | |
" bFullscreenSupported: %d\n" | |
" ColorTable: (RGB)\n", | |
g_console_info_ex.wPopupAttributes, | |
g_console_info_ex.bFullscreenSupported); | |
for (i = 0; i < countof(g_console_info_ex.ColorTable); i++) | |
{ | |
cr = g_console_info_ex.ColorTable[i]; | |
WGET2_TRACE (2, " %u,%u,%u\n", (unsigned)(cr >> 16), (unsigned)(cr & 0xFF00) >> 8, (unsigned)(cr & 0xFF)); | |
} | |
} | |
return (g_stdout_hnd != INVALID_HANDLE_VALUE); | |
} | |
static void wget_set_colour (unsigned short colour) | |
{ | |
static unsigned short last_colour; | |
if (!wget_console_init2()) | |
return; | |
#if (ALWAYS_BOLD_AND_CURRENT_BACKGROUND) | |
if (colour == 0) | |
colour = g_console_info.wAttributes; | |
colour |= FOREGROUND_INTENSITY | (g_console_info.wAttributes & ~7); | |
#endif | |
if (colour != last_colour) | |
{ | |
fflush (g_file_out); | |
SetConsoleTextAttribute (g_stdout_hnd, colour); | |
last_colour = colour; | |
} | |
} | |
static void wget_trace_colour (unsigned short colour) | |
{ | |
static unsigned short last_colour; | |
if (!g_file_out || !g_stdout_hnd || g_stdout_hnd == INVALID_HANDLE_VALUE) | |
return; | |
fflush (g_file_out); | |
if (colour == 0) | |
colour = g_orig_info.wAttributes; | |
colour |= (g_orig_info.wAttributes & ~7); | |
if (colour != last_colour) | |
{ | |
fflush (g_file_out); | |
SetConsoleTextAttribute (g_stdout_hnd, colour); | |
last_colour = colour; | |
} | |
} | |
/* | |
* A simple ANSI parser for the Windows Console. | |
* Not all CSI commands are implemented. Only those needed in Wget2 | |
* and particularly those for libwget/bar.c and tests/libtest.c. | |
*/ | |
static int normal_state (int ch); | |
static int got_ESC_state (int ch); | |
static int got_lpar_state (int ch); | |
static int got_rpar_state1 (int ch); | |
static int got_rpar_state2 (int ch); | |
static int get_rpar_arg_state (int ch); | |
static int get_arg_state (int ch); | |
static void set_colours (void); | |
static void get_default_colours (void); | |
static void set_default_colours (void); | |
static void cursor_save (void); | |
static void cursor_restore (void); | |
static void cursor_up (int lines); | |
static void cursor_down (int lines); | |
static void cursor_set_vert (int y); | |
static void cursor_set_horiz (int x); | |
static void erase_screen (int how); | |
static void scroll_up (int lines); | |
static void scroll_down (int lines); | |
typedef int (*state_func) (int ch); | |
static state_func ansi_state = normal_state; | |
static int par_num = 0, rpar_num = 0, param[16]; | |
static char rparam [200]; | |
static bool got_param = false; | |
static WORD attribute, underline, inverse, fore, back; | |
static unsigned blink, bold; | |
static COORD curs_pos; | |
static const char *state_name (state_func f) | |
{ | |
if (f == normal_state) | |
return ("normal_state"); | |
if (f == got_ESC_state) | |
return ("got_ESC_state"); | |
if (f == got_lpar_state) | |
return ("got_lpar_state"); | |
if (f == got_rpar_state1) | |
return ("got_rpar_state1"); | |
if (f == got_rpar_state2) | |
return ("got_rpar_state2"); | |
if (f == get_rpar_arg_state) | |
return ("get_rpar_arg_state"); | |
if (f == get_arg_state) | |
return ("get_arg_state"); | |
return ("??"); | |
} | |
#define WARN(fmt, ...) _warn (__LINE__, fmt, ## __VA_ARGS__) | |
static int _warn (unsigned line, const char *fmt, ...) | |
{ | |
va_list args; | |
int rc = 0; | |
FILE *f = g_file_out; | |
#ifdef ANSI_TEST | |
f = stderr; | |
rc = -1; | |
#endif | |
va_start (args, fmt); | |
fprintf (f, "WARNING (line %u): ", line); | |
vfprintf (f, fmt, args); | |
fflush (f); | |
va_end (args); | |
got_param = false; | |
ansi_state = normal_state; | |
return (rc); | |
} | |
static const char *ANSI_tracef (const char *fmt, ...) | |
{ | |
static char buf [300] = { '?' }; | |
static char *trace_ptr = buf; | |
static size_t left = sizeof(buf); | |
if (trace_level < ANSI_MIN_DEBUG_LEVEL) | |
return (buf); | |
if (!fmt) | |
{ | |
*trace_ptr = '\0'; | |
trace_ptr = buf; | |
left = sizeof(buf); | |
return (buf); | |
} | |
int len; | |
va_list args; | |
va_start (args, fmt); | |
len = vsnprintf (trace_ptr, left, fmt, args); | |
left -= len; | |
trace_ptr += len; | |
*trace_ptr++ = ','; | |
left--; | |
va_end (args); | |
return (buf); | |
} | |
static int ANSI_putchar (int ch) | |
{ | |
int rc = 0; | |
if (trace_level < ANSI_MIN_DEBUG_LEVEL) | |
{ | |
rc = putchar (ch); | |
return (rc < 0 ? -1 : 1); | |
} | |
ANSI_tracef ("putchar(%d)", ch); | |
return (rc); | |
} | |
static const char *ANSI_getchar (int ch) | |
{ | |
static char buf [5]; | |
if (trace_level < ANSI_MIN_DEBUG_LEVEL) | |
{ | |
buf[0] = ch; | |
buf[1] = '\0'; | |
} | |
else if (ch == '\033') | |
strcpy (buf, "\\e"); | |
else if (ch == '\n') | |
strcpy (buf, "\\n"); | |
else if (ch == '\r') | |
strcpy (buf, "\\r"); | |
else if (ch > 0 && ch < ' ') | |
snprintf (buf, sizeof(buf), "\\%d", ch); | |
else if (ch < 0) | |
snprintf (buf, sizeof(buf), "-\\%2X", -ch); | |
else if (ch >= 127) | |
snprintf (buf, sizeof(buf), "\\%2X", ch); | |
else | |
{ | |
buf[0] = ch; | |
buf[1] = '\0'; | |
} | |
return (buf); | |
} | |
static int ANSI_write (const char *buf, size_t len, FILE *stream) | |
{ | |
const char *s; | |
int rc, actions; | |
size_t i; | |
wget_console_init2(); | |
if (use_vts_mode || use_ansicon || | |
(ANSI_raw && trace_level < ANSI_MIN_DEBUG_LEVEL)) | |
return fputs (buf, stream); | |
wget_sema_wait(); | |
s = buf; | |
actions = 0; | |
for (i = 0; i < len; i++) | |
{ | |
state_func old_state = ansi_state; | |
int ch = *s++; | |
char p_buf [10]; | |
rc = (*ansi_state) (ch); | |
if (trace_level >= ANSI_MIN_DEBUG_LEVEL) | |
printf ("state: %-20s -> %-20s %-3s action: %-25s param[%d]: %s\n", | |
state_name(old_state), state_name(ansi_state), | |
ANSI_getchar(ch), ANSI_tracef(NULL), | |
par_num, got_param ? itoa(param[par_num],p_buf,10) : "<N/A>"); | |
#if defined(ANSI_TEST) | |
if (rc != 0 && rc != 1) | |
{ | |
errno = EILSEQ; | |
break; | |
} | |
#endif | |
actions += rc; | |
} | |
wget_sema_release(); | |
return (actions); | |
} | |
static BOOL our_console_stream (const FILE *f) | |
{ | |
return (f == stdout) || (g_file_out != NULL && f == g_file_out); | |
} | |
int wget_ANSI_puts (const char *buf) | |
{ | |
if (use_vts_mode || !g_file_out) | |
return puts (buf); | |
return (ANSI_write(buf, strlen(buf), g_file_out) + | |
ANSI_write("\n", 1, g_file_out)); /* since 'puts()' shall add a newline */ | |
} | |
int wget_ANSI_fputs (const char *string, FILE *const stream) | |
{ | |
if (use_vts_mode || !our_console_stream(stream)) | |
return fputs (string, stream); | |
return ANSI_write (string, strlen(string), stream); | |
} | |
size_t wget_ANSI_fwrite (const void *buf, size_t elements, size_t len, FILE *const stream) | |
{ | |
if (use_vts_mode || !our_console_stream(stream)) | |
return fwrite (buf, elements, len, stream); | |
len = elements * len; | |
if (len > strlen(buf)) | |
len = strlen (buf); | |
return ANSI_write (buf, len, stream); | |
} | |
int wget_ANSI_vprintf (const char *fmt, va_list args) | |
{ | |
#if (USE_VASNPRINTF) | |
size_t len; | |
char *buf = vasnprintf (NULL, &len, fmt, args); | |
if (!buf) | |
return (0); | |
ANSI_write (buf, len, g_file_out); | |
free (buf); | |
#else | |
char buf [1200]; | |
int len = vsnprintf (buf, sizeof(buf), fmt, args); | |
ANSI_write (buf, len, g_file_out); | |
#endif | |
fflush (g_file_out); | |
return (int) len; | |
} | |
int wget_ANSI_printf (char const *const fmt, ...) | |
{ | |
va_list args; | |
int rc; | |
va_start (args, fmt); | |
rc = wget_ANSI_vprintf (fmt, args); | |
va_end (args); | |
return (rc); | |
} | |
int wget_ANSI_fprintf (FILE *const stream, char const *const fmt, ...) | |
{ | |
int rc; | |
va_list args; | |
va_start (args, fmt); | |
if (use_vts_mode || !our_console_stream(stream)) | |
rc = vprintf (fmt, args); | |
else rc = wget_ANSI_vprintf (fmt, args); | |
va_end (args); | |
return (rc); | |
} | |
/* | |
* Normal parser state. Check for ESC ('\033'). | |
*/ | |
static int normal_state (int ch) | |
{ | |
if (ch == '\033') | |
{ | |
ansi_state = got_ESC_state; | |
return (0); | |
} | |
if (ch == '\0') | |
{ | |
ANSI_tracef ("\\0, no action"); | |
return (1); | |
} | |
if (ch == '\n') | |
ANSI_putchar ('\r'); | |
return ANSI_putchar (ch); | |
} | |
/* | |
* Parser for starting "ESC" sequence | |
*/ | |
static int got_ESC_state (int ch) | |
{ | |
switch (ch) | |
{ | |
case '[': | |
memset (¶m, 0, sizeof(param)); | |
par_num = 0; | |
got_param = false; | |
ansi_state = got_lpar_state; | |
return (0); | |
case ']': | |
ansi_state = got_rpar_state1; | |
return (0); | |
case '\\': | |
ansi_state = normal_state; | |
return (0); | |
case '*': | |
ansi_state = normal_state; | |
return (0); | |
case 24: | |
case 26: | |
ansi_state = normal_state; | |
ANSI_tracef ("<cancel ESC>"); | |
return (0); | |
case '\033': | |
ANSI_tracef ("<swallow ESC>"); | |
return (0); | |
default: | |
ansi_state = normal_state; | |
return ANSI_putchar (ch); | |
} | |
} | |
/* | |
* We'd got a "ESC[" sequence. | |
* Check for 1st parameter or take action on next character; 'ch'. | |
*/ | |
static int got_lpar_state (int ch) | |
{ | |
if (isdigit(ch)) /* got "ESC[<digit>" */ | |
{ | |
param [par_num] = ch - '0'; | |
got_param = true; | |
ansi_state = get_arg_state; | |
return (0); | |
} | |
if (ch == '?') | |
{ | |
param[0] = 0; | |
ansi_state = get_arg_state; | |
return (0); | |
} | |
if (ch == 'm') /* got "ESC[m" */ | |
{ | |
set_default_colours(); | |
ansi_state = normal_state; | |
return (0); | |
} | |
if (ch == 's') /* got "ESC[s" */ | |
{ | |
cursor_save(); | |
ansi_state = normal_state; | |
return (0); | |
} | |
if (ch == 'u') /* got "ESC[u" */ | |
{ | |
cursor_restore(); | |
ansi_state = normal_state; | |
return (0); | |
} | |
ansi_state = normal_state; | |
return ANSI_putchar (ch); | |
} | |
/* | |
* We'd got a "ESC]" sequence. The "Operating system command" state. | |
* Only accepted commands are: | |
* ESC ] 0 ; <string> \7 | |
* ESC ] 2 ; <string> \7 | |
*/ | |
static int got_rpar_state1 (int ch) | |
{ | |
if (ch == '0' || ch == '2') | |
ansi_state = got_rpar_state2; | |
else ansi_state = normal_state; | |
return (0); | |
} | |
/* | |
* Intermediate "Operating system command" state. | |
* Check for ';' | |
*/ | |
static int got_rpar_state2 (int ch) | |
{ | |
if (ch == ';') | |
{ | |
rpar_num = 0; | |
ansi_state = get_rpar_arg_state; | |
} | |
else | |
ansi_state = normal_state; | |
return (0); | |
} | |
/* | |
* The main "Operating system command" state. | |
* Gooble up string-title parameters until 'ch == \7'. | |
*/ | |
static int get_rpar_arg_state (int ch) | |
{ | |
if (ch == '\7') | |
{ | |
if (strlen(rparam) > 0) | |
SetConsoleTitle (rparam); | |
rparam [0] = '\0'; | |
ansi_state = normal_state; | |
} | |
else if (rpar_num <= countof(rparam)-2) | |
{ | |
rparam [rpar_num++] = ch; | |
rparam [rpar_num] = '\0'; | |
} | |
else | |
return WARN ("Too many rparam[] parameters: %d\n", rpar_num); | |
return (0); | |
} | |
/* | |
* Parser for "ESC[..<;..x>" sequence. | |
* Gooble up more parameters or take action on next character; 'ch'. | |
*/ | |
static int get_arg_state (int ch) | |
{ | |
if (isdigit(ch)) | |
{ | |
param[par_num] *= 10; | |
param[par_num] += ch - '0'; | |
return (0); | |
} | |
switch (ch) | |
{ | |
case ';': | |
ansi_state = got_lpar_state; | |
par_num++; /* Get value for next 'param[]' */ | |
if (par_num >= countof(param)-1) | |
return WARN ("Too many parameters: %d\n", par_num); | |
return (0); | |
case 'm': | |
set_colours(); | |
break; | |
case 'A': | |
cursor_up (param[0]); | |
break; | |
case 'B': | |
cursor_down (param[0]); | |
break; | |
case 'F': | |
cursor_set_vert (param[0]); | |
break; | |
case 'G': | |
cursor_set_horiz (param[0]); | |
break; | |
case 'J': | |
erase_screen (param[0]); | |
break; | |
case 'S': | |
scroll_up (param[0]); | |
break; | |
case 'T': | |
scroll_down (param[0]); | |
break; | |
case '\033': | |
ansi_state = normal_state; | |
break; | |
default: | |
return WARN ("Unknown CSI: %d\n", ch); | |
} | |
got_param = false; | |
ansi_state = normal_state; | |
return (0); | |
} | |
/* | |
* Some of this code is taken from 'less'. | |
* Thanks to Mark Nudelman for this code. Making sense of this | |
* hard to understand Microsoft function! | |
*/ | |
static void do_scroll (int lines) | |
{ | |
SMALL_RECT r_src, r_clip; | |
CHAR_INFO ch_inf = { .Char.AsciiChar = ' ', | |
.Attributes = g_console_info.wAttributes | |
}; | |
COORD dest; | |
/* The src rectangle is the entire visible screen. | |
*/ | |
r_src.Left = 0; | |
r_src.Top = g_console_info.srWindow.Top; | |
r_src.Right = g_console_info.srWindow.Right - g_console_info.srWindow.Left; | |
r_src.Bottom = g_console_info.srWindow.Bottom - g_console_info.srWindow.Top; | |
r_clip = r_src; | |
if (lines < 0) | |
{ | |
/* The clip rectangle is the visible screen minus the last 'lines'. | |
*/ | |
r_clip.Bottom += lines; | |
} | |
else | |
{ | |
/* The clip rectangle is the visible screen minus the first 'lines'. | |
*/ | |
r_clip.Top += lines; | |
} | |
/* Move the source window up 'lines' rows. | |
*/ | |
dest.X = r_src.Left; | |
dest.Y = r_src.Top - lines; | |
ScrollConsoleScreenBuffer (g_stdout_hnd, &r_src, &r_clip, dest, &ch_inf); | |
} | |
/* | |
* CSI <n> S: | |
* Scroll whole page up by <n> (default 1) lines. | |
* New lines are added at the bottom. | |
*/ | |
static void scroll_up (int lines) | |
{ | |
ANSI_tracef ("do_scroll(%d)", -lines); | |
lines = max (1, lines); | |
lines = min (lines, g_console_info.dwSize.Y-1); | |
do_scroll (-lines); | |
} | |
/* | |
* CSI <n> T: | |
* Scroll whole page down by <n> (default 1) lines. | |
* New lines are added at the top. | |
*/ | |
static void scroll_down (int lines) | |
{ | |
ANSI_tracef ("do_scroll(%d)", lines); | |
lines = max (1, lines); | |
lines = min (lines, g_console_info.dwSize.Y-1); | |
do_scroll (lines); | |
} | |
static void cursor_save (void) | |
{ | |
ANSI_tracef ("cursor_save()"); | |
if (ANSI_raw) | |
return; | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
curs_pos.X = g_console_info.dwCursorPosition.X; | |
curs_pos.Y = g_console_info.dwCursorPosition.Y; | |
} | |
static void cursor_restore (void) | |
{ | |
ANSI_tracef ("cursor_restore()"); | |
if (!ANSI_raw) | |
SetConsoleCursorPosition (g_stdout_hnd, curs_pos); | |
} | |
static void bound_and_set_cursor (WORD x, WORD y) | |
{ | |
x = max (0, x); | |
x = min (x, g_console_info.dwSize.X-1); | |
y = max (0, y); | |
y = min (y, g_console_info.dwSize.Y-1); | |
g_console_info.dwCursorPosition.X = x; | |
g_console_info.dwCursorPosition.Y = y; | |
SetConsoleCursorPosition (g_stdout_hnd, g_console_info.dwCursorPosition); | |
} | |
/* | |
* CSI <n> A: Cursor Up. | |
*/ | |
static void cursor_up (int lines) | |
{ | |
ANSI_tracef ("cursor_up(%d)", lines); | |
if (ANSI_raw) | |
return; | |
lines = max (1, lines); | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
bound_and_set_cursor (g_console_info.dwCursorPosition.X, | |
g_console_info.dwCursorPosition.Y - lines); | |
} | |
/* | |
* CSI <n> B: Cursor Down. | |
*/ | |
static void cursor_down (int lines) | |
{ | |
ANSI_tracef ("cursor_down(%d)", lines); | |
if (ANSI_raw) | |
return; | |
lines = max (1, lines); | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
bound_and_set_cursor (g_console_info.dwCursorPosition.X, | |
g_console_info.dwCursorPosition.Y + lines); | |
} | |
/* | |
* CSI <n> F: | |
* Moves cursor to beginning of the line n (default 1) lines up. | |
*/ | |
static void cursor_set_vert (int y) | |
{ | |
ANSI_tracef ("cursor_set_vert(%d)", y); | |
if (ANSI_raw) | |
return; | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
bound_and_set_cursor (0, g_console_info.dwCursorPosition.Y - y); | |
} | |
/* | |
* CSI <n> G: | |
* Cursor Horizontal Absolute. | |
* Moves the cursor to column <n> (default 1). | |
*/ | |
static void cursor_set_horiz (int x) | |
{ | |
ANSI_tracef ("cursor_set_horiz(%d)", x); | |
if (ANSI_raw) | |
return; | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
bound_and_set_cursor (x, g_console_info.dwCursorPosition.Y); | |
} | |
/* | |
* CSI <n> J: | |
* Clears part of the screen. | |
* If <n> is 0 (or missing), clear from cursor to end of screen. | |
* If <n> is 1, clear from cursor to beginning of the screen. | |
* If <n> is 2, clear entire screen (and moves cursor to upper left). | |
* If <n> is 3, clear entire screen and delete all lines saved in the scrollback buffer. | |
* | |
* This implementation doesn't support n = 3. | |
*/ | |
static void erase_screen (int how) | |
{ | |
WORD x = 0, y = 0; | |
int len = 0; | |
bool clr_eos = (how == 0); | |
bool clr_bos = (how == 1); | |
bool clr_all = (how == 2); | |
ANSI_tracef ("erase_screen(%d)", how); | |
if (ANSI_raw || how > 2) | |
return; | |
GetConsoleScreenBufferInfo (g_stdout_hnd, &g_console_info); | |
if (clr_eos) | |
{ | |
x = g_console_info.dwCursorPosition.X; | |
y = g_console_info.dwCursorPosition.Y; | |
len = g_console_info.dwSize.X - g_console_info.dwCursorPosition.X + | |
g_console_info.dwSize.X * (g_console_info.dwSize.Y - g_console_info.dwCursorPosition.Y - 1); | |
} | |
else if (clr_bos) | |
{ | |
len = g_console_info.dwCursorPosition.X + 1 + | |
(g_console_info.dwSize.X * g_console_info.dwCursorPosition.Y); | |
} | |
else if (clr_all) | |
{ | |
len = g_console_info.dwSize.X * g_console_info.dwSize.Y; | |
} | |
if (len > 0) | |
{ | |
COORD coord = { .X = x, .Y = y }; | |
DWORD written; | |
FillConsoleOutputCharacter (g_stdout_hnd, ' ', len, coord, &written); | |
FillConsoleOutputAttribute (g_stdout_hnd, g_console_info.wAttributes, len, coord, &written); | |
if (clr_all) | |
{ | |
coord.X = coord.Y = 0; | |
SetConsoleCursorPosition (g_stdout_hnd, coord); | |
} | |
} | |
} | |
static void get_default_colours (void) | |
{ | |
ANSI_tracef ("get_default_colours()"); | |
attribute = g_console_info.wAttributes; | |
fore = g_console_info.wAttributes & 4; | |
back = g_console_info.wAttributes >> 4; | |
bold = (attribute & FOREGROUND_INTENSITY); | |
blink = (attribute & BACKGROUND_INTENSITY); | |
underline = 0; | |
inverse = 0; | |
} | |
static void set_default_colours (void) | |
{ | |
get_default_colours(); | |
wget_set_colour (attribute); | |
ANSI_tracef ("wget_set_colour(%d)", attribute); | |
} | |
static void colour_set_bold (int on) | |
{ | |
bold = on ? FOREGROUND_INTENSITY : 0; | |
attribute = (inverse + underline + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_bold(%d)", on); | |
wget_set_colour (attribute); | |
} | |
static void colour_set_underline (int on) | |
{ | |
#if 1 | |
underline = on ? COMMON_LVB_UNDERSCORE : 0; | |
attribute = (underline + inverse + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_underline(%d)", on); | |
wget_set_colour (attribute); | |
#else | |
if (p_GetCurrentConsoleFontEx && p_SetCurrentConsoleFontEx) | |
{ | |
CONSOLE_FONT_INFOEX fi_ex; | |
fi_ex.cbSize = sizeof(fi_ex); | |
(*p_GetCurrentConsoleFontEx) (g_stdout_hnd, FALSE, &fi_ex); | |
fi_ex.FontWeight = on ? 700 : 400; /* !! fix this */ | |
fi_ex.cbSize = sizeof(fi_ex); | |
(*p_SetCurrentConsoleFontEx) (g_stdout_hnd, FALSE, &fi_ex); | |
ANSI_tracef ("colour_set_underline(%d)", on); | |
} | |
else | |
colour_set_bold (on); | |
#endif | |
} | |
static void colour_set_blink (int on) | |
{ | |
blink = on ? BACKGROUND_INTENSITY : 0; | |
attribute = (inverse + underline + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_blink(%d)", on); | |
wget_set_colour (attribute); | |
} | |
static void colour_set_inverse (int on) | |
{ | |
#if 1 | |
inverse = on ? COMMON_LVB_REVERSE_VIDEO : 0; | |
#else | |
WORD bck = back; | |
back = fore & 7; | |
fore = bck; | |
#endif | |
attribute = (inverse + underline + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_inverse()"); | |
wget_set_colour (attribute); | |
} | |
static void colour_set_foreground (int col) | |
{ | |
fore = col; | |
attribute = (inverse + underline + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_foreground(%d)", col); | |
wget_set_colour (attribute); | |
} | |
static void colour_set_background (int col) | |
{ | |
back = col; | |
attribute = (inverse + underline + bold + blink + fore + (back << 4)); | |
ANSI_tracef ("colour_set_background(%d)", col); | |
wget_set_colour (attribute); | |
} | |
/* | |
* CSI <n> m: | |
* Sets SGR parameters, including text colour. | |
* After a CSI can be zero or more parameters separated with ;. | |
* With no parameters, CSI m is treated as CSI 0 m (reset / normal). | |
*/ | |
static void set_colours (void) | |
{ | |
/* Ref. https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | |
*/ | |
static const WORD colour_map[] = { | |
0, /* ESC[30m -> Black */ | |
FOREGROUND_RED, /* ESC[31m -> Red */ | |
FOREGROUND_GREEN, /* ESC[32m -> Green */ | |
FOREGROUND_RED | FOREGROUND_GREEN, /* ESC[33m -> Yellow */ | |
FOREGROUND_BLUE, /* ESC[34m -> Blue */ | |
FOREGROUND_RED | FOREGROUND_BLUE, /* ESC[35m -> Magenta */ | |
FOREGROUND_BLUE | FOREGROUND_GREEN, /* ESC[36m -> Cyan */ | |
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN /* ESC[37m -> White */ | |
}; | |
int i, val; | |
for (i = 0; i <= par_num; i++) | |
{ | |
val = param [i]; | |
switch (val) | |
{ | |
case 0: /* reset attributes to default */ | |
case 22: | |
set_default_colours(); | |
break; | |
case 1: | |
colour_set_bold (1); | |
break; | |
case 2: | |
colour_set_bold (0); | |
break; | |
case 4: | |
colour_set_underline (1); | |
break; | |
case 21: | |
colour_set_bold (0); | |
break; | |
case 24: | |
colour_set_underline (0); | |
break; | |
case 25: | |
colour_set_blink (0); | |
break; | |
case 5: | |
case 6: | |
case 26: | |
colour_set_blink (1); | |
break; | |
case 7: | |
colour_set_inverse (1); | |
break; | |
case 8: | |
colour_set_foreground (back); | |
break; | |
default: | |
if (val >= 30 && val <= 37) | |
colour_set_foreground (colour_map[val-30]); | |
else if (val >= 40 && val <= 47) | |
colour_set_background (colour_map[val-40]); | |
} | |
} | |
par_num = 0; | |
} | |
#if defined(ANSI_TEST) | |
#include "ansi-test-1.h" | |
#define CHUNK_SIZE 500 | |
static BOOL WINAPI console_handler (DWORD event); | |
int main (int argc, const char **argv) | |
{ | |
const char *p, *end, *start = (const char*) ansi_test_1; | |
int rc1, total; | |
size_t len; | |
LoadLibrary ("exc-abort.dll"); | |
wget_trace_init(); | |
wget_console_init2(); | |
SetConsoleCtrlHandler (console_handler, TRUE); | |
if (trace_level >= 1) | |
{ | |
printf ("input code page: %u\n", GetConsoleCP()); | |
printf ("output code page: %u\n", GetConsoleOutputCP()); | |
if (p_GetCurrentConsoleFontEx) | |
{ | |
CONSOLE_FONT_INFOEX fi_ex; | |
char family [100] = ""; | |
BOOL rc2; | |
memset (&fi_ex, '\0', sizeof(fi_ex)); | |
fi_ex.cbSize = sizeof(fi_ex); | |
rc2 = (*p_GetCurrentConsoleFontEx) (g_stdout_hnd, FALSE, &fi_ex); | |
if (fi_ex.FontFamily & TMPF_FIXED_PITCH) | |
strcat (family, " Variable pitch"); | |
if (fi_ex.FontFamily & TMPF_VECTOR) | |
strcat (family, ",Vector"); | |
if (fi_ex.FontFamily & TMPF_TRUETYPE) | |
strcat (family, ",TrueType"); | |
if (fi_ex.FontFamily & TMPF_DEVICE) | |
strcat (family, ",Device Font"); | |
printf ("fi_ex.nFont: %lu, rc2: %d/%lu\n", fi_ex.nFont, rc2, !rc2 ? GetLastError() : 0); | |
printf ("fi_ex.dwFontSize: %u, %u\n", fi_ex.dwFontSize.X, fi_ex.dwFontSize.Y); | |
printf ("fi_ex.FontFamily: %u: %s\n", fi_ex.FontFamily, *family ? family+1 : "?"); | |
printf ("fi_ex.FontWeight: %u\n", fi_ex.FontWeight); | |
printf ("fi_ex.FaceName: %S\n", fi_ex.FaceName); | |
if (rc2) | |
{ | |
COORD xy = GetConsoleFontSize (g_stdout_hnd, fi_ex.nFont); | |
printf ("GetConsoleFontSize(): %u, %u\n", xy.X, xy.Y); | |
} | |
} | |
} | |
total = wget_ANSI_printf ("\033]0;%s\7", "ANSI-test demo"); | |
//total += wget_ANSI_printf ("\033]0;%-*s\033", countof(rparam)+10, "Illegal oversized OSC sequence"); | |
len = sizeof(ansi_test_1); | |
end = start + len; | |
for (p = start; p < end; p += CHUNK_SIZE) | |
{ | |
rc1 = wget_ANSI_printf ("%.*s", min(CHUNK_SIZE,len), p); | |
if (rc1 < 0 && !use_vts_mode && !use_ansicon) | |
{ | |
fprintf (stderr, "wget_ANSI_printf() failed at chunk %d. errno: %d\n", | |
1+(p-start)/CHUNK_SIZE, errno); | |
break; | |
} | |
len = min (end-p, CHUNK_SIZE); | |
total += rc1; | |
} | |
if (g_stdout_hnd != INVALID_HANDLE_VALUE) | |
SetConsoleTextAttribute (g_stdout_hnd, g_console_info.wAttributes); | |
fprintf (stderr, "----------------------------------------------\n" | |
"total: %d, sizeof(ansi_test_1): %u.\n", | |
total, (unsigned)sizeof(ansi_test_1)); | |
return (0); | |
} | |
/* | |
* Return the name for the console-events we might receive. | |
*/ | |
static const char *console_event_name (DWORD event) | |
{ | |
return (event == CTRL_C_EVENT ? "CTRL_C_EVENT" : | |
event == CTRL_BREAK_EVENT ? "CTRL_BREAK_EVENT" : | |
event == CTRL_CLOSE_EVENT ? "CTRL_CLOSE_EVENT" : | |
event == CTRL_LOGOFF_EVENT ? "CTRL_LOGOFF_EVENT" : | |
event == CTRL_SHUTDOWN_EVENT ? "CTRL_SHUTDOWN_EVENT" : | |
"UNKNOWN EVENT"); | |
} | |
static BOOL WINAPI console_handler (DWORD event) | |
{ | |
fprintf (g_file_out, "\nGot %s.\n", console_event_name(event)); | |
fflush (g_file_out); | |
if (event == CTRL_CLOSE_EVENT) | |
Sleep (1000); | |
return (FALSE); | |
} | |
#endif | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MSWINDOWS_H | |
#define MSWINDOWS_H | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
#if defined(LIBWGET_STATIC) | |
#define _WGETAPI | |
#elif defined(BUILDING_LIBWGET) | |
#define _WGETAPI __declspec(dllexport) | |
#else | |
#define _WGETAPI __declspec(dllimport) | |
#endif | |
#include <stdio.h> | |
/* Use Gnulibs' 'vasnprintf()' in 'wget_ANSI_vprintf()' | |
*/ | |
#if USE_VASNPRINTF | |
#include <vasnprintf.h> | |
#endif | |
/* | |
* Defined in newer <sal.h> for MSVC. | |
*/ | |
#ifndef _Printf_format_string_ | |
#define _Printf_format_string_ | |
#endif | |
#if defined(__GNUC__) || defined(__clang__) | |
#define _MS_ATTR_PRINTF(_1,_2) __attribute__((format(printf,_1,_2))) | |
#else | |
#define _MS_ATTR_PRINTF(_1,_2) /* nothing */ | |
#endif | |
/* in libwget/mswindows.c: | |
*/ | |
_WGETAPI int wget_console_init2 (void); | |
_WGETAPI int wget_sema_wait (void); | |
_WGETAPI int wget_sema_release (void); | |
_WGETAPI void wget_trace (int level, const char *file, unsigned line, | |
_Printf_format_string_ const char *fmt, ...) | |
_MS_ATTR_PRINTF(4,5); | |
_WGETAPI size_t wget_ANSI_fwrite (const void *buf, size_t elements, size_t len, FILE *const stream); | |
_WGETAPI int wget_ANSI_fputs (const char *string, FILE *const stream); | |
_WGETAPI int wget_ANSI_puts (const char *buf); | |
_WGETAPI int wget_ANSI_printf (_Printf_format_string_ const char *fmt, ...) | |
_MS_ATTR_PRINTF(1,2); | |
_WGETAPI int wget_ANSI_fprintf (FILE *const stream, | |
_Printf_format_string_ char const *const fmt, ...) | |
_MS_ATTR_PRINTF(2,3); | |
_WGETAPI int wget_ANSI_vprintf (_Printf_format_string_ const char *fmt, va_list args) | |
_MS_ATTR_PRINTF(1,0); | |
#if !defined(INSIDE_MSWINDOWS_C) | |
/* | |
* Since these are really 'rpl_XX()' inside Gnulib's <stdio.h>. | |
* Redefine all of them to 'wget_ANSI_XX()' functions that should handle | |
* ANSI-sequences. | |
*/ | |
#undef fwrite | |
#undef fputs | |
#undef puts | |
#undef printf | |
#undef fprintf | |
#define fwrite(buf, elements, len, stream) wget_ANSI_fwrite (buf, elements, len, stream) | |
#define fputs(string, stream) wget_ANSI_fputs (string, stream) | |
#define puts(string) wget_ANSI_puts (string) | |
#define printf(...) wget_ANSI_printf (__VA_ARGS__) | |
#define fprintf(...) wget_ANSI_fprintf (__VA_ARGS__) | |
#endif | |
#undef localtime_r | |
#undef gmtime_r | |
_WGETAPI struct tm *wget_localtime_r (const time_t *_Time, struct tm *_Tm); | |
_WGETAPI struct tm *wget_gmtime_r (const time_t *_Time, struct tm *_Tm); | |
#define localtime_r(time, tm) wget_localtime_r (time, tm) | |
#define gmtime_r(time, tm) wget_gmtime_r (time, tm) | |
/* in libwget/console.c: | |
*/ | |
extern HANDLE g_stdout_hnd; | |
extern CONSOLE_SCREEN_BUFFER_INFO g_console_info; | |
#ifndef TRACE_COLOR_START | |
#define TRACE_COLOR_START (FOREGROUND_INTENSITY | 2) /* bright green */ | |
#endif | |
#ifndef TRACE_COLOR_ARGS | |
#define TRACE_COLOR_ARGS (FOREGROUND_INTENSITY | 7) /* bright white */ | |
#endif | |
#define WGET2_TRACE(level, fmt, ...) \ | |
wget_trace (level, __FILE__, __LINE__, fmt, ## __VA_ARGS__) | |
#if defined(USE_WSAPOLL) && 0 | |
/* | |
* Use Winsock's 'WSAPoll()' here since 'poll() in GnuLib does not work so well. | |
* Without a 'HAVE_POLL', causes Wget2 to use 'select()' in libwget/io.c. | |
* | |
* Will prevent including <poll.h> | |
*/ | |
#define _GNULIB_POLL_H 1 | |
#define HAVE_POLL 1 | |
#undef poll /* just in case */ | |
static int _GL_INLINE poll (struct pollfd *p, int num, int timeout) | |
{ | |
p->fd = _get_osfhandle (p->fd); | |
return WSAPoll (p, num, timeout); | |
} | |
#endif | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment