/*****************************************************************************
 * file    lib/management/cmd_SSH2.c  
 * author  YuLiang
 * version 1.0.0
 * date    20-Mar-2025
 * brief   This file provides all the SSH2 cmd related operation functions.
 ******************************************************************************
 * Attention
 *
 * 
© COPYRIGHT(c) 2025 LandPower
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of LandPower nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/
/* Includes ------------------------------------------------------------------*/
//#ifdef HAVE_CONFIG_H
#include "config.h"
//#endif
/* 标准C库头文件. */
#include 
#include 
#include 
#include 
/* 用户代码头文件. */
#include "main.h"
#include "vty.h"
#include "cmd.h"
#include "mtimer.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define VTYCMD_PORT "2222"
#define VTYCMD_RSA_PATH "/home/root/ssh/ssh_host_rsa_key"
#define VTYCMD_HOSTKEY_PATH "/home/root/ssh/ssh_host_ed25519_key"
#define VTYCMD_BUF_OUT_LEN 32768
typedef struct _vtycmd_ctrl
{
    vty_t *vtycmd;
    ssh_bind sshbind;
    ssh_session session;
    ssh_channel channel;
    struct ssh_server_callbacks_struct server_cb;
    struct ssh_channel_callbacks_struct channel_cb;
    uint8_t is_connect; 
    uint8_t is_recv;
    uint16_t cmd_idx;
    char cmd[VTY_BUFSIZ];
    uint16_t out_buf_start;
    uint16_t out_buf_end;
    char out_buf[VTYCMD_BUF_OUT_LEN];
} vtycmd_ctrl_t;
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* cmd vty结构体. */
static vtycmd_ctrl_t vtycmd_ctrl;
/* Private function prototypes -----------------------------------------------*/
/* 在终端上显示当前配置信息. */
CMD(show_sshcmd,
    show_sshcmd_cmd,
    "show sshcmd",
    SHOW_STR
    "SSHCMD\n")
{
    vty_out(vty, "Connect: %s\r\n\n", vtycmd_ctrl.is_connect ? "yes" : "no");
    return CMD_SUCCESS;
}
/* Insert a word into vty interface with overwrite mode. */
void _vtycmd_insert_word(char *str)
{
    int len = strlen(str);
    ssh_channel_write(vtycmd_ctrl.channel, str, len);
    strcpy(&vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx], str);
    vtycmd_ctrl.cmd_idx += len;
    vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
}
/* commandLine '?'响应函数. */
int _vtycmd_question(void)
{
    array_t *cmd_line = NULL;
    cmd_line = cmd_strs_create(vtycmd_ctrl.cmd);
    if (NULL == cmd_line)
    {
        cmd_line = array_init(1, MTYPE_CLI);
        array_append(cmd_line, '\0', MTYPE_CLI);
    }
    else
        if (vtycmd_ctrl.cmd_idx && isspace((int)vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx - 1]))
            array_append(cmd_line, '\0', MTYPE_CLI);
    vty_question(vtycmd_ctrl.vtycmd, cmd_line);
    cmd_strs_free(cmd_line);
    vty_prompt(vtycmd_ctrl.vtycmd);
    vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd);
    return 0;
}
/* 用于自动补齐命令行关键字. */
void _vtycmd_completion_append(array_t *cmd_line, char *word, int32_t status)
{
     uint32_t index = array_active(cmd_line) - 1;
     uint32_t len = 0;
     uint32_t i = 0;
    if (NULL == array_get(cmd_line, index))
    {
        _vtycmd_insert_word(word);
    }
    else
    {
        len = strlen(array_get(cmd_line, index));
        for(i = 0; i < len; i++)
        {
            if (vtycmd_ctrl.cmd_idx)
            {
                vtycmd_ctrl.cmd_idx--;
                vty_out(vtycmd_ctrl.vtycmd, "%c%c%c", 8, ' ', 8);
            }
        }
        _vtycmd_insert_word(word);
    }
    if (CMD_COMPLETE_FULL_MATCH == status)
        _vtycmd_insert_word(" ");
}
/* commandLine 'TAB'响应函数. */
int _vtycmd_completion(void)
{
    array_t *cmd_line = NULL;
    char **match_strs = NULL;
    int32_t complete_status = CMD_ERR_NO_MATCH;
    cmd_line = cmd_strs_create(vtycmd_ctrl.cmd);
    if (NULL == cmd_line)
    {
        cmd_line = array_init(1, MTYPE_CLI);
        array_append(cmd_line, '\0', MTYPE_CLI);
    }
    else
        if (vtycmd_ctrl.cmd_idx && isspace((int)vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx - 1]))
            array_append(cmd_line, '\0', MTYPE_CLI);
    match_strs = cmd_complete_command(cmd_line, vtycmd_ctrl.vtycmd, &complete_status);
    if (NULL == match_strs)
    {
        cmd_strs_free(cmd_line);
        return 0;
    }
    if (CMD_COMPLETE_MATCH == complete_status ||
        CMD_COMPLETE_FULL_MATCH == complete_status)
    {
        _vtycmd_completion_append(cmd_line, match_strs[0], complete_status);
        // if (match_strs) {
        //     fprintf(stderr, "Match strings: %s\n", match_strs[0]);
        // }
    }
    else
    {
        vty_print_word(vtycmd_ctrl.vtycmd, match_strs);
        vty_prompt(vtycmd_ctrl.vtycmd);
        vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd);
    }
    vty_free_match_strs(match_strs);
    cmd_strs_free(cmd_line);
    return 0;
}
/* Internal functions --------------------------------------------------------*/
/* Print command line history.  This function is called from
   vty_next_line and vty_previous_line. */
static void _vtycmd_history_print(vty_t *vty)
{
    uint16_t i= 0;
    for(i = 0; i < vtycmd_ctrl.cmd_idx; i++)
    {
        vty_out(vtycmd_ctrl.vtycmd, "%c%c%c", 8, ' ', 8);
    }
    vtycmd_ctrl.cmd_idx = 0;
    
    /* Get previous line from history buffer */
    vtycmd_ctrl.cmd_idx = strlen(vty->hist[vty->hp]);
    memcpy(vtycmd_ctrl.cmd, vty->hist[vty->hp], vtycmd_ctrl.cmd_idx);
    vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
    /* Redraw current line */
    vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd);
}
/* Show previous command line history. */
static void _vtycmd_previous_line(vty_t *vty)
{
    unsigned int try_index = 0;
    try_index = vty->hp;
    if (try_index == 0)
        try_index = VTY_MAXHIST - 1;
    else
        try_index--;
    if (vty->hist[try_index] == NULL)
        return;
    else
        vty->hp = try_index;
    _vtycmd_history_print(vty);
}
/* Show next command line history. */
static void _vtycmd_next_line(vty_t *vty)
{
    unsigned int try_index = 0;
    if (vty->hp == vty->hindex)
        return;
    /* Try is there history exist or not. */
    try_index = vty->hp;
    if (try_index == (VTY_MAXHIST - 1))
        try_index = 0;
    else
        try_index++;
    /* If there is not history return. */
    if (vty->hist[try_index] == NULL)
        return;
    else
        vty->hp = try_index;
    _vtycmd_history_print(vty);
}
int _vtycmd_cmd(char *buf, uint32_t len)
{
    uint16_t i = 0;
    vty_t *vty = vtycmd_ctrl.vtycmd; 
    for (i = 0; i < len; i++) 
    {
        switch (buf[i])
        {
        case '\033':
            if (i + 2 < len  && buf[i + 1] == '[')
            {
                if (buf[i + 2] == 'A')
                {
                    _vtycmd_previous_line(vty);
                }
                else if (buf[i + 2] == 'B')
                {
                    _vtycmd_next_line(vty);
                }
                i += 2;
            }
            break;
        case CONTROL('H'):
        case 0x7f:
            if (vtycmd_ctrl.cmd_idx)
            {
                vtycmd_ctrl.cmd_idx--;
                vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
                vty_out(vty, "%c%c%c", 8, ' ', 8);
            }
            break;
        case '\n':
        case '\r':
            vty_out(vty, "%s", VTY_NEWLINE);
            /* 执行命令 */
            vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
            vty->length = vtycmd_ctrl.cmd_idx;
            snprintf(vty->buf, VTY_BUFSIZ, "%s", vtycmd_ctrl.cmd);
            vty_execute(vty);
            vtycmd_ctrl.cmd_idx = 0;
            vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
            break;
        case '\t':
            _vtycmd_completion();
            break;
        case '?':
            _vtycmd_question();
            break;
        default:
            if (buf[i] > 31 && buf[i] < 127)
            {
                vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = buf[i];
                vtycmd_ctrl.cmd_idx++;
                vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0;
                vty_out(vty, "%c", buf[i]);
            }
            break;
        }
    }
    
    return E_NONE;
}
    
/* 客户端通讯相关的回调 */
int _vtycmd_channel_pty_request(ssh_session session, ssh_channel channel,
                const char *term, int cols, int rows, int py, int px,
                void *userdata)
{
    ssh_channel_write(vtycmd_ctrl.channel, "Username: ", 10);
    ssh_channel_request_pty_size(channel, term, cols, rows);
    return SSH_OK;
}
int _vtycmd_channel_pty_resize(ssh_session session, ssh_channel channel, int cols,
               int rows, int py, int px, void *userdata)
{
    return ssh_channel_change_pty_size(channel, cols, rows);
}
int _vtycmd_channel_shell_request(ssh_session session, ssh_channel channel,
                  void *userdata)
{
    return SSH_OK;
}
int _vtycmd_channel_exec_request(ssh_session session, ssh_channel channel,
                 const char *command, void *userdata)
{
    return ssh_channel_request_exec(channel, command);
}
int _vtycmd_channel_subsystem_request(ssh_session session, ssh_channel channel,
                      const char *subsystem, void *userdata)
{
    return  SSH_OK;
}
int _vtycmd_channel_env_request_function(ssh_session session, ssh_channel channel, const char *env_name,
                         const char *env_value, void *userdata)
{
    return ssh_channel_request_env(channel, env_name, env_value);
}
/* 数据处理 */
int _vtycmd_channel_data_function(ssh_session session, ssh_channel channel, void *data,
                  uint32_t len, int is_stderr, void *userdata)
{
    vtycmd_ctrl.is_recv = TRUE;
    _vtycmd_cmd((char*)data, len);
    return len;
}
void _vtycmd_channel_close(ssh_session session, ssh_channel channel, void *userdata)
{
    vtycmd_ctrl.is_connect = FALSE;
}
                  
/* 服务端认证通道相关回调 */
int _vtycmd_server_auth_pass(ssh_session session, const char *user, const char *password, void *userdata)
{
    if (strcmp("root", user) == 0 && strcmp("123456", password) == 0)
    {
        return SSH_AUTH_SUCCESS;
    }
    else
    {
        return SSH_AUTH_DENIED;
    }
}
/* 空认证方法 */
int _vtycmd_server_auth_none_callback(ssh_session session, const char *user, void *userdata)
{
    return SSH_AUTH_SUCCESS;
}
/* pubkey 认证方法 */
int _vtycmd_server_auth_pubkey(ssh_session session, const char *user, struct ssh_key_struct *pubkey,
                char signature_state, void *userdata)
{
    return SSH_AUTH_SUCCESS;
}
int _vtycmd_server_request_callback(ssh_session session, const char *service, void *userdata)
{
    return SSH_OK;
}
ssh_channel _vtycmd_server_open_channel(ssh_session session, void *userdata)
{
    vtycmd_ctrl.channel = ssh_channel_new(session);
    ssh_callbacks_init(&vtycmd_ctrl.channel_cb);
    ssh_set_channel_callbacks(vtycmd_ctrl.channel, &vtycmd_ctrl.channel_cb);
        
    return vtycmd_ctrl.channel;
}
/* SSH2 命令行主函数 */
void *_vtycmd_handle(void *arg)
{
    /* 初始化 libssh */
    if (ssh_init() < 0)
    {
        log_err(LOG_CLI, "ssh_init failed\r\n");
        return NULL;
    }
    /* 初始化结构体 */
    vtycmd_ctrl.server_cb.auth_password_function = _vtycmd_server_auth_pass;
    vtycmd_ctrl.server_cb.auth_none_function = _vtycmd_server_auth_none_callback;
    vtycmd_ctrl.server_cb.auth_pubkey_function = _vtycmd_server_auth_pubkey;
    vtycmd_ctrl.server_cb.service_request_function = _vtycmd_server_request_callback;
    vtycmd_ctrl.server_cb.channel_open_request_session_function = _vtycmd_server_open_channel;
    vtycmd_ctrl.channel_cb.channel_data_function = _vtycmd_channel_data_function;
    vtycmd_ctrl.channel_cb.channel_close_function = _vtycmd_channel_close;
    vtycmd_ctrl.channel_cb.channel_pty_request_function = _vtycmd_channel_pty_request;
    vtycmd_ctrl.channel_cb.channel_shell_request_function = _vtycmd_channel_shell_request;
    vtycmd_ctrl.channel_cb.channel_pty_window_change_function = _vtycmd_channel_pty_resize;
    vtycmd_ctrl.channel_cb.channel_exec_request_function = _vtycmd_channel_exec_request;
    vtycmd_ctrl.channel_cb.channel_env_request_function = _vtycmd_channel_env_request_function;
    vtycmd_ctrl.channel_cb.channel_subsystem_request_function = _vtycmd_channel_subsystem_request;
    /* 创建 SSH2 接口 */
    vtycmd_ctrl.sshbind = ssh_bind_new();
    ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, VTYCMD_PORT);
    ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_RSAKEY, VTYCMD_RSA_PATH);
    ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_HOSTKEY, VTYCMD_HOSTKEY_PATH);
    if (ssh_bind_listen(vtycmd_ctrl.sshbind) < 0)
    {
        log_err(LOG_CLI, "Error listening to socket: %s\n", ssh_get_error(vtycmd_ctrl.sshbind));
        return NULL;
    }
    while(1)
    {
        /* 等待连接 */
        ssh_session session = ssh_new();
        if (ssh_bind_accept(vtycmd_ctrl.sshbind, session) != SSH_OK)
        {
            DBG(DBG_M_CLI, "Error accept to socket: %s\n", ssh_get_error(vtycmd_ctrl.sshbind));
            continue;
        }
        /* 只能接收一个连接 */
        if (vtycmd_ctrl.is_connect)
        {
            ssh_disconnect(session);
            ssh_free(session);
            continue;
        }
        /* 初始化连接 */
        log_notice(LOG_CLI, "SSH2 command is connect.\n");
        vtycmd_ctrl.is_connect = TRUE;
        ssh_callbacks_init(&vtycmd_ctrl.server_cb);
        ssh_set_server_callbacks(session, &vtycmd_ctrl.server_cb);
        ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
        if (ssh_handle_key_exchange(session) != SSH_OK)
        {
            DBG(DBG_M_CLI, "Error performing key exchange: %s\n", ssh_get_error(session));
            ssh_disconnect(session);
            ssh_free(session);
            continue;
        }
        ssh_set_blocking(session, 0);
        ssh_event event = ssh_event_new();
        ssh_event_add_session(event, session);
        /* 处理连接断开 */
        while(1)
        {
            if (ssh_event_dopoll(event, 3000) == SSH_ERROR
                || !vtycmd_ctrl.is_connect)
            {
                /* 终端断开处理 */
                log_notice(LOG_CLI, "SSH2 command is disconnect.\n");
                ssh_channel_close(vtycmd_ctrl.channel);
                ssh_channel_free(vtycmd_ctrl.channel);
                ssh_disconnect(session);
                ssh_event_free(event);
                ssh_free(session);
                vtycmd_ctrl.is_connect = FALSE;
                if (CONFIG_NODE == vtycmd_ctrl.vtycmd->node)
                {
                    vty_config_unlock(vtycmd_ctrl.vtycmd);
                }
                vtycmd_ctrl.vtycmd->node = USERNAME_NODE;
                break;
            }
        }
    }
    return NULL;
}
/* 用于判断终端是否超时 */
void* _vtycmd_timer(void *arg)
{
    if (!vtycmd_ctrl.is_recv && vtycmd_ctrl.is_connect)
    {
        vtycmd_ctrl.is_connect = FALSE;
    }
    vtycmd_ctrl.is_recv = FALSE;
    /* 重新加入定时器. */
    mtimer_add(_vtycmd_timer, NULL, 120, "VTYCMD_TIMER");
    return NULL;
}
/* SSH2 命令行发送数据 */
void *_vtycmd_send_handle(void *arg)
{
    char *buf = NULL;
    uint16_t end = 0;
    
    while(1)
    {
        usleep(100000);
        
        if (!vtycmd_ctrl.is_connect)
        {
            continue;
        }
        
        end = vtycmd_ctrl.out_buf_end;
        buf = vtycmd_ctrl.out_buf + vtycmd_ctrl.out_buf_start;
        
        if (end == vtycmd_ctrl.out_buf_start)
        {
            continue;
        }
        
        if (end < vtycmd_ctrl.out_buf_start)
        {
            ssh_channel_write(vtycmd_ctrl.channel, buf, VTYCMD_BUF_OUT_LEN - vtycmd_ctrl.out_buf_start);
            vtycmd_ctrl.out_buf_start = 0;
        }
        else
        {
            ssh_channel_write(vtycmd_ctrl.channel, buf, end - vtycmd_ctrl.out_buf_start);
            vtycmd_ctrl.out_buf_start = end;
        }
    }
    return NULL;
}
/* Interface functions -------------------------------------------------------*/
/* 初始化 SSH2 命令行 */
void vtycmd_init(void)
{   
    /* Make vty structure. */
    vtycmd_ctrl.vtycmd = vty_create();
    vtycmd_ctrl.vtycmd->type = VTY_CMD;
    vtycmd_ctrl.vtycmd->node = USERNAME_NODE;
    memset(vtycmd_ctrl.vtycmd->hist, 0, sizeof(vtycmd_ctrl.vtycmd->hist));
    vtycmd_ctrl.vtycmd->hp = 0;
    vtycmd_ctrl.vtycmd->hindex = 0;
    cmd_install_element(COMMON_NODE, &show_sshcmd_cmd);
}
/* 启动 SSH2 命令行 */
void vtycmd_cmd_init(void)
{
    pthread_t pid;
    struct sched_param param;
    pthread_attr_t attr;
    int32_t rv = 0;
    /* 配置线程RR调度, 优先级75 */
    pthread_attr_init(&attr);
    param.sched_priority = 75;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, ¶m);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _vtycmd_handle, NULL);
    if (rv != 0)
    {
        log_err(LOG_DEFAULT, "vtycmd_cmd_init can't create pthread %d!", rv);
    }
    else
    {
        thread_m_add("CMD", pid);
    }
    pthread_attr_destroy(&attr);
    mtimer_add(_vtycmd_timer, NULL, 120, "VTYCMD_TIMER");
    /* 配置线程RR调度, 优先级75 */
    pthread_attr_init(&attr);
    param.sched_priority = 75;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, ¶m);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _vtycmd_send_handle, NULL);
    if (rv != 0)
    {
        log_err(LOG_DEFAULT, "vtycmd_cmd_init can't create pthread %d!", rv);
    }
    else
    {
        thread_m_add("CMD_SNED", pid);
    }
    pthread_attr_destroy(&attr);
}
/* cmd 是否连接 */
bool vtycmd_connect(void)
{   
    return vtycmd_ctrl.is_connect;
}
/* 命令行输出 */
void vtycmd_print(const char *format, va_list va)
{
    char *buf = vtycmd_ctrl.out_buf + vtycmd_ctrl.out_buf_end;
    char buf_temp[VTY_BUFSIZ];
    char *buf_temp_p = buf_temp;
    uint16_t start = 0;;
    int len = 0;
    int len_remain = 0;
    int len_temp = 0;
    if (!vtycmd_ctrl.is_connect)
    {
        vtycmd_ctrl.out_buf_start = 0;
        vtycmd_ctrl.out_buf_end = 0;
        return;
    }
    len = vsnprintf(buf_temp, VTY_BUFSIZ, format, va);
    if (len <= 0)
    {
        return;
    }
    /* 计算可写入的数据量 */
    start = vtycmd_ctrl.out_buf_start;
    if (vtycmd_ctrl.out_buf_end >= start)
    {
        len_remain = VTYCMD_BUF_OUT_LEN - 1 - (vtycmd_ctrl.out_buf_end - start);
    }
    else
    {
        len_remain = start - vtycmd_ctrl.out_buf_end - 1;
    }
    if (len_remain <= 0)
    {
        return;
    }
    
    if (len > len_remain)
    {
        len = len_remain;
    }
    /* 超过 out_buf 需要分段存储 */
    if (len + vtycmd_ctrl.out_buf_end > VTYCMD_BUF_OUT_LEN)
    {
        len_temp = VTYCMD_BUF_OUT_LEN - vtycmd_ctrl.out_buf_end;
        memcpy(buf, buf_temp_p, len_temp);
        vtycmd_ctrl.out_buf_end = 0;
        buf = vtycmd_ctrl.out_buf;
        buf_temp_p = buf_temp_p + len_temp;
        len -= len_temp;
        
        memcpy(buf, buf_temp_p, len);
        vtycmd_ctrl.out_buf_end += len;
    }
    else
    {
        memcpy(buf, buf_temp_p, len);
        len_temp = vtycmd_ctrl.out_buf_end + len;
        if (VTYCMD_BUF_OUT_LEN == len_temp)
        {
            vtycmd_ctrl.out_buf_end = 0;
        }
        else
        {
            vtycmd_ctrl.out_buf_end = len_temp;
        }
    }
}
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/