/* strftime.c (emx+gcc) -- Copyright (c) 1992-1996 by Eberhard Mattes */

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <emx/locale.h>
#include <emx/time.h>

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];
  unsigned char c;
  int mbn, shift;

  if (!_tzset_flag) _tzset ();
  shift = 0; n = 0;
  while ((c = *format) != 0)
    {
      if (c == '%')
        {
          ++format; p = NULL;
          switch (*format)
            {
            case 0:
            case '%':
              CHR ('%');
              if (*format == 0)
                --format;
              break;
            case 'a':
              p = _cur_lcf_time.wdays1[t->tm_wday];
              INS (p, strlen (p));
              break;
            case 'A':
              p = _cur_lcf_time.wdays2[t->tm_wday];
              INS (p, strlen (p));
              break;
            case 'b':
            case 'h':
              p = _cur_lcf_time.months1[t->tm_mon];
              INS (p, strlen (p));
              break;
            case 'B':
              p = _cur_lcf_time.months2[t->tm_mon];
              INS (p, strlen (p));
              break;
            case 'c':
              FMT (_cur_lcf_time.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 ? _cur_lcf_time.pm : _cur_lcf_time.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 (_cur_lcf_time.date_fmt);
              break;
            case 'X':
              FMT (_cur_lcf_time.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 (t->tm_isdst >= 0)
                {
                  p = _tzname[(t->tm_isdst ? 1 : 0)];
                  INS (p, strlen (p));
                }
              break;
            default:
              break;
            }
        }
      else if (c <= 127 || _cur_mbyte.mode == _MB_8BITS)
        {
          if (n < size)
            string[n++] = c;
          else
            return 0;
        }
      else
        {
          mbn = _mbtowc (NULL, format, MB_LEN_MAX, &shift);
          if (mbn < 1)
            mbn = 1;
          if (n + mbn > size)
            return 0;
          while (mbn > 0)
            {
              string[n++] = *format++;
              --mbn;
            }
          --format;
        }
      ++format;
    }
  if (n < size)
    {
      string[n] = 0;
      return n;
    }
  else
    return 0;
}
