summaryrefslogblamecommitdiffstats
path: root/mbbsd/vote.c
blob: ca288481e3a210b5fc59c13dd8c85a5213ded627 (plain) (tree)
1
                                                     







































                                                                          






                                                                  











































































                                                                        
                  











                                                                
                








                                                            







































                                                                            
                     














































                                                                











































                                                                          
                                                    

                                        
                                                      












                                            
                     



                      
                             

















                                                                
















                                              



                      


































































































































































                                                                            
                             























                                                            
 

                                             
                        
                                                                       






                                                
                                              











































                                                                            
                                     
























                                                                         
                                                       





                                            
                                                    






                                                            
              





















                                                             
                                                                  









































                                                                             
                                        







                                                

                                                     






























































































































































                                                                             
                                                                              






















































                                                                            

                                                                             











































































                                                                        
                             
























                                                            

                                             

                        
                                                                       







                                                
                                                  























                                           
/* $Id: vote.c,v 1.7 2002/05/13 03:20:04 ptt Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "pttstruct.h"
#include "modes.h"
#include "common.h"
#include "perm.h"
#include "proto.h"

static int total;
extern int numboards;
extern boardheader_t *bcache;     /* Thor: for speed up */
extern char *err_board_update;
extern char *fn_board;
extern char *msg_seperator;
extern int t_lines, t_columns;  /* Screen size / width */
extern int b_lines;             /* Screen bottom line number: t_lines-1 */
extern int currmode;
extern int usernum;
extern char currboard[];        /* name of currently selected board */
extern userec_t cuser;

static char STR_bv_control[] = "control";  /* 投票日期 選項 */
static char STR_bv_desc[] = "desc";        /* 投票目的 */
static char STR_bv_ballots[] = "ballots";
static char STR_bv_flags[] = "flags";
static char STR_bv_comments[] = "comments"; /* 投票者的建意 */
static char STR_bv_limited[] = "limited"; /* 私人投票 */
static char STR_bv_title[] = "vtitle";

static char STR_bv_results[] = "results";

static char STR_new_control[] = "control0\0";  /* 投票日期 選項 */
static char STR_new_desc[] = "desc0\0";        /* 投票目的 */
static char STR_new_ballots[] = "ballots0\0";
static char STR_new_flags[] = "flags0\0";
static char STR_new_comments[] = "comments0\0"; /* 投票者的建意 */
static char STR_new_limited[] = "limited0\0"; /* 私人投票 */
static char STR_new_title[] = "vtitle0\0";

int strip_ansi(char *buf, char *str, int mode) {
    register int ansi, count = 0;

    for(ansi = 0; *str /*&& *str != '\n' */; str++) {
    if(*str == 27) {
        if(mode) {
        if(buf)
            *buf++ = *str;
        count++;
        }
        ansi = 1;
    } else if(ansi && strchr("[;1234567890mfHABCDnsuJKc=n", *str)) {
        if((mode == NO_RELOAD && !strchr("c=n", *str)) ||
           (mode == ONLY_COLOR && strchr("[;1234567890m", *str))) {
        if(buf)
            *buf++ = *str;
        count++;
        }
        if(strchr("mHn ", *str))
        ansi = 0;
    } else {
        ansi =0;
        if(buf)
        *buf++ = *str;
        count++;
    }
    }
    if(buf)
    *buf = '\0';
    return count;
}

void b_suckinfile(FILE *fp, char *fname) {
    FILE *sfp;

    if((sfp = fopen(fname, "r"))) {
    char inbuf[256];

    while(fgets(inbuf, sizeof(inbuf), sfp))
        fputs(inbuf, fp);
    fclose(sfp);
    }
}

static void b_count(char *buf, int counts[]) {
    char inchar;
    int fd;

    memset(counts, 0, 31 * sizeof(counts[0]));
    total = 0;
    if((fd = open(buf, O_RDONLY)) != -1) {
    flock(fd, LOCK_EX);         /* Thor: 防止多人同時算 */
    while(read(fd, &inchar, 1) == 1) {
        counts[(int)(inchar - 'A')]++;
        total++;
    }
    flock(fd, LOCK_UN);
    close(fd);
    }
}


static int b_nonzeroNum(char *buf) {
    int i = 0;
    char inchar;
    int fd;

    if((fd = open(buf, O_RDONLY)) != -1) {
    while(read(fd, &inchar, 1) == 1)
        if(inchar)
        i++;
    close(fd);
    }
    return i;
}
extern time_t now;
static void vote_report(char *bname, char *fname, char *fpath) {
    register char *ip;
    time_t dtime;
    int fd, bid;
    fileheader_t header;

    ip = fpath;
    while(*(++ip));
    *ip++ = '/';

    /* get a filename by timestamp */

    dtime = now;
    for(;;) {
    sprintf(ip, "M.%ld.A", ++dtime);
    fd = open(fpath, O_CREAT | O_EXCL | O_WRONLY, 0644);
    if(fd >= 0)
        break;
    dtime++;
    }
    close(fd);
    
    unlink(fpath);
    link(fname, fpath);
    
    /* append record to .DIR */
    
    memset(&header, 0, sizeof(fileheader_t));
    strcpy(header.owner, "[馬路探子]");
    sprintf(header.title, "[%s] 看板 選情報導", bname);
    {
    register struct tm *ptime = localtime(&dtime);
    
    sprintf(header.date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
    }
    strcpy(header.filename, ip);

    strcpy(ip, ".DIR");
    if((fd = open(fpath, O_WRONLY | O_CREAT, 0644)) >= 0) {
    flock(fd, LOCK_EX);
    lseek(fd, 0, SEEK_END);
    write(fd, &header, sizeof(fileheader_t));
    flock(fd, LOCK_UN);
    close(fd);
        if((bid = getbnum(bname)) > 0)
           setbtotal(bid);

    }
   
}

static void b_result_one(boardheader_t *fh, int ind) {
    FILE *cfp, *tfp, *frp, *xfp;
    char *bname ;
    char buf[STRLEN];
    char inbuf[80];
    int counts[31];
    int num ;
    int junk;
    char b_control[64];
    char b_newresults[64];
    char b_report[64];
    time_t closetime;

    fh->bvote--;

    if(fh->bvote==0)
        fh->bvote=2;
    else if(fh->bvote==2)
        fh->bvote=1;

    if(ind) {
    sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, ind);
    sprintf(STR_new_control, "%s%d", STR_bv_control, ind);
    sprintf(STR_new_desc, "%s%d", STR_bv_desc, ind);
    sprintf(STR_new_flags, "%s%d", STR_bv_flags, ind);
    sprintf(STR_new_comments, "%s%d", STR_bv_comments, ind);
    sprintf(STR_new_limited, "%s%d", STR_bv_limited, ind);
    sprintf(STR_new_title, "%s%d", STR_bv_title, ind);
    } else {
    strcpy(STR_new_ballots, STR_bv_ballots);
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_desc, STR_bv_desc);
    strcpy(STR_new_flags, STR_bv_flags);
    strcpy(STR_new_comments, STR_bv_comments);
    strcpy(STR_new_limited, STR_bv_limited);
    strcpy(STR_new_title, STR_bv_title);
    }

    bname = fh->brdname;

    setbfile(buf, bname, STR_new_control);
    cfp = fopen(buf,"r");
    fscanf(cfp, "%d\n%lu\n", &junk, &closetime);
    fclose(cfp);

    setbfile(b_control, bname, "tmp");
    if(rename(buf, b_control) == -1)
    return;
    setbfile(buf, bname, STR_new_flags);
    num = b_nonzeroNum(buf);
    unlink(buf);
    setbfile(buf, bname, STR_new_ballots);
    b_count(buf, counts);
    unlink(buf);

    setbfile(b_newresults, bname, "newresults");
    if((tfp = fopen(b_newresults, "w")) == NULL)
    return;

    setbfile(buf, bname, STR_new_title);

    if((xfp=fopen(buf,"r"))) {
    fgets(inbuf, sizeof(inbuf), xfp);
    fprintf(tfp, "%s\n◆ 投票名稱: %s\n\n", msg_seperator, inbuf);
    }
    
    fprintf(tfp, "%s\n◆ 投票中止於: %s\n\n◆ 票選題目描述:\n\n",
        msg_seperator, ctime(&closetime));
    fh->vtime = now;
    
    setbfile(buf, bname, STR_new_desc);
    
    b_suckinfile(tfp, buf);
    unlink(buf);
    
    if((cfp = fopen(b_control, "r"))) {
    fgets(inbuf, sizeof(inbuf), cfp);
    fgets(inbuf, sizeof(inbuf), cfp);
    fprintf(tfp, "\n◆投票結果:(共有 %d 人投票,每人最多可投 %d 票)\n",
        num, junk);
    while(fgets(inbuf, sizeof(inbuf), cfp)) {
        inbuf[(strlen(inbuf) - 1)] = '\0';
        num = counts[inbuf[0] - 'A'];
        fprintf(tfp, "    %-42s %3d 票   %02.2f%%\n", inbuf + 3, num,
            (float)(num*100)/(float)(total));
    }
    fclose(cfp);
    }
    unlink(b_control);
    
    fprintf(tfp, "%s\n◆ 使用者建議:\n\n", msg_seperator);
    setbfile(buf, bname, STR_new_comments);
    b_suckinfile(tfp, buf);
    unlink(buf);
    
    fprintf(tfp, "%s\n◆ 總票數 = %d 票\n\n", msg_seperator, total);
    fclose(tfp);
    
    setbfile(b_report, bname, "report");
    if((frp = fopen(b_report, "w"))) {
    b_suckinfile(frp, b_newresults);
    fclose(frp);
    }
    sprintf(inbuf, "boards/%c/%s", bname[0], bname);
    vote_report(bname, b_report, inbuf);
    if(!(fh->brdattr &BRD_NOCOUNT)) {
    sprintf(inbuf, "boards/%c/%s", 'R', "Record");
    vote_report(bname, b_report, inbuf);
    }
    unlink(b_report);
    
    tfp = fopen(b_newresults, "a");
    setbfile(buf, bname, STR_bv_results);
    b_suckinfile(tfp, buf);
    fclose(tfp);
    Rename(b_newresults, buf);
}

static void b_result(boardheader_t *fh) {
    FILE *cfp;
    time_t closetime;
    int i;
    char buf[STRLEN];
    char temp[STRLEN];

    for(i = 0; i < 20; i++) {
    if(i)
        sprintf(STR_new_control, "%s%d", STR_bv_control, i);
        else
        strcpy(STR_new_control, STR_bv_control);

        setbfile(buf, fh->brdname, STR_new_control);
        cfp = fopen(buf,"r");
        if (!cfp)
            continue;
        fgets(temp,sizeof(temp),cfp);
        fscanf(cfp, "%lu\n", &closetime);
        fclose(cfp);
        if(closetime < now)
        b_result_one(fh,i);
    }
}

static int b_close(boardheader_t *fh) {

    if(fh->bvote == 2) {
    if(fh->vtime < now - 3 * 86400) {
        fh->bvote = 0;
        return 1;
    }
    else
        return 0;
    }
    b_result(fh);
    return 1;
}

int b_closepolls() {
    static char *fn_vote_polling = ".polling";
    boardheader_t *fhp;
    FILE *cfp;
    int pos, dirty;
    time_t last;
    char timebuf[100];

/* Edited by CharlieL for can't auto poll bug */

    if((cfp = fopen(fn_vote_polling,"r"))) {
        fgets(timebuf,100*sizeof(char),cfp);
        sscanf(timebuf, "%lu", &last);
        fclose(cfp);
        if(last + 3600 >= now)
            return 0;
    }

    if((cfp = fopen(fn_vote_polling, "w")) == NULL)
    return 0;
    fprintf(cfp, "%lu\n%s\n", now, ctime(&now));
    fclose(cfp);

    dirty = 0;
    for(fhp = bcache, pos = 1; pos <= numboards; fhp++, pos++) {
    if(fhp->bvote && b_close(fhp)) {
        if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
        outs(err_board_update);
        dirty = 1;
    }
    }
    if(dirty) /* vote flag changed */
    reset_board(pos);

    return 0;
}

static int vote_view(char *bname, int index) {
    boardheader_t *fhp;
    FILE* fp;
    char buf[STRLEN], genbuf[STRLEN], inbuf[STRLEN];
    struct stat stbuf;
    int fd, num = 0, i, pos, counts[31];
    time_t closetime;

    if(index) {
    sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, index);
    sprintf(STR_new_control, "%s%d", STR_bv_control, index);
    sprintf(STR_new_desc, "%s%d", STR_bv_desc, index);
    sprintf(STR_new_flags, "%s%d", STR_bv_flags, index);
    sprintf(STR_new_comments, "%s%d", STR_bv_comments, index);
    sprintf(STR_new_limited, "%s%d", STR_bv_limited, index);
    sprintf(STR_new_title, "%s%d", STR_bv_title, index);
    } else {
    strcpy(STR_new_ballots, STR_bv_ballots);
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_desc, STR_bv_desc);
    strcpy(STR_new_flags, STR_bv_flags);
    strcpy(STR_new_comments, STR_bv_comments);
    strcpy(STR_new_limited, STR_bv_limited);
    strcpy(STR_new_title, STR_bv_title);
    }

    setbfile(buf, bname, STR_new_ballots);
    if((fd = open(buf, O_RDONLY)) > 0) {
    fstat(fd, &stbuf);
    close(fd);
    } else
    stbuf.st_size = 0;
    
    setbfile(buf, bname, STR_new_title);
    move(0, 0);
    clrtobot();
    
    if((fp = fopen(buf, "r"))) {
    fgets(inbuf, sizeof(inbuf), fp);
    prints("\n投票名稱: %s", inbuf);
    }
    
    setbfile(buf, bname, STR_new_control);
    fp = fopen(buf, "r");
    fgets(inbuf, sizeof(inbuf), fp);
    fscanf(fp, "%lu\n", &closetime);

    prints("\n◆ 預知投票紀事: 每人最多可投 %d 票,目前共有 %d 票,\n"
       "本次投票將結束於 %s", atoi(inbuf),  stbuf.st_size,
       ctime(&closetime));
    
    /* Thor: 開放 票數 預知 */
    setbfile(buf, bname, STR_new_flags);
    num = b_nonzeroNum(buf);
    
    setbfile(buf, bname, STR_new_ballots);
    b_count(buf, counts);
    
    prints("共有 %d 人投票\n", num);
    total = 0;
    
    while(fgets(inbuf, sizeof(inbuf), fp)) {
    inbuf[(strlen(inbuf) - 1)] = '\0';
    inbuf[30] = '\0';         /* truncate */
    i = inbuf[0] - 'A';
    num = counts[i];
    move(i % 15 + 6, i / 15 * 40);
    prints("  %-32s%3d 票", inbuf, num);
    total += num;
    }
    fclose(fp);
    pos = getbnum(bname);
    fhp = bcache + pos - 1;
    move(t_lines - 3, 0);
    prints("◆ 目前總票數 = %d 票", total);
    getdata(b_lines - 1, 0, "(A)取消投票 (B)提早開票 (C)繼續?[C] ", genbuf,
        4, LCECHO);
    if(genbuf[0] == 'a') {
    setbfile(buf, bname, STR_new_control);
    unlink(buf);
    setbfile(buf, bname, STR_new_flags);
    unlink(buf);
    setbfile(buf, bname, STR_new_ballots);
    unlink(buf);
    setbfile(buf, bname, STR_new_desc);
    unlink(buf);
    setbfile(buf, bname, STR_new_limited);
    unlink(buf);
    setbfile(buf,bname, STR_new_title);
    unlink(buf);
    
    if(fhp->bvote)
        fhp->bvote--;
    if (fhp->bvote == 2)
        fhp->bvote = 1;
    
    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
        outs(err_board_update);
    reset_board(pos);
    } else if(genbuf[0] == 'b') {
    b_result_one(fhp,index);
    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
        outs(err_board_update);
    
    reset_board(pos);
    }
    return FULLUPDATE;
}

static int vote_view_all(char *bname) {
    int i;
    int x = -1;
    FILE *fp, *xfp;
    char buf[STRLEN], genbuf[STRLEN];
    char inbuf[80];
    
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_title, STR_bv_title);
    setbfile(buf, bname, STR_new_control);
    move(0, 0);
    if((fp=fopen(buf,"r"))) {
    prints("(0) ");
    x = 0;
    fclose(fp);
    
    setbfile(buf, bname, STR_new_title);
    if((xfp=fopen(buf,"r")))
        fgets(inbuf, sizeof(inbuf), xfp);
    else
        strcpy(inbuf, "無標題");
    prints("%s\n", inbuf);
    fclose(xfp);
    }

    for(i = 1; i < 20; i++) {
    sprintf(STR_new_control, "%s%d", STR_bv_control, i);
    sprintf(STR_new_title, "%s%d", STR_bv_title, i);
    setbfile(buf, bname, STR_new_control);
    if((fp=fopen(buf,"r"))) {
        prints("(%d) ", i);
        x = i;
        fclose(fp);

        setbfile(buf, bname, STR_new_title);
        if((xfp=fopen(buf,"r")))
        fgets(inbuf, sizeof(inbuf), xfp);
        else
        strcpy(inbuf, "無標題");
        prints("%s\n", inbuf);
        fclose(xfp);
    }
    }

    if(x < 0)
    return FULLUPDATE;
    sprintf(buf, "要看幾號投票 [%d] ", x);
    
    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);


    if(atoi(genbuf) < 0 || atoi(genbuf) > 20)
    sprintf(genbuf,"%d",x);
    if(genbuf[0] != '0')
    sprintf(STR_new_control, "%s%d", STR_bv_control, atoi(genbuf));
    else
    strcpy(STR_new_control, STR_bv_control);
    
    setbfile(buf, bname, STR_new_control);

    if((fp=fopen(buf,"r"))) {
    fclose(fp);
    return vote_view(bname, atoi(genbuf));
    }
    else
    return FULLUPDATE;
}

static int vote_maintain(char *bname) {
    FILE *fp = NULL;
    char inbuf[STRLEN], buf[STRLEN];
    int num = 0, aborted, pos, x, i;
    time_t closetime;
    boardheader_t *fhp;
    char genbuf[4];

    if(!(currmode & MODE_BOARD))
    return 0;
    if((pos = getbnum(bname)) <= 0)
    return 0;

    stand_title("舉辦投票");
    fhp = bcache + pos - 1;

/* CharlieL */
    if(fhp->bvote != 2 && fhp->bvote !=0) {
    getdata(b_lines - 1, 0,
        "(V)觀察目前投票 (M)舉辦新投票 (A)取消所有投票 (Q)繼續 [Q]",
        genbuf, 4, LCECHO);
    if(genbuf[0] == 'v')
        return vote_view_all(bname);
    else if(genbuf[0] == 'a') {
        fhp->bvote=0;
        
        setbfile(buf, bname, STR_bv_control);
        unlink(buf);
        setbfile(buf, bname, STR_bv_flags);
        unlink(buf);
        setbfile(buf, bname, STR_bv_ballots);
        unlink(buf);
        setbfile(buf, bname, STR_bv_desc);
        unlink(buf);
        setbfile(buf, bname, STR_bv_limited);
        unlink(buf);
        setbfile(buf, bname, STR_bv_title);
        unlink(buf);
        
        for(i = 1; i < 20; i++) {
        sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, i);
        sprintf(STR_new_control, "%s%d", STR_bv_control, i);
        sprintf(STR_new_desc, "%s%d", STR_bv_desc, i);
        sprintf(STR_new_flags, "%s%d", STR_bv_flags, i);
        sprintf(STR_new_comments, "%s%d", STR_bv_comments, i);
        sprintf(STR_new_limited, "%s%d", STR_bv_limited, i);
        sprintf(STR_new_title, "%s%d", STR_bv_title, i);
        
        setbfile(buf, bname, STR_new_control);
        unlink(buf);
        setbfile(buf, bname, STR_new_flags);
        unlink(buf);
        setbfile(buf, bname, STR_new_ballots);
        unlink(buf);
        setbfile(buf, bname, STR_new_desc);
        unlink(buf);
        setbfile(buf, bname, STR_new_limited);
        unlink(buf);
        setbfile(buf, bname, STR_new_title);
        unlink(buf);
        }
        if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
        outs(err_board_update);
        
        return FULLUPDATE;
    } else if(genbuf[0] != 'm' || fhp->bvote >= 20)
        return FULLUPDATE;
    }

    strcpy(STR_new_control, STR_bv_control);
    setbfile(buf,bname, STR_new_control);
    x = 0;
    while(x < 20 && (fp = fopen(buf,"r")) != NULL) {
    fclose(fp);
        x++;
        sprintf(STR_new_control, "%s%d", STR_bv_control, x);
    setbfile(buf, bname, STR_new_control);
    }
    if(fp)
    fclose(fp);
    if(x >=20)
        return FULLUPDATE;
    if(x) {
    sprintf(STR_new_ballots, "%s%d", STR_bv_ballots,x);
    sprintf(STR_new_control, "%s%d", STR_bv_control,x);
    sprintf(STR_new_desc, "%s%d", STR_bv_desc,x);
    sprintf(STR_new_flags, "%s%d", STR_bv_flags,x);
    sprintf(STR_new_comments, "%s%d", STR_bv_comments,x);
    sprintf(STR_new_limited, "%s%d", STR_bv_limited,x);
    sprintf(STR_new_title, "%s%d", STR_bv_title,x);
    } else {
    strcpy(STR_new_ballots, STR_bv_ballots);
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_desc, STR_bv_desc);
    strcpy(STR_new_flags, STR_bv_flags);
    strcpy(STR_new_comments, STR_bv_comments);
    strcpy(STR_new_limited, STR_bv_limited);
    strcpy(STR_new_title, STR_bv_title);
    }
    clear();
    move(0,0);
    prints("第 %d 號投票\n", x);
    setbfile(buf, bname, STR_new_title);
    getdata(4, 0, "請輸入投票名稱", inbuf, sizeof(inbuf), LCECHO);
    if(inbuf[0]=='\0')
    strcpy(inbuf,"不知名的");
    fp = fopen(buf, "w");
    fprintf(fp, "%s", inbuf);
    fclose(fp);
    
    prints("按任何鍵開始編輯此次 [投票宗旨]");
    pressanykey();
    setbfile(buf, bname, STR_new_desc);
    aborted = vedit(buf, NA, NULL);
    if(aborted== -1) {
    clear();
    outs("取消此次投票");
    pressanykey();
    return FULLUPDATE;
    }
    aborted = 0;
    setbfile(buf, bname, STR_new_flags);
    unlink(buf);
    
    getdata(4, 0,
        "是否限定投票者名單:(y)編籍可投票人員名單[n]任何人皆可投票:[N]",
        inbuf, 2, LCECHO);
    setbfile(buf, bname, STR_new_limited);
    if(inbuf[0] == 'y') {
    fp = fopen(buf, "w");
    fprintf(fp,"此次投票設限");
    fclose(fp);
    friend_edit(FRIEND_CANVOTE);
    } else {
    if(dashf(buf))
        unlink(buf);
    }
    clear();
    getdata(0, 0, "此次投票進行幾天 (一到十天)?", inbuf, 4, DOECHO);

    closetime = atoi(inbuf);
    if(closetime <= 0)
    closetime = 1;
    else if(closetime >10)
    closetime = 10;
    
    closetime = closetime * 86400 + now;
    setbfile(buf, bname, STR_new_control);
    fp = fopen(buf, "w");
    fprintf(fp, "00\n%lu\n", closetime);

    outs("\n請依序輸入選項, 按 ENTER 完成設定");
    num = 0;
    while(!aborted) {
    sprintf(buf, "%c) ", num + 'A');
    getdata((num % 15) + 2, (num / 15) * 40, buf,
        inbuf, sizeof(inbuf), DOECHO);
    if(*inbuf) {
        fprintf(fp, "%1c) %s\n", (num+'A'), inbuf);
        num++;
    }
    if((*inbuf == '\0' && num >= 1) || num == 30)
        aborted = 1;
    }
    sprintf(buf, "請問每人最多可投幾票?([1]~%d): ", num);
    
    getdata(t_lines-3, 0, buf, inbuf, 3, DOECHO);
    
    if(atoi(inbuf) <= 0 || atoi(inbuf) > num)
    strcpy(inbuf,"1");
    
    rewind(fp);
    fprintf(fp, "%2d\n", MAX(1, atoi(inbuf)));
    fclose(fp);
    
    if(fhp->bvote == 2)
        fhp->bvote = 0;
    else if(fhp->bvote == 1)
        fhp->bvote = 2;
    else if(fhp->bvote == 2)
        fhp->bvote = 1;
    
    fhp->bvote ++;
    
    if(substitute_record(fn_board, fhp, sizeof(*fhp), pos) == -1)
    outs(err_board_update);
    reset_board(pos);
    outs("開始投票了!");
    
    return FULLUPDATE;
}

static int vote_flag(char *bname, int index, char val) {
    char buf[256], flag;
    int fd, num, size;

    if(index)
    sprintf(STR_new_flags, "%s%d", STR_bv_flags, index);
    else
    strcpy(STR_new_flags, STR_bv_flags);
    
    num = usernum - 1;
    setbfile(buf, bname, STR_new_flags);
    if((fd = open(buf, O_RDWR | O_CREAT, 0600)) == -1)
    return -1;
    size = lseek(fd, 0, SEEK_END);
    memset(buf, 0, sizeof(buf));
    while(size <= num) {
    write(fd, buf, sizeof(buf));
    size += sizeof(buf);
    }
    lseek(fd, num, SEEK_SET);
    read(fd, &flag, 1);
    if(flag == 0 && val != 0) {
    lseek(fd, num, SEEK_SET);
    write(fd, &val, 1);
    }
    close(fd);
    return flag;
}

static int same(char compare, char list[], int num) {
    int n;
    int rep = 0;

    for(n = 0; n < num; n++) {
    if(compare == list[n])
        rep = 1;
    if(rep == 1)
        list[n] = list[n + 1];
    }
    return rep;
}

static int user_vote_one(char *bname, int ind) {
    FILE* cfp,*fcm;
    char buf[STRLEN];
    boardheader_t *fhp;
    int pos = 0, i = 0, count = 0, tickets, fd;
    char inbuf[80], choices[31], vote[4], chosen[31];
    time_t closetime;

    if(ind) {
    sprintf(STR_new_ballots, "%s%d", STR_bv_ballots, ind);
    sprintf(STR_new_control, "%s%d", STR_bv_control, ind);
    sprintf(STR_new_desc, "%s%d", STR_bv_desc, ind);
    sprintf(STR_new_flags, "%s%d", STR_bv_flags, ind);
    sprintf(STR_new_comments, "%s%d", STR_bv_comments, ind);
    sprintf(STR_new_limited, "%s%d", STR_bv_limited, ind);
    } else {
    strcpy(STR_new_ballots, STR_bv_ballots);
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_desc, STR_bv_desc);
    strcpy(STR_new_flags, STR_bv_flags);
    strcpy(STR_new_comments, STR_bv_comments);
    strcpy(STR_new_limited, STR_bv_limited);
    }

    setbfile(buf, bname, STR_new_control);
    cfp = fopen(buf,"r");
    if(!cfp)
        return FULLUPDATE;

    setbfile(buf, bname, STR_new_limited); /* Ptt */
    if(dashf(buf)) {
    setbfile(buf, bname, FN_CANVOTE);
    if(!belong(buf, cuser.userid)) {
        fclose(cfp);
        outs("\n\n對不起! 這是私人投票..你並沒有受邀唷!");
        pressanykey();
        return FULLUPDATE;
    } else {
        outs("\n\n恭喜你受邀此次私人投票....<按任意鍵檢視此次受邀名單>");
        pressanykey();
        more(buf, YEA);
    }
    }
    if(vote_flag(bname, ind, '\0')) {
    outs("\n\n此次投票,你已投過了!");
    pressanykey();
    return FULLUPDATE;
    }
    
    setutmpmode(VOTING);
    setbfile(buf, bname, STR_new_desc);
    more(buf, YEA);

    stand_title("投票箱");
    if((pos = getbnum(bname)) <= 0)
    return 0;

    fhp = bcache + pos - 1;
    fgets(inbuf, sizeof(inbuf), cfp);
    tickets = atoi(inbuf);
    fscanf(cfp,"%lu\n", &closetime);

    prints("投票方式:確定好您的選擇後,輸入其代碼(A, B, C...)即可。\n"
       "此次投票你可以投 %1d 票。"
       "按 0 取消投票 , 1 完成投票\n"
       "此次投票將結束於:%s \n",
       tickets, ctime(&closetime));
    move(5, 0);
    memset(choices, 0, sizeof(choices));
    memset(chosen , 0, sizeof(chosen));

    while(fgets(inbuf, sizeof(inbuf), cfp)) {
    move((count % 15) + 5, (count / 15) * 40);
    prints( " %s", strtok(inbuf, "\n\0"));
    choices[count++] = inbuf[0];
    }
    fclose(cfp);

    while(1) {
    vote[0] = vote[1] = '\0';
    move(t_lines - 2, 0);
    prints("你還可以投 %2d 票", tickets - i);
    getdata(t_lines - 4, 0, "輸入您的選擇: ", vote, sizeof(vote), DOECHO);
    *vote = toupper(*vote);
    if(vote[0] == '0' || (!vote[0] && !i)) {
        outs("記的再來投喔!!");
        break;
    } else if(vote[0] == '1' && i)
        ;
    else if(!vote[0])
        continue;
    else if(index(choices, vote[0]) == NULL) /* 無效 */
        continue;
    else if(same(vote[0], chosen, i)) {
        move(((vote[0] - 'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
        prints(" ");
        i--;
        continue;
    } else {
        if(i == tickets)
        continue;
        chosen[i] = vote[0];
        move(((vote[0]-'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
        prints("*");
        i++;
        continue;
    }
    
    if(vote_flag(bname, ind, vote[0]) != 0)
        prints("重覆投票! 不予計票。");
    else {
        setbfile(buf, bname, STR_new_ballots);
        if((fd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0600)) == 0)
        outs("無法投入票匭\n");
        else {
        struct stat statb;
        char buf[3], mycomments[3][74], b_comments[80];
        
        for(i = 0; i < 3; i++)
            strcpy(mycomments[i], "\n");

        flock(fd, LOCK_EX);
        for(count = 0; count < 31; count++) {
            if(chosen[count])
            write(fd, &chosen[count], 1);
        }
        flock(fd, LOCK_UN);
        fstat(fd, &statb);
        close(fd);
        getdata(b_lines - 2, 0,
            "您對這次投票有什麼寶貴的意見嗎?(y/n)[N]",
            buf, 3 ,DOECHO);
        if(buf[0] == 'Y' || buf[0] == 'y'){
            do {
            move(5,0);clrtobot();
            outs("請問您對這次投票有什麼寶貴的意見?"
                 "最多三行,按[Enter]結束");
            for(i = 0; (i < 3) &&
                getdata(7 + i, 0, ":",
                    mycomments[i], sizeof(mycomments[i]),
                    DOECHO); i++);
            getdata(b_lines-2,0, "(S)儲存 (E)重新來過 "
                "(Q)取消?[S]", buf, 3, LCECHO);
            } while(buf[0] == 'E' || buf[0] == 'e');
            if(buf[0] == 'Q' || buf[0] == 'q')
            break;
            setbfile(b_comments, bname, STR_new_comments);
            if(mycomments[0])
            if((fcm = fopen(b_comments, "a"))){
                fprintf(fcm, 
                             "\033[36m○使用者\033[1;36m %s "
                         "\033[;36m的建議:\033[m\n",
                    cuser.userid);
                for(i = 0; i < 3; i++)
                fprintf(fcm, "    %s\n", mycomments[i]);
                fprintf(fcm, "\n");
                fclose(fcm);
                }
        }
        move(b_lines - 1 ,0);
        prints("已完成投票!\n");
        }
    }
    break;
    }
    pressanykey();
    return FULLUPDATE;
}

static int user_vote(char *bname) {
    int pos;
    boardheader_t *fhp;
    char buf[STRLEN];
    FILE* fp,*xfp;
    int i, x = -1;
    char genbuf[STRLEN];
    char inbuf[80];
    
    if((pos = getbnum(bname)) <= 0)
    return 0;
    
    fhp = bcache + pos - 1;
    
    move(0,0);
    clrtobot();
    
    if(fhp->bvote == 2 || fhp->bvote == 0) {
    outs("\n\n目前並沒有任何投票舉行。");
    pressanykey();
    return FULLUPDATE;
    }
    
    if(!HAS_PERM(PERM_LOGINOK)) {
    outs("\n對不起! 您未滿二十歲, 還沒有投票權喔!");
    pressanykey();
    return FULLUPDATE;
    }
    
    strcpy(STR_new_control, STR_bv_control);
    strcpy(STR_new_title, STR_bv_title);
    setbfile(buf, bname, STR_new_control);
    move(0, 0);
    if((fp = fopen(buf, "r"))) {
    prints("(0) ");
    x = 0;
    fclose(fp);

    setbfile(buf, bname, STR_new_title);
    if((xfp = fopen(buf,"r")))
        fgets(inbuf, sizeof(inbuf), xfp);
    else
        strcpy(inbuf, "無標題");
    prints("%s\n", inbuf);
    fclose(xfp);
    }
    
    for(i = 1; i < 20; i++) {
    sprintf(STR_new_control, "%s%d", STR_bv_control, i);
    sprintf(STR_new_title, "%s%d", STR_bv_title, i);
    setbfile(buf, bname, STR_new_control);
    if((fp = fopen(buf, "r"))) {
        prints("(%d) ", i);
        x = i;
        fclose(fp);
        
        setbfile(buf, bname, STR_new_title);
        if((xfp = fopen(buf, "r")))
        fgets(inbuf, sizeof(inbuf), xfp);
        else
        strcpy(inbuf, "無標題");
        prints("%s\n", inbuf);
        fclose(xfp);
    }
    }
    
    if(x < 0)
    return FULLUPDATE;
    
    sprintf(buf, "要投幾號投票 [%d] ", x);
    
    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);
    
    if(atoi(genbuf) < 0 || atoi(genbuf) > 20)
    sprintf(genbuf,"%d",x);
    
    if(genbuf[0] != '0')
    sprintf(STR_new_control, "%s%d", STR_bv_control, atoi(genbuf));
    else
    strcpy(STR_new_control, STR_bv_control);
    
    setbfile(buf, bname, STR_new_control);

    if((fp = fopen(buf, "r"))){
    fclose(fp);
    
    return user_vote_one(bname, atoi(genbuf));
    } else
    return FULLUPDATE;
}

static int vote_results(char *bname) {
    char buf[STRLEN];

    setbfile(buf, bname, STR_bv_results);
    if(more(buf, YEA) == -1)
    outs("\n目前沒有任何投票的結果。");
    return FULLUPDATE;
}

int b_vote_maintain() {
    return vote_maintain(currboard);
}

int b_vote() {
    return user_vote(currboard);
}

int b_results() {
    return vote_results(currboard);
}