nis-util
1.0.D108
|
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 :