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