1
0
mirror of https://gitlab.com/80486DX2-66/gists synced 2024-12-26 11:30:03 +05:30
gists/c-programming/strings/portable_basename.c

259 lines
6.1 KiB
C

/*
* portable_basename.c
*
* Author: Intel A80486DX2-66
* License: Unlicense
*/
#include "portable_basename.h"
static char* correct_slashes(const char* path) {
char* new_path = strdup(path);
if (new_path == NULL)
return NULL;
char* ptr = new_path;
while (*ptr != '\0') {
if (*ptr == '\\')
*ptr = '/';
ptr++;
}
return new_path;
}
char* portable_basename(const char* raw_path) {
char* path = correct_slashes(raw_path);
if (path == NULL)
return NULL;
char* base = malloc(FILENAME_MAX * sizeof(char));
if (base == NULL) {
free(path);
return NULL;
}
size_t fname_len = strlen(path);
const char* last_slash = strrchr(path, '/');
if (last_slash != NULL)
fname_len = strlen(last_slash + 1);
memcpy(base, last_slash + 1, fname_len);
base[fname_len] = '\0';
return base;
}
#ifdef TEST
# include <inttypes.h>
# include <stdbool.h>
# include <stdio.h>
# include <time.h>
// system identification
# ifdef _WIN32
# define SYS_NT
# include <windows.h>
# endif
# if defined(__unix__) || defined(__APPLE__)
# define SYS_UNIX
# endif
// macros
# if defined(SYS_UNIX) && !defined(CLOCK_MONOTONIC_RAW)
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
# endif
// function prototypes
long double clock_hr_microsec(void);
# ifdef SYS_NT
void WinAPI_perror(const char* s);
# endif
static void func_expect(const char* path, const char* expected_output,
bool expect_to_succeed);
long double clock_hr_microsec(void) {
# ifdef SYS_NT
// TODO: FIXME: less accurate than Unix
LARGE_INTEGER counter;
if (!QueryPerformanceCounter(&counter)) {
WinAPI_perror("QueryPerformanceCounter");
exit(EXIT_FAILURE);
}
return (long double) (counter.QuadPart * 1000000ULL);
# elif SYS_UNIX
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
return ((long double) (ts.tv_sec * 1000000000ULL + ts.tv_nsec)) / 1000.l;
# else
return (long double) (clock() * 1000000ULL);
# endif
}
// macros
# ifdef SYS_NT
# define WinAPI_perror_COMMON_MACRO \
fprintf(stderr, "%s: %lu, %s", s, errorCode, errorString)
// function implementations
void WinAPI_perror(const char* s) {
DWORD errorCode = GetLastError();
LPSTR errorString = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&errorString,
0,
NULL
);
if (errorString == NULL) {
errorString = "???\n";
WinAPI_perror_COMMON_MACRO;
} else {
WinAPI_perror_COMMON_MACRO;
LocalFree(errorString);
}
}
# endif
// macros
# if defined(SYS_NT)
LONGLONG QPF_frequency;
# define CLOCK_HR_DIFFTIME_MS(t2, t1) \
((long double) ((t2) - (t1))) / ((long double) QPF_frequency)
# elif defined(SYS_UNIX)
# define CLOCK_HR_DIFFTIME_MS(t2, t1) \
((t2) - (t1))
# else
# define CLOCK_HR_DIFFTIME_MS(t2, t1) \
((long double) ((t2) - (t1)) * 1000000.L) / \
((long double) CLOCKS_PER_SEC)
# endif
// global variables
uintmax_t tests_failed = 0;
// function implementations
static void func_expect(const char* path, const char* expected_output,
bool expect_to_succeed) {
long double t1 = 0, t2 = 0;
t1 = clock_hr_microsec();
char* output = portable_basename(path);
t2 = clock_hr_microsec();
if (output == NULL) {
perror("portable_basename");
exit(EXIT_FAILURE);
}
const char* caption = expect_to_succeed ? "Expected output "
: "Unexpected output";
long double t = CLOCK_HR_DIFFTIME_MS(t2, t1);
printf("Input path : '%s'\n"
"%s: '%s'\n"
"Actual output : '%s'\n"
"Time, clock_t : %.3Lf microsec\n"
"Test result : ", path, caption, expected_output, output, t);
if ((bool) (strcmp(output, expected_output)) == expect_to_succeed) {
tests_failed++;
free(output);
puts("Failed!\n");
return;
}
puts("Passed\n");
free(output);
fflush(stdout);
}
// macros
#define STRINGIZE(x) #x
#define INT2STR(x) STRINGIZE(x)
#define TIMES_TO_CALL 65535
#include<unistd.h>
int main(void) {
# ifdef SYS_NT
// set console output code page to the native one
SetConsoleOutputCP(GetACP());
// get CPU frequency
LARGE_INTEGER freq;
if (!QueryPerformanceFrequency(&freq)) {
WinAPI_perror("QueryPerformanceFrequency");
return EXIT_FAILURE;
}
QPF_frequency = freq.QuadPart;
# endif
// positive tests
func_expect("/", "", true);
func_expect("/usr/include/stdlib.h", "stdlib.h", true);
func_expect("C:\\Windows\\Fonts\\consola.ttf", "consola.ttf", true);
func_expect("\\..\\..\\directory\\.e.x.e.c.u.t.a.b.l.e.",
".e.x.e.c.u.t.a.b.l.e.", true);
// negative tests
func_expect("/a/.b.c.", "b.c", false);
func_expect("/a/.b.c.", ".b.c", false);
func_expect("/a/.b.c.", "b.c.", false);
func_expect("/a/.b.c.\\", ".b.c.", false);
printf("Failed tests: %" PRIuMAX "\n", tests_failed);
// timing test
puts("\nBenchmarking (" INT2STR(TIMES_TO_CALL) " calls):");
fflush(stdout);
#define BENCHMARKING_STRING_SET \
"a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r\\s\\t\\u\\v\\w\\x\\y\\z\\.\\file", \
"/mnt/cdrom/usr/bin/include/library/test_files/test.h"
const char* string_set[] = {
BENCHMARKING_STRING_SET,
// HACK: try to prevent caching by using different pointers
BENCHMARKING_STRING_SET,
BENCHMARKING_STRING_SET,
BENCHMARKING_STRING_SET
};
const size_t string_set_len = sizeof(string_set) / sizeof(char*);
long double t_beginning = 0, t_end = 0;
long double t_total = 0.l;
for (uint_fast16_t i = 0; i < TIMES_TO_CALL; i++) {
const char* selected_string = string_set[i % string_set_len];
t_beginning = clock_hr_microsec();
char* output = portable_basename(selected_string);
t_end = clock_hr_microsec();
if (output == NULL) {
perror("portable_basename");
return EXIT_FAILURE;
}
free(output);
t_total += CLOCK_HR_DIFFTIME_MS(t_end, t_beginning);
}
printf("Total time: %.3Lf microsec, avg. %.3Lf microsec\n", t_total,
t_total / ((long double) TIMES_TO_CALL));
return (tests_failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
#endif /* TEST */