mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
194 lines
4.9 KiB
C
194 lines
4.9 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 2001-2002, 2006 The University of Melbourne.
|
|
// Copyright (C) 2014, 2016, 2018, 2023 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// This module contains utility functions for the rest of the Mercury runtime.
|
|
//
|
|
// Author: petdr
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_runtime_util.h"
|
|
#include "mercury_string.h"
|
|
#include "mercury_windows.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#if defined(MR_WIN32) && !defined(MR_CYGWIN)
|
|
#include <wchar.h>
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
static void
|
|
generic_strerror(char *buf, size_t buflen, int errnum)
|
|
{
|
|
MR_snprintf(buf, buflen, "Error %d", errnum);
|
|
}
|
|
|
|
const char *
|
|
MR_strerror(int errnum, char *buf, size_t buflen)
|
|
{
|
|
#if defined(MR_HAVE_STRERROR_S) && !defined(MR_MINGW)
|
|
// MSVC has strerror_s. It also exists in C11 Annex K and is enabled by
|
|
// defining a preprocessor macro __STDC_WANT_LIB_EXT1__
|
|
//
|
|
// On MinGW-w64, strerror_s results in an undefined reference to strerror_s
|
|
// in MSVCRT.DLL on Windows XP. Avoid it until we drop support for XP.
|
|
|
|
if (strerror_s(buf, buflen, errnum) != 0) {
|
|
generic_strerror(buf, buflen, errnum);
|
|
}
|
|
return buf;
|
|
#elif defined(MR_HAVE_STRERROR_R)
|
|
// The XSI-compliant and Mac OS X strerror_r populates buf unless it fails.
|
|
// The GNU-specific strerror_r does not always populate buf.
|
|
|
|
#if !defined(__GNU_LIBRARY__) || \
|
|
((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE)
|
|
int x = strerror_r(errnum, buf, buflen);
|
|
if (x != 0) {
|
|
generic_strerror(buf, buflen, errnum);
|
|
}
|
|
return buf;
|
|
#else
|
|
const char *s = strerror_r(errnum, buf, buflen);
|
|
return s;
|
|
#endif
|
|
#else
|
|
// Fallback using deprecated variables. This is used on MinGW at least.
|
|
//
|
|
// strerror_l is another thread-safe alternative, specified in POSIX.
|
|
// It is locale-sensitive and takes a locale argument so we don't use it
|
|
// for now.
|
|
|
|
if (errnum >= 0 && errnum < sys_nerr && sys_errlist[errnum] != NULL) {
|
|
return sys_errlist[errnum];
|
|
} else {
|
|
generic_strerror(buf, buflen, errnum);
|
|
return buf;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FILE *
|
|
MR_checked_fopen(const char *filename, const char *message, const char *mode)
|
|
{
|
|
FILE *file;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
errno = 0;
|
|
file = fopen(filename, mode);
|
|
if (file == NULL) {
|
|
fprintf(stderr, "Mercury runtime: couldn't %s file `%s': %s\n",
|
|
message, filename, MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return file;
|
|
}
|
|
|
|
void
|
|
MR_checked_fclose(FILE *file, const char *filename)
|
|
{
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
errno = 0;
|
|
if (fclose(file) != 0) {
|
|
fprintf(stderr, "Mercury runtime: error closing file `%s': %s\n",
|
|
filename, MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_checked_atexit(void (*func)(void))
|
|
{
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
errno = 0;
|
|
if (atexit(func) != 0) {
|
|
fprintf(stderr, "Mercury runtime: error in call to atexit: %s\n",
|
|
MR_strerror(errno, errbuf, sizeof(errbuf)));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
#if ! defined(MR_HAVE_PUTENV) && defined(MR_HAVE__PUTENV)
|
|
#define putenv _putenv
|
|
#endif
|
|
|
|
int
|
|
MR_setenv(const char *name, const char *value, int overwrite)
|
|
{
|
|
#if defined(MR_HAVE_SETENV)
|
|
return setenv(name, value, overwrite);
|
|
#elif defined(MR_HAVE_PUTENV) || defined(MR_HAVE__PUTENV)
|
|
char *env;
|
|
int length;
|
|
int result;
|
|
|
|
if (!overwrite && getenv(name) != NULL) {
|
|
return 0;
|
|
}
|
|
|
|
length = strlen(name) + strlen(value) + 2;
|
|
env = MR_NEW_ARRAY(char, length);
|
|
|
|
env[0] = '\0';
|
|
strcat(env, name);
|
|
strcat(env, "=");
|
|
strcat(env, value);
|
|
|
|
result = putenv(env);
|
|
|
|
MR_free(env);
|
|
|
|
return result;
|
|
#else
|
|
#error "MR_setenv: unable to define"
|
|
#endif
|
|
}
|
|
|
|
const char *
|
|
MR_get_program_basename(const char *program_name)
|
|
{
|
|
const char *basename;
|
|
|
|
#if defined(MR_WIN32) && !defined(MR_CYGWIN)
|
|
|
|
wchar_t wname[_MAX_FNAME];
|
|
|
|
errno_t err = _wsplitpath_s(MR_utf8_to_wide(program_name),
|
|
NULL, 0, // Ignore drive.
|
|
NULL, 0, // Ignore directories.
|
|
wname, _MAX_FNAME,
|
|
NULL, 0 // Ignore .exe extension.
|
|
);
|
|
if (err != 0) {
|
|
MR_fatal_error("MR_get_program_basename: could not split path");
|
|
}
|
|
basename = MR_wide_to_utf8(wname, NULL);
|
|
|
|
#else
|
|
|
|
// NOTE: on Cygwin, the .exe extension, if present, will not be
|
|
// included in argv[0], so we do not need to strip it here.
|
|
|
|
const char *slash;
|
|
|
|
basename = MR_copy_string(program_name);
|
|
slash = strrchr(basename, '/');
|
|
if (slash != NULL) {
|
|
basename = slash + 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
return basename;
|
|
}
|