pans
2017-01-09 be84c3ce8e106905a25c960aeb1ae3aa91317827


git-svn-id: http://192.168.1.226/svn/proxy@82 454eff88-639b-444f-9e54-f578c98de674
5个文件已添加
867 ■■■■■ 已修改文件
Lighttpd/conf.back/ctrlc.cpp 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Lighttpd/conf.back/fastcgi.conf 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Lighttpd/conf.back/lighttpd.conf 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Lighttpd/mycgi/ccgi.cpp 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Lighttpd/mycgi/mycgi.cpp 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Lighttpd/conf.back/ctrlc.cpp
New file
@@ -0,0 +1,431 @@
#ifndef nullptr
#define nullptr NULL
#endif
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <cctype>
using namespace std;
// http://www.fastcgi.com
#include <fcgi_stdio.h>
#include <fcgi_config.h>
// http://uriparser.sourceforge.net
#include <uriparser/Uri.h>
// http://www.postgresql.org
//#include <pgsql/libpq-fe.h>
#include <libpq-fe.h>
// https://github.com/open-source-parsers/jsoncpp
#include <json/json.h>
#define SOCK_TCP
#include <cmdhub/client.h>
#include "sqlmaker/sqlmaker.h"
my_id_t get_my_id()
{
    static my_id_t myid = 0;
    myid++;
    return myid;
}
char to_upper(char c)
{
    return toupper((int)c);
}
extern char **environ;
static char **g_initial_nv = nullptr;
static size_t g_req_count = 0;
stringstream g_log_msg;
stringstream g_http_res;
typedef void (*url_process_func_t)();
typedef map<string, url_process_func_t> upf_map_t;
static upf_map_t g_url_processors;
typedef map<string, string> str_map_t;
static void http_res_200(const char* msg)
{
    FCGI_printf("Status: 20 OK\r\n" "Content-type: text/html\r\n" "\r\n" "%s", msg);
}
static void http_res_400()
{
    FCGI_printf("Status: 400 Bad Request\r\n" "Content-type: text/html\r\n" "\r\n");
}
static void http_res_401()
{
    FCGI_printf("Status: 401 Unauthorized\r\n" "Content-type: text/html\r\n" "\r\n");
}
static void http_res_404()
{
    FCGI_printf("Status: 404 Not Found\r\n" "\r\n");
}
static void http_res_500()
{
    FCGI_printf("Status: 500 Internal Server Error\r\n" "\r\n");
}
static void http_res_500(int problem_code = 0)
{
    FCGI_printf("Status: 500 Internal Server Error\r\n" "Content-type: text/html\r\n" "\r\n" "Problem code = %d", problem_code);
}
static void http_res_500(const char* msg)
{
    FCGI_printf("Status: 500 Internal Server Error\r\n" "Content-type: text/html\r\n" "\r\n" "Message = %s", msg);
}
size_t read_shell(const char* cmd, char* buffer, size_t buffer_size, int* exit_status = nullptr, int* exit_code = nullptr)
{
    if (cmd == nullptr)
        return 0;
    FCGI_FILE* fcmd = FCGI_popen(cmd, "r");
    if (fcmd == nullptr)
        return 0;
    size_t red = FCGI_fread(buffer, sizeof(char), buffer_size - 1, fcmd);
    int es = FCGI_pclose(fcmd);
    if (exit_status != nullptr)
        *exit_status = es;
    if (exit_code != nullptr)
        *exit_code = WEXITSTATUS(es);
    return red;
}
void run_in_bash(const char* cmd, int* exit_status = nullptr, int* exit_code = nullptr)
{
#define BASH_WRAPPER "su -s /bin/bash -c '%s' http"
    char buffer[512];
    snprintf(buffer, sizeof(buffer) - 1, BASH_WRAPPER, cmd);
    int es = system(buffer);
    if (exit_status != nullptr)
        *exit_status = es;
    if (exit_code != nullptr)
        *exit_code = WEXITSTATUS(es);
}
static void print_env(const char *label, char **envp)
{
    FCGI_printf("%s:<br>\n<pre>\n", label);
    for ( ; *envp != NULL; envp++)
    {
        FCGI_printf("%s\n", *envp);
    }
    FCGI_printf("</pre><p>\n");
}
static void hpf_echo()
{
    g_log_msg.flush();
    char *contentLength = getenv("CONTENT_LENGTH");
    int len;
    FCGI_printf("Content-type: text/html\r\n"
                "\r\n"
                "<title>FastCGI echo</title>"
                "<h1>FastCGI echo</h1>\n"
                "Request number %d,  Process ID: %d<p>\n", g_req_count, getpid());
    if (contentLength != NULL)
        len = strtol(contentLength, NULL, 10);
    else
        len = 0;
    if (len <= 0)
    {
        FCGI_printf("No data from standard input.<p>\n");
    }
    else
    {
        int i, ch;
        FCGI_printf("Standard input:<br>\n<pre>\n");
        for (i = 0; i < len; i++)
        {
            if ((ch = getchar()) < 0)
            {
                FCGI_printf("Error: Not enough bytes received on standard input<p>\n");
                break;
            }
            putchar(ch);
        }
        FCGI_printf("\n</pre><p>\n");
    }
    print_env("Request environment", environ);
    print_env("Initial environment", g_initial_nv);
}
bool parse_query_items(str_map_t& query_items)
{
    char* req_REQUEST_URI;
    req_REQUEST_URI = getenv("REQUEST_URI");
    if (req_REQUEST_URI == nullptr)
    {
        g_log_msg << "can not get REQUEST_URI" << endl;
        goto error_ret;
    }
    char* req_QUERY_STRING;
    req_QUERY_STRING = getenv("QUERY_STRING");
    if (req_QUERY_STRING == nullptr)
    {
        g_log_msg << "can not get QUERY_STRING" << endl;
        goto error_ret;
    }
    else if (req_QUERY_STRING[0] == '\0')
    {
        g_log_msg << "QUERY_STRING is empty" << endl;
        return true;
    }
    UriParserStateA state;
    UriUriA uri;
    state.uri = &uri;
    if (uriParseUriA(&state, req_REQUEST_URI) != URI_SUCCESS)
    {
        g_log_msg << "uriParseUriA not success" << endl;
        goto error_ret_uri_1;
    }
    UriQueryListA * query_list;
    query_list = nullptr;
    int ql_item_count;
    ql_item_count = 0;
    if (uriDissectQueryMallocA(&query_list, &ql_item_count, uri.query.first, uri.query.afterLast) != URI_SUCCESS)
    {
        g_log_msg << "uriDissectQueryMallocA not success" << endl;
        goto error_ret_uri_2;
    }
    //debug:
    //FCGI_printf("Content-type: text/json\r\n" "\r\n");
    UriQueryListA* iter_query_list;
    iter_query_list = query_list;
    while (iter_query_list != nullptr)
    {
        const char* qi_key = iter_query_list->key;
        const char* qi_value = iter_query_list->value;
        if(qi_key == nullptr || qi_value == nullptr)
        {
            g_log_msg << "query_list contains null key or value" << endl;
            goto error_ret_uri_2;
        }
        query_items[string(qi_key)] = string(qi_value);
        //debug:
        //FCGI_printf("%s=%s\n", qi_key, qi_value);
        iter_query_list = iter_query_list->next;
    }
    uriFreeQueryListA(query_list);
    uriFreeUriMembersA(&uri);
    return true;
error_ret_uri_2:
    uriFreeQueryListA(query_list);
error_ret_uri_1:
    uriFreeUriMembersA(&uri);
error_ret:
    return false;
}
size_t get_post_content(char* buffer, size_t max_size, size_t offset_content = 0)
{
    if (buffer == NULL || max_size == 0)
        return 0;
    const char* contentLength = getenv("CONTENT_LENGTH");
    size_t len = 0;
    if (contentLength != NULL)
        len = strtol(contentLength, NULL, 10);
    if (len == 0)
        return 0;
    if (offset_content > 0)
        fseek(stdin, offset_content, SEEK_SET);
    return fread(buffer, sizeof(char), min(max_size, len), stdin);
}
const string* try_get_str_from_map(const str_map_t& str_map, const string& key, const string* default_value = nullptr)
{
    str_map_t::const_iterator iter = str_map.find(key);
    if (iter == str_map.end())
        return default_value;
    else
        return &(iter->second);
}
static void hpf_fishbowl_fish_count()
{
    g_log_msg.flush();
    str_map_t query_items;
    if (! parse_query_items(query_items))
    {
        http_res_500(g_log_msg.str().c_str());
        return;
    }
    Json::Value json_root;
    bool json_ret = true;
    const string* qi_cmd = try_get_str_from_map(query_items, string("cmd"));
    if (qi_cmd == nullptr)
    {
        json_root["msg"] = "cmd not needed";
    }
    else
    {
        if (strcmp("list", qi_cmd->c_str()) == 0)
        {
            json_ret = hpf_fishbowl_fish_count_list(query_items, json_root);
        }
        else if (strcmp("variate", qi_cmd->c_str()) == 0)
        {
            json_ret = hpf_fishbowl_fish_count_variate(query_items, json_root);
        }
        else if (strcmp("summed", qi_cmd->c_str()) == 0)
        {
            json_ret = hpf_fishbowl_fish_count_summed(query_items, json_root);
        }
        else if (strcmp("graph", qi_cmd->c_str()) == 0)
        {
        }
        else
        {
            json_root["msg"] = "cmd not supported";
        }
    }
    if (json_ret)
    {
        printf("Content-type: application/json\r\n" "\r\n");
        printf("%s", json_root.toStyledString().c_str());
    }
}
static void hpf_fishbowl_load_v4l2grab()
{
    g_log_msg.flush();
    errno = 0;
    bool ret = false;
    int exit_code = 0;
    const std::string cmd("exec/LOAD_V4L2GRAB");
    std::string result;
    CmdhubClientSession session;
    session.recv_retry = 20;
    if (ret = cmdhub_client_init(session))
    {
        if (ret = cmdhub_client_connect(session))
        {
            ret = cmdhub_client_command(session, cmd, result, exit_code);
            cmdhub_client_destory(session);
        }
    }
    Json::Value json_root;
    json_root["return"] = ret;
    if (ret)
    {
        json_root["exitcode"] = exit_code;
        json_root["result"] = result;
    }
    else
    {
        json_root["errno"] = errno;
    }
    printf("Content-type: application/json\r\n" "\r\n");
    printf("%s", json_root.toStyledString().c_str());
}
int main ()
{
    g_initial_nv = environ;
    g_url_processors["/echo"] = hpf_echo;
    g_url_processors["/fishbowl-basic-data"] = hpf_fishbowl_basic_data;
    g_url_processors["/fishbowl-bdtype"] = hpf_fishbowl_bdtype;
    g_url_processors["/fishbowl-fish-count"] = hpf_fishbowl_fish_count;
    g_url_processors["/fishbowl-fish-type"] = hpf_fishbowl_fish_type;
    g_url_processors["/fishbowl-fish-diary"] = hpf_fishbowl_fish_diary;
    g_url_processors["/fishbowl-load-v4l2grab"] = hpf_fishbowl_load_v4l2grab;
    //g_url_processors["/fishbowl-reminder"] = hpf_fishbowl_reminder;
    if(psql_conn() == nullptr)
    {
        cerr << g_log_msg.rdbuf();
        exit(EXIT_FAILURE);
    }
    cerr << "begin accept access" << endl;
    int fcgi_accept_status;
    while ((fcgi_accept_status = FCGI_Accept()) >= 0)
    {
        g_req_count++;
        g_log_msg.flush();
        char* req_PATH_INFO = getenv("PATH_INFO");
        if(req_PATH_INFO == nullptr)
        {
            cerr << "PATH_INFO is null" << endl;
            exit(EXIT_FAILURE);
        }
        upf_map_t::iterator iter_upf = g_url_processors.find(req_PATH_INFO);
        if (iter_upf == g_url_processors.end())
        {
            http_res_404();
        }
        else
        {
            url_process_func_t proc = iter_upf->second;
            proc();
        }
    }
    PGconn* conn = psql_conn();
    if (conn != nullptr)
        PQfinish(conn);
    cerr << "exit gracefully, fcgi_accept_status=" << fcgi_accept_status << endl;
    return 0;
}
Lighttpd/conf.back/fastcgi.conf
New file
@@ -0,0 +1,113 @@
#######################################################################
##
##  FastCGI Module
## ---------------
##
## See http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI
##
server.modules += ( "mod_fastcgi" )
##
## PHP Example
## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini.
##
## The number of php processes you will get can be easily calculated:
##
## num-procs = max-procs * ( 1 + PHP_FCGI_CHILDREN )
##
## for the php-num-procs example it means you will get 17*5 = 85 php
## processes. you always should need this high number for your very
## busy sites. And if you have a lot of RAM. :)
##
fastcgi.server = (
#diy
                    "/mycgi" =>
                    (
                        "bin-path" => "/opt/httpd/mycgi",
                        "check-local" => "disable",
                        "socket" => "/tmp/mycgi.socket"
                    )
                )
##
## Ruby on Rails Example
##
## Normally you only run one Rails application on one vhost.
##
#$HTTP["host"] == "rails1.example.com" {
#  server.document-root  = server_root + "/rails/someapp/public"
#  server.error-handler-404 = "/dispatch.fcgi"
#  fastcgi.server = ( ".fcgi" =>
#    ("someapp" =>
#      ( "socket" => socket_dir + "/someapp-fcgi.socket",
#        "bin-path" => server_root + "/rails/someapp/public/dispatch.fcgi",
#        "bin-environment" => (
#              "RAILS_ENV" => "production",
#              "TMP" => home_dir + "/rails/someapp",
#        ),
#      )
#    )
#  )
#}
##
## Another example with multiple rails applications on one vhost.
##
## http://blog.lighttpd.net/articles/2005/11/23/lighttpd-1-4-8-and-multiple-rails-apps
##
#$HTTP["host"] == "rails2.example.com" {
#  $HTTP["url"] =~ "^/someapp1" {
#    server.document-root  = server_root + "/rails/someapp1/public"
#    server.error-handler-404 = "/dispatch.fcgi"
#    fastcgi.server = ( ".fcgi" =>
#      ("someapp1" =>
#        ( "socket" => socket_dir + "/someapp1-fcgi.socket",
#          "bin-path" => server_root + "/rails/someapp1/public/dispatch.fcgi",
#          "bin-environment" => (
#                "RAILS_ENV" => "production",
#                "TMP" => home_dir + "/rails/someapp1",
#          ),
#          "strip-request-uri" => "/someapp1/"
#        )
#      )
#    )
#  }
#
#  $HTTP["url"] =~ "^/someapp2" {
#    server.document-root  = server_root + "/rails/someapp2/public"
#    server.error-handler-404 = "/dispatch.fcgi"
#    fastcgi.server = ( ".fcgi" =>
#      ("someapp2" =>
#        ( "socket" => socket_dir + "/someapp2-fcgi.socket",
#          "bin-path" => server_root + "/rails/someapp2/public/dispatch.fcgi",
#          "bin-environment" => (
#                "RAILS_ENV" => "production",
#                "TMP" => home_dir + "/rails/someapp2",
#          ),
#          "strip-request-uri" => "/someapp2/"
#        )
#      )
#    )
#  }
#}
## chrooted webserver + external PHP
##
## $ spawn-fcgi -f /usr/bin/php-cgi -p 2000 -a 127.0.0.1 -C 8
##
## webserver chrooted to /srv/www/
## php running outside the chroot
#
#fastcgi.server = (
#  ".php" => ((
#    "host" => "127.0.0.1",
#    "port" => "2000",
#    "docroot" => "/srv/www/servers/www.example.org/htdocs/"
#  )))
#
#server.chroot = "/srv/www"
#server.document-root = "/servers/wwww.example.org/htdocs/"
#
##
#######################################################################
Lighttpd/conf.back/lighttpd.conf
New file
@@ -0,0 +1,39 @@
# See /usr/share/doc/lighttpd
# and http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ConfigurationOptions
server.port = 80
server.username = "http"
server.groupname = "http"
server.document-root = "/srv/http"
server.errorlog = "/var/log/lighttpd/error.log"
server.modules += (
    "mod_accesslog",
    "mod_fastcgi"
)
accesslog.filename = "/var/log/lighttpd/accesslog.log"
dir-listing.activate = "enable"
index-file.names = ( "index.html" )
mimetype.assign = (
    ".html" => "text/html",
    ".txt" => "text/plain",
    ".css" => "text/css",
    ".js" => "application/x-javascript",
    ".jpg" => "image/jpeg",
    ".jpeg" => "image/jpeg",
    ".bmp" => "image/bmp",
    ".gif" => "image/gif",
    ".png" => "image/png",
    "" => "application/octet-stream"
)
fastcgi.debug = 1
fastcgi.server = (
    ".php" => ((
        "bin-path" => "/usr/bin/php-cgi",
        "socket" => "/tmp/php.socket"
    )),
    #"/ctrlc" => ((
    #    "bin-path" => "/opt/my_service/ctrlcweb/mycgi",
    #    "check-local" => "disable",
    #    "socket" => "/tmp/ctrlcweb-mycgi.socket"
    #))
)
Lighttpd/mycgi/ccgi.cpp
New file
@@ -0,0 +1,255 @@
#ifndef nullptr
#define nullptr NULL
#endif
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <cctype>
using namespace std;
// http://www.fastcgi.com
#include <fcgi_stdio.h>
#include <fcgi_config.h>
// http://www.postgresql.org
//#include <pgsql/libpq-fe.h>
#include <libpq-fe.h>
// http://uriparser.sourceforge.net
#include <uriparser/Uri.h>
// https://github.com/open-source-parsers/jsoncpp
//#include <json/json.h>
extern char **environ;
static char **g_initial_nv = nullptr;
static size_t g_req_count = 0;
stringstream g_log_msg;
stringstream g_http_res;
typedef void (*url_process_func_t)();
typedef map<string, url_process_func_t> upf_map_t;
static upf_map_t g_url_processors;
typedef map<string, string> str_map_t;
//=======错误提示=======
static void http_res_200(const char* msg)
{
    FCGI_printf("Status: 20 OK\r\n" "Content-type: text/html\r\n" "\r\n" "%s", msg);
}
static void http_res_400()
{
    FCGI_printf("Status: 400 Bad Request\r\n" "Content-type: text/html\r\n" "\r\n");
}
static void http_res_401()
{
    FCGI_printf("Status: 401 Unauthorized\r\n" "Content-type: text/html\r\n" "\r\n");
}
static void http_res_404()
{
    FCGI_printf("Status: 404 Not Found\r\n" "\r\n");
}
static void http_res_500()
{
    FCGI_printf("Status: 500 test Internal Server Error   \r\n" "\r\n");
}
static void http_res_500(int problem_code = 0)
{
    FCGI_printf("Status: 500 Internal Server Error\r\n" "Content-type: text/html\r\n" "\r\n" "Problem code = %d", problem_code);
}
static void http_res_500(const char* msg)
{
    FCGI_printf("Status: 500 Internal Server Error\r\n" "Content-type: text/html\r\n" "\r\n" "Message = %s", msg);
}
//=======错误提示==end=====
size_t read_shell(const char* cmd, char* buffer, size_t buffer_size, int* exit_status = nullptr, int* exit_code = nullptr)
{
    if (cmd == nullptr)
        return 0;
    FCGI_FILE* fcmd = FCGI_popen(cmd, "r");
    if (fcmd == nullptr)
        return 0;
    size_t red = FCGI_fread(buffer, sizeof(char), buffer_size - 1, fcmd);
    int es = FCGI_pclose(fcmd);
    if (exit_status != nullptr)
        *exit_status = es;
    if (exit_code != nullptr)
        *exit_code = WEXITSTATUS(es);
    return red;
}
void run_in_bash(const char* cmd, int* exit_status = nullptr, int* exit_code = nullptr)
{
#define BASH_WRAPPER "su -s /bin/bash -c '%s' http"
    char buffer[512];
    snprintf(buffer, sizeof(buffer) - 1, BASH_WRAPPER, cmd);
    int es = system(buffer);
    if (exit_status != nullptr)
        *exit_status = es;
    if (exit_code != nullptr)
        *exit_code = WEXITSTATUS(es);
}
static void print_env(const char *label, char **envp)
{
    FCGI_printf("%s:<br>\n<pre>\n", label);
    for ( ; *envp != NULL; envp++)
    {
        FCGI_printf("%s\n", *envp);
    }
    FCGI_printf("</pre><p>\n");
}
static void hpf_echo()
{
    g_log_msg.flush();
    char *contentLength = getenv("CONTENT_LENGTH");
    int len;
    FCGI_printf("Content-type: text/html\r\n"
                "\r\n"
                "<title>FastCGI echo</title>"
                "<h1>FastCGI echo</h1>\n"
                "Request number %d,  Process ID: %d<p>\n", g_req_count, getpid());
    if (contentLength != NULL)
        len = strtol(contentLength, NULL, 10);
    else
        len = 0;
    if (len <= 0)
    {
        FCGI_printf("No data from standard input.<p>\n");
    }
    else
    {
        int i, ch;
        FCGI_printf("Standard input:<br>\n<pre>\n");
        for (i = 0; i < len; i++)
        {
            if ((ch = getchar()) < 0)
            {
                FCGI_printf("Error: Not enough bytes received on standard input<p>\n");
                break;
            }
            putchar(ch);
        }
        FCGI_printf("\n</pre><p>\n");
    }
    print_env("Request environment", environ);
    print_env("Initial environment", g_initial_nv);
}
bool parse_query_items(str_map_t& query_items)
{
    return true;
}
size_t get_post_content(char* buffer, size_t max_size, size_t offset_content = 0)
{
    if (buffer == NULL || max_size == 0)
        return 0;
    const char* contentLength = getenv("CONTENT_LENGTH");
    size_t len = 0;
    if (contentLength != NULL)
        len = strtol(contentLength, NULL, 10);
    if (len == 0)
        return 0;
    if (offset_content > 0)
        fseek(stdin, offset_content, SEEK_SET);
    return fread(buffer, sizeof(char), min(max_size, len), stdin);
}
const string* try_get_str_from_map(const str_map_t& str_map, const string& key, const string* default_value = nullptr)
{
    str_map_t::const_iterator iter = str_map.find(key);
    if (iter == str_map.end())
        return default_value;
    else
        return &(iter->second);
}
static void hpf_fishbowl_fish_count()
{
}
static void hpf_fishbowl_load_v4l2grab()
{
}
int main ()
{
    g_initial_nv = environ;
    cerr << "begin accept access" << endl;
    int fcgi_accept_status;
    while ((fcgi_accept_status = FCGI_Accept()) >= 0)
    {
        g_req_count++;
        g_log_msg.flush();
        char* req_PATH_INFO = getenv("PATH_INFO");
        if(req_PATH_INFO == nullptr)
        {
            cerr << "PATH_INFO is null" << endl;
            exit(EXIT_FAILURE);
        }
        upf_map_t::iterator iter_upf = g_url_processors.find(req_PATH_INFO);
        if (iter_upf == g_url_processors.end())
        {
            http_res_404();
        }
        else
        {
            url_process_func_t proc = iter_upf->second;
            proc();
        }
    }
    cerr << "exit gracefully, fcgi_accept_status=" << fcgi_accept_status << endl;
    return 0;
}
Lighttpd/mycgi/mycgi.cpp
New file
@@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcgi_stdio.h>
#include <fcgi_config.h>
int main(void)
{
    int fcgi_accept_status;
    while ((fcgi_accept_status = FCGI_Accept()) >= 0)
    {
    int count = 0;
    printf("Content-type: text/html\r\n"
        "\r\n"
        "<title>CGI Hello!</title>"
        "<h1>CGI Hello!</h1>"
        "Request number %d running on host <i>%s</i>\n",
        ++count, getenv("SERVER_NAME"));
    }
    return 0;
}