回文章列表
作者cache.bbs@cpu.tfcis.org (Gphone實驗室) 看板: itoc
標題[功能] Smart Merge 修文自動合併
時間動力核心 (2009/10/20 Tue 20:41:15)
這個 patch 可以讓使用者在大E編輯文章~存檔的這段過程中
若有其他人推文/wiki編輯時會自動合併內容

不過實際上仍然會有 synchronization 的問題, 以及推文數的漏計
可以再加上 ◆ [功能] 重新計算推文數 這個 patch



author: hrs113355.bbs <AT> xdbbs.twbbs.org
        cache.bbs     <AT> bbs.ee.ncku.edu.tw


首先新增這個檔案到 /include 中
https://opensvn.csie.org/NCKUEEBBS/bbs/src/include/fnv_hash.h


接下來修改以下的部分:

include/bbs.h

+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>

...

 #include "theme.h"              /* custom theme */
 #include "proto.h"              /* prototype of functions */
+#include "fnv_hash.h"           /* for fnv_hash (Smart Merge) */

#endif                           /* _BBS_H_ */


include/config.h

#define SMerge          /* Smart Merge 修文自動合併 */

src/post.c

+/* ----------------------------------------------------- */
+/* Smart Merge 修文自動合併                              */
+/* ----------------------------------------------------- */
+
+/* cache.081225: (TODO) refine Smart Merge patch for M3-itoc */
+
+#ifdef SMerge
+/* append data from tail of src (starting point=off) to dst */
+int
+AppendTail(const char *src, const char *dst, int off)
+{
+    int fi, fo, bytes;
+    char buf[8192];
+
+    fi=open(src, O_RDONLY);
+    if(fi<0) return -1;
+
+    fo=open(dst, O_WRONLY | O_APPEND | O_CREAT, 0600);
+    if(fo<0) {close(fi); return -1;}
+    // flock(dst, LOCK_SH);
+
+   if(off > 0)
+        lseek(fi, (off_t)off, SEEK_SET);
+
+    while((bytes=read(fi, buf, sizeof(buf)))>0)
+    {
+         write(fo, buf, bytes);
+    }
+    // flock(dst, LOCK_UN);
+    close(fo);
+    close(fi);
+    return 0;
+}
+
+
+// return: 0 - ok; otherwise - fail.
+static int
+hash_partial_file( char *path, size_t sz, unsigned char output[SMHASHLEN] )
+{
+    int fd;
+    size_t n;
+    unsigned char buf[1024];
+
+    Fnv64_t fnvseed = FNV1_64_INIT;
+    assert(SMHASHLEN == sizeof(fnvseed));
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0)
+        return 1;
+
+    while(  sz > 0 &&
+            (n = read(fd, buf, sizeof(buf))) > 0 )
+    {
+        if (n > sz) n = sz;
+        fnvseed = fnv_64_buf(buf, (int) n, fnvseed);
+        sz -= n;
+    }
+    close(fd);
+
+    if (sz > 0) // file is different
+        return 2;
+
+    memcpy(output, (void*) &fnvseed, sizeof(fnvseed));
+    return HASHPF_RET_OK;
+}
+
+#endif

...

 int
 post_edit(xo)
   XO *xo;
 {
   char fpath[64];
   HDR *hdr;
   FILE *fp;

+#ifdef SMerge
+  struct stat st;
+  time_t oldmt, newmt;
+  off_t oldsz, newsz;
+  char genbuf[64];
+  char SmartMerge = 1, DoRecord = 0, MergeDone = 0;
+#endif

   hdr = (HDR *) xo_pool + (xo->pos - xo->top);
   hdr_fpath(genbuf, xo->dir, hdr);
+
+#ifdef SMerge
+  sprintf(fpath, "tmp/edit.%s.%ld", cuser.userid, time(NULL));
+
+  f_cp(genbuf, fpath, O_APPEND);                /* do Copy */
+  stat(fpath, &st);
+  oldsz = st.st_size;
+  stat(genbuf, &st);
+  oldmt = st.st_mtime;
+
+  while (1)
+  {
+        unsigned char oldsum[SMHASHLEN] = {0}, newsum[SMHASHLEN] = {0};
+
+        if (hash_partial_file(fpath, oldsz, oldsum) != HASHPF_RET_OK)
+            SmartMerge = 0;
+
+        if (HAS_PERM(PERM_ALLBOARD))                        /* 站長修改 */
+        {
+
+           if(!vedit(fpath, 0))
+             {
+                if(vans("是否留下修改文章記錄?[Y/n] ") != 'n')
+                  DoRecord = 1;
+             }
+           else
+             return XO_HEAD;
+        }
+        else if (cuser.userlevel && (is_author(hdr)) )  /* 原作者修改 */
+        {
+            if (!vedit(fpath, 0))        /* 若非取消則加上修改資訊 */
+              DoRecord = 1;
+            else
+              return XO_HEAD;
+        }
+        else      /* itoc.010301: 提供使用者修改(但不能儲存)其他人發表的文章 */
+        {
+          #ifdef HAVE_REFUSEMARK
+            if (!chkrestrict(hdr))
+              return XO_NONE;
+          #endif
+          vedit(fpath, -1);
+          return XO_HEAD;
+        }
+
+        stat(genbuf, &st); newmt = st.st_mtime; newsz = st.st_size;
+        if (SmartMerge && newmt == oldmt || newsz < oldsz)
+            SmartMerge = 0;
+        if (SmartMerge && hash_partial_file(genbuf, oldsz, newsum) != HASHPF_RET_OK)
+            SmartMerge = 0;
+        if (SmartMerge && memcmp(oldsum, newsum, sizeof(newsum)) != 0)
+            SmartMerge = 0;
+
+        if (SmartMerge)
+        {
+            SmartMerge = 0;
+
+            clear();
+            move(b_lines-8, 0);
+            outs("\033[1;33m ▲ 檔案已被修改過! ▲\033[m\n\n");
+            outs("進行自動合併 [Smart Merge]...\n\n");
+
+            if (AppendTail(genbuf, fpath, oldsz) == 0)
+            {
+
+                oldmt = newmt;
+                outs("\033[1;32m合併成功\,新修改(或推文)已加入您的文章中。\033[m\n");
+                MergeDone = 1;
+
+                //hrs113355: since smartmerge was still dealing with some
+                //problem about synchronization, we shouldn't show the message
+                //to inform users that there would be no race condition.
+
+                //vmsg("合併過程中異動的推文數將不會被採計");
+
+                //cache.090127 (TODO) add a score counter
+
+            }
+            else
+            {
+                outs("\033[1;31m自動合併失敗。 請改用人工手動編輯合併。\033[m");
+                vmsg("合併失敗");
+            }
+        }
+        if (oldmt != newmt)
+        {
+            int c = 0;
+
+            clear();
+            move(b_lines-8, 0);
+            outs("\033[1;33m ▲ 檔案已被修改過! ▲\033[m\n\n");
+
+            outs("可能是您在編輯的過程中有人進行推文或修文。\n\n"
+                 "您可以選擇直接覆蓋\檔案(y)、放棄(n),\n"
+                 "或是\033[1;37m重新編輯\033[m(新文會被貼到剛編的檔案後面)(e)。\n");
+            c = vans("要直接覆蓋\檔案/取消/重新編輯? [Y/n/e]");
+
+            if (c == 'n')
+                return XO_HEAD;
+
+            if (c == 'e')
+            {
+                FILE *fp, *src;
+
+                /* merge new and old stuff */
+                fp = fopen(fpath, "at");
+                src = fopen(genbuf, "rt");
+
+                if(!fp)
+                {
+                    vmsg("抱歉,檔案已損毀。");
+                    if(src) fclose(src);
+                    unlink(fpath); // fpath is a temp file
+                    return XO_HEAD;
+                }
+
+                if(src)
+                {
+                    int c = 0;
+
+                    fprintf(fp, MSG_SEPERATOR "\n");
+                    fprintf(fp, "以下為被修改過的最新內容: ");
+                    fprintf(fp,
+                            " (%s)\n",
+                            Btime(&newmt));
+                    fprintf(fp, MSG_SEPERATOR "\n");
+                    while ((c = fgetc(src)) >= 0)
+                        fputc(c, fp);
+                    fclose(src);
+
+                    // update oldsz, old mt records
+                    stat(genbuf, &st); oldmt = st.st_mtime; oldsz = st.st_size;
+                }
+                fclose(fp);
+                continue;
+            }
+        }
+
+      //let's do something instead to keep the link with hard-linking posts
+        FILE *fp = fopen(genbuf, "w");
+        f_suck(fp, fpath);
+        fclose(fp);
+        break;
+
+        //f_mv(fpath, genbuf);
+        //break;
+
+    }
+
+    if (DoRecord)
+    {
+        if (fp = fopen(genbuf, "a"))
+        {
+            ve_banner(fp, 1);
+            fclose(fp);
+        }
+
+    btime_update(currbno);
+    if (MergeDone)
+      vmsg("合併完成");
+
+    }
+
+#else//undef SMerge
   if (HAS_PERM(PERM_ALLBOARD))          /* 站長修改 */
   {
 #ifdef HAVE_REFUSEMARK
     if (!chkrestrict(hdr))


...


     vedit(fpath, -1);
   }

+#endif
+
   /* return post_head(xo); */
   return XO_HEAD;   /* itoc.021226: XZ_POST 和 XZ_XPOST 共用 post_edit() */
 }


--
=[:摃��:﹎Origin ]|[  Ocpu.tfcis.org  ]|[�搟說:]=
=[:摃�╱�:Author ]|[  itlab24.ee.ncku.edu.tw  ]|[�:]=
文章選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻