nis-util  1.0.D108
lib/colon.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2001, 2008, 2009, 2011, 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
00016 // along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/ac/ctype.h>
00020 #include <lib/ac/stdarg.h>
00021 #include <libexplain/fclose.h>
00022 #include <libexplain/fopen.h>
00023 #include <libexplain/getc.h>
00024 #include <libexplain/output.h>
00025 #include <libexplain/realloc.h>
00026 
00027 #include <lib/colon.h>
00028 #include <lib/configuration.h>
00029 #include <lib/input/file.h>
00030 #include <lib/input/remove_hash_comments.h>
00031 #include <lib/rcstring/accumulator.h>
00032 
00033 
00034 colon::~colon()
00035 {
00036     close();
00037 }
00038 
00039 
00040 colon::colon(const rcstring &a_filename):
00041     filename(a_filename),
00042     non_printing_whine(0),
00043     token(token_eof),
00044     error_count(0),
00045     discard_comments_flag(false),
00046     discard_blank_lines_flag(false),
00047     explained_sequence_errors(false)
00048 {
00049 }
00050 
00051 
00052 void
00053 colon::open(void)
00054 {
00055     if (ip)
00056         return;
00057     ip = input_file::create(filename);
00058     if (discard_comments_flag)
00059         ip = input_remove_hash_comments::create(ip);
00060     error_count = 0;
00061 }
00062 
00063 
00064 void
00065 colon::close(void)
00066 {
00067     if (error_count)
00068     {
00069         explain_output_error_and_die
00070         (
00071             "%s: found %d fatal error%s",
00072             filename.c_str(),
00073             error_count,
00074             (error_count == 1 ? "" : "s")
00075         );
00076     }
00077     ip.reset();
00078     error_count = 0;
00079 }
00080 
00081 
00082 source_location
00083 colon::get_source_location(void)
00084     const
00085 {
00086     if (!ip)
00087         return source_location("file has been closed", 666);
00088     return ip->get_source_location();
00089 }
00090 
00091 
00092 void
00093 colon::lex(void)
00094 {
00095     rcstring_accumulator acc;
00096     for (;;)
00097     {
00098         token_locn = get_source_location();
00099         int c = ip->get();
00100         switch (c)
00101         {
00102         case input::eof:
00103             token = token_eof;
00104             return;
00105 
00106         case '\n':
00107             non_printing_whine = 0;
00108             token = token_eoln;
00109             return;
00110 
00111         case ':':
00112             token = token_colon;
00113             return;
00114 
00115         default:
00116             for (;;)
00117             {
00118                 if
00119                 (
00120 #ifdef AMERICAN_ASCII_ONLY
00121                     !isprint(c) && !isspace(c)
00122 #else
00123                     !c
00124 #endif
00125                 &&
00126                     !non_printing_whine
00127                 )
00128                 {
00129                     error(token_locn, "line contains non printing character");
00130                 }
00131                 acc.push_back((unsigned char)c);
00132 
00133                 c = ip->get();
00134                 if (c == input::eof)
00135                     break;
00136                 if (c == '\n' || c == ':')
00137                 {
00138                     ip->unget(c);
00139                     break;
00140                 }
00141             }
00142             token_value = acc.mkstr();
00143             token = token_string;
00144             return;
00145         }
00146     }
00147 }
00148 
00149 
00150 void
00151 colon::error(const source_location &locn, const char *fmt, ...)
00152 {
00153     va_list ap;
00154     va_start(ap, fmt);
00155     verror(locn, fmt, ap);
00156     va_end(ap);
00157 }
00158 
00159 
00160 void
00161 colon::verror(const source_location &locn, const char *fmt, va_list ap)
00162 {
00163     rcstring msg = rcstring::vformat(fmt, ap);
00164     explain_output_error("%s: %s", locn.get_both().c_str(), msg.c_str());
00165     if (!msg.ends_with("..."))
00166     {
00167         ++error_count;
00168         if (error_count >= 50)
00169         {
00170             explain_output_error_and_die
00171             (
00172                 "%s: too many fatal errors, aborting",
00173                 filename.c_str()
00174             );
00175         }
00176     }
00177 }
00178 
00179 
00180 void
00181 colon::pedantic_error(const source_location &locn, const char *fmt, ...)
00182 {
00183     va_list ap;
00184     va_start(ap, fmt);
00185     vpedantic_error(locn, fmt, ap);
00186     va_end(ap);
00187 }
00188 
00189 
00190 void
00191 colon::vpedantic_error(const source_location &locn, const char *fmt, va_list ap)
00192 {
00193     if (cfg.get_bool(SECTION_GLOBAL, "pedantic"))
00194         verror(locn, fmt, ap);
00195     else
00196         vwarning(locn, fmt, ap);
00197 }
00198 
00199 
00200 void
00201 colon::warning(const source_location &locn, const char *fmt, ...)
00202 {
00203     va_list ap;
00204     va_start(ap, fmt);
00205     vwarning(locn, fmt, ap);
00206     va_end(ap);
00207 }
00208 
00209 
00210 void
00211 colon::vwarning(const source_location &locn, const char *fmt, va_list ap)
00212 {
00213     // For maximum portability, always "use up" the arguments, even if
00214     // we don't issue the warning.
00215     rcstring msg = rcstring::vformat(fmt, ap);
00216 
00217     if (cfg.get_bool(SECTION_GLOBAL, ITEM_WARNING))
00218     {
00219         explain_output_error
00220         (
00221             "%s: warning: %s",
00222             locn.get_both().c_str(),
00223             msg.c_str()
00224         );
00225     }
00226 }
00227 
00228 
00229 bool
00230 colon::read_one_line(source_location &line_locn, line_t &line)
00231 {
00232     //
00233     // open the file if it isn't open already
00234     //
00235     open();
00236 
00237     //
00238     // Make sure the output is empty.
00239     //
00240     line.clear();
00241 
00242     //
00243     // Keep chewing tokens until we've read a whole line.
00244     //
00245     token = token_eoln;
00246     for (;;)
00247     {
00248         token_ty prev = token;
00249         lex();
00250         switch (token)
00251         {
00252         case token_eof:
00253             if (prev == token_colon)
00254                 line.push_back(rcstring(""));
00255             if (line.size() > 0)
00256             {
00257                 error(token_locn, "last line needs newline");
00258                 return true;
00259             }
00260             return false;
00261 
00262         case token_eoln:
00263             if (prev == token_colon)
00264             {
00265                 if (line.empty())
00266                     line_locn = token_locn;
00267                 line.push_back(rcstring());
00268             }
00269             if (line.empty() && discard_blank_lines_flag)
00270                 continue;
00271             return true;
00272 
00273         case token_colon:
00274             if (prev != token_string)
00275             {
00276                 if (line.empty())
00277                     line_locn = token_locn;
00278                 line.push_back(rcstring());
00279             }
00280             break;
00281 
00282         case token_string:
00283             if (line.empty())
00284                 line_locn = token_locn;
00285             line.push_back(token_value);
00286             break;
00287 
00288         default:
00289             error(token_locn, "malformed line");
00290             for (;;)
00291             {
00292                 lex();
00293                 if (token == token_eof)
00294                     return false;
00295                 if (token == token_eoln)
00296                     break;
00297             }
00298             break;
00299         }
00300     }
00301 }
00302 
00303 
00304 void
00305 colon::discard_blank_lines(void)
00306 {
00307     discard_blank_lines_flag = true;
00308 }
00309 
00310 
00311 void
00312 colon::discard_comments(void)
00313 {
00314     discard_comments_flag = true;
00315 }
00316 
00317 
00318 void
00319 colon::explain_sequence_errors(void)
00320 {
00321     if (explained_sequence_errors)
00322         return;
00323     explained_sequence_errors = true;
00324     explain_output_error
00325     (
00326         "by keeping the file sorted, common typographical errors show "
00327         "up as ordering errors"
00328     );
00329 }
00330 
00331 
00332 // vim: set ts=8 sw=4 et :