在一些项目中,我们需要在项目的bin目录中生成可执行文件,除了参数处理外,有个很重要的过程就是获取程序名,如下是比较严谨的获取程序名的方式,来源于PostgreSQL。
/*
* Extracts the actual name of the program as called stripped of .exe suffix if any
*/
const char *get_progname(const char *argv0)
{
const char *nodir_name;
char *progname;
nodir_name = last_dir_separator(argv0);
if (nodir_name)
nodir_name++;
else
nodir_name = skip_drive(argv0);
/*
* Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
* called only once.
*/
progname = strdup(nodir_name);
if (progname == NULL)
{
fprintf(stderr, "%s: out of memory\n", nodir_name);
abort(); /* This could exit the postmaster */
}
#if defined(__CYGWIN__) || defined(WIN32)
/* strip ".exe" suffix, regardless of case */
if (strlen(progname) > sizeof(EXE) - 1 &&
pg_strcasecmp(progname + strlen(progname) - (sizeof(EXE) - 1), EXE) == 0)
progname[strlen(progname) - (sizeof(EXE) - 1)] = '\0';
#endif
return progname;
}
/*
* last_dir_separator
*
* Find the location of the last directory separator, return
* NULL if not found.
*/
char *last_dir_separator(const char *filename)
{
const char *p,
*ret = NULL;
for (p = skip_drive(filename); *p; p++)
if (IS_DIR_SEP(*p))
ret = p;
return unconstify(char *, ret);
}
/*
* skip_drive
*
* On Windows, a path may begin with "C:" or "//network/". Advance over
* this and point to the effective start of the path.
*/
#ifdef WIN32
static char *skip_drive(const char *path)
{
if (IS_DIR_SEP(path[0]) && IS_DIR_SEP(path[1]))
{
path += 2;
while (*path && !IS_DIR_SEP(*path))
path++;
}
else if (isalpha((unsigned char) path[0]) && path[1] == ':')
{
path += 2;
}
return (char *) path;
}
#else
#define skip_drive(path) (path)
#endif
/*
* is_absolute_path and IS_DIR_SEP
*
* By using macros here we avoid needing to include path.c in libpq.
*/
#ifndef WIN32
#define IS_DIR_SEP(ch) IS_NONWINDOWS_DIR_SEP(ch)
#define is_absolute_path(filename) is_nonwindows_absolute_path(filename)
#else
#define IS_DIR_SEP(ch) IS_WINDOWS_DIR_SEP(ch)
#define is_absolute_path(filename) is_windows_absolute_path(filename)
#endif
#define IS_NONWINDOWS_DIR_SEP(ch) ((ch) == '/')
#define is_nonwindows_absolute_path(filename) \
( \
IS_NONWINDOWS_DIR_SEP((filename)[0]) \
)
#define IS_WINDOWS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\')
/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */
#define is_windows_absolute_path(filename) \
( \
IS_WINDOWS_DIR_SEP((filename)[0]) || \
(isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \
IS_WINDOWS_DIR_SEP((filename)[2])) \
)
/*
* Macro that allows to cast constness and volatile away from an expression, but doesn't
* allow changing the underlying type. Enforcement of the latter
* currently only works for gcc like compilers.
*
* Please note IT IS NOT SAFE to cast constness away if the result will ever
* be modified (it would be undefined behaviour). Doing so anyway can cause
* compiler misoptimizations or runtime crashes (modifying readonly memory).
* It is only safe to use when the result will not be modified, but API
* design or language restrictions prevent you from declaring that
* (e.g. because a function returns both const and non-const variables).
*
* Note that this only works in function scope, not for global variables (it'd
* be nice, but not trivial, to improve that).
*/
#if defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P)
#define unconstify(underlying_type, expr) \
(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), const underlying_type), \
"wrong cast"), \
(underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), volatile underlying_type), \
"wrong cast"), \
(underlying_type) (expr))
#else
#define unconstify(underlying_type, expr) \
((underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
((underlying_type) (expr))
#endif
/*
* Macros to support compile-time assertion checks.
*
* If the "condition" (a compile-time-constant expression) evaluates to false,
* throw a compile error using the "errmessage" (a string literal).
*
* gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic
* placement restrictions. These macros make it safe to use as a statement
* or in an expression, respectively.
*
* Otherwise we fall back on a kluge that assumes the compiler will complain
* about a negative width for a struct bit-field. This will not include a
* helpful error message, but it beats not getting an error at all.
*/
#ifndef __cplusplus
#ifdef HAVE__STATIC_ASSERT
#define StaticAssertStmt(condition, errmessage) \
do { _Static_assert(condition, errmessage); } while(0)
#define StaticAssertExpr(condition, errmessage) \
((void) ({ StaticAssertStmt(condition, errmessage); true; }))
#else /* !HAVE__STATIC_ASSERT */
#define StaticAssertStmt(condition, errmessage) \
((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
#define StaticAssertExpr(condition, errmessage) \
StaticAssertStmt(condition, errmessage)
#endif /* HAVE__STATIC_ASSERT */
#else /* C++ */
#if defined(__cpp_static_assert) && __cpp_static_assert >= 200410
#define StaticAssertStmt(condition, errmessage) \
static_assert(condition, errmessage)
#define StaticAssertExpr(condition, errmessage) \
({ static_assert(condition, errmessage); })
#else
#define StaticAssertStmt(condition, errmessage) \
do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0)
#define StaticAssertExpr(condition, errmessage) \
((void) ({ StaticAssertStmt(condition, errmessage); }))
#endif
#endif /* C++ */
/*
* Case-independent comparison of two null-terminated strings.
*/
int
pg_strcasecmp(const char *s1, const char *s2)
{
for (;;)
{
unsigned char ch1 = (unsigned char) *s1++;
unsigned char ch2 = (unsigned char) *s2++;
if (ch1 != ch2)
{
if (ch1 >= 'A' && ch1 <= 'Z')
ch1 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch1) && isupper(ch1))
ch1 = tolower(ch1);
if (ch2 >= 'A' && ch2 <= 'Z')
ch2 += 'a' - 'A';
else if (IS_HIGHBIT_SET(ch2) && isupper(ch2))
ch2 = tolower(ch2);
if (ch1 != ch2)
return (int) ch1 - (int) ch2;
}
if (ch1 == 0)
break;
}
return 0;
}
/* msb for char */
#define HIGHBIT (0x80)
#define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT)
关于静态编译检查:
#include <stdio.h>
#include <stdbool.h>
#define StaticAssertStmt(condition, errmessage) \
do { _Static_assert(condition, errmessage); } while(0)
#define StaticAssertExpr(condition, errmessage) \
((void) ({ StaticAssertStmt(condition, errmessage); true; }))
#define unconstify(underlying_type, expr) \
(StaticAssertExpr(__builtin_types_compatible_p(__typeof__(expr), const underlying_type), \
"wrong cast"), \
(underlying_type)(expr))
int main() {
const int a = 42;
int b = unconstify(int, a); // 通过静态编译检查
printf("b = %d\n", b);
// 下面这行会导致静态编译错误,因为double和int类型不兼容
double c = unconstify(double, a); // 静态编译检查失败
return 0;
}