nis-util  1.0.D108
lib/configuration/parse.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/ac/string.h>
00020 #include <libexplain/output.h>
00021 
00022 #include <lib/configuration.h>
00023 #include <lib/configuration/item/boolean.h>
00024 #include <lib/configuration/item/integer.h>
00025 #include <lib/configuration/item/string.h>
00026 #include <lib/input/remove_hash_comments.h>
00027 #include <lib/rcstring/accumulator.h>
00028 
00029 
00030 rcstring
00031 configuration::canonical_name(const rcstring &name)
00032 {
00033     rcstring_accumulator acc;
00034     const char *cp = name.c_str();
00035     while (!isalnum((unsigned char)*cp))
00036         ++cp;
00037     bool insert_one_hyphen = false;
00038     for (;;)
00039     {
00040         unsigned char c = *cp++;
00041         if (!c)
00042         {
00043             if (acc.empty())
00044                 return "empty";
00045             return acc.mkstr();
00046         }
00047         if (isalnum(c))
00048         {
00049             if (insert_one_hyphen)
00050                 acc.push_back('-');
00051             acc.push_back(c);
00052             insert_one_hyphen = false;
00053         }
00054         else
00055             insert_one_hyphen = true;
00056     }
00057 }
00058 
00059 
00060 void
00061 configuration::parse(level_t level, const input::pointer &ip0)
00062 {
00063     input::pointer ip = input_remove_hash_comments::create(ip0);
00064     int number_of_errors = 0;
00065     rcstring section_name = SECTION_GLOBAL;
00066     for (;;)
00067     {
00068         source_location locn;
00069         rcstring line;
00070         if (!ip->read_one_line(locn, line))
00071             break;
00072         line = line.trim();
00073 
00074         // blank lines
00075         if (line.empty())
00076             continue;
00077 
00078         // section headers
00079         if (line.front() == '[' && line.back() == ']')
00080         {
00081             section_name = line.substr(1, line.size() - 2).trim();
00082 
00083             // make sure we like the section name
00084             {
00085                 rcstring suggest = canonical_name(section_name);
00086                 if (suggest != section_name)
00087                 {
00088                     explain_output_error
00089                     (
00090                         "%s: section name %s not valid, "
00091                             "did you mean %s instead?",
00092                         locn.get_both().c_str(),
00093                         section_name.quote_c().c_str(),
00094                         suggest.quote_c().c_str()
00095                     );
00096                     ++number_of_errors;
00097                     section_name = suggest;
00098                 }
00099             }
00100 
00101             // the global section is actually nameless
00102             if (section_name == "global" || section_name == "default")
00103                 section_name = SECTION_GLOBAL;
00104             continue;
00105         }
00106 
00107         // name = value
00108         {
00109             const char *cp = line.c_str();
00110             const char *eq = strchr(cp, '=');
00111             if (eq)
00112             {
00113                 rcstring name = rcstring(cp, eq - cp).trim();
00114                 ++eq;
00115                 const char *end = cp + line.size();
00116                 rcstring value = rcstring(eq, end - eq).trim();
00117 
00118                 // make sure we like the item name
00119                 {
00120                     rcstring suggest = canonical_name(name);
00121                     if (suggest != name)
00122                     {
00123                         explain_output_error
00124                         (
00125                             "%s: item name %s not valid, "
00126                                 "did you mean %s instead?",
00127                             locn.get_both().c_str(),
00128                             name.quote_c().c_str(),
00129                             suggest.quote_c().c_str()
00130                         );
00131                         ++number_of_errors;
00132                         name = suggest;
00133                     }
00134                 }
00135 
00136                 //
00137                 // Create a type-specific item.  This avoids repeatedly
00138                 // turning strings into long or bool for corresponding
00139                 // usage.
00140                 //
00141                 configuration_item::pointer item =
00142                     configuration_item_boolean::create_if_candidate
00143                     (
00144                         locn,
00145                         level,
00146                         name,
00147                         value
00148                     );
00149                 if (!item)
00150                 {
00151                     item =
00152                         configuration_item_integer::create_if_candidate
00153                         (
00154                             locn,
00155                             level,
00156                             name,
00157                             value
00158                         );
00159                 }
00160                 if (!item)
00161                 {
00162                     item =
00163                         configuration_item_string::create
00164                         (
00165                             locn,
00166                             level,
00167                             name,
00168                             value
00169                         );
00170                     set(section_name, item);
00171                 }
00172                 set(section_name, item);
00173                 continue;
00174             }
00175         }
00176 
00177         // looks like garbage
00178         explain_output_error("%s: malformed line", locn.get_both().c_str());
00179         ++number_of_errors;
00180     }
00181     if (number_of_errors > 0)
00182     {
00183         explain_output_error_and_die
00184         (
00185             "%s: found %d fatal error%s",
00186             ip->get_source_location().get_file_name().c_str(),
00187             number_of_errors,
00188             (number_of_errors == 1 ? "" : "s")
00189         );
00190     }
00191 }
00192 
00193 
00194 // vim: set ts=8 sw=4 et :