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 00054 #include <jwsc++/config.h> 00055 00056 #include <cstdlib> 00057 #include <cstring> 00058 #include <cctype> 00059 #include <iostream> 00060 #include <fstream> 00061 #include <sstream> 00062 #include <list> 00063 #include <vector> 00064 00065 #include "jwsc++/base/exception.h" 00066 #include "jwsc++/base/option.h" 00067 00068 #ifdef JWSCXX_HAVE_DMALLOC 00069 #include <dmalloc.h> 00070 #endif 00071 00072 00073 using std::ios_base; 00074 using std::istream; 00075 using std::ostream; 00076 using std::ifstream; 00077 using std::ostringstream; 00078 using std::list; 00079 using std::vector; 00080 using std::strlen; 00081 using std::strcmp; 00082 using std::strncmp; 00083 using std::isspace; 00084 00085 using namespace jwscxx::base; 00086 00087 00088 /* ---------------------------- Option ------------------------------------ */ 00089 00095 Option::Option(char s_name, const char* l_name, const char* desc) 00096 throw (Arg_error) 00097 { 00098 if (!s_name && !l_name) 00099 { 00100 throw Arg_error("Option must have a short and/or long name"); 00101 } 00102 00103 this->s_name = s_name; 00104 this->l_name = l_name; 00105 this->desc = desc; 00106 } 00107 00108 00114 bool Option::is_s_name_option(const char* arg) const 00115 { 00116 if (strlen(arg) != 2) 00117 { 00118 return false; 00119 } 00120 00121 char str[3] = {'-', s_name, 0}; 00122 00123 return (strcmp(arg, str) == 0); 00124 } 00125 00126 00132 bool Option::is_l_name_option(const char* arg) const 00133 { 00134 char str[3] = {'-', '-', 0}; 00135 00136 if (strncmp(arg, str, 2) != 0) 00137 { 00138 return false; 00139 } 00140 00141 arg += 2; 00142 00143 const char* eq = arg; 00144 while (*eq != '=' && *eq != 0) 00145 { 00146 eq++; 00147 } 00148 00149 size_t arg_len = eq - arg; 00150 00151 if (arg_len != strlen(l_name)) 00152 { 00153 return false; 00154 } 00155 00156 return (strncmp(arg, l_name, arg_len) == 0); 00157 } 00158 00159 /* -------------------------------------------------------------------------- */ 00160 00161 00162 00163 00164 00165 00166 00167 00168 /* -------------------------- Option_with_arg ----------------------------- */ 00169 00176 Option_with_arg::Option_with_arg 00177 ( 00178 char s_name, 00179 const char* l_name, 00180 const char* desc, 00181 void (*func)(const char* arg) throw (Arg_error) 00182 ) 00183 throw (Arg_error) 00184 : Option(s_name, l_name, desc) 00185 { 00186 this->func = func; 00187 } 00188 00189 00190 00200 int Option_with_arg::process(int argc, char** argv) const 00201 throw (Arg_error) 00202 { 00203 for (int i = 0; i < argc; i++) 00204 { 00205 if (l_name && is_l_name_option(argv[ i ])) 00206 { 00207 func(get_l_name_arg(argv[ i ])); 00208 return i; 00209 } 00210 if (s_name && is_s_name_option(argv[ i ])) 00211 { 00212 func(((i+1) < argc) ? argv[ i+1 ] : 0); 00213 return i; 00214 } 00215 } 00216 00217 return -1; 00218 } 00219 00220 00237 void Option_with_arg::print(ostream& out, int width) const throw (Arg_error) 00238 { 00239 if (width > 60) 00240 { 00241 throw Arg_error("Option name field width > 60"); 00242 } 00243 00244 int s_name_len = 2; 00245 int l_name_len = 2 + ((l_name) ? strlen(l_name)+4 : 0); 00246 int opt_len = 1 + s_name_len + 2 + l_name_len + 2; 00247 00248 int opt_padding = (opt_len < width) ? width - opt_len : 0; 00249 00250 out << " "; 00251 00252 if (s_name) 00253 out << "-" << s_name; 00254 else 00255 out << " "; 00256 00257 if (s_name && l_name) 00258 out << ","; 00259 else 00260 out << " "; 00261 00262 out << " "; 00263 00264 if (l_name) 00265 out << "--" << l_name << "=ARG"; 00266 else 00267 out << " "; 00268 00269 out << " "; 00270 00271 if (opt_padding > 0) 00272 { 00273 out.width(opt_padding); 00274 out << ""; 00275 out.width(0); 00276 } 00277 00278 if (!desc || strlen(desc) == 0) 00279 { 00280 out << "\n"; 00281 return; 00282 } 00283 00284 char desc_buff[81] = {0}; 00285 char* desc_buff_word_ptr = desc_buff; 00286 char* desc_buff_ptr = desc_buff; 00287 const char* desc_char_word_ptr = desc; 00288 const char* desc_char_ptr = desc; 00289 00290 int num_lines = 1; 00291 int len = 0; 00292 00293 while (*desc_char_ptr != '\0') 00294 { 00295 int desc_max_len; 00296 00297 if (num_lines == 1) 00298 { 00299 desc_max_len = 80 - (opt_len + opt_padding); 00300 } 00301 else 00302 { 00303 desc_max_len = 80 - width; 00304 } 00305 00306 if (len == desc_max_len) 00307 { 00308 if (desc_buff == desc_buff_word_ptr) 00309 { 00310 desc_char_word_ptr = desc_char_ptr; 00311 } 00312 else 00313 { 00314 desc_char_ptr = desc_char_word_ptr; 00315 *desc_buff_word_ptr = '\0'; 00316 } 00317 00318 if (num_lines > 1) 00319 { 00320 out.width(width); 00321 out << ""; 00322 out.width(0); 00323 } 00324 00325 desc_buff_ptr = desc_buff; 00326 while (*desc_buff_ptr == ' ') 00327 desc_buff_ptr++; 00328 out << desc_buff_ptr << "\n"; 00329 00330 desc_buff_word_ptr = desc_buff; 00331 desc_buff_ptr = desc_buff; 00332 00333 len = 0; 00334 num_lines++; 00335 } 00336 00337 *desc_buff_ptr = *desc_char_ptr; 00338 desc_char_ptr++; 00339 desc_buff_ptr++; 00340 len++; 00341 00342 if (*desc_char_ptr == ' ') 00343 { 00344 desc_buff_word_ptr = desc_buff_ptr; 00345 desc_char_word_ptr = desc_char_ptr; 00346 } 00347 } 00348 00349 *desc_buff_ptr = *desc_char_ptr; 00350 00351 if (*desc_buff != '\0') 00352 { 00353 if (num_lines > 1) 00354 { 00355 out.width(width); 00356 out << ""; 00357 out.width(0); 00358 } 00359 00360 desc_buff_ptr = desc_buff; 00361 while (*desc_buff_ptr == ' ') 00362 desc_buff_ptr++; 00363 out << desc_buff_ptr << "\n"; 00364 } 00365 } 00366 00367 00374 const char* Option_with_arg::get_l_name_arg(const char* str) const 00375 { 00376 while (*str && *str != '=') 00377 { 00378 str++; 00379 } 00380 00381 if (*str) 00382 { 00383 return str+1; 00384 } 00385 00386 return 0; 00387 } 00388 00389 /* -------------------------------------------------------------------------- */ 00390 00391 00392 00393 00394 00395 00396 00397 00398 /* -------------------------- Option_no_arg ------------------------------- */ 00399 00406 Option_no_arg::Option_no_arg 00407 ( 00408 char s_name, 00409 const char* l_name, 00410 const char* desc, 00411 void (*func)(void) 00412 ) 00413 throw (Arg_error) 00414 : Option(s_name, l_name, desc) 00415 { 00416 this->func = func; 00417 } 00418 00419 00431 int Option_no_arg::process(int argc, char** argv) const throw (Arg_error) 00432 { 00433 for (int i = 0; i < argc; i++) 00434 { 00435 if ((l_name && is_l_name_option(argv[ i ])) || 00436 (s_name && is_s_name_option(argv[ i ]))) 00437 { 00438 func(); 00439 return i; 00440 } 00441 } 00442 00443 return -1; 00444 } 00445 00446 00463 void Option_no_arg::print(ostream& out, int width) const throw (Arg_error) 00464 { 00465 if (width > 60) 00466 { 00467 throw Arg_error("Option name field width > 60"); 00468 } 00469 00470 int s_name_len = 2; 00471 int l_name_len = 2 + ((l_name) ? strlen(l_name) : 0); 00472 int opt_len = 1 + s_name_len + 2 + l_name_len + 2; 00473 00474 int opt_padding = (opt_len < width) ? width - opt_len : 0; 00475 00476 00477 out << " "; 00478 00479 if (s_name) 00480 out << "-" << s_name; 00481 else 00482 out << " "; 00483 00484 if (s_name && l_name) 00485 out << ","; 00486 else 00487 out << " "; 00488 00489 out << " "; 00490 00491 if (l_name) 00492 out << "--" << l_name; 00493 else 00494 out << " "; 00495 00496 out << " "; 00497 00498 if (opt_padding > 0) 00499 { 00500 out.width(opt_padding); 00501 out << ""; 00502 out.width(0); 00503 } 00504 00505 if (!desc || strlen(desc) == 0) 00506 { 00507 out << "\n"; 00508 return; 00509 } 00510 00511 char desc_buff[81] = {0}; 00512 char* desc_buff_word_ptr = desc_buff; 00513 char* desc_buff_ptr = desc_buff; 00514 const char* desc_char_word_ptr = desc; 00515 const char* desc_char_ptr = desc; 00516 00517 int num_lines = 1; 00518 int len = 0; 00519 00520 while (*desc_char_ptr != '\0') 00521 { 00522 int desc_max_len; 00523 00524 if (num_lines == 1) 00525 { 00526 desc_max_len = 80 - (opt_len + opt_padding); 00527 } 00528 else 00529 { 00530 desc_max_len = 80 - width; 00531 } 00532 00533 if (len == desc_max_len) 00534 { 00535 if (desc_buff == desc_buff_word_ptr) 00536 { 00537 desc_char_word_ptr = desc_char_ptr; 00538 } 00539 else 00540 { 00541 desc_char_ptr = desc_char_word_ptr; 00542 *desc_buff_word_ptr = '\0'; 00543 } 00544 00545 if (num_lines > 1) 00546 { 00547 out.width(width); 00548 out << ""; 00549 out.width(0); 00550 } 00551 00552 desc_buff_ptr = desc_buff; 00553 while (*desc_buff_ptr == ' ') 00554 desc_buff_ptr++; 00555 out << desc_buff_ptr << "\n"; 00556 00557 desc_buff_word_ptr = desc_buff; 00558 desc_buff_ptr = desc_buff; 00559 00560 len = 0; 00561 num_lines++; 00562 } 00563 00564 *desc_buff_ptr = *desc_char_ptr; 00565 desc_char_ptr++; 00566 desc_buff_ptr++; 00567 len++; 00568 00569 if (*desc_char_ptr == ' ') 00570 { 00571 desc_buff_word_ptr = desc_buff_ptr; 00572 desc_char_word_ptr = desc_char_ptr; 00573 } 00574 } 00575 00576 *desc_buff_ptr = *desc_char_ptr; 00577 00578 if (*desc_buff != '\0') 00579 { 00580 if (num_lines > 1) 00581 { 00582 out.width(width); 00583 out << ""; 00584 out.width(0); 00585 } 00586 00587 desc_buff_ptr = desc_buff; 00588 while (*desc_buff_ptr == ' ') 00589 desc_buff_ptr++; 00590 out << desc_buff_ptr << "\n"; 00591 } 00592 } 00593 00594 /* -------------------------------------------------------------------------- */ 00595 00596 00597 00598 00599 00600 00601 00602 00603 /* ---------------------------- Options ----------------------------------- */ 00604 00606 Options::~Options() 00607 { 00608 for (list<Option*>::iterator it = opts.begin(); it != opts.end(); it++) 00609 { 00610 delete *it; 00611 } 00612 } 00613 00614 00621 void Options::add(Option* opt) 00622 { 00623 opts.push_back(opt); 00624 } 00625 00626 00635 int Options::process(int argc, char** argv) const throw (Arg_error) 00636 { 00637 int n = 0; 00638 00639 for (int i = 0; i < argc; i++) 00640 { 00641 bool processed = false; 00642 00643 for (list<Option*>::const_iterator it = opts.begin(); 00644 it != opts.end(); it++) 00645 { 00646 if ((*it)->process(1, &(argv[ i ])) >= 0) 00647 { 00648 processed = true; 00649 break; 00650 } 00651 } 00652 00653 if (!processed && is_an_option(argv[ i ])) 00654 { 00655 ostringstream ost; 00656 ost << "Unknown option '" << argv[ i ] << '\''; 00657 throw Arg_error(ost.str()); 00658 } 00659 else if (!processed) 00660 { 00661 n++; 00662 } 00663 } 00664 00665 return n; 00666 } 00667 00668 00675 void Options::process(const char* fname) const throw (IO_error, Arg_error) 00676 { 00677 ostringstream ost; 00678 ifstream in; 00679 00680 in.open(fname); 00681 if (in.fail()) 00682 { 00683 ost << fname << ": Could not open file"; 00684 throw IO_error(ost.str()); 00685 } 00686 00687 try 00688 { 00689 process(in); 00690 } 00691 catch (Arg_error e) 00692 { 00693 ost << fname << ": " << e.get_msg(); 00694 throw Arg_error(ost.str()); 00695 } 00696 00697 in.close(); 00698 if (in.fail()) 00699 { 00700 ost << fname << ": Could not close file"; 00701 throw IO_error(ost.str()); 00702 } 00703 } 00704 00705 00711 void Options::process(istream& in) const throw (Arg_error) 00712 { 00713 char c; 00714 00715 while (in.peek() != EOF) 00716 { 00717 c = in.get(); 00718 if (in.fail()) 00719 { 00720 throw Arg_error("Could not get character from input"); 00721 } 00722 00723 if (c == '#') 00724 { 00725 in.ignore(255, '\n'); 00726 if (in.fail()) 00727 { 00728 throw Arg_error("Could not ignore line"); 00729 } 00730 } 00731 else if (!isspace(c)) 00732 { 00733 // NEED TO ALLOCATE A CONSTANT STRING FOR EACH LINE READ 00734 char* buf = (char*) calloc(256, sizeof(char)); 00735 buf[0] = '-'; 00736 buf[1] = '-'; 00737 buf[255] = 0; 00738 00739 in.putback(c); 00740 in.getline(&(buf[2]), 253); 00741 if (in.fail()) 00742 { 00743 throw Arg_error("Could not read line"); 00744 } 00745 00746 bool processed = false; 00747 00748 for (list<Option*>::const_iterator it = opts.begin(); 00749 it != opts.end(); it++) 00750 { 00751 if ((*it)->process(1, &buf) >= 0) 00752 { 00753 processed = true; 00754 break; 00755 } 00756 } 00757 if (!processed && is_an_option(buf)) 00758 { 00759 ostringstream ost; 00760 ost << "Unknown option '" << &(buf[2]) << '\''; 00761 throw Arg_error(ost.str()); 00762 } 00763 } 00764 } 00765 } 00766 00767 00784 void Options::print(ostream& out, int width) const 00785 { 00786 for (list<Option*>::const_iterator it = opts.begin(); 00787 it != opts.end(); it++) 00788 { 00789 (*it)->print(out, width); 00790 } 00791 } 00792 00793 00795 bool Options::is_an_option(const char* arg) const 00796 { 00797 int len = strlen(arg); 00798 00799 if (len < 2) 00800 { 00801 return false; 00802 } 00803 00804 if (len == 2 && arg[0] == '-') 00805 { 00806 return true; 00807 } 00808 00809 if (arg[0] == '-' && arg[1] == '-') 00810 { 00811 return true; 00812 } 00813 00814 return false; 00815 } 00816 00817 00818 /* -------------------------------------------------------------------------- */