JWS C Library
C language utility library
|
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 }