diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-20 19:33:49 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-20 19:33:49 +0800 |
commit | 6c7b18b32d87c2a835f7e5c48faac4a8ad44668b (patch) | |
tree | e88e1b2b1007f4ddcd348a4a1bf1d13c515c4564 /console/fav.c | |
parent | f59699c22c130373cda3cc4cb6fab5bae510bd5a (diff) | |
download | pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar.gz pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar.bz2 pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar.lz pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar.xz pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.tar.zst pttbbs-6c7b18b32d87c2a835f7e5c48faac4a8ad44668b.zip |
- (internal/exp) first draft of new layoutpiaip.newlayout
git-svn-id: http://opensvn.csie.org/pttbbs/branches/piaip.newlayout@4013 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'console/fav.c')
-rw-r--r-- | console/fav.c | 1320 |
1 files changed, 1320 insertions, 0 deletions
diff --git a/console/fav.c b/console/fav.c new file mode 100644 index 00000000..8a6c1e2f --- /dev/null +++ b/console/fav.c @@ -0,0 +1,1320 @@ +/* $Id$ */ +#include "bbs.h" + +/** + * Structure + * ========= + * fav 檔的前兩個 byte 是版號,接下來才是真正的 data。 + * + * fav 的主要架構如下: + * + * fav_t - 用來裝各種 entry(fav_type_t) 的 directory + * 進入我的最愛時,看到的東西就是根據 fav_t 生出來的。 + * 裡面紀錄者,這一個 level 中有多少個看板、目錄、分隔線。(favh) + * 是一個 array (with pre-allocated buffer) + * + * fav_type_t - fav entry 的 base class + * 存取時透過 type 變數來得知正確的型態。 + * + * fav_board_t / fav_line_t / fav_folder_t - derived class + * 詳細情形請參考 fav.h 中的定義。 + * 以 cast_(board|line|folder)_t 來將一個 fav_type_t 作動態轉型。 + * + * Policy + * ====== + * 為了避免過度的資料搬移,當將一個 item 從我的最愛中移除時,只將他的 + * FAVH_FAV flag 移除。而沒有這個 flag 的 item 也不被視為我的最愛。 + * + * 我的最愛中,沒設 FAVH_FAV 的資料,將在某些時候,如寫入檔案時,呼叫 + * rebuild_fav 清除乾淨。 + * + * Others + * ====== + * 站長搬移看板所用的 t ,因為不能只存在 nbrd 裡面,又不然再弄出額外的空間, + * 所以當站長不在我的最愛按了 t ,會把這個記錄暫存在 fav 中 + * (FAVH_ADM_TAG == 1, FAVH_FAV == 0)。 + */ + + +/* the total number of items, every level. */ +static int fav_number; + +/* definition of fav stack, the top one is in use now. */ +static int fav_stack_num = 0; +static fav_t *fav_stack[FAV_MAXDEPTH] = {0}; + +static char dirty = 0; + +/* fav_tmp is for recordinge while copying, moving, etc. */ +static fav_t *fav_tmp; +//static int fav_tmp_snum; /* the sequence number in favh in fav_t */ + +#if 1 // DEPRECATED +static void fav4_read_favrec(FILE *frp, fav_t *fp); +#endif + +static void fav_free_branch(fav_t *fp); + +/** + * cast_(board|line|folder) 一族用於將 base class 作轉型 + * (不檢查實際 data type) + */ +inline static fav_board_t *cast_board(fav_type_t *p){ + return (fav_board_t *)p->fp; +} + +inline static fav_line_t *cast_line(fav_type_t *p){ + return (fav_line_t *)p->fp; +} + +inline static fav_folder_t *cast_folder(fav_type_t *p){ + return (fav_folder_t *)p->fp; +} + +/** + * 傳回指定的 fp(dir) 中的 fp->DataTail, 第一個沒用過的位置的 index + */ +inline static int get_data_tail(fav_t *fp){ + return fp->DataTail; +} + +/** + * 傳回指定 dir 中所用的 entry 的總數 (只算真的在裡面,而不算已被移除的) + */ +inline int get_data_number(fav_t *fp){ + return fp->nBoards + fp->nLines + fp->nFolders; +} + +/** + * 傳回目前所在的 dir pointer + */ +inline fav_t *get_current_fav(void){ + if (fav_stack_num == 0) + return NULL; + return fav_stack[fav_stack_num - 1]; +} + +/** + * 將 ft(entry) cast 成一個 dir + */ +inline fav_t *get_fav_folder(fav_type_t *ft){ + return cast_folder(ft)->this_folder; +} + +inline int get_item_type(fav_type_t *ft){ + return ft->type; +} + +/** + * 將一個指定的 dir pointer 存下來,之後可用 fav_get_tmp_fav 來存用 + */ +inline static void fav_set_tmp_folder(fav_t *fp){ + fav_tmp = fp; +} + +inline static fav_t *fav_get_tmp_fav(void){ + return fav_tmp; +} + +/** + * 將 fp(dir) 記的數量中,扣除一單位 ft(entry) + */ +static void fav_decrease(fav_t *fp, fav_type_t *ft) +{ + dirty = 1; + + switch (get_item_type(ft)){ + case FAVT_BOARD: + fp->nBoards--; + break; + case FAVT_LINE: + fp->nLines--; + break; + case FAVT_FOLDER: + fp->nFolders--; + break; + } + fav_number--; +} + +/** + * 將 fp(dir) 記的數量中,增加一單位 ft(entry) + */ +static void fav_increase(fav_t *fp, fav_type_t *ft) +{ + dirty = 1; + + switch (get_item_type(ft)){ + case FAVT_BOARD: + fp->nBoards++; + break; + case FAVT_LINE: + fp->nLines++; + cast_line(ft)->lid = ++fp->lineID; + break; + case FAVT_FOLDER: + fp->nFolders++; + cast_folder(ft)->fid = ++fp->folderID; + break; + } + fav_number++; + fp->DataTail++; +} + +inline static int get_folder_num(fav_t *fp) { + return fp->nFolders; +} + +/** + * get_(folder|line)_id 傳回 fp 中一個新的 folder/line id + */ +inline static int get_folder_id(fav_t *fp) { + return fp->folderID; +} + +inline static int get_line_id(fav_t *fp) { + return fp->lineID; +} + +inline static int get_line_num(fav_t *fp) { + return fp->nLines; +} + +/** + * 設定某個 flag。 + * @bit: 目前所有 flags 有: FAVH_FAV, FAVH_TAG, FAVH_UNREAD, FAVH_ADM_TAG + * @param cmd: FALSE: unset, TRUE: set, EXCH: opposite + */ +void set_attr(fav_type_t *ft, int bit, char cmd){ + if (ft == NULL) + return; + if (cmd == EXCH) + ft->attr ^= bit; + else if (cmd == TRUE) + ft->attr |= bit; + else + ft->attr &= ~bit; + dirty = 1; +} + +inline int is_set_attr(fav_type_t *ft, char bit){ + return ft->attr & bit; +} + +char *get_item_title(fav_type_t *ft) +{ + switch (get_item_type(ft)){ + case FAVT_BOARD: + assert(0<=cast_board(ft)->bid-1 && cast_board(ft)->bid-1<MAX_BOARD); + return bcache[cast_board(ft)->bid - 1].brdname; + case FAVT_FOLDER: + return cast_folder(ft)->title; + case FAVT_LINE: + return "----"; + } + return NULL; +} + +static char *get_item_class(fav_type_t *ft) +{ + switch (get_item_type(ft)){ + case FAVT_BOARD: + assert(0<=cast_board(ft)->bid-1 && cast_board(ft)->bid-1<MAX_BOARD); + return bcache[cast_board(ft)->bid - 1].title; + case FAVT_FOLDER: + return "目錄"; + case FAVT_LINE: + return "----"; + } + return NULL; +} + + +static int get_type_size(int type) +{ + switch (type){ + case FAVT_BOARD: + return sizeof(fav_board_t); + case FAVT_FOLDER: + return sizeof(fav_folder_t); + case FAVT_LINE: + return sizeof(fav_line_t); + } + assert(0); + return 0; +} + +inline static void* fav_malloc(int size){ + void *p; + assert(size>0); + p = (void *)malloc(size); + assert(p); + memset(p, 0, size); + return p; +} + +/** + * 只複製 fav_type_t + */ +inline static void +fav_item_copy(fav_type_t *target, const fav_type_t *source){ + target->type = source->type; + target->attr = source->attr; + target->fp = source->fp; +} + +inline fav_t *get_fav_root(void){ + return fav_stack[0]; +} + +/** + * 是否為有效的 entry + */ +inline int valid_item(fav_type_t *ft){ + return ft->attr & FAVH_FAV; +} + +static int is_need_rebuild_fav(fav_t *fp) +{ + int i, nData; + fav_type_t *ft; + + nData = fp->DataTail; + + for (i = 0; i < nData; i++){ + if (!valid_item(&fp->favh[i])) + return 1; + + ft = &fp->favh[i]; + switch (get_item_type(ft)){ + case FAVT_BOARD: + case FAVT_LINE: + break; + case FAVT_FOLDER: + if(is_need_rebuild_fav(get_fav_folder(&fp->favh[i]))) + return 1; + break; + default: + return 1; + } + } + return 0; +} +/** + * 清除 fp(dir) 中無效的 entry/dir。「無效」指的是沒有 FAVH_FAV flag,所以 + * 不包含不存在的看板。 + */ +static void rebuild_fav(fav_t *fp) +{ + int i, j, nData; + fav_type_t *ft; + + fav_number -= get_data_number(fp); + fp->lineID = fp->folderID = 0; + fp->nLines = fp->nFolders = fp->nBoards = 0; + nData = fp->DataTail; + fp->DataTail = 0; + + for (i = 0, j = 0; i < nData; i++){ + if (!valid_item(&fp->favh[i])) + continue; + + ft = &fp->favh[i]; + switch (get_item_type(ft)){ + case FAVT_BOARD: + case FAVT_LINE: + break; + case FAVT_FOLDER: + rebuild_fav(get_fav_folder(&fp->favh[i])); + break; + default: + continue; + } + fav_increase(fp, &fp->favh[i]); + if (i != j) + fav_item_copy(&fp->favh[j], &fp->favh[i]); + j++; + } + fp->DataTail = get_data_number(fp); +} + +inline void fav_cleanup(void) +{ + if (is_need_rebuild_fav(get_fav_root())) + rebuild_fav(get_fav_root()); +} + +/* sort the fav */ +static int favcmp_by_name(const void *a, const void *b) +{ + return strcasecmp(get_item_title((fav_type_t *)a), get_item_title((fav_type_t *)b)); +} + +void fav_sort_by_name(void) +{ + fav_t *fp = get_current_fav(); + + if (fp == NULL) + return; + + dirty = 1; + rebuild_fav(fp); + qsort(fp->favh, get_data_number(fp), sizeof(fav_type_t), favcmp_by_name); +} + +static int favcmp_by_class(const void *a, const void *b) +{ + fav_type_t *f1, *f2; + int cmp; + + f1 = (fav_type_t *)a; + f2 = (fav_type_t *)b; + if (get_item_type(f1) == FAVT_FOLDER) + return -1; + if (get_item_type(f2) == FAVT_FOLDER) + return 1; + if (get_item_type(f1) == FAVT_LINE) + return 1; + if (get_item_type(f2) == FAVT_LINE) + return -1; + + cmp = strncasecmp(get_item_class(f1), get_item_class(f2), 4); + if (cmp) + return cmp; + return strcasecmp(get_item_title(f1), get_item_title(f2)); +} + +void fav_sort_by_class(void) +{ + fav_t *fp = get_current_fav(); + + if (fp == NULL) + return; + + dirty = 1; + rebuild_fav(fp); + qsort(fp->favh, get_data_number(fp), sizeof(fav_type_t), favcmp_by_class); +} + +/** + * The following is the movement operations in the user interface. + */ + +/** + * 目錄層數是否達到最大值 FAV_MAXDEPTH + */ +inline int fav_stack_full(void){ + return fav_stack_num >= FAV_MAXDEPTH; +} + +inline static int fav_stack_push_fav(fav_t *fp){ + if (fav_stack_full()) + return -1; + fav_stack[fav_stack_num++] = fp; + return 0; +} + +inline static int fav_stack_push(fav_type_t *ft){ +// if (ft->type != FAVT_FOLDER) +// return -1; + return fav_stack_push_fav(get_fav_folder(ft)); +} + +inline static void fav_stack_pop(void){ + fav_stack[--fav_stack_num] = NULL; +} + +void fav_folder_in(int fid) +{ + fav_type_t *tmp = getfolder(fid); + if (get_item_type(tmp) == FAVT_FOLDER){ + fav_stack_push(tmp); + } +} + +void fav_folder_out(void) +{ + fav_stack_pop(); +} + +static int is_valid_favtype(int type) +{ + switch (type){ + case FAVT_BOARD: + case FAVT_FOLDER: + case FAVT_LINE: + return 1; + } + return 0; +} + +/** + * @return 0 if success, -1 if failed + */ +static int read_favrec(FILE *frp, fav_t *fp) +{ + /* TODO handle read errors */ + int i; + fav_type_t *ft; + + fread(&fp->nBoards, sizeof(fp->nBoards), 1, frp); + fread(&fp->nLines, sizeof(fp->nLines), 1, frp); + fread(&fp->nFolders, sizeof(fp->nFolders), 1, frp); + fp->DataTail = get_data_number(fp); + fp->nAllocs = fp->DataTail + FAV_PRE_ALLOC; + fp->lineID = fp->folderID = 0; + fp->favh = (fav_type_t *)fav_malloc(sizeof(fav_type_t) * fp->nAllocs); + fav_number += get_data_number(fp); + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + fread(&ft->type, sizeof(ft->type), 1, frp); + if(!is_valid_favtype(ft->type)) { + ft->type = 0; + return -1; + } + fread(&ft->attr, sizeof(ft->attr), 1, frp); + ft->fp = (void *)fav_malloc(get_type_size(ft->type)); + + switch (ft->type) { + case FAVT_FOLDER: + fread(&cast_folder(ft)->fid, sizeof(char), 1, frp); + fread(&cast_folder(ft)->title, BTLEN + 1, 1, frp); + break; + case FAVT_BOARD: + case FAVT_LINE: + fread(ft->fp, get_type_size(ft->type), 1, frp); + break; + } + } + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + switch (ft->type) { + case FAVT_FOLDER: { + fav_t *p = (fav_t *)fav_malloc(sizeof(fav_t)); + if(read_favrec(frp, p)<0) { + fav_free_branch(p); + return -1; + } + cast_folder(ft)->this_folder = p; + cast_folder(ft)->fid = ++(fp->folderID); + break; + } + case FAVT_LINE: + cast_line(ft)->lid = ++(fp->lineID); + break; + } + } + return 0; +} + +/** + * 從記錄檔中 load 出我的最愛。 + * TODO create default fav, and add SYSOP/PttNewHand (see reginit_fav) + */ +int fav_load(void) +{ + FILE *frp; + char buf[PATHLEN]; + unsigned short version; + fav_t *fp; + if (fav_stack_num > 0) + return -1; + setuserfile(buf, FAV); + + if (!dashf(buf)) { +#if 1 // DEPRECATED + char old[PATHLEN]; + setuserfile(old, FAV4); + if (dashf(old)) { + if ((frp = fopen(old, "r")) == NULL) + return -1; + fp = (fav_t *)fav_malloc(sizeof(fav_t)); + fav_number = 0; + fav4_read_favrec(frp, fp); + fav_stack_push_fav(fp); + fclose(frp); + + fav_save(); + setuserfile(old, FAV ".bak"); + Copy(buf, old); + } + else +#endif + { + fp = (fav_t *)fav_malloc(sizeof(fav_t)); + fav_number = 0; + fav_stack_push_fav(fp); + } + return 0; + } + + if ((frp = fopen(buf, "r")) == NULL) + return -1; +#ifdef CRITICAL_MEMORY + // kcwu: dirty hack, avoid 64byte slot. use 128 instead. + fp = (fav_t *)fav_malloc(sizeof(fav_t)+64); +#else + fp = (fav_t *)fav_malloc(sizeof(fav_t)); +#endif + fav_number = 0; + fread(&version, sizeof(version), 1, frp); + // if (version != FAV_VERSION) { ... } + if(read_favrec(frp, fp)<0) { + // load fail + fav_free_branch(fp); + fav_number = 0; + fp = (fav_t *)fav_malloc(sizeof(fav_t)); + } + fav_stack_push_fav(fp); + fclose(frp); + dirty = 0; + return 0; +} + +/* write to the rec file */ +static void write_favrec(FILE *fwp, fav_t *fp) +{ + int i; + fav_type_t *ft; + + if (fp == NULL) + return; + + fwrite(&fp->nBoards, sizeof(fp->nBoards), 1, fwp); + fwrite(&fp->nLines, sizeof(fp->nLines), 1, fwp); + fwrite(&fp->nFolders, sizeof(fp->nFolders), 1, fwp); + fp->DataTail = get_data_number(fp); + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + fwrite(&ft->type, sizeof(ft->type), 1, fwp); + fwrite(&ft->attr, sizeof(ft->attr), 1, fwp); + + switch (ft->type) { + case FAVT_FOLDER: + fwrite(&cast_folder(ft)->fid, sizeof(char), 1, fwp); + fwrite(&cast_folder(ft)->title, BTLEN + 1, 1, fwp); + break; + case FAVT_BOARD: + case FAVT_LINE: + fwrite(ft->fp, get_type_size(ft->type), 1, fwp); + break; + } + } + + for(i = 0; i < fp->DataTail; i++){ + if (fp->favh[i].type == FAVT_FOLDER) + write_favrec(fwp, get_fav_folder(&fp->favh[i])); + } +} + +/** + * 把記錄檔 save 進我的最愛。 + * @note fav_cleanup() 會先被呼叫。 + */ +int fav_save(void) +{ + FILE *fwp; + char buf[PATHLEN], buf2[PATHLEN]; + unsigned short version = FAV_VERSION; + fav_t *fp = get_fav_root(); + if (fp == NULL) + return -1; + + fav_cleanup(); + if (!dirty) + return 0; + + setuserfile(buf2, FAV); + snprintf(buf, sizeof(buf), "%s.tmp.%x",buf2, getpid()); + fwp = fopen(buf, "w"); + if(fwp == NULL) + return -1; + fwrite(&version, sizeof(version), 1, fwp); + write_favrec(fwp, fp); + + if(fclose(fwp)==0) + Rename(buf, buf2); + + return 0; +} + +/** + * remove ft (設為 invalid,實際上會等到 save 時才清除) + */ +static inline void fav_free_item(fav_type_t *ft) +{ + set_attr(ft, 0xFFFF, FALSE); +} + +/** + * delete ft from fp + */ +static int fav_remove(fav_t *fp, fav_type_t *ft) +{ + if (fp == NULL || ft == NULL) + return -1; + fav_free_item(ft); + fav_decrease(fp, ft); + return 0; +} + +/** + * free the mem of fp recursively + */ +static void fav_free_branch(fav_t *fp) +{ + int i; + fav_type_t *ft; + if (fp == NULL) + return; + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + switch(get_item_type(ft)){ + case FAVT_FOLDER: + fav_free_branch(cast_folder(ft)->this_folder); + break; + case FAVT_BOARD: + case FAVT_LINE: + if (ft->fp) + free(ft->fp); + break; + } + } + free(fp->favh); + free(fp); + fp = NULL; +} + +/** + * free the mem of the whole fav tree recursively + */ +void fav_free(void) +{ + fav_free_branch(get_fav_root()); + + /* reset the stack */ + fav_stack_num = 0; +} + +/** + * 從目前的 dir 中找出特定類別 (type)、id 為 id 的 entry。 + * 找不到傳回 NULL + */ +static fav_type_t *get_fav_item(int id, int type) +{ + int i; + fav_type_t *ft; + fav_t *fp = get_current_fav(); + + if (fp == NULL) + return NULL; + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + if (!valid_item(ft) || get_item_type(ft) != type) + continue; + if (fav_getid(ft) == id) + return ft; + } + return NULL; +} + +/** + * 從目前的 dir 中 remove 特定類別 (type)、id 為 id 的 entry。 + */ +void fav_remove_item(int id, char type) +{ + fav_remove(get_current_fav(), get_fav_item(id, type)); +} + +/** + * get*(bid) 傳回目前的 dir 中該類別 id == bid 的 entry。 + */ +fav_type_t *getadmtag(int bid) +{ + int i; + fav_t *fp = get_fav_root(); + fav_type_t *ft; + assert(0<=bid-1 && bid-1<MAX_BOARD); + for (i = 0; i < fp->DataTail; i++) { + ft = &fp->favh[i]; + if (get_item_type(ft) == FAVT_BOARD && cast_board(ft)->bid == bid && is_set_attr(ft, FAVH_ADM_TAG)) + return ft; + } + return NULL; +} + +fav_type_t *getboard(int bid) +{ + assert(0<=bid-1 && bid-1<MAX_BOARD); + return get_fav_item(bid, FAVT_BOARD); +} + +fav_type_t *getfolder(int fid) +{ + return get_fav_item(fid, FAVT_FOLDER); +} + +char *get_folder_title(int fid) +{ + return get_item_title(get_fav_item(fid, FAVT_FOLDER)); +} + + +char getbrdattr(int bid) +{ + fav_type_t *fb = getboard(bid); + if (!fb) + return 0; + return fb->attr; +} + +int fav_getid(fav_type_t *ft) +{ + switch(get_item_type(ft)){ + case FAVT_FOLDER: + return cast_folder(ft)->fid; + case FAVT_LINE: + return cast_line(ft)->lid; + case FAVT_BOARD: + return cast_board(ft)->bid; + } + return -1; +} + +inline static int is_maxsize(void){ + return fav_number >= MAX_FAV; +} + +/** + * 每次一個 dir 滿時,這個 function 會將 buffer 大小調大 FAV_PRE_ALLOC 個, + * 直到上限為止。 + */ +static int enlarge_if_full(fav_t *fp) +{ + fav_type_t * p; + /* enlarge the volume if need. */ + if (is_maxsize()) + return -1; + if (fp->DataTail < fp->nAllocs) + return 1; + + /* realloc and clean the tail */ + p = (fav_type_t *)realloc(fp->favh, sizeof(fav_type_t) * (fp->nAllocs + FAV_PRE_ALLOC)); + if( p == NULL ) + return -1; + + fp->favh = p; + memset(fp->favh + fp->nAllocs, 0, sizeof(fav_type_t) * FAV_PRE_ALLOC); + fp->nAllocs += FAV_PRE_ALLOC; + return 0; +} + +/** + * pre-append an item, and return the reference back. + */ +static fav_type_t *fav_preappend(fav_t *fp, int type) +{ + fav_type_t *item; + if (enlarge_if_full(fp) < 0) + return NULL; + item = &fp->favh[fp->DataTail]; + item->fp = fav_malloc(get_type_size(type)); + item->attr = FAVH_FAV; + item->type = type; + fav_increase(fp, item); + return item; +} + +static void move_in_folder(fav_t *fav, int src, int dst) +{ + int i, count; + fav_type_t tmp; + + if (fav == NULL) + return; + count = get_data_number(fav); + if (src >= fav->DataTail) + src = count; + if (dst >= fav->DataTail) + dst = count; + if (src == dst) + return; + + dirty = 1; + + /* Find real locations of src and dst in fav->favh[] */ + for(count = i = 0; count <= src && i < fav->DataTail; i++) + if (valid_item(&fav->favh[i])) + count++; + if (count != src + 1) return; + src = i - 1; + for(count = i = 0; count <= dst && i < fav->DataTail; i++) + if (valid_item(&fav->favh[i])) + count++; + if (count != dst + 1) return; + dst = i - 1; + + fav_item_copy(&tmp, &fav->favh[src]); + + if (src < dst) { + for(i = src; i < dst; i++) + fav_item_copy(&fav->favh[i], &fav->favh[i + 1]); + } + else { // dst < src + for(i = src; i > dst; i--) + fav_item_copy(&fav->favh[i], &fav->favh[i - 1]); + } + fav_item_copy(&fav->favh[dst], &tmp); +} + +/** + * 將目前目錄中第 src 個 entry 移到 dst。 + * @note src/dst 是 user 實際上看到的位置,也就是不包含 invalid entry。 + */ +void move_in_current_folder(int from, int to) +{ + move_in_folder(get_current_fav(), from, to); +} + +/** + * the following defines the interface of add new fav_XXX + */ + +/** + * allocate 一個 folder entry + */ +inline static fav_t *alloc_folder_item(void){ + fav_t *fp = (fav_t *)fav_malloc(sizeof(fav_t)); + fp->nAllocs = FAV_PRE_ALLOC; + fp->favh = (fav_type_t *)fav_malloc(sizeof(fav_type_t) * FAV_PRE_ALLOC); + return fp; +} + +/** + * 新增一分隔線 + * @return 加入的 entry 指標 + */ +fav_type_t *fav_add_line(void) +{ + fav_t *fp = get_current_fav(); + if (is_maxsize()) + return NULL; + if (fp == NULL || get_line_num(fp) >= MAX_LINE) + return NULL; + return fav_preappend(fp, FAVT_LINE); +} + +/** + * 新增一目錄 + * @return 加入的 entry 指標 + */ +fav_type_t *fav_add_folder(void) +{ + fav_t *fp = get_current_fav(); + fav_type_t *ft; + if (is_maxsize() || fav_stack_full()) + return NULL; + if (fp == NULL || get_folder_num(fp) >= MAX_FOLDER) + return NULL; + ft = fav_preappend(fp, FAVT_FOLDER); + if (ft == NULL) + return NULL; + cast_folder(ft)->this_folder = alloc_folder_item(); + return ft; +} + +/** + * 將指定看板加入目前的目錄。 + * @return 加入的 entry 指標 + * @note 不允許同一個板被加入兩次 + */ +fav_type_t *fav_add_board(int bid) +{ + fav_t *fp = get_current_fav(); + fav_type_t *ft = getboard(bid); + + if (fp == NULL) + return NULL; + if (ft != NULL) + return ft; + + if (is_maxsize()) + return NULL; + ft = fav_preappend(fp, FAVT_BOARD); + if (ft == NULL) + return NULL; + cast_board(ft)->bid = bid; + return ft; +} + +/** + * for administrator to move/administrate board + */ +fav_type_t *fav_add_admtag(int bid) +{ + fav_t *fp = get_fav_root(); + fav_type_t *ft; + if (is_maxsize()) + return NULL; + ft = fav_preappend(fp, FAVT_BOARD); + set_attr(ft, FAVH_FAV, FALSE); + cast_board(ft)->bid = bid; + // turn on FAVH_ADM_TAG + set_attr(ft, FAVH_ADM_TAG, TRUE); + return ft; +} + + +/* everything about the tag in fav mode. + * I think we don't have to implement the function 'cross-folder' tag.*/ + +/** + * 將目前目錄下,由 type & id 指定的 entry 標上/取消 tag + * @param bool 同 set_attr + * @note 若同一個目錄不幸有同樣的東西,只有第一個會作用。 + */ +void fav_tag(int id, char type, char bool) { + fav_type_t *ft = get_fav_item(id, type); + if (ft != NULL) + set_attr(ft, FAVH_TAG, bool); +} + +static void fav_dosomething_tagged_item(fav_t *fp, int (*act)(fav_t *, fav_type_t *)) +{ + int i; + for(i = 0; i < fp->DataTail; i++){ + if (valid_item(&fp->favh[i]) && is_set_attr(&fp->favh[i], FAVH_TAG)) + if ((*act)(fp, &fp->favh[i]) < 0) + break; + } +} + +static int remove_tagged_item(fav_t *fp, fav_type_t *ft) +{ + // do not remove the folder if it's on the stack + if (get_item_type(ft) == FAVT_FOLDER) { + int i; + for(i = 0; i < FAV_MAXDEPTH && fav_stack[i] != NULL; i++) { + if (fav_stack[i] == get_fav_folder(ft)){ + set_attr(ft, FAVH_TAG, FALSE); + return 0; + } + } + } + return fav_remove(fp, ft); +} + +inline static int fav_remove_tagged_item(fav_t *fp){ + fav_dosomething_tagged_item(fp, remove_tagged_item); + return 0; +} + +/* add an item into a fav_t. + * here we must give the line and foler a new id to prevent an old one is exist. + */ +static int add_and_remove_tag(fav_t *fp, fav_type_t *ft) +{ + fav_type_t *tmp; + // do not remove the folder if it's on the stack + if (get_item_type(ft) == FAVT_FOLDER) { + int i; + for(i = 0; i < FAV_MAXDEPTH && fav_stack[i] != NULL; i++) { + if (fav_stack[i] == get_fav_folder(ft)){ + set_attr(ft, FAVH_TAG, FALSE); + return 0; + } + } + } + tmp = fav_preappend(fav_get_tmp_fav(), ft->type); + if (ft->type == FAVT_FOLDER) { + strlcpy(cast_folder(tmp)->title, cast_folder(ft)->title, BTLEN + 1); + cast_folder(tmp)->this_folder = cast_folder(ft)->this_folder; + } + else { + memcpy(tmp->fp, ft->fp, get_type_size(ft->type)); + } + + + free(ft->fp); + ft->fp = NULL; + set_attr(tmp, FAVH_TAG, FALSE); + fav_remove(fp, ft); + return 0; +} + +inline static int fav_add_tagged_item(fav_t *fp){ + if (fp == fav_get_tmp_fav()) + return -1; + fav_dosomething_tagged_item(fp, add_and_remove_tag); + return 0; +} + +static void fav_do_recursively(fav_t *fp, int (*act)(fav_t *)) +{ + int i; + fav_type_t *ft; + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + if (!valid_item(ft)) + continue; + if (get_item_type(ft) == FAVT_FOLDER && get_fav_folder(ft) != NULL){ + fav_do_recursively(get_fav_folder(ft), act); + } + } + (*act)(fp); +} + +static void fav_dosomething_all_tagged_item(int (*act)(fav_t *)) +{ + fav_do_recursively(get_fav_root(), act); +} + +/** + * fav_*_all_tagged_item 在整個我的最愛上對已標上 tag 的 entry 做某件事。 + */ +void fav_remove_all_tagged_item(void) +{ + fav_dosomething_all_tagged_item(fav_remove_tagged_item); +} + +void fav_add_all_tagged_item(void) +{ + fav_set_tmp_folder(get_current_fav()); + fav_dosomething_all_tagged_item(fav_add_tagged_item); +} + +inline static int remove_tag(fav_t *fp, fav_type_t *ft) +{ + set_attr(ft, FAVH_TAG, FALSE); + return 0; +} + +inline static int remove_tags(fav_t *fp) +{ + fav_dosomething_tagged_item(fp, remove_tag); + return 0; +} + +/** + * 移除我的最愛所有的 tags + */ +void fav_remove_all_tag(void) +{ + fav_dosomething_all_tagged_item(remove_tags); +} + +/** + * 設定 folder 的中文名稱 + */ +void fav_set_folder_title(fav_type_t *ft, char *title) +{ + if (get_item_type(ft) != FAVT_FOLDER) + return; + dirty = 1; + strlcpy(cast_folder(ft)->title, title, sizeof(cast_folder(ft)->title)); +} + +#define BRD_OLD 0 +#define BRD_NEW 1 +#define BRD_END 2 +/** + * 如果 user 開啟 FAVNEW_FLAG 的功能: + * mode == 1: update 看板,並將新看板加入我的最愛。 + * mode == 0: update 資訊但不加入。 + * + * @return 加入的看板數 + * PS. count 就讓它數完,才不會在下一次 login 又從一半開始數。 + */ +int updatenewfav(int mode) +{ + /* mode: 0: don't write to fav 1: write to fav */ + int i, fd, brdnum; + int count = 0; + char fname[80], *brd; + + if(!(cuser.uflag2 & FAVNEW_FLAG)) + return 0; + + setuserfile(fname, FAVNB); + + if( (fd = open(fname, O_RDWR | O_CREAT, 0600)) != -1 ){ + + assert(numboards>=0); + brdnum = numboards; /* avoid race */ + + if ((brd = (char *)malloc((brdnum + 1) * sizeof(char))) == NULL) + return -1; + memset(brd, 0, (brdnum + 1) * sizeof(char)); + + i = read(fd, brd, brdnum * sizeof(char)); + if (i < 0) { + free(brd); + close(fd); + vmsg("favorite subscription error"); + return -1; + } + + brd[i] = BRD_END; + + for(i = 0; i < brdnum && brd[i] != BRD_END; i++){ + if(brd[i] == BRD_NEW){ + /* check the permission if the board exsits */ + if(bcache[i].brdname[0] && HasBoardPerm(&bcache[i])){ + if(mode && !(bcache[i].brdattr & BRD_SYMBOLIC)) { + fav_add_board(i + 1); + count++; + } + brd[i] = BRD_OLD; + } + } + else{ + if(!bcache[i].brdname[0]) + brd[i] = BRD_NEW; + } + } + + if( i < brdnum) { // the board number may change + for(; i < brdnum; ++i){ + if(bcache[i].brdname[0] && HasBoardPerm(&bcache[i])){ + if(mode && !(bcache[i].brdattr & BRD_SYMBOLIC)) { + fav_add_board(i + 1); + count++; + } + brd[i] = BRD_OLD; + } + else + brd[i] = BRD_NEW; + } + } + + brd[i] = BRD_END; + + lseek(fd, 0, SEEK_SET); + write(fd, brd, (brdnum + 1) * sizeof(char)); + + free(brd); + close(fd); + } + + return count; +} + +void subscribe_newfav(void) +{ + updatenewfav(0); +} + +// create defaults for new user +void reginit_fav(void) +{ + int bid = 0; + + fav_load(); // for creating root + +#ifdef GLOBAL_NEWBIE + bid = getbnum(GLOBAL_NEWBIE); + if (bid > 0) fav_add_board(bid); +#endif + +#ifdef GLOBAL_TEST + bid = getbnum(GLOBAL_TEST); + if (bid > 0) fav_add_board(bid); +#endif + +#ifdef GLOBAL_ASKBOARD + bid = getbnum(GLOBAL_ASKBOARD); + if (bid > 0) fav_add_board(bid); +#endif + +#ifdef GLOBAL_SYSOP + bid = getbnum(GLOBAL_SYSOP); + if (bid > 0) fav_add_board(bid); +#endif + + fav_save(); +} + +#if 1 // DEPRECATED +typedef struct { + char fid; + char title[BTLEN + 1]; + int this_folder; +} fav_folder4_t; + +typedef struct { + int bid; + time4_t lastvisit; /* UNUSED */ + char attr; +} fav4_board_t; + +static int fav4_get_type_size(int type) +{ + switch (type){ + case FAVT_BOARD: + return sizeof(fav4_board_t); + case FAVT_FOLDER: + return sizeof(fav_folder_t); + case FAVT_LINE: + return sizeof(fav_line_t); + } + return 0; +} + +static void fav4_read_favrec(FILE *frp, fav_t *fp) +{ + int i; + fav_type_t *ft; + + fread(&fp->nBoards, sizeof(fp->nBoards), 1, frp); + fread(&fp->nLines, sizeof(fp->nLines), 1, frp); + fread(&fp->nFolders, sizeof(fp->nFolders), 1, frp); + fp->DataTail = get_data_number(fp); + fp->nAllocs = fp->DataTail + FAV_PRE_ALLOC; + fp->lineID = fp->folderID = 0; + fp->favh = (fav_type_t *)fav_malloc(sizeof(fav_type_t) * fp->nAllocs); + fav_number += get_data_number(fp); + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + fread(&ft->type, sizeof(ft->type), 1, frp); + fread(&ft->attr, sizeof(ft->attr), 1, frp); + ft->fp = (void *)fav_malloc(fav4_get_type_size(ft->type)); + + /* TODO A pointer has different size between 32 and 64-bit arch. + * But the pointer in fav_folder_t is irrelevant here. + * In order not to touch the current .fav4, fav_folder4_t is used + * here. It should be FIXED in the next version. */ + switch (ft->type) { + case FAVT_FOLDER: + fread(ft->fp, sizeof(fav_folder4_t), 1, frp); + break; + case FAVT_BOARD: + case FAVT_LINE: + fread(ft->fp, fav4_get_type_size(ft->type), 1, frp); + break; + } + } + + for(i = 0; i < fp->DataTail; i++){ + ft = &fp->favh[i]; + switch (ft->type) { + case FAVT_FOLDER: { + fav_t *p = (fav_t *)fav_malloc(sizeof(fav_t)); + fav4_read_favrec(frp, p); + cast_folder(ft)->this_folder = p; + cast_folder(ft)->fid = ++(fp->folderID); + break; + } + case FAVT_LINE: + cast_line(ft)->lid = ++(fp->lineID); + break; + } + } +} +#endif + +// vim:ts=8:sw=4 |