summaryrefslogblamecommitdiffstats
path: root/mbbsd/admin.c
blob: b8dc902d0039aed5625f51fb44b17ae29d834d5d (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
          
                
 
                  
   
                





                                                             
                                                              










                                                                                      
                                                  
                                                     



           
                
   
            
 

                          

                                

                                  
                  
                   

                                             



                                            








                          

                                          


                                       

                                                         


                                                                




                                    
                                                             
 
                     















                                                               
          
                                                 



                             
                                                  
                                                           
                                
 
                
            














                                                                                

                    
                 
     
                                                                          




                             
                               
                       
                                             

                      
 
                        































                                                     
         
 
                      
                       
                                             
                      
 
                                   


                                            


                                                            
                        

                                                             

                                             







                                                                     
                         
                   

                              

                                             
                             
                 
                                         
                                                      
                                    
                                 




                     
 




                           
   
                       
 




                                  
   
                          

                                



                                                       

                       


                                                         





                                                           




                         
                                   
                     
                                   
              



                                       


             
           
                                      
 



                                                             
            
                                                          

                      
 
               
                                        







                                                        
           
                                                                     




                                 
              

                     
         



                                             



                 



                                          


                               













                                                                   
                                



                                                                 

                                  
                   





                                                                           





                                                           
                                




                                                                  




                                                       
                                                           
                                



                                                   
                    
    
                                                                      


                                

                                     

                   



                                                                          
      

 

                                                     
                                        

                                                                
                     
                   


                                      





                                                     
                                                                




















                                                               





                                                


                                                     
                             
         
                                           
                                                                    




                                                                    
                                 







                                                       
     


              
   





                                        
                         
                                                                                
                      

                  
                                        


                                                                     
 

                                
                                                                          
                                    
                                                                               
                                                                                            
                                           

                   
             
                                                   
                                          




                                                    
             
                                                   


                                                         
                           

              
             
                                      

                                            
                                                    

                                        
                                           
                                       
                                               


                
                                                       




                                                              
                                          


               


                             
             
                                                   

                                         
                                            
                                                                    
                        
                                                                     

                                                                            
                                                







                                                                    
                           

                                                        

                                        


                                                           
                                                





                                                                    

                                                                    
                  


                                                               
                                             
                                           
         
              

                                                           





                                                                                                   
                                                
                                                                  
                                                                                      
                                                

                                                              
                           


                                            

              



                                                       



                                               

                                                                          

                                       
                                                   
                                                                      


                      
 

                                                                          
                                    


                      
                                    
 

                             
                                                                   
                                  
                      
                                                                      

                                                                             
                         
                                                        
         
                   
                                      
                                                                    
                                                                                   





                                                            

                                  
                                                 





                                                                   











                                                
 
                                                                                
                                                                                     
                              
                                                                                       
                                 
                                


                                                   
 






                                                                
 
                                                                         
 
                                                                  

                         


                                                    


                                             
 



                                             
                                   
                                                

                                                                    
                           
                                                 





                                                                                          
                                                                   




             
              
   
             


                              
                            
                                          
                





                       
   
            
 

                                             
 
                         


                                                                                             
                                                                        
                          
                          
      
               
                                                                                           
                                                                               
                 
                                                        
      
                                                                            

                     




















                                 
















                               
                                                                                 










                                          






                                    
                                                                                 

                                          
                       

                                          
                       

                                          
                       

                                          
                       
                
                                             
         







                                  
                                             




                             
                                                                                




                                                                                
                                                                
                    



                            
             
                                      



                                           





                                     

                 
             

                                   



                                  

      



                                     
                                        
                                                  


                      
                                                       


                                  
                                            

                                                                       
                       







                                                                                             





                                                                      
   
                                    


                             
                                


                                           
 
                             
                            
                                        

                  
        

                                                       
                      
                                                


                                                       
                                

                  
 
                                   
 
                            
 
                                                           
                  
                                                                        
                                       
 

                                                                       
                                                 

                  
                                  


                                  
 
                                  






                                                                     
                         
     
                                           

                                       
 











                                                   
 
                       
                                                                           
                   
                                                                           






                                                       
 
                                                                    
                                                                                 
                          
                                                                                         
                            


                                                    
 






                                                                  

                                
                                         
                  
                               






                                                                 
                                                  




                             
                         
                                        
































                                                                           
                                












                                           





                                                                    
          






                                           

                                                              


                     
                               
 
              
                                            


                     

                                         


                         
                                             
                   
                                                    
                   
                                                      
                   
                                                  

                   
              

                 


                                      


                     






                                                                 
                   
                                        
               
 

                                                               


                     


                                                               


                       

                                      

                    

                     

                     
                          

               
                    





                     
                          

                                      



































                                                          
                        
                                                              
   
                                                                             

                                
                                       
                        
                                                               
      
                        
                                                   

                                        
                                         
                       
                                                                           
                                                               
                                                                 


                             
      
                                 

                                     
                                     
                                                    
                                       
                                             

                                         
                       
               


                                          
                  
 












                                                                     

                 
                                             
               

                                                      





                                                            
                
                         
                             

                                                        
                                   
 
                           
                           
                                                   

                                        
                                                        

                                                                
                                                            
                  
                                                             
                                                                
     
                                                            
      
                                            
                                                                        
                 
                                                     
                                                          
                                                            
                                                       

                                     
                                                   
                     

                                                                     
                                                            
                                          
                     

                                     



                                                                            

                                     



                        
 
                             
                     

                                                   

                                                                      
                                                     




                                            
 
                                         
                                         
                                                              
                     
                                    


                               
                                                                    
                                                        
                                                           


                                                                 

                                    
 
                                    
                                                                     





                                                                    

                                                        
                                                                              
                                                                 

                                                        
                                                                              
                                                
                                                  

                                                                       




                                                                    

                             
                                       



                                                                 
                         

                                                           
                                                                              
                                                                     







                                                                      
                                             

                               
                     

                                                   




                                                                      
 
                    
                                                  
                                                                     
 
                       

                                                                               
      
 
                                                              


                                                                          
                                                            
                                                  
                                                                      
 
                                            

                                                                                 
 


                                                          

                                                   
                                                                      
                                                             



                                                         

                                                                  



                      
 








                                                                    



                  























                                                             
                                                             
                                                                  
               



































































































































                                                                                       



                                                                   
                                 





                                                  

                               
                         




                                                





                    
   
















































                                                                      
 



                                    

                                      
     





                                                   
     

                            



                 
 
   
                                                     
 

                                 

                    


                                                                       
                               
                           




                                               
                           
 







                                              



                 






                              
                     
     



                                            
 


                                                                                
 


                                       
 



                                    






                                                    

                                                                   

                                                             
                      
                             





                                                   


                         
                                                  
                         
                                                        
                                                         

                                                                          








                                                                       
 










                                                                











                                        
 



                                        
 



                                                                 
                                                    


                              
                                                    
                          
 




                                                    




                                   
 




                                                                                       
                     


                                            
                     
                          
 
                                    
                                         





                                                           








                                                        
 
                                       




                                                           




                                                        
 
                                         




                                                           



                                                                                  
 




                                                        
 


                                        
 



                                            
 
                                                        
                      



                                        
 
                                
                                       

                                            
                                                



















                                                                                 

         

                                   
         




                                                                     
 




                                                                                       
 



                                                                            

                              







                                        
                                           

                                                                                       
 



















                                                                  
 
                          
 










                                            




                           
   
                






                                                 


                                   


                                      


                                                 


                                     
                          
                          
                                   





                             



                                                                   
                                                         

                                      
                      
                                                 
                           
                                                 



                           
                               



                                                                                    
                                             
     









                                                                    
      

      
 


             
   
                  

                                                              
                                        
                                       
        
                                                   


             
           
                                                                     


                                        
                                                                                          



                                                          
            
                                                                                
                                                                
     

 
   
                


                                  
                                                                   
                                          
                                           
 
                                                                              
                      
                 
                             


                                                      
                         
                       
                               


                     
                                                     

                     
 
            
 

                                                 
                 
 








                                                                              
 
                                                      
                                          
               
 
                         

                                                   
                 
     
 
                             



                                                        
                         
                                
                                         

                                                   
         
                                                                   
                            
            
                                                       


                        
                                             
                    




                                          
                             
                                         


                                                   

                   
                                                                   

                            
     
 
                
 
                                                        
                                                                         


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

/* 進站水球宣傳 */
int
m_loginmsg(void)
{
  char msg[100];
  move(21,0);
  clrtobot();
  if(SHM->loginmsg.pid && SHM->loginmsg.pid != currutmp->pid)
    {
      outs("目前已經有以下的 進站水球設定請先協調好再設定..");
      getmessage(SHM->loginmsg);
    }
  getdata(22, 0, 
     "進站水球:本站活動,不干擾使用者為限,設定者離站自動取消,確定要設?(y/N)",
          msg, 3, LCECHO);

  if(msg[0]=='y' &&

     getdata_str(23, 0, "設定進站水球:", msg, 56, DOECHO, SHM->loginmsg.last_call_in))
    {
          SHM->loginmsg.pid=currutmp->pid; /*站長不多 就不管race condition */
          strcpy(SHM->loginmsg.last_call_in, msg);
          strcpy(SHM->loginmsg.userid, cuser.userid);
    }
  return 0;
}

/* 使用者管理 */
int
m_user(void)
{
    userec_t        xuser;
    int             id;
    char            genbuf[200];

    stand_title("使用者設定");
    usercomplete(msg_uid, genbuf);
    if (*genbuf) {
    move(2, 0);
    if ((id = getuser(genbuf, &xuser))) {
        user_display(&xuser, 1);
        if( HasUserPerm(PERM_ACCOUNTS) )
        uinfo_query(&xuser, 1, id);
        else
        pressanykey();
    } else {
        outs(err_uid);
        clrtoeol();
        pressanykey();
    }
    }
    return 0;
}

static int retrieve_backup(userec_t *user)
{
    int     uid;
    char    src[PATHLEN], dst[PATHLEN];
    char    ans;

    if ((uid = searchuser(user->userid, user->userid))) {
    userec_t orig;
    passwd_query(uid, &orig);
    strlcpy(user->passwd, orig.passwd, sizeof(orig.passwd));
    setumoney(uid, user->money);
    passwd_update(uid, user);
    return 0;
    }

    ans = getans("目前的 PASSWD 檔沒有此 ID,新增嗎?[y/N]");

    if (ans != 'y') {
    vmsg("目前的 PASSWDS 檔沒有此 ID,請先新增此帳號");
    return -1;
    }

    if (setupnewuser((const userec_t *)user) >= 0) {
    sethomepath(dst, user->userid);
    if (!dashd(dst)) {
        snprintf(src, sizeof(src), "tmp/%s", user->userid);
        if (!dashd(src) || !Rename(src, dst))
        mkuserdir(user->userid);
    }
    return 0;
    }
    return -1;
}

static int
search_key_user(const char *passwdfile, int mode)
{
    userec_t        user;
    int             ch;
    int             coun = 0;
    FILE            *fp1 = fopen(passwdfile, "r");
    char            friendfile[128]="", key[22], *keymatch;
    int         keytype = 0;

    assert(fp1);
    clear();
    if (!mode)
    {
    getdata(0, 0, "請輸入id :", key, sizeof(key), DOECHO);
    } else {
    // improved search
    getdata(0, 0, "搜尋哪種欄位?"
        "([0]全部 1.ID 2.姓名 3.暱稱 4.地址 5.email 6.IP 7.認證/電話) ",
        key, 2, DOECHO);
    if (isascii(key[0]) && isdigit(key[0]))
        keytype = key[0] - '0';
    if (keytype < 0 || keytype > 7)
        keytype = 0;
    getdata(0, 0, "請輸入關鍵字: ", key, sizeof(key), DOECHO);
    }

    if(!key[0]) {
    fclose(fp1);
    return 0;
    }
    while ((fread(&user, sizeof(user), 1, fp1)) > 0 && coun < MAX_USERS) {

    // skip empty records
    if (!user.userid[0])
        continue;

    if (!(++coun & 0xFF)) {
        move(1, 0);
        prints("第 [%d] 筆資料\n", coun);
        refresh();
    }

        keymatch = NULL;

    if (!mode)
    {
        // only verify id
        if (!strcasecmp(user.userid, key))
        keymatch = user.userid; 
    } else {
        // search by keytype
        if ((!keytype || keytype == 1) &&
        strcasestr(user.userid, key))
        keymatch = user.userid;
        else if ((!keytype || keytype == 2) &&
        strcasestr(user.realname, key))
        keymatch = user.realname;
        else if ((!keytype || keytype == 3) &&
        strcasestr(user.nickname, key))
        keymatch = user.nickname;
        else if ((!keytype || keytype == 4) &&
        strcasestr(user.address, key))
        keymatch = user.address;
        else if ((!keytype || keytype == 5) &&
        strcasestr(user.email, key))
        keymatch = user.email;
        else if ((!keytype || keytype == 6) &&
        strcasestr(user.lasthost, key))
        keymatch = user.lasthost;
        else if ((!keytype || keytype == 7) &&
        strcasestr(user.justify, key))
        keymatch = user.justify;
        else if ((!keytype) &&
        strcasestr(user.mychicken.name, key))
        keymatch = user.mychicken.name; 
    }

        if(keymatch) {
        move(1, 0);
        prints("第 [%d] 筆資料\n", coun);
        refresh();

        user_display(&user, 1);
        if (HasUserPerm(PERM_ACCOUNTS))
        uinfo_query(&user, 1, coun);

        outs(ANSI_COLOR(44) "               空白鍵" \
         ANSI_COLOR(37) ":搜尋下一個          " \
         ANSI_COLOR(33)" Q" ANSI_COLOR(37)": 離開");
        outs(mode ? 
                 "      A: add to namelist " ANSI_RESET " " :
         "      S: 取用備份資料    " ANSI_RESET " ");
        while (1) {
        while ((ch = igetch()) == 0);
                if (ch == 'a' || ch=='A' )
                  {
                   if(!friendfile[0])
                    {
                     friend_special();
                     setfriendfile(friendfile, FRIEND_SPECIAL);
                    }
                   friend_add(user.userid, FRIEND_SPECIAL, keymatch);
                   break;
                  }
        if (ch == ' ')
            break;
        if (ch == 'q' || ch == 'Q') {
            fclose(fp1);
            return 0;
        }
        if (ch == 's' && !mode) {
            if (retrieve_backup(&user) >= 0) {
            fclose(fp1);
            return 0;
            }
        }
        }
    }
    }

    fclose(fp1);
    return 0;
}

/* 以任意 key 尋找使用者 */
int
search_user_bypwd(void)
{
    search_key_user(FN_PASSWD, 1);
    return 0;
}

/* 尋找備份的使用者資料 */
int
search_user_bybakpwd(void)
{
    char           *choice[] = {
    "PASSWDS.NEW1", "PASSWDS.NEW2", "PASSWDS.NEW3",
    "PASSWDS.NEW4", "PASSWDS.NEW5", "PASSWDS.NEW6",
    "PASSWDS.BAK"
    };
    int             ch;

    clear();
    move(1, 1);
    outs("請輸入你要用來尋找備份的檔案 或按 'q' 離開\n");
    outs(" [" ANSI_COLOR(1;31) "1" ANSI_RESET "]一天前,"
     " [" ANSI_COLOR(1;31) "2" ANSI_RESET "]兩天前," 
     " [" ANSI_COLOR(1;31) "3" ANSI_RESET "]三天前\n");
    outs(" [" ANSI_COLOR(1;31) "4" ANSI_RESET "]四天前,"
     " [" ANSI_COLOR(1;31) "5" ANSI_RESET "]五天前,"
     " [" ANSI_COLOR(1;31) "6" ANSI_RESET "]六天前\n");
    outs(" [7]備份的\n");
    do {
    move(5, 1);
    outs("選擇 => ");
    ch = igetch();
    if (ch == 'q' || ch == 'Q')
        return 0;
    } while (ch < '1' || ch > '7');
    ch -= '1';
    if( access(choice[ch], R_OK) != 0 )
    vmsg("檔案不存在");
    else
    search_key_user(choice[ch], 0);
    return 0;
}

static void
bperm_msg(const boardheader_t * board)
{
    prints("\n設定 [%s] 看板之(%s)權限:", board->brdname,
       board->brdattr & BRD_POSTMASK ? "發表" : "閱\讀");
}

unsigned int
setperms(unsigned int pbits, const char * const pstring[])
{
    register int    i;

    move(4, 0);
    for (i = 0; i < NUMPERMS / 2; i++) {
    prints("%c. %-20s %-15s %c. %-20s %s\n",
           'A' + i, pstring[i],
           ((pbits >> i) & 1 ? "ˇ" : "X"),
           i < 10 ? 'Q' + i : '0' + i - 10,
           pstring[i + 16],
           ((pbits >> (i + 16)) & 1 ? "ˇ" : "X"));
    }
    clrtobot();
    while (
       (i = getkey("請按 [A-5] 切換設定,按 [Return] 結束:"))!='\r')
    {
    if (isdigit(i))
        i = i - '0' + 26;
    else if (isalpha(i))
        i = tolower(i) - 'a';
    else {
        bell();
        continue;
    }

    pbits ^= (1 << i);
    move(i % 16 + 4, i <= 15 ? 24 : 64);
    outs((pbits >> i) & 1 ? "ˇ" : "X");
    }
    return pbits;
}

#ifdef CHESSCOUNTRY
static void
AddingChessCountryFiles(const char* apath)
{
    char filename[PATHLEN];
    char symbolicname[PATHLEN];
    char adir[PATHLEN];
    FILE* fp;
    fileheader_t fh;

    setadir(adir, apath);

    /* creating chess country regalia */
    snprintf(filename, sizeof(filename), "%s/chess_ensign", apath);
    close(open(filename, O_CREAT | O_WRONLY, 0644));

    strlcpy(symbolicname, apath, sizeof(symbolicname));
    stampfile(symbolicname, &fh);
    symlink("chess_ensign", symbolicname);

    strcpy(fh.title, "◇ 棋國國徽 (不能刪除,系統需要)");
    strcpy(fh.owner, str_sysop);
    append_record(adir, &fh, sizeof(fileheader_t));

    /* creating member list */
    snprintf(filename, sizeof(filename), "%s/chess_list", apath);
    if (!dashf(filename)) {
    fp = fopen(filename, "w");
    assert(fp);
    fputs("棋國國名\n"
        "帳號            階級    加入日期        等級或被誰俘虜\n"
        "──────    ───  ─────      ───────\n",
        fp);
    fclose(fp);
    }

    strlcpy(symbolicname, apath, sizeof(symbolicname));
    stampfile(symbolicname, &fh);
    symlink("chess_list", symbolicname);

    strcpy(fh.title, "◇ 棋國成員表 (不能刪除,系統需要)");
    strcpy(fh.owner, str_sysop);
    append_record(adir, &fh, sizeof(fileheader_t));

    /* creating profession photos' dir */
    snprintf(filename, sizeof(filename), "%s/chess_photo", apath);
    mkdir(filename, 0755);

    strlcpy(symbolicname, apath, sizeof(symbolicname));
    stampfile(symbolicname, &fh);
    symlink("chess_photo", symbolicname);

    strcpy(fh.title, "◆ 棋國照片檔 (不能刪除,系統需要)");
    strcpy(fh.owner, str_sysop);
    append_record(adir, &fh, sizeof(fileheader_t));
}
#endif /* defined(CHESSCOUNTRY) */

/* 自動設立精華區 */
void
setup_man(const boardheader_t * board, const boardheader_t * oldboard)
{
    char            genbuf[200];

    setapath(genbuf, board->brdname);
    mkdir(genbuf, 0755);

#ifdef CHESSCOUNTRY
    if (oldboard == NULL || oldboard->chesscountry != board->chesscountry)
    if (board->chesscountry != CHESSCODE_NONE)
        AddingChessCountryFiles(genbuf);
    // else // doesn't remove files..
#endif
}

void delete_symbolic_link(boardheader_t *bh, int bid)
{
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    memset(bh, 0, sizeof(boardheader_t));
    substitute_record(fn_board, bh, sizeof(boardheader_t), bid);
    reset_board(bid);
    sort_bcache(); 
    log_usies("DelLink", bh->brdname);
}

int dir_cmp(const void *a, const void *b)
{
  return (atoi( &((fileheader_t *)a)->filename[2] ) -
          atoi( &((fileheader_t *)b)->filename[2] ));
}

void merge_dir(const char *dir1, const char *dir2, int isoutter)
{
     int i, pn, sn;
     fileheader_t *fh;
     char *p1, *p2, bakdir[128], file1[128], file2[128];
     strcpy(file1,dir1);
     strcpy(file2,dir2);
     if((p1=strrchr(file1,'/')))
     p1 ++;
     else
     p1 = file1;
     if((p2=strrchr(file2,'/')))
     p2 ++;
     else
     p2 = file2;

     pn=get_num_records(dir1, sizeof(fileheader_t));
     sn=get_num_records(dir2, sizeof(fileheader_t));
     if(!sn) return;
     fh= (fileheader_t *)malloc( (pn+sn)*sizeof(fileheader_t));
     get_records(dir1, fh, sizeof(fileheader_t), 1, pn);
     get_records(dir2, fh+pn, sizeof(fileheader_t), 1, sn);
     if(isoutter)
         {
             for(i=0; i<sn; i++)
               if(fh[pn+i].owner[0])
                   strcat(fh[pn+i].owner, "."); 
         }
     qsort(fh, pn+sn, sizeof(fileheader_t), dir_cmp);
     sprintf(bakdir,"%s.bak", dir1);
     Rename(dir1, bakdir);
     for(i=1; i<=pn+sn; i++ )
        {
         if(!fh[i-1].filename[0]) continue;
         if(i == pn+sn ||  strcmp(fh[i-1].filename, fh[i].filename))
     {
                fh[i-1].recommend =0;
        fh[i-1].filemode |= 1;
                append_record(dir1, &fh[i-1], sizeof(fileheader_t));
        strcpy(p1, fh[i-1].filename);
                if(!dashf(file1))
              {
              strcpy(p2, fh[i-1].filename);
              Copy(file2, file1);
              } 
     }
         else
                fh[i].filemode |= fh[i-1].filemode;
        }
     
     free(fh);
}

int
m_mod_board(char *bname)
{
    boardheader_t   bh, newbh;
    int             bid;
    char            genbuf[256], ans[4];

    bid = getbnum(bname);
    if (!bid || !bname[0] || get_record(fn_board, &bh, sizeof(bh), bid) == -1) {
    vmsg(err_bid);
    return -1;
    }
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    prints("看板名稱:%s\n看板說明:%s\n看板bid:%d\n看板GID:%d\n"
       "板主名單:%s", bh.brdname, bh.title, bid, bh.gid, bh.BM);
    bperm_msg(&bh);

    /* Ptt 這邊斷行會檔到下面 */
    move(9, 0);
    snprintf(genbuf, sizeof(genbuf), "(E)設定 (V)違法/解除%s%s [Q]取消?",
        HasUserPerm(PERM_SYSOP |
             PERM_BOARD) ? " (B)Vote (S)救回 (C)合併 (G)賭盤解卡" : "",
        HasUserPerm(PERM_SYSSUBOP | PERM_SYSSUPERSUBOP | PERM_BOARD) ? " (D)刪除" : "");
    getdata(10, 0, genbuf, ans, 3, LCECHO);

    switch (*ans) {
    case 'g':
    if (HasUserPerm(PERM_SYSOP | PERM_BOARD)) {
        char            path[PATHLEN];
        setbfile(genbuf, bname, FN_TICKET_LOCK);
        setbfile(path, bname, FN_TICKET_END);
        rename(genbuf, path);
    }
    break;
    case 's':
    if (HasUserPerm(PERM_SYSOP | PERM_BOARD)) {
      snprintf(genbuf, sizeof(genbuf),
           BBSHOME "/bin/buildir boards/%c/%s &",
           bh.brdname[0], bh.brdname);
        system(genbuf);
    }
    break;
    case 'c':
    if (HasUserPerm(PERM_SYSOP)) {
       char frombname[20], fromdir[256];
#ifdef MERGEBBS
       if(getans("是否匯入SOB看板? (y/N)")=='y')
       { 
                 setbdir(genbuf, bname);
             m_sob_brd(bname, fromdir);
         if(!fromdir[0]) break;
                 merge_dir(genbuf, fromdir, 1);
           }
       else{
#endif
        CompleteBoard(MSG_SELECT_BOARD, frombname);
            if (frombname[0] == '\0' || !getbnum(frombname) ||
        !strcmp(frombname,bname))
                         break;
            setbdir(genbuf, bname);
            setbdir(fromdir, frombname);
            merge_dir(genbuf, fromdir, 0);
#ifdef MERGEBBS
       }
#endif
        touchbtotal(bid);
    }
    break;
    case 'b':
    if (HasUserPerm(PERM_SYSOP | PERM_BOARD)) {
        char            bvotebuf[10];

        memcpy(&newbh, &bh, sizeof(bh));
        snprintf(bvotebuf, sizeof(bvotebuf), "%d", newbh.bvote);
        move(20, 0);
        prints("看板 %s 原來的 BVote:%d", bh.brdname, bh.bvote);
        getdata_str(21, 0, "新的 Bvote:", genbuf, 5, LCECHO, bvotebuf);
        newbh.bvote = atoi(genbuf);
        assert(0<=bid-1 && bid-1<MAX_BOARD);
        substitute_record(fn_board, &newbh, sizeof(newbh), bid);
        reset_board(bid);
        log_usies("SetBoardBvote", newbh.brdname);
        break;
    } else
        break;
    case 'v':
    memcpy(&newbh, &bh, sizeof(bh));
    outs("看板目前為");
    outs((bh.brdattr & BRD_BAD) ? "違法" : "正常");
    getdata(21, 0, "確定更改?", genbuf, 5, LCECHO);
    if (genbuf[0] == 'y') {
        if (newbh.brdattr & BRD_BAD)
        newbh.brdattr = newbh.brdattr & (!BRD_BAD);
        else
        newbh.brdattr = newbh.brdattr | BRD_BAD;
        assert(0<=bid-1 && bid-1<MAX_BOARD);
        substitute_record(fn_board, &newbh, sizeof(newbh), bid);
        reset_board(bid);
        log_usies("ViolateLawSet", newbh.brdname);
    }
    break;
    case 'd':
    if (!(HasUserPerm(PERM_SYSOP | PERM_BOARD) ||
            (HasUserPerm(PERM_SYSSUPERSUBOP) && GROUPOP())))
        break;
    getdata_str(9, 0, msg_sure_ny, genbuf, 3, LCECHO, "N");
    if (genbuf[0] != 'y' || !bname[0])
        outs(MSG_DEL_CANCEL);
    else if (bh.brdattr & BRD_SYMBOLIC) {
        delete_symbolic_link(&bh, bid);
    }
    else {
        strlcpy(bname, bh.brdname, sizeof(bh.brdname));
        snprintf(genbuf, sizeof(genbuf),
            "/bin/tar zcvf tmp/board_%s.tgz boards/%c/%s man/boards/%c/%s >/dev/null 2>&1;"
            "/bin/rm -fr boards/%c/%s man/boards/%c/%s",
            bname, bname[0], bname, bname[0],
            bname, bname[0], bname, bname[0], bname);
        system(genbuf);
        memset(&bh, 0, sizeof(bh));
        snprintf(bh.title, sizeof(bh.title),
             "     %s 看板 %s 刪除", bname, cuser.userid);
        post_msg(GLOBAL_SECURITY, bh.title, "請注意刪除的合法性", "[系統安全局]");
        assert(0<=bid-1 && bid-1<MAX_BOARD);
        substitute_record(fn_board, &bh, sizeof(bh), bid);
        reset_board(bid);
            sort_bcache(); 
        log_usies("DelBoard", bh.title);
        outs("刪板完畢");
    }
    break;
    case 'e':
    if( bh.brdattr & BRD_SYMBOLIC ){
        vmsg("禁止更動連結看板,請直接修正原看板");
        break;
    }
    move(8, 0);
    outs("直接按 [Return] 不修改該項設定");
    memcpy(&newbh, &bh, sizeof(bh));

    while (getdata(9, 0, "新看板名稱:", genbuf, IDLEN + 1, DOECHO)) {
        if (getbnum(genbuf)) {
        move(3, 0);
        outs("錯誤! 板名雷同");
        } else if ( !invalid_brdname(genbuf) ){
        strlcpy(newbh.brdname, genbuf, sizeof(newbh.brdname));
        break;
        }
    }

    do {
        getdata_str(12, 0, "看板類別:", genbuf, 5, DOECHO, bh.title);
        if (strlen(genbuf) == 4)
        break;
    } while (1);

    strcpy(newbh.title, genbuf);

    newbh.title[4] = ' ';

    getdata_str(14, 0, "看板主題:", genbuf, BTLEN + 1, DOECHO,
            bh.title + 7);
    if (genbuf[0])
        strlcpy(newbh.title + 7, genbuf, sizeof(newbh.title) - 7);
    if (getdata_str(15, 0, "新板主名單:", genbuf, IDLEN * 3 + 3, DOECHO,
            bh.BM)) {
        trim(genbuf);
        strlcpy(newbh.BM, genbuf, sizeof(newbh.BM));
    }
#ifdef CHESSCOUNTRY
    if (HasUserPerm(PERM_SYSOP)) {
        snprintf(genbuf, sizeof(genbuf), "%d", bh.chesscountry);
        if (getdata_str(16, 0, "設定棋國 (0)無 (1)五子棋 (2)象棋 (3)圍棋", ans,
            sizeof(ans), LCECHO, genbuf)){
        newbh.chesscountry = atoi(ans);
        if (newbh.chesscountry > CHESSCODE_MAX ||
            newbh.chesscountry < CHESSCODE_NONE)
            newbh.chesscountry = bh.chesscountry;
        }
    }
#endif /* defined(CHESSCOUNTRY) */
    if (HasUserPerm(PERM_SYSOP|PERM_BOARD)) {
        move(1, 0);
        clrtobot();
        newbh.brdattr = setperms(newbh.brdattr, str_permboard);
        move(1, 0);
        clrtobot();
    }
    {
        const char* brd_symbol;
        if (newbh.brdattr & BRD_GROUPBOARD)
            brd_symbol = "Σ";
        else if (newbh.brdattr & BRD_NOTRAN)
        brd_symbol = "◎";
        else
        brd_symbol = "●";

        newbh.title[5] = brd_symbol[0];
        newbh.title[6] = brd_symbol[1];
    }

    if (HasUserPerm(PERM_SYSOP|PERM_BOARD) && !(newbh.brdattr & BRD_HIDE)) {
        getdata_str(14, 0, "設定讀寫權限(Y/N)?", ans, sizeof(ans), LCECHO, "N");
        if (*ans == 'y') {
        getdata_str(15, 0, "限制 [R]閱\讀 (P)發表?", ans, sizeof(ans), LCECHO,
                "R");
        if (*ans == 'p')
            newbh.brdattr |= BRD_POSTMASK;
        else
            newbh.brdattr &= ~BRD_POSTMASK;

        move(1, 0);
        clrtobot();
        bperm_msg(&newbh);
        newbh.level = setperms(newbh.level, str_permid);
        clear();
        }
    }

    getdata(b_lines - 1, 0, "請您確定(Y/N)?[Y]", genbuf, 4, LCECHO);

    if ((*genbuf != 'n') && memcmp(&newbh, &bh, sizeof(bh))) {
        char buf[64];

        if (strcmp(bh.brdname, newbh.brdname)) {
        char            src[60], tar[60];

        setbpath(src, bh.brdname);
        setbpath(tar, newbh.brdname);
        Rename(src, tar);

        setapath(src, bh.brdname);
        setapath(tar, newbh.brdname);
        Rename(src, tar);
        }
        setup_man(&newbh, &bh);
        assert(0<=bid-1 && bid-1<MAX_BOARD);
        substitute_record(fn_board, &newbh, sizeof(newbh), bid);
        reset_board(bid);
            sort_bcache(); 
        log_usies("SetBoard", newbh.brdname);

        snprintf(buf, sizeof(buf), "[看板變更] %s (by %s)", bh.brdname, cuser.userid);
        snprintf(genbuf, sizeof(genbuf),
            "板名: %s => %s\n"
            "板主: %s => %s\n",
            bh.brdname, newbh.brdname, bh.BM, newbh.BM);
        post_msg(GLOBAL_SECURITY, buf, genbuf, "[系統安全局]");
    }
    }
    return 0;
}

/* 設定看板 */
int
m_board(void)
{
    char            bname[32];

    stand_title("看板設定");
    CompleteBoardAndGroup(msg_bid, bname);
    if (!*bname)
    return 0;
    m_mod_board(bname);
    return 0;
}

/* 設定系統檔案 */
int
x_file(void)
{
    int             aborted, num;
    char            ans[4], *fpath, buf[256];

    move(b_lines - 7, 0);
    /* Ptt */
    outs("設定 (1)身份確認信 (4)post注意事項 (5)錯誤登入訊息 (6)註冊範例 (7)通過確認通知\n");
    outs("     (8)email post通知 (9)系統功\能精靈 (A)茶樓 (B)站長名單 (C)email通過確認\n");
    outs("     (D)新使用者需知 (E)身份確認方法 (F)歡迎畫面 (G)進站畫面 "
#ifdef MULTI_WELCOME_LOGIN
     "(X)刪除進站畫面"
#endif
     "\n");
    outs("     (H)看板期限 (I)故鄉 (J)出站畫面 (K)生日卡 (L)節日 (M)外籍使用者認證通知\n");
    outs("     (N)外籍使用者過期警告通知 (O)看板列表 help (P)文章列表 help\n");
#ifdef PLAY_ANGEL
    outs("     (R)小天使認證通知 (S)小天使功\能說明\n");
#endif
    getdata(b_lines - 1, 0, "[Q]取消[1-9 A-P]?", ans, sizeof(ans), LCECHO);

    switch (ans[0]) {
    case '1':
    fpath = "etc/confirm";
    break;
    case '4':
    fpath = "etc/post.note";
    break;
    case '5':
    fpath = "etc/goodbye";
    break;
    case '6':
    fpath = "etc/register";
    break;
    case '7':
    fpath = "etc/registered";
    break;
    case '8':
    fpath = "etc/emailpost";
    break;
    case '9':
    fpath = "etc/hint";
    break;
    case 'b':
    fpath = "etc/sysop";
    break;
    case 'c':
    fpath = "etc/bademail";
    break;
    case 'd':
    fpath = "etc/newuser";
    break;
    case 'e':
    fpath = "etc/justify";
    break;
    case 'f':
    fpath = "etc/Welcome";
    break;
    case 'g':
#ifdef MULTI_WELCOME_LOGIN
    getdata(b_lines - 1, 0, "第幾個進站畫面[0-4]", ans, sizeof(ans), LCECHO);
    if (ans[0] == '1') {
        fpath = "etc/Welcome_login.1";
    } else if (ans[0] == '2') {
        fpath = "etc/Welcome_login.2";
    } else if (ans[0] == '3') {
        fpath = "etc/Welcome_login.3";
    } else if (ans[0] == '4') {
        fpath = "etc/Welcome_login.4";
    } else {
        fpath = "etc/Welcome_login.0";
    }
#else
    fpath = "etc/Welcome_login";
#endif
    break;

#ifdef MULTI_WELCOME_LOGIN
    case 'x':
    getdata(b_lines - 1, 0, "第幾個進站畫面[1-4]", ans, sizeof(ans), LCECHO);
    if (ans[0] == '1') {
        unlink("etc/Welcome_login.1");
        vmsg("ok");
    } else if (ans[0] == '2') {
        unlink("etc/Welcome_login.2");
        vmsg("ok");
    } else if (ans[0] == '3') {
        unlink("etc/Welcome_login.3");
        vmsg("ok");
    } else if (ans[0] == '4') {
        unlink("etc/Welcome_login.4");
        vmsg("ok");
    } else {
        vmsg("所指定的進站畫面無法刪除");
    }
    return FULLUPDATE;

#endif

    case 'h':
    fpath = "etc/expire.conf";
    break;
    case 'i':
    fpath = "etc/domain_name_query.cidr";
    break;
    case 'j':
    fpath = "etc/Logout";
    break;
    case 'k':
    mouts(b_lines - 3, 0, "1.摩羯  2.水瓶  3.雙魚  4.牡羊  5.金牛  6.雙子");
    mouts(b_lines - 2, 0, "7.巨蟹  8.獅子  9.處女 10.天秤 11.天蠍 12.射手");
    getdata(b_lines - 1, 0, "請選擇 [1-12]", ans, sizeof(ans), LCECHO);
    num = atoi(ans);
    if (num <= 0 || num > 12)
        return FULLUPDATE;
    snprintf(buf, sizeof(buf), "etc/Welcome_birth.%d", num);
    fpath = buf;
    break;
    case 'l':
    fpath = "etc/feast";
    break;
    case 'm':
    fpath = "etc/foreign_welcome";
    break;
    case 'n':
    fpath = "etc/foreign_expired_warn";
    break;
    case 'o':
    fpath = "etc/boardlist.help";
    break;
    case 'p':
    fpath = "etc/board.help";
    break;

#ifdef PLAY_ANGEL
    case 'r':
    fpath = "etc/angel_notify";
    break;

    case 's':
    fpath = "etc/angel_usage";
    break;
#endif

    default:
    return FULLUPDATE;
    }
    aborted = vedit(fpath, NA, NULL);
    vmsgf("\n\n系統檔案[%s]: %s", fpath,
     (aborted == -1) ? "未改變" : "更新完畢");
    return FULLUPDATE;
}

static int add_board_record(const boardheader_t *board)
{
    int bid;
    if ((bid = getbnum("")) > 0) {
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    substitute_record(fn_board, board, sizeof(boardheader_t), bid);
    reset_board(bid);
        sort_bcache(); 
    } else if (append_record(fn_board, (fileheader_t *)board, sizeof(boardheader_t)) == -1) {
    return -1;
    } else {
    addbrd_touchcache();
    }
    return 0;
}

/**
 * open a new board
 * @param whatclass In which sub class
 * @param recover   Forcely open a new board, often used for recovery.
 * @return -1 if failed
 */
int
m_newbrd(int whatclass, int recover)
{
    boardheader_t   newboard;
    char            ans[4];
    char            genbuf[200];

    stand_title("建立新板");
    memset(&newboard, 0, sizeof(newboard));

    newboard.gid = whatclass;
    if (newboard.gid == 0) {
    vmsg("請先選擇一個類別再開板!");
    return -1;
    }
    do {
    if (!getdata(3, 0, msg_bid, newboard.brdname,
             sizeof(newboard.brdname), DOECHO))
        return -1;
    } while (invalid_brdname(newboard.brdname));

    do {
    getdata(6, 0, "看板類別:", genbuf, 5, DOECHO);
    if (strlen(genbuf) == 4)
        break;
    } while (1);

    strcpy(newboard.title, genbuf);

    newboard.title[4] = ' ';

    getdata(8, 0, "看板主題:", genbuf, BTLEN + 1, DOECHO);
    if (genbuf[0])
    strlcpy(newboard.title + 7, genbuf, sizeof(newboard.title) - 7);
    setbpath(genbuf, newboard.brdname);

    if (!recover && 
        (getbnum(newboard.brdname) > 0 || mkdir(genbuf, 0755) == -1)) {
    vmsg("此看板已經存在! 請取不同英文板名");
    return -1;
    }
    newboard.brdattr = BRD_NOTRAN;
#ifdef DEFAULT_AUTOCPLOG
    newboard.brdattr |= BRD_CPLOG;
#endif

    if (HasUserPerm(PERM_SYSOP)) {
    move(1, 0);
    clrtobot();
    newboard.brdattr = setperms(newboard.brdattr, str_permboard);
    move(1, 0);
    clrtobot();
    }
    getdata(9, 0, "是看板? (N:目錄) (Y/n):", genbuf, 3, LCECHO);
    if (genbuf[0] == 'n')
    {
    newboard.brdattr |= BRD_GROUPBOARD;
    newboard.brdattr &= ~BRD_CPLOG;
    }

    {
        const char* brd_symbol;
        if (newboard.brdattr & BRD_GROUPBOARD)
            brd_symbol = "Σ";
        else if (newboard.brdattr & BRD_NOTRAN)
        brd_symbol = "◎";
        else
        brd_symbol = "●";

        newboard.title[5] = brd_symbol[0];
        newboard.title[6] = brd_symbol[1];
    }

    newboard.level = 0;
    getdata(11, 0, "板主名單:", newboard.BM, sizeof(newboard.BM), DOECHO);
#ifdef CHESSCOUNTRY
    if (getdata_str(12, 0, "設定棋國 (0)無 (1)五子棋 (2)象棋 (3)圍棋", ans,
        sizeof(ans), LCECHO, "0")){
    newboard.chesscountry = atoi(ans);
    if (newboard.chesscountry > CHESSCODE_MAX ||
        newboard.chesscountry < CHESSCODE_NONE)
        newboard.chesscountry = CHESSCODE_NONE;
    }
#endif /* defined(CHESSCOUNTRY) */

    if (HasUserPerm(PERM_SYSOP) && !(newboard.brdattr & BRD_HIDE)) {
    getdata_str(14, 0, "設定讀寫權限(Y/N)?", ans, sizeof(ans), LCECHO, "N");
    if (*ans == 'y') {
        getdata_str(15, 0, "限制 [R]閱\讀 (P)發表?", ans, sizeof(ans), LCECHO, "R");
        if (*ans == 'p')
        newboard.brdattr |= BRD_POSTMASK;
        else
        newboard.brdattr &= (~BRD_POSTMASK);

        move(1, 0);
        clrtobot();
        bperm_msg(&newboard);
        newboard.level = setperms(newboard.level, str_permid);
        clear();
    }
    }

    add_board_record(&newboard);
    getbcache(whatclass)->childcount = 0;
    pressanykey();
    setup_man(&newboard, NULL);
    outs("\n新板成立");
    post_newboard(newboard.title, newboard.brdname, newboard.BM);
    log_usies("NewBoard", newboard.title);
    pressanykey();
    return 0;
}

int make_symbolic_link(const char *bname, int gid)
{
    boardheader_t   newboard;
    int bid;
    
    bid = getbnum(bname);
    if(bid==0) return -1;
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    memset(&newboard, 0, sizeof(newboard));

    /*
     * known issue:
     *   These two stuff will be used for sorting.  But duplicated brdnames
     *   may cause wrong binary-search result.  So I replace the last 
     *   letters of brdname to '~'(ascii code 126) in order to correct the
     *   resuilt, thought I think it's a dirty solution.
     *
     *   Duplicate entry with same brdname may cause wrong result, if
     *   searching by key brdname.  But people don't need to know a board
     *   is symbolic, so just let SYSOP know it. You may want to read
     *   board.c:load_boards().
     */

    strlcpy(newboard.brdname, bname, sizeof(newboard.brdname));
    newboard.brdname[strlen(bname) - 1] = '~';
    strlcpy(newboard.title, bcache[bid - 1].title, sizeof(newboard.title));
    strcpy(newboard.title + 5, "@看板連結");

    newboard.gid = gid;
    BRD_LINK_TARGET(&newboard) = bid;
    newboard.brdattr = BRD_NOTRAN | BRD_SYMBOLIC;

    if (add_board_record(&newboard) < 0)
    return -1;
    return bid;
}

int make_symbolic_link_interactively(int gid)
{
    char buf[32];

    CompleteBoard(msg_bid, buf);
    if (!buf[0])
    return -1;

    stand_title("建立看板連結");

    if (make_symbolic_link(buf, gid) < 0) {
    vmsg("看板連結建立失敗");
    return -1;
    }
    log_usies("NewSymbolic", buf);
    return 0;
}

/* FIXME 真是一團垃圾
 *
 * fdata 用了太多 magic number
 * return value 應該是指 reason (return index + 1)
 * ans[0] 指的是帳管選擇的「錯誤的欄位」 (Register 選單裡看到的那些)
 */
static int
auto_scan(char fdata[][STRLEN], char ans[])
{
    int             good = 0;
    int             count = 0;
    int             i;
    char            temp[10];

    if (!strncmp(fdata[1], "小", 2) || strstr(fdata[1], "丫")
    || strstr(fdata[1], "誰") || strstr(fdata[1], "不")) {
    ans[0] = '0';
    return 1;
    }
    strlcpy(temp, fdata[1], 3);

    /* 疊字 */
    if (!strncmp(temp, &(fdata[1][2]), 2)) {
    ans[0] = '0';
    return 1;
    }
    if (strlen(fdata[1]) >= 6) {
    if (strstr(fdata[1], "陳水扁")) {
        ans[0] = '0';
        return 1;
    }
    if (strstr("趙錢孫李周吳鄭王", temp))
        good++;
    else if (strstr("杜顏黃林陳官余辛劉", temp))
        good++;
    else if (strstr("蘇方吳呂李邵張廖應蘇", temp))
        good++;
    else if (strstr("徐謝石盧施戴翁唐", temp))
        good++;
    }
    if (!good)
    return 0;

    if (!strcmp(fdata[2], fdata[3]) ||
    !strcmp(fdata[2], fdata[4]) ||
    !strcmp(fdata[3], fdata[4])) {
    ans[0] = '4';
    return 5;
    }
    if (strstr(fdata[2], "大")) {
    if (strstr(fdata[2], "台") || strstr(fdata[2], "淡") ||
        strstr(fdata[2], "交") || strstr(fdata[2], "政") ||
        strstr(fdata[2], "清") || strstr(fdata[2], "警") ||
        strstr(fdata[2], "師") || strstr(fdata[2], "銘傳") ||
        strstr(fdata[2], "中央") || strstr(fdata[2], "成") ||
        strstr(fdata[2], "輔") || strstr(fdata[2], "東吳"))
        good++;
    } else if (strstr(fdata[2], "女中"))
    good++;

    if (strstr(fdata[3], "地球") || strstr(fdata[3], "宇宙") ||
    strstr(fdata[3], "信箱")) {
    ans[0] = '2';
    return 3;
    }
    if (strstr(fdata[3], "市") || strstr(fdata[3], "縣")) {
    if (strstr(fdata[3], "路") || strstr(fdata[3], "街")) {
        if (strstr(fdata[3], "號"))
        good++;
    }
    }
    for (i = 0; fdata[4][i]; i++) {
    if (isdigit((int)fdata[4][i]))
        count++;
    }

    if (count <= 4) {
    ans[0] = '3';
    return 4;
    } else if (count >= 7)
    good++;

    if (good >= 3) {
    ans[0] = 'y';
    return -1;
    } else
    return 0;
}

#define REJECT_REASONS (6)
#define FN_REGISTER_LOG "register.log"

// read count entries from regsrc to a temp buffer
FILE *
pull_regform(const char *regfile, char *workfn, int count)
{
    FILE *fp = NULL;

    snprintf(workfn, PATHLEN, "%s.tmp", regfile);
    if (dashf(workfn)) {
    vmsg("其他 SYSOP 也在審核註冊申請單");
    return NULL;
    }

    // count < 0 means unlimited pulling
    Rename(regfile, workfn);
    if ((fp = fopen(workfn, "r")) == NULL) {
    vmsgf("系統錯誤,無法讀取註冊資料檔: %s", workfn);
    return NULL;
    }
    return fp;
}

// write all left in "remains" to regfn.
void
pump_regform(const char *regfn, FILE *remains)
{
    // restore trailing tickets
    char buf[PATHLEN];
    FILE *fout = fopen(regfn, "at");
    if (!fout)
    return;

    while (fgets(buf, sizeof(buf), remains))
    fputs(buf, fout);
    fclose(fout);
}

/* 處理 Register Form */
// TODO XXX process someone directly, according to target_uid.
int
scan_register_form(const char *regfile, int automode, const char *target_uid)
{
    char            genbuf[200];
    char    *logfile = FN_REGISTER_LOG;
    char    *field[] = {
    "uid", "name", "career", "addr", "phone", "email", NULL
    };
    char    *finfo[] = {
    "帳號", "真實姓名", "服務單位", "目前住址",
    "連絡電話", "電子郵件信箱", NULL
    };
    char    *reason[REJECT_REASONS+1] = {
    "輸入真實姓名",
    "詳填「(畢業)學校及『系』『級』」或「服務單位(含所屬縣市及職稱)」",
    "填寫完整的住址資料 (含縣市名稱, 台北市請含行政區域)",
    "詳填連絡電話 (含區域碼, 中間不用加 '-', '(', ')'等符號",
    "確實填寫註冊申請表",
    "用中文填寫申請單",
    NULL
    };
    char    *autoid = "AutoScan";
    userec_t        muser;
    FILE           *fn, *fout, *freg;
    char            fdata[6][STRLEN];
    char            fname[STRLEN] = "", buf[STRLEN];
    char            ans[4], *ptr, *uid;
    int             n = 0, unum = 0, tid = 0;
    int             nSelf = 0, nAuto = 0;

    uid = cuser.userid;
    move(2, 0);

    fn = pull_regform(regfile, fname, -1);
    if (!fn)
    return -1;

    while( fgets(genbuf, STRLEN, fn) ){
    memset(fdata, 0, sizeof(fdata));
    do {
        if( genbuf[0] == '-' )
        break;
        if ((ptr = (char *)strstr(genbuf, ": "))) {
        *ptr = '\0';
        for (n = 0; field[n]; n++) {
            if (strcmp(genbuf, field[n]) == 0) {
            strlcpy(fdata[n], ptr + 2, sizeof(fdata[n]));
            if ((ptr = (char *)strchr(fdata[n], '\n')))
                *ptr = '\0';
            }
        }
        }
    } while( fgets(genbuf, STRLEN, fn) );
    tid ++;

    if ((unum = getuser(fdata[0], &muser)) == 0) {
        move(2, 0);
        clrtobot();
        outs("系統錯誤,查無此人\n\n");
        for (n = 0; field[n]; n++)
        prints("%s     : %s\n", finfo[n], fdata[n]);
        pressanykey();
    } else {
        if (automode)
        uid = autoid;

        if ((!automode || !auto_scan(fdata, ans))) {
        uid = cuser.userid;

        move(1, 0);
        clrtobot();
        prints("帳號位置    : %d\n", unum);
        user_display(&muser, 1);
        move(14, 0);
        prints(ANSI_COLOR(1;32) "------------- "
            "請站長嚴格審核使用者資料,這是第 %d 份"
            "------------" ANSI_RESET "\n", tid);
            prints("  %-12s: %s\n", finfo[0], fdata[0]);
#ifdef FOREIGN_REG
        prints("0.%-12s: %s%s\n", finfo[1], fdata[1],
               muser.uflag2 & FOREIGN ? " (外籍)" : "");
#else
        prints("0.%-12s: %s\n", finfo[1], fdata[1]);
#endif
        for (n = 2; field[n]; n++) {
            prints("%d.%-12s: %s\n", n - 1, finfo[n], fdata[n]);
        }
        if (muser.userlevel & PERM_LOGINOK) {
            ans[0] = getkey("此帳號已經完成註冊, "
                    "更新(Y/N/Skip)?[N] ");
            if (ans[0] != 'y' && ans[0] != 's')
            ans[0] = 'd';
        } else {
            if (search_ulist(unum) == NULL)
            {
            move(b_lines, 0); clrtoeol();
            outs("是否接受此資料(Y/N/Q/Del/Skip)?[S] ");
            // FIXME if the user got online here
                ans[0] = igetch();
            }
            else
            ans[0] = 's';
            ans[0] = tolower(ans[0]);
            if (ans[0] != 'y' && ans[0] != 'n' && 
            ans[0] != 'q' && ans[0] != 'd' && 
            !('0' <= ans[0] && ans[0] < ('0' + REJECT_REASONS)))
            ans[0] = 's';
            ans[1] = 0;
        }
        nSelf++;
        } else
        nAuto++;

        switch (ans[0]) {
        case 'q':
        if ((freg = fopen(regfile, "a"))) {
            for (n = 0; field[n]; n++)
            fprintf(freg, "%s: %s\n", field[n], fdata[n]);
            fprintf(freg, "----\n");
            while (fgets(genbuf, STRLEN, fn))
            fputs(genbuf, freg);
            fclose(freg);
        }
        case 'd':
        break;

        case '0': case '1': case '2':
        case '3': case '4': case '5':
        /* please confirm match REJECT_REASONS here */
        case 'n':
        if (ans[0] == 'n') {
            int nf = 0;
            move(8, 0);
            clrtobot();
            outs("請提出退回申請表原因,按 <enter> 取消\n");
            for (n = 0; n < REJECT_REASONS; n++)
            prints("%d) 請%s\n", n, reason[n]);
            outs("\n"); // preserved for prompt
            for (nf = 0; field[nf]; nf++)
            prints("%s: %s\n", finfo[nf], fdata[nf]);
        } else
            buf[0] = ans[0];

        if (ans[0] != 'n' ||
            getdata(9 + n, 0, "退回原因: ", buf, 60, DOECHO))
            if ((buf[0] - '0') >= 0 && (buf[0] - '0') < n) {
            int             i;
            fileheader_t    mhdr;
            char            title[128], buf1[80];
            FILE           *fp;

            sethomepath(buf1, muser.userid);
            stampfile(buf1, &mhdr);
            strlcpy(mhdr.owner, cuser.userid, sizeof(mhdr.owner));
            strlcpy(mhdr.title, "[註冊失敗]", TTLEN);
            mhdr.filemode = 0;
            sethomedir(title, muser.userid);
            if (append_record(title, &mhdr, sizeof(mhdr)) != -1) {
                char rejfn[PATHLEN];
                fp = fopen(buf1, "w");
                
                for(i = 0; buf[i] && i < sizeof(buf); i++){
                if (buf[i] >= '0' && buf[i] < '0'+n)
                {
                    fprintf(fp, "[退回原因] 請%s\n",
                        reason[buf[i] - '0']);
                }
                }

                fclose(fp);

                // build reject file
                setuserfile(rejfn, "justify.reject");
                Copy(buf1, rejfn);
            }
            if ((fout = fopen(logfile, "a"))) {
                for (n = 0; field[n]; n++)
                fprintf(fout, "%s: %s\n", field[n], fdata[n]);
                fprintf(fout, "Date: %s\n", Cdate(&now));
                fprintf(fout, "Rejected: %s [%s]\n----\n",
                    uid, buf);
                fclose(fout);
            }
            break;
            }
        move(10, 0);
        clrtobot();
        outs("取消退回此註冊申請表");
        /* no break? */

        case 's':
        if ((freg = fopen(regfile, "a"))) {
            for (n = 0; field[n]; n++)
            fprintf(freg, "%s: %s\n", field[n], fdata[n]);
            fprintf(freg, "----\n");
            fclose(freg);
        }
        break;

        default:
        outs("以下使用者資料已經更新:\n");
        mail_muser(muser, "[註冊成功\囉]", "etc/registered");

#if FOREIGN_REG_DAY > 0
        if(muser.uflag2 & FOREIGN)
            mail_muser(muser, "[出入境管理局]", "etc/foreign_welcome");
#endif

        muser.userlevel |= (PERM_LOGINOK | PERM_POST);
        strlcpy(muser.realname, fdata[1], sizeof(muser.realname));
        strlcpy(muser.address, fdata[3], sizeof(muser.address));
        strlcpy(muser.email, fdata[5], sizeof(muser.email));
        snprintf(genbuf, sizeof(genbuf), "%s:%s:%s",
             fdata[4], fdata[2], uid);
        strlcpy(muser.justify, genbuf, sizeof(muser.justify));

        passwd_update(unum, &muser);
        // XXX TODO notify users?
        sendalert(muser.userid,  ALERT_PWD_PERM); // force to reload perm

        sethomefile(buf, muser.userid, "justify");
        log_file(buf, LOG_CREAT, genbuf);

        if ((fout = fopen(logfile, "a"))) {
            for (n = 0; field[n]; n++)
            fprintf(fout, "%s: %s\n", field[n], fdata[n]);
            fprintf(fout, "Date: %s\n", Cdate(&now));
            fprintf(fout, "Approved: %s\n", uid);
            fprintf(fout, "----\n");
            fclose(fout);
        }
        sethomefile(genbuf, muser.userid, "justify.wait");
        unlink(genbuf);
        break;
        }
    }
    }

    fclose(fn);
    unlink(fname);

    move(0, 0);
    clrtobot();

    move(5, 0);
    prints("您審了 %d 份註冊單,AutoScan 審了 %d 份", nSelf, nAuto);

    pressanykey();
    return (0);
}

#ifdef EXP_ADMIN_REGFORM

#define FORMS_IN_PAGE (10)
#define REASON_LEN (60)
static const char *reasonstr[REJECT_REASONS] = {
    "輸入真實姓名",
    "詳填(畢業)學校『系』『級』或服務單位(含所屬縣市及職稱)",
    "填寫完整的住址資料 (含縣市名稱, 台北市請含行政區域)",
    "詳填連絡電話 (含區碼, 中間不加 '-', '(', ')' 等符號)",
    "確實填寫註冊申請表",
    "用中文填寫申請單",
};

static void
prompt_regform_ui()
{
    move(b_lines, 0);
    outs(ANSI_COLOR(30;47)  "  "
        ANSI_COLOR(31) "y" ANSI_COLOR(30) "接受 "
        ANSI_COLOR(31) "n" ANSI_COLOR(30) "拒絕 "
        ANSI_COLOR(31) "d" ANSI_COLOR(30) "刪除 "
        ANSI_COLOR(31) "s" ANSI_COLOR(30) "跳過 "
        ANSI_COLOR(31) "u" ANSI_COLOR(30) "復原 "
        " "
        ANSI_COLOR(31) "0-9jk↑↓" ANSI_COLOR(30) "移動 "
        ANSI_COLOR(31) "空白/PgDn" ANSI_COLOR(30) "儲存+下頁 "
        " "
        ANSI_COLOR(31) "q/END" ANSI_COLOR(30) "結束  "
        ANSI_RESET);
}

void
resolve_reason(char *s, int y)
{
    const char *reason_prompt = 
    " (1)真實姓名 (2)詳填系級 (3)完整住址"
    " (4)詳填電話 (5)確實填寫 (6)中文填寫";

    s[0] = 0;
    move(y, 0);
    outs(reason_prompt); outs("\n");

    do {
    getdata(y+1, 0, 
        "退回原因: ", s, REASON_LEN, DOECHO);

    // convert abbrev reasons (format: single digit, or multiple digites)
    if (s[0] >= '1' &&
        s[0] < '1'+REJECT_REASONS)
    {
        if (s[1] == 0) // simple replace ment
        {
        strlcpy(s+2, reasonstr[s[0]-'1'],
            REASON_LEN-2);
        s[0] = 0xbd; // '請'[0];
        s[1] = 0xd0; // '請'[1];
        } else {
        // strip until all digites
        char *p = s;
        while (*p)
        {
            if (*p < '1' || *p >= '1'+REJECT_REASONS)
            *p = ' ';
            p++;
        }
        strip_blank(s, s);
        strlcat(s, " [多重原因]", REASON_LEN);
        }
    } 

    if (strlen(s) < 4)
        vmsg("原因太短。");
    } while (strlen(s) < 4);
}

void 
regform_accept(const char *userid, const char *justify)
{
    char buf[PATHLEN];
    int unum = 0;
    userec_t muser;

    unum = getuser(userid, &muser);
    if (unum == 0)
    return; // invalid user

    muser.userlevel |= (PERM_LOGINOK | PERM_POST);
    strlcpy(muser.justify, justify, sizeof(muser.justify));
    // manual accept sets email to 'x'
    strlcpy(muser.email, "x", sizeof(muser.email)); 

    // handle files
    sethomefile(buf, muser.userid, "justify.wait");
    unlink(buf);
    sethomefile(buf, muser.userid, "justify.reject");
    unlink(buf);
    sethomefile(buf, muser.userid, "justify");
    log_filef(buf, LOG_CREAT, "%s\n", muser.justify);

    // update password file
    passwd_update(unum, &muser);

    // alert online users?
    sendalert(muser.userid,  ALERT_PWD_PERM|ALERT_PWD_JUSTIFY); // force to reload perm

#if FOREIGN_REG_DAY > 0
    if(muser.uflag2 & FOREIGN)
    mail_muser(muser, "[出入境管理局]", "etc/foreign_welcome");
    else
#endif
    // last: send notification mail
    mail_muser(muser, "[註冊成功\囉]", "etc/registered");
}

void 
regform_reject(const char *userid, char *reason)
{
    char buf[PATHLEN];
    FILE *fp = NULL;
    int unum = 0;
    userec_t muser;

    unum = getuser(userid, &muser);
    if (unum == 0)
    return; // invalid user

    muser.userlevel &= ~(PERM_LOGINOK | PERM_POST);

    // handle files
    sethomefile(buf, muser.userid, "justify.wait");
    unlink(buf);

    // update password file
    passwd_update(unum, &muser);

    // alert notify users?
    sendalert(muser.userid,  ALERT_PWD_PERM); // force to reload perm

    // last: send notification
    mkuserdir(muser.userid);
    sethomefile(buf, muser.userid, "justify.reject");
    fp = fopen(buf, "wt");
    assert(fp);
    syncnow();
    fprintf(fp, "%s 註冊失敗。\n", Cdate(&now));

    if (reason[0] >= '1' && reason[0] < '1' + REJECT_REASONS) // multiple abbrev loop
    {
    int i = 0;
    for (i = 0; i < REASON_LEN && 
        reason[i] >= '1' && reason[i] < '1' + REJECT_REASONS; i++)
        fprintf(fp, "[退回原因] 請%s\n", reasonstr[reason[i] - '1']);
    } else {
    fprintf(fp, "[退回原因] %s\n", reason);
    }
    fclose(fp);
    mail_muser(muser, "[註冊失敗]", buf);
}

// TODO define and use structure instead, even in reg request file.
//
typedef struct {
    // current format:
    // (optional) num: unum, date
    // [0] uid: xxxxx   (IDLEN=12)
    // [1] name: RRRRRR (20)
    // [2] career: YYYYYYYYYYYYYYYYYYYYYYYYYY (40)
    // [3] addr: TTTTTTTTT (50)
    // [4] phone: 02DDDDDDDD (20)
    // [5] email: x (50) (deprecated)
    // [6] mobile: (deprecated)
    // [7] ----
    char userid[IDLEN+1];

    char exist;
    char online;
    char pad   [ 5];     // IDLEN(12)+1+1+1+5=20

    char name  [20];
    char career[40];
    char addr  [50];
    char phone [20];
} RegformEntry;

int
load_regform_entry(RegformEntry *pre, FILE *fp)
{
    char buf[STRLEN];
    char *v;

    memset(pre, 0, sizeof(RegformEntry));
    while (fgets(buf, sizeof(buf), fp))
    {
    if (buf[0] == '-')
        break;
    buf[sizeof(buf)-1] = 0;
    v = strchr(buf, ':');
    if (v == NULL)
        continue;
    *v++ = 0;
    if (*v == ' ') v++;
    chomp(v);

    if (strcmp(buf, "uid") == 0)
        strlcpy(pre->userid, v, sizeof(pre->userid));
    else if (strcmp(buf, "name") == 0)
        strlcpy(pre->name, v, sizeof(pre->name));
    else if (strcmp(buf, "career") == 0)
        strlcpy(pre->career, v, sizeof(pre->career));
    else if (strcmp(buf, "addr") == 0)
        strlcpy(pre->addr, v, sizeof(pre->addr));
    else if (strcmp(buf, "phone") == 0)
        strlcpy(pre->phone, v, sizeof(pre->phone));
    }
    return pre->userid[0] ? 1 : 0;
}

int
print_regform_entry(const RegformEntry *pre, FILE *fp, int close)
{
    fprintf(fp, "uid: %s\n",    pre->userid);
    fprintf(fp, "name: %s\n",   pre->name);
    fprintf(fp, "career: %s\n", pre->career);
    fprintf(fp, "addr: %s\n",   pre->addr);
    fprintf(fp, "phone: %s\n",  pre->phone);
    fprintf(fp, "email: %s\n",  "x");
    if (close)
    fprintf(fp, "----\n");
    return 1;
}

int
append_regform(const RegformEntry *pre, const char *logfn, 
    const char *varname, const char *varval1, const char *varval2)
{
    FILE *fout = fopen(logfn, "at");
    if (!fout)
    return 0;

    print_regform_entry(pre, fout, 0);
    if (varname && *varname)
    {
    syncnow();
    fprintf(fout, "Date: %s\n", Cdate(&now));
    if (!varval1) varval1 = "";
    fprintf(fout, "%s: %s", varname, varval1);
    if (varval2) fprintf(fout, " %s", varval2);
    fprintf(fout, "\n");
    }
    // close it
    fprintf(fout, "----\n");
    fclose(fout);
    return 1;
}


int
handle_register_form(const char *regfile, int dryrun)
{
    int unum = 0;
    int yMsg = FORMS_IN_PAGE*2+1;
    FILE *fp = NULL;
    userec_t muser;
    RegformEntry forms [FORMS_IN_PAGE];
    char ans    [FORMS_IN_PAGE];
    char rejects[FORMS_IN_PAGE][REASON_LEN];    // reject reason length
    char fname  [PATHLEN] = "";
    char justify[REGLEN+1];
    int cforms = 0, // current loaded forms
    parsed = 0, // total parsed forms
    ci = 0, // cursor index
    ch = 0, // input key
    i, blanks;
    long fsz = 0, fpos = 0;

    // prepare reg tickets
    if (dryrun)
    {
    // directly open regfile to try
    fp = fopen(regfile, "rt");
    } else {
    fp = pull_regform(regfile, fname, -1);
    }

    if (!fp)
    return 0;

    // retreieve file info
    fpos = ftell(fp);
    fseek(fp, 0, SEEK_END);
    fsz = ftell(fp);
    fseek(fp, fpos, SEEK_SET);
    if (!fsz) fsz = 1;

    while (ch != 'q')
    {
    // initialize and prepare
    memset(ans, 0, sizeof(ans));
    memset(rejects, 0, sizeof(rejects));
    cforms = 0;

    // load forms
    while (cforms < FORMS_IN_PAGE && load_regform_entry(&forms[cforms], fp))
        cforms++, parsed ++;

    // if no more forms then leave.
    if (cforms < 1)
        break;

    // adjust cursor if required
    if (ci >= cforms)
        ci = cforms-1;

    // display them all.
    clear();
    for (i = 0; i < cforms; i++)
    {
        // fetch user information
        memset(&muser, 0, sizeof(muser));
        unum = getuser(forms[i].userid, &muser);
        forms[i].exist = unum ? 1 : 0;
        if (unum) forms[i].online = search_ulist(unum) ? 1 : 0;

        // if already got login level, delete by default.
        if (!unum)
        ans[i] = 'd';
        else {
        if (muser.userlevel & PERM_LOGINOK)
            ans[i] = 'd';
        else if (forms[i].online)
            ans[i] = 's';
        }

        // print
        move(i*2, 0);
        prints("  %2d%s %s%-12s " ANSI_RESET, 
            i+1, 
            (unum == 0) ? ANSI_COLOR(1;31) "D" :
            ( (muser.userlevel & PERM_LOGINOK) ? 
              ANSI_COLOR(1;33) "Y" : forms[i].online ? "s" : "."),
            forms[i].online ?  ANSI_COLOR(1;35) : ANSI_COLOR(1),
            forms[i].userid);

        prints( ANSI_COLOR(1;31) "%19s " 
            ANSI_COLOR(1;32) "%-40s" ANSI_RESET"\n", 
            forms[i].name, forms[i].career);

        move(i*2+1, 0); 
        prints("      %-50s%20s\n", forms[i].addr, forms[i].phone);
    }

    // display page info
    {
        char msg[STRLEN];
        fpos = ftell(fp);
        if (fpos > fsz) fsz = fpos*10;
        snprintf(msg, sizeof(msg),
            " 已顯示 %d 份註冊單 (%2d%%)  ",
            parsed, (int)(fpos*100/fsz));
        prints(ANSI_COLOR(7) "\n%78s" ANSI_RESET "\n", msg);
    }

    // handle user input
    prompt_regform_ui();
    ch = 0;
    while (ch != 'q' && ch != ' ') {
        ch = cursor_key(ci*2, 0);
        switch (ch)
        {
        // nav keys
        case KEY_UP:
        case 'k':
            if (ci > 0) ci--;
            break;

        case KEY_DOWN:
        case 'j':
            ch = 'j'; // go next
            break;

            // quick nav (assuming to FORMS_IN_PAGE=10)
        case '1': case '2': case '3': case '4': case '5':
        case '6': case '7': case '8': case '9':
            ci = ch - '1';
            if (ci >= cforms) ci = cforms-1;
            break;
        case '0':
            ci = 10-1;
            if (ci >= cforms) ci = cforms-1;
            break;

            /*
        case KEY_HOME: ci = 0; break;
        case KEY_END:  ci = cforms-1; break;
            */

            // go next page
        case KEY_PGDN:
        case ' ':
            ch = ' ';
            break;

            // abort
        case KEY_END:
        case 'q':
            ch = 'q';
            if (getans("確定要離開了嗎? (本頁變更將不會儲存) [y/N]: ") != 'y')
            {
            prompt_regform_ui();
            ch = 0;
            continue;
            }
            break;

            // function keys
        case 'y':   // accept
            if (forms[ci].online)
            {
            vmsg("暫不開放審核在線上使用者。");
            break;
            }
        case 's':   // skip
        case 'd':   // delete
        case KEY_DEL: //delete
            if (ch == KEY_DEL) ch = 'd';

            grayout(ci*2, ci*2+1, GRAYOUT_DARK);
            move_ansi(ci*2, 4); outc(ch);
            ans[ci] = ch;
            ch = 'j'; // go next
            break;

        case 'u':   // undo
            if (forms[ci].online)
            {
            vmsg("暫不開放審核在線上使用者。");
            break;
            }
            grayout(ci*2, ci*2+1, GRAYOUT_NORM);
            move_ansi(ci*2, 4); outc('.');
            ans[ci] = 0;
            ch = 'j'; // go next
            break;

        case 'n':   // reject
            if (forms[ci].online)
            {
            vmsg("暫不開放審核在線上使用者。");
            break;
            }
            // query for reason
            resolve_reason(rejects[ci], yMsg);
            move(yMsg, 0);
            prints(" %s 退回原因:\n %s\n", forms[ci].userid, rejects[ci]);

            // do reject
            grayout(ci*2, ci*2+1, GRAYOUT_DARK);
            move_ansi(ci*2, 4); outc(ch);
            ans[ci] = ch;
            ch = 'j'; // go next

            prompt_regform_ui();
            break;
        } // switch(ch)

        // change cursor
        if (ch == 'j' && ++ci >= cforms)
        ci = cforms -1;
    } // while(ch != QUIT/SAVE)

    // if exit, we still need to skip all read forms
    if (ch == 'q')
    {
        for (i = 0; i < cforms; i++)
        ans[i] = 's';
    }

    // page complete (save).
    assert(ch == ' ' || ch == 'q');

    // solving blank (undecided entries)
    for (i = 0, blanks = 0; i < cforms; i++)
        if (ans[i] == 0) blanks ++;
    if (blanks) {
        char rsn[REASON_LEN];
        ch = getans("尚未指定的 %d 個項目要: (S跳過/y通過/n拒絕): ", blanks);
        if (ch == 'y') {
        // do nothing.
        } else if (ch == 'n') {
        // query reject reason
        resolve_reason(rsn, yMsg);
        } else ch = 's';

        for (i = 0; i < cforms; i++)
        {
        if (ans[i] != 0)
            continue;
        ans[i] = ch;
        if (ch != 'n')
            continue;
        strlcpy(rejects[i], rsn, REASON_LEN);
        }
    }

    // save/commit if required.
    if (dryrun) 
    {
        // prmopt for debug
        clear();
        stand_title("測試模式");
        outs("您正在執行測試模式,所以剛審的註冊單並不會生效。\n"
            "下面列出的是剛才您審完的結果:\n\n");

        for (i = 0; i < cforms; i++)
        {
        if (ans[i] == 'y')
            snprintf(justify, sizeof(justify), // build justify string
                "%s:%s:%s", forms[i].phone, forms[i].career, cuser.userid);

        prints("%2d. %-12s - %c %s\n", i+1, forms[i].userid, ans[i],
            ans[i] == 'n' ? rejects[i] : 
            ans[i] == 'y' ? justify : "");
        }
        if (ch != 'q')
        pressanykey();
    } 
    else 
    {
        // real functionality
        for (i = 0; i < cforms; i++)
        {
        if (ans[i] == 'y')
        {
            // build justify string
            snprintf(justify, sizeof(justify), 
                "%s:%s:%s", forms[i].phone, forms[i].career, cuser.userid);

            regform_accept(forms[i].userid, justify);
            // log form to FN_REGISTER_LOG
            append_regform(&forms[i], FN_REGISTER_LOG,
                "Approved", cuser.userid, NULL);
        }
        else if (ans[i] == 'n')
        {
            regform_reject(forms[i].userid, rejects[i]);
            // log form to FN_REGISTER_LOG
            append_regform(&forms[i], FN_REGISTER_LOG,
                "Rejected", cuser.userid, rejects[i]);
        }
        else if (ans[i] == 's')
        {
            // append form back to fn_register
            append_regform(&forms[i], fn_register,
                NULL, NULL, NULL);
        }
        }
    } // !dryrun

    } // while (ch != 'q')

    // cleaning left regforms
    if (!dryrun)
    {
    pump_regform(regfile, fp);
    fclose(fp);
        unlink(fname);
    } else {
    // directly close file should be OK.
    fclose(fp);
    }

    return 0;
}

#endif // EXP_ADMIN_REGFORM

int
m_register(void)
{
    FILE           *fn;
    int             x, y, wid, len;
    char            ans[4];
    char            genbuf[200];

    if ((fn = fopen(fn_register, "r")) == NULL) {
    outs("目前並無新註冊資料");
    return XEASY;
    }
    stand_title("審核使用者註冊資料");
    y = 2;
    x = wid = 0;

    while (fgets(genbuf, STRLEN, fn) && x < 65) {
    if (strncmp(genbuf, "uid: ", 5) == 0) {
        move(y++, x);
        outs(genbuf + 5);
        len = strlen(genbuf + 5);
        if (len > wid)
        wid = len;
        if (y >= t_lines - 3) {
        y = 2;
        x += wid + 2;
        }
    }
    }
    fclose(fn);
    getdata(b_lines - 1, 0, 
#ifdef EXP_ADMIN_REGFORM
        "開始審核嗎(Auto自動/Yes手動/No不審/Exp新界面)?[N] ", 
#else
        "開始審核嗎(Auto自動/Yes手動/No不審)?[N] ", 
#endif
        ans, sizeof(ans), LCECHO);
    if (ans[0] == 'a')
    scan_register_form(fn_register, 1, NULL);
    else if (ans[0] == 'y')
    scan_register_form(fn_register, 0, NULL);

#ifdef EXP_ADMIN_REGFORM
    else if (ans[0] == 'e')
    {
#ifdef EXP_ADMIN_REGFORM_DRYRUN
    clear();
    outs("現在要進入的是實驗性的新界面,純供測試用。\n"
        "請注意: 等下一切審核、刪除、等等動作都是假的,不會真的寫入系統。");
    pressanykey();
    handle_register_form(fn_register, 1);
#else
    int dryrun = 0;
    if (getans("你要進行純測試(T)還是真的執行審核(y)?") == 'y')
    {
        vmsg("進入實際執行模式,所有審核動作都是真的。");
        dryrun = 0;
    } else {
        vmsg("測試模式。");
        dryrun = 1;
    }
    handle_register_form(fn_register, dryrun);
#endif
    }
#endif

    return 0;
}

int
cat_register(void)
{
    if (system("cat register.new.tmp >> register.new") == 0 &&
    unlink("register.new.tmp") == 0)
    vmsg("OK 嚕~~ 繼續去奮鬥吧!!");
    else
    vmsg("沒辦法CAT過去呢 去檢查一下系統吧!!");
    return 0;
}

static void
give_id_money(const char *user_id, int money, const char *mail_title)
{
    char            tt[TTLEN + 1] = {0};

    if (deumoney(searchuser(user_id, NULL), money) < 0) { // TODO if searchuser() return 0
    move(12, 0);
    clrtoeol();
    prints("id:%s money:%d 不對吧!!", user_id, money);
    pressanykey();
    } else {
    snprintf(tt, sizeof(tt), "%s : %d " MONEYNAME " 幣", mail_title, money);
    mail_id(user_id, tt, "etc/givemoney.why", "[PTT 銀行]");
    }
}

int
give_money(void)
{
    FILE           *fp, *fp2;
    char           *ptr, *id, *mn;
    char            buf[200] = "", reason[100], tt[TTLEN + 1] = "";
    int             to_all = 0, money = 0;
    int             total_money=0, count=0;

    getdata(0, 0, "指定使用者(S) 全站使用者(A) 取消(Q)?[S]", buf, 3, LCECHO);
    if (buf[0] == 'q')
    return 1;
    else if (buf[0] == 'a') {
    to_all = 1;
    getdata(1, 0, "發多少錢呢?", buf, 20, DOECHO);
    money = atoi(buf);
    if (money <= 0) {
        move(2, 0);
        vmsg("輸入錯誤!!");
        return 1;
    }
    } else {
    if (vedit("etc/givemoney.txt", NA, NULL) < 0)
        return 1;
    }

    clear();

    unlink("etc/givemoney.log");
    if (!(fp2 = fopen("etc/givemoney.log", "w")))
    return 1;

    getdata(0, 0, "動用國庫!請輸入正當理由(如活動名稱):", reason, 40, LCECHO);
    fprintf(fp2,"\n使用理由: %s\n", reason);

    getdata(1, 0, "要發錢了嗎(Y/N)[N]", buf, 3, LCECHO);
    if (buf[0] != 'y')
       {
        fclose(fp2);
    return 1;
       }

    getdata(1, 0, "紅包袋標題 :", tt, TTLEN, DOECHO);
    fprintf(fp2,"\n紅包袋標題: %s\n", tt);
    move(2, 0);

    vmsg("編紅包袋內容");
    if (vedit("etc/givemoney.why", NA, NULL) < 0) {
        fclose(fp2);
    return 1;
    }

    stand_title("發錢中...");
    if (to_all) {
    int             i, unum;
    for (unum = SHM->number, i = 0; i < unum; i++) {
        if (bad_user_id(SHM->userid[i]))
        continue;
        id = SHM->userid[i];
        give_id_money(id, money, tt);
            fprintf(fp2,"給 %s : %d\n", id, money);
            count++;
    }
        sprintf(buf, "(%d人:%d"MONEYNAME"幣)", count, count*money);
        strcat(reason, buf);
    } else {
    if (!(fp = fopen("etc/givemoney.txt", "r+"))) {
        fclose(fp2);
        return 1;
    }
    while (fgets(buf, sizeof(buf), fp)) {
        clear();
        if (!(ptr = strchr(buf, ':')))
        continue;
        *ptr = '\0';
        id = buf;
        mn = ptr + 1;
            money = atoi(mn);
        give_id_money(id, money, tt);
            fprintf(fp2,"給 %s : %d\n", id, money);
            total_money += money;
            count++;
    }
    fclose(fp);
        sprintf(buf, "(%d人:%d"MONEYNAME"幣)", count, total_money);
        strcat(reason, buf);
    
    }

    fclose(fp2);

    sprintf(buf, "%s 紅包機: %s", cuser.userid, reason);
    post_file(GLOBAL_SECURITY, buf, "etc/givemoney.log", "[紅包機報告]");
    pressanykey();
    return FULLUPDATE;
}