| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | [common] | 
 |  |  | domain="studio.iot-yun.club" | 
 |  |  |  | 
 |  |  | [host1] | 
 |  |  | ip=47.74.239.156 | 
 |  |  | port=10001 | 
 |  |  |  | 
 |  |  | [host2] | 
 |  |  | ip=122.51.234.174 | 
 |  |  | port=10001 | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      Copyright:  (C) 2018 LingYun IoT Studio | 
 |  |  |  *                  All rights reserved. | 
 |  |  |  * | 
 |  |  |  *       Filename:  ddns_client.c | 
 |  |  |  *    Description:  This is a ddns client code to report public IP address to  | 
 |  |  |  *                  server, it just need to send the domain name, and server  | 
 |  |  |  *                  can get its public IP address. | 
 |  |  |  *                  | 
 |  |  |  *        Version:  1.0.0(1/1/2019) | 
 |  |  |  *         Author:  Guo Wenxue <guowenxue@gmail.com> | 
 |  |  |  *      ChangeLog:  1, Release initial version on "2019-1-1 01:38:08 PM" | 
 |  |  |  *                  | 
 |  |  |  ********************************************************************************/ | 
 |  |  | #include <stdio.h> | 
 |  |  | #include <errno.h> | 
 |  |  | #include <string.h> | 
 |  |  | #include <unistd.h> | 
 |  |  | #include <sys/types.h> | 
 |  |  | #include <sys/socket.h> | 
 |  |  | #include <netinet/in.h> | 
 |  |  | #include <arpa/inet.h> | 
 |  |  | #include <getopt.h> | 
 |  |  | #include <libgen.h> | 
 |  |  |  | 
 |  |  | #include "iniparser.h" | 
 |  |  |  | 
 |  |  | #define MAX_HOSTS             3 | 
 |  |  | #define PROG_VERSION          "v1.0.0" | 
 |  |  |  | 
 |  |  | int socket_send_domain(char *serverip, int port, char *msg); | 
 |  |  |  | 
 |  |  | static void program_usage(char *progname) | 
 |  |  | {  | 
 |  |  |     printf("Usage: %s [OPTION]...\n", progname);  | 
 |  |  |     printf(" %s is LingYun studio DDNS client program to report public IP address\n", progname);  | 
 |  |  |  | 
 |  |  |     printf("\nMandatory arguments to long options are mandatory for short options too:\n");  | 
 |  |  |     printf(" -c[conf    ]  Specify configure file\n");  | 
 |  |  |     printf(" -h[help    ]  Display this help information\n");  | 
 |  |  |     printf(" -v[version ]  Display the program version\n");  | 
 |  |  | 	 | 
 |  |  |     printf("\n%s version %s\n", progname, PROG_VERSION); return; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | int main(int argc, char **argv) | 
 |  |  | { | 
 |  |  |     char              *conf_file = "./ddns.conf"; | 
 |  |  |     char              *progname=NULL; | 
 |  |  |     int                opt; | 
 |  |  |     dictionary        *ini; | 
 |  |  |     char              *str; | 
 |  |  |     int                val; | 
 |  |  |     char              *domain; | 
 |  |  |     char              *serverip; | 
 |  |  |     int                port; | 
 |  |  |     int                i; | 
 |  |  |     char               key[64]; | 
 |  |  |  | 
 |  |  |     struct option long_options[] = {  | 
 |  |  |         {"conf", required_argument, NULL, 'c'}, | 
 |  |  |         {"version", no_argument, NULL, 'v'}, | 
 |  |  |         {"help", no_argument, NULL, 'h'}, | 
 |  |  |         {NULL, 0, NULL, 0} | 
 |  |  |     };  | 
 |  |  |  | 
 |  |  |     progname = (char *)basename(argv[0]); | 
 |  |  |  | 
 |  |  |     /*  Parser the command line parameters */  | 
 |  |  |     while ((opt = getopt_long(argc, argv, "c:dvh", long_options, NULL)) != -1)  | 
 |  |  |     {    | 
 |  |  |         switch (opt) | 
 |  |  |         {    | 
 |  |  |             case 'c': /*  Set configure file */  | 
 |  |  |                 conf_file = optarg; | 
 |  |  |                 break;  | 
 |  |  |  | 
 |  |  |             case 'v':  /*  Get software version */  | 
 |  |  |                 printf("%s version %s\n", progname, PROG_VERSION);  | 
 |  |  |                 return 0;  | 
 |  |  |  | 
 |  |  |             case 'h':  /*  Get help information */  | 
 |  |  |                 program_usage(progname);  | 
 |  |  |                 return 0;  | 
 |  |  |  | 
 |  |  |             default: | 
 |  |  |                 break; | 
 |  |  |         }  | 
 |  |  |     }  | 
 |  |  |  | 
 |  |  |     ini = iniparser_load(conf_file);  | 
 |  |  |     if( !ini )  | 
 |  |  |     {    | 
 |  |  |         printf("ERROR: cannot parse file: '%s'\n", conf_file);  | 
 |  |  |         return -1;  | 
 |  |  |     }    | 
 |  |  |  | 
 |  |  |     domain=iniparser_getstring(ini, "common:domain", NULL); | 
 |  |  |     if( !domain ) | 
 |  |  |     { | 
 |  |  |         printf("ERROR: cannot parse domain in '%s'\n", conf_file);  | 
 |  |  |         return -1;  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     for(i=1; i<MAX_HOSTS; i++) | 
 |  |  |     { | 
 |  |  |         memset(key, 0, sizeof(key)); | 
 |  |  |         snprintf(key, sizeof(key), "host%d:ip", i); | 
 |  |  |  | 
 |  |  |         serverip = iniparser_getstring(ini, key, NULL); | 
 |  |  |  | 
 |  |  |         snprintf(key, sizeof(key), "host%d:port", i); | 
 |  |  |         port = iniparser_getint(ini, key, 0); | 
 |  |  |  | 
 |  |  |         if( !serverip || !port ) | 
 |  |  |         { | 
 |  |  |             continue; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         printf("==> Start send domain '%s' to [%s:%d]\n", domain, serverip, port); | 
 |  |  |         socket_send_domain(serverip, port, domain); | 
 |  |  |         printf("\n"); | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     iniparser_freedict(ini); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | int socket_send_domain(char *serverip, int port, char *msg) | 
 |  |  | { | 
 |  |  |     int                     conn_fd = -1; | 
 |  |  |     int                     rv = -1;     | 
 |  |  |     struct sockaddr_in      serv_addr; | 
 |  |  |  | 
 |  |  |     struct sockaddr_in      addr; | 
 |  |  |     struct timeval          timeo = {6, 0}; | 
 |  |  |     socklen_t               len = sizeof(timeo); | 
 |  |  |  | 
 |  |  |     conn_fd = socket(AF_INET, SOCK_STREAM, 0); | 
 |  |  |     if(conn_fd < 0) | 
 |  |  |     { | 
 |  |  |         printf("ERROR: create socket failure: %s\n", strerror(errno)); | 
 |  |  |         return -1; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     memset(&serv_addr, 0, sizeof(serv_addr)); | 
 |  |  |     serv_addr.sin_family = AF_INET; | 
 |  |  |     serv_addr.sin_port = htons(port); | 
 |  |  |     inet_aton( serverip, &serv_addr.sin_addr ); | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     setsockopt(conn_fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len); | 
 |  |  |  | 
 |  |  |     if( connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))  < 0) | 
 |  |  |     { | 
 |  |  |         printf("ERROR: connect to server [%s:%d] failure: %s\n", serverip, port, strerror(errno)); | 
 |  |  |         goto cleanup; | 
 |  |  |     } | 
 |  |  |      | 
 |  |  |     if( write(conn_fd, msg, strlen(msg)) < 0 ) | 
 |  |  |     { | 
 |  |  |         printf("ERROR: Write data to server [%s:%d] failure: %s\n", serverip, port, strerror(errno));  | 
 |  |  |         goto cleanup; | 
 |  |  |     } | 
 |  |  |     printf("ok\n"); | 
 |  |  |  | 
 |  |  |     sleep(1); | 
 |  |  |      | 
 |  |  | cleanup: | 
 |  |  |     close(conn_fd); | 
 |  |  | } | 
 |  |  |  | 
 
| New file | 
 |  |  | 
 |  |  | #include <stdio.h>
 | 
 |  |  | #include <stdlib.h>
 | 
 |  |  | #include <unistd.h>
 | 
 |  |  | #include <string.h>
 | 
 |  |  | #include <errno.h>
 | 
 |  |  | #include <ctype.h>
 | 
 |  |  | #include <time.h>
 | 
 |  |  | #include <pthread.h>
 | 
 |  |  | #include <getopt.h>
 | 
 |  |  | #include <libgen.h>  | 
 |  |  | #include <sys/types.h>    | 
 |  |  | #include <sys/socket.h>
 | 
 |  |  | #include <arpa/inet.h>
 | 
 |  |  | #include <netinet/in.h>  | 
 |  |  | #include <sys/epoll.h>
 | 
 |  |  | #include <sys/resource.h>
 | 
 |  |  | 
 | 
 |  |  | #include "logger.h"
 | 
 |  |  | 
 | 
 |  |  | #define LOGGER              "/var/log/ddns_server.log"
 | 
 |  |  | 
 | 
 |  |  | #define MAX_EVENTS          512
 | 
 |  |  | #define ARRAY_SIZE(x)       (sizeof(x)/sizeof(x[0]))
 | 
 |  |  | 
 | 
 |  |  | static inline void print_usage(char *progname);
 | 
 |  |  | int socket_server_init(char *listen_ip, int listen_port);
 | 
 |  |  | void set_socket_rlimit(void);
 | 
 |  |  | 
 | 
 |  |  | int main(int argc, char **argv)
 | 
 |  |  | {
 | 
 |  |  |     int                       listenfd, connfd;
 | 
 |  |  |     int                       serv_port = 0;
 | 
 |  |  |     int                       daemon_run = 0;
 | 
 |  |  |     char                     *progname = NULL;
 | 
 |  |  |     int                       opt;
 | 
 |  |  |     int                       rv;
 | 
 |  |  |     int                       i, j;
 | 
 |  |  |     int                       found;
 | 
 |  |  |     char                      buf[1024];
 | 
 |  |  | 
 | 
 |  |  |     int                       epollfd;
 | 
 |  |  |     struct epoll_event        event;
 | 
 |  |  |     struct epoll_event        event_array[MAX_EVENTS];
 | 
 |  |  |     int                       events;
 | 
 |  |  |       | 
 |  |  |     struct option             long_options[] =  | 
 |  |  |     {    | 
 |  |  |         {"daemon", no_argument, NULL, 'b'},
 | 
 |  |  |         {"port", required_argument, NULL, 'p'},
 | 
 |  |  |         {"help", no_argument, NULL, 'h'},
 | 
 |  |  |         {NULL, 0, NULL, 0}
 | 
 |  |  |     };   | 
 |  |  | 
 | 
 |  |  |     progname = basename(argv[0]);
 | 
 |  |  | 
 | 
 |  |  |     /*  Parser the command line parameters */
 | 
 |  |  |     while ((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)  | 
 |  |  |     {    | 
 |  |  |         switch (opt)
 | 
 |  |  |         {    | 
 |  |  |             case 'b':
 | 
 |  |  |                 daemon_run=1;
 | 
 |  |  |                 break;
 | 
 |  |  | 
 | 
 |  |  |             case 'p':
 | 
 |  |  |                 serv_port = atoi(optarg);
 | 
 |  |  |                 break;
 | 
 |  |  | 
 | 
 |  |  |             case 'h':  /*  Get help information */
 | 
 |  |  |                 print_usage(progname);
 | 
 |  |  |                 return EXIT_SUCCESS;
 | 
 |  |  | 
 | 
 |  |  |             default:
 | 
 |  |  |                 break;
 | 
 |  |  |         }    | 
 |  |  |     }    | 
 |  |  | 
 | 
 |  |  |     if( !serv_port )  | 
 |  |  |     {    | 
 |  |  |         print_usage(progname);
 | 
 |  |  |         return -1;  | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     if( logger_init(LOGGER, LOG_LEVEL_DEBUG, 10) < 0 )
 | 
 |  |  |     {
 | 
 |  |  |         printf("initial logger file '%s' failure\n", LOGGER);
 | 
 |  |  |         return 0;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     set_socket_rlimit(); /* set max open socket count */
 | 
 |  |  | 
 | 
 |  |  |     if( (listenfd=socket_server_init(NULL, serv_port)) < 0 )
 | 
 |  |  |     {
 | 
 |  |  |         log_err("ERROR: %s server listen on port %d failure\n", argv[0],serv_port);
 | 
 |  |  |         return -2;
 | 
 |  |  |     }
 | 
 |  |  |     log_dbg("%s server start to listen on port %d\n", argv[0],serv_port);
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     /* set program running on background */
 | 
 |  |  |     if( daemon_run )  | 
 |  |  |     {
 | 
 |  |  |         daemon(0, 0);
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     if( (epollfd=epoll_create(MAX_EVENTS)) < 0 )
 | 
 |  |  |     {
 | 
 |  |  |         log_err("epoll_create() failure: %s\n", strerror(errno));
 | 
 |  |  |         return -3;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     //event.events = EPOLLIN|EPOLLET;
 | 
 |  |  |     event.events = EPOLLIN;
 | 
 |  |  |     event.data.fd = listenfd;
 | 
 |  |  | 
 | 
 |  |  |     if( epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) < 0)
 | 
 |  |  |     {
 | 
 |  |  |         log_err("epoll add listen socket failure: %s\n", strerror(errno));
 | 
 |  |  |         return -4;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     for ( ; ; )  | 
 |  |  |     {
 | 
 |  |  |         /* program will blocked here */
 | 
 |  |  |         events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1);
 | 
 |  |  |         if(events < 0)
 | 
 |  |  |         {
 | 
 |  |  |             log_err("epoll failure: %s\n", strerror(errno));
 | 
 |  |  |             break;
 | 
 |  |  |         }
 | 
 |  |  |         else if(events == 0)
 | 
 |  |  |         {
 | 
 |  |  |             log_err("epoll get timeout\n");
 | 
 |  |  |             continue;
 | 
 |  |  |         }
 | 
 |  |  | 
 | 
 |  |  |         /* rv>0 is the active events count */
 | 
 |  |  |         for(i=0; i<events; i++)
 | 
 |  |  |         {
 | 
 |  |  |             if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
 | 
 |  |  |             {
 | 
 |  |  |                 log_err("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));
 | 
 |  |  |                 epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
 | 
 |  |  |                 close(event_array[i].data.fd);
 | 
 |  |  |             }
 | 
 |  |  | 
 | 
 |  |  |             /* listen socket get event means new client start connect now */
 | 
 |  |  |             if( event_array[i].data.fd == listenfd )
 | 
 |  |  |             {  | 
 |  |  |                 if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0)
 | 
 |  |  |                 {
 | 
 |  |  |                     log_err("accept new client failure: %s\n", strerror(errno));
 | 
 |  |  |                     continue;
 | 
 |  |  |                 }
 | 
 |  |  | 
 | 
 |  |  |                 event.data.fd = connfd;  | 
 |  |  |                 event.events =  EPOLLIN;
 | 
 |  |  |                 if( epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0 )
 | 
 |  |  |                 {
 | 
 |  |  |                     log_err("epoll add client socket failure: %s\n", strerror(errno));
 | 
 |  |  |                     close(event_array[i].data.fd);
 | 
 |  |  |                     continue;
 | 
 |  |  |                 }
 | 
 |  |  |                 log_dbg("epoll add new client socket[%d] ok.\n", connfd);
 | 
 |  |  |             }
 | 
 |  |  | 
 | 
 |  |  |             else /* already connected client socket get data incoming */
 | 
 |  |  |             {
 | 
 |  |  |                 if( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <= 0)
 | 
 |  |  |                 {
 | 
 |  |  |                     log_err("socket[%d] read failure or get disconncet and will be removed.\n", event_array[i].data.fd);
 | 
 |  |  |                     epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
 | 
 |  |  |                     close(event_array[i].data.fd);
 | 
 |  |  |                     continue;
 | 
 |  |  |                 }
 | 
 |  |  |                 else  | 
 |  |  |                 {
 | 
 |  |  |                     log_dbg("socket[%d] read get %d bytes data: %s\n", event_array[i].data.fd, rv, buf);  | 
 |  |  |                 }  | 
 |  |  |             }  | 
 |  |  |         } /* for(i=0; i<rv; i++) */  | 
 |  |  |     } /* while(1) */
 | 
 |  |  | 
 | 
 |  |  | CleanUp:
 | 
 |  |  |     close(listenfd);
 | 
 |  |  |     return 0;
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | static inline void print_usage(char *progname)
 | 
 |  |  | {  | 
 |  |  |     printf("Usage: %s [OPTION]...\n", progname);
 | 
 |  |  |      | 
 |  |  |     printf(" %s is a socket server program, which used to verify client and echo back string from it\n", progname);  | 
 |  |  |     printf("\nMandatory arguments to long options are mandatory for short options too:\n");  | 
 |  |  |      | 
 |  |  |     printf(" -b[daemon ]  set program running on background\n");
 | 
 |  |  |     printf(" -p[port    ]  Socket server port address\n");
 | 
 |  |  |     printf(" -h[help    ]  Display this help information\n");
 | 
 |  |  |      | 
 |  |  | 
 | 
 |  |  |     printf("\nExample: %s -b -p 8900\n", progname);
 | 
 |  |  |     return ;
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  | int socket_server_init(char *listen_ip, int listen_port)
 | 
 |  |  | {
 | 
 |  |  |     struct sockaddr_in        servaddr;
 | 
 |  |  |     int                       rv = 0;
 | 
 |  |  |     int                       on = 1;
 | 
 |  |  |     int                       listenfd;
 | 
 |  |  | 
 | 
 |  |  |     if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 | 
 |  |  |     {
 | 
 |  |  |         printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
 | 
 |  |  |         return -1;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     /* Set socket port reuseable, fix 'Address already in use' bug when socket server restart */
 | 
 |  |  |     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
 | 
 |  |  | 
 | 
 |  |  |     memset(&servaddr, 0, sizeof(servaddr));
 | 
 |  |  |     servaddr.sin_family = AF_INET;    | 
 |  |  |     servaddr.sin_port = htons(listen_port);  | 
 |  |  | 
 | 
 |  |  |     if( !listen_ip )  /* Listen all the local IP address */
 | 
 |  |  |     {
 | 
 |  |  |         servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   | 
 |  |  |     }
 | 
 |  |  |     else /* listen the specified IP address  */
 | 
 |  |  |     {
 | 
 |  |  |         if (inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0)
 | 
 |  |  |         {
 | 
 |  |  |             printf("inet_pton() set listen IP address failure.\n");
 | 
 |  |  |         rv = -2;
 | 
 |  |  |             goto CleanUp;
 | 
 |  |  |         }
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | 
 | 
 |  |  |     if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
 | 
 |  |  |     {
 | 
 |  |  |         printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
 | 
 |  |  |         rv = -3;
 | 
 |  |  |         goto CleanUp;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  |     if(listen(listenfd, 13) < 0)
 | 
 |  |  |     {
 | 
 |  |  |         printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
 | 
 |  |  |         rv = -4;
 | 
 |  |  |         goto CleanUp;
 | 
 |  |  |     }
 | 
 |  |  | 
 | 
 |  |  | CleanUp:
 | 
 |  |  |     if(rv<0)
 | 
 |  |  |         close(listenfd);
 | 
 |  |  |     else
 | 
 |  |  |         rv = listenfd;
 | 
 |  |  | 
 | 
 |  |  |     return rv;
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 |  |  | /* Set open file description count to max */
 | 
 |  |  | void set_socket_rlimit(void)
 | 
 |  |  | {
 | 
 |  |  |      struct rlimit limit = {0};
 | 
 |  |  | 
 | 
 |  |  |      getrlimit(RLIMIT_NOFILE, &limit );
 | 
 |  |  |      limit.rlim_cur  = limit.rlim_max;
 | 
 |  |  |      setrlimit(RLIMIT_NOFILE, &limit );
 | 
 |  |  | 
 | 
 |  |  |      printf("set socket open fd max count to %d\n", (int)limit.rlim_max);
 | 
 |  |  | }
 | 
 |  |  | 
 | 
 
| New file | 
 |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @file    ini_dictionary.c | 
 |  |  |    @author  N. Devillard | 
 |  |  |    @brief   Implements a dictionary for string variables. | 
 |  |  |  | 
 |  |  |    This module implements a simple dictionary object, i.e. a list | 
 |  |  |    of string/string associations. This object is useful to store e.g. | 
 |  |  |    informations retrieved from a configuration file (ini files). | 
 |  |  | */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                                 Includes | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  | #include "ini_dictionary.h" | 
 |  |  |  | 
 |  |  | #include <stdio.h> | 
 |  |  | #include <stdlib.h> | 
 |  |  | #include <string.h> | 
 |  |  | #include <unistd.h> | 
 |  |  |  | 
 |  |  | /** Maximum value size for integers and doubles. */ | 
 |  |  | #define MAXVALSZ    1024 | 
 |  |  |  | 
 |  |  | /** Minimal allocated number of entries in a dictionary */ | 
 |  |  | #define DICTMINSZ   128 | 
 |  |  |  | 
 |  |  | /** Invalid key token */ | 
 |  |  | #define DICT_INVALID_KEY    ((char*)-1) | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                             Private functions | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | /* Doubles the allocated size associated to a pointer */ | 
 |  |  | /* 'size' is the current allocated size. */ | 
 |  |  | static void * mem_double(void * ptr, int size) | 
 |  |  | { | 
 |  |  |     void * newptr ; | 
 |  |  |   | 
 |  |  |     newptr = calloc(2*size, 1); | 
 |  |  |     if (newptr==NULL) { | 
 |  |  |         return NULL ; | 
 |  |  |     } | 
 |  |  |     memcpy(newptr, ptr, size); | 
 |  |  |     free(ptr); | 
 |  |  |     return newptr ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Duplicate a string | 
 |  |  |   @param    s String to duplicate | 
 |  |  |   @return   Pointer to a newly allocated string, to be freed with free() | 
 |  |  |  | 
 |  |  |   This is a replacement for strdup(). This implementation is provided | 
 |  |  |   for systems that do not have it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | static char * xstrdup(const char * s) | 
 |  |  | { | 
 |  |  |     char * t ; | 
 |  |  |     if (!s) | 
 |  |  |         return NULL ; | 
 |  |  |     t = (char*)malloc(strlen(s)+1) ; | 
 |  |  |     if (t) { | 
 |  |  |         strcpy(t,s); | 
 |  |  |     } | 
 |  |  |     return t ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                             Function codes | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Compute the hash key for a string. | 
 |  |  |   @param    key     Character string to use for key. | 
 |  |  |   @return   1 unsigned int on at least 32 bits. | 
 |  |  |  | 
 |  |  |   This hash function has been taken from an Article in Dr Dobbs Journal. | 
 |  |  |   This is normally a collision-free function, distributing keys evenly. | 
 |  |  |   The key is stored anyway in the struct so that collision can be avoided | 
 |  |  |   by comparing the key itself in last resort. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | unsigned dictionary_hash(const char * key) | 
 |  |  | { | 
 |  |  |     int         len ; | 
 |  |  |     unsigned    hash ; | 
 |  |  |     int         i ; | 
 |  |  |  | 
 |  |  |     len = strlen(key); | 
 |  |  |     for (hash=0, i=0 ; i<len ; i++) { | 
 |  |  |         hash += (unsigned)key[i] ; | 
 |  |  |         hash += (hash<<10); | 
 |  |  |         hash ^= (hash>>6) ; | 
 |  |  |     } | 
 |  |  |     hash += (hash <<3); | 
 |  |  |     hash ^= (hash >>11); | 
 |  |  |     hash += (hash <<15); | 
 |  |  |     return hash ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Create a new dictionary object. | 
 |  |  |   @param    size    Optional initial size of the dictionary. | 
 |  |  |   @return   1 newly allocated dictionary objet. | 
 |  |  |  | 
 |  |  |   This function allocates a new dictionary object of given size and returns | 
 |  |  |   it. If you do not know in advance (roughly) the number of entries in the | 
 |  |  |   dictionary, give size=0. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | dictionary * dictionary_new(int size) | 
 |  |  | { | 
 |  |  |     dictionary  *   d ; | 
 |  |  |  | 
 |  |  |     /* If no size was specified, allocate space for DICTMINSZ */ | 
 |  |  |     if (size<DICTMINSZ) size=DICTMINSZ ; | 
 |  |  |  | 
 |  |  |     if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) { | 
 |  |  |         return NULL; | 
 |  |  |     } | 
 |  |  |     d->size = size ; | 
 |  |  |     d->val  = (char **)calloc(size, sizeof(char*)); | 
 |  |  |     d->key  = (char **)calloc(size, sizeof(char*)); | 
 |  |  |     d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); | 
 |  |  |     return d ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete a dictionary object | 
 |  |  |   @param    d   dictionary object to deallocate. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Deallocate a dictionary object and all memory associated to it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_del(dictionary * d) | 
 |  |  | { | 
 |  |  |     int     i ; | 
 |  |  |  | 
 |  |  |     if (d==NULL) return ; | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]!=NULL) | 
 |  |  |             free(d->key[i]); | 
 |  |  |         if (d->val[i]!=NULL) | 
 |  |  |             free(d->val[i]); | 
 |  |  |     } | 
 |  |  |     free(d->val); | 
 |  |  |     free(d->key); | 
 |  |  |     free(d->hash); | 
 |  |  |     free(d); | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get a value from a dictionary. | 
 |  |  |   @param    d       dictionary object to search. | 
 |  |  |   @param    key     Key to look for in the dictionary. | 
 |  |  |   @param    def     Default value to return if key not found. | 
 |  |  |   @return   1 pointer to internally allocated character string. | 
 |  |  |  | 
 |  |  |   This function locates a key in a dictionary and returns a pointer to its | 
 |  |  |   value, or the passed 'def' pointer if no such key can be found in | 
 |  |  |   dictionary. The returned character pointer points to data internal to the | 
 |  |  |   dictionary object, you should not try to free it or modify it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char * dictionary_get(dictionary * d, const char * key, char * def) | 
 |  |  | { | 
 |  |  |     unsigned    hash ; | 
 |  |  |     int         i ; | 
 |  |  |  | 
 |  |  |     hash = dictionary_hash(key); | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         /* Compare hash */ | 
 |  |  |         if (hash==d->hash[i]) { | 
 |  |  |             /* Compare string, to avoid hash collisions */ | 
 |  |  |             if (!strcmp(key, d->key[i])) { | 
 |  |  |                 return d->val[i] ; | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     return def ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Set a value in a dictionary. | 
 |  |  |   @param    d       dictionary object to modify. | 
 |  |  |   @param    key     Key to modify or add. | 
 |  |  |   @param    val     Value to add. | 
 |  |  |   @return   int     0 if Ok, anything else otherwise | 
 |  |  |  | 
 |  |  |   If the given key is found in the dictionary, the associated value is | 
 |  |  |   replaced by the provided one. If the key cannot be found in the | 
 |  |  |   dictionary, it is added to it. | 
 |  |  |  | 
 |  |  |   It is Ok to provide a NULL value for val, but NULL values for the dictionary | 
 |  |  |   or the key are considered as errors: the function will return immediately | 
 |  |  |   in such a case. | 
 |  |  |  | 
 |  |  |   Notice that if you dictionary_set a variable to NULL, a call to | 
 |  |  |   dictionary_get will return a NULL value: the variable will be found, and | 
 |  |  |   its value (NULL) is returned. In other words, setting the variable | 
 |  |  |   content to NULL is equivalent to deleting the variable from the | 
 |  |  |   dictionary. It is not possible (in this implementation) to have a key in | 
 |  |  |   the dictionary without value. | 
 |  |  |  | 
 |  |  |   This function returns non-zero in case of failure. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int dictionary_set(dictionary * d, const char * key, const char * val) | 
 |  |  | { | 
 |  |  |     int         i ; | 
 |  |  |     unsigned    hash ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || key==NULL) return -1 ; | 
 |  |  |      | 
 |  |  |     /* Compute hash for this key */ | 
 |  |  |     hash = dictionary_hash(key) ; | 
 |  |  |     /* Find if value is already in dictionary */ | 
 |  |  |     if (d->n>0) { | 
 |  |  |         for (i=0 ; i<d->size ; i++) { | 
 |  |  |             if (d->key[i]==NULL) | 
 |  |  |                 continue ; | 
 |  |  |             if (hash==d->hash[i]) { /* Same hash value */ | 
 |  |  |                 if (!strcmp(key, d->key[i])) {   /* Same key */ | 
 |  |  |                     /* Found a value: modify and return */ | 
 |  |  |                     if (d->val[i]!=NULL) | 
 |  |  |                         free(d->val[i]); | 
 |  |  |                     d->val[i] = val ? xstrdup(val) : NULL ; | 
 |  |  |                     /* Value has been modified: return */ | 
 |  |  |                     return 0 ; | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     /* Add a new value */ | 
 |  |  |     /* See if dictionary needs to grow */ | 
 |  |  |     if (d->n==d->size) { | 
 |  |  |  | 
 |  |  |         /* Reached maximum size: reallocate dictionary */ | 
 |  |  |         d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ; | 
 |  |  |         d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ; | 
 |  |  |         d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; | 
 |  |  |         if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { | 
 |  |  |             /* Cannot grow dictionary */ | 
 |  |  |             return -1 ; | 
 |  |  |         } | 
 |  |  |         /* Double size */ | 
 |  |  |         d->size *= 2 ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /* Insert key in the first empty slot. Start at d->n and wrap at | 
 |  |  |        d->size. Because d->n < d->size this will necessarily | 
 |  |  |        terminate. */ | 
 |  |  |     for (i=d->n ; d->key[i] ; ) { | 
 |  |  |         if(++i == d->size) i = 0; | 
 |  |  |     } | 
 |  |  |     /* Copy key */ | 
 |  |  |     d->key[i]  = xstrdup(key); | 
 |  |  |     d->val[i]  = val ? xstrdup(val) : NULL ; | 
 |  |  |     d->hash[i] = hash; | 
 |  |  |     d->n ++ ; | 
 |  |  |     return 0 ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete a key in a dictionary | 
 |  |  |   @param    d       dictionary object to modify. | 
 |  |  |   @param    key     Key to remove. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function deletes a key in a dictionary. Nothing is done if the | 
 |  |  |   key cannot be found. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_unset(dictionary * d, const char * key) | 
 |  |  | { | 
 |  |  |     unsigned    hash ; | 
 |  |  |     int         i ; | 
 |  |  |  | 
 |  |  |     if (key == NULL) { | 
 |  |  |         return; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     hash = dictionary_hash(key); | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         /* Compare hash */ | 
 |  |  |         if (hash==d->hash[i]) { | 
 |  |  |             /* Compare string, to avoid hash collisions */ | 
 |  |  |             if (!strcmp(key, d->key[i])) { | 
 |  |  |                 /* Found key */ | 
 |  |  |                 break ; | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     if (i>=d->size) | 
 |  |  |         /* Key not found */ | 
 |  |  |         return ; | 
 |  |  |  | 
 |  |  |     free(d->key[i]); | 
 |  |  |     d->key[i] = NULL ; | 
 |  |  |     if (d->val[i]!=NULL) { | 
 |  |  |         free(d->val[i]); | 
 |  |  |         d->val[i] = NULL ; | 
 |  |  |     } | 
 |  |  |     d->hash[i] = 0 ; | 
 |  |  |     d->n -- ; | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Dump a dictionary to an opened file pointer. | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    f   Opened file pointer. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Dumps a dictionary onto an opened file pointer. Key pairs are printed out | 
 |  |  |   as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as | 
 |  |  |   output file pointers. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_dump(dictionary * d, FILE * out) | 
 |  |  | { | 
 |  |  |     int     i ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || out==NULL) return ; | 
 |  |  |     if (d->n<1) { | 
 |  |  |         fprintf(out, "empty dictionary\n"); | 
 |  |  |         return ; | 
 |  |  |     } | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]) { | 
 |  |  |             fprintf(out, "%20s\t[%s]\n", | 
 |  |  |                     d->key[i], | 
 |  |  |                     d->val[i] ? d->val[i] : "UNDEF"); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /* Test code */ | 
 |  |  | #ifdef TESTDIC | 
 |  |  | #define NVALS 20000 | 
 |  |  | int main(int argc, char *argv[]) | 
 |  |  | { | 
 |  |  |     dictionary  *   d ; | 
 |  |  |     char    *   val ; | 
 |  |  |     int         i ; | 
 |  |  |     char        cval[90] ; | 
 |  |  |  | 
 |  |  |     /* Allocate dictionary */ | 
 |  |  |     printf("allocating...\n"); | 
 |  |  |     d = dictionary_new(0); | 
 |  |  |      | 
 |  |  |     /* Set values in dictionary */ | 
 |  |  |     printf("setting %d values...\n", NVALS); | 
 |  |  |     for (i=0 ; i<NVALS ; i++) { | 
 |  |  |         sprintf(cval, "%04d", i); | 
 |  |  |         dictionary_set(d, cval, "salut"); | 
 |  |  |     } | 
 |  |  |     printf("getting %d values...\n", NVALS); | 
 |  |  |     for (i=0 ; i<NVALS ; i++) { | 
 |  |  |         sprintf(cval, "%04d", i); | 
 |  |  |         val = dictionary_get(d, cval, DICT_INVALID_KEY); | 
 |  |  |         if (val==DICT_INVALID_KEY) { | 
 |  |  |             printf("cannot get value for key [%s]\n", cval); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     printf("unsetting %d values...\n", NVALS); | 
 |  |  |     for (i=0 ; i<NVALS ; i++) { | 
 |  |  |         sprintf(cval, "%04d", i); | 
 |  |  |         dictionary_unset(d, cval); | 
 |  |  |     } | 
 |  |  |     if (d->n != 0) { | 
 |  |  |         printf("error deleting values\n"); | 
 |  |  |     } | 
 |  |  |     printf("deallocating...\n"); | 
 |  |  |     dictionary_del(d); | 
 |  |  |     return 0 ; | 
 |  |  | } | 
 |  |  | #endif | 
 |  |  | /* vim: set ts=4 et sw=4 tw=75 */ | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @file    ini_dictionary.h | 
 |  |  |    @author  N. Devillard | 
 |  |  |    @brief   Implements a dictionary for string variables. | 
 |  |  |  | 
 |  |  |    This module implements a simple dictionary object, i.e. a list | 
 |  |  |    of string/string associations. This object is useful to store e.g. | 
 |  |  |    informations retrieved from a configuration file (ini files). | 
 |  |  | */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | #ifndef _INI_DICTIONARY_H_ | 
 |  |  | #define _INI_DICTIONARY_H_ | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                                 Includes | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | #include <stdio.h> | 
 |  |  | #include <stdlib.h> | 
 |  |  | #include <string.h> | 
 |  |  | #include <unistd.h> | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                                 New types | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Dictionary object | 
 |  |  |  | 
 |  |  |   This object contains a list of string/string associations. Each | 
 |  |  |   association is identified by a unique string key. Looking up values | 
 |  |  |   in the dictionary is speeded up by the use of a (hopefully collision-free) | 
 |  |  |   hash function. | 
 |  |  |  */ | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | typedef struct _dictionary_ { | 
 |  |  |     int             n ;     /** Number of entries in dictionary */ | 
 |  |  |     int             size ;  /** Storage size */ | 
 |  |  |     char        **  val ;   /** List of string values */ | 
 |  |  |     char        **  key ;   /** List of string keys */ | 
 |  |  |     unsigned     *  hash ;  /** List of hash values for keys */ | 
 |  |  | } dictionary ; | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                             Function prototypes | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Compute the hash key for a string. | 
 |  |  |   @param    key     Character string to use for key. | 
 |  |  |   @return   1 unsigned int on at least 32 bits. | 
 |  |  |  | 
 |  |  |   This hash function has been taken from an Article in Dr Dobbs Journal. | 
 |  |  |   This is normally a collision-free function, distributing keys evenly. | 
 |  |  |   The key is stored anyway in the struct so that collision can be avoided | 
 |  |  |   by comparing the key itself in last resort. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | unsigned dictionary_hash(const char * key); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Create a new dictionary object. | 
 |  |  |   @param    size    Optional initial size of the dictionary. | 
 |  |  |   @return   1 newly allocated dictionary objet. | 
 |  |  |  | 
 |  |  |   This function allocates a new dictionary object of given size and returns | 
 |  |  |   it. If you do not know in advance (roughly) the number of entries in the | 
 |  |  |   dictionary, give size=0. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | dictionary * dictionary_new(int size); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete a dictionary object | 
 |  |  |   @param    d   dictionary object to deallocate. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Deallocate a dictionary object and all memory associated to it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_del(dictionary * vd); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get a value from a dictionary. | 
 |  |  |   @param    d       dictionary object to search. | 
 |  |  |   @param    key     Key to look for in the dictionary. | 
 |  |  |   @param    def     Default value to return if key not found. | 
 |  |  |   @return   1 pointer to internally allocated character string. | 
 |  |  |  | 
 |  |  |   This function locates a key in a dictionary and returns a pointer to its | 
 |  |  |   value, or the passed 'def' pointer if no such key can be found in | 
 |  |  |   dictionary. The returned character pointer points to data internal to the | 
 |  |  |   dictionary object, you should not try to free it or modify it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char * dictionary_get(dictionary * d, const char * key, char * def); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Set a value in a dictionary. | 
 |  |  |   @param    d       dictionary object to modify. | 
 |  |  |   @param    key     Key to modify or add. | 
 |  |  |   @param    val     Value to add. | 
 |  |  |   @return   int     0 if Ok, anything else otherwise | 
 |  |  |  | 
 |  |  |   If the given key is found in the dictionary, the associated value is | 
 |  |  |   replaced by the provided one. If the key cannot be found in the | 
 |  |  |   dictionary, it is added to it. | 
 |  |  |  | 
 |  |  |   It is Ok to provide a NULL value for val, but NULL values for the dictionary | 
 |  |  |   or the key are considered as errors: the function will return immediately | 
 |  |  |   in such a case. | 
 |  |  |  | 
 |  |  |   Notice that if you dictionary_set a variable to NULL, a call to | 
 |  |  |   dictionary_get will return a NULL value: the variable will be found, and | 
 |  |  |   its value (NULL) is returned. In other words, setting the variable | 
 |  |  |   content to NULL is equivalent to deleting the variable from the | 
 |  |  |   dictionary. It is not possible (in this implementation) to have a key in | 
 |  |  |   the dictionary without value. | 
 |  |  |  | 
 |  |  |   This function returns non-zero in case of failure. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int dictionary_set(dictionary * vd, const char * key, const char * val); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete a key in a dictionary | 
 |  |  |   @param    d       dictionary object to modify. | 
 |  |  |   @param    key     Key to remove. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function deletes a key in a dictionary. Nothing is done if the | 
 |  |  |   key cannot be found. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_unset(dictionary * d, const char * key); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Dump a dictionary to an opened file pointer. | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    f   Opened file pointer. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Dumps a dictionary onto an opened file pointer. Key pairs are printed out | 
 |  |  |   as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as | 
 |  |  |   output file pointers. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void dictionary_dump(dictionary * d, FILE * out); | 
 |  |  |  | 
 |  |  | #endif | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @file    iniparser.c | 
 |  |  |    @author  N. Devillard | 
 |  |  |    @brief   Parser for ini files. | 
 |  |  | */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | /*---------------------------- Includes ------------------------------------*/ | 
 |  |  | #include <ctype.h> | 
 |  |  | #include "iniparser.h" | 
 |  |  |  | 
 |  |  | /*---------------------------- Defines -------------------------------------*/ | 
 |  |  | #define ASCIILINESZ         (1024) | 
 |  |  | #define INI_INVALID_KEY     ((char*)-1) | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                         Private to this module | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |  * This enum stores the status for each parsed line (internal use only). | 
 |  |  |  */ | 
 |  |  | typedef enum _line_status_ { | 
 |  |  |     LINE_UNPROCESSED, | 
 |  |  |     LINE_ERROR, | 
 |  |  |     LINE_EMPTY, | 
 |  |  |     LINE_COMMENT, | 
 |  |  |     LINE_SECTION, | 
 |  |  |     LINE_VALUE | 
 |  |  | } line_status ; | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Convert a string to lowercase. | 
 |  |  |   @param    s   String to convert. | 
 |  |  |   @return   ptr to statically allocated string. | 
 |  |  |  | 
 |  |  |   This function returns a pointer to a statically allocated string | 
 |  |  |   containing a lowercased version of the input string. Do not free | 
 |  |  |   or modify the returned string! Since the returned string is statically | 
 |  |  |   allocated, it will be modified at each function call (not re-entrant). | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | static char * strlwc(const char * s) | 
 |  |  | { | 
 |  |  |     static char l[ASCIILINESZ+1]; | 
 |  |  |     int i ; | 
 |  |  |  | 
 |  |  |     if (s==NULL) return NULL ; | 
 |  |  |     memset(l, 0, ASCIILINESZ+1); | 
 |  |  |     i=0 ; | 
 |  |  |     while (s[i] && i<ASCIILINESZ) { | 
 |  |  |         l[i] = (char)tolower((int)s[i]); | 
 |  |  |         i++ ; | 
 |  |  |     } | 
 |  |  |     l[ASCIILINESZ]=(char)0; | 
 |  |  |     return l ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Remove blanks at the beginning and the end of a string. | 
 |  |  |   @param    s   String to parse. | 
 |  |  |   @return   ptr to statically allocated string. | 
 |  |  |  | 
 |  |  |   This function returns a pointer to a statically allocated string, | 
 |  |  |   which is identical to the input string, except that all blank | 
 |  |  |   characters at the end and the beg. of the string have been removed. | 
 |  |  |   Do not free or modify the returned string! Since the returned string | 
 |  |  |   is statically allocated, it will be modified at each function call | 
 |  |  |   (not re-entrant). | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | static char * strstrip(const char * s) | 
 |  |  | { | 
 |  |  |     static char l[ASCIILINESZ+1]; | 
 |  |  |     char * last ; | 
 |  |  |      | 
 |  |  |     if (s==NULL) return NULL ; | 
 |  |  |      | 
 |  |  |     while (isspace((int)*s) && *s) s++; | 
 |  |  |     memset(l, 0, ASCIILINESZ+1); | 
 |  |  |     strcpy(l, s); | 
 |  |  |     last = l + strlen(l); | 
 |  |  |     while (last > l) { | 
 |  |  |         if (!isspace((int)*(last-1))) | 
 |  |  |             break ; | 
 |  |  |         last -- ; | 
 |  |  |     } | 
 |  |  |     *last = (char)0; | 
 |  |  |     return (char*)l ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get number of sections in a dictionary | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @return   int Number of sections found in dictionary | 
 |  |  |  | 
 |  |  |   This function returns the number of sections found in a dictionary. | 
 |  |  |   The test to recognize sections is done on the string stored in the | 
 |  |  |   dictionary: a section name is given as "section" whereas a key is | 
 |  |  |   stored as "section:key", thus the test looks for entries that do not | 
 |  |  |   contain a colon. | 
 |  |  |  | 
 |  |  |   This clearly fails in the case a section name contains a colon, but | 
 |  |  |   this should simply be avoided. | 
 |  |  |  | 
 |  |  |   This function returns -1 in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getnsec(dictionary * d) | 
 |  |  | { | 
 |  |  |     int i ; | 
 |  |  |     int nsec ; | 
 |  |  |  | 
 |  |  |     if (d==NULL) return -1 ; | 
 |  |  |     nsec=0 ; | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (strchr(d->key[i], ':')==NULL) { | 
 |  |  |             nsec ++ ; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     return nsec ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get name for section n in a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    n   Section number (from 0 to nsec-1). | 
 |  |  |   @return   Pointer to char string | 
 |  |  |  | 
 |  |  |   This function locates the n-th section in a dictionary and returns | 
 |  |  |   its name as a pointer to a string statically allocated inside the | 
 |  |  |   dictionary. Do not free or modify the returned string! | 
 |  |  |  | 
 |  |  |   This function returns NULL in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char * iniparser_getsecname(dictionary * d, int n) | 
 |  |  | { | 
 |  |  |     int i ; | 
 |  |  |     int foundsec ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || n<0) return NULL ; | 
 |  |  |     foundsec=0 ; | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (strchr(d->key[i], ':')==NULL) { | 
 |  |  |             foundsec++ ; | 
 |  |  |             if (foundsec>n) | 
 |  |  |                 break ; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     if (foundsec<=n) { | 
 |  |  |         return NULL ; | 
 |  |  |     } | 
 |  |  |     return d->key[i] ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Dump a dictionary to an opened file pointer. | 
 |  |  |   @param    d   Dictionary to dump. | 
 |  |  |   @param    f   Opened file pointer to dump to. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function prints out the contents of a dictionary, one element by | 
 |  |  |   line, onto the provided file pointer. It is OK to specify @c stderr | 
 |  |  |   or @c stdout as output files. This function is meant for debugging | 
 |  |  |   purposes mostly. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_dump(dictionary * d, FILE * f) | 
 |  |  | { | 
 |  |  |     int     i ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || f==NULL) return ; | 
 |  |  |     for (i=0 ; i<d->size ; i++) { | 
 |  |  |         if (d->key[i]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (d->val[i]!=NULL) { | 
 |  |  |             fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); | 
 |  |  |         } else { | 
 |  |  |             fprintf(f, "[%s]=UNDEF\n", d->key[i]); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Save a dictionary to a loadable ini file | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    f   Opened file pointer to dump to | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function dumps a given dictionary into a loadable ini file. | 
 |  |  |   It is Ok to specify @c stderr or @c stdout as output files. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_dump_ini(dictionary * d, FILE * f) | 
 |  |  | { | 
 |  |  |     int     i ; | 
 |  |  |     int     nsec ; | 
 |  |  |     char *  secname ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || f==NULL) return ; | 
 |  |  |  | 
 |  |  |     nsec = iniparser_getnsec(d); | 
 |  |  |     if (nsec<1) { | 
 |  |  |         /* No section in file: dump all keys as they are */ | 
 |  |  |         for (i=0 ; i<d->size ; i++) { | 
 |  |  |             if (d->key[i]==NULL) | 
 |  |  |                 continue ; | 
 |  |  |             fprintf(f, "%s = %s\n", d->key[i], d->val[i]); | 
 |  |  |         } | 
 |  |  |         return ; | 
 |  |  |     } | 
 |  |  |     for (i=0 ; i<nsec ; i++) { | 
 |  |  |         secname = iniparser_getsecname(d, i) ; | 
 |  |  |         iniparser_dumpsection_ini(d, secname, f) ; | 
 |  |  |     } | 
 |  |  |     fprintf(f, "\n"); | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Save a dictionary section to a loadable ini file | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    s   Section name of dictionary to dump | 
 |  |  |   @param    f   Opened file pointer to dump to | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function dumps a given section of a given dictionary into a loadable ini | 
 |  |  |   file.  It is Ok to specify @c stderr or @c stdout as output files. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) | 
 |  |  | { | 
 |  |  |     int     j ; | 
 |  |  |     char    keym[ASCIILINESZ+1]; | 
 |  |  |     int     seclen ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || f==NULL) return ; | 
 |  |  |     if (! iniparser_find_entry(d, s)) return ; | 
 |  |  |  | 
 |  |  |     seclen  = (int)strlen(s); | 
 |  |  |     fprintf(f, "\n[%s]\n", s); | 
 |  |  |     sprintf(keym, "%s:", s); | 
 |  |  |     for (j=0 ; j<d->size ; j++) { | 
 |  |  |         if (d->key[j]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (!strncmp(d->key[j], keym, seclen+1)) { | 
 |  |  |             fprintf(f, | 
 |  |  |                     "%-30s = %s\n", | 
 |  |  |                     d->key[j]+seclen+1, | 
 |  |  |                     d->val[j] ? d->val[j] : ""); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     fprintf(f, "\n"); | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the number of keys in a section of a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    s   Section name of dictionary to examine | 
 |  |  |   @return   Number of keys in section | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getsecnkeys(dictionary * d, char * s) | 
 |  |  | { | 
 |  |  |     int     seclen, nkeys ; | 
 |  |  |     char    keym[ASCIILINESZ+1]; | 
 |  |  |     int j ; | 
 |  |  |  | 
 |  |  |     nkeys = 0; | 
 |  |  |  | 
 |  |  |     if (d==NULL) return nkeys; | 
 |  |  |     if (! iniparser_find_entry(d, s)) return nkeys; | 
 |  |  |  | 
 |  |  |     seclen  = (int)strlen(s); | 
 |  |  |     sprintf(keym, "%s:", s); | 
 |  |  |  | 
 |  |  |     for (j=0 ; j<d->size ; j++) { | 
 |  |  |         if (d->key[j]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (!strncmp(d->key[j], keym, seclen+1))  | 
 |  |  |             nkeys++; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     return nkeys; | 
 |  |  |  | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the number of keys in a section of a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    s   Section name of dictionary to examine | 
 |  |  |   @return   pointer to statically allocated character strings | 
 |  |  |  | 
 |  |  |   This function queries a dictionary and finds all keys in a given section. | 
 |  |  |   Each pointer in the returned char pointer-to-pointer is pointing to | 
 |  |  |   a string allocated in the dictionary; do not free or modify them. | 
 |  |  |    | 
 |  |  |   This function returns NULL in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char ** iniparser_getseckeys(dictionary * d, char * s) | 
 |  |  | { | 
 |  |  |  | 
 |  |  |     char **keys; | 
 |  |  |  | 
 |  |  |     int i, j ; | 
 |  |  |     char    keym[ASCIILINESZ+1]; | 
 |  |  |     int     seclen, nkeys ; | 
 |  |  |  | 
 |  |  |     keys = NULL; | 
 |  |  |  | 
 |  |  |     if (d==NULL) return keys; | 
 |  |  |     if (! iniparser_find_entry(d, s)) return keys; | 
 |  |  |  | 
 |  |  |     nkeys = iniparser_getsecnkeys(d, s); | 
 |  |  |  | 
 |  |  |     keys = (char**) malloc(nkeys*sizeof(char*)); | 
 |  |  |  | 
 |  |  |     seclen  = (int)strlen(s); | 
 |  |  |     sprintf(keym, "%s:", s); | 
 |  |  |      | 
 |  |  |     i = 0; | 
 |  |  |  | 
 |  |  |     for (j=0 ; j<d->size ; j++) { | 
 |  |  |         if (d->key[j]==NULL) | 
 |  |  |             continue ; | 
 |  |  |         if (!strncmp(d->key[j], keym, seclen+1)) { | 
 |  |  |             keys[i] = d->key[j]; | 
 |  |  |             i++; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     return keys; | 
 |  |  |  | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key | 
 |  |  |   @param    d       Dictionary to search | 
 |  |  |   @param    key     Key string to look for | 
 |  |  |   @param    def     Default value to return if key not found. | 
 |  |  |   @return   pointer to statically allocated character string | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the pointer passed as 'def' is returned. | 
 |  |  |   The returned char pointer is pointing to a string allocated in | 
 |  |  |   the dictionary, do not free or modify it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char * iniparser_getstring(dictionary * d, const char * key, char * def) | 
 |  |  | { | 
 |  |  |     char * lc_key ; | 
 |  |  |     char * sval ; | 
 |  |  |  | 
 |  |  |     if (d==NULL || key==NULL) | 
 |  |  |         return def ; | 
 |  |  |  | 
 |  |  |     lc_key = strlwc(key); | 
 |  |  |     sval = dictionary_get(d, lc_key, def); | 
 |  |  |     return sval ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to an int | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   integer | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  | 
 |  |  |   Supported values for integers include the usual C notation | 
 |  |  |   so decimal, octal (starting with 0) and hexadecimal (starting with 0x) | 
 |  |  |   are supported. Examples: | 
 |  |  |  | 
 |  |  |   "42"      ->  42 | 
 |  |  |   "042"     ->  34 (octal -> decimal) | 
 |  |  |   "0x42"    ->  66 (hexa  -> decimal) | 
 |  |  |  | 
 |  |  |   Warning: the conversion may overflow in various ways. Conversion is | 
 |  |  |   totally outsourced to strtol(), see the associated man page for overflow | 
 |  |  |   handling. | 
 |  |  |  | 
 |  |  |   Credits: Thanks to A. Becker for suggesting strtol() | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getint(dictionary * d, const char * key, int notfound) | 
 |  |  | { | 
 |  |  |     char    *   str ; | 
 |  |  |  | 
 |  |  |     str = iniparser_getstring(d, key, INI_INVALID_KEY); | 
 |  |  |     if (str==INI_INVALID_KEY) return notfound ; | 
 |  |  |     return (int)strtol(str, NULL, 0); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | int iniparser_getlong(dictionary * d, const char * key, int notfound) | 
 |  |  | { | 
 |  |  |     char    *   str ; | 
 |  |  |  | 
 |  |  |     str = iniparser_getstring(d, key, INI_INVALID_KEY); | 
 |  |  |     if (str==INI_INVALID_KEY) return notfound ; | 
 |  |  |     return strtol(str, NULL, 0); | 
 |  |  | } | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to a double | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   double | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | double iniparser_getdouble(dictionary * d, const char * key, double notfound) | 
 |  |  | { | 
 |  |  |     char    *   str ; | 
 |  |  |  | 
 |  |  |     str = iniparser_getstring(d, key, INI_INVALID_KEY); | 
 |  |  |     if (str==INI_INVALID_KEY) return notfound ; | 
 |  |  |     return atof(str); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to a boolean | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   integer | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  | 
 |  |  |   A true boolean is found if one of the following is matched: | 
 |  |  |  | 
 |  |  |   - A string starting with 'y' | 
 |  |  |   - A string starting with 'Y' | 
 |  |  |   - A string starting with 't' | 
 |  |  |   - A string starting with 'T' | 
 |  |  |   - A string starting with '1' | 
 |  |  |  | 
 |  |  |   A false boolean is found if one of the following is matched: | 
 |  |  |  | 
 |  |  |   - A string starting with 'n' | 
 |  |  |   - A string starting with 'N' | 
 |  |  |   - A string starting with 'f' | 
 |  |  |   - A string starting with 'F' | 
 |  |  |   - A string starting with '0' | 
 |  |  |  | 
 |  |  |   The notfound value returned if no boolean is identified, does not | 
 |  |  |   necessarily have to be 0 or 1. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getboolean(dictionary * d, const char * key, int notfound) | 
 |  |  | { | 
 |  |  |     char    *   c ; | 
 |  |  |     int         ret ; | 
 |  |  |  | 
 |  |  |     c = iniparser_getstring(d, key, INI_INVALID_KEY); | 
 |  |  |     if (c==INI_INVALID_KEY) return notfound ; | 
 |  |  |     if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { | 
 |  |  |         ret = 1 ; | 
 |  |  |     } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { | 
 |  |  |         ret = 0 ; | 
 |  |  |     } else { | 
 |  |  |         ret = notfound ; | 
 |  |  |     } | 
 |  |  |     return ret; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Finds out if a given entry exists in a dictionary | 
 |  |  |   @param    ini     Dictionary to search | 
 |  |  |   @param    entry   Name of the entry to look for | 
 |  |  |   @return   integer 1 if entry exists, 0 otherwise | 
 |  |  |  | 
 |  |  |   Finds out if a given entry exists in the dictionary. Since sections | 
 |  |  |   are stored as keys with NULL associated values, this is the only way | 
 |  |  |   of querying for the presence of sections in a dictionary. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_find_entry( | 
 |  |  |     dictionary  *   ini, | 
 |  |  |     const char  *   entry | 
 |  |  | ) | 
 |  |  | { | 
 |  |  |     int found=0 ; | 
 |  |  |     if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { | 
 |  |  |         found = 1 ; | 
 |  |  |     } | 
 |  |  |     return found ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Set an entry in a dictionary. | 
 |  |  |   @param    ini     Dictionary to modify. | 
 |  |  |   @param    entry   Entry to modify (entry name) | 
 |  |  |   @param    val     New value to associate to the entry. | 
 |  |  |   @return   int 0 if Ok, -1 otherwise. | 
 |  |  |  | 
 |  |  |   If the given entry can be found in the dictionary, it is modified to | 
 |  |  |   contain the provided value. If it cannot be found, -1 is returned. | 
 |  |  |   It is Ok to set val to NULL. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_set(dictionary * ini, const char * entry, const char * val) | 
 |  |  | { | 
 |  |  |     return dictionary_set(ini, strlwc(entry), val) ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete an entry in a dictionary | 
 |  |  |   @param    ini     Dictionary to modify | 
 |  |  |   @param    entry   Entry to delete (entry name) | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   If the given entry can be found, it is deleted from the dictionary. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_unset(dictionary * ini, const char * entry) | 
 |  |  | { | 
 |  |  |     dictionary_unset(ini, strlwc(entry)); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Load a single line from an INI file | 
 |  |  |   @param    input_line  Input line, may be concatenated multi-line input | 
 |  |  |   @param    section     Output space to store section | 
 |  |  |   @param    key         Output space to store key | 
 |  |  |   @param    value       Output space to store value | 
 |  |  |   @return   line_status value | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | static line_status iniparser_line( | 
 |  |  |     char * input_line, | 
 |  |  |     char * section, | 
 |  |  |     char * key, | 
 |  |  |     char * value) | 
 |  |  | {    | 
 |  |  |     line_status sta ; | 
 |  |  |     char        line[ASCIILINESZ+1]; | 
 |  |  |     static char left_line[ASCIILINESZ+1]; | 
 |  |  |     int         len, offset ; | 
 |  |  |  | 
 |  |  |     strcpy(line, strstrip(input_line)); | 
 |  |  |     len = (int)strlen(line); | 
 |  |  |  | 
 |  |  |     sta = LINE_UNPROCESSED ; | 
 |  |  |     if (len<1) { | 
 |  |  |         /* Empty line */ | 
 |  |  |         sta = LINE_EMPTY ; | 
 |  |  |         memset(input_line, 0, len); | 
 |  |  |     } else if (line[0]=='#' || line[0]==';') { | 
 |  |  |         /* Comment line */ | 
 |  |  |         sta = LINE_COMMENT ;  | 
 |  |  |         memset(input_line, 0, len); | 
 |  |  |     } else if (line[0]=='[') { | 
 |  |  |         /* Section name */ | 
 |  |  |         sscanf(line, "[%[^]]", section); | 
 |  |  |         strcpy(section, strstrip(section)); | 
 |  |  |         strcpy(section, strlwc(section)); | 
 |  |  |  | 
 |  |  |         /* Left configure will go to next time to parser */ | 
 |  |  |         offset = strlen(section) + 2; | 
 |  |  |         strcpy( left_line, strstrip(&(line[offset])) ); | 
 |  |  |         strcpy( left_line, strstrip(left_line)); | 
 |  |  |  | 
 |  |  |         if( strlen(left_line) > 0) | 
 |  |  |         { | 
 |  |  |             strcpy(input_line, left_line); | 
 |  |  |             strcat(input_line, "\n"); | 
 |  |  |         } | 
 |  |  |         else | 
 |  |  |         { | 
 |  |  |             memset(input_line, 0, len);  | 
 |  |  |         } | 
 |  |  |         sta = LINE_SECTION ; | 
 |  |  |     } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 | 
 |  |  |            ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2 | 
 |  |  |            ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) { | 
 |  |  |         char      *ptr = NULL;  | 
 |  |  |  | 
 |  |  |         /* Usual key=value, with or without comments */ | 
 |  |  |         strcpy(key, strstrip(key)); | 
 |  |  |         strcpy(key, strlwc(key)); | 
 |  |  |         strcpy(value, strstrip(value)); | 
 |  |  |         /* | 
 |  |  |          * sscanf cannot handle '' or "" as empty values | 
 |  |  |          * this is done here | 
 |  |  |          */ | 
 |  |  |         if (!strncmp(value, "\"\"", 2) || (!strncmp(value, "''", 2)) ) { | 
 |  |  |             value[0]=0 ; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         ptr = strchr(line, '='); | 
 |  |  |         if('\''==*(ptr+1) || '\"'==*(ptr+1)) | 
 |  |  |         { | 
 |  |  |             offset = strlen(key)+strlen(value) + 1 + 2; /* Skip $key='$val' */ | 
 |  |  |         } | 
 |  |  |         else | 
 |  |  |         { | 
 |  |  |             offset = strlen(key)+strlen(value) + 1; /* Skip $key=$val */ | 
 |  |  |         } | 
 |  |  |         strcpy( left_line, strstrip(&(line[offset])) );  | 
 |  |  |          | 
 |  |  |         if( strlen(left_line) > 0) | 
 |  |  |         { | 
 |  |  |             strcpy(input_line, left_line); | 
 |  |  |             strcat(input_line, "\n"); | 
 |  |  |         } | 
 |  |  |         else | 
 |  |  |         { | 
 |  |  |             memset(input_line, 0, len);  | 
 |  |  |         } | 
 |  |  |         sta = LINE_VALUE ; | 
 |  |  |     } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 | 
 |  |  |            ||  sscanf(line, "%[^=] %[=]", key, value) == 2) { | 
 |  |  |         /* | 
 |  |  |          * Special cases: | 
 |  |  |          * key= | 
 |  |  |          * key=; | 
 |  |  |          * key=# | 
 |  |  |          */ | 
 |  |  |         strcpy(key, strstrip(key)); | 
 |  |  |         strcpy(key, strlwc(key)); | 
 |  |  |         value[0]=0 ; | 
 |  |  |         sta = LINE_VALUE ; | 
 |  |  |     } else { | 
 |  |  |         /* Generate syntax error */ | 
 |  |  |         sta = LINE_ERROR ; | 
 |  |  |         memset(input_line, 0, len); | 
 |  |  |     } | 
 |  |  |     return sta ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Parse an ini file and return an allocated dictionary object | 
 |  |  |   @param    ininame Name of the ini file to read. | 
 |  |  |   @return   Pointer to newly allocated dictionary | 
 |  |  |  | 
 |  |  |   This is the parser for ini files. This function is called, providing | 
 |  |  |   the name of the file to be read. It returns a dictionary object that | 
 |  |  |   should not be accessed directly, but through accessor functions | 
 |  |  |   instead. | 
 |  |  |  | 
 |  |  |   The returned dictionary must be freed using iniparser_freedict(). | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | dictionary * iniparser_load(const char * ininame) | 
 |  |  | { | 
 |  |  |     FILE * in ; | 
 |  |  |  | 
 |  |  |     char line    [ASCIILINESZ+1] ; | 
 |  |  |     char section [ASCIILINESZ+1] ; | 
 |  |  |     char key     [ASCIILINESZ+1] ; | 
 |  |  |     char tmp     [ASCIILINESZ+1] ; | 
 |  |  |     char val     [ASCIILINESZ+1] ; | 
 |  |  |  | 
 |  |  |     int  last=0 ; | 
 |  |  |     int  len ; | 
 |  |  |     int  lineno=0 ; | 
 |  |  |     int  errs=0; | 
 |  |  |  | 
 |  |  |     dictionary * dict ; | 
 |  |  |  | 
 |  |  |     if ((in=fopen(ininame, "r"))==NULL) { | 
 |  |  |         fprintf(stderr, "iniparser: cannot open %s\n", ininame); | 
 |  |  |         return NULL ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     dict = dictionary_new(0) ; | 
 |  |  |     if (!dict) { | 
 |  |  |         fclose(in); | 
 |  |  |         return NULL ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     memset(line,    0, ASCIILINESZ); | 
 |  |  |     memset(section, 0, ASCIILINESZ); | 
 |  |  |     memset(key,     0, ASCIILINESZ); | 
 |  |  |     memset(val,     0, ASCIILINESZ); | 
 |  |  |     last=0 ; | 
 |  |  |  | 
 |  |  |     while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { | 
 |  |  |         lineno++ ; | 
 |  |  | CONTINUE_PARSER: | 
 |  |  |         len = (int)strlen(line)-1; | 
 |  |  |         if (len==0) | 
 |  |  |             continue; | 
 |  |  |         /* Safety check against buffer overflows */ | 
 |  |  |         if (line[len]!='\n') { | 
 |  |  |             fprintf(stderr, | 
 |  |  |                     "iniparser: input line too long in %s (%d)\n", | 
 |  |  |                     ininame, | 
 |  |  |                     lineno); | 
 |  |  |             dictionary_del(dict); | 
 |  |  |             fclose(in); | 
 |  |  |             return NULL ; | 
 |  |  |         } | 
 |  |  |         /* Get rid of \n and spaces at end of line */ | 
 |  |  |         while ((len>=0) && | 
 |  |  |                 ((line[len]=='\n') || (isspace(line[len])))) { | 
 |  |  |             line[len]=0 ; | 
 |  |  |             len-- ; | 
 |  |  |         } | 
 |  |  |         /* Detect multi-line */ | 
 |  |  |         if (line[len]=='\\') { | 
 |  |  |             /* Multi-line value */ | 
 |  |  |             last=len ; | 
 |  |  |             continue ; | 
 |  |  |         } else { | 
 |  |  |             last=0 ; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         switch ( iniparser_line(line, section, key, val) ) { | 
 |  |  |             case LINE_EMPTY: | 
 |  |  |             case LINE_COMMENT: | 
 |  |  |             break ; | 
 |  |  |  | 
 |  |  |             case LINE_SECTION: | 
 |  |  |             errs = dictionary_set(dict, section, NULL); | 
 |  |  |             break ; | 
 |  |  |  | 
 |  |  |             case LINE_VALUE: | 
 |  |  |             sprintf(tmp, "%s:%s", section, key); | 
 |  |  |             errs = dictionary_set(dict, tmp, val) ; | 
 |  |  |             break ; | 
 |  |  |  | 
 |  |  |             case LINE_ERROR: | 
 |  |  |             fprintf(stderr, "iniparser: syntax error in %s (%d):\n", | 
 |  |  |                     ininame, | 
 |  |  |                     lineno); | 
 |  |  |             fprintf(stderr, "-> %s\n", line); | 
 |  |  |             errs++ ; | 
 |  |  |             break; | 
 |  |  |  | 
 |  |  |             default: | 
 |  |  |             break ; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         if( strlen(line) > 0) | 
 |  |  |         { | 
 |  |  |             goto CONTINUE_PARSER; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         memset(line, 0, ASCIILINESZ); | 
 |  |  |         last=0; | 
 |  |  |         if (errs<0) { | 
 |  |  |             fprintf(stderr, "iniparser: memory allocation failure\n"); | 
 |  |  |             break ; | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |     if (errs) { | 
 |  |  |         dictionary_del(dict); | 
 |  |  |         dict = NULL ; | 
 |  |  |     } | 
 |  |  |     fclose(in); | 
 |  |  |     return dict ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Free all memory associated to an ini dictionary | 
 |  |  |   @param    d Dictionary to free | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Free all memory associated to an ini dictionary. | 
 |  |  |   It is mandatory to call this function before the dictionary object | 
 |  |  |   gets out of the current context. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_freedict(dictionary * d) | 
 |  |  | { | 
 |  |  |     dictionary_del(d); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /* vim: set ts=4 et sw=4 tw=75 */ | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |    @file    cp_iniparser.h | 
 |  |  |    @author  N. Devillard | 
 |  |  |    @brief   Parser for ini files. | 
 |  |  | */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | #ifndef _INIPARSER_H_ | 
 |  |  | #define _INIPARSER_H_ | 
 |  |  |  | 
 |  |  | /*--------------------------------------------------------------------------- | 
 |  |  |                                 Includes | 
 |  |  |  ---------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | #include <stdio.h> | 
 |  |  | #include <stdlib.h> | 
 |  |  | #include <string.h> | 
 |  |  |  | 
 |  |  | /* | 
 |  |  |  * The following #include is necessary on many Unixes but not Linux. | 
 |  |  |  * It is not needed for Windows platforms. | 
 |  |  |  * Uncomment it if needed. | 
 |  |  |  */ | 
 |  |  | /* #include <unistd.h> */ | 
 |  |  |  | 
 |  |  | #include "ini_dictionary.h" | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get number of sections in a dictionary | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @return   int Number of sections found in dictionary | 
 |  |  |  | 
 |  |  |   This function returns the number of sections found in a dictionary. | 
 |  |  |   The test to recognize sections is done on the string stored in the | 
 |  |  |   dictionary: a section name is given as "section" whereas a key is | 
 |  |  |   stored as "section:key", thus the test looks for entries that do not | 
 |  |  |   contain a colon. | 
 |  |  |  | 
 |  |  |   This clearly fails in the case a section name contains a colon, but | 
 |  |  |   this should simply be avoided. | 
 |  |  |  | 
 |  |  |   This function returns -1 in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | int iniparser_getnsec(dictionary * d); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get name for section n in a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    n   Section number (from 0 to nsec-1). | 
 |  |  |   @return   Pointer to char string | 
 |  |  |  | 
 |  |  |   This function locates the n-th section in a dictionary and returns | 
 |  |  |   its name as a pointer to a string statically allocated inside the | 
 |  |  |   dictionary. Do not free or modify the returned string! | 
 |  |  |  | 
 |  |  |   This function returns NULL in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | char * iniparser_getsecname(dictionary * d, int n); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Save a dictionary to a loadable ini file | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    f   Opened file pointer to dump to | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function dumps a given dictionary into a loadable ini file. | 
 |  |  |   It is Ok to specify @c stderr or @c stdout as output files. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | void iniparser_dump_ini(dictionary * d, FILE * f); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Save a dictionary section to a loadable ini file | 
 |  |  |   @param    d   Dictionary to dump | 
 |  |  |   @param    s   Section name of dictionary to dump | 
 |  |  |   @param    f   Opened file pointer to dump to | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function dumps a given section of a given dictionary into a loadable ini | 
 |  |  |   file.  It is Ok to specify @c stderr or @c stdout as output files. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  |  | 
 |  |  | void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Dump a dictionary to an opened file pointer. | 
 |  |  |   @param    d   Dictionary to dump. | 
 |  |  |   @param    f   Opened file pointer to dump to. | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   This function prints out the contents of a dictionary, one element by | 
 |  |  |   line, onto the provided file pointer. It is OK to specify @c stderr | 
 |  |  |   or @c stdout as output files. This function is meant for debugging | 
 |  |  |   purposes mostly. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_dump(dictionary * d, FILE * f); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the number of keys in a section of a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    s   Section name of dictionary to examine | 
 |  |  |   @return   Number of keys in section | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getsecnkeys(dictionary * d, char * s); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the number of keys in a section of a dictionary. | 
 |  |  |   @param    d   Dictionary to examine | 
 |  |  |   @param    s   Section name of dictionary to examine | 
 |  |  |   @return   pointer to statically allocated character strings | 
 |  |  |  | 
 |  |  |   This function queries a dictionary and finds all keys in a given section. | 
 |  |  |   Each pointer in the returned char pointer-to-pointer is pointing to | 
 |  |  |   a string allocated in the dictionary; do not free or modify them. | 
 |  |  |  | 
 |  |  |   This function returns NULL in case of error. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char ** iniparser_getseckeys(dictionary * d, char * s); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key | 
 |  |  |   @param    d       Dictionary to search | 
 |  |  |   @param    key     Key string to look for | 
 |  |  |   @param    def     Default value to return if key not found. | 
 |  |  |   @return   pointer to statically allocated character string | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the pointer passed as 'def' is returned. | 
 |  |  |   The returned char pointer is pointing to a string allocated in | 
 |  |  |   the dictionary, do not free or modify it. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | char * iniparser_getstring(dictionary * d, const char * key, char * def); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to an int | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   integer | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  | 
 |  |  |   Supported values for integers include the usual C notation | 
 |  |  |   so decimal, octal (starting with 0) and hexadecimal (starting with 0x) | 
 |  |  |   are supported. Examples: | 
 |  |  |  | 
 |  |  |   - "42"      ->  42 | 
 |  |  |   - "042"     ->  34 (octal -> decimal) | 
 |  |  |   - "0x42"    ->  66 (hexa  -> decimal) | 
 |  |  |  | 
 |  |  |   Warning: the conversion may overflow in various ways. Conversion is | 
 |  |  |   totally outsourced to strtol(), see the associated man page for overflow | 
 |  |  |   handling. | 
 |  |  |  | 
 |  |  |   Credits: Thanks to A. Becker for suggesting strtol() | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getint(dictionary * d, const char * key, int notfound); | 
 |  |  | int iniparser_getlong(dictionary * d, const char * key, int notfound); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to a double | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   double | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | double iniparser_getdouble(dictionary * d, const char * key, double notfound); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Get the string associated to a key, convert to a boolean | 
 |  |  |   @param    d Dictionary to search | 
 |  |  |   @param    key Key string to look for | 
 |  |  |   @param    notfound Value to return in case of error | 
 |  |  |   @return   integer | 
 |  |  |  | 
 |  |  |   This function queries a dictionary for a key. A key as read from an | 
 |  |  |   ini file is given as "section:key". If the key cannot be found, | 
 |  |  |   the notfound value is returned. | 
 |  |  |  | 
 |  |  |   A true boolean is found if one of the following is matched: | 
 |  |  |  | 
 |  |  |   - A string starting with 'y' | 
 |  |  |   - A string starting with 'Y' | 
 |  |  |   - A string starting with 't' | 
 |  |  |   - A string starting with 'T' | 
 |  |  |   - A string starting with '1' | 
 |  |  |  | 
 |  |  |   A false boolean is found if one of the following is matched: | 
 |  |  |  | 
 |  |  |   - A string starting with 'n' | 
 |  |  |   - A string starting with 'N' | 
 |  |  |   - A string starting with 'f' | 
 |  |  |   - A string starting with 'F' | 
 |  |  |   - A string starting with '0' | 
 |  |  |  | 
 |  |  |   The notfound value returned if no boolean is identified, does not | 
 |  |  |   necessarily have to be 0 or 1. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_getboolean(dictionary * d, const char * key, int notfound); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Set an entry in a dictionary. | 
 |  |  |   @param    ini     Dictionary to modify. | 
 |  |  |   @param    entry   Entry to modify (entry name) | 
 |  |  |   @param    val     New value to associate to the entry. | 
 |  |  |   @return   int 0 if Ok, -1 otherwise. | 
 |  |  |  | 
 |  |  |   If the given entry can be found in the dictionary, it is modified to | 
 |  |  |   contain the provided value. If it cannot be found, -1 is returned. | 
 |  |  |   It is Ok to set val to NULL. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_set(dictionary * ini, const char * entry, const char * val); | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Delete an entry in a dictionary | 
 |  |  |   @param    ini     Dictionary to modify | 
 |  |  |   @param    entry   Entry to delete (entry name) | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   If the given entry can be found, it is deleted from the dictionary. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_unset(dictionary * ini, const char * entry); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Finds out if a given entry exists in a dictionary | 
 |  |  |   @param    ini     Dictionary to search | 
 |  |  |   @param    entry   Name of the entry to look for | 
 |  |  |   @return   integer 1 if entry exists, 0 otherwise | 
 |  |  |  | 
 |  |  |   Finds out if a given entry exists in the dictionary. Since sections | 
 |  |  |   are stored as keys with NULL associated values, this is the only way | 
 |  |  |   of querying for the presence of sections in a dictionary. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | int iniparser_find_entry(dictionary * ini, const char * entry) ; | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Parse an ini file and return an allocated dictionary object | 
 |  |  |   @param    ininame Name of the ini file to read. | 
 |  |  |   @return   Pointer to newly allocated dictionary | 
 |  |  |  | 
 |  |  |   This is the parser for ini files. This function is called, providing | 
 |  |  |   the name of the file to be read. It returns a dictionary object that | 
 |  |  |   should not be accessed directly, but through accessor functions | 
 |  |  |   instead. | 
 |  |  |  | 
 |  |  |   The returned dictionary must be freed using iniparser_freedict(). | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | dictionary * iniparser_load(const char * ininame); | 
 |  |  |  | 
 |  |  | /*-------------------------------------------------------------------------*/ | 
 |  |  | /** | 
 |  |  |   @brief    Free all memory associated to an ini dictionary | 
 |  |  |   @param    d Dictionary to free | 
 |  |  |   @return   void | 
 |  |  |  | 
 |  |  |   Free all memory associated to an ini dictionary. | 
 |  |  |   It is mandatory to call this function before the dictionary object | 
 |  |  |   gets out of the current context. | 
 |  |  |  */ | 
 |  |  | /*--------------------------------------------------------------------------*/ | 
 |  |  | void iniparser_freedict(dictionary * d); | 
 |  |  |  | 
 |  |  | #endif | 
 
| New file | 
 |  |  | 
 |  |  | /********************************************************************************* | 
 |  |  |  *      Copyright:  (C) 2018 LingYun IoT System Studio | 
 |  |  |  *                  All rights reserved. | 
 |  |  |  * | 
 |  |  |  *       Filename:  logger.c | 
 |  |  |  *    Description:  This file is the linux infrastructural logger system library, which | 
 |  |  |  *                  is not thread safe. | 
 |  |  |  *                  | 
 |  |  |  *        Version:  1.0.0(08/08/2012~) | 
 |  |  |  *         Author:  Guo Wenxue <guowenxue@gmail.com> | 
 |  |  |  *      ChangeLog:  1, Release initial version on "08/08/2018 04:24:01 PM" | 
 |  |  |  *                  | 
 |  |  |  ********************************************************************************/ | 
 |  |  |  | 
 |  |  | #include "logger.h" | 
 |  |  |  | 
 |  |  | #define PRECISE_TIME_FACTOR 1000 | 
 |  |  |  | 
 |  |  | static unsigned long log_rollback_size = LOG_ROLLBACK_NONE; | 
 |  |  |  | 
 |  |  | static st_logger _g_logger; | 
 |  |  | static st_logger *g_logger = &_g_logger; | 
 |  |  |  | 
 |  |  | char *log_str[LOG_LEVEL_MAX + 1] = { "", "F", "E", "W", "N", "D", "I", "T", "M" }; | 
 |  |  |  | 
 |  |  | static char *log_time_format = "%Y-%m-%d %H:%M:%S"; | 
 |  |  |  | 
 |  |  | static void logger_default_signal_handler(int sig) | 
 |  |  | { | 
 |  |  |     if (sig == SIGHUP) | 
 |  |  |     { | 
 |  |  |         signal(SIGHUP, logger_default_signal_handler); | 
 |  |  |         log_fatal("SIGHUP received - reopenning log file [%s]", g_logger->file); | 
 |  |  |         logger_reopen(); | 
 |  |  |     } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | static void logger_banner(char *prefix) | 
 |  |  | { | 
 |  |  |     fprintf(g_logger->fp, "%s log \"%s\" on level [%s] size [%lu], log system version %s\n", | 
 |  |  |             prefix, g_logger->file, log_str[g_logger->level], log_rollback_size / 1024, LOG_VERSION_STR); | 
 |  |  | #if 0 | 
 |  |  | #ifdef LOG_FILE_LINE | 
 |  |  |     fprintf(g_logger->fp, " [Date]    [Time]   [Level] [PID/TID] [File/Line]  [Content]\n"); | 
 |  |  | #else | 
 |  |  |     fprintf(g_logger->fp, " [Date]    [Time]   [Level] [PID/TID] [Content]\n"); | 
 |  |  | #endif | 
 |  |  | #endif | 
 |  |  |     fprintf(g_logger->fp, "-----------------------------------------------------------------------------\n"); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | static void check_and_rollback(void) | 
 |  |  | { | 
 |  |  |     if (log_rollback_size != LOG_ROLLBACK_NONE) | 
 |  |  |     { | 
 |  |  |         long _curOffset = ftell(g_logger->fp); | 
 |  |  |  | 
 |  |  |         if ((_curOffset != -1) && (_curOffset >= log_rollback_size)) | 
 |  |  |         { | 
 |  |  |             char cmd[512]; | 
 |  |  |  | 
 |  |  |             snprintf(cmd, sizeof(cmd), "cp -f %s %s.roll", g_logger->file, g_logger->file); | 
 |  |  |             system(cmd); | 
 |  |  |  | 
 |  |  |             if (-1 == fseek(g_logger->fp, 0L, SEEK_SET)) | 
 |  |  |                 fprintf(g_logger->fp, "log rollback fseek failed \n"); | 
 |  |  |  | 
 |  |  |             rewind(g_logger->fp); | 
 |  |  |  | 
 |  |  |             truncate(g_logger->file, 0); | 
 |  |  |             logger_banner("Already rollback"); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | int logger_init(char *filename, int level, int log_size) | 
 |  |  | { | 
 |  |  |     struct sigaction act; | 
 |  |  |  | 
 |  |  |     if( !filename || strlen(filename)<=0 ) | 
 |  |  |     { | 
 |  |  |         fprintf(stderr, "%s() invalid input arguments\n", __FUNCTION__); | 
 |  |  |         return -1; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     memset(g_logger, 0, sizeof(st_logger)); | 
 |  |  |  | 
 |  |  |     strncpy(g_logger->file, filename, FILENAME_LEN);  | 
 |  |  |     g_logger->level = level; | 
 |  |  |     g_logger->size = log_size;  | 
 |  |  |  | 
 |  |  |     log_rollback_size = g_logger->size <= 0 ? LOG_ROLLBACK_NONE : g_logger->size*1024;    /* Unit KiB */ | 
 |  |  |  | 
 |  |  |      | 
 |  |  |     /* logger to console, just need point to standard error */ | 
 |  |  |     if (!strcmp(g_logger->file, DBG_LOG_FILE)) | 
 |  |  |     { | 
 |  |  |         g_logger->fp = stderr; | 
 |  |  |         log_rollback_size = LOG_ROLLBACK_NONE; | 
 |  |  |         g_logger->flag |= LOGGER_CONSOLE; | 
 |  |  |         goto OUT; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     g_logger->fp = fopen(g_logger->file, "a+"); | 
 |  |  |     if ( !g_logger->fp ) | 
 |  |  |     { | 
 |  |  |         fprintf(stderr, "Open log file \"%s\" failure\n", g_logger->file); | 
 |  |  |         return -2; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | OUT: | 
 |  |  |     act.sa_handler = logger_default_signal_handler; | 
 |  |  |     sigemptyset(&act.sa_mask); | 
 |  |  |     act.sa_flags = 0; | 
 |  |  |     sigaction(SIGHUP, &act, NULL); | 
 |  |  |  | 
 |  |  |     logger_banner("\nInitialize"); | 
 |  |  |  | 
 |  |  |     return 0; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | void logger_raw(const char *fmt, ...) | 
 |  |  | { | 
 |  |  |     va_list argp; | 
 |  |  |  | 
 |  |  |     if ( !g_logger->fp ) | 
 |  |  |         return; | 
 |  |  |  | 
 |  |  |     check_and_rollback(); | 
 |  |  |  | 
 |  |  |     va_start(argp, fmt); | 
 |  |  |     vfprintf(g_logger->fp, fmt, argp); | 
 |  |  |     va_end(argp); | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | void logger_term(void) | 
 |  |  | { | 
 |  |  |     if ( !g_logger->fp ) | 
 |  |  |         return; | 
 |  |  |  | 
 |  |  |     logger_banner("\nTerminate"); | 
 |  |  |     logger_raw("\n\n"); | 
 |  |  |  | 
 |  |  |     fflush(g_logger->fp); | 
 |  |  |  | 
 |  |  |     fclose(g_logger->fp); | 
 |  |  |     g_logger->fp = NULL; | 
 |  |  |  | 
 |  |  |     memset(g_logger, 0, sizeof(*g_logger)); | 
 |  |  |  | 
 |  |  |     return ; | 
 |  |  | } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | int logger_reopen(void) | 
 |  |  | { | 
 |  |  |     int rc = 0; | 
 |  |  |  | 
 |  |  |     if (g_logger->flag & LOGGER_CONSOLE ) | 
 |  |  |     { | 
 |  |  |         fflush(g_logger->fp); | 
 |  |  |         g_logger->fp = stderr; | 
 |  |  |         return 0; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     if( g_logger->fp ) | 
 |  |  |     { | 
 |  |  |         logger_banner("\nClose"); | 
 |  |  |         fflush(g_logger->fp); | 
 |  |  |  | 
 |  |  |         g_logger->fp = fopen(g_logger->file, "a+");  | 
 |  |  |          | 
 |  |  |         if ( !g_logger->fp ) | 
 |  |  |             rc = -2; | 
 |  |  |     } | 
 |  |  |     else | 
 |  |  |     { | 
 |  |  |         rc = -3; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     if (!rc) | 
 |  |  |     { | 
 |  |  |         logger_banner("\nReopen"); | 
 |  |  |     } | 
 |  |  |     return rc; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | static void logger_printout(char *level, char *fmt, va_list argp) | 
 |  |  | { | 
 |  |  |     char buf[MAX_LOG_MESSAGE_LEN]; | 
 |  |  |     struct tm *local; | 
 |  |  |     struct timeval now; | 
 |  |  |     char timestr[256]; | 
 |  |  |  | 
 |  |  |     pthread_t tid; | 
 |  |  |  | 
 |  |  |     check_and_rollback(); | 
 |  |  |  | 
 |  |  | #ifdef MULTHREADS | 
 |  |  |     tid = pthread_self(); | 
 |  |  | #else  | 
 |  |  |     tid = getpid(); | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  |     gettimeofday(&now, NULL); | 
 |  |  |     local = localtime(&now.tv_sec); | 
 |  |  |  | 
 |  |  |     strftime(timestr, 256, log_time_format, local); | 
 |  |  |     vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); | 
 |  |  |  | 
 |  |  | #ifdef DUMPLICATE_OUTPUT | 
 |  |  |     printf("%s.%03ld [%s] [%06lu]: %s", | 
 |  |  |            timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, buf); | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  |     if (g_logger->fp) | 
 |  |  |         fprintf(g_logger->fp, "%s.%03ld [%s] [%06lu]: %s", timestr, now.tv_usec / PRECISE_TIME_FACTOR, | 
 |  |  |                 level, tid, buf); | 
 |  |  |  | 
 |  |  |     if (g_logger->fp) | 
 |  |  |         fflush(g_logger->fp); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | static void logger_printout_line(char *level, char *fmt, char *file, int line, va_list argp) | 
 |  |  | { | 
 |  |  |     char buf[MAX_LOG_MESSAGE_LEN]; | 
 |  |  |     struct tm *local; | 
 |  |  |     struct timeval now; | 
 |  |  |     char timestr[256]; | 
 |  |  |  | 
 |  |  |     pthread_t tid; | 
 |  |  |  | 
 |  |  |     check_and_rollback(); | 
 |  |  |  | 
 |  |  | #ifdef MULTHREADS | 
 |  |  |     tid = pthread_self(); | 
 |  |  | #else  | 
 |  |  |     tid = getpid(); | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  |     gettimeofday(&now, NULL); | 
 |  |  |     local = localtime(&now.tv_sec); | 
 |  |  |  | 
 |  |  |     strftime(timestr, 256, log_time_format, local); | 
 |  |  |     vsnprintf(buf, MAX_LOG_MESSAGE_LEN, fmt, argp); | 
 |  |  |  | 
 |  |  | #ifdef DUMPLICATE_OUTPUT | 
 |  |  |     printf("%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", | 
 |  |  |            timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  |     if (g_logger->fp) | 
 |  |  |         fprintf(g_logger->fp, "%s.%03ld [%s] [%06lu] (%s [%04d]) : %s", | 
 |  |  |                 timestr, now.tv_usec / PRECISE_TIME_FACTOR, level, tid, file, line, buf); | 
 |  |  |  | 
 |  |  |     if (g_logger->fp) | 
 |  |  |         fflush(g_logger->fp); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | void logger_comm(int level, char *fmt, ...) | 
 |  |  | { | 
 |  |  |     va_list argp; | 
 |  |  |  | 
 |  |  |     if ( level > g_logger->level ) | 
 |  |  |         return; | 
 |  |  |  | 
 |  |  |     va_start(argp, fmt); | 
 |  |  |     logger_printout(log_str[level], fmt, argp); | 
 |  |  |     va_end(argp); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | void logger_line(int level, char *file, int line, char *fmt, ...) | 
 |  |  | { | 
 |  |  |     va_list argp; | 
 |  |  |  | 
 |  |  |     if ( level > g_logger->level ) | 
 |  |  |         return; | 
 |  |  |  | 
 |  |  |     va_start(argp, fmt); | 
 |  |  |     logger_printout_line(log_str[level], fmt, file, line, argp); | 
 |  |  |  | 
 |  |  |     va_end(argp); | 
 |  |  | } | 
 |  |  |  | 
 |  |  | #define LINELEN 81 | 
 |  |  | #define CHARS_PER_LINE 16 | 
 |  |  | static char *print_char = | 
 |  |  |     "                " | 
 |  |  |     "                " | 
 |  |  |     " !\"#$%&'()*+,-./" | 
 |  |  |     "0123456789:;<=>?" | 
 |  |  |     "@ABCDEFGHIJKLMNO" | 
 |  |  |     "PQRSTUVWXYZ[\\]^_" | 
 |  |  |     "`abcdefghijklmno" | 
 |  |  |     "pqrstuvwxyz{|}~ " | 
 |  |  |     "                " | 
 |  |  |     "                " | 
 |  |  |     " ???????????????" | 
 |  |  |     "????????????????"  | 
 |  |  |     "????????????????"  | 
 |  |  |     "????????????????"  | 
 |  |  |     "????????????????"  | 
 |  |  |     "????????????????"; | 
 |  |  |  | 
 |  |  | void logger_dump(int level, char *buf, int len) | 
 |  |  | { | 
 |  |  |     int rc; | 
 |  |  |     int idx; | 
 |  |  |     char prn[LINELEN]; | 
 |  |  |     char lit[CHARS_PER_LINE + 2]; | 
 |  |  |     char hc[4]; | 
 |  |  |     short line_done = 1; | 
 |  |  |  | 
 |  |  |     if ( level > g_logger->level ) | 
 |  |  |         return; | 
 |  |  |  | 
 |  |  |     rc = len; | 
 |  |  |     idx = 0; | 
 |  |  |     lit[CHARS_PER_LINE] = '\0'; | 
 |  |  |  | 
 |  |  |     while (rc > 0) | 
 |  |  |     { | 
 |  |  |         if (line_done) | 
 |  |  |             snprintf(prn, LINELEN, "%08X: ", idx); | 
 |  |  |  | 
 |  |  |         do | 
 |  |  |         { | 
 |  |  |             unsigned char c = buf[idx]; | 
 |  |  |             snprintf(hc, 4, "%02X ", c); | 
 |  |  |             strncat(prn, hc, LINELEN); | 
 |  |  |  | 
 |  |  |             lit[idx % CHARS_PER_LINE] = print_char[c]; | 
 |  |  |         } | 
 |  |  |         while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); | 
 |  |  |  | 
 |  |  |         line_done = (idx % CHARS_PER_LINE) == 0; | 
 |  |  |         if (line_done) | 
 |  |  |         { | 
 |  |  | #ifdef DUMPLICATE_OUTPUT | 
 |  |  |             printf("%s  %s\n", prn, lit); | 
 |  |  | #endif | 
 |  |  |             if (g_logger->fp) | 
 |  |  |                 fprintf(g_logger->fp, "%s  %s\n", prn, lit); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     if (!line_done) | 
 |  |  |     { | 
 |  |  |         int ldx = idx % CHARS_PER_LINE; | 
 |  |  |         lit[ldx++] = print_char[(int)buf[idx]]; | 
 |  |  |         lit[ldx] = '\0'; | 
 |  |  |  | 
 |  |  |         while ((++idx % CHARS_PER_LINE) != 0) | 
 |  |  |             strncat(prn, "   ", LINELEN); | 
 |  |  |  | 
 |  |  | #ifdef DUMPLICATE_OUTPUT | 
 |  |  |         printf("%s  %s\n", prn, lit); | 
 |  |  | #endif | 
 |  |  |         if (g_logger->fp) | 
 |  |  |             fprintf(g_logger->fp, "%s  %s\n", prn, lit); | 
 |  |  |  | 
 |  |  |     } | 
 |  |  | } | 
 
| New file | 
 |  |  | 
 |  |  | /******************************************************************************** | 
 |  |  |  *      Copyright:  (C) 2018 LingYun IoT System Studio | 
 |  |  |  *                  All rights reserved. | 
 |  |  |  * | 
 |  |  |  *       Filename:  logger.h | 
 |  |  |  *    Description:  This file is the linux infrastructural logger system library | 
 |  |  |  * | 
 |  |  |  *        Version:  1.0.0(08/08/2018~) | 
 |  |  |  *         Author:  Guo Wenxue <guowenxue@gmail.com> | 
 |  |  |  *      ChangeLog:  1, Release initial version on "08/08/2018 05:16:56 PM" | 
 |  |  |  *                  | 
 |  |  |  ********************************************************************************/ | 
 |  |  |  | 
 |  |  | #ifndef __LOGGER_H_ | 
 |  |  | #define __LOGGER_H_ | 
 |  |  |  | 
 |  |  | #include <stdarg.h> | 
 |  |  | #include <stdio.h> | 
 |  |  | #include <stdlib.h> | 
 |  |  | #include <string.h> | 
 |  |  | #include <unistd.h> | 
 |  |  | #include <signal.h> | 
 |  |  | #include <time.h> | 
 |  |  | #include <errno.h> | 
 |  |  |  | 
 |  |  | #include <pthread.h> | 
 |  |  | #include <sys/types.h> | 
 |  |  | #include <sys/time.h> | 
 |  |  |  | 
 |  |  | #define LOG_VERSION_STR             "2.0.0" | 
 |  |  |  | 
 |  |  | #ifndef FILENAME_LEN | 
 |  |  | #define FILENAME_LEN                64 | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  | #define DEFAULT_LOGFILE             "logger.log" | 
 |  |  | #define DBG_LOG_FILE                "console"  /*  Debug mode log file is console */  | 
 |  |  |  | 
 |  |  | #define LOG_ROLLBACK_SIZE           512    /* Default rollback log size  */ | 
 |  |  | #define LOG_ROLLBACK_NONE           0      /* Set rollback size to 0 will not rollback  */ | 
 |  |  |  | 
 |  |  | #define MAX_LOG_MESSAGE_LEN         0x1000 | 
 |  |  |  | 
 |  |  | //#define DUMPLICATE_OUTPUT  /* Log to file and printf on console  */ | 
 |  |  | #define LOG_FILE_LINE      /* Log the file and line */ | 
 |  |  |  | 
 |  |  | enum | 
 |  |  | { | 
 |  |  |     LOG_LEVEL_DISB = 0,               /*  Disable "Debug" */ | 
 |  |  |     LOG_LEVEL_FATAL,                  /*  Debug Level "Fatal" */ | 
 |  |  |     LOG_LEVEL_ERROR,                  /*  Debug Level "ERROR" */ | 
 |  |  |     LOG_LEVEL_WARN,                   /*  Debug Level "warnning" */ | 
 |  |  |     LOG_LEVEL_NRML,                   /*  Debug Level "Normal" */ | 
 |  |  |     LOG_LEVEL_DEBUG,                  /*  Debug Level "Debug" */ | 
 |  |  |     LOG_LEVEL_INFO,                   /*  Debug Level "Information" */ | 
 |  |  |     LOG_LEVEL_TRACE,                  /*  Debug Level "Trace" */ | 
 |  |  |     LOG_LEVEL_MAX, | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | #define LOGGER_CONSOLE             1<<1 | 
 |  |  | #define LOGGER_FILE                0<<1 | 
 |  |  |  | 
 |  |  | #define LOGGER_LEVEL_OPT           1<<2 /*  The log level is sepcified by the command option */ | 
 |  |  | typedef struct _st_logger | 
 |  |  | { | 
 |  |  |     unsigned char      flag;  /* This logger pointer is malloc() or passed by argument */ | 
 |  |  |     char               file[FILENAME_LEN]; | 
 |  |  |     int                level; | 
 |  |  |     int                size; | 
 |  |  |  | 
 |  |  |     FILE               *fp; | 
 |  |  | } st_logger; | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /* log_size unit KB  */ | 
 |  |  | extern int  logger_init(char *filename, int level, int log_size); | 
 |  |  | extern int  logger_reopen(void); | 
 |  |  |  | 
 |  |  | extern void logger_term(void); | 
 |  |  |  | 
 |  |  | extern void logger_comm(int level, char *fmt, ...); | 
 |  |  | extern void logger_line(int level, char *file, int line, char *fmt, ...); | 
 |  |  |  | 
 |  |  | extern void logger_dump(int level, char *buf, int len); | 
 |  |  |  | 
 |  |  | #ifdef LOG_FILE_LINE | 
 |  |  | #define log_trace(fmt, ...) logger_line(LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_info(fmt, ...)  logger_line(LOG_LEVEL_INFO,  __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_dbg(fmt, ...)   logger_line(LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_nrml(fmt, ...)  logger_line(LOG_LEVEL_NRML,  __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_warn(fmt, ...)  logger_line(LOG_LEVEL_WARN,  __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_err(fmt, ...)   logger_line(LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_fatal(fmt, ...) logger_line(LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__) | 
 |  |  | #else | 
 |  |  | #define log_trace(fmt, ...) logger_comm(LOG_LEVEL_TRACE, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_info(fmt, ...)  logger_comm(tLOG_LEVEL_INFO,  fmt, ##__VA_ARGS__) | 
 |  |  | #define log_dbg(fmt, ...)   logger_comm(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_nrml(fmt, ...)  logger_comm(LOG_LEVEL_NRML,  fmt, ##__VA_ARGS__) | 
 |  |  | #define log_warn(fmt, ...)  logger_comm(LOG_LEVEL_WARN,  fmt, ##__VA_ARGS__) | 
 |  |  | #define log_err(fmt, ...)   logger_comm(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__) | 
 |  |  | #define log_fatal(fmt, ...) logger_comm(LOG_LEVEL_FATAL, fmt, ##__VA_ARGS__) | 
 |  |  | #endif | 
 |  |  |  | 
 |  |  |  | 
 |  |  | #endif /* __LOGGER_H_ */ | 
 
| New file | 
 |  |  | 
 |  |  |  | 
 |  |  | CC=gcc | 
 |  |  |  | 
 |  |  | all: prepare ddns_client ddns_server | 
 |  |  |     @echo "Compile Done!" | 
 |  |  |  | 
 |  |  | prepare: | 
 |  |  |     @make clean | 
 |  |  |  | 
 |  |  | ddns_client: ddns_client.c ini_dictionary.c iniparser.c | 
 |  |  |     ${CC} $^ -o $@ | 
 |  |  |  | 
 |  |  | ddns_server: ddns_server.c logger.c | 
 |  |  |     ${CC} $^ -o $@ | 
 |  |  |  | 
 |  |  |  | 
 |  |  | clean: | 
 |  |  |     @rm -f ddns_client ddns_server | 
 |  |  |     @rm -f *.o | 
 |  |  |  | 
 |  |  | distclean: clean | 
 |  |  |     @rm -f tags cscope.* |