Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active April 26, 2023 22:20
Show Gist options
  • Save gvanem/6e574d133106e5d99f6dba1d80e3702b to your computer and use it in GitHub Desktop.
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'.
#!/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))
#
# GNU Makefile for Wget2 (MSVC and clang-cl).
# 32-bit only.
#
# By <gvanem@yahoo.no> 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], [bug-wget@gnu.org], [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 <gvanem@yahoo.no>."
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
/*
* 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 (&param, 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
#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