Line data Source code
1 : //#include <stdlib.h>
2 : #include <stdint.h>
3 :
4 : #include <iostream>
5 : #include <string>
6 :
7 : #include "conffwk/Configuration.hpp"
8 : #include "conffwk/ConfigObject.hpp"
9 :
10 : using namespace dunedaq::conffwk;
11 :
12 0 : ERS_DECLARE_ISSUE(
13 : conffwk_test_rw,
14 : BadCommandLine,
15 : "bad command line: " << reason,
16 : ((const char*)reason)
17 : )
18 :
19 0 : ERS_DECLARE_ISSUE(
20 : conffwk_test_rw,
21 : ConfigException,
22 : "caught dunedaq::conffwk::Exception exception",
23 : )
24 :
25 : static void
26 0 : usage()
27 : {
28 0 : std::cout <<
29 : "Usage: conffwk_test_rw -d data_name -s schema_name -p plugin_spec\n"
30 : "\n"
31 : "Options/Arguments:\n"
32 : " -d data_name name of creating data file\n"
33 : " -s schema_name name of including schema file\n"
34 : " -p plugin_spec conffwk plugin specification (oksconflibs | rdbconffwk:server-name)\n"
35 : "\n"
36 : "Description:\n"
37 0 : " The utility tests creation of files and objects using different plugins.\n\n";
38 0 : }
39 :
40 : static void
41 0 : no_param(const char * s)
42 : {
43 0 : std::ostringstream text;
44 0 : text << "no parameter for " << s << " provided";
45 0 : ers::fatal(conffwk_test_rw::BadCommandLine(ERS_HERE, text.str().c_str()));
46 0 : exit(EXIT_FAILURE);
47 0 : }
48 :
49 : ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
50 :
51 0 : template<class T> void set_value(ConfigObject& o, const std::string& name, T value)
52 : {
53 0 : o.set_by_val(name, value);
54 0 : }
55 :
56 0 : template<class T> void set_ref(ConfigObject& o, const std::string& name, T value)
57 : {
58 0 : o.set_by_ref(name, value);
59 0 : }
60 :
61 0 : template<class T> void check_value(ConfigObject& o, const std::string& name, T v1)
62 : {
63 0 : T v2;
64 0 : o.get(name, v2);
65 0 : if(v1 != v2) {
66 0 : std::cerr << "ERROR reading attribute: \'" << name << '\'' << std::endl;
67 : }
68 : else {
69 0 : std::cout << "TEST " << name << " of " << o << " is OK\n";
70 : }
71 0 : }
72 :
73 0 : void check_object(ConfigObject& o, const std::string& name, ConfigObject * o1)
74 : {
75 0 : ConfigObject o2;
76 0 : o.get(name, o2);
77 0 : if(o1 == 0) {
78 0 : if(!o2.is_null()) {
79 0 : std::cerr << "ERROR reading relationship: \'" << name << "\' (read an object instead of NULL)" << std::endl;
80 : }
81 : }
82 : else {
83 0 : if(o2.is_null()) {
84 0 : std::cerr << "ERROR reading relationship: \'" << name << "\' (read NULL instead of object)" << std::endl;
85 : }
86 : else {
87 0 : if(!(o2 == *o1)) {
88 0 : std::cerr << "ERROR reading relationship: \'" << name << "\' (read and wrote objects are different)" << std::endl;
89 : }
90 : }
91 : }
92 :
93 0 : std::cout << "TEST value of " << name << " relationship of object " << o << " is OK: read " << o2 << std::endl;
94 0 : }
95 :
96 0 : void check_objects(ConfigObject& o, const std::string& name, const std::vector<const ::ConfigObject*> o1)
97 : {
98 0 : std::vector<ConfigObject> o2;
99 :
100 0 : o.get(name, o2);
101 :
102 0 : if(o1.size() != o2.size()) {
103 0 : std::cerr << "ERROR reading relationship: \'" << name << "\' (read vector of different size)" << std::endl;
104 : }
105 : else {
106 0 : for(unsigned int i = 0; i < o1.size(); ++i) {
107 0 : if(!(*o1[i] == o2[i])) {
108 0 : std::cerr << "ERROR reading relationship: \'" << name << "\' (objects " << i << " are different)" << std::endl;
109 : }
110 : }
111 : }
112 :
113 0 : std::cout << "TEST values of " << name << " relationship of object " << o << " is OK: read ";
114 0 : for(unsigned int i = 0; i < o1.size(); ++i) {
115 0 : if(i != 0) std::cout << ", ";
116 0 : std::cout << o2[i];
117 : }
118 0 : std::cout << std::endl;
119 0 : }
120 :
121 0 : void check_file_path(ConfigObject& o, const std::string& file_name)
122 : {
123 0 : std::string value = o.contained_in();
124 :
125 0 : std::cout << "TEST object " << &o << " is contained in \'" << value << "\': ";
126 :
127 0 : if(value == file_name)
128 : {
129 0 : std::cout << "OK\n";
130 : }
131 : else
132 : {
133 0 : std::cout << "FAILED (expected \'" << file_name << "\')\n";
134 : }
135 0 : }
136 :
137 :
138 0 : void check_rename(ConfigObject& o, const std::string& name)
139 : {
140 0 : std::cout << "TEST object " << &o << " was renamed to \'" << name << "\': ";
141 :
142 0 : if(name == o.UID())
143 : {
144 0 : std::cout << "OK\n";
145 : }
146 : else
147 : {
148 0 : std::cout << "FAILED\n";
149 : }
150 0 : }
151 :
152 :
153 : #define INIT(T, X, V) \
154 : for(T v = X - 16; v <= X;) { \
155 : V.push_back(++v); \
156 : }
157 :
158 : ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
159 :
160 :
161 :
162 0 : int main(int argc, char *argv[])
163 : {
164 0 : const char * schema_name = 0;
165 0 : const char * data_name = 0;
166 0 : const char * plugin_name = 0;
167 :
168 0 : for(int i = 1; i < argc; i++) {
169 0 : const char * cp = argv[i];
170 :
171 0 : if(!strcmp(cp, "-h") || !strcmp(cp, "--help")) {
172 0 : usage();
173 0 : return 0;
174 : }
175 0 : else if(!strcmp(cp, "-d") || !strcmp(cp, "--data-name")) {
176 0 : if(++i == argc) { no_param(cp); } else { data_name = argv[i]; }
177 : }
178 0 : else if(!strcmp(cp, "-s") || !strcmp(cp, "--schema-name")) {
179 0 : if(++i == argc) { no_param(cp); } else { schema_name = argv[i]; }
180 : }
181 0 : else if(!strcmp(cp, "-p") || !strcmp(cp, "--plugin-spec")) {
182 0 : if(++i == argc) { no_param(cp); } else { plugin_name = argv[i]; }
183 : }
184 : else {
185 0 : std::ostringstream text;
186 0 : text << "unexpected parameter: \'" << cp << "\'; run command with --help to see valid command line options.";
187 0 : ers::fatal(conffwk_test_rw::BadCommandLine(ERS_HERE, text.str().c_str()));
188 0 : return (EXIT_FAILURE);
189 0 : }
190 : }
191 :
192 0 : if(!data_name) {
193 0 : ers::fatal(conffwk_test_rw::BadCommandLine(ERS_HERE, "no data filename given"));
194 0 : return (EXIT_FAILURE);
195 : }
196 :
197 0 : if(!schema_name) {
198 0 : ers::fatal(conffwk_test_rw::BadCommandLine(ERS_HERE, "no schema filename given"));
199 0 : return (EXIT_FAILURE);
200 : }
201 :
202 0 : if(!plugin_name) {
203 0 : ers::fatal(conffwk_test_rw::BadCommandLine(ERS_HERE, "no plugin specification given (oksconflibs, rdbconffwk:server-name)"));
204 0 : return (EXIT_FAILURE);
205 : }
206 :
207 0 : try {
208 0 : ::Configuration db(plugin_name);
209 :
210 0 : db.create(data_name, std::list<std::string>(1,schema_name));
211 :
212 0 : ConfigObject o1;
213 0 : db.create(data_name, "Dummy", "#1", o1);
214 :
215 0 : ConfigObject o2;
216 0 : db.create(data_name, "Dummy", "#2", o2);
217 :
218 0 : ConfigObject o3;
219 0 : db.create(data_name, "Second", "#3", o3);
220 :
221 0 : ConfigObject o4;
222 0 : db.create(data_name, "Third", "#4", o4);
223 :
224 0 : ConfigObject o5;
225 0 : db.create(data_name, "Third", "#5", o5);
226 :
227 0 : ConfigObject o6;
228 0 : db.create(data_name, "Third", "#6", o6);
229 :
230 :
231 : // put objects into implementation cache
232 :
233 0 : check_value(o1, "bool", false);
234 0 : check_value(o2, "bool", false);
235 0 : check_value(o3, "bool", false);
236 0 : check_value(o4, "bool", false);
237 0 : check_value(o5, "bool", false);
238 0 : check_value(o6, "bool", false);
239 :
240 :
241 0 : bool bool_value (true);
242 0 : int8_t int8_value (0x7F);
243 0 : uint8_t uint8_value (0xFF);
244 0 : int16_t int16_value (0x7FFF);
245 0 : uint16_t uint16_value (0xFFFF);
246 0 : int32_t int32_value (0x7FFFFFFF);
247 0 : int32_t uint32_value (0xFFFFFFFF);
248 0 : int64_t int64_value ((uint64_t)(-1)/2-1);
249 0 : uint64_t uint64_value ((uint64_t)(-1));
250 0 : float float_value (123.456);
251 0 : double double_value (1234567890.123456);
252 0 : std::string string_value ("This is a test string.");
253 0 : std::string enum_value ("FIRST");
254 0 : std::string class_value ("Third");
255 :
256 0 : std::vector<bool> bool_values; bool_values.push_back(true); bool_values.push_back(false); bool_values.push_back(true);
257 0 : std::vector<int8_t> int8_values; INIT(int8_t, 0x7E, int8_values);
258 0 : std::vector<uint8_t> uint8_values; INIT(uint8_t, 0xFE, uint8_values);
259 0 : std::vector<int16_t> int16_values; INIT(int16_t, 0x7FFE, int16_values);
260 0 : std::vector<uint16_t> uint16_values; INIT(uint16_t, 0xFFFE, uint16_values);
261 0 : std::vector<int32_t> int32_values; INIT(int32_t, 0x7FFFFFFE, int32_values);
262 0 : std::vector<int32_t> uint32_values; INIT(uint32_t, 0xFFFFFFFE, uint32_values);
263 0 : std::vector<int64_t> int64_values; INIT(int64_t, (std::numeric_limits<int64_t>::max()-2), int64_values);
264 0 : std::vector<uint64_t> uint64_values; INIT(uint64_t, (std::numeric_limits<uint64_t>::max()-1), uint64_values);
265 0 : std::vector<float> float_values; INIT(float, 123.456, float_values);
266 0 : std::vector<double> double_values; INIT(double, 1234567890.123456, double_values);
267 0 : std::vector<std::string> strings_values; strings_values.push_back("test20"); strings_values.push_back("test30"); strings_values.push_back("test10");
268 0 : std::vector<std::string> enum_values; enum_values.push_back("THIRD"); enum_values.push_back("SECOND"); enum_values.push_back("FIRST");
269 0 : std::vector<std::string> class_values; class_values.push_back("Dummy"); class_values.push_back("Second"); class_values.push_back("Third");
270 :
271 0 : set_ref(o1, "bool_vector", bool_values);
272 0 : set_ref(o1, "sint8_vector", int8_values);
273 0 : set_ref(o1, "uint8_vector", uint8_values);
274 0 : set_ref(o1, "sint16_vector", int16_values);
275 0 : set_ref(o1, "uint16_vector", uint16_values);
276 0 : set_ref(o1, "sint32_vector", int32_values);
277 0 : set_ref(o1, "uint32_vector", int32_values);
278 0 : set_ref(o1, "sint64_vector", int64_values);
279 0 : set_ref(o1, "uint64_vector", uint64_values);
280 0 : set_ref(o1, "float_vector", float_values);
281 0 : set_ref(o1, "double_vector", double_values);
282 0 : set_ref(o1, "string_vector", strings_values);
283 0 : o1.set_enum("enum_vector", enum_values);
284 0 : o1.set_class("classref_vector", class_values);
285 :
286 0 : set_value(o1, "bool", bool_value);
287 0 : set_value(o1, "sint8", int8_value);
288 0 : set_value(o1, "uint8", uint8_value);
289 0 : set_value(o1, "sint16", int16_value);
290 0 : set_value(o1, "uint16", uint16_value);
291 0 : set_value(o1, "sint32", int32_value);
292 0 : set_value(o1, "uint32", uint32_value);
293 0 : set_value(o1, "sint64", int64_value);
294 0 : set_value(o1, "uint64", uint64_value);
295 0 : set_value(o1, "float", float_value);
296 0 : set_value(o1, "double", double_value);
297 0 : set_ref(o1, "string", string_value);
298 0 : o1.set_enum("enum", enum_value);
299 0 : o1.set_class("classref", class_value);
300 :
301 0 : std::vector<const ::ConfigObject*> vec4; vec4.push_back(&o1); vec4.push_back(&o2);
302 0 : std::vector<const ::ConfigObject*> vec5; vec5.push_back(&o3); vec5.push_back(&o4);
303 0 : std::vector<const ::ConfigObject*> vec6; vec6.push_back(&o3);
304 :
305 0 : o3.set_objs("Dummy", vec4);
306 0 : o3.set_obj("Another", &o1);
307 :
308 0 : o4.set_objs("Dummy", vec4);
309 0 : o4.set_obj("Another", &o2);
310 0 : o4.set_obj("Single", &o6);
311 :
312 0 : o5.set_objs("Dummy", vec4);
313 0 : o5.set_obj("Another", &o3);
314 0 : o5.set_obj("Single", 0);
315 0 : o5.set_objs("Seconds", vec5);
316 :
317 0 : o6.set_objs("Dummy", vec4);
318 0 : o6.set_obj("Another", &o3);
319 0 : o6.set_obj("Single", 0);
320 0 : o6.set_objs("Seconds", vec6);
321 :
322 0 : check_value(o1, "bool", bool_value);
323 0 : check_value(o1, "sint8", int8_value);
324 0 : check_value(o1, "uint8", uint8_value);
325 0 : check_value(o1, "sint16", int16_value);
326 0 : check_value(o1, "uint16", uint16_value);
327 0 : check_value(o1, "sint32", int32_value);
328 0 : check_value(o1, "uint32", uint32_value);
329 0 : check_value(o1, "sint64", int64_value);
330 0 : check_value(o1, "uint64", uint64_value);
331 0 : check_value(o1, "float", float_value);
332 0 : check_value(o1, "double", double_value);
333 0 : check_value(o1, "string", string_value);
334 0 : check_value(o1, "enum", enum_value);
335 0 : check_value(o1, "classref", class_value);
336 0 : check_value(o1, "bool_vector", bool_values);
337 0 : check_value(o1, "sint8_vector", int8_values);
338 0 : check_value(o1, "uint8_vector", uint8_values);
339 0 : check_value(o1, "sint16_vector", int16_values);
340 0 : check_value(o1, "uint16_vector", uint16_values);
341 0 : check_value(o1, "sint32_vector", int32_values);
342 0 : check_value(o1, "uint32_vector", int32_values);
343 0 : check_value(o1, "sint64_vector", int64_values);
344 0 : check_value(o1, "uint64_vector", uint64_values);
345 0 : check_value(o1, "float_vector", float_values);
346 0 : check_value(o1, "double_vector", double_values);
347 0 : check_value(o1, "string_vector", strings_values);
348 0 : check_value(o1, "enum_vector", enum_values);
349 0 : check_value(o1, "classref_vector", class_values);
350 :
351 0 : check_objects(o3, "Dummy", vec4);
352 0 : check_object(o3, "Another", &o1);
353 0 : check_objects(o4, "Dummy", vec4);
354 0 : check_object(o4, "Another", &o2);
355 0 : check_object(o4, "Single", &o6);
356 0 : check_objects(o5, "Seconds", vec5);
357 0 : check_objects(o6, "Dummy", vec4);
358 0 : check_object(o6, "Another", &o3);
359 0 : check_object(o6, "Single", 0);
360 0 : check_objects(o6, "Seconds", vec6);
361 :
362 0 : check_file_path(o1, data_name);
363 0 : check_file_path(o3, data_name);
364 0 : check_file_path(o4, data_name);
365 :
366 0 : {
367 0 : std::list<std::string> modified;
368 :
369 0 : db.get_updated_dbs(modified);
370 :
371 0 : std::cout << "There are updated " << modified.size() << " files:\n";
372 0 : for(std::list<std::string>::const_iterator i = modified.begin(); i != modified.end(); ++i) {
373 0 : std::cout << " * \"" << *i << "\"" << std::endl;
374 : }
375 0 : }
376 :
377 0 : db.commit("test application (conffwk/test/conffwk_test_rw.cpp): create first data");
378 :
379 :
380 : // create file names for 2 intermediate and 4 leave files;
381 : // file $F1 will include $F11 and $F12
382 : // file $F2 will include $F21, $F22 and $F12
383 : // existing file $data_name will include $F1 and $F2
384 :
385 0 : std::string f11(data_name); f11 += ".1.1";
386 0 : std::string f12(data_name); f12 += ".1.2";
387 0 : std::string f21(data_name); f21 += ".2.1";
388 0 : std::string f22(data_name); f22 += ".2.2";
389 :
390 0 : db.create(f11, std::list<std::string>(1,schema_name));
391 0 : db.create(f12, std::list<std::string>(1,schema_name));
392 0 : db.create(f21, std::list<std::string>(1,schema_name));
393 0 : db.create(f22, std::list<std::string>(1,schema_name));
394 :
395 0 : std::string f1(data_name); f1 += ".1";
396 0 : std::string f2(data_name); f2 += ".2";
397 :
398 0 : {
399 0 : std::list<std::string> includes(1,schema_name); includes.push_back(f11); includes.push_back(f12);
400 0 : db.create(f1, includes);
401 0 : }
402 :
403 0 : {
404 0 : std::list<std::string> includes(1,schema_name); includes.push_back(f21); includes.push_back(f22); includes.push_back(f12);
405 0 : db.create(f2, includes);
406 0 : }
407 :
408 0 : struct {
409 : const std::string * file;
410 : const char * id;
411 0 : } data [12] = {
412 : {&f1, "f1-1"},
413 : {&f1, "f1-2"},
414 : {&f2, "f2-1"},
415 : {&f2, "f2-2"},
416 : {&f11, "f11-1"},
417 : {&f11, "f11-2"},
418 : {&f12, "f12-1"},
419 : {&f12, "f12-2"},
420 : {&f21, "f21-1"},
421 : {&f21, "f21-2"},
422 : {&f22, "f22-1"},
423 : {&f22, "f22-2"}
424 0 : };
425 :
426 0 : for(int i = 0; i < 12; ++i) {
427 0 : ConfigObject o;
428 0 : db.create(*data[i].file, "Dummy", data[i].id, o);
429 0 : check_file_path(o, *data[i].file);
430 0 : }
431 :
432 0 : db.add_include(data_name, f1);
433 0 : db.add_include(data_name, f2);
434 :
435 0 : db.commit("test application (conffwk/test/conffwk_test_rw.cpp): create 6 nested files");
436 :
437 0 : std::cout << "\n\nTEST VALIDITY OF OBJECTS AFTER REMOVAL OF INCLUDES: Removing include \"" << f1 << "\"\n\n";
438 :
439 0 : db.remove_include(data_name, f1);
440 :
441 0 : const char * removed_objects_by_include[] = {"f1-1", "f1-2", "f11-1", "f11-2"}; // these objects have to be removed (note, f12 is still included by f2)
442 :
443 0 : for(int i = 0; i < 12; ++i) {
444 0 : std::cout << "TEST object " << data[i].id << " existence after removal of includes: ";
445 0 : bool state(false);
446 0 : try {
447 0 : ConfigObject o;
448 0 : db.get("Dummy", data[i].id, o);
449 0 : for(int j = 0; j < 4; ++j) {
450 0 : if(!strcmp(removed_objects_by_include[j], data[i].id)) {
451 0 : std::cout << "FAILED, object was not removed"; state = true; break;
452 : }
453 : }
454 0 : if(state == false) {
455 0 : std::cout << "OK, object was not removed";
456 : }
457 0 : }
458 0 : catch(dunedaq::conffwk::NotFound& ex) {
459 0 : for(int j = 0; j < 4; ++j) {
460 0 : if(!strcmp(removed_objects_by_include[j], data[i].id)) {
461 0 : std::cout << "OK, object was removed"; state = true; break;
462 : }
463 : }
464 0 : if(state == false) {
465 0 : std::cout << "FAILED, object was removed";
466 : }
467 0 : }
468 0 : std::cout << std::endl;
469 : }
470 :
471 0 : std::cout << "\n\nTEST VALIDITY OF OBJECTS AFTER REMOVAL OF COMPOSITE PARENT: Destroying object \"" << o5 << "\"\n\n";
472 :
473 0 : const char * existing_objects[] = {"#1", "#2", "#3", "#4", "#5", "#6" };
474 0 : const char * removed_objects_by_composite_parent[] = {"#4", "#5"}; // these objects have to be removed (note, #3 is still referenced by #6
475 :
476 0 : db.destroy_obj(o5);
477 :
478 0 : std::cout << "TEST deleted object " << o5.UID() << " existence: " << (o5.is_deleted() ? "OK (is_deleted returns TRUE)" : "FAILED (is_deleted returns FALSE)") << std::endl;
479 :
480 0 : check_file_path(o1, data_name);
481 0 : check_file_path(o3, data_name);
482 0 : check_file_path(o6, data_name);
483 :
484 0 : for(int i = 0; i < 6; ++i) {
485 0 : std::cout << "TEST object " << existing_objects[i] << " existence: ";
486 :
487 0 : bool state(false);
488 0 : try {
489 0 : ConfigObject o;
490 0 : db.get("Dummy", existing_objects[i], o);
491 0 : for(int j = 0; j < 2; ++j) {
492 0 : if(!strcmp(removed_objects_by_composite_parent[j], existing_objects[i])) {
493 0 : std::cout << "FAILED, object was not removed"; state = true; break;
494 : }
495 : }
496 0 : if(state == false) {
497 0 : std::cout << "OK, object was not removed";
498 : }
499 0 : }
500 0 : catch(dunedaq::conffwk::NotFound& ex) {
501 0 : for(int j = 0; j < 2; ++j) {
502 0 : if(!strcmp(removed_objects_by_composite_parent[j], existing_objects[i])) {
503 0 : std::cout << "OK, object was removed"; state = true; break;
504 : }
505 : }
506 0 : if(state == false) {
507 0 : std::cout << "FAILED, object was removed";
508 : }
509 0 : }
510 0 : std::cout << std::endl;
511 : }
512 :
513 :
514 0 : std::cout << "\n\nTEST INCLUDES\n\n";
515 :
516 0 : std::list<std::string> includes;
517 0 : db.get_includes(data_name, includes);
518 :
519 0 : std::cout << "* file \"" << data_name << "\" includes " << includes.size() << " files:\n";
520 0 : for (auto& x : includes)
521 0 : std::cout << " - " << x << std::endl;
522 :
523 0 : std::cout << "test " << ((includes.size() == 2) ? "PASSED" : "FAILED") << std::endl;
524 :
525 0 : includes.clear();
526 0 : db.get_includes("", includes);
527 :
528 0 : std::cout << "* there is " << includes.size() << " top-level files:\n";
529 0 : for (auto& x : includes)
530 0 : std::cout << " - " << x << std::endl;
531 :
532 0 : std::cout << "test " << ((includes.size() == 1) ? "PASSED" : "FAILED") << std::endl;
533 :
534 :
535 0 : std::cout << "\n\nTEST RENAME\n\n";
536 :
537 0 : o1.rename("#new1");
538 0 : check_rename(o1, "#new1");
539 :
540 : // rename existing object to deleted
541 :
542 0 : std::cout << "TEST deleted object " << o4.UID() << " existence: " << (o4.is_deleted() ? "OK" : "FAILED") << std::endl;
543 0 : const std::string deleted_name(o4.UID());
544 0 : o6.rename(deleted_name);
545 0 : check_rename(o6, deleted_name);
546 0 : std::cout << "TEST deleted object " << deleted_name << " after renamed existing object to it's ID: " << (!o4.is_deleted() ? "OK" : "FAILED") << std::endl;
547 0 : check_rename(o4, deleted_name);
548 :
549 0 : return 0;
550 0 : }
551 0 : catch (dunedaq::conffwk::Exception & ex) {
552 0 : ers::fatal(conffwk_test_rw::ConfigException(ERS_HERE, ex));
553 0 : }
554 :
555 0 : return (EXIT_FAILURE);
556 : }
|