summaryrefslogblamecommitdiffstats
path: root/mbbsd/talk.c
blob: 0f0b66fe0d0f1a7005b046b68bc48c51417a3bab (plain) (tree)
1
                                                      












































































                                                                             
                            


















                                                         

                                                






                                        
                           





                                                      


                                                               
                         
                              

                                                                         
                                        

                       
                                           





                                                                   
                                  
                            

                                                              







                                                                  

                                                                       



                                                         
                           




                                                                            
                             




                                                                       
                          















                                                                     
                                  

                           

                               



                      

                                  
                           

                                  



                       
    



                              

                               






                              

                                  





                       
 















                                 



















                                                                         

 

                              


                                                     





                                                    
                                          





                                                                          
         


                                                         






                                                

                                              
                







                                            
                    
                                


             

                                                                     

                              
    




                                                                                                                              
    




                                                                       

                                                  


                                                                  

                           







                                              

                                       



                                                  
                          










                                                                                   

                                       

                  

                                     
                                          
                                               




                                                                   
             








                                                          

                          




                                  







                                                            





                                              
        










                                                                      
        
                                                       
                                                   
                                                      
                                                                     
                                                  
        
                                                                          

                                                 



                                                                


                                                                












                                                               
                                                 





                                               
                                                 
                                   
                           



                                   
                                                                       

                                                                    
                                                                      


                                      





                                                          
                               
                                                         
                   
                                        





                                                 
                                                  
                                   
                           





                          
                             
                                                      
                

                                  
              







                             


                                                      
                                                      
                  
             
                                 

                                                                     
                                      
                                       
         





                                                                           
                               






                                   



                                                                





                          



                                                                









                          
                                    
 
                          

                      











                                                              


                                                                



                     


                             
                    
                         
               










                                                                 

                                                                           






                                                         
                                                                      





                                                                               
                       



































                                                                          










                                                              
                           



                     
                   













                                                                                 


                                                           



                                                       
                                                                      


























                                                                                      
                                                                                               














                                                                           


                        
















                                                              
                                                               




                                   
                                 










                                                                  

                                             

                                             


                                                                   
































                                                                         
                                  







                                         
 

                   
                             



                            
                                     
                                 

                                                                            
                         




















                                                              

                                              







                                                   

                                                  




                                     
                      







                                                          
                                                   











                                                                 

                





                                         
                                      












                                                         
                                      













                                                         
                              




                                            
                               




































                                                    
                                       







                                            
                                       













                                                                        

                           




























                                                                  
         























                                                                         
              
                       
                               





                                                    

                                 







                                                           
                            



                                 
                        


























                                                                            
              













                                                                             
                         





































                                                                         
                                                         


                                     

                                                       


                                     
                                                                            


                                     
                                                   

                                     

                                                              

                           
         


                                                                             
                         





















                                                                          
                       


















                                                        
                      






                                            
                                                                         





                                
                                                                         















                                                                    
                  
                          
                                 

                                                 
                                                                   







                                                                        
                                                         







                                                                 
                     







                                                                      
                                                             











                                                                            
            


                                  
                              








                                                                 
                           








                                    
                      


                                                   
                              
















                                        
             

                              
                       















                                                                             








                                                                                   







































                                                                                 
                             


                                                     
                                                   



                                                                
                              







                                                                    

                                        




                                                                              

                        



                           

                                                               




                                  
                   
                                  
                         



















                                                          
                                  



                                     

                                                         


















                                                                     


                                           





























                                                                        
                                    




















                                                                      





                                                                      



















                                                                              

                             


















































                                                                        
               
                                                                
                               








                                                                              
                                                             
                                                               

                                                                            
                                
                                                                       
                                                




                                                                         
                                                               


                                                                            

 






                                                 



                                                                       
                                      





                                                
                                















                                                                                
             



                       
                                           

                                                           
                                              










                                         
                                         

                                  
                             






































































                                                                              
                       



                                                            
                        
















                                                            
                                   










                                          
                                          




                                                         
                                          









                                                                        
                                          



















                                                                        
                                  









                                      

                                  









                                          
                          




































































                                                                        
                                                                       








                                                                         
                                      







                                                                          
                                                                                                            




















































                                                               
                                          





















                                                              
                                









                                                        
                       


                                          
                                                           

                                                                
                                                                     

             
                                                                   

                        

                        




                                                
                                                         


                                                                          
                                         





                                                              
                             










                                                                                  
                         












                                                              
                                                
























                                                                       
                                                                 


















                                                           
                                                                  

                               
                


                                                  
                                                   












                                                          
                                                           












                                             

                 











                                     

                 



                                                

                








                                                                        
                                              













                                                                          
       


















                                                                         

                    








                                  

                 














                                  
                            










                                                                        
                                                         






                       
                                                 



















                                                                      

                    

































                                                                              
                       




                                                                      
                           






                                                                        
                                           







                                                  
                                                             





                                                
                                        





                                                                     
                     






















                              

                                         




                                           
                 







                                                     
                       








                                
 



                                                
                                 






                                
         
                     
                               















                                                                
                       


                             
         







                          
/* $Id: talk.c,v 1.13 2002/03/16 15:11:10 ptt Exp $ */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "config.h"
#include "pttstruct.h"
#include "common.h"
#include "perm.h"
#include "modes.h"
#include "proto.h"

#define QCAST   int (*)(const void *, const void *)

extern userinfo_t *currutmp;
extern char *ModeTypeTable[MAX_MODES];
extern char *fn_overrides;
extern int usernum;
extern char *msg_sure_ny;
extern char *msg_cancel;
extern unsigned int currstat;
extern char *fn_writelog;
extern FILE *fp_writelog;
extern pid_t currpid;
extern int b_lines;     /* Screen bottom line number: t_lines-1 */
extern int t_lines, t_columns;  /* Screen size / width */
extern char *fn_talklog;
extern char currauthor[IDLEN + 2];
extern char *msg_usr_left;
extern char *msg_uid;
extern char *BBSName;
extern int p_lines;     /* a Page of Screen line numbers: tlines-4 */
extern char fromhost[];
extern char *err_uid;
extern int talkrequest;
extern char *msg_shortulist;
extern char *msg_nobody;
extern boardheader_t *bcache;
extern int curr_idle_timeout;
extern userec_t cuser;
extern userec_t xuser;


static char *IdleTypeTable[] = {
    "偶在花呆啦", "情人來電", "覓食中", "拜見周公", "假死狀態", "我在思考"
};
static char *sig_des[] = {
    "鬥雞", "聊天", "", "下棋", "象棋", "暗棋"
};

#define MAX_SHOW_MODE 3
#define M_INT 15        /* monitor mode update interval */
#define P_INT 20        /* interval to check for page req. in
                 * talk/chat */
#define BOARDFRI  1

typedef struct talkwin_t {
    int curcol, curln;
    int sline, eline;
} talkwin_t;

typedef struct pickup_t {
    userinfo_t *ui;
    time_t idle;
    int friend;
} pickup_t;

extern int bind( /* int,struct sockaddr *, int */ );
extern char *getuserid();
extern struct utmpfile_t *utmpshm;
extern int watermode, wmofo;
extern water_t water[6], *swater[5], *water_which;
extern char *friend_file[8], water_usies;

/* 記錄 friend 的 user number */
//#define PICKUP_WAYS     7     //關掉女士優先
#define PICKUP_WAYS     6

static int pickup_way = 0;
static char *fcolor[11] = {
    "", "\033[36m", "\033[32m", "\033[1;32m",
    "\033[33m", "\033[1;33m", "\033[1;37m", "\033[1;37m",
    "\033[31m", "\033[1;35m", "\033[1;36m"
};
static char save_page_requestor[40];
static char page_requestor[40];
static char description[30];
static FILE *flog;


char *modestring(userinfo_t * uentp, int simple)
{
    static char modestr[40];
    static char *notonline = "不在站上";
    register int mode = uentp->mode;
    register char *word;
    int fri_stat;

/* for debugging */
    if (mode >= MAX_MODES){
    syslog(LOG_WARNING, "what!? mode = %d", mode);
    word = ModeTypeTable[mode % MAX_MODES];
    }
    else
    word = ModeTypeTable[mode];
    fri_stat = friend_stat(currutmp, uentp);
    if( !(HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_SEECLOAK)) &&
    ((uentp->invisible || (fri_stat & HRM)) &&
     !((fri_stat & HFM) && (fri_stat & HRM)))             )
    return notonline;
    else if (mode == EDITING){
    sprintf(modestr, "E:%s",
        ModeTypeTable[uentp->destuid < EDITING ? uentp->destuid :
                  EDITING]);
    word = modestr;
    }
    else if (!mode && *uentp->chatid == 1){
    if (!simple)
        sprintf(modestr, "回應 %s", getuserid(uentp->destuid));
    else
        sprintf(modestr, "回應呼叫");
    }
    else if (!mode && *uentp->chatid == 2)
    if (uentp->msgcount < 10){
        char *cnum[10] =
        {"", "一", "兩", "三", "四", "五", "六", "七",
         "八", "九"};
        sprintf(modestr, "中%s顆水球", cnum[uentp->msgcount]);
    }
    else
        sprintf(modestr, "不行了 @_@");
    else if (!mode && *uentp->chatid == 3)
    sprintf(modestr, "水球準備中");
    else if (!mode)
    return (uentp->destuid == 6) ? uentp->chatid :
        IdleTypeTable[(0 <= uentp->destuid && uentp->destuid < 6) ?
              uentp->destuid : 0];
    else if (simple)
    return word;
    else if (uentp->in_chat && mode == CHATING)
    sprintf(modestr, "%s (%s)", word, uentp->chatid);
    else if (mode == TALK){
    if (!isvisible_uid(uentp->destuid))/* Leeym 對方(紫色)隱形 */
        sprintf(modestr, "%s", "交談 空氣");/* Leeym 大家自己發揮吧! */
    else
        sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));
    }
    else if (mode == M_FIVE){
    if (!isvisible_uid(uentp->destuid))
        sprintf(modestr, "%s", "五子棋 空氣");
    else
        sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));
    }
    else if (mode == CHC){
    if (isvisible_uid(uentp->destuid))
        sprintf(modestr, "%s", "下象棋");
    else
        sprintf(modestr, "下象棋 %s", getuserid(uentp->destuid));
    }
    else if (mode != PAGE && mode != TQUERY)
    return word;
    else
    sprintf(modestr, "%s %s", word, getuserid(uentp->destuid));

    return (modestr);
}

int set_friend_bit(userinfo_t * me, userinfo_t * ui) {
    int unum, *myfriends, hit=0, n;

    /* 判斷對方是否為我的朋友 ? */
    unum = ui->uid;
    myfriends = me->friend;
    while ((n = *myfriends++)){
    if (unum == n){
        hit = IFH;
        break;
    }
    }
    
    /* 判斷我是否為對方的朋友 ? */
    myfriends = ui->friend;
    while ((unum = *myfriends++)){
    if (unum == me->uid){
        hit |= HFM;
        break;
    }
    }
    
/* 判斷對方是否為我的仇人 ? */

    unum = ui->uid;
    myfriends = me->reject;
    while ((n = *myfriends++)){
    if (unum == n){
        hit |= IRH;
        break;
    }
    }

/* 判斷我是否為對方的仇人 ? */
    myfriends = ui->reject;
    while ((unum = *myfriends++)){
    if (unum == me->uid){
        hit |= HRM;
        break;
    }
    }
    return hit;
}

int reverse_friend_stat(int stat)
{
     int stat1=0;
     if(stat & IFH)
          stat1 |=HFM;
     if(stat & IRH)
          stat1 |=HRM;
     if(stat & HFM)
          stat1 |=IFH;
     if(stat & HRM)
          stat1 |=IRH;   
     if(stat & IBH)
          stat1 |=IBH;   
     return stat1; 
}

int login_friend_online(void)
{
    userinfo_t *uentp;
    int i, stat, stat1;
    int offset=(int) (currutmp - &utmpshm->uinfo[0]);
    for (i=0;i<utmpshm->number && currutmp->friendtotal<MAX_FRIEND; i++){
    uentp = (utmpshm->sorted[utmpshm->currsorted][0][i]);
    if(uentp && uentp->uid && (stat=set_friend_bit(currutmp,uentp))){
        stat1=reverse_friend_stat(stat);
        stat <<= 24;
        stat |= (int) (uentp - &utmpshm->uinfo[0]);
        currutmp->friend_online[currutmp->friendtotal++]=stat; 
        if(uentp!=currutmp && uentp->friendtotal<MAX_FRIEND){
        stat1 <<= 24;
        stat1 |= offset;
        uentp->friend_online[uentp->friendtotal++]=stat1;
        }
    }
    } 
    return 0;
}

int logout_friend_online(void)
{
    int i, j, k;
    int offset=(int) (currutmp - &utmpshm->uinfo[0]);
    userinfo_t *ui;
    while(currutmp->friendtotal){
    i = currutmp->friendtotal-1;
    j = (currutmp->friend_online[i] & 0xFFFFFF);
    currutmp->friend_online[i]=0;
    ui = &utmpshm->uinfo[j]; 
    if(ui->pid && ui!=currutmp){
            for(k=0; k<ui->friendtotal && 
            (int)(ui->friend_online[k] & 0xFFFFFF) !=offset; k++);
            if(k<ui->friendtotal){
        ui->friendtotal--;
        ui->friend_online[k]=ui->friend_online[ui->friendtotal];
        ui->friend_online[ui->friendtotal]=0;
        }
    }
    currutmp->friendtotal--;
    currutmp->friend_online[currutmp->friendtotal]=0;
    }
    return 0;
}


int friend_stat(userinfo_t *me, userinfo_t * ui)
{
  int i, j, hit=0;
  /* 看板好友 */
  if (me->brc_id && ui->brc_id == me->brc_id){
      hit = IBH;
  }
  for(i=0;me->friend_online[i];i++){
      j = (me->friend_online[i] & 0xFFFFFF);
      if(ui == &utmpshm->uinfo[j]){
      hit |= me->friend_online[i] >>24;
      break;
      }
  }
  if (PERM_HIDE(ui))
      return hit & ST_FRIEND;   
  return hit;
}

int isvisible_stat(userinfo_t * me, userinfo_t * uentp, int fri_stat)
{
    if (uentp->userid[0] == 0)
    return 0;
    
    if (PERM_HIDE(uentp) && !(PERM_HIDE(me)))/* 對方紫色隱形而你沒有 */
    return 0;
    else if ((me->userlevel & PERM_SYSOP) ||
             ((fri_stat & HRM) && (fri_stat & HFM)))                            /* 站長看的見任何人 */
    return 1;
    
    if (uentp->invisible && !(me->userlevel & PERM_SEECLOAK)) return 0;

    return (fri_stat & HRM) ? 0 : 1;
}

int isvisible(userinfo_t * me, userinfo_t * uentp)
{
   return isvisible_stat(currutmp, uentp, friend_stat(me, uentp));
}

int isvisible_uid(int tuid)
{
    userinfo_t *uentp;

    if(!tuid || !(uentp = search_ulist(tuid)))
       return 1;
    return isvisible(currutmp, uentp);
}

/* 真實動作 */
static void my_kick(userinfo_t * uentp)
{
    char genbuf[200];

    getdata(1, 0, msg_sure_ny, genbuf, 4, LCECHO);
    clrtoeol();
    if (genbuf[0] == 'y'){
    sprintf(genbuf, "%s (%s)", uentp->userid, uentp->username);
    log_usies("KICK ", genbuf);
    if((uentp->pid <= 0 || kill(uentp->pid, SIGHUP) == -1) && (errno == ESRCH))
        purge_utmp(uentp);
    outs("踢出去囉");
    }
    else
    outs(msg_cancel);
    pressanykey();
}

static void chicken_query(char *userid)
{
    char buf[100];

    if (getuser(userid)){
    if (xuser.mychicken.name[0]){
        time_diff(&(xuser.mychicken));
        if (!isdeadth(&(xuser.mychicken))){
        show_chicken_data(&(xuser.mychicken), NULL);
        sprintf(buf, "\n\n以上是 %s 的寵物資料..", userid);
        outs(buf);
        }
    }
    else{
        move(1, 0);
        clrtobot();
        sprintf(buf, "\n\n%s 並沒有養寵物..", userid);
        outs(buf);
    }
    pressanykey();
    }
}

int my_query(char *uident)
{
    userec_t muser;
    int tuid, i, fri_stat=0;
    unsigned long int j;
    userinfo_t *uentp;
    static const char *money[10] =
    {"債台高築", "赤貧", "清寒", "普通", "小康",
     "小富", "中富", "大富翁", "富可敵國", "比爾蓋\天"};
    static const char *sex[8] =
    {MSG_BIG_BOY, MSG_BIG_GIRL,
     MSG_LITTLE_BOY, MSG_LITTLE_GIRL,
     MSG_MAN, MSG_WOMAN, MSG_PLANT, MSG_MIME};
    
    if ((tuid = getuser(uident))){
    memcpy(&muser, &xuser, sizeof(muser));
    move(1, 0);
    clrtobot();
    move(1, 0);
    setutmpmode(TQUERY);
    currutmp->destuid = tuid;
    
    j = muser.money;
    for (i = 0; i < 10 && j > 10; i++)
        j /= 10;
    prints("《ID暱稱》%s(%s)%*s《經濟狀況》%s\n",
           muser.userid,
           muser.username,
           26 - strlen(muser.userid) - strlen(muser.username), "",
           money[i]);
    prints("《上站次數》%d次", muser.numlogins);
    move(2, 40);
    prints("《文章篇數》%d篇\n", muser.numposts);
    
    if((uentp = (userinfo_t *) search_ulist(tuid)))
        fri_stat=friend_stat(currutmp, uentp); 
    prints("\033[1;33m《目前動態》%-28.28s\033[m",
           (uentp && isvisible_stat(currutmp, uentp, fri_stat)) ?
           modestring(uentp, 0) : "不在站上");
    
    outs(((uentp && uentp->mailalert) || load_mailalert(muser.userid))
         ? "《私人信箱》有新進信件還沒看\n" :
         "《私人信箱》所有信件都看過了\n");
    prints("《上次上站》%-28.28s《上次故鄉》%s\n",
           Cdate(&muser.lastlogin),
           (muser.lasthost[0] ? muser.lasthost : "(不詳)"));
        if ((uentp && fri_stat&HFM) || HAS_PERM(PERM_SYSOP))
        prints("《 性  別 》%-28.28s《私有財產》%ld 銀兩\n",
           sex[muser.sex % 8],
           muser.money);
    prints("《五子棋戰績》%3d 勝 %3d 敗 %3d 和      "
           "《象棋戰績》%3d 勝 %3d 敗 %3d 和",
           muser.five_win, muser.five_lose, muser.five_tie,
           muser.chc_win, muser.chc_lose, muser.chc_tie);
    showplans(uident);
    pressanykey();
    return FULLUPDATE;
    }
    return DONOTHING;
}

static char t_last_write[200] = "";

void water_scr(water_t *tw, int which, char type)
{
    if( type == 1 ){
    int    i;
    int    colors[] = {33, 37, 33, 37, 33};
    move(8 + which, 28);prints(" ");
    move(8 + which, 28);
    prints("\033[1;37;45m  %c %-14s \033[0m",
           tw->uin ? ' ' : 'x',
           tw->userid);
    for( i = 0 ; i < 5 ; ++i ){
        move(16 + i, 4);
        prints(" ");
        move(16 + i, 4);
        if( tw->msg[ (tw->top - i + 4) % 5 ].last_call_in[0] != 0 )
        prints("\033[0m   \033[1;%d;44m★%-64s\033[0m   \n",
               colors[i],
               tw->msg[ (tw->top - i + 4) % 5 ].last_call_in);
        else
        prints("\033[0m \n");
    }

    move(21, 4);prints(" ");
    move(21, 4);
    prints("\033[0m   \033[1;37;46m%-66s\033[0m   \n",
           tw->msg[5].last_call_in);

    move(0, 0);prints(" ");
    move(0, 0);prints("\033[0m反擊 %s:", tw->userid);
    clrtoeol();
    move(0, strlen(tw->userid) + 6);
    }
    else{
    move(8 + which, 28);
    prints("123456789012345678901234567890");
    //  refresh();
    move(8 + which, 28);
    prints("\033[1;37;44m  %c %-13s \033[0m",
           tw->uin ? ' ' : 'x',
           tw->userid);
    //  refresh();
    }
}

void my_write2(void)
{
    int     i, ch, currstat0;
    char    genbuf[256], msg[80], done = 0, c0, which;
    water_t *tw;
    unsigned        char    mode0;

    wmofo = 0;
    currstat0 = currstat;
    c0 = currutmp->chatid[0];
    mode0 = currutmp->mode;
    currutmp->mode = 0;
    currutmp->chatid[0] = 3;
    currstat = XMODE;

    // init screen
    move(7, 28);
    prints("\033[1;33;46m ↑ 水球反擊對象 ↓\033[0m");
    for( i = 0 ; i < 5 ; ++i )
    if( swater[i] == NULL || swater[i]->pid == 0 )
        break;
    else{
        if( swater[i]->uin &&
        (swater[i]->pid != swater[i]->uin->pid ||
         strcmp(swater[i]->userid, swater[i]->uin->userid)) )
        swater[i]->uin = NULL;
        water_scr(swater[i], i, 0);
    }
    move(15, 4);
    prints("\033[0m \033[1;35m◇\033[1;36m────────────────"
       "─────────────────\033[1;35m◇\033[0m ");
    move(22, 4);
    prints(" \033[1;35m◇\033[1;36m────────────────"
       "─────────────────\033[1;35m◇\033[0m ");
    water_scr(swater[0], 0, 1);
    refresh();

    which = 0;
    do{
    switch( (ch = igetkey()) ){
    case Ctrl('T'):
    case KEY_UP:
        if( water_usies != 1 ){
        water_scr(swater[(int)which], which, 0);
        which = (which - 1 + water_usies) % water_usies;
        water_scr(swater[(int)which], which, 1);
        refresh();
        }
        break;

    case KEY_DOWN:
    case Ctrl('R'):
        if( water_usies != 1 ){
        water_scr(swater[(int)which], which, 0);
        which = (which + 1 + water_usies) % water_usies;
        water_scr(swater[(int)which], which, 1);
        refresh();
        }
        break;

    case KEY_LEFT:
        done = 1;
        break;

    default:
        done = 1;
        tw = swater[(int)which];

        if( !tw->uin )
        break;

        if( ch != '\r' && ch != '\n' ){
        msg[0] = ch, msg[1] = 0;
        }
        else
        msg[0] = 0;
        move(0, 0);prints("\033[m"); clrtoeol();
        refresh();
        sprintf(genbuf, "攻擊 %s:", tw->userid);
        if( !oldgetdata(0, 0, genbuf, msg,
                80-strlen(tw->userid)-6, DOECHO) )
        break;

        if( my_write(tw->pid, msg, tw->userid, 4, tw->uin) )
        strncpy(tw->msg[5].last_call_in, t_last_write,
            sizeof(tw->msg[5].last_call_in));
        break;
    }
    } while( !done );

    currstat = currstat0;
    currutmp->chatid[0] = c0;
    currutmp->mode = mode0;
    if( wmofo == 1 )
    write_request(0);
    wmofo = -1;
}

/* 
   被呼叫的時機:
   1. 丟群組水球 flag = 1 (pre-edit)
   2. 回水球     flag = 0
   3. 上站aloha  flag = 2 (pre-edit)
   4. 廣播       flag = 3 if SYSOP, otherwise flag = 1 (pre-edit)
   5. 丟水球     flag = 0
   6. my_write2  flag = 4 (pre-edit) but confirm
*/
int my_write(pid_t pid, char *prompt, char *id, int flag, userinfo_t *puin)
{
    int len, currstat0 = currstat, fri_stat;
    char msg[80], destid[IDLEN + 1];
    char genbuf[200], buf[200], c0 = currutmp->chatid[0];
    unsigned char mode0 = currutmp->mode;
    time_t now;
    struct tm *ptime;
    userinfo_t *uin;
    uin = (puin != NULL) ? puin : (userinfo_t *)search_ulist_pid(pid);
    strcpy(destid, id);
    
    if(!uin && !(flag == 0 && water_which->count> 0)) {
      outmsg("\033[1;33;41m糟糕! 對方已落跑了(不在站上)! \033[37m~>_<~\033[m");
    clrtoeol();
    refresh();
    watermode = -1;
    return 0;
    }
    currutmp->mode = 0;
    currutmp->chatid[0] = 3;
    currstat = XMODE;
    
    time(&now);
    ptime = localtime(&now);
    
    if(flag == 0) {
    /* 一般水球 */
    watermode = 0;
    if(!(len = getdata(0, 0, prompt, msg, 56, DOECHO))) {
        outmsg("\033[1;33;42m算了! 放你一馬...\033[m");
        clrtoeol();
        refresh();
        currutmp->chatid[0] = c0;
        currutmp->mode = mode0;
        currstat = currstat0;
        watermode = -1;
        return 0;
    }
    
    if(watermode > 0) {
        int i;
        
        i = (water_which->top- watermode + MAX_REVIEW) % MAX_REVIEW;
        uin = (userinfo_t *)search_ulist_pid(water_which->msg[i].pid);
        strcpy(destid, water_which->msg[i].userid);
    }
    } else {
    /* pre-edit 的水球 */
    strcpy(msg, prompt);
    len = strlen(msg);
    }
    
    strip_ansi(msg, msg, 0);
    if(uin && *uin->userid && (flag == 0 || flag == 4)) {
    sprintf(buf, "丟給 %s : %s [Y/n]?", uin->userid, msg);
    getdata(0, 0, buf, genbuf, 3, LCECHO);
    if(genbuf[0] == 'n') {
        outmsg("\033[1;33;42m算了! 放你一馬...\033[m");
        clrtoeol();
        refresh();
        currutmp->chatid[0] = c0;
        currutmp->mode = mode0;
        currstat = currstat0;
        watermode = -1;
        return 0;
    }
    }
    
    watermode = -1;
    if(!uin || !*uin->userid || strcasecmp(destid, uin->userid)) {
    outmsg("\033[1;33;41m糟糕! 對方已落跑了(不在站上)! \033[37m~>_<~\033[m");
    clrtoeol();
    refresh();
    currutmp->chatid[0] = c0;
    currutmp->mode = mode0;
    currstat = currstat0;
    return 0;
    }
    
    fri_stat=friend_stat(currutmp, uin); 
    time(&now);
    if(flag != 2) { /* aloha 的水球不用存下來 */
    /* 存到自己的水球檔 */
    if(!fp_writelog){
        sethomefile(genbuf, cuser.userid, fn_writelog);
        fp_writelog = fopen(genbuf, "a");
    }
    if(fp_writelog) {
        fprintf(fp_writelog, "To %s: %s [%s]\n", 
            uin->userid, msg, Cdatelite(&now));
        snprintf(t_last_write, 66, "To %s: %s", uin->userid, msg);
    }
    }
    
    if(flag == 3 && uin->msgcount) {
    /* 不懂 */
    uin->destuip = currutmp - &utmpshm->uinfo[0];
    uin->sig = 2;
    if(uin->pid > 0) kill(uin->pid, SIGUSR1);
    } else if(flag != 2 &&
          !HAS_PERM(PERM_SYSOP) &&
          (uin->pager == 3 || 
           uin->pager == 2 || 
           (uin->pager == 4 &&
        !(fri_stat & HFM))))
    outmsg("\033[1;33;41m糟糕! 對方防水了! \033[37m~>_<~\033[m");
    else {
    if(uin->msgcount < MAX_MSGS) {
        unsigned char pager0 = uin->pager;
        
        uin->pager = 2;
        uin->msgs[uin->msgcount].pid = currpid;
        strcpy(uin->msgs[uin->msgcount].userid, cuser.userid);
        strcpy(uin->msgs[uin->msgcount++].last_call_in, msg);
        uin->pager = pager0;
    } else if (flag != 2)
        outmsg("\033[1;33;41m糟糕! 對方不行了! (收到太多水球) \033[37m@_@\033[m");
    
    if(uin->msgcount >= 1 && (uin->pid <= 0 || kill(uin->pid, SIGUSR2) == -1) && flag != 2)
        outmsg("\033[1;33;41m糟糕! 沒打中! \033[37m~>_<~\033[m");
    else if(uin->msgcount == 1 && flag != 2)
        outmsg("\033[1;33;44m水球砸過去了! \033[37m*^o^*\033[m");
    else if(uin->msgcount > 1 && uin->msgcount < MAX_MSGS && flag != 2)
        outmsg("\033[1;33;44m再補上一粒! \033[37m*^o^*\033[m");
    }
    
    clrtoeol();
    refresh();
    
    currutmp->chatid[0] = c0;
    currutmp->mode = mode0;
    currstat = currstat0;
    return 1;
}

void t_display_new(void)
{
    static int t_display_new_flag=0;
    int i, off=2;
    if (t_display_new_flag)
    return;
    else
    t_display_new_flag = 1;

    if( WATERMODE(WATER_ORIG) )
    water_which = &water[0];
    else
    off =3;

    if (water[0].count && watermode > 0){
    move(1, 0);
    outs("───────水─球─回─顧───");
    outs(WATERMODE(WATER_ORIG)                           ?
         "──────用[Ctrl-R Ctrl-T]鍵切換─────" :
         "用[Ctrl-R Ctrl-T Ctrl-F Ctrl-G ]鍵切換────");
    if( WATERMODE(WATER_NEW) ){
        move(2, 0);
        clrtoeol();
        for (i = 0; i<6 ; i++){
        if(i>0)
                  if(swater[i-1])
                   {
                    if(swater[i-1]->uin &&
                          swater[i-1]->uin->pid!=swater[i-1]->pid)
                               swater[i-1]->uin=NULL;
            prints("%s%c%-13.13s\033[m",
                           swater[i-1]!=water_which? "" :
               swater[i-1]->uin?"\033[1;33;47m":
               "\033[1;33;45m",
                           !swater[i-1]->uin?'#':' ',
                           swater[i-1]->userid);
                   }
                  else
                    prints("              ");
        else
            prints("%s 全部  \033[m",
               water_which==&water[0]?"\033[1;33;47m ":
               " "
               );
        }
    }
    
    for (i = 0; i < water_which->count; i++){
        int a = (water_which->top - i - 1 + MAX_REVIEW) % MAX_REVIEW,
        len = 75-strlen(water_which->msg[a].last_call_in)
        -strlen(water_which->msg[a].userid);
        if(len<0) len=0;
        
        move(i + (WATERMODE(WATER_ORIG)?2:3), 0);
        clrtoeol();
        if (watermode - 1 != i)
        prints("\033[1;33;46m %s \033[37;45m %s \033[m%*s",
               water_which->msg[a].userid,
               water_which->msg[a].last_call_in, len,
               "");
        else
        prints("\033[1;44m>\033[1;33;47m%s "
               "\033[37;45m %s \033[m%*s",
               water_which->msg[a].userid, 
               water_which->msg[a].last_call_in,
               len,"");
    }
    
    if (t_last_write[0]){
        move(i + off, 0);
        clrtoeol();
        prints(t_last_write);
        i++;
    }
    move(i + off, 0);
    outs("──────────────────────"
         "─────────────────");
    if( WATERMODE(WATER_NEW) )
     while( i++ <= water[0].count ) {
        move(i + off, 0);
        clrtoeol();
    }
    }

    t_display_new_flag = 0;
}

int t_display(void)
{
    char genbuf[200], ans[4];
    if(fp_writelog){
    fclose(fp_writelog);
    fp_writelog=NULL;
    }
    setuserfile(genbuf, fn_writelog);
    if (more(genbuf, YEA) != -1){
    getdata(b_lines - 1, 0, "清除(C) 移至備忘錄(M) 保留(R) (C/M/R)?[R]",
        ans, 3, LCECHO);
    if (*ans == 'm'){
        fileheader_t mymail;
        char title[128], buf[80];

        sethomepath(buf, cuser.userid);
        stampfile(buf, &mymail);

        mymail.savemode = 'H';  /* hold-mail flag */
        mymail.filemode = FILE_READ;
        strcpy(mymail.owner, "[備.忘.錄]");
        strcpy(mymail.title, "熱線\033[37;41m記錄\033[m");
        sethomedir(title, cuser.userid);
        Rename(genbuf, buf);
        append_record(title, &mymail, sizeof(mymail));
    }
    else if (*ans == 'c')
        unlink(genbuf);
    return FULLUPDATE;
    }
    return DONOTHING;
}

static void do_talk_nextline(talkwin_t * twin)
{
    twin->curcol = 0;
    if (twin->curln < twin->eline)
    ++(twin->curln);
    else
    region_scroll_up(twin->sline, twin->eline);
    move(twin->curln, twin->curcol);
}

static void do_talk_char(talkwin_t * twin, int ch)
{
    extern screenline_t *big_picture;
    screenline_t *line;
    int i;
    char ch0, buf[81];

    if (isprint2(ch)){
    ch0 = big_picture[twin->curln].data[twin->curcol];
    if (big_picture[twin->curln].len < 79)
        move(twin->curln, twin->curcol);
    else
        do_talk_nextline(twin);
    outc(ch);
    ++(twin->curcol);
    line = big_picture + twin->curln;
    if (twin->curcol < line->len){ /* insert */
        ++(line->len);
        memcpy(buf, line->data + twin->curcol, 80);
        save_cursor();
        do_move(twin->curcol, twin->curln);
        ochar(line->data[twin->curcol] = ch0);
        for (i = twin->curcol + 1; i < line->len; i++)
        ochar(line->data[i] = buf[i - twin->curcol - 1]);
        restore_cursor();
    }
    line->data[line->len] = 0;
    return;
    }
    
    switch (ch){
    case Ctrl('H'):
    case '\177':
    if (twin->curcol == 0)
        return;
    line = big_picture + twin->curln;
    --(twin->curcol);
    if (twin->curcol < line->len){
        --(line->len);
        save_cursor();
        do_move(twin->curcol, twin->curln);
        for (i = twin->curcol; i < line->len; i++)
        ochar(line->data[i] = line->data[i + 1]);
        line->data[i] = 0;
        ochar(' ');
        restore_cursor();
    }
    move(twin->curln, twin->curcol);
    return;
    case Ctrl('D'):
    line = big_picture + twin->curln;
    if (twin->curcol < line->len){
        --(line->len);
        save_cursor();
        do_move(twin->curcol, twin->curln);
        for (i = twin->curcol; i < line->len; i++)
        ochar(line->data[i] = line->data[i + 1]);
        line->data[i] = 0;
        ochar(' ');
        restore_cursor();
    }
    return;
    case Ctrl('G'):
    bell();
    return;
    case Ctrl('B'):
    if (twin->curcol > 0){
        --(twin->curcol);
        move(twin->curln, twin->curcol);
    }
    return;
    case Ctrl('F'):
    if (twin->curcol < 79){
        ++(twin->curcol);
        move(twin->curln, twin->curcol);
    }
    return;
    case KEY_TAB:
    twin->curcol += 8;
    if (twin->curcol > 80)
        twin->curcol = 80;
    move(twin->curln, twin->curcol);
    return;
    case Ctrl('A'):
    twin->curcol = 0;
    move(twin->curln, twin->curcol);
    return;
    case Ctrl('K'):
    clrtoeol();
    return;
    case Ctrl('Y'):
    twin->curcol = 0;
    move(twin->curln, twin->curcol);
    clrtoeol();
    return;
    case Ctrl('E'):
    twin->curcol = big_picture[twin->curln].len;
    move(twin->curln, twin->curcol);
    return;
    case Ctrl('M'):
    case Ctrl('J'):
    line = big_picture + twin->curln;
    strncpy(buf, line->data, line->len);
    buf[line->len] = 0;
    do_talk_nextline(twin);
    break;
    case Ctrl('P'):
    line = big_picture + twin->curln;
    strncpy(buf, line->data, line->len);
    buf[line->len] = 0;
    if (twin->curln > twin->sline){
        --(twin->curln);
        move(twin->curln, twin->curcol);
    }
    break;
    case Ctrl('N'):
    line = big_picture + twin->curln;
    strncpy(buf, line->data, line->len);
    buf[line->len] = 0;
    if (twin->curln < twin->eline){
        ++(twin->curln);
        move(twin->curln, twin->curcol);
    }
    break;
    }
    trim(buf);
    if (*buf)
    fprintf(flog, "%s%s: %s%s\n",
        (twin->eline == b_lines - 1) ? "\033[1;35m" : "",
        (twin->eline == b_lines - 1) ?
        getuserid(currutmp->destuid) : cuser.userid, buf,
        (ch == Ctrl('P')) ? "\033[37;45m(Up)\033[m" : "\033[m");
}

static void do_talk(int fd)
{
    struct talkwin_t mywin, itswin;
    char mid_line[128], data[200];
    int i, datac, ch;
    int im_leaving = 0;
    FILE *log;
    struct tm *ptime;
    time_t now;
    char genbuf[200], fpath[100];

    time(&now);
    ptime = localtime(&now);

    sethomepath(fpath, cuser.userid);
    strcpy(fpath, tempnam(fpath, "talk_"));
    flog = fopen(fpath, "w");

    setuserfile(genbuf, fn_talklog);

    if ((log = fopen(genbuf, "w")))
    fprintf(log, "[%d/%d %d:%02d] & %s\n",
        ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour,
        ptime->tm_min, save_page_requestor);
    setutmpmode(TALK);

    ch = 58 - strlen(save_page_requestor);
    sprintf(genbuf, "%s【%s", cuser.userid, cuser.username);
    i = ch - strlen(genbuf);
    if (i >= 0)
    i = (i >> 1) + 1;
    else{
    genbuf[ch] = '\0';
    i = 1;
    }
    memset(data, ' ', i);
    data[i] = '\0';

    sprintf(mid_line, "\033[1;46;37m  談天說地  \033[45m%s%s】"
        " 與  %s%s\033[0m", data, genbuf, save_page_requestor, data);

    memset(&mywin, 0, sizeof(mywin));
    memset(&itswin, 0, sizeof(itswin));

    i = b_lines >> 1;
    mywin.eline = i - 1;
    itswin.curln = itswin.sline = i + 1;
    itswin.eline = b_lines - 1;

    clear();
    move(i, 0);
    outs(mid_line);
    move(0, 0);

    add_io(fd, 0);

    while (1){
    ch = igetkey();
    if (ch == I_OTHERDATA){
        datac = recv(fd, data, sizeof(data), 0);
        if (datac <= 0)
        break;
        for (i = 0; i < datac; i++)
        do_talk_char(&itswin, data[i]);
    }
    else{
        if (ch == Ctrl('C')){
        if (im_leaving)
            break;
        move(b_lines, 0);
        clrtoeol();
        outs("再按一次 Ctrl-C 就正式中止談話囉!");
        im_leaving = 1;
        continue;
        }
        if (im_leaving){
        move(b_lines, 0);
        clrtoeol();
        im_leaving = 0;
        }
        switch (ch){
        case KEY_LEFT:  /* 把2byte的鍵改為一byte */
        ch = Ctrl('B');
        break;
        case KEY_RIGHT:
        ch = Ctrl('F');
        break;
        case KEY_UP:
        ch = Ctrl('P');
        break;
        case KEY_DOWN:
        ch = Ctrl('N');
        break;
        }
        data[0] = (char) ch;
        if (send(fd, data, 1, 0) != 1)
        break;
        if (log)
        fprintf(log, "%c", (ch == Ctrl('M')) ? '\n' : (char) *data);
        do_talk_char(&mywin, *data);
    }
    }
    if (log)
    fclose(log);

    add_io(0, 0);
    close(fd);

    if (flog){
    char ans[4];
    extern screenline_t *big_picture;
    extern unsigned char scr_lns;
    int i;

    time(&now);
    fprintf(flog, "\n\033[33;44m離別畫面 [%s] ...     \033[m\n",
        Cdatelite(&now));
    for (i = 0; i < scr_lns; i++)
        fprintf(flog, "%.*s\n", big_picture[i].len, big_picture[i].data);
    fclose(flog);
    more(fpath, NA);
    getdata(b_lines - 1, 0, "清除(C) 移至備忘錄(M). (C/M)?[C]",
        ans, 4, LCECHO);
    if (*ans == 'm'){
        fileheader_t mymail;
        char title[128];

        sethomepath(genbuf, cuser.userid);
        stampfile(genbuf, &mymail);
        mymail.savemode = 'H';  /* hold-mail flag */
        mymail.filemode = FILE_READ;
        strcpy(mymail.owner, "[備.忘.錄]");
        sprintf(mymail.title, "對話記錄 \033[1;36m(%s)\033[m",
            getuserid(currutmp->destuid));
        sethomedir(title, cuser.userid);
        Rename(fpath, genbuf);
        append_record(title, &mymail, sizeof(mymail));
    }
    else
        unlink(fpath);
    flog = 0;
    }
    setutmpmode(XINFO);
}

#define lockreturn(unmode, state) if(lockutmpmode(unmode, state)) return 

static void my_talk(userinfo_t * uin, int fri_stat) {
    int sock, msgsock, length, ch, error = 0;
    struct sockaddr_in server;
    pid_t pid;
    char c;
    char genbuf[4];

    unsigned char mode0 = currutmp->mode;

    ch = uin->mode;
    strcpy(currauthor, uin->userid);

    if (ch == EDITING || ch == TALK || ch == CHATING || ch == PAGE ||
    ch == MAILALL || ch == MONITOR || ch == M_FIVE || ch == CHC ||
    (!ch && (uin->chatid[0] == 1 || uin->chatid[0] == 3)) ||
    uin->lockmode == M_FIVE || uin->lockmode == CHC){
    outs("人家在忙啦");
    }
    else if (!HAS_PERM(PERM_SYSOP) &&
         (((fri_stat& HRM) && !(fri_stat& HFM)) ||
          ((!uin->pager) && !(fri_stat & HFM)) ) ){
    outs("對方關掉呼叫器了");
    }
    else if (!HAS_PERM(PERM_SYSOP) &&
         (((fri_stat & HRM) && !(fri_stat& HFM)) || uin->pager == 2 )) {
    outs("對方拔掉呼叫器了");
    }
    else if (!HAS_PERM(PERM_SYSOP) &&
         !(fri_stat & HFM) && uin->pager == 4){
    outs("對方只接受好友的呼叫");
    }
    else if (!(pid = uin->pid) /*|| (kill(pid, 0) == -1) */ ){
    //resetutmpent();
    outs(msg_usr_left);
    }
    else{
    showplans(uin->userid);
    getdata(2, 0, "要和他(她) (T)談天(F)下五子棋(P)鬥寵物"
        "(C)下象棋(D)下暗棋(N)沒事找錯人了?[N] ", genbuf, 4, LCECHO);
    switch (*genbuf){
    case 'y':
    case 't':
        uin->sig = SIG_TALK;
        break;
    case 'f':
        lockreturn(M_FIVE, LOCK_THIS);
        uin->sig = SIG_GOMO;
        break;
    case 'c':
        lockreturn(CHC, LOCK_THIS);
        uin->sig = SIG_CHC;
        break;
    case 'd':
        uin->sig = SIG_DARK;
        break;
    case 'p':
        reload_chicken();
        getuser(uin->userid);
        if (uin->lockmode == CHICKEN || currutmp->lockmode == CHICKEN)
        error = 1;
        if (!cuser.mychicken.name[0] || !xuser.mychicken.name[0])
        error = 2;
        if (error){
        outmsg(error == 2 ? "並非兩人都養寵物" :
               "有一方的寵物正在使用中");
        bell();
        refresh();
        sleep(1);
        return;
        }
        uin->sig = SIG_PK;
        break;
    default:
        return;
    }

    uin->turn = 1;
    currutmp->turn = 0;
    strcpy(uin->mateid, currutmp->userid);
    strcpy(currutmp->mateid, uin->userid);

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0){
        perror("sock err");
        unlockutmpmode();
        return;
    }
    server.sin_family = PF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = 0;
    if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0){
        close(sock);
        perror("bind err");
        unlockutmpmode();
        return;
    }
    length = sizeof(server);
    if (getsockname(sock, (struct sockaddr *) &server, &length) < 0){
        close(sock);
        perror("sock name err");
        unlockutmpmode();
        return;
    }
    currutmp->sockactive = YEA;
    currutmp->sockaddr = server.sin_port;
    currutmp->destuid = uin->uid;
    setutmpmode(PAGE);
    uin->destuip = currutmp - &utmpshm->uinfo[0];
    if(pid > 0) kill(pid, SIGUSR1);
    clear();
    prints("正呼叫 %s.....\n鍵入 Ctrl-D 中止....", uin->userid);

    listen(sock, 1);
    add_io(sock, 5);
    while (1){
        ch = igetch();
        if (ch == I_TIMEOUT){
        ch = uin->mode;
        if (!ch && uin->chatid[0] == 1 &&
            uin->destuip == currutmp - &utmpshm->uinfo[0]){
            bell();
            outmsg("對方回應中...");
            refresh();
        }
        else if (ch == EDITING || ch == TALK || ch == CHATING ||
             ch == PAGE || ch == MAILALL || ch == MONITOR ||
             ch == M_FIVE || ch == CHC ||
             (!ch && (uin->chatid[0] == 1 ||
                  uin->chatid[0] == 3))){
            add_io(0, 0);
            close(sock);
            currutmp->sockactive = currutmp->destuid = 0;
            outmsg("人家在忙啦");
            pressanykey();
            unlockutmpmode();
            return;
        }
        else{
#ifdef linux
            add_io(sock, 20);   /* added for linux... achen */
#endif
            move(0, 0);
            outs("再");
            bell();

            uin->destuip = currutmp - &utmpshm->uinfo[0];
            if(pid <= 0 || kill(pid, SIGUSR1) == -1){
#ifdef linux
            add_io(sock, 20);   /* added 4 linux... achen */
#endif
            outmsg(msg_usr_left);
            refresh();
            pressanykey();
            unlockutmpmode();
            return;
            }
            continue;
        }
        }
        
        if (ch == I_OTHERDATA)
        break;

        if (ch == '\004'){
        add_io(0, 0);
        close(sock);
        currutmp->sockactive = currutmp->destuid = 0;
        unlockutmpmode();
        return;
        }
    }

    msgsock = accept(sock, (struct sockaddr *) 0, (int *) 0);
    if (msgsock == -1){
        perror("accept");
        unlockutmpmode();
        return;
    }
    add_io(0, 0);
    close(sock);
    currutmp->sockactive = NA;
    read(msgsock, &c, sizeof c);

    if (c == 'y'){
        sprintf(save_page_requestor, "%s (%s)",
            uin->userid, uin->username);
        /* gomo */
        switch (uin->sig){
        case SIG_DARK:
        main_dark(msgsock, uin);
        break;
        case SIG_PK:
        chickenpk(msgsock);
        break;
        case SIG_GOMO:
        gomoku(msgsock);
        break;
        case SIG_CHC:
        chc(msgsock);
        break;
        case SIG_TALK:
        default:
        do_talk(msgsock);
        }
    }
    else{
        move(9, 9);
        outs("【回音】 ");
        switch (c){
        case 'a':
        outs("我現在很忙,請等一會兒再 call 我,好嗎?");
        break;
        case 'b':
        prints("對不起,我有事情不能跟你 %s....", sig_des[uin->sig]);
        break;
        case 'd':
        outs("我要離站囉..下次再聊吧..........");
        break;
        case 'c':
        outs("請不要吵我好嗎?");
        break;
        case 'e':
        outs("找我有事嗎?請先來信唷....");
        break;
        case 'f':
        {
            char msgbuf[60];
            
            read(msgsock, msgbuf, 60);
            prints("對不起,我現在不能跟你 %s,因為\n", sig_des[uin->sig]);
            move(10, 18);
            outs(msgbuf);
        }
        break;
        case '1':
        prints("%s?先拿100銀兩來..", sig_des[uin->sig]);
        break;
        case '2':
        prints("%s?先拿1000銀兩來..", sig_des[uin->sig]);
        break;
        default:
        prints("我現在不想 %s 啦.....:)", sig_des[uin->sig]);
        }
        close(msgsock);
    }
    }
    currutmp->mode = mode0;
    currutmp->destuid = 0;
    unlockutmpmode();
    pressanykey();
}

/* 選單式聊天介面 */
#define US_PICKUP       1234
#define US_RESORT       1233
#define US_ACTION       1232
#define US_REDRAW       1231

static void t_showhelp() {
    clear();

    outs("\033[36m【 休閒聊天使用說明 】\033[m\n\n"
     "(←)(e)         結束離開             (h)             看使用說明\n"
     "(↑)/(↓)(n)    上下移動             (TAB)           切換排序方式\n"
     "(PgUp)(^B)      上頁選單             ( )(PgDn)(^F)   下頁選單\n"
     "(Hm)/($)(Ed)    首/尾                (S)             "
     "來源/好友描述/戰績 切換\n"
     "(m)             寄信                 (q/c)           "
     "查詢網友/寵物\n"
     "(r)             閱\讀信件            (l)             看上次熱訊\n"
     "(f)             全部/好友列表        (數字)          跳至該使用者\n"
     "(p)             切換呼叫器           (g/i)           給錢/切換心情\n"
     "(a/d/o)         好友 增加/刪除/修改  (/)(s)          網友ID/暱稱搜尋");

    if (HAS_PERM(PERM_PAGE)){
    outs("\n\n\033[36m【 交談專用鍵 】\033[m\n\n"
         "(→)(t)(Enter)  跟他/她聊天\n"
         "(w)             熱線 Call in\n"
         "(W)切換水球方式 一般 / 進階 / 未來\n"
         "(b)             對好友廣播 (一定要在好友列表中)\n"
         "(^R)            即時回應 (有人 Call in 你時)");
    }

    if (HAS_PERM(PERM_SYSOP)){
    outs("\n\n\033[36m【 站長專用鍵 】\033[m\n\n");
    if (HAS_PERM(PERM_SYSOP))
        outs("(u)/(H)         設定使用者資料/切換隱形模式\n");
    outs("(R)/(K)         查詢使用者的真實姓名/把壞蛋踢出去\n");
    }
    pressanykey();
}

static int listcuent(userinfo_t * uentp)
{
    if((!uentp->invisible || HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_SEECLOAK)))
    AddNameList(uentp->userid);
    return 0;
}

static void creat_list()
{
    CreateNameList();
    apply_ulist(listcuent);
}

static int search_pickup(int num, int actor, pickup_t pklist[])
{
    char genbuf[IDLEN + 2];

    move(1, 0);
    creat_list();
    namecomplete(msg_uid, genbuf);
    if (genbuf[0]){
    int n = (num + 1) % actor;
    while (n != num){
        if (!strcasecmp(pklist[n].ui->userid, genbuf))
        return n;
        if (++n >= actor)
        n = 0;
    }
    }
    return -1;
}

/* Kaede show friend description */
static char *friend_descript(char *uident) {
    static char *space_buf = "                    ";
    static char desc_buf[80];
    char fpath[80], name[IDLEN + 2], *desc, *ptr;
    int len, flag;
    FILE *fp;
    char genbuf[200];

    setuserfile(fpath, friend_file[0]);

    if ((fp = fopen(fpath, "r"))){
    sprintf(name, "%s ", uident);
    len = strlen(name);
    desc = genbuf + 13;

    while ((flag = (int) fgets(genbuf, STRLEN, fp))){
        if (!memcmp(genbuf, name, len)){
        if ((ptr = strchr(desc, '\n')))
            ptr[0] = '\0';
        if (desc)
            break;
        }
    }
    fclose(fp);
    if (desc && flag)
        strcpy(desc_buf, desc);
    else
        return space_buf;

    return desc_buf;
    }
    else
    return space_buf;
}

static char *descript(int show_mode, userinfo_t * uentp, time_t diff,
              fromcache_t * fcache)
{
    switch (show_mode){
    case 1:
    return friend_descript(uentp->userid);
    case 0:
    return (((uentp->pager != 2 && uentp->pager != 3 && diff) ||
         HAS_PERM(PERM_SYSOP)) ?
#ifdef WHERE
        uentp->from_alias ? fcache->replace[uentp->from_alias] :
        uentp->from
#else
        uentp->from
#endif
        : "*");
    case 2:
    sprintf(description, "%3d/%3d/%3d", uentp->five_win,
        uentp->five_lose, uentp->five_tie);
    description[20] = 0;
    return description;
    default:
    syslog(LOG_WARNING, "damn!!! what's wrong?? show_mode = %d",
           show_mode);
    return "";
    }
}

static int pickup_user_cmp(time_t now, int sortedway, int cmp_fri, 
    pickup_t pklist[], int *bfriends_number, int *ifh_number,
    int *hfm_number,int *irh_number, char *keyword)
{
    int i, fri_stat, is_friend, count=0, diff;
    userinfo_t *uentp;
    for (i=0;i<utmpshm->number;i++){
        uentp = (utmpshm->sorted[utmpshm->currsorted][sortedway][i]);
    if (!uentp || !uentp->pid) continue;
    fri_stat = friend_stat(currutmp, uentp);
    if(uentp->uid==currutmp->uid)
        fri_stat = fri_stat|IFH|HFM;
        is_friend = (fri_stat & IRH) && !(fri_stat & IFH) ? 0 :
            fri_stat & ST_FRIEND;
    if (!isvisible_stat(currutmp, uentp, fri_stat) || 
                  ((cmp_fri==1 && !is_friend) ||
                  (cmp_fri==-1 && is_friend)) ||
                  (keyword[0] && !strcasestr(uentp->username,keyword))
          ) continue;
    if (bfriends_number && fri_stat & IBH) (*bfriends_number)++;
    if (ifh_number && fri_stat & IFH) (*ifh_number)++;
    if (hfm_number && fri_stat & HFM) (*hfm_number)++;
    if (irh_number && fri_stat & IRH) (*irh_number)++;
#ifdef SHOW_IDLE_TIME
    diff = now - uentp->lastact;
#ifdef DOTIMEOUT
        /* prevent fault /dev mount from kicking out users */
    if ((diff > curr_idle_timeout + 10) &&
        (diff < 60 * 60 * 24 * 5)){
        if ((uentp->pid <= 0 || kill(uentp->pid, SIGHUP) == -1) &&
        (errno == ESRCH))
        purge_utmp(uentp);
        continue;
    }
#endif
    pklist[count].idle = diff;
#endif
    pklist[count].friend = fri_stat;
    pklist[count].ui = uentp;
    count++;
    }
    return count;
}

static int cmputmpfriend(const void *i, const void *j)
{
    if((((pickup_t*)j)->friend&ST_FRIEND)==(((pickup_t*)i)->friend&ST_FRIEND))
    return strcasecmp( ((pickup_t*)i)->ui->userid,
               ((pickup_t*)j)->ui->userid);
    else
    return (((pickup_t*)j)->friend&ST_FRIEND) -
           (((pickup_t*)i)->friend&ST_FRIEND);
} 

static void pickup_user(void)
{
    static int real_name = 0;
    static int show_mode = 0;
    static int show_uid = 0;
    static int show_board = 0;
    static int show_pid = 0;
    static int num = 0;
    char genbuf[200];

#ifdef WHERE
    extern struct fromcache_t *fcache;
#endif

    register userinfo_t *uentp;
    register pid_t pid0 = 0;    /* Ptt 定位 */
    register int id0 = 0;   /* US_PICKUP時的游標用 */
    register int state = US_PICKUP, ch;
    register int actor = 0, head, foot;
    int fri_stat, bfriends_number, ifh_number, irh_number, hfm_number;
    int savemode = currstat;
    int i, sortedway;           /* 只是loop有用到 */
    time_t diff, freshtime;
    pickup_t pklist[USHM_SIZE]; /* parameter Ptt註 */
/* num : 現在的游標位 */
/* foot: 此頁的腳腳 */
    char buf[20],keyword[13]="";        /* actor:共有多少user */
    char pagerchar[5] = "* -Wf";
    char *msg_pickup_way[PICKUP_WAYS] =
    {
        "嗨! 朋友",
    "網友代號",
    "網友動態",
    "發呆時間",
    "來自何方",
    "五子棋  ",
//  "女士優先"
    };
    char *MODE_STRING[MAX_SHOW_MODE] =
    {
    "故鄉",
    "好友描述",
    "五子棋戰績"
    };
    char
    *Mind[] =
    {"   ",
     "^-^", "^_^", "Q_Q", "@_@", "/_\\", "=_=", "-_-", "-.-", ">_<",
     "-_+", "!_!", "o_o", "z_Z", "O_O", "O.O", "$_$", "^*^", "O_<",
     "喜!", "怒!", "哀!", "樂!", ":) ", ":( ", ":~ ", ":q ", ":O ",
     ":D ", ":p ", ";) ", ":> ", ";> ", ":< ", ":)~", ":D~", ">< ",
     "^^;", "^^|", "哭;",  NULL};
     
    while (1) {
    if (utmpshm->uptime > freshtime || state == US_PICKUP ||
            state ==US_RESORT){
        state = US_PICKUP;
        time(&freshtime);
        ifh_number=hfm_number=irh_number=bfriends_number = actor = ch = 0;
            if(pickup_way==0)
            sortedway=0;
            else
        sortedway=pickup_way-1;

        //qsort(pklist,actor,sizeof(pickup_t),cmputmpfriend);
            if(pickup_way==0 || (cuser.uflag & FRIEND_FLAG)){
                actor=pickup_user_cmp(freshtime, sortedway, 1, 
                      pklist, &bfriends_number, &ifh_number,
                      &hfm_number, NULL, keyword);
                if(sortedway==0)
            qsort(pklist,actor,sizeof(pickup_t),cmputmpfriend);
                if(!(cuser.uflag & FRIEND_FLAG))
            actor=pickup_user_cmp(freshtime, sortedway, -1, 
                      pklist+actor, NULL, NULL, NULL,
                      &irh_number, keyword);
        }
            else{
                actor=pickup_user_cmp(freshtime, sortedway, 0, 
                      pklist, &bfriends_number, &ifh_number,
                      &hfm_number, &irh_number, keyword);
        }


        if (!actor){
                if(keyword[0]){
            mprints(b_lines-1,0,
                "搜尋不到任何人 !!");
            keyword[0]=0;
            pressanykey();
            continue;
        }
        getdata(b_lines - 1, 0,
            "你的朋友還沒上站,要看看一般網友嗎(Y/N)?[Y]",
            genbuf, 4, LCECHO);
        if (genbuf[0] != 'n'){
            cuser.uflag &= ~FRIEND_FLAG;
            continue;
        }
        return;
        }
    }
    if (state >= US_ACTION){
        showtitle((cuser.uflag & FRIEND_FLAG) ? "好友列表" : "休閒聊天",
              BBSName);
        prints("  排序:[%s] 上站人數:%-4d\033[1;32m我的朋友:%-3d"
           "\033[33m與我為友:%-3d\033[36m板友:%-4d\033[31m壞人:"
           "%-2d\033[m\n"
           "\033[7m  %s P%c代號         %-17s%-17s%-13s%-10s\033[m\n",
           msg_pickup_way[pickup_way], actor, ifh_number,
           hfm_number,  bfriends_number, irh_number,
           show_uid ? "UID" : "No.", 
          (HAS_PERM(PERM_SEECLOAK) || HAS_PERM(PERM_SYSOP)) ? 'C' : ' ',
           real_name ? "姓名" : "暱稱",
           MODE_STRING[show_mode],
           show_board ? "Board" : "動態",
           show_pid ? "       PID" : "備註  發呆"
        );
    }
    else{
        move(3, 0);
        clrtobot();
    }
    if (pid0)
        for (ch = 0; ch < actor; ch++){
        if (pid0 == (pklist[ch].ui)->pid &&
            id0 == 256 * pklist[ch].ui->userid[0] +
            pklist[ch].ui->userid[1]){
            num = ch;
        }
        }
    if (num < 0)
        num = 0;
    else if (num >= actor)
        num = actor - 1;
    head = (num / p_lines) * p_lines;
    foot = head + p_lines;
    if (foot > actor)
        foot = actor;
    for (ch = head; ch < foot; ch++){
        uentp = pklist[ch].ui;

        if (!uentp->pid){
            prints("%5d   < 離站中..>\n",ch);
        continue;
        }
#ifdef SHOW_IDLE_TIME
        diff = pklist[ch].idle;
        if (diff > 59990) diff = 59990;   /* Doma: 以免一大串的發呆時間 */
        if (diff > 0)
        sprintf(buf, "%3ld'%02ld", diff / 60, diff % 60);
        else
        buf[0] = '\0';
#else
        buf[0] = '\0';
#endif
            i = pklist[ch].friend;
#ifdef SHOWPID
        if (show_pid)
        sprintf(buf, "%6d", uentp->pid);
#endif
        if (PERM_HIDE(uentp))
        state = 9;
        else if(currutmp == uentp)
        state =10;
        else if(i & IRH && !(i & IFH))
            state = 8;
        else
        state =(i&ST_FRIEND)>>2;
        diff = uentp->pager & !(i&HRM);
        prints("%5d %c%c%s%-13s%-17.16s\033[m%-17.16s%-13.13s"
                 "\33[33m%-4.4s\33[m%s\n",
#ifdef SHOWUID
           show_uid ? uentp->uid :
#endif
           (ch + 1),
           (i & HRM) ? 'X' :
           pagerchar[uentp->pager % 5],
           (uentp->invisible ? ')' : ' '),
              fcolor[state],
           /* %s */
           uentp->userid,

           /* %-13s 暱稱 */
#ifdef REALINFO
           real_name ? uentp->realname :
#endif
           uentp->username,
           /* %-17.16s 故鄉 */
           descript(show_mode, uentp, diff, fcache),

           /* %-17.16s 看板 */
#ifdef SHOWBOARD
           show_board ? (uentp->brc_id == 0 ? "" :
                 bcache[uentp->brc_id - 1].brdname) :
#endif
           /* %-13.13s */
           modestring(uentp, 0),
           /* %4s 備註 */
           ((uentp->userlevel & PERM_VIOLATELAW) ? "通緝" :
            (uentp->birth ? "壽星" :
             Mind[uentp->mind])),
           /* %s 發呆 */
           buf);
    }
    if (state == US_PICKUP)
        continue;

    move(b_lines, 0);
    outs("\033[31;47m(TAB/f)\033[30m排序/好友 \033[31m(t)\033[30m聊天 "
         "\033[31m(a/d/o)\033[30m交友 \033[31m(q)\033[30m查詢 "
         "\033[31m(w)\033[30m水球 \033[31m(m)\033[30m寄信 \033[31m(h)"
         "\033[30m線上輔助 \033[m");
    state = 0;
    while (!state){
        ch = cursor_key(num + 3 - head, 0);
        if (ch == KEY_RIGHT || ch == '\n' || ch == '\r')
        ch = 't';

        switch (ch){
        case KEY_LEFT:
        case 'e':
        case 'E':
        if(!keyword[0]) return;
        keyword[0]=0;
            state = US_PICKUP;
        break;

        case KEY_TAB:
        pickup_way = (pickup_way + 1) % PICKUP_WAYS;
        state = US_RESORT;
        num = 0;
        break;

        case KEY_DOWN:
        case 'n':
        case 'j':
        if (++num < actor){
            if (num >= foot)
            state = US_REDRAW;
            break;
        }
        case '0':
        case KEY_HOME:
        num = 0;
        if (head)
            state = US_REDRAW;
        break;
        case 'H':
        if (HAS_PERM(PERM_SYSOP)){
            currutmp->userlevel ^= PERM_DENYPOST;
            state = US_REDRAW;
        }
        break;
        case 'D':
        if (HAS_PERM(PERM_SYSOP)){
            char buf[100];

            sprintf(buf, "代號 [%s]:", currutmp->userid);
            if (!getdata(1, 0, buf, currutmp->userid, IDLEN + 1,
                 DOECHO))
            strcpy(currutmp->userid, cuser.userid);
            state = US_REDRAW;
        }
        break;
        case 'F':
        if (HAS_PERM(PERM_SYSOP)){
            char buf[100];

            sprintf(buf, "故鄉 [%s]:", currutmp->from);
            if (!getdata(1, 0, buf, currutmp->from, 17, DOECHO))
            strncpy(currutmp->from, fromhost, 23);
            state = US_REDRAW;
        }
        break;
        case 'C':
#if !HAVE_FREECLOAK
        if (HAS_PERM(PERM_CLOAK))
#endif
        {
            currutmp->invisible ^= 1;
            state = US_REDRAW;
        }
        break;
        case ' ':
        case KEY_PGDN:
        case Ctrl('F'):
        if (foot < actor){
            num += p_lines;
            state = US_REDRAW;
            break;
        }
        if (head)
            num = 0;
        state = US_PICKUP;
        break;
        case KEY_UP:
        case 'k':
        if (--num < head){
            if (num < 0){
            num = actor - 1;
            if (actor == foot)
                break;
            }
            state = US_REDRAW;
        }
        break;
        case KEY_PGUP:
        case Ctrl('B'):
        case 'P':
        if (head){
            num -= p_lines;
            state = US_REDRAW;
            break;
        }

        case KEY_END:
        case '$':
        num = actor - 1;
        if (foot < actor)
            state = US_REDRAW;
        break;

        case '/':
            getdata_buf(b_lines-1,0,"請輸入暱稱關鍵字:",keyword, 12,
                 DOECHO);
        state = US_PICKUP;
            break;
        case 's':
        if ((i = search_pickup(num, actor, pklist)) >= 0)
            num = i;
        state = US_ACTION;
        break;

        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        {       /* Thor: 可以打數字跳到該人 */
        int tmp;
        if ((tmp = search_num(ch, actor - 1)) >= 0)
            num = tmp;
        state = US_REDRAW;
        }
        break;
#ifdef REALINFO
        case 'R':       /* 顯示真實姓名 */
        if (HAS_PERM(PERM_SYSOP))
            real_name ^= 1;
        state = US_PICKUP;
        break;
#endif
#ifdef SHOWUID
        case 'U':
        if (HAS_PERM(PERM_SYSOP))
            show_uid ^= 1;
        state = US_PICKUP;
        break;
#endif
#ifdef  SHOWBOARD
        case 'Y':
        if (HAS_PERM(PERM_SYSOP))
            show_board ^= 1;
        state = US_PICKUP;
        break;
#endif
#ifdef  SHOWPID
        case '#':
        if (HAS_PERM(PERM_SYSOP))
            show_pid ^= 1;
        state = US_PICKUP;
        break;
#endif

        case 'b':       /* broadcast */
        if (cuser.uflag & FRIEND_FLAG || HAS_PERM(PERM_SYSOP)){
            int actor_pos = actor;
            char ans[4];

            state = US_PICKUP;
            if (!getdata(0, 0, "廣播訊息:", genbuf, 60, DOECHO))
            break;
            if (getdata(0, 0, "確定廣播? [Y]", ans, 4, LCECHO) &&
            *ans == 'n')
            break;
            while (actor_pos){
            uentp = pklist[--actor_pos].ui;
                fri_stat = pklist[actor_pos].friend; 
            if (uentp->pid &&
                currpid != uentp->pid &&
                uentp->pid > 0 && kill(uentp->pid, 0) != -1 &&
                (HAS_PERM(PERM_SYSOP) ||
                 (uentp->pager != 3 &&
                  (uentp->pager != 4 || fri_stat & HFM))))
                my_write(uentp->pid, genbuf, uentp->userid, HAS_PERM(PERM_SYSOP) ? 3 : 1, NULL);
            }
        }
        break;
        case 'S':       /* 顯示好友描述 */
        show_mode = (++show_mode) % MAX_SHOW_MODE;
        state = US_PICKUP;
        break;
        case 'u':       /* 線上修改資料 */
        case 'K':       /* 把壞蛋踢出去 */
        if (!HAS_PERM(PERM_ACCOUNTS))
            continue;
        state = US_ACTION;
        break;
        case 'i':
        state = US_ACTION;
        break;
        case Ctrl('S'):
        state = US_ACTION;
        break;
        case 't':
        case 'w':
        if (!(cuser.userlevel & PERM_LOGINOK))
            continue;
        state = US_ACTION;
        break;
        case 'a':
        case 'd':
        case 'o':
        case 'f':
        case 'g':
        if (!HAS_PERM(PERM_LOGINOK))
            /* 註冊才有 Friend */
            break;
        if (ch == 'f')
        {
            cuser.uflag ^= FRIEND_FLAG;
            state = US_PICKUP;
            break;
        }
        state = US_ACTION;
        break;
        case 'q':
        case 'c':
        case 'm':
        case 'r':
        case 'l':
        /* guest 只能 query */
        if (!cuser.userlevel && ch != 'q' && ch != 'l')
            break;
        case 'h':
        state = US_ACTION;
        break;
        case 'p':
        if (HAS_PERM(PERM_BASIC)){
            t_pager();
            state = US_REDRAW;
        }
        break;
        case 'W':
        {
            int     tmp;
            char    *wm[3] = {"一般", "進階", "未來"};
            tmp = cuser.uflag2 & WATER_MASK;
            cuser.uflag2 -= tmp;
            tmp = (tmp + 1) % 3;
            cuser.uflag2 |= tmp;
            prints("切換到 %s 水球模式", wm[tmp]);
            refresh();
            sleep(1);
            state = US_REDRAW;
        }
        default:        /* refresh screen */
        state = US_REDRAW;
        }
    }

    if (state != US_ACTION){
        pid0 = 0;
        continue;
    }

    /* Ptt decide cur */
    uentp = pklist[num].ui;
        fri_stat = friend_stat(currutmp, uentp);
    pid0 = uentp->pid;
    id0 = 256 * uentp->userid[0] + uentp->userid[1];

    if (ch == 'w'){
        if ((uentp->pid != currpid) &&
        (HAS_PERM(PERM_SYSOP) ||
         (uentp->pager != 3 &&
          (fri_stat & HFM || uentp->pager != 4)))){
        cursor_show(num + 3 - head, 0);
        sprintf(genbuf, "Call-In %s :", uentp->userid);
        my_write(uentp->pid, genbuf, uentp->userid, 0, NULL);
        }
    }
    else if (ch == 'l'){        /* Thor: 看 Last call in */
        t_display();
    }
    else{
        switch (ch){
        case 'r':
        m_read();
        break;
        case 'g':       /* give money */
        move(b_lines - 2, 0);
        if (strcmp(uentp->userid, cuser.userid)){
            sprintf(genbuf, "要給 %s 多少錢呢?  ", uentp->userid);
            outs(genbuf);
            if (getdata(b_lines - 1, 0, "[銀行轉帳]:", genbuf, 7,
                LCECHO)){
            clrtoeol();
            if ((ch = atoi(genbuf)) <= 0)
                break;
            reload_money();
            if (ch > cuser.money)
                outs("\033[41m 現金不足~~\033[m");
            else{
                deumoney(uentp->uid, ch);
                sprintf(genbuf, "\033[44m 嗯..還剩下 %d 錢.."
                    "\033[m", demoney(-1*ch));
                outs(genbuf);
                sprintf(genbuf, "%s\t給%s\t%d\t%s", cuser.userid,
                    uentp->userid, ch,
                    ctime(&currutmp->lastact));
                log_file(FN_MONEY, genbuf);
                mail_redenvelop(cuser.userid, uentp->userid, ch, 'Y');
            }
            }
            else{
            clrtoeol();
            outs("\033[41m 交易取消! \033[m");
            }
        }
        else
            outs("\033[33m 自己給自己? 耍笨..\033[m");
        refresh();
        sleep(1);
        break;
#ifdef SHOWMIND
        case 'i':
        move(3,0);
        clrtobot();
        for (i = 1; Mind[i]!=NULL; i++){
            move(5+(i-1)/7,((i-1)%7)*10);
            prints("%2d: %s",i,Mind[i]);
        }
        getdata(b_lines - 1, 0, "你現在的心情 0:無 q不變 [q]:",
                genbuf, 3, LCECHO);
        if (genbuf[0] && genbuf[0] != 'q')
                   currutmp->mind=atoi(genbuf)%i;
        state = US_REDRAW;
        break;
#endif
        case 'a':
        friend_add(uentp->userid, FRIEND_OVERRIDE);
        friend_load();
        state = US_PICKUP;
        break;
        case 'd':
        friend_delete(uentp->userid, FRIEND_OVERRIDE);
        friend_load();
        state = US_PICKUP;
        break;
        case 'o':
        t_override();
        state = US_PICKUP;
        break;
        case 'K':
        if (uentp->pid > 0 && kill(uentp->pid, 0) != -1){
            move(1, 0);
            clrtobot();
            move(2, 0);
            my_kick(uentp);
            state = US_PICKUP;
        }
        break;
        case 'm':
        stand_title("寄  信");
        prints("[寄信] 收信人:%s", uentp->userid);
        my_send(uentp->userid);
        break;
        case 'q':
        strcpy(currauthor, uentp->userid);
        my_query(uentp->userid);
        break;
        case 'c':
        chicken_query(uentp->userid);
        break;
        case 'u':{      /* Thor: 可線上查看及修改使用者 */
        int id;
        userec_t muser;
        
        strcpy(currauthor, uentp->userid);
        stand_title("使用者設定");
        move(1, 0);
        if ((id = getuser(uentp->userid))){
            memcpy(&muser, &xuser, sizeof(muser));
            user_display(&muser, 1);
            uinfo_query(&muser, 1, id);
        }
        }
        break;

        case 'h':       /* Thor: 看 Help */
        t_showhelp();
        break;

        case 't':
        if (uentp->pid != currpid &&
            (strcmp(uentp->userid, cuser.userid))){
            move(1, 0);
            clrtobot();
            move(3, 0);
            my_talk(uentp, fri_stat);
            state = US_PICKUP;
        }
        break;
        }
    }
    setutmpmode(savemode);
    }
}

int t_users(void)
{
    int destuid0 = currutmp->destuid;
    int mode0 = currutmp->mode;
    int stat0 = currstat;

    setutmpmode(LUSERS);
    pickup_user();
    currutmp->mode = mode0;
    currutmp->destuid = destuid0;
    currstat = stat0;
    return 0;
}

int t_pager(void)
{
    currutmp->pager = (currutmp->pager + 1) % 5;
    return 0;
}

int t_idle(void)
{
    int destuid0 = currutmp->destuid;
    int mode0 = currutmp->mode;
    int stat0 = currstat;
    char genbuf[20];
    char buf[80], passbuf[PASSLEN];

    setutmpmode(IDLE);
    getdata(b_lines - 1, 0, "理由:[0]發呆 (1)接電話 (2)覓食 (3)打瞌睡 "
        "(4)裝死 (5)羅丹 (6)其他 (Q)沒事?", genbuf, 3, DOECHO);
    if (genbuf[0] == 'q' || genbuf[0] == 'Q'){
    currutmp->mode = mode0;
    currstat = stat0;
    return 0;
    }
    else if (genbuf[0] >= '1' && genbuf[0] <= '6')
    currutmp->destuid = genbuf[0] - '0';
    else
    currutmp->destuid = 0;

    if (currutmp->destuid == 6)
    if (!cuser.userlevel ||
        !getdata(b_lines - 1, 0, "發呆的理由:", currutmp->chatid, 11,
             DOECHO))
        currutmp->destuid = 0;
    do{
    move(b_lines - 2, 0);
    clrtoeol();
    sprintf(buf, "(鎖定螢幕)發呆原因: %s", (currutmp->destuid != 6) ?
        IdleTypeTable[currutmp->destuid] : currutmp->chatid);
    outs(buf);
    refresh();
    getdata(b_lines - 1, 0, MSG_PASSWD, passbuf, PASSLEN, NOECHO);
    passbuf[8] = '\0';
    }
    while (!checkpasswd(cuser.passwd, passbuf) &&
       strcmp(STR_GUEST, cuser.userid));

    currutmp->mode = mode0;
    currutmp->destuid = destuid0;
    currstat = stat0;

    return 0;
}

int t_qchicken(void)
{
    char uident[STRLEN];

    stand_title("查詢寵物");
    usercomplete(msg_uid, uident);
    if (uident[0])
    chicken_query(uident);
    return 0;
}

int t_query(void)
{
    char uident[STRLEN];

    stand_title("查詢網友");
    usercomplete(msg_uid, uident);
    if (uident[0])
    my_query(uident);
    return 0;
}

int t_talk() {
    char uident[16];
    int tuid, unum, ucount;
    userinfo_t *uentp;
    char genbuf[4];
/*
    if (count_ulist() <= 1){
    outs("目前線上只有您一人,快邀請朋友來光臨【" BBSNAME "】吧!");
    return XEASY;
    }
*/
    stand_title("打開話匣子");
    creat_list();
    namecomplete(msg_uid, uident);
    if (uident[0] == '\0')
    return 0;

    move(3, 0);
    if (!(tuid = searchuser(uident)) || tuid == usernum){
    outs(err_uid);
    pressanykey();
    return 0;
    }

/* multi-login check */
    unum = 1;
    while ((ucount = count_logins(tuid, 0)) > 1){
    outs("(0) 不想 talk 了...\n");
    count_logins(tuid, 1);
    getdata(1, 33, "請選擇一個聊天對象 [0]:", genbuf, 4, DOECHO);
    unum = atoi(genbuf);
    if (unum == 0)
        return 0;
    move(3, 0);
    clrtobot();
    if (unum > 0 && unum <= ucount)
        break;
    }

    if ((uentp = (userinfo_t *) search_ulistn(tuid, unum)))
    my_talk(uentp, friend_stat(currutmp, uentp));

    return 0;
}

/* 有人來串門子了,回應呼叫器 */
static userinfo_t *uip;
void talkreply(void)
{
    struct hostent *h;
    char buf[4];
    struct sockaddr_in sin;
    char genbuf[200];
    int a, sig = currutmp->sig;

    talkrequest = NA;
    uip = &utmpshm->uinfo[currutmp->destuip];
    sprintf(page_requestor, "%s (%s)", uip->userid, uip->username);
    currutmp->destuid = uip->uid;
    currstat = XMODE;       /* 避免出現動畫 */

    clear();

    prints("\n\n");
    prints("       (Y) 讓我們 %s 吧!"
       "     (A) 我現在很忙,請等一會兒再 call 我\n", sig_des[sig]);
    prints("       (N) 我現在不想 %s"
       "      (B) 對不起,我有事情不能跟你 %s\n",
       sig_des[sig], sig_des[sig]);
    prints("       (C) 請不要吵我好嗎?"
       "     (D) 我要離站囉..下次再聊吧.......\n");
    prints("       (E) 有事嗎?請先來信"
       "     (F) \033[1;33m我自己輸入理由好了...\033[m\n");
    prints("       (1) %s?先拿100銀兩來"
       "  (2) %s?先拿1000銀兩來..\n\n", sig_des[sig], sig_des[sig]);

    getuser(uip->userid);
    currutmp->msgs[0].pid = uip->pid;
    strcpy(currutmp->msgs[0].userid, uip->userid);
    strcpy(currutmp->msgs[0].last_call_in, "呼叫、呼叫,聽到請回答 (Ctrl-R)");
    prints("對方來自 [%s],共上站 %d 次,文章 %d 篇\n",
       uip->from, xuser.numlogins, xuser.numposts);
    showplans(uip->userid);
    show_call_in(0, 0);

    sprintf(genbuf, "你想跟 %s %s啊?請選擇(Y/N/A/B/C/D/E/F/1/2)[N] ",
        page_requestor, sig_des[sig]);
    getdata(0, 0, genbuf, buf, 4, LCECHO);

    if (uip->mode != PAGE){
    sprintf(genbuf, "%s已停止呼叫,按Enter繼續...", page_requestor);
    getdata(0, 0, genbuf, buf, 4, LCECHO);
    return;
    }
    currutmp->msgcount = 0;
    strcpy(save_page_requestor, page_requestor);
    memset(page_requestor, 0, sizeof(page_requestor));
    if (!(h = gethostbyname("localhost"))){
    perror("gethostbyname");
    return;
    }
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = h->h_addrtype;
    memcpy(&sin.sin_addr, h->h_addr, h->h_length);
    sin.sin_port = uip->sockaddr;
    a = socket(sin.sin_family, SOCK_STREAM, 0);
    if ((connect(a, (struct sockaddr *) &sin, sizeof(sin)))){
    perror("connect err");
    return;
    }
    if (!buf[0] || !strchr("yabcdef12", buf[0]))
    buf[0] = 'n';
    write(a, buf, 1);
    if (buf[0] == 'f' || buf[0] == 'F'){
    if (!getdata(b_lines, 0, "不能的原因:", genbuf, 60, DOECHO))
        strcpy(genbuf, "不告訴你咧 !! ^o^");
    write(a, genbuf, 60);
    }
    uip->destuip = currutmp - &utmpshm->uinfo[0];
    if (buf[0] == 'y')
    switch (sig){
    case SIG_DARK:
        main_dark(a, uip);
        break;
    case SIG_PK:
        chickenpk(a);
        break;
    case SIG_GOMO:
        gomoku(a);
        break;
    case SIG_CHC:
        chc(a);
        break;
    case SIG_TALK:
    default:
        do_talk(a);
    }
    else
    close(a);
    clear();
}

/* 網友動態簡表 */
/* not used
static int shortulist(userinfo_t * uentp)
{
    static int lineno, fullactive, linecnt;
    static int moreactive, page, num;
    char uentry[50];
    int state;

    if (!lineno){
    lineno = 3;
    page = moreactive ? (page + p_lines * 3) : 0;
    linecnt = num = moreactive = 0;
    move(1, 70);
    prints("Page: %d", page / (p_lines) / 3 + 1);
    move(lineno, 0);
    }

    if (uentp == NULL){
    int finaltally;

    clrtoeol();
    move(++lineno, 0);
    clrtobot();
    finaltally = fullactive;
    lineno = fullactive = 0;
    return finaltally;
    }

    if ((!HAS_PERM(PERM_SYSOP) &&
     !HAS_PERM(PERM_SEECLOAK) &&
     uentp->invisible) ||
    ((friend_stat(currutmp, uentp) & HRM) &&
     !HAS_PERM(PERM_SYSOP))){
    if (lineno >= b_lines)
        return 0;
    if (num++ < page)
        return 0;
    memset(uentry, ' ', 25);
    uentry[25] = '\0';
    }
    else{
    fullactive++;
    if (lineno >= b_lines){
        moreactive = 1;
        return 0;
    }
    if (num++ < page)
        return 0;

        state = (currutmp == uentp) ? 10 :
                     (friend_stat(currutmp,uentp)&ST_FRIEND)>>2;

    if (PERM_HIDE(uentp))
        state = 9;

    sprintf(uentry, "%s%-13s%c%-10s%s ", fcolor[state],
        uentp->userid, uentp->invisible ? '#' : ' ',
        modestring(uentp, 1), state ? "\033[0m" : "");
    }
    if (++linecnt < 3){
    strcat(uentry, "│");
    outs(uentry);
    }
    else{
    outs(uentry);
    linecnt = 0;
    clrtoeol();
    move(++lineno, 0);
    }
    return 0;
}
*/