00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035
00036 #include <string.h>
00037 #include <stdlib.h>
00038 #include <assert.h>
00039 #include <cpl.h>
00040
00041
00042 #include "irplib_plugin.h"
00043 #include "irplib_utils.h"
00044 #include "irplib_error.h"
00045
00046
00056
00057
00058
00059
00060
00061
00062
00063 #ifndef LINE_LEN_MAX
00064 #define LINE_LEN_MAX 1024
00065 #endif
00066
00067
00068 #define DEV_RANDOM "/dev/urandom"
00069
00070
00071 #define recipe_assert(bool) \
00072 ((bool) ? (cpl_msg_debug(cpl_func, \
00073 "OK in " __FILE__ " line %d (CPL-error state: '%s' in %s): %s",__LINE__, \
00074 cpl_error_get_message(), cpl_error_get_where(), #bool), 0) \
00075 : (cpl_msg_error(cpl_func, \
00076 "Failure in " __FILE__ " line %d (CPL-error state: '%s' in %s): %s", \
00077 __LINE__, cpl_error_get_message(), cpl_error_get_where(), #bool), 1))
00078
00079
00080
00081
00082
00083
00084
00085 static const cpl_parameter * irplib_parameterlist_get(const cpl_parameterlist *,
00086 const char *,
00087 const char *,
00088 const char *);
00089
00090 static void recipe_msg_set_level_from_env(void);
00091 static void recipe_parameterlist_set_defaults(cpl_parameterlist *);
00092 static void recipe_frameset_load(cpl_frameset *, const char *);
00093
00094 static int recipe_sof_test_random(cpl_plugin *, size_t, const char *[]);
00095 static int recipe_sof_test_from_env(cpl_plugin *);
00096
00099
00100
00101
00102
00103
00113
00114 const char * irplib_parameterlist_get_string(const cpl_parameterlist * self,
00115 const char * instrume,
00116 const char * recipe,
00117 const char * parameter)
00118 {
00119
00120 const cpl_parameter * par = irplib_parameterlist_get(self, instrume,
00121 recipe, parameter);
00122 const char * value;
00123
00124 cpl_ensure(par != NULL, cpl_error_get_code(), NULL);
00125
00126 cpl_ensure(cpl_parameter_get_type(par) == CPL_TYPE_STRING,
00127 CPL_ERROR_TYPE_MISMATCH, NULL);
00128
00129 value = cpl_parameter_get_string(par);
00130
00131 cpl_ensure(value != NULL, cpl_error_get_code(), NULL);
00132
00133 return value;
00134
00135 }
00136
00137
00147
00148 cpl_boolean irplib_parameterlist_get_bool(const cpl_parameterlist * self,
00149 const char * instrume,
00150 const char * recipe,
00151 const char * parameter)
00152 {
00153
00154 const cpl_parameter * par = irplib_parameterlist_get(self, instrume,
00155 recipe, parameter);
00156 const cpl_error_code prestate = cpl_error_get_code();
00157 cpl_boolean value;
00158
00159 cpl_ensure(par != NULL, cpl_error_get_code(), CPL_FALSE);
00160
00161 cpl_ensure(cpl_parameter_get_type(par) == CPL_TYPE_BOOL,
00162 CPL_ERROR_TYPE_MISMATCH, CPL_FALSE);
00163
00164 value = cpl_parameter_get_bool(par);
00165
00166
00167 if (cpl_error_get_code() != prestate) cpl_error_set_where(cpl_func);
00168
00169 return value;
00170
00171 }
00172
00173
00174
00184
00185 int irplib_parameterlist_get_int(const cpl_parameterlist * self,
00186 const char * instrume,
00187 const char * recipe,
00188 const char * parameter)
00189 {
00190
00191 const cpl_parameter * par = irplib_parameterlist_get(self, instrume,
00192 recipe, parameter);
00193 const cpl_error_code prestate = cpl_error_get_code();
00194 int value;
00195
00196 cpl_ensure(par != NULL, cpl_error_get_code(), 0);
00197
00198 cpl_ensure(cpl_parameter_get_type(par) == CPL_TYPE_INT,
00199 CPL_ERROR_TYPE_MISMATCH, 0);
00200
00201 value = cpl_parameter_get_int(par);
00202
00203
00204 if (cpl_error_get_code() != prestate) cpl_error_set_where(cpl_func);
00205
00206 return value;
00207 }
00208
00209
00219
00220 double irplib_parameterlist_get_double(const cpl_parameterlist * self,
00221 const char * instrume,
00222 const char * recipe,
00223 const char * parameter)
00224 {
00225
00226 const cpl_parameter * par = irplib_parameterlist_get(self, instrume,
00227 recipe, parameter);
00228 const cpl_error_code prestate = cpl_error_get_code();
00229 double value;
00230
00231 cpl_ensure(par != NULL, cpl_error_get_code(), 0.0);
00232
00233 cpl_ensure(cpl_parameter_get_type(par) == CPL_TYPE_DOUBLE,
00234 CPL_ERROR_TYPE_MISMATCH, 0.0);
00235
00236 value = cpl_parameter_get_double(par);
00237
00238
00239 if (cpl_error_get_code() != prestate) cpl_error_set_where(cpl_func);
00240
00241 return value;
00242 }
00243
00244
00258
00259 cpl_error_code irplib_parameterlist_set_string(cpl_parameterlist * self,
00260 const char * instrume,
00261 const char * recipe,
00262 const char * parameter,
00263 const char * defvalue,
00264 const char * alias,
00265 const char * context,
00266 const char * man)
00267 {
00268
00269 cpl_error_code error;
00270 cpl_parameter * par;
00271 char * paramname = irplib_sprintf("%s.%s.%s",
00272 instrume, recipe,
00273 parameter);
00274
00275 cpl_ensure_code(paramname != NULL, cpl_error_get_code());
00276
00277 par = cpl_parameter_new_value(paramname, CPL_TYPE_STRING, man, context,
00278 defvalue);
00279 cpl_free(paramname);
00280
00281 cpl_ensure_code(par != NULL, cpl_error_get_code());
00282
00283 error = cpl_parameter_set_alias(par, CPL_PARAMETER_MODE_CLI, alias);
00284 cpl_ensure_code(!error, error);
00285
00286 error = cpl_parameter_disable(par, CPL_PARAMETER_MODE_ENV);
00287 cpl_ensure_code(!error, error);
00288
00289 error = cpl_parameterlist_append(self, par);
00290 cpl_ensure_code(!error, error);
00291
00292 return CPL_ERROR_NONE;
00293 }
00294
00295
00296
00310
00311 cpl_error_code irplib_parameterlist_set_bool(cpl_parameterlist * self,
00312 const char * instrume,
00313 const char * recipe,
00314 const char * parameter,
00315 cpl_boolean defvalue,
00316 const char * alias,
00317 const char * context,
00318 const char * man)
00319 {
00320
00321 cpl_error_code error;
00322 cpl_parameter * par;
00323 char * paramname = irplib_sprintf("%s.%s.%s",
00324 instrume, recipe,
00325 parameter);
00326
00327 cpl_ensure_code(paramname != NULL, cpl_error_get_code());
00328
00329 par = cpl_parameter_new_value(paramname, CPL_TYPE_BOOL, man, context,
00330 defvalue);
00331 cpl_free(paramname);
00332
00333 cpl_ensure_code(par != NULL, cpl_error_get_code());
00334
00335 error = cpl_parameter_set_alias(par, CPL_PARAMETER_MODE_CLI, alias);
00336 cpl_ensure_code(!error, error);
00337
00338 error = cpl_parameter_disable(par, CPL_PARAMETER_MODE_ENV);
00339 cpl_ensure_code(!error, error);
00340
00341 error = cpl_parameterlist_append(self, par);
00342 cpl_ensure_code(!error, error);
00343
00344 return CPL_ERROR_NONE;
00345 }
00346
00347
00348
00349
00363
00364 cpl_error_code irplib_parameterlist_set_int(cpl_parameterlist * self,
00365 const char * instrume,
00366 const char * recipe,
00367 const char * parameter,
00368 int defvalue,
00369 const char * alias,
00370 const char * context,
00371 const char * man)
00372 {
00373
00374 cpl_error_code error;
00375 cpl_parameter * par;
00376 char * paramname = irplib_sprintf("%s.%s.%s",
00377 instrume, recipe,
00378 parameter);
00379
00380 cpl_ensure_code(paramname != NULL, cpl_error_get_code());
00381
00382 par = cpl_parameter_new_value(paramname, CPL_TYPE_INT, man, context,
00383 defvalue);
00384 cpl_free(paramname);
00385
00386 cpl_ensure_code(par != NULL, cpl_error_get_code());
00387
00388 error = cpl_parameter_set_alias(par, CPL_PARAMETER_MODE_CLI, alias);
00389 cpl_ensure_code(!error, error);
00390
00391 error = cpl_parameter_disable(par, CPL_PARAMETER_MODE_ENV);
00392 cpl_ensure_code(!error, error);
00393
00394 error = cpl_parameterlist_append(self, par);
00395 cpl_ensure_code(!error, error);
00396
00397 return CPL_ERROR_NONE;
00398 }
00399
00400
00401
00415
00416 cpl_error_code irplib_parameterlist_set_double(cpl_parameterlist * self,
00417 const char * instrume,
00418 const char * recipe,
00419 const char * parameter,
00420 double defvalue,
00421 const char * alias,
00422 const char * context,
00423 const char * man)
00424 {
00425
00426 cpl_error_code error;
00427 cpl_parameter * par;
00428 char * paramname = irplib_sprintf("%s.%s.%s",
00429 instrume, recipe,
00430 parameter);
00431
00432 cpl_ensure_code(paramname != NULL, cpl_error_get_code());
00433
00434 par = cpl_parameter_new_value(paramname, CPL_TYPE_DOUBLE, man, context,
00435 defvalue);
00436 cpl_free(paramname);
00437
00438 cpl_ensure_code(par != NULL, cpl_error_get_code());
00439
00440 error = cpl_parameter_set_alias(par, CPL_PARAMETER_MODE_CLI, alias);
00441 cpl_ensure_code(!error, error);
00442
00443 error = cpl_parameter_disable(par, CPL_PARAMETER_MODE_ENV);
00444 cpl_ensure_code(!error, error);
00445
00446 error = cpl_parameterlist_append(self, par);
00447 cpl_ensure_code(!error, error);
00448
00449 return CPL_ERROR_NONE;
00450 }
00451
00452
00453
00465
00466 int irplib_plugin_test(cpl_pluginlist * self, size_t nstr, const char *astr[]) {
00467
00468 cpl_plugin * plugin;
00469 cpl_recipe * recipe;
00470 int (*recipe_create) (cpl_plugin *);
00471 int (*recipe_exec ) (cpl_plugin *);
00472 int (*recipe_deinit) (cpl_plugin *);
00473 const cpl_msg_severity msg_level = cpl_msg_get_level();
00474
00475 int recipe_nfail = 0;
00476 cpl_boolean is_debug;
00477
00478
00479 cpl_msg_set_level(CPL_MSG_OFF);
00480 recipe_msg_set_level_from_env();
00481
00482 is_debug = cpl_msg_get_level() <= CPL_MSG_DEBUG ? CPL_TRUE : CPL_FALSE;
00483
00484 assert( nstr == 0 || astr != NULL );
00485
00486 plugin = cpl_pluginlist_get_first(self);
00487
00488 if (plugin == NULL) {
00489 cpl_msg_warning(cpl_func, "With an empty pluginlist, "
00490 "no tests can be made");
00491 cpl_msg_set_level(msg_level);
00492 return 0;
00493 }
00494
00495 if (is_debug) cpl_plugin_dump(plugin, stdout);
00496
00497 recipe_create = cpl_plugin_get_init(plugin);
00498 recipe_exec = cpl_plugin_get_exec(plugin);
00499 recipe_deinit = cpl_plugin_get_deinit(plugin);
00500
00501
00502 if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {
00503 cpl_msg_warning(cpl_func, "This plugin is not of type recipe, "
00504 "cannot test further");
00505 cpl_msg_set_level(msg_level);
00506 return 0;
00507 }
00508
00509 recipe_nfail += recipe_assert( recipe_create(plugin) == 0);
00510
00511 recipe = (cpl_recipe *) plugin;
00512
00513 recipe_nfail += recipe_assert( recipe->parameters != NULL);
00514
00515 recipe_parameterlist_set_defaults(recipe->parameters);
00516
00517 if (is_debug) cpl_parameterlist_dump(recipe->parameters, stdout);
00518
00519 recipe->frames = cpl_frameset_new();
00520
00521 cpl_msg_info(cpl_func,"Checking handling of pre-existing CPL error state - "
00522 "may produce warning(s)/error(s):");
00523 cpl_error_set(cpl_func, CPL_ERROR_UNSPECIFIED);
00524
00525 recipe_nfail += recipe_assert( recipe_exec(plugin) != 0 );
00526
00527 recipe_nfail += recipe_assert( cpl_error_get_code() == CPL_ERROR_UNSPECIFIED );
00528
00529 irplib_error_dump(CPL_MSG_WARNING, CPL_MSG_DEBUG);
00530 cpl_msg_debug(cpl_func, "Resetting error state");
00531 irplib_error_reset();
00532
00533 cpl_msg_info(cpl_func,"Checking handling of empty frameset - "
00534 "may produce warning(s)/error(s):");
00535
00536 recipe_nfail += recipe_assert( recipe_exec(plugin) != 0 );
00537
00538 recipe_nfail += recipe_assert( cpl_error_get_code() != CPL_ERROR_NONE );
00539
00540 irplib_error_dump(CPL_MSG_WARNING, CPL_MSG_DEBUG);
00541 cpl_msg_debug(cpl_func, "Resetting error state");
00542 irplib_error_reset();
00543
00544 cpl_msg_info(cpl_func,"Checking handling of dummy frameset - "
00545 "may produce warning(s)/error(s):");
00546 do {
00547 cpl_frame * f = cpl_frame_new();
00548 recipe_nfail += recipe_assert( cpl_frame_set_filename(f, "/dev/null") == 0);
00549 recipe_nfail += recipe_assert( cpl_frame_set_tag(f, "RECIPE_DUMMY_TAG") == 0);
00550 recipe_nfail += recipe_assert( cpl_frameset_insert(recipe->frames, f) == 0);
00551
00552
00553 recipe_nfail += recipe_assert( recipe_exec(plugin) != 0 );
00554
00555 recipe_nfail += recipe_assert( cpl_error_get_code() != CPL_ERROR_NONE );
00556
00557 irplib_error_dump(CPL_MSG_WARNING, CPL_MSG_DEBUG);
00558 cpl_msg_debug(cpl_func, "Resetting error state");
00559 irplib_error_reset();
00560
00561 recipe_nfail += recipe_assert( cpl_frameset_erase_frame(recipe->frames, f)
00562 == CPL_ERROR_NONE );
00563
00564 } while (0);
00565
00566 recipe_nfail += recipe_sof_test_random(plugin, nstr, astr);
00567
00568 recipe_nfail += recipe_sof_test_from_env(plugin);
00569
00570 cpl_frameset_delete(recipe->frames);
00571
00572 recipe_nfail += recipe_assert( recipe_deinit(plugin) == 0 );
00573
00574 recipe_nfail += recipe_assert( cpl_error_get_code() == CPL_ERROR_NONE );
00575
00576 if (recipe_nfail) cpl_msg_error(cpl_func, "%d test(s) failed", recipe_nfail);
00577
00578 cpl_msg_set_level(msg_level);
00579 return recipe_nfail;
00580 }
00581
00584
00601
00602 static void recipe_msg_set_level_from_env(void)
00603 {
00604
00605 const char * level = getenv("RECIPE_MSG_LEVEL");
00606
00607 if (level == NULL) return;
00608
00609 if (!strcmp(level, "debug"))
00610 cpl_msg_set_level(CPL_MSG_DEBUG);
00611 else if (!strcmp(level, "info"))
00612 cpl_msg_set_level(CPL_MSG_INFO);
00613 else if (!strcmp(level, "warning"))
00614 cpl_msg_set_level(CPL_MSG_WARNING);
00615 else if (!strcmp(level, "error"))
00616 cpl_msg_set_level(CPL_MSG_ERROR);
00617 else if (!strcmp(level, "off"))
00618 cpl_msg_set_level(CPL_MSG_OFF);
00619
00620 return;
00621
00622 }
00623
00624
00625
00626
00636
00637 static void recipe_parameterlist_set_defaults(cpl_parameterlist *parlist)
00638 {
00639
00640 cpl_parameter * p = cpl_parameterlist_get_first(parlist);
00641
00642 for (; p != NULL; p = cpl_parameterlist_get_next(parlist)) {
00643
00644 if (cpl_parameter_get_default_flag(p)) continue;
00645
00646 cpl_msg_debug(cpl_func, __FILE__ " line %u: OK", __LINE__);
00647
00648 switch (cpl_parameter_get_type(p)) {
00649 case CPL_TYPE_BOOL:
00650 cpl_parameter_set_bool(p, cpl_parameter_get_default_bool(p));
00651 break;
00652 case CPL_TYPE_INT:
00653 cpl_parameter_set_int(p, cpl_parameter_get_default_int(p));
00654 break;
00655 case CPL_TYPE_DOUBLE:
00656 cpl_parameter_set_double(p, cpl_parameter_get_default_double(p));
00657 break;
00658 case CPL_TYPE_STRING:
00659 {
00660 const char * s_default = cpl_parameter_get_default_string(p);
00661
00662 cpl_parameter_set_string(p, s_default != NULL ? s_default : "");
00663 break;
00664 }
00665
00666 default:
00667 assert( 0 );
00668 }
00669 }
00670 }
00671
00672
00673
00680
00681 static int recipe_sof_test_random(cpl_plugin * plugin, size_t nstr,
00682 const char *astr[])
00683 {
00684 cpl_recipe * recipe = (cpl_recipe*)plugin;
00685
00686 int (*recipe_exec) (cpl_plugin *);
00687
00688
00689 int recipe_nfail = 0;
00690 size_t i;
00691
00692
00693 if (nstr < 1) return recipe_nfail;
00694
00695 cpl_msg_info(cpl_func, "Testing recipe with %u " DEV_RANDOM " as input ",
00696 (unsigned)nstr);
00697
00698 for (i = 0; i < nstr; i++) {
00699 cpl_frame * f = cpl_frame_new();
00700
00701 recipe_nfail += recipe_assert( cpl_frame_set_filename(f, DEV_RANDOM) == 0);
00702 recipe_nfail += recipe_assert( cpl_frame_set_tag(f, astr[i]) == 0);
00703 recipe_nfail += recipe_assert( cpl_frameset_insert(recipe->frames, f) == 0);
00704 }
00705
00706 recipe_exec = cpl_plugin_get_exec(plugin);
00707
00708
00709 recipe_nfail += recipe_assert( recipe_exec(plugin) != 0 );
00710
00711 recipe_nfail += recipe_assert( cpl_error_get_code() != CPL_ERROR_NONE );
00712
00713 irplib_error_dump(CPL_MSG_WARNING, CPL_MSG_DEBUG);
00714 cpl_msg_debug(cpl_func, "Resetting error state");
00715 irplib_error_reset();
00716
00717 for (i = 0; i < nstr; i++) {
00718 cpl_frame * f = cpl_frameset_get_first(recipe->frames);
00719 recipe_nfail +=
00720 recipe_assert( cpl_frameset_erase_frame(recipe->frames, f)
00721 == CPL_ERROR_NONE );
00722 }
00723
00724 return recipe_nfail;
00725 }
00726
00727
00728
00735
00736 static int recipe_sof_test_from_env(cpl_plugin * plugin)
00737 {
00738 cpl_recipe * recipe = (cpl_recipe*)plugin;
00739 const char * recipename = cpl_plugin_get_name(plugin);
00740 const char * var_name = "RECIPE_SOF_PATH";
00741 const char * sof_path = getenv(var_name);
00742
00743
00744 int recipe_nfail = 0;
00745
00746 char * sof_name;
00747
00748 if (sof_path == NULL) {
00749 cpl_msg_warning(cpl_func, "Environment variable %s is unset: "
00750 "No SOFs to check", var_name);
00751 return recipe_nfail;
00752 }
00753
00754 cpl_msg_debug(cpl_func, "Checking for SOFs in %s", sof_path);
00755
00756 if (recipename == NULL) {
00757 recipe_nfail += recipe_assert(recipename != NULL);
00758 return recipe_nfail;
00759 }
00760
00761 sof_name = irplib_sprintf("%s/%s.sof", sof_path, recipename);
00762
00763 cpl_msg_debug(cpl_func, "Checking for SOF %s", sof_name);
00764
00765 recipe_frameset_load(recipe->frames, sof_name);
00766
00767 if (!cpl_frameset_is_empty(recipe->frames)) {
00768
00769 int (*recipe_exec ) (cpl_plugin *);
00770
00771 recipe_exec = cpl_plugin_get_exec(plugin);
00772
00773 cpl_msg_info(cpl_func,"Checking handling of SOF: %s", sof_name);
00774 recipe_nfail += recipe_assert( recipe_exec(plugin) == 0 );
00775 recipe_nfail += recipe_assert( cpl_error_get_code() == CPL_ERROR_NONE );
00776
00777 }
00778
00779 cpl_free(sof_name);
00780
00781 return recipe_nfail;
00782 }
00783
00784
00798
00799
00800 static void recipe_frameset_load(cpl_frameset * set, const char *name)
00801 {
00802
00803 FILE *fp;
00804 char line[LINE_LEN_MAX];
00805 char path[LINE_LEN_MAX], group[LINE_LEN_MAX], tag[LINE_LEN_MAX];
00806 int line_number;
00807
00808 assert( set != NULL );
00809 assert( name != NULL );
00810
00811 fp = fopen(name, "r");
00812 if (fp == NULL) {
00813 cpl_msg_debug(cpl_func, "Unable to open SOF file '%s'", name);
00814 return;
00815 }
00816
00817
00818 for (line_number = 0; fgets(line, LINE_LEN_MAX - 1, fp); line_number++) {
00819
00820 cpl_frame_group grp;
00821 cpl_frame * frame;
00822 int n;
00823
00824 if (line[0] == '#') continue;
00825
00826 n = sscanf(line, "%s %s %s", path, tag, group);
00827
00828 if (n < 1) {
00829 cpl_msg_warning(cpl_func, "Spurious line no. %d in %s: %s",
00830 line_number, name, line);
00831 break;
00832 }
00833
00834
00835 frame = cpl_frame_new();
00836
00837
00838 cpl_frame_set_filename(frame, path);
00839
00840
00841 cpl_frame_set_tag(frame, n == 1 ? "" : tag);
00842
00843 cpl_frameset_insert(set, frame);
00844
00845
00846 if (n < 3) continue;
00847
00848 if (!strcmp(group, CPL_FRAME_GROUP_RAW_ID))
00849 grp = CPL_FRAME_GROUP_RAW;
00850 else if (!strcmp(group, CPL_FRAME_GROUP_CALIB_ID))
00851 grp = CPL_FRAME_GROUP_CALIB;
00852 else if (!strcmp(group, CPL_FRAME_GROUP_PRODUCT_ID))
00853 grp = CPL_FRAME_GROUP_PRODUCT;
00854 else
00855 grp = CPL_FRAME_GROUP_NONE;
00856
00857 cpl_frame_set_group(frame, grp);
00858 }
00859
00860 fclose(fp);
00861
00862 return;
00863
00864 }
00865
00866
00867
00877
00878 static const cpl_parameter * irplib_parameterlist_get(const cpl_parameterlist
00879 * self,
00880 const char * instrume,
00881 const char * recipe,
00882 const char * parameter)
00883 {
00884
00885 const cpl_parameter * par;
00886 char * paramname;
00887
00888
00889 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
00890 cpl_ensure(instrume != NULL, CPL_ERROR_NULL_INPUT, NULL);
00891 cpl_ensure(recipe != NULL, CPL_ERROR_NULL_INPUT, NULL);
00892 cpl_ensure(parameter != NULL, CPL_ERROR_NULL_INPUT, NULL);
00893
00894 paramname = irplib_sprintf("%s.%s.%s", instrume, recipe,
00895 parameter);
00896
00897 cpl_ensure(paramname != NULL, cpl_error_get_code(), NULL);
00898
00899
00900 par = cpl_parameterlist_find((cpl_parameterlist*)self, paramname);
00901
00902 cpl_free(paramname);
00903
00904 return par;
00905
00906 }
00907