/* General system functions
Copyright 2003-2005 Craig Franklin
Copyright 2015-2016 Molnár Károly
*/
#include "stdhdr.h"
#include "libgputils.h"
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#ifdef STDC_HEADERS
#include <stdarg.h>
#endif
const char *gp_header_path;
const char *gp_lkr_path;
const char *gp_lib_path;
bool absolute_path_warning = true;
/*------------------------------------------------------------------------------------------------*/
/* initialize the library */
FUNC(void) gp_init(void) {
/* load the environmental variables */
gp_header_path = getenv("GPUTILS_HEADER_PATH");
gp_lkr_path = getenv("GPUTILS_LKR_PATH");
gp_lib_path = getenv("GPUTILS_LIB_PATH");
}
/*------------------------------------------------------------------------------------------------*/
/* little endian functions */
void gp_fputl16(int Data, FILE *Fp) {
assert(-32678<=Data && Data<32768);
fwrite(&Data,1,2,Fp);
}
/*------------------------------------------------------------------------------------------------*/
void gp_fputl32(int Data, FILE *Fp) {
fwrite(&Data,1,4,Fp);
}
/*------------------------------------------------------------------------------------------------*/
void gp_fputzero(size_t Number, FILE *Fp) {
while (Number > 0) {
fputc(0, Fp);
--Number;
}
}
/*------------------------------------------------------------------------------------------------*/
void gp_fputvar(const void *Data, size_t Number, FILE *Fp) {
const uint8_t *d;
size_t i;
d = (const uint8_t *)Data;
for (i = 0; i < Number; i++) {
fputc(d[i], Fp);
}
}
/* big endian functions */
FUNC(int32_t) gp_getb32(const uint8_t *Addr) {
int32_t value;
value = (int32_t)Addr[0] << 24;
value |= (int32_t)Addr[1] << 16;
value |= (int32_t)Addr[2] << 8;
value |= (int32_t)Addr[3];
return value;
}
/*------------------------------------------------------------------------------------------------*/
// needed by gpcod.c
void gp_putb32(uint8_t *Addr, uint32_t Data) {
Addr[0] = (Data >> 24) & 0xff;
Addr[1] = (Data >> 16) & 0xff;
Addr[2] = (Data >> 8) & 0xff;
Addr[3] = Data & 0xff;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(bool) gp_num_range_is_overlapped(int Area_start, int Area_end, int Ref_start, int Ref_end) {
int min;
int max;
int w0;
int w1;
int width0;
int width1;
if (Area_start > Area_end) {
/* Swap these values. */
int w0 = Area_start;
Area_start = Area_end;
Area_end = w0;
}
if (Ref_start > Ref_end) {
/* Swap these values. */
int w0 = Ref_start;
Ref_start = Ref_end;
Ref_end = w0;
}
min = (Area_start < Ref_start) ? Area_start : Ref_start;
max = (Area_end > Ref_end) ? Area_end : Ref_end;
/*
min Area max
| | |
V V Ref |
+----------+ | |
w0 V V
+-----------------+
w1
+---------------------------------+
width0
*/
w0 = Area_end - Area_start + 1;
w1 = Ref_end - Ref_start + 1;
width0 = max - min + 1;
width1 = w0 + w1;
return width0 < width1;
}
/*------------------------------------------------------------------------------------------------*/
//unsigned gp_find_lowest_bit(unsigned Bits) {
// unsigned i;
// if (!Bits) return sizeof(unsigned)<<3;
// for (i=0; !(Bits&1); Bits>>=1,i++); // bit-scan-forward or -reverse?
// return i;
//}
/*------------------------------------------------------------------------------------------------*/
//unsigned gp_find_highest_bit(unsigned Bits) {
// unsigned i = sizeof(unsigned)<<3;
// const unsigned msb = 1U<<(i-1);
// if (!Bits) return i;
// for (;--i,!(Bits&msb);Bits<<=1);
// return i;
//}
/*------------------------------------------------------------------------------------------------*/
FUNC(void) gp_date_string(char *Buffer, size_t Sizeof_buffer) {
time_t now;
const struct tm *now_tm;
#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
char format[60];
#endif
time(&now);
now_tm = localtime(&now);
if (now_tm) {
#if defined(HAVE_LOCALE_H) && defined(HAVE_LANGINFO_H)
setlocale(LC_ALL, "");
snprintf(format, sizeof(format), "%s %s", nl_langinfo(D_FMT), nl_langinfo(T_FMT));
strftime(Buffer, Sizeof_buffer, format, now_tm);
setlocale(LC_ALL, "C");
#else
snprintf(Buffer, Sizeof_buffer,
"%d-%d-%d %02d:%02d:%02d",
now_tm->tm_mon + 1,
now_tm->tm_mday,
1900 + now_tm->tm_year,
now_tm->tm_hour,
now_tm->tm_min,
now_tm->tm_sec);
#endif
}
else if (Sizeof_buffer > 0) {
Buffer[0] = '\0';
}
}
/*------------------------------------------------------------------------------------------------*/
/*
FUNC(void*) gp_malloc(size_t Size, const char *File, size_t Line) {
void *m;
if (!Size) return NULL;
if ((m = malloc(Size)) == NULL) {
fprintf(stderr, "%s.%s(%u) -- Could not allocate %u bytes of memory. {%s:%u}\n",
__FILE__, "malloc", Size, File, Line);
exit(1);
}
return m;
}
*/
/*------------------------------------------------------------------------------------------------*/
/*
FUNC(void*) gp_calloc(size_t Nmemb, size_t Size, const char *File, size_t Line) {
void *m;
if (!Nmemb || !Size) return NULL;
if ((m = calloc(Nmemb, Size)) == NULL) {
fprintf(stderr, "%s.%s() -- Could not allocate %" SIZE_FMTu " bytes of memory. {%s.LINE-%" SIZE_FMTu ", %s()}\n",
__FILE__, __func__, Nmemb * Size, File, Line, Func);
exit(1);
}
return m;
}
*/
/*------------------------------------------------------------------------------------------------*/
FUNC(void*) gp_realloc(void *Mem, size_t Size, const char *File, size_t Line) {
void *m;
if (!Size) {free(Mem); return 0;}
if (!(m = realloc(Mem, Size))) {
fprintf(stderr, "%s.%s(%p,%u) -- Could not reallocate %u bytes of memory. {%s:%u}\n",
__FILE__, "realloc", Mem, Size, Size, File, Line);
exit(1);
}
return m;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_strdup(const char *String, const char *File, size_t Line) {
char *s;
if (!String) return NULL;
if ((s = strdup(String)) == NULL) {
fprintf(stderr, "%s.%s(\"%s\") -- Could not allocate string {%s:%u}, error: %s.\n",
__FILE__, "strdup", String, File, Line, strerror(errno));
exit(1);
}
return s;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_strndup(const char *String, size_t Length, const char *File, size_t Line) {
char *s;
if (!String) return NULL;
if ((s = strndup(String, Length)) == NULL) {
fprintf(stderr, "%s:%s(\"%s\",%u) -- Could not allocate string {%s:%u}, error: %s.\n",
__FILE__, "strndup", String, Length, File, Line, strerror(errno));
exit(1);
}
return s;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_strdup_lower_case(const char *Name) {
char ch;
char *pnew;
char *ptr;
ptr = pnew = GP_Strdup(Name);
if (ptr) while ((ch = *ptr) != '\0') {
*ptr = (tolower)(ch);
ptr++;
}
return pnew;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_strdup_upper_case(const char *Name) {
char ch;
char *pnew;
char *ptr;
ptr = pnew = GP_Strdup(Name);
if (ptr) while ((ch = *ptr) != '\0') {
*ptr = (toupper)(ch);
ptr++;
}
return pnew;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_strncpy(char *Dest, const char *Src, size_t Maxlen) {
char ch;
assert(Dest);
assert(Src);
if (!Maxlen) return NULL;
do {
if (--Maxlen == 0) {
*Dest = '\0';
break;
}
ch = *Src++;
*Dest++ = ch;
} while (ch != '\0');
return Dest;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_arch_strncpy(char *Dest, const char *Src, size_t Maxlen) {
char ch;
assert(Dest);
assert(Src);
if (!Maxlen) return NULL;
while (true) {
if (--Maxlen == 0) {
break;
}
ch = *Src;
if (ch == '\0') {
break;
}
*Dest = ch;
++Src;
++Dest;
};
return Dest;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(char*) gp_stptoupper(char *Dest, const char *Src, size_t Maxlen) {
char ch;
assert(Dest);
assert(Src);
if (!Maxlen) return NULL;
do {
if (--Maxlen == 0) {
*Dest = '\0';
break;
}
ch = *Src++;
*Dest++ = (toupper)(ch);
} while (ch != '\0');
return Dest;
}
/*------------------------------------------------------------------------------------------------*/
/*
C_str : Beginning address of C style string area.
C_max_size : Size of C style string area.
Pascal_str : Beginning address of Pascal style string area.
Pascal_max_size : Size of Pascal style string area, included the string length.
Is_limited_length: This a pointer wherewith the function indicates that limitation occurred.
*/
FUNC(char*) gp_str_from_Pstr(char *C_str, size_t C_max_size, const uint8_t *Pascal_str, size_t Pascal_max_size,
bool *Is_limited_length) {
size_t length;
bool limit;
assert(C_str);
assert(Pascal_str);
/* This is a Pascal style string, read the length. */
length = *Pascal_str++; /* The real beginning of string. */
limit = false;
if (length >= Pascal_max_size) {
/* The string too long, corrects the size. */
length = Pascal_max_size - 1;
limit = true;
}
if (length >= C_max_size) {
/* The storage area too little. */
length = C_max_size - 1;
limit = true;
}
memcpy(C_str, Pascal_str, length);
C_str[length] = '\0';
if (Is_limited_length) *Is_limited_length = limit;
return C_str;
}
/*------------------------------------------------------------------------------------------------*/
/*
Pascal_str : Beginning address of Pascal style string area.
Pascal_max_size : Size of Pascal style string area, included the string length.
C_str : Beginning address of C style string area.
Is_limited_length: This a pointer wherewith the function indicates that limitation occurred.
*/
FUNC(size_t) gp_Pstr_from_str(uint8_t *Pascal_str, size_t Pascal_max_size, const char *C_str, bool *Is_limited_length) {
size_t length;
bool limit=false;
assert(Pascal_str);
assert(C_str);
length = strlen(C_str);
if (length >= Pascal_max_size) {
/* Truncate the length. */
length = Pascal_max_size - 1;
limit = true;
}
/* Store the string length. */
*Pascal_str++ = length; /* The real beginning of string. */
memcpy(Pascal_str, C_str, length);
if (Is_limited_length) *Is_limited_length = limit;
return length;
}
/*------------------------------------------------------------------------------------------------*/
/*
C_str : Beginning address of C style string area.
Pascal_max_size : Size of Pascal style string area, included the string length.
Is_limited_length: This a pointer wherewith the function indicates that limitation occurred.
*/
FUNC(size_t) gp_strlen_Plimit(const char *C_str, size_t Pascal_max_size, bool *Is_limited_length) {
size_t length;
bool limit=false;
assert(C_str);
length = strlen(C_str);
if (length >= Pascal_max_size) {
length = Pascal_max_size - 1;
limit = true;
}
if (Is_limited_length) *Is_limited_length = limit;
return length;
}
/*------------------------------------------------------------------------------------------------*/
FUNC(size_t) gp_align_text(char *Buffer, size_t Buffer_length, size_t Current_length, size_t Aligned_to_length) {
int lp;
size_t length;
if ((Current_length < (Buffer_length - 1)) && (Aligned_to_length > Current_length)) {
Buffer_length -= Current_length;
length = Aligned_to_length - Current_length;
if (length >= Buffer_length) {
length = Buffer_length - 1;
}
lp = snprintf(&Buffer[Current_length], Buffer_length - Current_length, "%*s", (int)length, " ");
return lp > 0 ? Current_length + lp : Current_length;
}
return Current_length;
}
/*------------------------------------------------------------------------------------------------*/
CFUNC(size_t) gp_exclamation(char *Buffer, size_t Buffer_length, size_t Current_length, const char *Format, ...) {
int lp;
size_t length;
va_list ap;
length = gp_align_text(Buffer, Buffer_length, Current_length, EXPLANATION_DISTANCE);
va_start(ap, Format);
lp = vsnprintf(&Buffer[length], Buffer_length - length, Format, ap);
va_end(ap);
return ((lp >= 0) ? (length + lp) : Current_length);
}
/*------------------------------------------------------------------------------------------------*/
/* fetch the absolute path of the filename */
FUNC(char*) gp_absolute_path(char *File_name) {
#ifdef HAVE_WINDOWS_H
/* It would be better to test for GetFullPathName, but the test won't
work with a cross compiler. So if windows.h exists, we assume that
GetFullPathName is available. */
#define FILE_BUFFER_SIZE 512
char file_buffer[FILE_BUFFER_SIZE];
char *file_ptr;
int num_chars;
num_chars = GetFullPathName(File_name, FILE_BUFFER_SIZE, file_buffer, &file_ptr);
if (!num_chars) {
gp_error("Can't fetch full path of %s.", File_name);
return File_name;
}
else {
return GP_Strdup(file_buffer);
}
#else
#ifdef HAVE_REALPATH
char *resolved_name;
resolved_name = realpath(File_name, NULL);
if (resolved_name == NULL) {
gp_error("Can't fetch full path of \"%s\".", File_name);
return File_name;
}
else {
return resolved_name;
}
#else
if (absolute_path_warning) {
gp_warning("Host system does not support absolute paths.");
absolute_path_warning = false;
}
return File_name;
#endif
#endif
}
/*------------------------------------------------------------------------------------------------*/
FUNC(void) gp_checkarg(const char*arg, const char*opts) {
if (!arg) return; // another problem
if (arg[0]!='-') return; // cannot be a long option
if (arg[1]!='-') return; // cannot be a long option
if (arg[2]) {
const char*a = arg+2;
const char*e = strchr(a,'='); // possible end of long option
if (!e) e = a+strlen(a); // end of long option
size_t la = e-a; // length of given tag, 1 = minimum
for (;*opts;opts+=strlen(opts)+1) {
const char*q0 = opts+1; // long option tag
const char*qe = strpbrk(q0,"\1\2"); // possible end of tag
if (!qe) qe = q0+strlen(q0); // end of tag
size_t lo = qe-q0; // length of tag to compare, can be 0!
if (la==lo && !memcmp(a,q0,la)) goto silly;
}
}else{
silly:
fprintf(stderr, "Error: This option may not be parameter of another option: \"%s\"\n",arg);
exit(1);
}
}
// This "getopt" function uses a callback for every found option,
// and uses a compact double-zero-terminated string for control.
FUNC(void) gp_getopt(const char*const*argv,const char*opts,void(_stdcall*cb)(void*,char,const char*),void*cd) {
bool parsestop = false; // after "-" or "--", end parsing for options, callback with plain argument
const char*argv0 = *argv; // keep program name for error messages
while (*++argv) { // The argv[] array is zero terminated by C/C++ standard
const char*arg = *argv;
if (parsestop) cb(cd,0,arg);
else if (arg[0]=='-') {
if (arg[1]==arg[0]) {
if (!arg[2]) parsestop = true;
else{ // scan for known long option
const char*a = arg+2;
const char*a2 = strchr(a,'='); // allow "=" between tag and value
const char*e = a2 ? a2 : a+strlen(a);
size_t la = e-a; // length of given tag, 1 = minimum
for(const char*q=opts;*q;q+=strlen(q)+1) {
const char*q0 = q+1; // skip short option for now
const char*q1 = strpbrk(q0,"\1\2"); // scan for "argument" terminator
const char*qe = q1 ? q1 : q0+strlen(q0);
size_t lo = qe-q0; // length of tag to compare, can be 0!
if (la==lo && !memcmp(a,q0,la)) { // correct long option, look for argument
const char*b = 0;
if (q1) if (*q1==1) {
b = a2 ? a2+1 : *++argv; // unconditionally take next argument, even if 0
if (!b) fprintf(stderr,"%s: option '%s' requires an argument\n",argv0,arg);
}else{
if (a2) b = a2+1;
else if (argv[1] && argv[1][0]!='-') b = *++argv; // take next arg if not looking like an option
}
cb(cd,*q,b); // call back (in any case)
if (!*argv) return; // break-out when next argument is nullptr
goto found;
}
}
fprintf(stderr,"%s: unrecognized option '%s'\n",argv0,arg);
cb(cd,-1,arg); // take argument as (possibly wrong) file name?
found:;
}
}else if (!arg[1]) parsestop = true;
// process concatenated short options
else for (const char*a = arg;*++a;) {
const char*b = a[1]=='=' ? a+2 : a[1] ? a+1 : 0;
for(const char*q=opts;*q;q+=strlen(q)+1) {
if (*a==*q) {
const char*q1 = strpbrk(q+1,"\1\2");
if (q1) {
if (*q1==1) { // required argument?
if (!b) b = *++argv; // if no tail, take next arg
if (!b) fprintf(stderr,"%s: option requires an argument -- %c\n",argv0,*a);
}else{ // optional argument?
if (!b) { // if no tail, check next arg
b = argv[1] && argv[1][0]!='-' ? *++argv : 0;
}
}
}else b = 0; // Always no argument
goto found2; // exit inner loop with known option
}
}
fprintf(stderr,"%s: invalid option -- %c\n",argv0,*a);
found2:
cb(cd,*a,b);
if (b) break; // exit outer loop when argument was taken
// As side effect, unknown options are reported with argument, e.g.
// "-?=1" goes to callback with c == '?' and arg == "1".
// "-?" (same as "-h") c == '?' and arg == nullptr
// whereas "=" forces non-nullptr argument:
// "-?=" c == '?' and arg == ""
// however known 'h' produces:
// "-h==" c == 'h' and arg == nullptr
// and then
// c == '=' and arg == "" (for the second '=')
}
}else cb(cd,0,arg); // non-option argument (file name etc.)
}
}
// This functor-like class cuts long strings into words and checks whether it will fit
// onto the screen.
// Moreover, it emits TAB characters and take the current position into account;
// tab width is fixed to 8.
// UTF-8 non-combining characters are measured correctly. Assume monospaced font.
struct PrintW{
int left,width;
mutable int x;
char last;
void tab(int) const;
const PrintW&operator()(int x) const {tab(x);return*this;}
void put(char c) const {if (c) {putchar(c); x+=measure(c);}}
const PrintW&operator<<(char c) const {put(c);return*this;}
void put(const char*s, size_t slen) const {if (slen) do put(*s++); while(--slen);}
void put(const char*s) const {while (*s) put(*s++);}
const PrintW&operator<<(const char*s) const {put(s);return*this;}
int measure(char c) const;
static int measure(const char*s,size_t slen);
void operator()(const char*s);
void put_option(const char*arg,bool optional);
PrintW(int l,int w):left(l),width(w),x(0),last(0) {}
};
void PrintW::tab(int col) const{
while (x+7<col) put('\t');
while (x<col) put(' ');
}
int PrintW::measure(char c) const{
if (c < -0x40) return 0; // don't count UTF-8 trailing bytes
if (!c) return 0; // don't count "no character"
if (c=='\t') return 8-(x&7); // spaces to next tabstop
if (c=='\n') return -x; // let rewind to zero
return 1; // one character
}
// For simplicity, skip 0x80..0xBF character codes, will work with most UTF-8
int PrintW::measure(const char*s, size_t slen) {
int len=0;
if (slen) do if (*s++ >= -0x40) ++len; while(--slen);
return len;
}
void PrintW::operator()(const char*s) {
int indent=0;
while (*s) {
const char*eow = strpbrk(s,"\n\t "); // only these three characters are allowed!
if (!eow) eow = s+strlen(s);
int len = measure(s,eow-s);
if (x+measure(last)+len>width) {
put('\n');
tab(left+indent);
}else{
put(last);
if (last=='\n') { // the first '\n' in text switches the indent to 3 as a bullet list is expected to follow
tab(left);
indent=3;
}
}
put(s,eow-s);
if ((last=*(s=eow))) ++s;
}
put('\n');
}
void PrintW::put_option(const char*arg,bool optional) {
if (!arg || !*arg) return;
put(' ');
if (optional) put('['); // show bracket for optional argument
put(arg);
if (optional) put(']');
}
// This generic usage printout function uses the same opts as gp_getopt()
// and writes them out using localizable desc string list.
// Important: Options and descriptions must fit one-by-one.
// <left> must be less than <width> and both non-negative!
// TODO: Summary line (always the same?)
// TODO: Auto-detect screen width
// TODO: UTF-8 to OEMCP conversion for Windows (for non-english description)
// TODO: Use string resource for localized description (Windows only)
CFUNC(void) gp_usage(const char*argv0,const char*opts,const char*desc,
int left,int width,const char*footer,...) {
if (argv0) printf("Usage: %s [options] file\n",argv0);
printf("Options: [defaults in brackets after descriptions]\n");
PrintW pr(left+2,width); // this class avoids need for C++11 lambdas
for(;*opts;opts+=strlen(opts)+1, desc+=strlen(desc)+1) {
const char*q0=opts+1; // long option (if any)
const char*q1=strpbrk(q0,"\1\2"); // required (1) / optional (2) argument
const char*arg=0; // argument name?
if (q1) arg=q1+1; // Here is the name
else q1=q0+strlen(q0); // no (0) argument
if (*opts>' ') { // Have short option?
pr(2)<<'-'<<*opts; // show it
pr.put_option(arg,*q1==2);
if (*q0>' ') pr<<',';
pr<<' '; // TODO: Nicht ausgeben wenn TAB folgen kann
}
if (*q0>' ') {
pr(6);
pr<<'-'<<'-';
pr.put(q0,q1-q0);
pr.put_option(arg,*q1==2);
pr<<' '<<' ';
}
if (pr.x>pr.left+8) pr<<'\n';
pr(left);
pr(desc);
}
if (footer) {
va_list va;
va_start(va,footer);
vprintf(footer,va);
va_end(va);
}
printf("\tReport bugs to: %s\n",PACKAGE_BUGREPORT);
}
Detected encoding: UTF-8 | 0
|