Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active January 15, 2021 12:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gvanem/4290a4c74ac75983928502303cb4c0c2 to your computer and use it in GitHub Desktop.
Save gvanem/4290a4c74ac75983928502303cb4c0c2 to your computer and use it in GitHub Desktop.
A Gnu-makefile to generate a file (curl_opt_err.c) with all current 'CURLOPT_x' and 'CURLE_x' values.
#
# A Gnu-makefile to generate a C-program ($(PROGRAM).c) with all
# current 'CURLOPT_x' and 'CURLE_x' values.
#
# NB! must be invoked from libcurl's './lib' directory.
#
# By G. Vanem <gvanem@yahoo.no> 2014 - 2018.
#
# The basename of what this makefile generates;
# A 'curl_opt_err.c' and a 'curl_opt_err.exe' (on Windows).
# Should work on non-Windows too; will produce a 'curl_opt_err'
# by the below '$(PROGRAM)$(EXE):' rule.
#
# To use MSVC, do a:
# c:\curl\lib> make -f mk-opt-err-list.mak CC=cl
#
# Or for 'clang-cl', do a:
# c:\curl\lib> make -f mk-opt-err-list.mak CC=clang-cl
#
# Or for 'gcc', do a:
# c:\curl\lib> make -f mk-opt-err-list.mak CC=gcc
#
PROGRAM = curl_opt_err
THIS_FILE = $(MAKEFILE_LIST)
#
# The following assumes you have a MSys/Cygwin 'echo.exe' program with colour support.
# On non-Windows that should be no issue (?)
#
BRIGHT_GREEN = \e[1;32m
colour_msg = @echo -e "$(1)\e[0m"
green_msg = $(call colour_msg,$(BRIGHT_GREEN)$(strip $(1)))
#
# Extract all 'CURLOPT()' macros in '../include/curl/curl.h' and
# build a list of *current* 'CURLOPT_x' using the 'ADD_CURRENT_OPT()' macro.
#
OPTIONS = grep '^\ \ CURLOPT(' ../include/curl/curl.h | cut -f1 -d, | sed 's/CURLOPT(//'
OPTIONS := $(shell $(OPTIONS))
#
# Extract all 'CURLE_x' enums in '../include/curl/curl.h' and
# build a list for the below 'ADD_ERR()' loop.
#
ERRORS = grep --only-matching '^\ \ CURLE_[_A-Z0-9]*' ../include/curl/curl.h
ERRORS := $(shell $(ERRORS))
#
# Count all '#define CURL' lines generated in '$(PROGRAM).symbols'.
#
NUM_SYMBOLS = $(shell grep -c --only-matching '^\#define CURL' $(PROGRAM).symbols)
#
# List obsolete '#define CURLOPT_x' lines generated in '$(PROGRAM).symbols'.
# I.e. 'x_LAST' was last featured in version x.y.z.
#
CURLOPT_OBSOLETE = $(shell grep --only-matching '^\#define CURLOPT_.*_LAST' $(PROGRAM).symbols | cut -f2 -d' ')
define Usage
Usage: "$(MAKE) -f mk-opt-err-list.mak CC=[cl | clang-cl | gcc] [all | clean]"
endef
#
# Check the given $(CC).
#
ifeq ($(CC),gcc)
CFLAGS = -Wall
LDFLAGS =
else ifeq ($(CC),cl)
CFLAGS = -nologo -MD -Zi
MSVC_STYLE = 1
else ifeq ($(CC),clang-cl)
export CL=
CFLAGS = -nologo -MD -Zi -W4 -Wno-microsoft-include
MSVC_STYLE = 1
else
$(error $(Usage))
endif
CFLAGS += -I../include
ifeq ($(OS),Windows_NT)
EXE = .exe
ifeq ($(CC),gcc)
CFLAGS += -m32
LDFLAGS = -m32 # Assumes a dual-mode MinGW
endif
endif
ifeq ($(MSVC_STYLE),1)
LDFLAGS = -nologo -incremental:no
endif
define MAIN_PROGRAM_TOP
/*
* This file has been generated by $(realpath $(THIS_FILE)) at
* $(shell date +%d-%B-%Y). DO NOT EDIT!
*/
#include "curl_setup.h"
/**
* "$(PROGRAM).symbols" is generated by 'perl ../docs/libcurl/symbols.pl'.
*
* \todo put this in a search-list to search for obsolete symbols.
* '!LIBCURL_HAS(sym)' -> 0, a symbol ending in '_LAST'.
*/
#include "$(PROGRAM).symbols"
/* No version info for these.
*/
#define CURLOPT_OBSOLETE40_FIRST 0
#define CURLOPT_OBSOLETE72_FIRST 0
#define CURLE_OBSOLETE20_FIRST 0
#define CURLE_OBSOLETE24_FIRST 0
#define CURLE_OBSOLETE29_FIRST 0
#define CURLE_OBSOLETE32_FIRST 0
#define CURLE_OBSOLETE40_FIRST 0
#define CURLE_OBSOLETE44_FIRST 0
#define CURLE_OBSOLETE46_FIRST 0
#define CURLE_OBSOLETE50_FIRST 0
#define CURLE_OBSOLETE51_FIRST 0
#define CURLE_OBSOLETE57_FIRST 0
/* # of CURLE_OBSOLETE codes.
*/
static unsigned num_obsoleted_err = 0;
struct key_val {
unsigned val;
const char *name;
unsigned added_ver; /* In what version was it added? (hex-encoded) */
unsigned removed_ver; /* In what version was it removed? (hex-encoded) */
const char *man3_page;
};
#if defined(_WIN32)
#include <io.h>
#define FILE_EXIST(fname) (access(fname,0) == 0)
#define SLASH '\\'
#else
#include <unistd.h>
#define FILE_EXIST(fname) (chmod(fname,0) != -1)
#define SLASH '/'
#endif
static char our_dir [1000]; /* The directory of '$(PROGRAM)$(EXE)' */
/* Since there seems to be no obsolete 'CURLE_x' values, there are no 'CURLE_x_LAST'.
*/
#define ADD_ERR(err) { err, #err, err ##_FIRST, 0, NULL }
#define ADD_CURRENT_OPT(opt) { opt, #opt, opt ##_FIRST, 0, "../docs/libcurl/opts/" #opt ".3" }
#define ADD_OBSOLETE_OPT(opt) { opt, #opt, 0, opt, "../docs/libcurl/opts/" #opt ".3" }
#define DIM(array) (int) (sizeof(array) / sizeof(array[0]))
/** \todo print obsoleted symbols as e.g. '<7.1.1' ?
*/
static const char *decode_version (const struct key_val *kv, int added)
{
static char ver_str [20];
unsigned ver = added ? kv->added_ver : kv->removed_ver;
if (ver == 0 /* || !_libcurl_has(kv->val) */)
return ("");
snprintf (ver_str, sizeof(ver_str), "%d.%d.%d", (ver >> 16) & 255, (ver & 0xFF00) >> 8, ver & 255);
return (ver_str);
}
#if 0
static const char *removed_in_version (struct key_val *kv)
{
static ver_str [20];
unsigned ver = kv->removed_ver;
if (!_libcurl_has(kv->val))
snprintf (ver_str, sizeof(ver_str), "%-6s", decode_version(kv,0));
else strcpy (ver_str, "");
return (ver_str);
}
#endif
endef
define MAIN_PROGRAM_BOTTOM
static const char *decode_err (const struct key_val *e)
{
if (!strncmp(e->name, "CURLE_OBSOLETE", sizeof("CURLE_OBSOLETE")-1))
{
num_obsoleted_err++;
return ("<Historic, obsolete>");
}
return curl_easy_strerror ((CURLcode)e->val);
}
static void print_all_CURLEs (void)
{
const struct key_val *e = errors;
int i;
puts ("\nCURLE_x value version curl_easy_strerror()\n"
"==================================================================");
for (i = 0; i < DIM(errors); i++, e++)
printf ("%-30.30s %2u %-6s %s\n",
e->name, e->val, decode_version(e,1), decode_err(e));
}
static const char *decode_opt (unsigned opt)
{
static char ret[100];
const char *type = "long"; /* CURLOPTTYPE_LONG == 0 */
if (opt >= CURLOPTTYPE_OFF_T)
{
opt -= CURLOPTTYPE_OFF_T;
type = "off_t";
}
else if (opt >= CURLOPTTYPE_FUNCTIONPOINT)
{
opt -= CURLOPTTYPE_FUNCTIONPOINT;
type = "function";
}
else if (opt >= CURLOPTTYPE_OBJECTPOINT)
{
opt -= CURLOPTTYPE_OBJECTPOINT;
type = "object/string";
}
snprintf (ret, sizeof(ret), "%3u: %s", opt, type);
return (ret);
}
static int man3_page_exist (const struct key_val *o, const char **expected_name)
{
static char file [1000];
*expected_name = NULL;
if (!o->man3_page)
return (0);
if (!strncmp(o->name,"CURLOPT_OBSOLETE",16)) /* Ignore these */
return (1);
snprintf (file, sizeof(file), "%s/%s", our_dir, o->man3_page);
*expected_name = file;
return FILE_EXIST (file);
}
static void print_all_CURLOPTs (void)
{
const struct key_val *o = current_options;
const char *p;
int i;
puts ("Idx CURLOPT_x value: arg type raw val version\n"
"================================================================================");
for (i = 0; i < DIM(current_options); i++, o++)
{
const char *expected_3name;
int exists = man3_page_exist (o, &expected_3name);
printf ("%3d: %-35.35s -> %-20s %5u %-6s",
i, o->name, decode_opt(o->val), o->val, decode_version(o,1));
if (!exists)
printf (" missing '%s'", expected_3name);
putchar ('\n');
}
puts ("\nThese are obsolete:");
o = obsolete_options;
for (i = 0; i < DIM(obsolete_options); i++, o++)
{
size_t last_len = strlen(o->name);
p = strrchr (o->name, '_');
if (p && !strcmp(p,"_LAST"))
last_len -= sizeof("_LAST") - 1;
printf ("%3d: %-67.*s %-6s\n",
i, (int)last_len, o->name, decode_version(o,0));
}
}
static int do_help (void)
{
const char *help = " -o: print all CURLOPT_x values.\n" $\\
" -e: print all CURLE_x values.\n";
printf ("Usage: %s%c%s\n%s", our_dir, SLASH, "$(PROGRAM)$(EXE)", help);
return (0);
}
int main (int argc, char **argv)
{
char *slash = strrchr (argv[0], SLASH);
if (slash)
{
strncpy (our_dir, argv[0], (size_t)(slash-argv[0]));
*slash = '\0';
}
else
strcpy (our_dir, ".");
if (argc != 2 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help") )
return do_help();
if (!strcmp(argv[1],"-o"))
print_all_CURLOPTs();
else if (!strcmp(argv[1],"-e"))
print_all_CURLEs();
printf ("\nCurrent ver: %s.\n", LIBCURL_VERSION);
printf ("Obsolete symbols: CURLOPT: %u\n"
" CURLE: %u.\n",
DIM(obsolete_options), num_obsoleted_err);
return (0);
}
endef
all: check_libdir $(PROGRAM)$(EXE)
$(PROGRAM).c: make_symbols $(THIS_FILE)
$(file > $@,$(MAIN_PROGRAM_TOP))
$(file >> $@, static const struct key_val current_options[] = {)
$(foreach o, $(sort $(OPTIONS)), \
$(file >> $@, ADD_CURRENT_OPT ($(o)),))
$(file >> $@, };)
$(file >> $@,)
$(file >> $@, static const struct key_val obsolete_options[] = {)
$(foreach o, $(sort $(CURLOPT_OBSOLETE)), \
$(file >> $@, ADD_OBSOLETE_OPT ($(o)),))
$(file >> $@, };)
$(file >> $@,)
$(file >> $@, static const struct key_val errors[] = {)
$(foreach e, $(ERRORS), \
$(file >> $@, ADD_ERR ($(e)), ) )
$(file >> $@, };)
$(file >> $@,$(MAIN_PROGRAM_BOTTOM))
$(call green_msg, $(words $(OPTIONS)) CURLOPT_x values generated for $@.)
$(call green_msg, $(words $(ERRORS)) CURLE_x values generated for $@.)
make_symbols: $(PROGRAM).symbols
$(call green_msg, $(NUM_SYMBOLS) CURL symbols generated for $<.)
@echo 'These are obsolete:'
@$(foreach o, $(CURLOPT_OBSOLETE), \
echo ' $(o)' ;)
$(PROGRAM).symbols: $(THIS_FILE)
$(call green_msg, Generating $@.)
@echo -ne "// Generated by 'perl ../docs/libcurl/symbols.pl'.\n// DO NOT EDIT.\n" > $@
(cd ../docs/libcurl; perl symbols.pl) >> $@
$(PROGRAM)$(EXE): $(PROGRAM).c
$(call green_msg, Compiling and linking $@.)
$(call compile_and_link, $@, $<)
@echo '$@ done.'
check_libdir:
ifeq ($(wildcard curl_setup.h),)
$(error Must be invoked from libcurl's './lib' directory.)
endif
ifeq ($(MSVC_STYLE),1)
define compile_and_link
$(CC) -c $(CFLAGS) $(2)
link -out:$(strip $(1)) $(LDFLAGS) $(2:.c=.obj) libcurl_imp.lib
rm -f $(2:.c=.obj)
endef
else
compile_and_link = $(CC) -o $(1) $(CFLAGS) $(LDFLAGS) $(2) -L . -lcurl
endif
clean:
rm -f $(PROGRAM).*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment