// a0.cpp
// Copyleft by Yuki Kamino
#include "stdafx.h"

#include "Storm.h"



//---------------------------
// Lex part
//---------------------------

// Reserved words
typedef struct
{
  int code;
  TCHAR *name;
} KeyData;

// for binary search, in ascending order
KeyData KeyList[] =
{
  TK_BOLD,          _T("Bold"),
  TK_BOTTOM,        _T("Bottom"),
  TK_CARD,          _T("Card"),
  TK_CELL,          _T("Cell"),
  TK_CENTER,        _T("Center"),
  TK_LINK,          _T("Choice"),
  TK_CIRCLE,        _T("Circle"),
  TK_COLOR,         _T("Color"),
  TK_COMMON,        _T("Common"),
  TK_CONTENTS,      _T("Con"),
  TK_CONTENTS,      _T("Contents"),
  TK_CREATURE,      _T("Creature"),
  TK_CROSS,         _T("Cross"),
  TK_DATA,          _T("Data"),
  TK_DATAFORMAT,    _T("DataFormat"),
  TK_DATE,          _T("Date"),
  TK_NDIV,          _T("Div"),
  TK_DWELL,         _T("Dwell"),
  TK_ELLIPSE,       _T("Ellipse"),
  TK_ELSE,          _T("Else"),
  TK_FONT,          _T("Font"),
  TK_FONTSIZE,      _T("FontSize"),
  TK_FOR,           _T("For"),
  TK_HYPER,         _T("Hyper"),
  TK_IDNUM,         _T("ID"),
  TK_IF,            _T("If"),
  TK_ITALIC,        _T("Italic"),
  TK_ITEM,          _T("Item"),
  TK_LET,           _T("Let"),
  TK_LINK,          _T("Link"),
  TK_LIST,          _T("List"),
  TK_LISTDATA,      _T("ListData"),
  TK_MEMO,          _T("Memo"),
  TK_MOD,           _T("Mod"),
  TK_MU_CHAR,       _T("MU_CHAR"),
  TK_MU_INCH,       _T("MU_INCH"),
  TK_MU_MM,         _T("MU_MM"),
  TK_MU_WCHAR,      _T("MU_WCHAR"),
  TK_NAME,          _T("Name"),
  TK_NUM,           _T("Num"),
  TK_NUMLET,        _T("NumLet"),
  TK_ONCLICK,       _T("OnClick"),
  TK_ONEOF,         _T("OneOf"),
  TK_ONOFF,         _T("OnOff"),
  TK_PLAY,          _T("Play"),
  TK_LISTDATA,      _T("Record"),
  TK_RECT,          _T("Rect"),
  TK_SCENE,         _T("Scene"),
  TK_SEC,           _T("Sec"),
  TK_SELECT,        _T("Select"),
  TK_SELECTOR,      _T("Selector"),
  TK_SET,           _T("Set"),
  TK_SETCHARSIZE,   _T("SetCharSize"),
  TK_SETMEASUREUNIT,  _T("SetMeasureUnit"),
  TK_SETNUM,        _T("SetNum"),
  TK_SETRANK,       _T("SetRank"),
  TK_SIZE,          _T("Size"),
  TK_SORTDOWN,      _T("SortDown"),
  TK_SORTUP,        _T("SortUp"),
  TK_SCENE,         _T("Stage"),
  TK_STEP,          _T("Step"),
  TK_STRITEM,       _T("Str"),
  TK_STRMULITEM,    _T("StrMul"),
  TK_TABLE,         _T("Table"),
  TK_WORK,          _T("Temp"),
  TK_TEXT,          _T("Text"),
  TK_TIME,          _T("Time"),
  TK_TIMER,         _T("Timer"),
  TK_TOP,           _T("Top"),
  TK_UNDER,         _T("Under"),
  TK_DATA,          _T("Unit"),
  TK_VAL,           _T("Val"),
  TK_VALLET,        _T("ValLet"),
  TK_VITEM,         _T("VItem"),
  TK_WID,           _T("Wid"),
  TK_WID,           _T("Width"),
  TK_WORD,          _T("Word"),
  TK_WORDLET,       _T("WordLet"),
  TK_WORDMUL,       _T("WordMul"),
  TK_WORK,          _T("Work"),
  TK_COMMON,        _T("World"),
  TK_XITEM,         _T("XItem"),
  TK_YITEM,         _T("YItem"),
};


typedef struct tagColorData
{
  TCHAR *name;
  COLORREF val;
} ColorData;

ColorData ColorVal[] =
{
  _T("Blue"), RGB(0, 103, 192),
  _T("Red"), RGB(255, 0, 0),
  _T("Orange"), RGB(243, 152, 0),
  _T("Purple"), RGB(167, 87, 168),
  _T("Green"), RGB(0, 128, 0),
  _T("Yellow"), RGB(255, 212, 0),
  _T("Peach"), RGB(245, 143, 152),
  _T("White"), RGB(255, 255, 255),
  _T("Black"), RGB(0, 0, 0),
  _T("Gray"), RGB(128, 128, 128),
  _T("Cyan"), RGB(0, 255, 255),
  _T("Magenta"), RGB(255, 0, 255),
  _T("Pink"), RGB(247, 171, 166),
};


int FindKeyword(TCHAR *name)
{
  int lo = 0;
  int hi = sizeof(KeyList) / sizeof(KeyList[0]) - 1;
  while (lo <= hi)
  {
    int mid = (lo + hi) / 2;
    int cmp = lstrcmp(name, KeyList[mid].name);
    if (cmp < 0)
      hi = mid - 1;
    else if (cmp > 0)
      lo = mid + 1;
    else
      return KeyList[mid].code;
  }

  for (int i = 0; i < sizeof(ColorVal)/sizeof(ColorVal[0]); i++)
  {
    if (!lstrcmp(name, ColorVal[i].name))
    {
      colValue = ColorVal[i].val;
      return TK_COLORVAL;
    }
  }
  return TK_ID;
}

BOOL CheckKeywordOrder()
{
  for (int i = 1; i < sizeof(KeyList) / sizeof(KeyList[0]); i++)
  {
    if (lstrcmp(KeyList[i-1].name, KeyList[i].name) >= 0)
    {
      MessageBox(NULL, _T("Bug : Keyword is not in order"), KeyList[i].name, MB_OK);
      return TRUE;
    }
  }
  return FALSE;
}


int lenStrIn;
BOOL bInExpr;

void KeepStrIn(int len)
{
  if (len >= lenStrIn)
  {
    lenStrIn = len + 16;  // a bit more
    if (strIn)
      delete [] strIn;
    strIn = new TCHAR[lenStrIn];
  }
}

void ClearStrIn()
{
  if (strIn)
    delete [] strIn;
  strIn = NULL;
  lenStrIn = NULL;
}

int GetTokenSub(BOOL bNoDot)
{
  // white space
  while (_istspace(*topBuf))
  {
    if (*topBuf == '\n')
      lineNo++;
    topBuf++;
  }

  // end of source
  if (!*topBuf)
    return TK_EOF;

  // ID or Keyword
  if (_istalpha(*topBuf) || *topBuf == '_')
  {
    TCHAR *p = nameIn;
    for (int n = 0; n < 120 && _istalnum(*topBuf) || *topBuf == '_'; n++)
      *p++ = *topBuf++;
    *p = '\0';
    textIn = nameIn;
    return FindKeyword(nameIn);
  }

  // string
  else if (*topBuf == '"')
  {
    topBuf++;
    for (TCHAR *p = topBuf; *p; p = CharNext(p))
    {
      if (*p == '"')
      {
        if (p[1] == '"')
        {
          p++;   // "" means "
        }
        else
        {
          int len = p - topBuf;
          KeepStrIn(len);
          int at = 0;
          for (int i = 0; i < len; i++)
          {
            if (i < len-1 && topBuf[i] == '"' && topBuf[i+1] == '"')
              continue;
            strIn[at++] = topBuf[i];
          }
          strIn[at] = '\0';
          topBuf = p + 1;
          textIn = strIn;
          return TK_STR;
        }
      }
      else if (*p == '\r' || *p == '\n')
      {
        lstrcpy(strErrMsg, _T("文字列の途中で改行"));
        return TK_ERROR;
      }
    }
    lstrcpy(strErrMsg, _T("文字列の途中でファイル終了"));
    return TK_ERROR;
  }

  // number
  else if (_istdigit(*topBuf) || !bInExpr && *topBuf == '-' && _istdigit(topBuf[1]))
  {
    TCHAR *pp;
    nVal = _tcstol(topBuf, &pp, 10);
    lenInt = pp - topBuf;
    if (bNoDot)
    {
      dVal = nVal;
      topBuf = pp;
      return TK_INT;
    }
    dVal = _tcstod(topBuf, &topBuf);
    return pp == topBuf ? TK_INT : TK_DBL;
  }

  // othe token
  else
  {
    TCHAR *p = topBuf + 1;
    switch (*topBuf)
    {
    case '#':
      uVal = (unsigned int)_tcstol(p, &topBuf, 16);
      if (p == topBuf)
      {
        lstrcpy(strErrMsg, _T("bad # format"));
        return TK_ERROR;
      }
      else
      {
        int r = uVal >> 16;
        int g = (uVal >> 8) & 0xff;
        int b = uVal & 0xff;
        colValue = RGB(r, g, b);
      }
      return TK_COLORVAL;
    case ',':
      topBuf++;
      return TK_COMMA;
    case '[':
      topBuf++;
      return TK_BEGINNAME;
    case ']':
      topBuf++;
      return TK_ENDNAME;
    case '(':
      topBuf++;
      return TK_IN;
    case ')':
      topBuf++;
      return TK_OUT;
    case '{':
      topBuf++;
      return TK_BEGIN;
    case '}':
      topBuf++;
      return TK_END;
    case '+':
      topBuf++;
      return TK_PLUS;
    case '-':
      topBuf++;
      return TK_MINUS;
    case '*':
      topBuf++;
      return TK_MULTI;
    case '/':
      if (topBuf[1] == '/')
      {
        topBuf++;
        topBuf++;
        while (*topBuf && *topBuf != '\n')
          topBuf = CharNext(topBuf);
        if (*topBuf == '\n')
        {
          topBuf++;
          lineNo++;
        }
        return TK_COMM;
      } 
      else if (topBuf[1] == '*')
      {
        topBuf++;
        topBuf++;
        while (*topBuf)
        {
          if (*topBuf == '\n')
            lineNo++;
          if (*topBuf == '*' && topBuf[1] == '/')
          {
            topBuf++;
            topBuf++;
            return TK_COMM;
          }
          else
            topBuf = CharNext(topBuf);
        }
        lstrcpy(strErrMsg, _T("eof in comment"));
        return TK_ERROR;
      }
      topBuf++;
      return TK_DIV;
    case '^':
      topBuf++;
      return TK_POW;
    case '=':
      topBuf++;
      return TK_EQ;
    case '!':
      topBuf++;
      if (*topBuf++ == '=')
      {
        topBuf++;
        return TK_NE;
      }
      else
      {
        lstrcpy(strErrMsg, _T("bad character after !"));
        return TK_ERROR;
      }
    case '<':
      topBuf++;
      if (*topBuf == '=')
      {
        topBuf++;
        return TK_LE;
      }
      else
        return TK_LT;
    case '>':
      topBuf++;
      if (*topBuf == '=')
      {
        topBuf++;
        return TK_GE;
      }
      else
        return TK_GT;
    case '&':
      topBuf++;
      return TK_AND;
    case '|':
      topBuf++;
      return TK_OR;
    case '@':
      topBuf++;
      return TK_AT;
    case ':':
      topBuf++;
      return TK_COLON;
    case '.':
      topBuf++;
      return TK_DOT;
    default:
      wsprintf(strErrMsg, _T("未定義文字 %d"), *topBuf);
      topBuf++;
      return TK_ERROR;
    }
  }

  return TK_EOF;
}

int GetToken(BOOL bNoDot)
{
  // when bakced
  if (backedToken >= 0)
  {
    int ret = backedToken;
    backedToken = -1;
    return ret;
  }

  // skip comment
  int token;
  do
  {
    token = GetTokenSub(bNoDot);
  } while (token == TK_COMM);
  return token;
}

void UngetToken(int token)
{
  backedToken = token;
}

TCHAR *TokenStr(int token)
{
  for (int i = 0; i < sizeof(KeyList) / sizeof(KeyList[0]); i++)
  {
    if (KeyList[i].code == token)
      return KeyList[i].name;
  }
  switch (token)
  {
  case TK_EOF:
    return _T("*EOF*");
  case TK_ERROR:
    return _T("*error*");
  case TK_BEGIN:
    return _T("{");
  case TK_END:
    return _T("}");
  case TK_ID:
    return _T("*id*");
  case TK_INT:
    return _T("*int*");
  case TK_UINT:
    return _T("*uint*");
  case TK_DBL:
    return _T("*double*");
  case TK_STR:
    return _T("*string*");
  case TK_MULTI:
    return _T("*");
  case TK_DIV:
    return _T("/");
  case TK_PLUS:
    return _T("+");
  case TK_MINUS:
    return _T("-");
  case TK_IN:
    return _T("(");
  case TK_OUT:
    return _T(")");
  case TK_COMMA:
    return _T(",");
  case TK_AT:
    return _T("@");
  case TK_COLON:
    return _T(":");
  case TK_BEGINNAME:
    return _T("[");
  case TK_ENDNAME:
    return _T("]");
  case TK_AND:
    return _T("&");
  case TK_OR:
    return _T("|");
  case TK_POW:
    return _T("^");
  case TK_EQ:
    return _T("=");
  case TK_NE:
    return _T("!=");
  case TK_LE:
    return _T("<=");
  case TK_LT:
    return _T("<");
  case TK_GE:
    return _T(">=");
  case TK_GT:
    return _T(">");
  }
  static TCHAR buf[16];
  wsprintf(buf, _T("#%d"), token);
  return buf;
}

BOOL CheckToken(int token, BOOL bNoDot)
{
  int tk = GetToken(bNoDot);
  if (tk != token)
  {
    wsprintf(strErrMsg, _T("token %s is not %s"), TokenStr(tk), TokenStr(token));
    return TRUE;
  }
  return FALSE;
}

BOOL CheckNum()
{
  int token = GetToken();
  if (token != TK_DBL && token != TK_INT)
  {
    wsprintf(strErrMsg, _T("%s is not num"), TokenStr(token));
    return TRUE;
  }
  return FALSE;
}

BOOL CheckText()
{
  int token = GetToken();
  if (token == TK_STR)
    textIn = strIn;
  else if (token == TK_ID)
    textIn = nameIn;
  else
  {
    wsprintf(strErrMsg, _T("token %s is not text"), TokenStr(token));
    return TRUE;
  }
  return FALSE;
}

#define APP_LEN 1024
TCHAR AppFile[APP_LEN + 1];
#define APP_TITLE 255
TCHAR AppTitle[APP_TITLE + 1];

BOOL GetSaveFile(HWND hWnd, BOOL bApp)
{
  OPENFILENAME ofn;
  ZeroMemory(&ofn, sizeof(ofn));
  ofn.lStructSize = sizeof(ofn);
  ofn.hwndOwner = hWnd;
  ofn.lpstrFilter = bApp ? _T("App Files(*.aaf)\0*.aaf\0All Files\0*.*\0\0") :
                           _T("Data Files(*.adf)\0*.adf\0All Files\0*.*\0\0");
  ofn.nMaxFile = _MAX_PATH;
  ofn.lpstrFile = bApp ? AppFile : FileName;
  ofn.nMaxFileTitle = APP_TITLE;
  ofn.lpstrFileTitle = bApp ? AppTitle : FileTitle;
  ofn.lpstrDefExt = bApp ? _T("aaf") : _T("adf");
  ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
  return GetSaveFileName(&ofn);
}

BOOL GetOpenFile(HWND hWnd, BOOL bApp)
{
  OPENFILENAME ofn;
  ZeroMemory(&ofn, sizeof(ofn));
  ofn.lStructSize = sizeof(ofn);
  ofn.hwndOwner = hWnd;
  ofn.lpstrFilter = bApp ? _T("App Files(*.aaf)\0*.aaf\0All Files\0*.*\0\0") :
                           _T("Data Files(*.adf)\0*.adf\0All Files\0*.*\0\0");
  ofn.nMaxFile = _MAX_PATH;
  ofn.lpstrFile = bApp ? AppFile : FileName;
  ofn.nMaxFileTitle = APP_TITLE;
  ofn.lpstrFileTitle = bApp ? AppTitle : FileTitle;
  ofn.lpstrDefExt = bApp ? _T("aaf") : _T("adf");
  ofn.Flags = OFN_HIDEREADONLY;
  return GetOpenFileName(&ofn);
}

BOOL bDirtyFlagD; /////////////////////
BOOL bDirtyFlagA; /////////////////////////

int CheckDirtyFlag(HWND hWnd)
{
  int ret = IDCANCEL;

  if (bDirtyFlagA)
  {
    ret = MessageBox(hWnd, _T("アプリは変更されています\n上書きしますか"), _T("save a"), MB_YESNOCANCEL);
    if (ret == IDCANCEL)
      return ret;
  }
  if (ret == IDYES)
  {
    // save app !!!!!
  }

  if (bDirtyFlagD)
  {
    ret = IDNO; ////////////////
//    ret = MessageBox(hWnd, _T("データは変更されています\n上書き保存しますか"), _T("save ?"), MB_YESNOCANCEL);
  }
  else
    ret = IDNO;

  if (ret == IDYES)
  {
    /*
    if (*FileName)
      OnSave(hWnd);
    else
    {
      if (!OnSaveAs(hWnd))
        ret = IDCANCEL;
    }
    */
  }

  return ret;
}

void OnClose(HWND hWnd)
{
  int ret = CheckDirtyFlag(hWnd);

  if (ret == IDYES ||ret == IDNO)
    DestroyWindow(hWnd);
}


void DoOpenA()
{
  HANDLE h = CreateFile(AppFile, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); //////////////
  if (h == INVALID_HANDLE_VALUE)
  {
    MessageBox(hMainWnd, _T("cannot open file"), AppFile, MB_OK);
    return;
  }
  int len = GetFileSize(h, NULL);
  int len2 = (len+1)/2;
  TCHAR *p = new TCHAR[len2 + 10];
  ReadFile(h, p, len, NULL, NULL);
  CloseHandle(h);
  p[len2] = 0;
  srcArasi = p;

  BOOL ret = ParseAaf(srcArasi + 1);
  if (ret)
  {
    TCHAR buf[200];
    wsprintf(buf, _T("Line %d : Arasiのエラー"), lineNo);
    MessageBox(hMainWnd, strErrMsg, buf, MB_OK);
    bDirtyFlagD = bDirtyFlagA = FALSE;
    return;
  }
}

void OnOpenA(HWND hWnd)
{
  if (CheckDirtyFlag(hWnd) == IDCANCEL)
    return;

  if (!GetOpenFile(hWnd, TRUE))
    return;

  DoOpenA();
}

void OnSaveA(HWND hWnd, BOOL bOver)
{
  if (!srcArasi || !*srcArasi)
    return;

  if (!bOver && !GetSaveFile(hWnd, TRUE))
    return;

  if (!*AppFile)
  {
    MessageBox(hWnd, _T("no file name"), _T("save File"), MB_OK);
    return;
  }

  HANDLE h = CreateFile(AppFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); //////////////
  if (h == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, _T("cannot open file"), AppFile, MB_OK);
    return;
  }

  int len = lstrlen(srcArasi);
  WriteFile(h, srcArasi, sizeof(TCHAR)*len, NULL, NULL);
  CloseHandle(h);
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
        UNREFERENCED_PARAMETER(lParam);
        switch (message)
        {
        case WM_INITDIALOG:
                return (INT_PTR)TRUE;

        case WM_COMMAND:
                if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
                {
                        EndDialog(hDlg, LOWORD(wParam));
                        return (INT_PTR)TRUE;
                }
                break;
        }
        return (INT_PTR)FALSE;
}

void ColorBox(HWND hDlg)
{
  CHOOSECOLOR cc;
  static COLORREF cr[32];
  ZeroMemory(&cc, sizeof(cc));
  cc.lStructSize = sizeof(cc);
  cc.hwndOwner = hDlg;
  cc.hInstance = NULL;
  cc.rgbResult = RGB(0, 0, 0);
  cc.lpCustColors = cr;
  cc.Flags = CC_ANYCOLOR | CC_RGBINIT | CC_FULLOPEN;
  if (ChooseColor(&cc))
  {
    unsigned u = cc.rgbResult;
    int b = (u & 0xFF0000) >> 16;
    int g = (u & 0xFF00) >> 8;
    int r = u & 0xFF;

    int st, en;
    SendMessage(GetDlgItem(hDlg, IDC_SRC), EM_GETSEL, (WPARAM)&st, (LPARAM)&en);
    TCHAR ss[200];
    wsprintf(ss, _T("Color(%d,%d,%d),"), r, g, b);

    int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_SRC));
    int len2 = len + lstrlen(ss) + 10;
    TCHAR *p = new TCHAR[len2];
    p[0] = 0xFEFF;  // BOM
    GetWindowText(GetDlgItem(hDlg, IDC_SRC), p + 1, len2-2);
    if (srcArasi)
      delete [] srcArasi;
    srcArasi = new TCHAR[len2];
    for (int i = 0; i < st+1; i++)
      srcArasi[i] = p[i];
    lstrcpy(srcArasi + st + 1, ss);
    lstrcat(srcArasi + st + 1, p + st + 1);
    SetWindowText(GetDlgItem(hDlg, IDC_SRC), srcArasi + 1);
   }
}

LOGFONT lfSelect;
int sizeSelect;

void FontBox(HWND hDlg)
{
  GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lfSelect);
  lstrcpy(lfSelect.lfFaceName, _T("MS ゴシック"));
  lfSelect.lfHeight = int(crtYRes*10/72);
  lfSelect.lfWidth = 0;
  CHOOSEFONT cf;
  ZeroMemory(&cf, sizeof(cf));
  cf.lStructSize = sizeof(cf);
  cf.hwndOwner = hDlg;
  cf.hDC = NULL;
  cf.lpLogFont = &lfSelect;
  cf.Flags = CF_INITTOLOGFONTSTRUCT;

  if (ChooseFont(&cf))
  {
    sizeSelect = cf.iPointSize;
    ///////////
    int st, en;
    SendMessage(GetDlgItem(hDlg, IDC_SRC), EM_GETSEL, (WPARAM)&st, (LPARAM)&en);
    TCHAR ss[200];
    wsprintf(ss, _T("Font(\"%s\",%d"), lfSelect.lfFaceName, cf.iPointSize);
    if (lfSelect.lfWeight == FW_BOLD)
      lstrcat(ss, _T(",Bold"));
    if (lfSelect.lfItalic)
      lstrcat(ss, _T(",Italic"));
    if (lfSelect.lfUnderline)
      lstrcat(ss, _T(",Under"));    
    lstrcat(ss, _T("),"));

    int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_SRC));
    int len2 = len + lstrlen(ss) + 10;
    TCHAR *p = new TCHAR[len2];
    p[0] = 0xFEFF;  // BOM
    GetWindowText(GetDlgItem(hDlg, IDC_SRC), p + 1, len2-2);
    if (srcArasi)
      delete [] srcArasi;
    srcArasi = new TCHAR[len2];
    for (int i = 0; i < st+1; i++)
      srcArasi[i] = p[i];
    lstrcpy(srcArasi + st + 1, ss);
    lstrcat(srcArasi + st + 1, p + st + 1);
    SetWindowText(GetDlgItem(hDlg, IDC_SRC), srcArasi + 1);
  }
}

INT_PTR CALLBACK EditArasi(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
        UNREFERENCED_PARAMETER(lParam);
  int n = LOWORD(wParam);
        switch (message)
        {
        case WM_INITDIALOG:
    if (srcArasi && *srcArasi)
    {
      SetWindowText(GetDlgItem(hDlg, IDC_SRC), srcArasi + 1); // 先頭はBOM
      SetFocus(GetDlgItem(hDlg, IDC_SRC));
    }
                return (INT_PTR)TRUE;

        case WM_COMMAND:
    switch (n)
    {
    case IDOK:
      {
        if (srcOld)
          delete [] srcOld;
        srcOld = srcArasi;
        int len = GetWindowTextLength(GetDlgItem(hDlg, IDC_SRC));
        srcArasi = new TCHAR[len + 3];
        GetDlgItemText(hDlg, IDC_SRC, srcArasi + 1, len + 1);
        srcArasi[0] = 0xFEFF;

        BOOL ret = ParseAaf(srcArasi + 1);
        if (ret)
        {
          TCHAR buf[200];
          wsprintf(buf, _T("Line %d : Arasiのエラー"), lineNo);
          MessageBox(hDlg, strErrMsg, buf, MB_OK);
          delete [] srcArasi;
          srcArasi = srcOld;
          srcOld = NULL;
          ParseAaf(srcArasi + 1);
          return (INT_PTR)TRUE;
        }
      }
      // no break
    case IDCANCEL:
                        EndDialog(hDlg, LOWORD(wParam));
                        return (INT_PTR)TRUE;
    case IDC_FONT:
      FontBox(hDlg);
      return (INT_PTR)TRUE;
    case IDC_COLOR:
      ColorBox(hDlg);
      return (INT_PTR)TRUE;
    default:
      break;
    }
                break;
        }
        return (INT_PTR)FALSE;
}

void AddVariable(Set *pSet, Variable *p)
{
  p->next = pSet->varTop;
  pSet->varTop = p;
}

Variable *FindVariable(Set *pSet, TCHAR *name)
{
  for (Variable *p = pSet->varTop; p; p = p->next)
  {
    if (!lstrcmp(p->name, name))
      return p;
  }
  return NULL;
}

void ShowError(TCHAR *str)
{
  SetWindowText(hMainWnd, str);
}

#define leap(y) ((y)%4 == 0 && ((y)%100 != 0 || (y)%400 == 0))

static int DaysOfMonth[2][12] = {
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
    31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

static int md[2][12] = {
       0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
       0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };

int TotalDays(int y, int m, int d)
{
  int ty = y-1;
  int t = ty*365 + ty/4 - ty/100 + ty/400;

  t += md[leap(y)][m-1];
  return t + d;
}

int TotalDays(int day)
{
  return TotalDays(day/10000, (day%10000)/100, day%100);
}

int DaysInMonth(int y, int m)
{
  return DaysOfMonth[leap(y)][m-1];
}

int ThisDay(int mode /* = 0 */)
{
  SYSTEMTIME st;
  GetLocalTime(&st);
  int n = st.wYear*10000 + st.wMonth*100 + st.wDay;
  if (mode == 1)
  {
    n = (n/10000)*10000 + 101;  // Jan.1
  }
  else if (mode == 2)
  {
    n = (n/10000)*10000 + 1231;  // Dec.31
  }
  else if (mode == 3)
  {
    n = (n/100)*100 + 1;         // 1st on the same month
  }
  else if (mode == 4)
  {
    int y = n/10000;
    int m = (n/100)%100;
    n = (n/100)*100 + DaysInMonth(y, m);
  }
  return n;
}

int ThisDayTime()
{
  SYSTEMTIME st;
  GetLocalTime(&st);
  return TotalDays(st.wYear, st.wMonth, st.wDay)*(24*60) + st.wHour*60 + st.wMinute;
}

void TotalToDate(int td, int& y, int& m, int& d)
{
  //  year
  td--;
  int ya = td / (365*400 + 97);
  int t1 = td % (365*400 + 97);
  int yb = t1 / (365*100 + 24);
  int t2 = t1 % (365*100 + 24);
  int yc = t2 / (365*4 + 1);
  int t3 = t2 % (365*4 + 1);
  int yd = t3 / 365;
  int t4 = t3 % 365;
  if (t1 == 365*400 + 96)                  // leap year : 400
  {
    y = (ya+1)*400;
    t4 = 365;
  }
  else
  {
    if (yd > 3)                            // leap year : 4
    {
      yd = 3;
      t4 = 365;
    }
    y = ya*400 + yb*100 + yc*4 + yd + 1;
  }

  // month
  static int *ipM;   // work : not yet deleted ?????????????
  if (!ipM)
  {
    ipM = new int[366*2];
    int at = 0;
    for (int i = 0; i < 12; i++)
    {
      int n = DaysOfMonth[0][i];
      for (int j = 0; j < n; j++)
      {
        ipM[at++] = i+1;
      }
    }
    for (int i = 0; i < 12; i++)
    {
      int n = DaysOfMonth[1][i];
      for (int j = 0; j < n; j++)
      {
        ipM[at++] = i+1;
      }
    }
  }
  int yy = leap(y);
  m = ipM[yy*365 + t4];

  // day
  d = t4 - md[yy][m-1] + 1;
}

int FromTotal(int td)
{
  int y, m, d;
  TotalToDate(td, y, m, d);
  return y * 10000 + m * 100 + d;
}

int CompleteDate(int date, int offset, int off2)
{
  int dd = ThisDay(); //defDate(date);
  if (date == 0)
    return dd;

  date += off2*10000;
  if (date > 10000 && date < 100 * 10000)
  {
    if (date/10000 < 50)
      date += 20000000;
    else
      date += 19000000;
  }

  if (date < 1)
    date = dd;
  else if (date < 100)
    date += (dd/100)*100;
  else if (date < 10000)
    date += (dd/10000)*10000;
  date += offset*10000;

  // returns -1 for bad date
  int d = date % 100;
  int m = (date % 10000)/100;
  int y = date/10000;
  if (m < 1 || m > 12)
    return -1;
  if (d < 1 || d > DaysOfMonth[leap(y)][m-1])
    return -1;

  return date;
}

int StrToDate(TCHAR *str)
{
  while (_istspace(*str))
    str++;
  if (*str == '0')
    return 0;

  int off2 = 0;

  // prev/next year
  int offset = 0;
  while (*str == '+' || *str == '-')
  {
    if (*str == '+')
      offset++;
    else
      offset--;
    str++;
  }

  // 20010203
  TCHAR *p;
  int dd = _tcstol(str, &p, 10);
  if (*p == 0)
    return CompleteDate(dd, offset, off2);

  int y, m, d;

  // 2001.2.3
  if (_stscanf_s(str, _T("%d.%d.%d"), &y, &m, &d) == 3)
    return CompleteDate(y * 10000 + m*100 + d, 0, off2);

  // 2001/2/3
  if (_stscanf_s(str, _T("%d/%d/%d"), &y, &m, &d) == 3)
    return CompleteDate(y * 10000 + m * 100 + d, 0, off2);

  // 2001-2-3
  if (_stscanf_s(str, _T("%d-%d-%d"), &y, &m, &d) == 3)
    return CompleteDate(y * 10000 + m * 100 + d, 0, off2);

  // 2-3, add year
  if (_stscanf_s(str, _T("%d-%d"), &m, &d) == 2)
    return CompleteDate(m*100+d, offset, off2);

  // 2/3, add year
  if (_stscanf_s(str, _T("%d/%d"), &m, &d) == 2)
    return CompleteDate(m*100+d, offset, off2);

  // 2.3
  if (_stscanf_s(str, _T("%d.%d"), &m, &d) == 2)
    return CompleteDate(m*100+d, offset, off2);

  // 3, add month/year
  if (_stscanf_s(str, _T("%d"), &d) == 1)
    return CompleteDate(d, offset, off2);

  // if nothing, return today
  return CompleteDate(0, offset, off2);
}

TCHAR *DateStr(int date, TCHAR buf[])
{
  buf[0] = 0;
  if (date <= 0 || date >= 99999999)
  {
    return buf;
  }
  wsprintf(buf, _T("%d.%d.%d"), date/10000, (date/100)%100, date%100);
  return buf;
}

char *GetDataPtr(DataSet *pD, int rec)
{
  int num = pD->RecNum;
  if (rec < 0)
  {
    if (!pD->RecTemp)
    {
      char *pp = new char[pD->total];
      ZeroMemory(pp, pD->total*sizeof(char));
      pD->RecTemp = pp;
    }
    return pD->RecTemp;
  }
  if (rec > num)
  {
    ShowError(_T("bad rec num in GetDataPtr"));
    return NULL;
  }

  if (rec == num)
  {
    if (num >= pD->RecMax)
    {
      pD->RecMax += 32;
      PCHAR *p = new PCHAR[pD->RecMax];
      ZeroMemory(p, pD->RecMax*sizeof(PCHAR));
      for (int i = 0; i < num; i++)
        p[i] = pD->RecData[i];
      if (pD->RecData)
        delete [] pD->RecData;
      pD->RecData = p;
    }

    if (!pD->RecData[rec])
    {
      char *pp = new char[pD->total];
      ZeroMemory(pp, pD->total*sizeof(char));
      pD->RecData[rec] = pp;
    }
    pD->RecNum++;
    if (!pD->bTemp)
      bDirtyFlagD = TRUE;
  }

  return pD->RecData[rec];
}

void SwapRecTo(DataSet *pD, int rec)
{
  if (rec < 0 || rec >= pD->RecNum)
    return;

  if (rec == pD->RecNum - 1)
    return;

  char *p = pD->RecData[pD->RecNum - 1];
  for (int i = pD->RecNum-1; i > rec; i--)
    pD->RecData[i] = pD->RecData[i-1];
  pD->RecData[rec] = p;
  if (!pD->bTemp)
    bDirtyFlagD = TRUE;
}

void ZapRecord(DataSet *pD)
{
  if (pD->RecNum > 0 && !pD->bTemp)
    bDirtyFlagD = TRUE;
  for (int i = 0; i < pD->RecNum; i++)
  {
    char *p = pD->RecData[i];
    if (!p)
      continue;
    for (int j = 0; j < pD->num; j++)
    {
      int type = pD->item[j].type;
      if (type == FT_STR || type == FT_STRMUL)
      {
        TCHAR *pp = *(TCHAR**)(p + pD->offset[j]);
        if (pp)
          delete [] pp;
      }
    }
    ZeroMemory(p, pD->total*sizeof(char));
  }
  pD->RecNum = 0;
}

void DeleteRecord(DataSet *pD, int rec)
{
  if (rec < 0 || rec >= pD->RecNum)
    return;
  char *p = pD->RecData[rec];
  if (!p)
    return;

  if (!pD->bTemp)
    bDirtyFlagD = TRUE;
  for (int i = 0; i < pD->num; i++)
  {
    int type = pD->item[i].type;
    if (type == FT_STR || type == FT_STRMUL)
    {
      TCHAR *pp = *(TCHAR**)(p + pD->offset[i]);
      if (pp)
        delete[] pp;
    }
  }
  ZeroMemory(p, pD->total*sizeof(char));

  for (int i = rec; i < pD->RecNum-1; i++)
    pD->RecData[i] = pD->RecData[i+1];
  pD->RecData[pD->RecNum-1] = NULL;
  delete [] p;
  pD->RecNum--;
}

BOOL IsIntItem(DataSet *pD, int pos)
{
  if (pos < 0 || pos >= pD->num)
    return FALSE;
  switch (pD->item[pos].type)
  {
  case FT_ID: case FT_INT: case FT_DATE: case FT_TIME: case FT_LINK:
    return TRUE;
  }
  return FALSE;
}

BOOL IsDblItem(DataSet *pD, int pos)
{
  if (pos < 0 || pos >= pD->num)
    return FALSE;
  if (pD->item[pos].type == FT_DBL)
    return TRUE;
  return FALSE;
}

BOOL IsStrItem(DataSet *pD, int pos)
{
  if (pos < 0 || pos >= pD->num)
    return FALSE;
  int type = pD->item[pos].type;
  if (type == FT_STR || type == FT_STRMUL)
    return TRUE;
  return FALSE;
}

BOOL bOneOf;

void SetDataInt(DataSet *pD, int rec, int pos, int nVal)
{
  if (pD->bReadOnly)
    return;
  if (!bOneOf)
    CompleteField();
  if (!IsIntItem(pD, pos))
    return;
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return;

  char *pp = p + pD->offset[pos];
  if (*(int*)pp == nVal)
    return;

  *(int*)pp = nVal;
  if (!pD->bTemp && !pD->item[pos].bTemp)
    bDirtyFlagD = TRUE;

  if (pos == pD->posID)
    pD->LastID = nVal;
}

void SetDataDbl(DataSet *pD, int rec, int pos, double dVal)
{
  if (pD->bReadOnly)
    return;
  CompleteField();
  if (!IsDblItem(pD, pos))
    return;
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return;
  char *pp = p + pD->offset[pos];
  if (*(double*)pp == dVal)
    return;

  *(double*)pp = dVal;
  if (!pD->bTemp && !pD->item[pos].bTemp)
    bDirtyFlagD = TRUE;
}

void SetDataStr(DataSet *pD, int rec, int pos, TCHAR *str)
{
  if (pD->bReadOnly)
    return;
  if (!bOneOf)
    CompleteField();
  if (!IsStrItem(pD, pos))
    return;
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return;
  char *q = p + pD->offset[pos];
  TCHAR *pp = *(TCHAR**)q;
  if (!pp && (!str || !*str))
    return;
  if (!str)
    str = _T("");
  if (pp && !lstrcmp(pp, str))
    return;

  if (pp)
    delete [] pp;
  pp = new TCHAR[lstrlen(str) + 1];
  lstrcpy(pp, str);
  *(TCHAR**)q = pp;
  if (!pD->bTemp && !pD->item[pos].bTemp)
    bDirtyFlagD = TRUE;
}

void IDSort(DataSet *pD)
{
  if (pD->posID < 0)
    return;
  int off = pD->offset[pD->posID];
  PCHAR *pRec = pD->RecData;
  for (int i = 1; i < pD->RecNum; i++)
  {
    int rec = i;
    char *p = pRec[i];
    int id = *(int*)(p + off);
    for (int j = i - 1; j >= 0; j--)
    {
      char *q = pRec[j];
      int id2 = *(int*)(q + off);
      if (id == id2)
      {
        ShowError(_T("id duped in IDSort"));
        return;
      }
      if (id > id2)
        break;
      rec = j;
      pRec[j+1] = pRec[j];
    }
    if (rec < i)
      pRec[rec] = p;
  }
}

int GetDataInt(DataSet *pD, int rec, int pos)
{
  if (!IsIntItem(pD, pos))
    return 0;
  if (rec >= pD->RecNum)
    return 0;
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return 0;
  char *pp = p + pD->offset[pos];
  return *(int*)pp;
}

double GetDataDbl(DataSet *pD, int rec, int pos)
{
  if (!IsDblItem(pD, pos))
    return 0;
  if (rec >= pD->RecNum)
    return 0;
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return 0;
  char *pp = p + pD->offset[pos];
  return *(double*)pp;
}

TCHAR *GetDataStr(DataSet *pD, int rec, int pos)
{
  if (!IsStrItem(pD, pos))
    return _T("");
  if (rec < 0 || rec >= pD->RecNum)
    return _T("");  ///////////////
  char *p = GetDataPtr(pD, rec);
  if (!p)
    return _T("");
  char *pp = p + pD->offset[pos];
  TCHAR *q = *(TCHAR**)pp;
  return q ? q : _T("");
}

TCHAR *GetDataAsStr(DataSet *pD, int rec, int pos, BOOL bSave)
{
  static TCHAR buf[256];
  if (pos < 0)
  {
    wsprintf(buf, _T("%d"), rec);
    return buf;
  }
  if (!pD || pos >= pD->num || rec < 0 || rec >= pD->RecNum)
  {
    if (!pD) return _T("abc"); /////////
    return _T("bbb");
  }
  int type = pD->item[pos].type;

  if (type == FT_DBL)
  {
    double val = GetDataDbl(pD, rec, pos);
    return ValToStr(val, 100, !bSave); //////////// 100 !!!!!!!!!!!!!
  }
  else if (type == FT_STR || type == FT_STRMUL)
  {
    TCHAR *p = GetDataStr(pD, rec, pos);
    return p ? p : _T("");
  }
  else if (type == FT_LINK)
  {
    int n = GetDataInt(pD, rec, pos);
    if (bSave)
    {
      wsprintf(buf, _T("%d"), n);
      return buf;
    }
    DataSet *pLink = pD->item[pos].link;
    if (n < 0)
    {
      wsprintf(buf, _T("<%d>"), n);
      return buf;
    }
    if (!pLink)
    {
      return _T("---");
    }
    if (pLink->posName < 0)
    {
      wsprintf(buf, _T("%d"), n);
      return buf;
    }
    int at = n;
    if (pLink->posID >= 0)
      at = FindIDData(pLink, n);

    if (at < 0)
      return _T("***");
    int pos = pLink->posName < 0 ? 0 : pLink->posName;
    TCHAR *p = GetDataStr(pLink, at, pos);
    return p ? p : _T("+++");
  }
  else
  {
    int val = GetDataInt(pD, rec, pos);
    if (type == FT_DATE)
    {
      if (bSave)
        wsprintf(buf, _T("%d"), val);
      else if (val == 0)
        buf[0] = 0;
      else
        wsprintf(buf, _T("%d.%d.%d"), val/10000, (val/100)%100, val%100);
    }
    else if (type == FT_TIME)
    {
      if (bSave)
        wsprintf(buf, _T("%d"), val);
      else if (val == 0)
        buf[0] = 0;
      else
      {
        int mm = val % 60;
        val /= 60;
        int hh = val % 24;
        val /= 24;
        int y, m, d;
        TotalToDate(val, y, m, d);
        wsprintf(buf, _T("%d.%d      "), m, d);
        wsprintf(buf + 7, _T("%d:%02d"), hh, mm);
      }
    }
    else // FT_ID or other int
    {
      wsprintf(buf, _T("%d"), val);
    }
    return buf;
  }

  // other case
  return _T("vv");
}

TCHAR *GetPathAsStr(Path *pPath, int rec)
{
  DataSet *pD = pPath->pD;
 // if (pPath->len == 1)
   // return GetDataAsStr(pD, rec, pPath->ip[0], FALSE);

  for (int i = 0; i < pPath->len-1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  return GetDataAsStr(pD, rec, pPath->ip[pPath->len-1], FALSE);
}

double GetPathDbl(Path *pPath, int rec)
{
  DataSet *pD = pPath->pD;
//  if (pPath->len == 1)
  //  return GetDataDbl(pD, rec, pPath->ip[0]);

  for (int i = 0; i < pPath->len-1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  return GetDataDbl(pD, rec, pPath->ip[pPath->len - 1]);
}

void SetPathDbl(Path *pPath, int rec, double x)
{
  DataSet *pD = pPath->pD;
  /*
  if (pPath->len == 1)
  {
    SetDataDbl(pD, rec, pPath->ip[0], x);
    return;
  }
  */

  for (int i = 0; i < pPath->len-1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  SetDataDbl(pD, rec, pPath->ip[pPath->len - 1], x);
}

int GetPathInt(Path *pPath, int rec)
{
  DataSet *pD = pPath->pD;

  for (int i = 0; i < pPath->len - 1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  return GetDataInt(pD, rec, pPath->ip[pPath->len - 1]);
}

int GetPathType(Path *pPath)
{
  DataSet *pD = pPath->pD;
  for (int i = 0; i < pPath->len - 1; i++)
  {
    pD = pD->item[pPath->ip[i]].link;
  }
  return pD->item[pPath->ip[pPath->len - 1]].type;
}

TCHAR *GetPathExt(Path *pPath, int rec)
{
  DataSet *pD = pPath->pD;
  if (pPath->len == 1)
  {
    if (pD->item[pPath->ip[0]].type == FT_LINK)
    {
      int n = GetDataInt(pD, rec, pPath->ip[0]);
      DataSet *pLink = pD->item[pPath->ip[0]].link;
      rec = FindIDData(pLink, n);
      if (pLink->posName < 0)
        return _T("");
      else
        return GetDataStr(pLink, rec, pLink->posName);
    }
    else
      return _T("");
  }
  else
  {
    for (int i = 0; i < pPath->len - 1; i++)
    {
      int n = GetDataInt(pD, rec, pPath->ip[i]);
      pD = pD->item[pPath->ip[i]].link;
      rec = FindIDData(pD, n);
    }
    if (pD->item[pPath->ip[pPath->len - 1]].type == FT_LINK)
    {
      int n = GetDataInt(pD, rec, pPath->ip[pPath->len - 1]);
      pD = pD->item[pPath->ip[pPath->len - 1]].link;
      rec = FindIDData(pD, n);
      if (pD->posName < 0)
        return _T("");
      else
        return GetDataStr(pD, rec, pD->posName);
    }
    else
      return _T("");
  }
  return _T("");
}

TCHAR *GetPathStr(Path *pPath, int rec)
{
  DataSet *pD = pPath->pD;
//  if (pPath->len == 1)
  //  return GetDataStr(pD, rec, pPath->ip[0]);

  for (int i = 0; i < pPath->len - 1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  return GetDataStr(pD, rec, pPath->ip[pPath->len - 1]);
}

void SetPathInt(Path *pPath, int rec, int n)
{
  DataSet *pD = pPath->pD;
  /*
  if (pPath->len == 1)
  {
    SetDataInt(pD, rec, pPath->ip[0], n);
    return;
  }
  */

  for (int i = 0; i < pPath->len - 1; i++)
  {
    int n = GetDataInt(pD, rec, pPath->ip[i]);
    pD = pD->item[pPath->ip[i]].link;
    rec = FindIDData(pD, n);
  }
  SetDataInt(pD, rec, pPath->ip[pPath->len - 1], n);
}

TCHAR *GetPathName(Path *pPath)
{
  DataSet *pD = pPath->pD;
  if (pPath->len == 1)
    return pD->item[pPath->ip[0]].name;

  for (int i = 0; i < pPath->len-1; i++)
  {
    pD = pD->item[pPath->ip[i]].link;
  }
  return pD->item[pPath->ip[pPath->len-1]].name;
}

int PathType(Path *p)
{
  DataSet *pD = p->pD;
  for (int i = 0; i < p->len-1; i++)
    pD = pD->item[p->ip[i]].link;
  return pD->item[p->ip[p->len - 1]].type;
}

int FindIDData(DataSet *pD, int id)
{
  if (!pD)
    return -1;
  int pos = pD->posID;
  if (pos < 0)
  {
    return id;
  }
  int low = 0;
  int high = pD->RecNum - 1;
  while (low <= high)
  {
    int mid = (low + high) / 2;
    int n = GetDataInt(pD, mid, pos);
    if (id < n)
      high = mid - 1;
    else if (id > n)
      low = mid + 1;
    else
      return mid;
  }
  return -1;
}

int NextID(DataSet *pD, BOOL bLast)
{
  if (!pD || pD->posID < 0)
    return 0;
  int step = pD->nStep > 0 ? pD->nStep : 1;
  if (!bLast && FindIDData(pD, pD->LastID + step) < 0)
    return pD->LastID + step;

  int n = 0;
  if (pD->RecNum > 0)
    n = GetDataInt(pD, pD->RecNum-1, pD->posID);
  return n + step;
}

static BOOL bEndTemp;
const int maxPath = 100;
DataSet *pLastDataSet;
int numDataItem, maxDataItem;
DataItem *pTempItem;


DataItem *NewDataItem()
{
  if (bEndTemp)
  {
    ShowError(_T("NewDataItem after end"));
    return NULL;
  }
  if (numDataItem >= maxDataItem)
  {
    maxDataItem += 32;
    DataItem *p = new DataItem[maxDataItem];
    ZeroMemory(p, sizeof(DataItem)*maxDataItem);
    for (int i = 0; i < numDataItem; i++)
      p[i] = pTempItem[i];
    if (pTempItem)
      delete [] pTempItem;
    pTempItem = p;
  }
  return &pTempItem[numDataItem++];
}

void ClearTempItem()
{
  if (pTempItem)
  {
    delete [] pTempItem;
    pTempItem = NULL;
  }
  maxDataItem = numDataItem = NULL;
  bEndTemp = TRUE;
}

void AddPath(DataSet *pD, int len, int *ip)
{
  Path *p = new Path;
  ZeroMemory(p, sizeof(Path));
  p->pD = pD;
  p->len = len;
  p->ip = new int[len];
  for (int i = 0; i < len; i++)
    p->ip[i] = ip[i];
  p->next = pD->pPathTop;
  pD->pPathTop = p;
}

void AddPathSub(DataSet *pD, DataSet *pDD, int len, int *ip)
{
  if (len >= maxPath)
    return;
  for (int i = 0; i < pDD->num; i++)
  {
    ip[len] = i;
    AddPath(pD, len + 1, ip);
    if (pDD->item[i].type == FT_LINK)
    {
      DataSet *pLink = pDD->item[i].link;
      AddPathSub(pD, pLink, len + 1, ip);
    }
  }
}

void AddPathTemp(int pos, DataSet *pLink)
{
  int ip[maxPath + 8];  // a bit more
  ip[0] = pos;
  AddPath(pLastDataSet, 1, ip);
  if (pLink)
    AddPathSub(pLastDataSet, pLink, 1, ip);
}

void CompleteField()
{
  if (numDataItem == 0 || !pLastDataSet)
    return;

  DataItem *p = new DataItem[numDataItem];
  for (int i = 0; i < numDataItem; i++)
    p[i] = pTempItem[i];

  pLastDataSet->item = p;
  pLastDataSet->num = numDataItem;
  int total = 0;
  int *offset = new int[numDataItem];
  for (int i = 0; i < numDataItem; i++)
  {
    offset[i] = total;
    switch (p[i].type)
    {
    case FT_ID: case FT_INT: case FT_DATE: case FT_TIME: case FT_SEC: case FT_LINK:
      total += sizeof(int);
      break;
    case FT_DBL:
      total += sizeof(double);
      break;
    case FT_STR: case FT_STRMUL:
      total += sizeof(PCHAR);
      break;
    default:
      ShowError(_T("bad type in CompleteField"));
      break;
    }
  }
  pLastDataSet->total = total;
  pLastDataSet->offset = offset;

  ZeroMemory(pTempItem, sizeof(DataItem)*numDataItem);
  numDataItem = 0;
  DataSet *pD = pLastDataSet;
  pLastDataSet = NULL;
}

DataSet *FindDataSet(TCHAR *name)
{
  for (DataSet *p = pDataSetTop; p; p = p->next)
  {
    if (!lstrcmp(p->name, name))
      return p;
  }
  return NULL;
}

DataSet *MakeDataSet(TCHAR *name)
{
  CompleteField();
  if (FindDataSet(name))
  {
    wsprintf(strErrMsg, _T("DataSet %s already exists"), name);
    return NULL;
  }
  DataSet *p = new DataSet;
  ZeroMemory(p, sizeof(DataSet));
  p->nStep = 10;
  p->posID = -1;
  p->posName = -1;
  p->name = new TCHAR[lstrlen(name) + 1];
  lstrcpy(p->name, name);
  p->next = NULL;
  if (!pDataSetTop)
    pDataSetTop = p;
  else
  {
    DataSet *pp;
    for ( pp = pDataSetTop; pp->next; pp = pp->next)
      ;
    pp->next = p;
  }
  pLastDataSet = p;
  return p;
}

DataSet *MakeLinkTo(TCHAR *name)
{
  DataSet *p = new DataSet;
  ZeroMemory(p, sizeof(DataSet));
  p->nStep = 10;
  p->posID = 0;
  p->posName = 1;
  p->name = new TCHAR[lstrlen(name) + 1];
  lstrcpy(p->name, name);
  p->num = 2;
  p->item = new DataItem[2];
  ZeroMemory(p->item, sizeof(DataItem)*2);
  TCHAR *str = _T("ID");
  p->item[0].name = new TCHAR[lstrlen(str) + 1];
  lstrcpy(p->item[0].name, str);
  str = _T("name");
  p->item[1].name = new TCHAR[lstrlen(str) + 1];
  lstrcpy(p->item[1].name, str);
  p->item[0].type = FT_ID;
  p->item[1].type = FT_STR;
  p->item[1].wid = 30;
  p->total = sizeof(int) + sizeof(PCHAR);
  p->offset = new int[2];
  p->offset[0] = 0;
  p->offset[1] = sizeof(int);
  p->next = NULL;
  if (!pDataSetTop)
    pDataSetTop = p;
  else
  {
    DataSet *pp;
    for ( pp = pDataSetTop; pp->next; pp = pp->next)
      ;
    pp->next = p;
  }
  return p;
}

DataItem *AddField(TCHAR *name, int type, DataSet *pLink)
{
  if (!pLastDataSet)
  {
    lstrcpy(strErrMsg, _T("no dataset in AddField"));
    return NULL;
  }
  int pos = numDataItem;
  DataItem *p = NewDataItem();
  if (!name)
    name = _T("");
  p->name = new TCHAR[lstrlen(name) + 1];
  lstrcpy(p->name, name);
  p->type  = type;
  p->link = pLink;
  AddPathTemp(pos, pLink);
  return p;
}

DataItem *LinkField(TCHAR *name, DataSet *pLink)
{
  return AddField(name, FT_LINK, pLink);
}

DataItem *IdField(TCHAR *name)
{
  if (pLastDataSet->posID >= 0)
  {
    ShowError(_T("id duped in IdField"));
    return NULL;
  }
  pLastDataSet->posID = numDataItem;
  return AddField(name, FT_ID, NULL);
}

DataItem *StrField(TCHAR *name)
{
  if (pLastDataSet->posName < 0)
    pLastDataSet->posName = numDataItem;
  return AddField(name, FT_STR, NULL);
}

DataItem *StrMulField(TCHAR *name)
{
  return AddField(name, FT_STRMUL, NULL);
}

DataItem *NameField(TCHAR *name)
{
  pLastDataSet->posName = numDataItem;
  return AddField(name, FT_STR, NULL);
}

DataItem *DateField(TCHAR *name)
{
  return AddField(name, FT_DATE, NULL);
}

DataItem *TimeField(TCHAR *name)
{
  return AddField(name, FT_TIME, NULL);
}

DataItem *SecField(TCHAR *name)
{
  return AddField(name, FT_SEC, NULL);
}

DataItem *IntField(TCHAR *name)
{
  return AddField(name, FT_INT, NULL);
}

DataItem *DblField(TCHAR *name)
{
  return AddField(name, FT_DBL, NULL);
}

BOOL GetDataSet(int type)
{
  if (CheckToken(TK_IN))
    return TRUE;
  if (CheckToken(TK_STR))
    return TRUE;
  if (FindDataSet(strIn))
  {
    wsprintf(strErrMsg, _T("DataSet %s duped"), strIn);
    return TRUE;
  }
  DataSet *pLink = NULL;
  DataSet *pD = MakeDataSet(strIn);
  if (!pD)
    return TRUE;
  pD->type = type;
  if (CheckToken(TK_OUT))
    return TRUE;
  if (CheckToken(TK_BEGIN))
    return TRUE;
  BOOL bWork = FALSE;
  while (1)
  {
    int token = GetToken();
    if (token == TK_END)
      break;
    if (token == TK_WORK)
    {
      bWork = TRUE;
      break;
    }
    if (CheckToken(TK_IN))
      return TRUE;
    TCHAR name[100], name2[100];
    if (CheckToken(TK_STR))
      return TRUE;
    if (lstrlen(strIn) > 90)
    {
      lstrcpy(strErrMsg, _T("item name too long"));
      return TRUE;
    }
    lstrcpy(name, strIn);

    DataItem *pi;
    switch (token)
    {
    case TK_IDNUM:
      if (!(pi = IdField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_NAME:
      if (!(pi = NameField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_STRITEM: case TK_WORD:
      if (!(pi = StrField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_STRMULITEM:
      if (!(pi = StrMulField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_LINK:
      lstrcpy(name2, name);
      token = GetToken();
      if (token != TK_COMMA)
      {
        UngetToken(token);
      }
      else
      {
        if (CheckToken(TK_STR))
          return TRUE;
        if (lstrlen(strIn) > 90)
        {
          lstrcpy(strErrMsg, _T("link name too long in GetDataSet"));
          return TRUE;
        }
        lstrcpy(name, strIn);
      }
      pLink = FindDataSet(name2);
      if (!pLink)
      {
        pLink = MakeLinkTo(name2);
      }
      if (!(pi = LinkField(name, pLink)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_VAL:
      if (!(pi = DblField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_NUM:
      if (!(pi = IntField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_DATE:
      if (!(pi = DateField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_TIME:
      if (!(pi = TimeField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    case TK_SEC:
      if (!(pi = SecField(name)))
        return TRUE;
      if (CheckToken(TK_OUT))
        return TRUE;
      break;
    default:
      wsprintf(strErrMsg, _T("bad token %s in GetDataSet"), TokenStr(token));
      return TRUE;
    }
    while (1)
    {
      token = GetToken();
      if (token == TK_COLON)
      {
        token = GetToken();
        if (token == TK_WID)
        {
          if (CheckToken(TK_IN))
            return TRUE;
          if (CheckNum())
            return TRUE;
          pi->wid = dVal;
          if (CheckToken(TK_OUT))
            return TRUE;
        }
        else if (token == TK_WORK)
        {
          pi->bTemp = TRUE;
        }
        else if (token == TK_ONEOF)
        {
          bOneOf = TRUE;
          if (CheckToken(TK_IN))
            return TRUE;
          int num = 0;
          int id = 10;
          while (1)
          {
            if (CheckToken(TK_STR))
              return TRUE;
            SetDataInt(pLink, num, 0, id);
            SetDataStr(pLink, num, 1, strIn);
            num++;
            id += 10;
            int t = GetToken();
            if (t == TK_OUT)
            {
              bOneOf = FALSE;
              break;
            }
            else if (t != TK_COMMA)
            {
              wsprintf(strErrMsg, _T("bad token %s in OneOf"), TokenStr(t));
              return TRUE;
            }
          }
          pLink->bReadOnly = TRUE;
        }
        else
        {
          wsprintf(strErrMsg, _T("bad token %s in GetDataSet"), token);
          return TRUE;
        }
      }
      else
      {
        UngetToken(token);
        break;
      }
    }
  }
  if (bWork)
  {
    curData = pD;
    pDStd = pD;
    if (CheckToken(TK_BEGIN))
      return TRUE;
    if (GetContents(FALSE))
      return TRUE;
    curData = NULL;
    pDStd = NULL;
    if (CheckToken(TK_END))
      return TRUE;
  }
  CompleteField();
  return FALSE;
}

int FindItem(DataSet *pD, TCHAR *name)
{
  if (!pD)
    return -1;
  if (pTempItem && numDataItem > 0)
  {
    for (int i = 0; i < numDataItem; i++)
    {
      if (!lstrcmp(pTempItem[i].name, name))
        return i;
    }
    return -1;
  }
  for (int i = 0; i < pD->num; i++)
  {
    if (!lstrcmp(pD->item[i].name, name))
      return i;
  }
  return -1;
}

int FindPathName(DataSet *pD, TCHAR *name, int len, int *ip)
{
  if (!pD)
    return 0;
  int n = FindItem(pD, name);
  if (n < 0)
    return 0;
  DataSet *pLink = pTempItem && numDataItem > 0 ? pTempItem[n].link : pD->item[n].link;
  ip[len] = n;
  int token = GetToken();
  if (token == TK_COMMA)
  {
    if (!pLink)
      return 0;
    if (CheckText())
      return 0;
    return FindPathName(pLink, textIn, len + 1, ip);
  }

  // this is the end
  else
  {
    UngetToken(token);
    return len + 1;
  }
  return 0;
}

Path *FindPath(DataSet *pD, TCHAR *name)
{
  int ip[maxPath + 8];
  int len = FindPathName(pD, name, 0, ip);
  if (len == 0)
  {
    return NULL;
  }
  for (Path *p = pD->pPathTop; p; p = p->next)
  {
    if (p->len == len)
    {
      BOOL sw = TRUE;
      for (int i = 0; i < len; i++)
      {
        if (p->ip[i] != ip[i])
        {
          sw = FALSE;
          break;
        }
      }
      if (sw)
        return p;
    }
  }
  return NULL;
}