/*
   Easy manipulation with strings in C

   06/2024: DOS switch removed
   11/2010: safer dynamic implementation
            vsnprintf expected to exist (C99)
            my alloc/free needed, see ground.h
   09/2002: default size changed to 3*128 (DOS) or 3*256

   Example:
     int i=3;
     stringinit(0,2);
     char *info=string("i=%d",i);
     FILE *f=fopen(string("file%d.dat",i),"w");
     printf("info: %s",info);

   Headers are in ground.h:

     void stringinit(int dummy,int n);
       dummy (not used - legacy)
       n=max number of strings available
       (if stringinit is not called, stringinit(128,3) is assumed)

     char *string(const char *format,...);
       returns pointer to a static string containing the result of
       sprintf(<static string>,format,data)

     char *vstring(const char *format,va_list args);
       as above, in the vprintf style

     char *strend(const char *str);
       returns pointer to the terminating 0

     char *strlast(const char *str);
       returns pointer to the last char, or NULL

     char *int2sumbin(int i);
       human-readable binary: int2sumbin(5) -> 4+1
*/

#ifndef allocarray
#  define allocarray(X,Y) alloc(X,(Y)*sizeof(X[0]))
#  define allocarrayzero(X,Y) alloczero(X,(Y)*sizeof(X[0]))

#  define allocone(X) alloc(X,sizeof(X[0]))
#  define alloconezero(X) alloczero(X,sizeof(X[0]))
#endif /*# allocarray */

static char **string_area; /* ended by sentinel NULL */
static char **last_string;

#if defined(va_copy) || defined(__va_copy)

// #if 0 // good for debugging

/* C99: vsnprintf expected to exist */

#  ifndef va_copy
#    define va_copy __va_copy
#  endif /*# va_copy */

void stringinit(int dummy,int n) /******************************* stringinit */
/*
   allocates n string pointers
   dummy not used (kept for compatibility reasons only)
*/
{
  int i;

  if (string_area) free(string_area);
  allocarrayzero(string_area,n+1);
  loop (i,0,n) alloc(string_area[i],8); /* size irrelevant */
  last_string=string_area;
}

char *vstring(const char *format,va_list args) /******************** vstring */
/*
   returns string as generated by the format and variable arg list
   results of max n succesive calls are valid: see stringinit(dummy,n)
*/
{
  char auxline[128];
  int l;
  va_list copyofargs;

  va_copy(copyofargs,args);
  if (!string_area) stringinit(128,3);
  if (!*(++last_string)) last_string=string_area;

  if (*last_string) free(*last_string);
  l=vsnprintf(auxline,128,format,args);
  va_end(args);

  alloc(*last_string,l+1);

  if (l>=128) vsprintf(*last_string,format,copyofargs);
  else strcpy(*last_string,auxline);

  va_end(copyofargs);
  return *last_string;
}

char *string(const char *format,...) /***************************** string */
/*
   returns string as generated by the format and its arguments
   results of max n succesive calls are valid: see stringinit(dummy,n)
*/
{
  va_list args;

  va_start(args,format);
  vstring(format,args);
  va_end(args);

  return *last_string;
}

#else /*# defined(va_copy) || defined(__va_copy) */

/* old version with fixed string lengths - dangerous! */

void stringinit(int len,int n) /******************************** stringinit */
/* allocates n strings of given len */
{
  int i;

  if (string_area) {
    for (i=0; string_area[i]; i++) free(string_area[i]);
    free(string_area); }
  alloc(string_area,(n+1)*sizeof(string_area[0]));
  loop (i,0,n) alloc(string_area[i],len);
  string_area[n]=NULL;
  last_string=string_area;
}

char *vstring(const char *format,va_list args) /******************** vstring */
/*
   returns string as generated by the format and variable arg list
   the max number (before overwriting) and lengths of strings: see stringinit
*/
{
  if (!string_area) stringinit(256,3);
  if (!*(++last_string)) last_string=string_area;
  vsprintf(*last_string,format,args);

  return *last_string;
}

char *string(const char *format,...) /******************************* string */
/*
   returns string as generated by the format and its argument
   the max number (before overwriting) and lengths of strings: see stringinit
*/
{
  va_list args;

  va_start(args,format);
  vstring(format,args);
  va_end(args);

  return *last_string;
}
#endif /*#!defined(va_copy) || defined(__va_copy) */

char *strend(const char *str) /************************************** strend */
/* returns pointer to the terminating 0 */
{
  char *c;

  if (!str) return NULL;
  for (c=(char*)str; *c; c++);

  return c;
}

char *strlast(const char *str) /************************************ strlast */
/* returns pointer to the last char, or NULL if str==NULL or str="" */
{
  char *c;

  if (!str || !*str) return NULL;
  for (c=(char*)str; *c; c++);

  return c-1;
}

char *int2sumbin(int i) /**************************************** int2sumbin */
/*
  returns binary representation of i in human-readable decimal form, e.g.:
    int2sumbin(5) -> 4+1
*/
{
  unsigned n;
  char line[256]; /* good enough for 1+...+2147483648 */
  char *c=line;

  *c=0;

  for (n=0x80000000u; n; n>>=1)
    if (n&(unsigned)i) c+=sprintf(c,"%u+",n);

  if (strlen(line)) c[-1]=0; /* remove last + */
  else strcpy(line,"0");

  return string("%s",line);
}
