summaryrefslogblamecommitdiffstats
path: root/mbbsd/vote.c
blob: 86e9f41e826b94a53b0d486f98ec7ea7774fad9d (plain) (tree)
1
2
3
4
5
6
7
8
9
          
                
 

                         



                                                                   
                                                                  








                                                                           
                                                                          


                                                                      


                            
                                                          

                             
               
                                 
              
 








                                   
                             
 

                                               
                                                  

                                                          



                                             



                           
                             

     





















                                         
    


                                    
 

                                    
 
                                                




                             
           
                                                            
 
                           
                       
 
                                                    
               

                                                          
                                                                   

                                   
                             
                       






                           
          




                           
 


                                           




                    
 
           





                                                  

               
                    



                                     
                
              

                                                            
                    



                  
 

                       
 
                               
 
                                             

                                                                                            

                                                      
 

                                                                
     
                                                          

                       
                                                            




                                                 

                                       

     

 
           
                                                     




                                          


                                   



                                     


                



                            
 
              






                                                                                           
            






                                                                             




                                          
                          
                            

                                                

                                                                

                

                                                   
                                      
                                     

                                        
                                   

                                          





                                          


                                                
                                                 

               

                                        
                                  
                                         
                                                                               
                    
     
                                                    

                                              
 
                                       
 

                           

                                        

                                         
                                                        
                                  
                                                         

                                                 
                                              
                                                                                                     

                                                                      



                      
                 
 
                                                                    


                                           
 
                                                                            
                
 
                                        
                                       


                                        
                                                                    
                                        
                                       
                                                                      


                                            
 






                                         
           



                              
                             




                                 
                                                                                          
            
                                                                              
 







                                                    
                                        


     
          

                           
 

                                          

                          
              





                     
   

              
                                          













                                                    
     
                                                    




                                                


                                                                          



                                       
                                                       




                         
          
                                      



                                                               

                                                 

                              







                                                                                                  
            






                                                                             


                                          

                                               
 


                                        

                                 
                                        
                                                          
                   
     

                                          
                            

                                             
      
                                                            
                                                   
 
                                                                                  
                              
 

                                        
                                                                  
 
                                          





                                           
 
              
 

                                                 
                                          
                                              
                                          
                                                                     






                           

               
                 


                           

                                                                       
                       
                           









                                              
                                            
                    

                       


                            

                                                                      

                                   
                                  
                                              
                                                                      
                                   
 




                         
          







                                                

                                                                      

                                          
                                 
                     

                   
 
                                            
                                      
                                             

                        
                                                                              
                              
     
                              



                                                          
                                              
                                     




                                                
                                          
                                                 

                            
                                                                                  
                                  


         
              
                          
                                                                      
 

                                                    
 
                                              
                                                  
                         

                                                          
        
                                                                          
 

                                          
                     
                                              
          


                          
          









                                                
                 
                                    

                 
                                                    

                           

                                             
                               
                                                   
                                   
                             
                                        


                                    











                                                 

                                      













                                                                    
 












                                                      
                                                                          
                                       
 
                              
                                                        

                              
                                                                      
                                          
          
                                                                           
                   
            

                                                          

                                              


                          






                                                                                         
            






                                                                             

            
               
                                                  
                                        
                                                                         
                         
                                                                          
                         
               

                             
 
                                               


                                       
                        
                                                 




                                        
 
                 
                                               

                                          
                          
                             
                   
                                                          


                                    
                       


                        
                                                                        

                            
                       
                      

                            
 
                                        

                                          
               
                                             
 
                                             
            
                                          
                      





                                                                            
         
                                                      
                                                     
                                   

                                                         

                  
                                                               
                        



                        
     
                                                                                 


                                                   



                                                           
 
               
                                                                
               










                                                                  

                               
                                             
 


                      
          



                                           
 
              
                                                                                    
        
                                                                    
 

                                        
                                                       
                  
                                  
                                
                         




                                    
                                






                                 
          


                                   
                                      
                        


                                                             


                              





                                                                                           
            





                                                                             


                                          


                          
 

                                                         
                                         
                                         
                        
                                                     

                              
                                                     


                           
                                      
                                                 

                          



                                       
                                                    
                                    


                           

                                          

                                            
      
                                                                   


                                        
                                      
 
                                              
                                       
 



                         
               


                     

                       



















                                                                          

                                 

                                                                                                
                               


                                          
                                                 
                                                     
                  

                                        
                     

















                                                              
                                                                  
                     
                                               

                                                                            
                               


                     
                             
                         
                                                                            
                        
                               


                     

                                                
                                                       

                                                  
                                                                           
                                                         
                  



                                                                          
                                                                        

                                   
                                                            
                                      
                                                         




                                       
                                                           

                                                     
                        

                                   
                                                                 
                                              
                                                                                  

                                                                          
                                                                                                    

                                                             

                                                                  


                                                             
                                                                       
                                                  
                                                   


                                                                        
                         
                 
                                     
                                                           



              

                 



                      
          










                                    
                 
 
                           

               
               

                                             
                                                 

                          
                                  
                                                 

                          

                                                                      

                                          
                                 




                                            
                                      
                                             

                        
                                                                              
                              
     
                              



                                                          
                                              
                                     


                               
 
                                                
                                          
                                                 

                            
                                                                                  
                                  

         

              
                          
 
                                                                      
 
                                                    

                                              
                                                  

                         

                                                          
        
                                                                          
 

                                          
                                                      
                   
 
                                                  



                          
          


                                

                                         
                             
                                                 


                      
   

                 


                                    
   

        


                                
   

           

                                   
/* $Id$ */
#include "bbs.h"

#define MAX_VOTE_PAGE   5

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";

#if 1 // backward compatible

static void
convert_to_newversion(FILE *fp, char *file, char *ballots)
{
    char buf[256], buf2[256];
    short blah;
    int count = -1, tmp, fd, fdw;
    FILE *fpw;

    assert(fp);
    flock(fileno(fp), LOCK_EX);
    rewind(fp);
    fgets(buf, sizeof(buf), fp);
    if (index(buf, ',')) {
    rewind(fp);
    flock(fileno(fp), LOCK_UN);
    return;
    }
    sscanf(buf, " %d", &tmp);

    if ((fd = open(ballots, O_RDONLY)) != -1) {
    sprintf(buf, "%s.new", ballots);
    fdw = open(buf, O_WRONLY | O_CREAT, 0600);
    flock(fd, LOCK_EX);     /* Thor: 防止多人同時算 */
    while (read(fd, &buf2[0], 1) == 1) {
        blah = buf2[0];
        if (blah >= 'A')
        blah -= 'A';
        write(fdw, &blah, sizeof(short));
    }
    flock(fd, LOCK_UN);
    close(fd);
    close(fdw);
    Rename(buf, ballots);
    }

    sprintf(buf2, "%s.new", file);
    if (!(fpw = fopen(buf2, "w"))) {
    rewind(fp);
    flock(fileno(fp), LOCK_UN);
    return;
    }
    fprintf(fpw, "000,000\n");
    while (fgets(buf, sizeof(buf), fp)) {
    fprintf(fpw, "%s", buf);
    count++;
    }
    rewind(fpw);
    fprintf(fpw, "%3d,%3d", count, tmp);
    fclose(fpw);
    flock(fileno(fp), LOCK_UN);
    fclose(fp);
    unlink(file);
    Rename(buf2, file);
    fp = fopen(file, "r");
}
#endif

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[], short item_num, int *total)
{
    short       choice;
    int             fd;

    memset(counts, 0, item_num * sizeof(counts[0]));
    *total = 0;
    if ((fd = open(buf, O_RDONLY)) != -1) {
    flock(fd, LOCK_EX); /* Thor: 防止多人同時算 */
    while (read(fd, &choice, sizeof(short)) == sizeof(short)) {
        if (choice >= item_num)
        continue;
        counts[choice]++;
        (*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;
}

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));
    strlcpy(header.owner, SHM->i18nstr[cuser.language][2378], sizeof(header.owner));
    snprintf(header.title, sizeof(header.title), SHM->i18nstr[cuser.language][2379], bname);
    {
    register struct tm *ptime = localtime(&dtime);

    snprintf(header.date, sizeof(header.date),
         "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
    }
    strlcpy(header.filename, ip, sizeof(header.filename));

    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, int *total)
{
    FILE           *cfp, *tfp, *frp, *xfp;
    char           *bname;
    char            buf[STRLEN];
    char            inbuf[80];
    int            *counts;
    int             people_num;
    short       item_num, 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) {
    snprintf(STR_new_ballots, sizeof(STR_new_ballots), "%s%d", STR_bv_ballots, ind);
    snprintf(STR_new_control, sizeof(STR_new_control),"%s%d", STR_bv_control, ind);
    snprintf(STR_new_desc, sizeof(STR_new_desc), "%s%d", STR_bv_desc, ind);
    snprintf(STR_new_flags, sizeof(STR_new_flags), "%s%d", STR_bv_flags, ind);
    snprintf(STR_new_comments, sizeof(STR_new_comments), "%s%d", STR_bv_comments, ind);
    snprintf(STR_new_limited, sizeof(STR_new_limited), "%s%d", STR_bv_limited, ind);
    snprintf(STR_new_title, sizeof(STR_new_title), "%s%d", STR_bv_title, ind);
    } else {
    strlcpy(STR_new_ballots, STR_bv_ballots, sizeof(STR_new_ballots));
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_desc, STR_bv_desc, sizeof(STR_new_desc));
    strlcpy(STR_new_flags, STR_bv_flags, sizeof(STR_new_flags));
    strlcpy(STR_new_comments, STR_bv_comments, sizeof(STR_new_comments));
    strlcpy(STR_new_limited, STR_bv_limited, sizeof(STR_new_limited));
    strlcpy(STR_new_title, STR_bv_title, sizeof(STR_new_title));
    }

    bname = fh->brdname;

    setbfile(buf, bname, STR_new_control);
    cfp = fopen(buf, "r");
#if 1 // backward compatible
    setbfile(b_control, bname, STR_new_ballots);
    convert_to_newversion(cfp, buf, b_control);
#endif
    fscanf(cfp, "%hd,%hd\n%lu\n", &item_num, &junk, &closetime);
    fclose(cfp);

    counts = (int *)malloc(item_num * sizeof(int));

    setbfile(b_control, bname, "tmp");
    if (rename(buf, b_control) == -1)
    return;
    setbfile(buf, bname, STR_new_flags);
    people_num = b_nonzeroNum(buf);
    unlink(buf);
    setbfile(buf, bname, STR_new_ballots);
#if 0 // backward compatible
    if (!newversion)
    b_count_old(buf, counts, total);
    else
#endif
    b_count(buf, counts, item_num, total);
    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, SHM->i18nstr[cuser.language][2380], msg_seperator, inbuf);
    fclose(xfp);
    }
    fprintf(tfp, SHM->i18nstr[cuser.language][2381],
        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, SHM->i18nstr[cuser.language][2382],
        people_num, junk);
    fprintf(tfp, SHM->i18nstr[cuser.language][2383]);
    for (junk = 0; junk < item_num; junk++) {
        fgets(inbuf, sizeof(inbuf), cfp);
        inbuf[(strlen(inbuf) - 1)] = '\0';
        fprintf(tfp, SHM->i18nstr[cuser.language][2384], inbuf + 3, counts[junk],                
            (float)(counts[junk] * 100) / (float)(people_num),
            (float)(counts[junk] * 100) / (float)(*total));
    }
    fclose(cfp);
    }
    unlink(b_control);
    free(counts);

    fprintf(tfp, SHM->i18nstr[cuser.language][2385], msg_seperator);
    setbfile(buf, bname, STR_new_comments);
    b_suckinfile(tfp, buf);
    unlink(buf);

    fprintf(tfp, SHM->i18nstr[cuser.language][2386], msg_seperator, *total);
    fclose(tfp);

    setbfile(b_report, bname, "report");
    if ((frp = fopen(b_report, "w"))) {
    b_suckinfile(frp, b_newresults);
    fclose(frp);
    }
    snprintf(inbuf, sizeof(inbuf), "boards/%c/%s", bname[0], bname);
    vote_report(bname, b_report, inbuf);
    if (!(fh->brdattr & BRD_NOCOUNT)) {
    snprintf(inbuf, sizeof(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, total;
    char            buf[STRLEN];
    char            temp[STRLEN];

    for (i = 0; i < 20; i++) {
    if (i)
        snprintf(STR_new_control, sizeof(STR_new_control), "%s%d", STR_bv_control, i);
    else
        strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_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, &total);
    }
}

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()
{
    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 vote_index)
{
    boardheader_t  *fhp;
    FILE           *fp;
    char            buf[STRLEN], genbuf[STRLEN], inbuf[STRLEN];
    short       item_num, i;
    int             num = 0, pos, *counts, total;
    time_t          closetime;

    if (vote_index) {
    snprintf(STR_new_ballots, sizeof(STR_new_ballots),"%s%d", STR_bv_ballots, vote_index);
    snprintf(STR_new_control, sizeof(STR_new_control), "%s%d", STR_bv_control, vote_index);
    snprintf(STR_new_desc, sizeof(STR_new_desc), "%s%d", STR_bv_desc, vote_index);
    snprintf(STR_new_flags, sizeof(STR_new_flags), "%s%d", STR_bv_flags, vote_index);
    snprintf(STR_new_comments, sizeof(STR_new_comments), "%s%d", STR_bv_comments, vote_index);
    snprintf(STR_new_limited, sizeof(STR_new_limited), "%s%d", STR_bv_limited, vote_index);
    snprintf(STR_new_title, sizeof(STR_new_title), "%s%d", STR_bv_title, vote_index);
    } else {
    strlcpy(STR_new_ballots, STR_bv_ballots, sizeof(STR_new_ballots));
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_desc, STR_bv_desc, sizeof(STR_new_desc));
    strlcpy(STR_new_flags, STR_bv_flags, sizeof(STR_new_flags));
    strlcpy(STR_new_comments, STR_bv_comments, sizeof(STR_new_comments));
    strlcpy(STR_new_limited, STR_bv_limited, sizeof(STR_new_limited));
    strlcpy(STR_new_title, STR_bv_title, sizeof(STR_new_title));
    }

    setbfile(buf, bname, STR_new_ballots);
    if ((num = dashs(buf)) < 0) /* file size */
    num = 0;

    setbfile(buf, bname, STR_new_title);
    move(0, 0);
    clrtobot();

    if ((fp = fopen(buf, "r"))) {
    fgets(inbuf, sizeof(inbuf), fp);
    prints(SHM->i18nstr[cuser.language][2387], inbuf);
    fclose(fp);
    }
    setbfile(buf, bname, STR_new_control);
    fp = fopen(buf, "r");
#if 1 // backward compatible
    setbfile(genbuf, bname, STR_new_ballots);
    convert_to_newversion(fp, buf, genbuf);
#endif
    fscanf(fp, "%hd,%hd\n%lu\n", &item_num, &i, &closetime);
    counts = (int *)malloc(item_num * sizeof(int));

    prints(SHM->i18nstr[cuser.language][2388], atoi(inbuf), (num / sizeof(short)),
       ctime(&closetime));

    /* Thor: 開放 票數 預知 */
    setbfile(buf, bname, STR_new_flags);
    prints(SHM->i18nstr[cuser.language][2389], b_nonzeroNum(buf));

    setbfile(buf, bname, STR_new_ballots);
#if 0 // backward compatible
    if (!newversion)
    b_count_old(buf, counts, &total);
    else
#endif
    b_count(buf, counts, item_num, &total);

    total = 0;

    for (i = num = 0; i < item_num; i++, num++) {
    fgets(inbuf, sizeof(inbuf), fp);
    inbuf[(strlen(inbuf) - 1)] = '\0';
    inbuf[30] = '\0';   /* truncate */
    move(num % 15 + 6, num / 15 * 40);
    prints(SHM->i18nstr[cuser.language][2390], inbuf, counts[i]);
    total += counts[i];
    if (num == 29) {
        num = -1;
        pressanykey();
        move(6, 0);
        clrtobot();
    }
    }
    fclose(fp);
    free(counts);
    pos = getbnum(bname);
    fhp = bcache + pos - 1;
    move(t_lines - 3, 0);
    prints(SHM->i18nstr[cuser.language][2391], total);
    getdata(b_lines - 1, 0, SHM->i18nstr[cuser.language][2392], 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, vote_index, &total);
    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];

    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_title, STR_bv_title, sizeof(STR_new_title));
    setbfile(buf, bname, STR_new_control);
    move(0, 0);
    if ((fp = fopen(buf, "r"))) {
    outs("(0) ");
    x = 0;
    fclose(fp);

    setbfile(buf, bname, STR_new_title);
    if ((xfp = fopen(buf, "r"))) {
        fgets(inbuf, sizeof(inbuf), xfp);
        fclose(xfp);
    } else
        strlcpy(inbuf, SHM->i18nstr[cuser.language][2393], sizeof(inbuf));
    prints("%s\n", inbuf);
    }
    for (i = 1; i < 20; i++) {
    snprintf(STR_new_control, sizeof(STR_new_control),
         "%s%d", STR_bv_control, i);
    snprintf(STR_new_title, sizeof(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);
        fclose(xfp);
        } else
        strlcpy(inbuf, SHM->i18nstr[cuser.language][2394], sizeof(inbuf));
        prints("%s\n", inbuf);
    }
    }

    if (x < 0)
    return FULLUPDATE;
    snprintf(buf, sizeof(buf), SHM->i18nstr[cuser.language][2395], x);

    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);


    if (atoi(genbuf) < 0 || atoi(genbuf) > 20)
    snprintf(genbuf, sizeof(genbuf), "%d", x);
    if (genbuf[0] != '0')
    snprintf(STR_new_control, sizeof(STR_new_control),
         "%s%d", STR_bv_control, atoi(genbuf));
    else
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));

    setbfile(buf, bname, STR_new_control);

    if (dashf(buf)) {
    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(SHM->i18nstr[cuser.language][2396]);
    fhp = bcache + pos - 1;

    /* CharlieL */
    if (fhp->bvote != 2 && fhp->bvote != 0) {
    getdata(b_lines - 1, 0,
        SHM->i18nstr[cuser.language][2397],
        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++) {
        snprintf(STR_new_ballots, sizeof(STR_new_ballots),
             "%s%d", STR_bv_ballots, i);
        snprintf(STR_new_control, sizeof(STR_new_control),
             "%s%d", STR_bv_control, i);
        snprintf(STR_new_desc, sizeof(STR_new_desc),
             "%s%d", STR_bv_desc, i);
        snprintf(STR_new_flags, sizeof(STR_new_flags),
             "%s%d", STR_bv_flags, i);
        snprintf(STR_new_comments, sizeof(STR_new_comments),
             "%s%d", STR_bv_comments, i);
        snprintf(STR_new_limited, sizeof(STR_new_limited),
             "%s%d", STR_bv_limited, i);
        snprintf(STR_new_title, sizeof(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;
    }
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    setbfile(buf, bname, STR_new_control);
    x = 0;
    while (x < 20 && (fp = fopen(buf, "r")) != NULL) { // TODO try access()
    fclose(fp);
    x++;
    snprintf(STR_new_control, sizeof(STR_new_control),
         "%s%d", STR_bv_control, x);
    setbfile(buf, bname, STR_new_control);
    }
    if (x >= 20)
    return FULLUPDATE;
    if (x) {
    snprintf(STR_new_ballots, sizeof(STR_new_ballots), "%s%d", STR_bv_ballots, x);
    snprintf(STR_new_control, sizeof(STR_new_control), "%s%d", STR_bv_control, x);
    snprintf(STR_new_desc, sizeof(STR_new_desc), "%s%d", STR_bv_desc, x);
    snprintf(STR_new_flags, sizeof(STR_new_flags), "%s%d", STR_bv_flags, x);
    snprintf(STR_new_comments, sizeof(STR_new_comments), "%s%d", STR_bv_comments, x);
    snprintf(STR_new_limited, sizeof(STR_new_limited), "%s%d", STR_bv_limited, x);
    snprintf(STR_new_title, sizeof(STR_new_title), "%s%d", STR_bv_title, x);
    } else {
    strlcpy(STR_new_ballots, STR_bv_ballots, sizeof(STR_new_ballots));
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_desc, STR_bv_desc, sizeof(STR_new_desc));
    strlcpy(STR_new_flags, STR_bv_flags, sizeof(STR_new_flags));
    strlcpy(STR_new_comments, STR_bv_comments, sizeof(STR_new_comments));
    strlcpy(STR_new_limited, STR_bv_limited, sizeof(STR_new_limited));
    strlcpy(STR_new_title, STR_bv_title, sizeof(STR_new_title));
    }
    clear();
    move(0, 0);
    prints(SHM->i18nstr[cuser.language][2398], x);
    setbfile(buf, bname, STR_new_title);
    getdata(4, 0, SHM->i18nstr[cuser.language][2399], inbuf, 50, LCECHO);
    if (inbuf[0] == '\0')
    strlcpy(inbuf, SHM->i18nstr[cuser.language][2400], sizeof(inbuf));
    fp = fopen(buf, "w");
    assert(fp);
    fprintf(fp, "%s", inbuf);
    fclose(fp);

    prints(SHM->i18nstr[cuser.language][2401]);
    pressanykey();
    setbfile(buf, bname, STR_new_desc);
    aborted = vedit(buf, NA, NULL);
    if (aborted == -1) {
    vmsg(SHM->i18nstr[cuser.language][2402]);
    return FULLUPDATE;
    }
    aborted = 0;
    setbfile(buf, bname, STR_new_flags);
    unlink(buf);

    getdata(4, 0,
        SHM->i18nstr[cuser.language][2403],
        inbuf, 2, LCECHO);
    setbfile(buf, bname, STR_new_limited);
    if (inbuf[0] == 'y') {
    fp = fopen(buf, "w");
    assert(fp);
    //fprintf(fp, SHM->i18nstr[cuser.language][2404]);
    fclose(fp);
    friend_edit(FRIEND_CANVOTE);
    } else {
    if (dashf(buf))
        unlink(buf);
    }
    clear();
    getdata(0, 0, SHM->i18nstr[cuser.language][2405], inbuf, 4, DOECHO);

    closetime = atoi(inbuf);
    if (closetime <= 0)
    closetime = 1;
    else if (closetime > 30)
    closetime = 30;

    closetime = closetime * 86400 + now;
    setbfile(buf, bname, STR_new_control);
    fp = fopen(buf, "w");
    assert(fp);
    fprintf(fp, "000,000\n%lu\n", closetime);

    outs(SHM->i18nstr[cuser.language][2406]);
    num = 0;
    x = 0;  /* x is the page number */
    while (!aborted) {
    if( num % 15 == 0 ){
        for( i = num ; i < num + 15 ; ++i ){
        snprintf(buf, sizeof(buf), "\033[1;30m%c)\033[m ", i + 'A');
        move((i % 15) + 2, (i / 15) * 40);
        prints(buf);
        }
    }
    snprintf(buf, sizeof(buf), "%c) ", num + 'A');
    getdata((num % 15) + 2, (num / 15) * 40, buf,
        inbuf, 37, DOECHO);
    if (*inbuf) {
        fprintf(fp, "%1c) %s\n", (num + 'A'), inbuf);
        num++;
    }
    if ((*inbuf == '\0' && num >= 1) || x == MAX_VOTE_PAGE)
        aborted = 1;
    if (num == 30) {
        x++;
        num = 0;
    }
    }
    snprintf(buf, sizeof(buf), SHM->i18nstr[cuser.language][2407], x * 30 + num);

    getdata(t_lines - 3, 0, buf, inbuf, 3, DOECHO);

    if (atoi(inbuf) <= 0 || atoi(inbuf) > (x * 30 + num)) {
    inbuf[0] = '1';
    inbuf[1] = 0;
    }

    rewind(fp);
    fprintf(fp, "%3d,%3d\n", x * 30 + num, 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(SHM->i18nstr[cuser.language][2408]);

    return FULLUPDATE;
}

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

    if (index)
    snprintf(STR_new_flags, sizeof(STR_new_flags), "%s%d", STR_bv_flags, index);
    else
    strlcpy(STR_new_flags, STR_bv_flags, sizeof(STR_new_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
user_vote_one(char *bname, int ind)
{
    FILE           *cfp, *fcm;
    char            buf[STRLEN], redo;
    boardheader_t  *fhp;
    short       pos = 0, i = 0, count, tickets, fd;
    short       curr_page, item_num, max_page;
    char            inbuf[80], choices[31], vote[4], *chosen;
    time_t          closetime;

    if (ind) {
    snprintf(STR_new_ballots, sizeof(STR_new_ballots), "%s%d", STR_bv_ballots, ind);
    snprintf(STR_new_control, sizeof(STR_new_control), "%s%d", STR_bv_control, ind);
    snprintf(STR_new_desc, sizeof(STR_new_desc), "%s%d", STR_bv_desc, ind);
    snprintf(STR_new_flags, sizeof(STR_new_flags),"%s%d", STR_bv_flags, ind);
    snprintf(STR_new_comments, sizeof(STR_new_comments), "%s%d", STR_bv_comments, ind);
    snprintf(STR_new_limited, sizeof(STR_new_limited), "%s%d", STR_bv_limited, ind);
    } else {
    strlcpy(STR_new_ballots, STR_bv_ballots, sizeof(STR_new_ballots));
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_desc, STR_bv_desc, sizeof(STR_new_desc));
    strlcpy(STR_new_flags, STR_bv_flags, sizeof(STR_new_flags));
    strlcpy(STR_new_comments, STR_bv_comments, sizeof(STR_new_comments));
    strlcpy(STR_new_limited, STR_bv_limited, sizeof(STR_new_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);
        vmsg(SHM->i18nstr[cuser.language][2409]);
        return FULLUPDATE;
    } else {
        vmsg(SHM->i18nstr[cuser.language][2410]);
        more(buf, YEA);
    }
    }
    if (vote_flag(bname, ind, '\0')) {
    vmsg(SHM->i18nstr[cuser.language][2411]);
    return FULLUPDATE;
    }
    setutmpmode(VOTING);
    setbfile(buf, bname, STR_new_desc);
    more(buf, YEA);

    stand_title(SHM->i18nstr[cuser.language][2412]);
    if ((pos = getbnum(bname)) <= 0)
    return 0;

    fhp = bcache + pos - 1;
#if 1 // backward compatible
    setbfile(buf, bname, STR_new_control);
    setbfile(inbuf, bname, STR_new_ballots);
    convert_to_newversion(cfp, buf, inbuf);
#endif
    fscanf(cfp, "%hd,%hd\n%lu\n", &item_num, &tickets, &closetime);
    chosen = (char *)malloc(item_num);
    memset(chosen, 0, item_num);
    memset(choices, 0, sizeof(choices));
    max_page = (item_num - 1)/ 30 + 1;

    prints(SHM->i18nstr[cuser.language][2413],
       tickets, ctime(&closetime));

#define REDO_DRAW   1
#define REDO_SCAN   2
    redo = REDO_DRAW;
    curr_page = 0;
    while (1) {

    if (redo) {
        int i, j;
        move(5, 0);
        clrtobot();

        /* 想不到好方法 因為不想整個讀進 memory
         * 而且大部分的投票不會超過一頁 所以再從檔案 scan */
        if (redo & REDO_SCAN) {
        for (i = 0; i < curr_page; i++)
            for (j = 0; j < 30; j++)
            fgets(inbuf, sizeof(inbuf), cfp);
        }

        count = 0;
        for (i = 0; i < 30 && fgets(inbuf, sizeof(inbuf), cfp); i++) {
        move((count % 15) + 5, (count / 15) * 40);
        prints("%c%s", chosen[curr_page * 30 + i] ? '*' : ' ',
            strtok(inbuf, "\n\0"));
        choices[count % 30] = inbuf[0];
        count++;
        }
        redo = 0;
    }

    vote[0] = vote[1] = '\0';
    move(t_lines - 2, 0);
    prints(SHM->i18nstr[cuser.language][2414], tickets - i);
    getdata(t_lines - 4, 0, SHM->i18nstr[cuser.language][2415], vote, sizeof(vote), DOECHO);
    *vote = toupper(*vote);

#define CURRENT_CHOICE \
    chosen[curr_page * 30 + vote[0] - 'A']
    if (vote[0] == '0' || (!vote[0] && !i)) {
        outs(SHM->i18nstr[cuser.language][2416]);
        break;
    } else if (vote[0] == '1' && i);
    else if (!vote[0])
        continue;
    else if (vote[0] == '<' && max_page > 1) {
        curr_page = (curr_page + max_page - 1) % max_page;
            rewind(cfp);
        fgets(inbuf, sizeof(inbuf), cfp);
            fgets(inbuf, sizeof(inbuf), cfp);
        redo = REDO_DRAW | REDO_SCAN;
        continue;
    }
    else if (vote[0] == '>' && max_page > 1) {
        curr_page = (curr_page + 1) % max_page;
        if (curr_page == 0) {
        rewind(cfp);
        fgets(inbuf, sizeof(inbuf), cfp);
        fgets(inbuf, sizeof(inbuf), cfp);
        }
        redo = REDO_DRAW;
        continue;
    }
    else if (index(choices, vote[0]) == NULL)   /* 無效 */
        continue;
    else if ( CURRENT_CHOICE ) { /* 已選 */
        move(((vote[0] - 'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
        prints(" ");
        CURRENT_CHOICE = 0;
        i--;
        continue;
    } else {
        if (i == tickets)
        continue;
        move(((vote[0] - 'A') % 15) + 5, (((vote[0] - 'A')) / 15) * 40);
        prints("*");
        CURRENT_CHOICE = 1;
        i++;
        continue;
    }

    if (vote_flag(bname, ind, vote[0]) != 0)
        prints(SHM->i18nstr[cuser.language][2417]);
    else {
        setbfile(buf, bname, STR_new_ballots);
        if ((fd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0600)) == 0)
        outs(SHM->i18nstr[cuser.language][2418]);
        else {
        struct stat     statb;
        char            buf[3], mycomments[3][74], b_comments[80];

        for (i = 0; i < 3; i++)
            strlcpy(mycomments[i], "\n", sizeof(mycomments[i]));

        flock(fd, LOCK_EX);
        for (count = 0; count < item_num; count++) {
            if (chosen[count])
            write(fd, &count, sizeof(short));
        }
        flock(fd, LOCK_UN);
        fstat(fd, &statb);
        close(fd);
        getdata(b_lines - 2, 0,
            SHM->i18nstr[cuser.language][2419],
            buf, 3, DOECHO);
        if (buf[0] == 'Y' || buf[0] == 'y') {
            do {
            move(5, 0);
            clrtobot();
            outs(SHM->i18nstr[cuser.language][2420]);
            for (i = 0; (i < 3) &&
                 getdata(7 + i, 0, SHM->i18nstr[cuser.language][2421],
                     mycomments[i], sizeof(mycomments[i]),
                     DOECHO); i++);
            getdata(b_lines - 2, 0, SHM->i18nstr[cuser.language][2422], 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,
                    SHM->i18nstr[cuser.language][2423],
                    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(SHM->i18nstr[cuser.language][2424]);
        }
    }
    break;
    }
    free(chosen);
    fclose(cfp);
    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) {
    vmsg(SHM->i18nstr[cuser.language][2425]);
    return FULLUPDATE;
    }
    if (!HAS_PERM(PERM_LOGINOK)) {
    vmsg(SHM->i18nstr[cuser.language][2426]);
    return FULLUPDATE;
    }
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));
    strlcpy(STR_new_title, STR_bv_title, sizeof(STR_new_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);
        fclose(xfp);
    } else
        strlcpy(inbuf, SHM->i18nstr[cuser.language][2427], sizeof(inbuf));
    prints("%s\n", inbuf);
    }
    for (i = 1; i < 20; i++) {
    snprintf(STR_new_control, sizeof(STR_new_control),
         "%s%d", STR_bv_control, i);
    snprintf(STR_new_title, sizeof(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);
        fclose(xfp);
        } else
        strlcpy(inbuf, SHM->i18nstr[cuser.language][2428], sizeof(inbuf));
        prints("%s\n", inbuf);
    }
    }

    if (x < 0)
    return FULLUPDATE;

    snprintf(buf, sizeof(buf), SHM->i18nstr[cuser.language][2429], x);

    getdata(b_lines - 1, 0, buf, genbuf, 4, LCECHO);

    if (atoi(genbuf) < 0 || atoi(genbuf) > 20)
    snprintf(genbuf, sizeof(genbuf), "%d", x);

    if (genbuf[0] != '0')
    snprintf(STR_new_control, sizeof(STR_new_control),
         "%s%d", STR_bv_control, atoi(genbuf));
    else
    strlcpy(STR_new_control, STR_bv_control, sizeof(STR_new_control));

    setbfile(buf, bname, STR_new_control);

    if ((fp = fopen(buf, "r"))) { // TODO try access()
    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)
    vmsg(SHM->i18nstr[cuser.language][2430]);
    return FULLUPDATE;
}

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

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

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