JWS C Library
C language utility library
option.c
Go to the documentation of this file.
00001 /*
00002  * This work is licensed under a Creative Commons 
00003  * Attribution-Noncommercial-Share Alike 3.0 United States License.
00004  * 
00005  *    http://creativecommons.org/licenses/by-nc-sa/3.0/us/
00006  * 
00007  * You are free:
00008  * 
00009  *    to Share - to copy, distribute, display, and perform the work
00010  *    to Remix - to make derivative works
00011  * 
00012  * Under the following conditions:
00013  * 
00014  *    Attribution. You must attribute the work in the manner specified by the
00015  *    author or licensor (but not in any way that suggests that they endorse you
00016  *    or your use of the work).
00017  * 
00018  *    Noncommercial. You may not use this work for commercial purposes.
00019  * 
00020  *    Share Alike. If you alter, transform, or build upon this work, you may
00021  *    distribute the resulting work only under the same or similar license to
00022  *    this one.
00023  * 
00024  * For any reuse or distribution, you must make clear to others the license
00025  * terms of this work. The best way to do this is by including this header.
00026  * 
00027  * Any of the above conditions can be waived if you get permission from the
00028  * copyright holder.
00029  * 
00030  * Apart from the remix rights granted under this license, nothing in this
00031  * license impairs or restricts the author's moral rights.
00032  */
00033 
00034 
00055 #include <jwsc/config.h>
00056 
00057 #include <stdlib.h>
00058 #include <stdio.h>
00059 #include <string.h>
00060 #include <errno.h>
00061 #include <assert.h>
00062 
00063 #include "jwsc/base/option.h"
00064 #include "jwsc/base/error.h"
00065 #include "jwsc/base/file_io.h"
00066 
00067 #ifdef JWSC_HAVE_DMALLOC
00068 #include <dmalloc.h>
00069 #endif
00070 
00071 
00083 static int is_option(const char* arg)
00084 {
00085     int c;
00086 
00087     if (strlen(arg) == 2 && arg[0] == '-' 
00088             && !((c=arg[1]) < 48 || (57 < c && c < 65) || (90 < c && c < 97)))
00089     {
00090         return 1;
00091     }
00092 
00093     if (strlen(arg) < 3 || arg[0] != '-' || arg[1] != '-')
00094     {
00095         return 0;
00096     }
00097                                                            
00098     c = arg[2];
00099                                                            
00100     if (c < 48 || (57 < c && c < 65) || (90 < c && c < 97))
00101     {
00102         return 0;
00103     }
00104                                                            
00105     return 1;
00106 }
00107 
00108 
00112 static int is_option_string(const char* arg, Option_string str)
00113 {
00114     int arg_len;
00115     int str_len;
00116     const char* c;
00117 
00118     arg_len = 0;
00119     c = arg;
00120     while (*c != '\0' && *c != '=')
00121     {
00122         c++;
00123         arg_len++;
00124     }
00125 
00126     str_len = strlen(str);
00127 
00128     if (arg_len < str_len)
00129     {
00130         return (strncmp(arg, str, str_len) == 0);
00131     }
00132 
00133     return (strncmp(arg, str, arg_len) == 0);
00134 }
00135 
00136 
00141 static int is_cmd_line_option_string(const char* arg, Option_string str)
00142 {
00143     int arg_len;
00144 
00145     arg_len = strlen(arg);
00146 
00147     if (arg_len < 3 || arg[0] != '-' || arg[1] != '-')
00148     {
00149         return 0;
00150     }
00151 
00152     return is_option_string((arg+2), str);
00153 }
00154 
00155 
00159 static int is_option_flag(const char* arg, Option_flag flag)
00160 {
00161     if (strlen(arg) != 2 || arg[0] != '-')
00162     {
00163         return 0;
00164     }
00165 
00166     return (arg[1] == flag);
00167 }
00168 
00169 
00176 static Option_arg get_option_string_arg(const char* str)
00177 {
00178     int len;
00179     int i;
00180     Option_arg arg = NULL;
00181 
00182     len = strlen(str);
00183 
00184     for (i = 0; i < len; i++)
00185     {
00186         if (str[ i ] == '=' && i+1 < len)
00187         {
00188             arg = &(str[ i+1 ]);
00189             break;
00190         }
00191     }
00192 
00193     return arg;
00194 }
00195 
00196 
00203 static Option_arg get_option_flag_arg
00204 (
00205     int          argc, 
00206     const char** argv, 
00207     int          index
00208 )
00209 {
00210     if (argc == index)
00211     {
00212         return NULL;
00213     }
00214 
00215     return argv[ index ];
00216 }
00217 
00218 
00229 void init_option_with_arg
00230 (
00231     Option_with_arg*     opt,
00232     Option_string        str, 
00233     Option_flag          flag, 
00234     Option_desc          desc,
00235     Option_func_with_arg func
00236 )
00237 {
00238     assert(opt != NULL);
00239     assert(str != NULL || flag != '\0');
00240     assert(func != NULL);
00241 
00242     opt->string = str;
00243     opt->flag   = flag;
00244     opt->desc   = desc;
00245     opt->func   = func;
00246 }
00247 
00248 
00259 void init_option_no_arg
00260 (
00261     Option_no_arg*     opt,
00262     Option_string      str, 
00263     Option_flag        flag, 
00264     Option_desc        desc,
00265     Option_func_no_arg func
00266 )
00267 {
00268     assert(opt != NULL);
00269     assert(str != NULL || flag != '\0');
00270     assert(func != NULL);
00271 
00272     opt->string = str;
00273     opt->flag   = flag;
00274     opt->desc   = desc;
00275     opt->func   = func;
00276 }
00277 
00278 
00298 Error* process_option_with_arg
00299 (
00300     int              argc, 
00301     const char**     argv, 
00302     int*             argi,
00303     Option_with_arg* opt
00304 )
00305 {
00306     int i;
00307     Option_arg arg = NULL;
00308     Error* e;
00309 
00310     *argi = 1;
00311     for (i = 1; i < argc; i++)
00312     {
00313         if (!is_option(argv[ i ]))
00314         {
00315             (*argi)++; continue;
00316         }
00317 
00318         if (is_cmd_line_option_string(argv[ i ], opt->string))
00319         {
00320             arg = get_option_string_arg(argv[ i ]);
00321             if ((e = opt->func(arg)) != NULL)
00322             {
00323                 return e;
00324             }
00325             break;
00326         }
00327         else if (is_option_flag(argv[ i ], opt->flag))
00328         {
00329             arg = get_option_flag_arg(argc, argv, i+1);
00330             if ((e = opt->func(arg)) != NULL)
00331             {
00332                 return e;
00333             }
00334             (*argi)++; i++;
00335             break;
00336         }
00337 
00338         (*argi)++;
00339     }
00340 
00341     return NULL;
00342 }
00343 
00344 
00361 Error* process_option_no_arg
00362 (
00363     int            argc, 
00364     const char**   argv, 
00365     int*           argi,
00366     Option_no_arg* opt
00367 )
00368 {
00369     int i;
00370     Error* e;
00371 
00372     *argi = 1;
00373     for (i = 1; i < argc; i++)
00374     {
00375         if (!is_option(argv[ i ]))
00376         {
00377             (*argi)++; continue;
00378         }
00379 
00380         if (is_cmd_line_option_string(argv[ i ], opt->string) || 
00381             is_option_flag(argv[ i ], opt->flag))
00382         {
00383             if ((e = opt->func()) != NULL)
00384             {
00385                 return e;
00386             }
00387             break;
00388         }
00389 
00390         (*argi)++;
00391     }
00392 
00393     return NULL;
00394 }
00395 
00396 
00429 Error* process_options
00430 (
00431     int              argc, 
00432     const char**     argv, 
00433     int*             argi,
00434     int              num_opts_no_arg,
00435     Option_no_arg*   opts_no_arg,
00436     int              num_opts_with_arg,
00437     Option_with_arg* opts_with_arg
00438 )
00439 {
00440     int i;
00441     int opt;
00442     int found;
00443 
00444     Option_with_arg* opt_with_arg;
00445     Option_no_arg*   opt_no_arg;
00446     Option_arg       arg = NULL;
00447     Error*           e;
00448 
00449     *argi = 1;
00450     for (i = 1; i < argc; i++)
00451     {
00452         if (!is_option(argv[ i ]))
00453         {
00454             return NULL;
00455         }
00456 
00457         found = 0;
00458 
00459         for (opt = 0; opt < num_opts_with_arg; opt++)
00460         {
00461             opt_with_arg = &(opts_with_arg[ opt ]);
00462 
00463             if (is_cmd_line_option_string(argv[ i ], opt_with_arg->string))
00464             {
00465                 arg = get_option_string_arg(argv[ i ]);
00466                 if ((e = opt_with_arg->func(arg)) != NULL)
00467                 {
00468                     return e;
00469                 }
00470                 found = 1;
00471                 break;
00472             }
00473             else if (is_option_flag(argv[ i ], opt_with_arg->flag))
00474             {
00475                 arg = get_option_flag_arg(argc, argv, i+1);
00476                 if ((e = opt_with_arg->func(arg)) != NULL)
00477                 {
00478                     return e;
00479                 }
00480                 found = 1;
00481                 (*argi)++; i++;
00482                 break;
00483             }
00484         }
00485         for (opt = 0; opt < num_opts_no_arg; opt++)
00486         {
00487             opt_no_arg = &(opts_no_arg[ opt ]);
00488 
00489             if (is_cmd_line_option_string(argv[ i ], opt_no_arg->string) || 
00490                     is_option_flag(argv[ i ], opt_no_arg->flag))
00491             {
00492                 if ((e = opt_no_arg->func()) != NULL)
00493                 {
00494                     return e;
00495                 }
00496                 found = 1;
00497                 break;
00498             }
00499         }
00500 
00501         if (!found)
00502         {
00503             const char* fmt = "Invalid option '%s'";
00504             int len = strlen(argv[ i ]) + strlen(fmt);
00505             char* msg = calloc(len+1,sizeof(char));
00506             sprintf(msg, fmt, argv[ i ]);
00507             e = JWSC_EARG(msg);
00508             free(msg);
00509             return e;
00510         }
00511 
00512         (*argi)++;
00513     }
00514 
00515     return NULL;
00516 }
00517 
00518 
00527 static void terminate_option_string(char* str)
00528 {
00529     char* c;
00530 
00531     c = str;
00532 
00533     while (*c != ' ' && *c != '\t' && *c != '\0')  c++;
00534 
00535     *c = '\0';
00536 }
00537 
00538 
00583 Error* process_options_from_file
00584 (
00585     const char*      fname,
00586     int              num_opts_no_arg,
00587     Option_no_arg*   opts_no_arg,
00588     int              num_opts_with_arg,
00589     Option_with_arg* opts_with_arg
00590 )
00591 {
00592     int opt;
00593     int found;
00594     int opt_str_len;
00595 
00596     char line[256] = {0};
00597     char* opt_str;
00598 
00599     FILE* fp;
00600 
00601     Option_with_arg* opt_with_arg;
00602     Option_no_arg*   opt_no_arg;
00603     Option_arg       arg = NULL;
00604     Error*           e;
00605 
00606 
00607     if ((fp = fopen(fname, "r")) == NULL)
00608     {
00609         return JWSC_EIO(strerror(errno));
00610     }
00611 
00612     while (skip_fp_spaces_and_comments(fp))
00613     {
00614         assert(fscanf(fp, "%255s", line) == 1);
00615         terminate_option_string(line);
00616         opt_str_len = strlen(line);
00617         assert((opt_str = malloc((opt_str_len+1)*sizeof(char))) != NULL);
00618         strcpy(opt_str, line);
00619         opt_str[opt_str_len] = '\0';
00620 
00621         found = 0;
00622 
00623         for (opt = 0; opt < num_opts_with_arg; opt++)
00624         {
00625             opt_with_arg = &(opts_with_arg[ opt ]);
00626 
00627             if (is_option_string(opt_str, opt_with_arg->string))
00628             {
00629                 arg = get_option_string_arg(opt_str);
00630                 if ((e = opt_with_arg->func(arg)) != NULL)
00631                 {
00632                     return e;
00633                 }
00634                 found = 1;
00635                 break;
00636             }
00637         }
00638         for (opt = 0; opt < num_opts_no_arg; opt++)
00639         {
00640             opt_no_arg = &(opts_no_arg[ opt ]);
00641 
00642             if (is_option_string(opt_str, opt_no_arg->string))
00643             {
00644                 if ((e = opt_no_arg->func()) != NULL)
00645                 {
00646                     return e;
00647                 }
00648                 found = 1;
00649                 break;
00650             }
00651         }
00652 
00653         if (!found)
00654         {
00655             const char* fmt = "Invalid option '%s'";
00656             int len = opt_str_len + strlen(fmt);
00657             char* msg = calloc(len+1,sizeof(char));
00658             sprintf(msg, fmt, opt_str);
00659             e = JWSC_EARG(msg);
00660             free(msg);
00661             return e;
00662         }
00663     }
00664 
00665     if (fclose(fp) != 0)
00666     {
00667         return JWSC_EIO(strerror(errno));
00668     }
00669 
00670     return NULL;
00671 }
00672 
00673 
00695 void print_option_with_arg
00696 (
00697     FILE*            fp,
00698     int              opt_width,
00699     Option_with_arg* opt
00700 )
00701 {
00702     int flag_width;
00703     int comma_width;
00704     int string_width;
00705     int min_width;
00706 
00707     int opt_padding;
00708     int desc_max_len;
00709     int num_lines;
00710     int len;
00711     int orig_opt_width;
00712     int opt_too_long;
00713 
00714     char  desc_buff[81] = {0};
00715     char* desc_buff_word_ptr;
00716     char* desc_buff_ptr;
00717     const char* desc_char_word_ptr;
00718     const char* desc_char_ptr;
00719 
00720     flag_width   = 3;
00721     string_width = (opt->string != NULL) ? 3 + strlen(opt->string) + 4 : 0;
00722     comma_width  = 1;
00723     min_width    = flag_width + comma_width + string_width + 2;
00724 
00725     orig_opt_width = (opt_width >= 0) ? opt_width : 0;
00726 
00727     if (opt_width <= 0 || opt_width < min_width)
00728     {
00729         opt_width = min_width;
00730     }
00731 
00732     assert(opt_width <= 60);
00733     len = 0;
00734 
00735     opt_padding  = opt_width - min_width;
00736 
00737     if (opt->flag != '\0')
00738         len += fprintf(fp, " -%c", opt->flag);
00739     else
00740         len += fprintf(fp, "   ");
00741 
00742     if (opt->flag != '\0' && string_width > 0)
00743         len += fprintf(fp, ",");
00744     else
00745         len += fprintf(fp, " ");
00746 
00747     if (string_width > 0)
00748         len += fprintf(fp, " --%s=ARG", opt->string);
00749     if (opt_padding > 0)
00750         len += fprintf(fp, "%*s", opt_padding, "");
00751     len += fprintf(fp, "  ");
00752 
00753     opt_too_long = len > orig_opt_width;
00754 
00755     desc_max_len  = 80 - opt_width;
00756 
00757     num_lines = 1;
00758 
00759     if (opt->desc == NULL || strlen(opt->desc) == 0)
00760     {
00761         fprintf(fp, "\n");
00762         return;
00763     }
00764 
00765     if (opt_too_long)
00766     {
00767         fprintf(fp, "\n");
00768         num_lines++;
00769     }
00770 
00771     desc_buff_word_ptr = desc_buff;
00772     desc_buff_ptr      = desc_buff;
00773     desc_char_word_ptr = opt->desc;
00774     desc_char_ptr      = opt->desc;
00775 
00776     len = 0;
00777 
00778     while (*desc_char_ptr != '\0')
00779     {
00780         if (len == desc_max_len)
00781         {
00782             if (desc_buff == desc_buff_word_ptr)
00783             {
00784                 desc_char_word_ptr = desc_char_ptr;
00785             }
00786             else
00787             {
00788                 desc_char_ptr = desc_char_word_ptr;
00789                 *desc_buff_word_ptr = '\0';
00790             }
00791 
00792             if (num_lines > 1)
00793             {
00794                 fprintf(fp, "%*s", orig_opt_width, "");
00795             }
00796 
00797             desc_buff_ptr = desc_buff;
00798             while (*desc_buff_ptr == ' ')
00799                 desc_buff_ptr++;
00800             fprintf(fp, "%s\n", desc_buff_ptr);
00801 
00802             desc_buff_word_ptr = desc_buff;
00803             desc_buff_ptr      = desc_buff;
00804 
00805             len = 0;
00806             num_lines++;
00807         }
00808 
00809         *desc_buff_ptr = *desc_char_ptr;
00810         desc_char_ptr++;
00811         desc_buff_ptr++;
00812         len++;
00813 
00814         if (*desc_char_ptr == ' ')
00815         {
00816             desc_buff_word_ptr = desc_buff_ptr;
00817             desc_char_word_ptr = desc_char_ptr;
00818         }
00819     }
00820 
00821     *desc_buff_ptr = *desc_char_ptr;
00822 
00823     if (*desc_buff != '\0')
00824     {
00825         if (num_lines > 1)
00826         {
00827             fprintf(fp, "%*s", orig_opt_width, "");
00828         }
00829 
00830         desc_buff_ptr = desc_buff;
00831         while (*desc_buff_ptr == ' ')
00832             desc_buff_ptr++;
00833         fprintf(fp, "%s\n", desc_buff_ptr);
00834     }
00835 }
00836 
00837 
00859 void print_option_no_arg
00860 (
00861     FILE*          fp,
00862     int            opt_width,
00863     Option_no_arg* opt
00864 )
00865 {
00866     int flag_width;
00867     int comma_width;
00868     int string_width;
00869     int min_width;
00870 
00871     int opt_padding;
00872     int desc_max_len;
00873     int num_lines;
00874     int len;
00875     int orig_opt_width;
00876 
00877     char  desc_buff[81] = {0};
00878     char* desc_buff_word_ptr;
00879     char* desc_buff_ptr;
00880     const char* desc_char_word_ptr;
00881     const char* desc_char_ptr;
00882 
00883     flag_width   = 3;
00884     string_width = (opt->string != NULL) ? 3 + strlen(opt->string) : 0;
00885     comma_width  = 1;
00886     min_width    = flag_width + comma_width + string_width + 2;
00887 
00888     orig_opt_width = (opt_width >= 0) ? opt_width : 0;
00889 
00890     if (opt_width <= 0 || opt_width < min_width)
00891     {
00892         opt_width = min_width;
00893     }
00894 
00895     assert(opt_width <= 60);
00896 
00897     opt_padding  = opt_width - min_width;
00898 
00899     if (opt->flag != '\0')
00900         fprintf(fp, " -%c", opt->flag);
00901     else
00902         fprintf(fp, "   ");
00903 
00904     if (opt->flag != '\0' && string_width > 0)
00905         fprintf(fp, ",");
00906     else
00907         fprintf(fp, " ");
00908 
00909     if (string_width > 0)
00910         fprintf(fp, " --%s", opt->string);
00911     if (opt_padding > 0)
00912         fprintf(fp, "%*s", opt_padding, "");
00913     fprintf(fp, "  ");
00914 
00915     desc_max_len  = 80 - opt_width;
00916 
00917     num_lines = 1;
00918 
00919     if (opt->desc == NULL || strlen(opt->desc) == 0)
00920     {
00921         fprintf(fp, "\n");
00922         return;
00923     }
00924 
00925     desc_buff_word_ptr = desc_buff;
00926     desc_buff_ptr      = desc_buff;
00927     desc_char_word_ptr = opt->desc;
00928     desc_char_ptr      = opt->desc;
00929 
00930     len = 0;
00931 
00932     while (*desc_char_ptr != '\0')
00933     {
00934         if (len == desc_max_len)
00935         {
00936             if (desc_buff == desc_buff_word_ptr)
00937             {
00938                 desc_char_word_ptr = desc_char_ptr;
00939             }
00940             else
00941             {
00942                 desc_char_ptr = desc_char_word_ptr;
00943                 *desc_buff_word_ptr = '\0';
00944             }
00945 
00946             if (num_lines > 1)
00947             {
00948                 fprintf(fp, "%*s", orig_opt_width, "");
00949             }
00950 
00951             desc_buff_ptr = desc_buff;
00952             while (*desc_buff_ptr == ' ')
00953                 desc_buff_ptr++;
00954             fprintf(fp, "%s\n", desc_buff_ptr);
00955 
00956             desc_buff_word_ptr = desc_buff;
00957             desc_buff_ptr      = desc_buff;
00958 
00959             len = 0;
00960             num_lines++;
00961         }
00962 
00963         *desc_buff_ptr = *desc_char_ptr;
00964         desc_char_ptr++;
00965         desc_buff_ptr++;
00966         len++;
00967 
00968         if (*desc_char_ptr == ' ')
00969         {
00970             desc_buff_word_ptr = desc_buff_ptr;
00971             desc_char_word_ptr = desc_char_ptr;
00972         }
00973     }
00974 
00975     *desc_buff_ptr = *desc_char_ptr;
00976 
00977     if (*desc_buff != '\0')
00978     {
00979         if (num_lines > 1)
00980         {
00981             fprintf(fp, "%*s", orig_opt_width, "");
00982         }
00983 
00984         desc_buff_ptr = desc_buff;
00985         while (*desc_buff_ptr == ' ')
00986             desc_buff_ptr++;
00987         fprintf(fp, "%s\n", desc_buff_ptr);
00988     }
00989 }
00990 
00991 
01006 void print_options
01007 (
01008     FILE*            fp,
01009     int              opt_width,
01010     int              num_opts_no_arg,
01011     Option_no_arg*   opts_no_arg,
01012     int              num_opts_with_arg,
01013     Option_with_arg* opts_with_arg
01014 )
01015 {
01016     int i;
01017     
01018     for (i = 0; i < num_opts_no_arg; i++)
01019     {
01020         print_option_no_arg(fp, opt_width, &(opts_no_arg[ i ]));
01021     }
01022     for (i = 0; i < num_opts_with_arg; i++)
01023     {
01024         print_option_with_arg(fp, opt_width, &(opts_with_arg[ i ]));
01025     }
01026 }