回精華區
作者TKyo.bbs@cpu.tfcis.org (暗黑貴公子) 看板: plan
標題[功能]新記帳手札
時間動力核心 (2005/04/26 Tue 17:04:56) Updated: 2006/04/17
/*
1.本次新記帳手札之新增功能, 是以原 "記帳手札" 功能實際運行一年半後
  依據 "天天使用的網友" 所提出之需求及為了介面 xover() 化而重新撰寫的版本

2.且整合 "原記帳手札資料檔轉檔程式" 於新記帳手札中
  欲換新版本的網友可以不用擔心

3.底下程式碼, 有許多地方因為顧慮到 telnet 顯示畫面而有所斷行

*/

: src/include/struct.h  /* 適當位置加入 */

#ifdef HAVE_CREDIT
typedef struct
{
  time_t stamp;         /* 資料建立日期時間 */
  int type;             /* 資料類別 */
  time_t chrono;        /* 使用者自訂之資料日期時間 */
  char flag;            /* 支出/收入 */
  int money;            /* 金額 */
  char useway;          /* 分類(食衣住行育樂) */
  char desc[75];        /* 說明 */
  char desc_way[5];     /* 分類自訂 */
}      CREDIT;

#define CREDIT_FOLDER   0x01    /* 資料夾 */
#define CREDIT_DATA     0x02    /* 一般資料 */

#define CREDIT_OUT      0x1     /* 支出 */
#define CREDIT_IN       0x2     /* 收入 */

#define CREDIT_OTHER    0       /* 其他 */
#define CREDIT_EAT      1       /* 食 */
#define CREDIT_WEAR     2       /* 衣 */
#define CREDIT_LIVE     3       /* 住 */
#define CREDIT_MOVE     4       /* 行 */
#define CREDIT_EDU      5       /* 育 */
#define CREDIT_PLAY     6       /* 樂 */
#define CREDIT_CUSTOM   7       /* 自訂 */
#endif

: src/include/theme.h   /* 適當位置加入 */

/* ----------------------------------------------------- */
/* 記帳手札顏色                                          */
/* ----------------------------------------------------- */

#ifdef HAVE_CREDIT
#define C_CREDIT_IN "\033[1;31m"
#define C_CREDIT_OUT    "\033[1;32m"
#define C_CREDIT_N      "\033[1;37m"
#define C_CREDIT_OTHER  "\033[1;33m"
#define C_CREDIT_TITLE  "\033[1;35m"
#endif


- #define NECKER_CREDIT "[←]離開 [C]換頁 [1]新增 [2]刪除 [3]全刪 [4]總計\n" \
- COLOR3 "  編號   日  期   收支  金  額  分類     說  明%*s                               \033[m"

+ #define NECKER_CREDIT   \
+ "[←]離開 [T]修改資料說明 [t]標籤 [d/D/^D]刪除 [m]移動 [^G]海錨功\能
 [h]說明\n" \
+ COLOR3 "  編號  日      期  收支 金    額  用途
 說              明%*s                    \033[m"


+ #define FEETER_CREDIT   \
+ COLOR1 " 記帳手札 "
  COLOR2 " (a/f)新增資料/資料夾 (E)修改 (s)統計 (S)排序 (g)收錄資料 (←)離開  "

: src/include/modes.h

#define XZ_GEM      (XO_ZONE + 13)  /* 精華區 */
+ #define XZ_CREDIT       (XO_ZONE + 14)  /* 記帳手札 */

: src/maple/menu.c : menu_other[]

#ifdef HAVE_CREDIT
- "bin/credit.so:main_credit", PERM_BASIC, - M_XMODE,
+ "bin/new_credit.so:credit_main", PERM_BASIC, - M_XMODE,
  "MoneyNote  ♂ 記帳手札 ♀",
#endif

: src/maple/xover.c : xz[]

- {NULL, NULL, M_GEM, FEETER_GEM}               /* XZ_GEM */
+ {NULL, NULL, M_GEM, FEETER_GEM},              /* XZ_GEM */
+ {NULL, NULL, M_XMODE, FEETER_CREDIT}          /* XZ_CREDIT */

: src/so/new_credit.c   /* 新增此程式 */

/*-------------------------------------------------------*/
/* new_credit.c     ( NTHU CS MapleBBS Ver 3.10 )        */
/*-------------------------------------------------------*/
/* target : 新記帳手札                                   */
/* author : kyo.bbs@cszone.org                           */
/* create : 05/01/03                                     */
/* update :   /  /                                       */
/*-------------------------------------------------------*/


#include "bbs.h"


#ifdef HAVE_CREDIT

extern XZ xz[];
extern char xo_pool[];
extern int TagNum;
extern TagItem TagList[];

static char CreditAnchor[64], CreditSailor[TTLEN + 1];

static int credit_anchor(XO *xo);
void XoCredit(char *folder, char *title);
static void credit_do_delete();

typedef struct
{
  int year;                     /* 年 */
  char month;                   /* 月 */
  char day;                     /* 日 */

  char flag;                    /* 支出/收入 */
  int money;                    /* 金額 */
  char useway;                  /* 類別(食衣住行育樂) */
  char desc[112];               /* 說明 */
}      CREDIT_OLD;

static time_t
make_time(year, month, day)
  int year, month, day;
{
  struct tm ptime;

  ptime.tm_sec = 0;
  ptime.tm_min = 0;
  ptime.tm_hour = 0;
  ptime.tm_mday = day;
  ptime.tm_mon = month - 1;
  ptime.tm_year = year - 1900;
  ptime.tm_isdst = 0;
#ifndef CYGWIN
  ptime.tm_zone = "GMT";
  ptime.tm_gmtoff = 0;
#endif

  return mktime(&ptime);
}

static char datemsg[40];

char *
BAtime(clock)
  time_t *clock;
{
  struct tm *t = localtime(clock);
  sprintf(datemsg, "%04d/%02d/%02d %02d:%02d:%02d",
  t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
  t->tm_hour, t->tm_min, t->tm_sec);
  return (datemsg);
}

void
credit_fpath(fpath, userid, fname)
  char *fpath;
  char *userid;
  char *fname;
{
  char buf[IDLEN + 1];

  str_lower(buf, userid);

  if (fname)
    sprintf(fpath, "usr/%c/%s/CREDIT/%s", buf[0], buf, fname);
  else
    sprintf(fpath, "usr/%c/%s/CREDIT", buf[0], buf);
}

void
credit_stampfpath(fpath, stamp)
  char *fpath;
  time_t stamp;
{
  char xname[32];

  xname[0] = 'F';
  archiv32(stamp, xname + 1);
  credit_fpath(fpath, cuser.userid, xname);
}

int
credit_edit(credit, echo)
  CREDIT *credit;
  int echo;
{
  char buf_command[3] = {0}, buf[80], month, day;
  char *menu[] = {buf_command,
      "1  支出",
      "2  收入",
      "Q  取消", NULL};
  char *menu2[] = {buf_command,
      "1  其他",
      "2  [食]",
      "3  [衣]",
      "4  [住]",
      "5  [行]",
      "6  [育]",
      "7  [樂]",
      "8  自訂", NULL};
  int i, year, mmday[12] = {31, 28, 31, 30, 31, 30,
                            31, 31, 30, 31, 30, 31}; /* 每個月的天數 */
  time_t now;
  struct tm *ptime;

  i = 3;
  move(i, 0);
  clrtobot();

  if (echo == GCARRY)
  {
    if (credit->flag == CREDIT_OUT)
      strcpy(buf_command, "1Q");
    else
      strcpy(buf_command, "2Q");
    ptime = localtime(&credit->chrono);
  }
  else
  {
    memset(credit, 0, sizeof(CREDIT));
    credit->flag = CREDIT_OUT;
    strcpy(buf_command, "1Q");
    now = time(NULL);
    ptime = localtime(&now);
  }

  credit->flag = pans(-1, -1, "選項", menu);

  if (credit->flag == 'q')
    return 0;

  if (credit->flag == '1')
    credit->flag = CREDIT_OUT;
  else
    credit->flag = CREDIT_IN;

  move(++i, 0);
  prints("收支:%s%s\033[m",
    (credit->flag == CREDIT_OUT) ? C_CREDIT_OUT : C_CREDIT_IN,
    menu[credit->flag] + 3);

  sprintf(buf, "%04d", ptime->tm_year + 1900);
  do
  {
    if (vget(++i, 0, "日期(年):", buf, 5, GCARRY))
      year = atoi(buf);
    else
      return 0;
  } while (year < 1);

  sprintf(buf, "%02d", ptime->tm_mon + 1);
  do
  {
    if (vget(++i, 0, "日期(月):", buf, 3, GCARRY))
      month = atoi(buf);
    else
      return 0;
  } while (month < 1 || month > 12);

  /* 平閏年的判別 */
  if ((year % 400 == 0) ||
     (year % 100 != 0 && year % 4 == 0))
  mmday[1] = 29;

  sprintf(buf, "%02d", ptime->tm_mday);
  do
  {
    if (vget(++i, 0, "日期(日):", buf, 3, GCARRY))
      day = atoi(buf);
    else
      return 0;
  } while (day < 1 || day > mmday[month - 1]);

  credit->chrono = make_time(year, month, day);

  if (echo == GCARRY)
    sprintf(buf, "%d", credit->money);
  do
  {
    if (vget(++i, 0,
      (credit->flag == CREDIT_OUT) ? "支出金額(元):" : "收入金額(元):",
      buf, 9, echo))
    {
      credit->money = atoi(buf);
    }
    else
      return 0;
  } while (credit->money < 1);

  if (credit->flag == CREDIT_OUT)        /* 支出才有記錄用途 */
  {
    if (echo == GCARRY)
      sprintf(buf_command, "%c%c",
        (credit->useway + '1'), (credit->useway + '1'));
    else
      strcpy(buf_command, "11");

    if ((credit->useway = (pans(-1, -1, "用途", menu2) - '1')) == 7)
    {
      if (!(vget(++i, 0, "用途:", credit->desc_way, BCLEN, echo)))
        return 0;
    }
    else
    {
      move(++i, 0);
      prints("用途:" C_CREDIT_OTHER "%s\033[m",
        menu2[credit->useway + 1] + 3);
    }
  }

  vget(++i, 0, "說明:", credit->desc, TTLEN, echo);

  return 1;
}

static int
credit_stamp(credit)
  CREDIT *credit;
{
  char fpath[64];
  int fd;

  credit_stampfpath(fpath, credit->stamp);
  if ((fd = open(fpath, O_WRONLY | O_CREAT | O_EXCL, 0600)) >= 0)
    close(fd);

  return fd;
}

static int
credit_add(xo, ctype)
  XO *xo;
  int ctype;
{
  CREDIT credit;
  int ans;
  char *menu[] = {"AQ",
      "A  記帳資料",
      "F  資料夾",
      "G  海錨功\能",
      "Q  離開", NULL};

  char *menu2[] = {"AQ",
      "A  加到最後",
      "I  插入目前位置",
      "N  下一個",
      "Q  離開", NULL};

  if ((ans = ctype) == 0)
    ans = pans(-1, -1, "選項", menu);

  switch (ans)
  {
    case 'q':
      return XO_NONE;
    case 'a':
    case 'f':

      if (ans == 'f')
      {
        if (!(vget(b_lines, 0, "標題:", credit.desc, TTLEN, DOECHO)))
          return XO_INIT;

        time(&credit.stamp);
        credit.type = CREDIT_FOLDER;
        if (credit_stamp(&credit) < 0)
          return XO_INIT;
      }
      else
      {
        if (!(credit_edit(&credit, DOECHO)))
          return XO_INIT;

        time(&credit.stamp);
        credit.type = CREDIT_DATA;
      }

      break;
    case 'g':
      credit_anchor(xo);
      return XO_NONE;
  }

  ans = pans(-1, -1, "存放位置", menu2);

  switch (ans)
  {
  case 'q':
    break;

  case 'i':
  case 'n':

    rec_ins(xo->dir, &credit, sizeof(CREDIT), xo->pos + (ans == 'n'), 1);
    break;

  case 'a':
  default:

    rec_add(xo->dir, &credit, sizeof(CREDIT));
    break;
  }

  return XO_INIT;
}

static int
credit_add_data(xo)     /* itoc.010419: 快速鍵 */
  XO *xo;
{
  return credit_add(xo, 'a');
}


static int
credit_add_folder(xo)       /* itoc.010419: 快速鍵 */
  XO *xo;
{
  return credit_add(xo, 'f');
}

static int
credit_change(xo)
  XO *xo;
{
  CREDIT *credit, old_credit;
  int pos, cur;

  pos = xo->pos;
  cur = pos - xo->top;
  credit = (CREDIT *) xo_pool + cur;

  if (credit->type & CREDIT_DATA)
  {
    old_credit = *credit;

    if (credit_edit(&old_credit, GCARRY))
    {
      if (memcmp(credit, &old_credit, sizeof(CREDIT)))
      {
        if (vans("確定要修改嗎?[N] ") == 'y')
        {
          rec_put(xo->dir, &old_credit, sizeof(CREDIT), pos, NULL);
          return XO_LOAD;
        }
      }
    }
  }

  return XO_BODY;
}

static int
credit_title(xo)
  XO *xo;
{
  CREDIT *credit, old_credit;
  int pos, cur;

  pos = xo->pos;
  cur = pos - xo->top;
  credit = (CREDIT *) xo_pool + cur;

  old_credit = *credit;
  if (old_credit.type & CREDIT_FOLDER)
  {
    if (!(vget(b_lines, 0, "標題:", old_credit.desc, TTLEN, GCARRY)))
      return XO_FOOT;
  }
  else if (old_credit.type & CREDIT_DATA)
    vget(b_lines, 0, "說明:", old_credit.desc, TTLEN, GCARRY);

  if (memcmp(credit, &old_credit, sizeof(CREDIT)))
    rec_put(xo->dir, &old_credit, sizeof(CREDIT), pos, NULL);

  return XO_LOAD;
}

static int
credit_sum(xo)
  XO *xo;
{
  CREDIT *credit;
  struct stat st;
  int fd, ch, i;
  int qyear, qafter, stop_sum;
  time_t qtime_src, qtime_desc;
  struct tm *ptime;
  char *menu[] = {"QQ",
      "1  全部統計",
      "2  依年份統計",
      "3  依年月統計",
      "4  依年月日期間隔統計",
      "Q  取消", NULL};
  char qmonth, qday, buf[100];
  usint way[8], moneyin, moneyout;
  int mmday[12] = {31, 28, 31, 30, 31, 30,
                   31, 31, 30, 31, 30, 31}; /* 每個月的天數 */

  ch = pans(-1, -1, "統計方式", menu);

  switch (ch)
  {
    case 'q':
      return XO_NONE;
    case '2':
    case '3':
    case '4':

      i = 3;
      move(i++, 0);
      clrtobot();

      move(i++, 0);
      prints("統計方式:" C_CREDIT_TITLE "%s\033[m", menu[ch - '0'] + 3);

      credit = (CREDIT *) xo_pool + xo->pos - xo->top;

      if (credit->type & CREDIT_FOLDER)
        ptime = localtime(&credit->stamp);
      else
        ptime = localtime(&credit->chrono);

      sprintf(buf, "%04d", ptime->tm_year + 1900);
      do
      {
        if (vget(++i, 0, "[篩選條件] 年份:", buf, 5, GCARRY))
          qyear = atoi(buf);
        else
          return XO_FOOT;
      } while (qyear < 1);

      if (ch == '2')
        break;

      sprintf(buf, "%02d", ptime->tm_mon + 1);
      do
      {
        if (vget(++i, 0, "[篩選條件] 月份:", buf, 3, GCARRY))
          qmonth = atoi(buf);
        else
          return XO_FOOT;
      } while (qmonth < 1 || qmonth > 12);

      if (ch == '3')
        break;

      /* 平閏年的判別 */
      if (((qyear + 1911) % 400 == 0) ||
        ((qyear + 1911) % 100 != 0 && (qyear + 1911) % 4 == 0))
      mmday[1] = 29;

      sprintf(buf, "%02d", ptime->tm_mday);
      do
      {
        if (vget(++i, 0, "[篩選條件] 日期:", buf, 3, GCARRY))
          qday = atoi(buf);
        else
          return XO_FOOT;
      } while (qday < 1 || qday > mmday[qmonth - 1]);

      qtime_src = make_time(qyear, qmonth, qday);

      qafter = 7;
      sprintf(buf, "%d", qafter);
      do
      {
        if (vget(++i, 0, "[篩選條件] 日期間隔天數:", buf, 3, GCARRY))
          qafter = atoi(buf);
        else
          qafter = 0;
      } while (qafter < 0 || qday > 100);

      qtime_desc = qtime_src + qafter * 86400;
  }

  if ((fd = open(xo->dir, O_RDONLY)) >= 0 && !fstat(fd, &st) && st.st_size > 0)
  {
    memset(way, 0, sizeof(way));
    moneyin = 0;
    moneyout = 0;

    mgets(-1);
    while (credit = mread(fd, sizeof(CREDIT)))
    {
      stop_sum = 0;

      if (credit->type & CREDIT_FOLDER)
        continue;

      ptime = localtime(&credit->chrono);

      switch (ch)
      {
        case '3':
          stop_sum = ((stop_sum == 0) &&
                     ((ptime->tm_mon + 1) == qmonth)) ? 0 : 1;
        case '2':
          stop_sum = ((stop_sum == 0) &&
                     ((ptime->tm_year + 1900) == qyear)) ? 0 : 1;
          break;
        case '4':
          stop_sum = ((stop_sum == 0) && ((credit->chrono >= qtime_src) &&
                     (credit->chrono <= qtime_desc))) ? 0 : 1;
          break;
      }

      if (stop_sum == 0)
      {
        if (credit->flag == CREDIT_OUT)
          way[credit->useway] += credit->money;
        else
          moneyin += credit->money;
      }
    }
  }
  close(fd);

  for (i = 0; i <= 7; i++)
    moneyout += way[i];

  i = 3;

  move(i, 0);
  clrtobot();
  move(++i, 0);

  switch (ch)
  {
    case '1':
      strcpy(buf, C_CREDIT_TITLE "全部統計結果");
      break;
    case '2':
      sprintf(buf, C_CREDIT_N " %d " C_CREDIT_TITLE "年統計結果", qyear);
      break;
    case '3':
      sprintf(buf, C_CREDIT_N " %d " C_CREDIT_TITLE "年"
                   C_CREDIT_N " %d " C_CREDIT_TITLE "月份統計結果",
        qyear, qmonth);
      break;
    case '4':
      {
        struct tm *ptime;

        ptime = localtime(&qtime_src);
        prints(C_CREDIT_N " %d " C_CREDIT_TITLE "年"
               C_CREDIT_N " %d " C_CREDIT_TITLE "月"
               C_CREDIT_N " %d " C_CREDIT_TITLE "日~",
          ptime->tm_year + 1900, ptime->tm_mon + 1, ptime->tm_mday);

        ptime = localtime(&qtime_desc);
        sprintf(buf, C_CREDIT_N " %d " C_CREDIT_TITLE "年"
                     C_CREDIT_N " %d " C_CREDIT_TITLE "月"
                     C_CREDIT_N " %d " C_CREDIT_TITLE "日期間隔統計結果",
          ptime->tm_year + 1900, ptime->tm_mon + 1, ptime->tm_mday);

        break;
      }
  }
  prints("%s\033[m\n" BCOLOR "%s\033[m\n", buf, msg_seperator);
  prints(C_CREDIT_IN "總收入  %12u\033[m 元\n", moneyin);
  prints(C_CREDIT_OUT "總支出  %12u\033[m 元\n", moneyout);

  if (moneyin > moneyout)
    prints(BCOLOR "%s\033[m\n" C_CREDIT_IN "  結餘  %12u\033[m 元\n\n",
      msg_seperator, moneyin - moneyout);
  else if (moneyin < moneyout)
    prints(BCOLOR "%s\033[m\n" C_CREDIT_OUT "  透支  %12u\033[m 元\n\n",
      msg_seperator, moneyout - moneyin);
  else
    prints(BCOLOR "%s\033[m\n" C_CREDIT_N "  打平  %12u\033[m 元\n\n",
      msg_seperator, 0);

  prints(C_CREDIT_TITLE "%s\033[m\n" BCOLOR "%s\033[m\n", "支出類別明細",
    msg_seperator);
  prints(C_CREDIT_OTHER " [食]   " C_CREDIT_OUT "%12u\033[m 元"
     C_CREDIT_OTHER "     [衣]   " C_CREDIT_OUT "%12u\033[m 元\n",
     way[CREDIT_EAT], way[CREDIT_WEAR]);
  prints(C_CREDIT_OTHER " [住]   " C_CREDIT_OUT "%12u\033[m 元"
     C_CREDIT_OTHER "     [行]   " C_CREDIT_OUT "%12u\033[m 元\n",
     way[CREDIT_LIVE], way[CREDIT_MOVE]);
  prints(C_CREDIT_OTHER " [育]   " C_CREDIT_OUT "%12u\033[m 元"
     C_CREDIT_OTHER "     [樂]   " C_CREDIT_OUT "%12u\033[m 元\n",
     way[CREDIT_EDU], way[CREDIT_PLAY]);
  prints(C_CREDIT_OTHER " 其他   " C_CREDIT_OUT "%12u\033[m 元"
     C_CREDIT_OTHER "     自訂   " C_CREDIT_OUT "%12u\033[m 元\n",
     way[CREDIT_OTHER], way[CREDIT_CUSTOM]);
  prints(BCOLOR "%s\033[m\n", msg_seperator);

  vmsg(NULL);

  return XO_BODY;
}

static int
credit_query(xo)
  XO *xo;
{
  CREDIT *credit;
  int pos, cur;

  pos = xo->pos;
  cur = pos - xo->top;
  credit = (CREDIT *) xo_pool + cur;

  if (credit->type & CREDIT_FOLDER)
  {
    char fpath[64];
    char title[TTLEN + 1];

    str_ncpy(title, credit->desc, sizeof(title));
    credit_stampfpath(fpath, credit->stamp);
    XoCredit(fpath, title);
    return XO_INIT;
  }
  else if (credit->type & CREDIT_DATA)
  {
    char *menu[] = {"QQ",
        "A  新增記帳資料",
        "F  新增資料夾",
        "T  修改記帳資料說明",
        "E  修改記帳資料",
        "S  記帳資料統計",
        "Q  取消", NULL};

    switch (pans(-1, -1, "功\能選單", menu))
    {
      case 'a':
        return credit_add_data(xo);
      case 'f':
        return credit_add_folder(xo);
      case 't':
        return credit_title(xo);
      case 'e':
        return credit_change(xo);
      case 's':
        return credit_sum(xo);
      default:
        return XO_NONE;
    }
  }
}

static void
delcredit(xo, credit)
  XO *xo;
  CREDIT *credit;
{
  if (credit->type & CREDIT_FOLDER)
  {
    char fpath[64];

    credit_stampfpath(fpath, credit->stamp);
    if (str_ncmp(CreditAnchor, fpath, sizeof(CreditAnchor)) == 0)
      CreditAnchor[0] = '\0';
    credit_do_delete(xo, fpath);
  }
}

static void
credit_do_delete(xo, folder)
  XO *xo;
  char *folder;
{
  CREDIT credit;
  FILE *fp;

  if (!(fp = fopen(folder, "r")))
    return;

  while (fread(&credit, sizeof(CREDIT), 1, fp) == 1)
    delcredit(xo, &credit);

  fclose(fp);
  unlink(folder);
}

static int
credit_delete(xo)
  XO *xo;
{
  if (vans(msg_del_ny) == 'y')
  {
    CREDIT *credit;

    credit = (CREDIT *) xo_pool + (xo->pos - xo->top);
    delcredit(xo, credit);

    if (!rec_del(xo->dir, sizeof(CREDIT), xo->pos, NULL))
      return XO_LOAD;
  }
  return XO_FOOT;
}

static int
credit_rangedel(xo)
  XO *xo;
{
  return xo_rangedel(xo, sizeof(CREDIT), NULL, delcredit);
}


static int
vfycredit(credit, pos)
  CREDIT *credit;
  int pos;
{
  return Tagger(credit->stamp, pos, TAG_NIN);
}


static int
credit_prune(xo)
  XO *xo;
{
  return xo_prune(xo, sizeof(CREDIT), vfycredit, delcredit);
}

static void
credit_item(num, credit)
  int num;
  CREDIT *credit;
{
  char *credit_way[] =
      {"其他", "[食]", "[衣]", "[住]", "[行]", "[育]", "[樂]", NULL};

  if (credit->type & CREDIT_FOLDER)
    prints("%6d %c" COLOR_ANN_F CHAR_ON "%-*.*s\033[m",
      num, tag_char(credit->stamp), d_cols + 68, d_cols + 68,
      credit->desc);
  else if (credit->type & CREDIT_DATA)
  {
    credit_way[7] = credit->desc_way;
    prints("%6d %c%-10.10s  %s %8d  " C_CREDIT_OTHER "%4s %-*.*s\033[m",
      num, tag_char(credit->stamp),
      BAtime(&credit->chrono),
      (credit->flag == CREDIT_OUT) ? C_CREDIT_OUT "支出" : C_CREDIT_IN "收入",
      credit->money,
      (credit->flag == CREDIT_OUT) ? credit_way[credit->useway] : "",
      d_cols + 38, d_cols + 38,
      credit->desc);
  }
}

static int
credit_body(xo)
  XO *xo;
{
  CREDIT *credit;
  int max, num, tail;

  move(3, 0);
  clrtobot();
  max = xo->max;
  if (max <= 0)
  {
    if ((max = credit_add(xo, 0)) != XO_NONE)
      return max;

    return XO_QUIT;
  }

  credit = (CREDIT *) xo_pool;
  num = xo->top;
  tail = num + XO_TALL;
  if (max > tail)
    max = tail;

  do
  {
    move(3 + num - xo->top, 0);
    credit_item(++num, credit++);
  } while (num < max);

  return XO_FOOT;   /* itoc.010403: 把 b_lines 填上 feeter */
}


static int
credit_head(xo)
  XO *xo;
{
  vs_head("記帳手札", xo->xyz);
  prints(NECKER_CREDIT, d_cols, "");

  return credit_body(xo);
}


static int
credit_load(xo)
  XO *xo;
{
  xo_load(xo, sizeof(CREDIT));
  return credit_body(xo);
}


static int
credit_init(xo)
  XO *xo;
{
  xo_load(xo, sizeof(CREDIT));
  return credit_head(xo);
}

static int
credit_tag(xo)
  XO *xo;
{
  CREDIT *credit;
  int tag, pos, cur;

  pos = xo->pos;
  cur = pos - xo->top;
  credit = (CREDIT *) xo_pool + cur;

  if (tag = Tagger(credit->stamp, pos, TAG_TOGGLE))
  {
    move(3 + cur, 0);
    credit_item(++pos, credit);
  }

  return xo->pos + 1 + XO_MOVE; /* lkchu.981201: 跳至下一項 */
}

static int
credit_cmp(a, b)
  CREDIT *a, *b;
{
  int iSeq, jSeq;
  int i, j;

  iSeq = (a->type & CREDIT_FOLDER) ? 1 : 0;
  jSeq = (b->type & CREDIT_FOLDER) ? 1 : 0;

  i = j = 0;

  if (!iSeq)
    i = a->chrono;

  if (!jSeq)
    j = b->chrono;

  if (!iSeq && !jSeq)
    return (i == j) ? str_cmp(a->desc, b->desc) : (i - j);
  else if (!jSeq)
    return -1;
  else if (!iSeq)
    return 1;
  else
    return str_cmp(a->desc, b->desc);
}

static int
credit_sort(xo)
  XO *xo;
{
  if (vans("執行資料排序嗎?[N] ") == 'y')
  {
    rec_sync(xo->dir, sizeof(CREDIT), credit_cmp, NULL);
    return credit_load(xo);
  }
  else
    return XO_NONE;
}


static int
credit_move(xo)
  XO *xo;
{
  CREDIT *credit;
  int pos, cur, i;
  char buf[40], ans[5];

  pos = xo->pos;
  cur = pos - xo->top;
  credit = (CREDIT *) xo_pool + cur;

  sprintf(buf, "請輸入第 %d 選項的新位置:", pos + 1);
  if (vget(b_lines, 0, buf, ans, 5, DOECHO))
  {
    i = atoi(ans) - 1;
    if (i < 0)
      i = 0;
    else if (i >= xo->max)
      i = xo->max - 1;

    if (i != pos)
    {
      if (!rec_del(xo->dir, sizeof(CREDIT), pos, NULL))
      {
        rec_ins(xo->dir, credit, sizeof(CREDIT), i, 1);
        xo->pos = i;
        return credit_load(xo);
      }
    }
  }
  return XO_FOOT;
}

static int
Ask_creditTag(msg)
  char *msg;
/* ----------------------------------------------------- */
/* return value :                                        */
/* -1   : 取消                                           */
/* 0    : single article                                 */
/* o.w. : whole tag list                                 */
/* ----------------------------------------------------- */
{
  int num;
  char buf_command[3] = {0};
  char *menu[] = {buf_command,
      "A  此項記帳資料",
      "T  已標籤記帳資料",
      "Q  離開", NULL};


  if (num = TagNum)
    strcpy(buf_command, "TQ");
  else
    strcpy(buf_command, "AQ");

  if (num)      /* itoc.020130: 有 TagNum 才問 */
  {
    switch (pans(-1, -1, msg, menu))
    {
    case 'q':
      return -1;

    case 'a':
      return 0;
    }
  }
  return num;
}


int
credit_gather(xo)
  XO *xo;
{
  CREDIT *credit, xcredit;
  int tag, locus;
  char *dir, *folder;
  time_t now;

  folder = CreditAnchor;

  if (!*folder)
  {
    zmsg("請先定錨以後再直接收錄至定錨區");
    return XO_NONE;
  }

  if (str_ncmp(folder, xo->dir, sizeof(CreditAnchor)) == 0)
  {
    zmsg("無法收錄記帳資料!\n目前資料夾為記帳手札定錨區【Ψ】。");
    return XO_NONE;
  }

  tag = Ask_creditTag("收錄至記帳手札定錨區【Ψ】");

  if (tag < 0)
    return XO_NONE;

  dir = xo->dir;
  credit = tag ? &xcredit : (CREDIT *) xo_pool + xo->pos - xo->top;

  locus = 0;
  now = time(0);

  do
  {
    if (tag)
      EnumTag(credit, dir, locus, sizeof(CREDIT));

    if (credit->type & CREDIT_DATA)
    {
      credit->stamp = now + locus;
      rec_add(folder, credit, sizeof(CREDIT));
    }
  } while (++locus < tag);

  zmsg("記帳資料收錄完成,但是資料夾不會被收錄。");

  return XO_NONE;
}

static int
credit_anchor(xo)
  XO *xo;
{
  char *folder;
  char *menu[] = {"QQ",
      "A  定錨",
      "D  拔錨",
      "J  就位",
      "Q  取消", NULL};


  folder = CreditAnchor;

  switch (pans(-1, -1, (*folder) ? "記帳手札【Ψ】" : "記帳手札", menu))
  {
  case 'a':
    strcpy(folder, xo->dir);
    str_ncpy(CreditSailor, xo->xyz, sizeof(CreditSailor));
    zmsg("記帳手札已定錨【Ψ】");
    break;
  case 'd':
    *folder = '\0';
    zmsg("記帳手札已拔錨");
    break;
  case 'j':
    if (!*folder)                     /* 沒有定錨就進入資源回收筒 */
    {
      zmsg("記帳手札尚未定錨!");
      return XO_NONE;
    }

    if (str_ncmp(folder, xo->dir, sizeof(CreditAnchor)) == 0)
      return XO_NONE;

    XoCredit(folder, CreditSailor);
    return XO_INIT;
    break;
  }

  /* return XO_NONE; */
  return XO_NONE;   /* itoc.010726: 把 b_lines 填上 feeter */
}

static int
credit_help(xo)
  XO *xo;
{
  xo_help("credit");
  return XO_HEAD;
}


static KeyFunc credit_cb[] =
{
  XO_INIT, credit_init,
  XO_LOAD, credit_load,
  XO_HEAD, credit_head,
  XO_BODY, credit_body,

  'r', credit_query,
  'a', credit_add_data,
  'f', credit_add_folder,
  Ctrl('D'), credit_prune,
  Ctrl('G'), credit_anchor,
  'E', credit_change,
  'T', credit_title,
  's', credit_sum,
  'd', credit_delete,
  'D', credit_rangedel,
  't', credit_tag,
  'g', credit_gather,
  'm', credit_move,
  'S', credit_sort,
  'h', credit_help
};

int
f_exists(fpath)
  char *fpath;
{
  int fd, fsize, ret;
  struct stat st;

  ret = 0;
  if ((fd = open(fpath, O_RDONLY)) >= 0)
  {
    if (fstat(fd, &st) || (fsize = st.st_size) > 0)
      ret = 1;

    close(fd);
  }

  return ret;
}

static int
check_old_credit(void)
{
  int i;
  char fpath[64], f_desc[64];
  char buf[80];
  CREDIT_OLD credit_old;
  CREDIT credit;

  usr_fpath(fpath, cuser.userid, FN_CREDIT);
  if (!(f_exists(fpath)))
    return 1;

  if (vans("使用前必須先轉換舊版記帳手札資料,轉檔嗎?[N] ") == 'y')
  {
    time_t now;

    credit_fpath(f_desc, cuser.userid, FN_CREDIT);
    i = 0;

    now = time(0);
    while (!(rec_get(fpath, &credit_old, sizeof(CREDIT_OLD), i)))
    {
        memset(&credit, 0, sizeof(CREDIT));
        credit.stamp = now + i;
        credit.type = CREDIT_DATA;
        credit.chrono = make_time(credit_old.year,
                                 credit_old.month,
                                 credit_old.day);
        credit.flag = credit_old.flag;
        credit.money = credit_old.money;
        credit.useway = credit_old.useway;
        str_ncpy(credit.desc, credit_old.desc, sizeof(credit.desc));
        rec_add(f_desc, &credit, sizeof(CREDIT));
        i++;
    }

    sprintf(buf, "rm %s", fpath);
    system(buf);
    return 1;
  }
  return 0;
}

void
XoCredit(folder, title)
  char *folder;
  char *title;
{
  XO *xo, *last;

  last = xz[XZ_CREDIT - XO_ZONE].xo;    /* record */

  xz[XZ_CREDIT - XO_ZONE].xo = xo = xo_new(folder);
  xz[XZ_CREDIT - XO_ZONE].cb = credit_cb;
  xo->xyz = title;

  xover(XZ_CREDIT);
  free(xo);

  xz[XZ_CREDIT - XO_ZONE].xo = last;    /* restore */
}

int
credit_main()
{
  char fpath[64];

  credit_fpath(fpath, cuser.userid, NULL);
  if (mkdir(fpath, 0700) != 0 && errno != EEXIST)
  {
    vmsg("建立記帳手札目錄時發生錯誤,請向站長提出");
    return XREDRAW;
  }

  if (check_old_credit())
  {
    credit_fpath(fpath, cuser.userid, FN_CREDIT);
    XoCredit(fpath, "主頁");
  }

  return 0;
}

#endif              /* HAVE_CREDIT */

/* 記帳手札 Help

                          ☆【記帳手札操作說明】☆
   【基本指令】─────────────────────────────
    (Enter/r/→) 記帳手札功能選單
    (a)          新增記帳資料
    (f)          新增資料夾
    (m)          移動資料位置
    (S)          資料排序
    (T)          修改記帳資料說明
    (E)          修改記帳資料
    (s)          統計記帳資料
    (^D/D)       標籤/範圍刪除
    (d)          刪除資料
    (t)          切換標籤
    (^G)         記帳手札海錨
    (g)          收錄記帳資料(支援標籤功能)
    (h)          說明

*/

--
  Origin:  Maple-itoc˙動力核心  cpu.tfcis.org 
  Author: TKyo cszone.twbbs.org 發表
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻