nis-util
1.0.D108
|
00001 // 00002 // nis-util - NIS Administration Utilities 00003 // Copyright (C) 2002, 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/rcstring/accumulator.h> 00020 #include <lib/space/protocols.h> 00021 00022 00023 space_protocols::~space_protocols() 00024 { 00025 discard_comments(); 00026 discard_blank_lines(); 00027 } 00028 00029 00030 space_protocols::space_protocols(const rcstring &a_filename) : 00031 space(a_filename), 00032 last_num(-1) 00033 { 00034 discard_comments(); 00035 discard_blank_lines(); 00036 } 00037 00038 00039 space_protocols::pointer 00040 space_protocols::create(const rcstring &a_filename) 00041 { 00042 return pointer(new space_protocols(a_filename)); 00043 } 00044 00045 00046 space_protocols::record::pointer 00047 space_protocols::get(void) 00048 { 00049 for (;;) 00050 { 00051 source_location line_locn; 00052 line_t line; 00053 if (!read_one_line(line_locn, line)) 00054 return record::pointer(); 00055 if (line.size() < 2) 00056 { 00057 error 00058 ( 00059 line_locn, 00060 "too few fields (expected 2, given %d)", 00061 (int)line.size() 00062 ); 00063 continue; 00064 } 00065 00066 // 00067 // The first field is the name. 00068 // 00069 rcstring rec_name = line[0]; 00070 rcstring name_suggestion; 00071 // NB: primary key is not allowed to contain upper case 00072 if (!is_ok_name(rec_name, false, name_suggestion)) 00073 { 00074 error 00075 ( 00076 line_locn, 00077 "protocol name %s not valid, suggest %s instead", 00078 rec_name.quote_c().c_str(), 00079 name_suggestion.quote_c().c_str() 00080 ); 00081 rec_name = name_suggestion; 00082 } 00083 00084 // 00085 // The second field is the number. 00086 // 00087 rcstring rec_number = line[1]; 00088 long rec_number_binary = 0; 00089 if (!rec_number.to_long(rec_number_binary)) 00090 { 00091 error 00092 ( 00093 line_locn, 00094 "protocol number %s not valid", 00095 rec_number.quote_c().c_str() 00096 ); 00097 continue; 00098 } 00099 if (rec_number_binary < 0 || rec_number_binary >= 256) 00100 { 00101 error 00102 ( 00103 line_locn, 00104 "protocol number %ld out of range (0..255)", 00105 rec_number_binary 00106 ); 00107 continue; 00108 } 00109 // normalise the number 00110 rec_number = rcstring::format("%ld", rec_number_binary); 00111 00112 // 00113 // Check record sequencing. 00114 // 00115 if (rec_number_binary < last_num) 00116 { 00117 error 00118 ( 00119 line_locn, 00120 "protocol number %ld out of order", 00121 rec_number_binary 00122 ); 00123 explain_sequence_errors(); 00124 } 00125 last_num = rec_number_binary; 00126 00127 // 00128 // The third and subsequent arguments are aliases. 00129 // 00130 record::aliases_t rec_aliases; 00131 for 00132 ( 00133 line_t::const_iterator it = line.begin() + 2; 00134 it != line.end(); 00135 ++it 00136 ) 00137 { 00138 rcstring alias_name = *it; 00139 // NB: aliases are permitted to contain upper case 00140 if (!is_ok_name(alias_name, true, name_suggestion)) 00141 { 00142 error 00143 ( 00144 line_locn, 00145 "protocol alias %s not valid, suggest %s instead", 00146 alias_name.quote_c().c_str(), 00147 name_suggestion.quote_c().c_str() 00148 ); 00149 alias_name = name_suggestion; 00150 } 00151 rec_aliases.push_back(alias_name); 00152 } 00153 00154 // 00155 // Create a new record. 00156 // 00157 record::pointer rp = 00158 record::create(line_locn, rec_name, rec_number, rec_aliases); 00159 00160 // 00161 // Check line length. 00162 // 00163 if (rp->representation().size() >= 512) 00164 { 00165 error 00166 ( 00167 rp->get_source_location(), 00168 "line too long (NIS limits records to 511 bytes)" 00169 ); 00170 } 00171 00172 // 00173 // Return the new record to the caller. 00174 // 00175 return rp; 00176 } 00177 } 00178 00179 00180 space_protocols::record::~record() 00181 { 00182 } 00183 00184 00185 space_protocols::record::record( 00186 const source_location &a_locn, 00187 const rcstring &a_name, 00188 const rcstring &a_number, 00189 const aliases_t &a_aliases 00190 ) : 00191 locn(a_locn), 00192 name(a_name), 00193 number(a_number), 00194 aliases(a_aliases) 00195 { 00196 } 00197 00198 00199 space_protocols::record::pointer 00200 space_protocols::record::create(const source_location &a_locn, 00201 const rcstring &a_name, const rcstring &a_number, 00202 const aliases_t &a_aliases) 00203 { 00204 return pointer(new record(a_locn, a_name, a_number, a_aliases)); 00205 } 00206 00207 00208 int 00209 space_protocols::record::get_number_binary(void) 00210 const 00211 { 00212 long value = 0; 00213 if (!number.to_long(value)) 00214 return -1; 00215 return value; 00216 } 00217 00218 00219 rcstring 00220 space_protocols::record::representation(void) 00221 const 00222 { 00223 rcstring_accumulator acc; 00224 acc.push_back(name); 00225 acc.push_back(' '); 00226 acc.push_back(number); 00227 for 00228 ( 00229 aliases_t::const_iterator it = aliases.begin(); 00230 it != aliases.end(); 00231 ++it 00232 ) 00233 { 00234 acc.push_back(' '); 00235 acc.push_back(*it); 00236 } 00237 return acc.mkstr(); 00238 } 00239 00240 00241 bool 00242 space_protocols::is_ok_name(const rcstring &text, bool upper_case_ok, 00243 rcstring &suggest) 00244 { 00245 if (text.empty()) 00246 { 00247 suggest = "a"; 00248 return false; 00249 } 00250 00251 // 00252 // See http://www.iana.org/assignments/protocol-numbers for assigned 00253 // numbers. 00254 // 00255 // According to http://www.iana.org/assignments/service-names 00256 // "A protocol name may be up to 40 characters taken from the set of 00257 // upper case letters, digits, and the punctuation character hyphen. 00258 // It must start with a letter, and end with a letter or digit." 00259 // 00260 enum { maxlen = 40 }; 00261 00262 // 00263 // However, IANA themselves don't follow the rules. 00264 // Examples are "3PC", "TP++", "AX.25" and "A/N" 00265 // 00266 // Traditionally unix protocol names have been lowercase, so we will 00267 // continue to allow lower case names. The upper_case_ok argument 00268 // is to indicate that upper case is acceptable in aliases. 00269 // 00270 const char *cp = text.c_str(); 00271 const char *end = 00272 cp + (text.size() < maxlen ? text.size() : (size_t)maxlen); 00273 rcstring_accumulator acc; 00274 bool all_numeric = true; 00275 while (cp < end) 00276 { 00277 unsigned char c = *cp++; 00278 switch (c) 00279 { 00280 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 00281 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 00282 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 00283 case 'v': case 'w': case 'x': case 'y': case 'z': 00284 all_numeric = false; 00285 break; 00286 00287 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 00288 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 00289 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 00290 case 'V': case 'W': case 'X': case 'Y': case 'Z': 00291 if (!upper_case_ok) 00292 c = tolower(c); 00293 all_numeric = false; 00294 break; 00295 00296 case '0': case '1': case '2': case '3': case '4': 00297 case '5': case '6': case '7': case '8': case '9': 00298 break; 00299 00300 #ifndef STRICT_IANA_RULES 00301 // 00302 // To cope with "TP++", "A/N" and "AX.25" we allow some 00303 // additional characters. 00304 // 00305 case '/': 00306 all_numeric = false; 00307 break; 00308 00309 case '.': 00310 case '+': 00311 #endif 00312 case '-': 00313 break; 00314 00315 default: 00316 c = '-'; 00317 break; 00318 } 00319 acc.push_back(c); 00320 } 00321 00322 // 00323 // To cope with the "3PC" case, we will allow leading digits. 00324 // 00325 #ifdef STRICT_IANA_RULES 00326 if (!isalpha(acc.front())) 00327 #else 00328 if (!isalnum(acc.front())) 00329 #endif 00330 { 00331 if (acc.size() >= maxlen) 00332 acc.pop_back(); 00333 acc.push_front('a'); 00334 all_numeric = false; 00335 } 00336 00337 // 00338 // To cope with the "TP++' case, we don't enforce the "ends in a 00339 // letter or digit" rule. 00340 // 00341 #ifdef STRICT_IANA_RULES 00342 if (!isalnum(acc.back())) 00343 { 00344 if (acc.size() >= maxlen) 00345 acc.pop_back(); 00346 acc.push_back('a'); 00347 all_numeric = false; 00348 } 00349 #endif 00350 00351 // one last check 00352 if (all_numeric) 00353 { 00354 if (acc.size() >= maxlen) 00355 acc.pop_back(); 00356 acc.push_front('a'); 00357 } 00358 00359 // build the suggestion 00360 suggest = acc.mkstr(); 00361 00362 // the name is ok if the suggestion is identical to the original text 00363 return (text == suggest); 00364 } 00365 00366 00367 // vim: set ts=8 sw=4 et :