/* strftime.c (emx+gcc) -- Copyright (c) 1992-1994 by Eberhard Mattes */

#include <sys/emx.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

struct loc_date_time
{
  char *months1[12];
  char *months2[12];
  char *wdays1[7];
  char *wdays2[7];
  char *date_time_fmt;
  char *date_fmt;
  char *time_fmt;
  char *am, *pm;
};

static struct loc_date_time const def_loc =
{
  {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
  {
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December" },
  {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
  {
    "Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday" },
  "%a %b %d %H:%M:%S %Y",
  "%m/%d/%y",
  "%H:%M:%S",
  "AM", "PM"
  };



static int ins (char *string, size_t *n, size_t size, const char *s,
    size_t len);
static int num2 (char *string, size_t *n, size_t size, int x);
static int num3 (char *string, size_t *n, size_t size, int x);
static int week (const struct tm *t, int first);

#define INS(STR,LEN) if (!ins (string, &n, size, (STR), (LEN))) return (0);
#define NUM2(X) if (!num2 (string, &n, size, (X))) return (0);
#define NUM3(X) if (!num3 (string, &n, size, (X))) return (0);
#define FMT(F) \
    i = strftime (string + n, size - n, (F), t); \
    if (i == 0) \
      return (0); \
    else \
      n += i;
#define CHR(C) \
    if (n < size) \
      string[n++] = C; \
    else \
      return (0);


static int ins (char *string, size_t *n, size_t size, const char *s,
                size_t len)
{
  size_t i;

  i = *n;
  if (len < size - i)
    {
      memcpy (string + i, s, len);
      *n += len;
      return (1);
    }
  else
    return (0);
}


static int num2 (char *string, size_t *n, size_t size, int x)
{
  int i;

  i = *n;
  if (2 < size - i)
    {
      string[i++] = (char)('0' + (x / 10) % 10);
      string[i++] = (char)('0' + (x % 10));
      *n = i;
      return (1);
    }
  else
    return (0);
}


static int num3 (char *string, size_t *n, size_t size, int x)
{
  int i;

  i = *n;
  if (3 < size - i)
    {
      string[i++] = (char)('0' + (x / 100) % 10);
      string[i++] = (char)('0' + (x / 10) % 10);
      string[i++] = (char)('0' + (x % 10));
      *n = i;
      return (1);
    }
  else
    return (0);
}


static int week (const struct tm *t, int first)
{
  int wd, tmp;

  wd = t->tm_wday - first;    /* wd = relative day in week (w.r.t. first) */
  if (wd < 0) wd += 7;
  tmp = t->tm_yday - wd;      /* Start of current week */
  if (tmp <= 0) return (0);   /* In partial first week */
  return ((tmp + 6) / 7);     /* Week number */
}


size_t strftime (char *string, size_t size, const char *format,
                 const struct tm *t)
{
  size_t i, n;
  char *p, tmp[40];
  const struct loc_date_time *lp;

  lp = &def_loc; n = 0;
  while (*format != 0)
    {
      if (format[0] == '%')
        {
          ++format; p = NULL;
          switch (*format)
            {
            case 0:
            case '%':
              CHR ('%');
              if (*format == 0)
                --format;
              break;
            case 'a':
              p = lp->wdays1[t->tm_wday];
              INS (p, strlen (p));
              break;
            case 'A':
              p = lp->wdays2[t->tm_wday];
              INS (p, strlen (p));
              break;
            case 'b':
            case 'h':
              p = lp->months1[t->tm_mon];
              INS (p, strlen (p));
              break;
            case 'B':
              p = lp->months2[t->tm_mon];
              INS (p, strlen (p));
              break;
            case 'c':
              FMT (lp->date_time_fmt);
              break;
            case 'd':
              NUM2 (t->tm_mday);
              break;
            case 'D':
              FMT ("%m/%d/%y");
              break;
            case 'e':
              NUM2 (t->tm_mday);
              if (string[n-2] == '0')
                string[n-2] = ' ';
              break;
            case 'H':
              NUM2 (t->tm_hour);
              break;
            case 'I':
              NUM2 ((t->tm_hour + 11) % 12 + 1);
              break;
            case 'j':
              NUM3 (t->tm_yday + 1);
              break;
            case 'm':
              NUM2 (t->tm_mon + 1);
              break;
            case 'M':
              NUM2 (t->tm_min);
              break;
            case 'n':
              CHR ('\n');
              break;
            case 'p':
              p = (t->tm_hour >= 12 ? lp->pm : lp->am);
              INS (p, strlen (p));
              break;
            case 'r':
              FMT ("%I:%M:%S %p");
              break;
            case 'S':
              NUM2 (t->tm_sec);
              break;
            case 't':
              CHR ('\t');
              break;
            case 'T':
              FMT ("%H:%M:%S");
              break;
            case 'U':
              NUM2 (week (t, 0));
              break;
            case 'w':
              NUM2 (t->tm_wday);
              break;
            case 'W':
              NUM2 (week (t, 1));
              break;
            case 'x':
              FMT (lp->date_fmt);
              break;
            case 'X':
              FMT (lp->time_fmt);
              break;
            case 'y':
              NUM2 (t->tm_year % 100);
              break;
            case 'Y':
              p = _itoa (t->tm_year + 1900, tmp, 10);
              INS (p, strlen (p));
              break;
            case 'Z':
              if (!_tzset_flag) tzset ();
              if (t->tm_isdst >= 0)
                {
                  p = tzname[(t->tm_isdst ? 1 : 0)];
                  INS (p, strlen (p));
                }
              break;
            default:
              break;
            }
        }
      else if (n < size)
        string[n++] = *format;
      else
        return (0);
      ++format;
    }
  if (n < size)
    {
      string[n] = 0;
      return (n);
    }
  else
    return (0);
}
