Source file: /~heha/hs/gputils64-210929.zip/libgputils/gpsystem.cpp

/* 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-80