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
00033
00034 #define PROGRAM_NAME "3dbucket"
00035 #define LAST_MOD_DATE "30 October 2003"
00036
00037 #include "mrilib.h"
00038
00039 #ifndef myXtFree
00040 # define myXtFree(xp) (XtFree((char *)(xp)) , (xp)=NULL)
00041 #endif
00042
00043
00044
00045 static THD_3dim_dataset_array * BUCK_dsar = NULL ;
00046 static XtPointer_array * BUCK_subv = NULL ;
00047 static int BUCK_nvox = -1 ;
00048 static int BUCK_dry = 0 ;
00049 static int BUCK_verb = 0 ;
00050 static int BUCK_type = -1 ;
00051 static int BUCK_glue = 0 ;
00052
00053 static char BUCK_output_prefix[THD_MAX_PREFIX] = "buck" ;
00054 static char BUCK_session[THD_MAX_NAME] = "./" ;
00055
00056 #define NSUBV(id) ( ((int *)BUCK_subv->ar[(id)])[0] )
00057 #define SUBV(id,jj) ( ((int *)BUCK_subv->ar[(id)])[(jj)+1] )
00058 #define DSUB(id) DSET_IN_3DARR(BUCK_dsar,(id))
00059
00060
00061
00062 void BUCK_read_opts( int , char ** ) ;
00063 void BUCK_Syntax(void) ;
00064 int * BUCK_get_subv( int , char * ) ;
00065
00066
00067
00068
00069
00070 void BUCK_read_opts( int argc , char * argv[] )
00071 {
00072 int nopt = 1 , ii ;
00073 char dname[THD_MAX_NAME] ;
00074 char subv[THD_MAX_NAME] ;
00075 char * cpt ;
00076 THD_3dim_dataset * dset ;
00077 int * svar ;
00078 char * str;
00079 int ok, ilen, nlen;
00080
00081 INIT_3DARR(BUCK_dsar) ;
00082 INIT_XTARR(BUCK_subv) ;
00083
00084 while( nopt < argc ){
00085
00086
00087
00088 if( strncmp(argv[nopt],"-prefix",6) == 0 ||
00089 strncmp(argv[nopt],"-output",6) == 0 ){
00090 if (BUCK_glue){
00091 fprintf(stderr,"-prefix and -glueto options are not compatible\n");
00092 exit(1) ;
00093 }
00094 nopt++ ;
00095 if( nopt >= argc ){
00096 fprintf(stderr,"need argument after -prefix!\n") ; exit(1) ;
00097 }
00098 MCW_strncpy( BUCK_output_prefix , argv[nopt++] , THD_MAX_PREFIX ) ;
00099 continue ;
00100 }
00101
00102
00103
00104 if( strncmp(argv[nopt],"-session",6) == 0 ){
00105 if (BUCK_glue){
00106 fprintf(stderr,
00107 "-session and -glueto options are not compatible\n");
00108 exit(1) ;
00109 }
00110 nopt++ ;
00111 if( nopt >= argc ){
00112 fprintf(stderr,"need argument after -session!\n") ; exit(1) ;
00113 }
00114 MCW_strncpy( BUCK_session , argv[nopt++] , THD_MAX_NAME ) ;
00115 continue ;
00116 }
00117
00118 if( strncmp(argv[nopt],"-dry",3) == 0 ){
00119 BUCK_dry = BUCK_verb = 1 ;
00120 nopt++ ; continue ;
00121 }
00122
00123 if( strncmp(argv[nopt],"-fbuc",4) == 0 ){
00124 BUCK_type = HEAD_FUNC_TYPE ;
00125 nopt++ ; continue ;
00126 }
00127
00128 if( strncmp(argv[nopt],"-abuc",4) == 0 ){
00129 BUCK_type = HEAD_ANAT_TYPE ;
00130 nopt++ ; continue ;
00131 }
00132
00133 if( strncmp(argv[nopt],"-verb",5) == 0 ){
00134 BUCK_verb = 1 ;
00135 nopt++ ; continue ;
00136 }
00137
00138 if( strncmp(argv[nopt],"-glueto",5) == 0 ){
00139 if( strncmp(BUCK_output_prefix, "buck", 5) != 0 ){
00140 fprintf(stderr,"-prefix and -glueto options are not compatible\n");
00141 exit(1) ;
00142 }
00143 if( strncmp(BUCK_session, "./", 5) != 0 ){
00144 fprintf(stderr,
00145 "-session and -glueto options are not compatible\n");
00146 exit(1) ;
00147 }
00148 BUCK_glue = 1 ;
00149 nopt++ ;
00150 if( nopt >= argc ){
00151 fprintf(stderr,"need argument after -glueto!\n") ; exit(1) ;
00152 }
00153
00154
00155 ok = 1;
00156 nlen = strlen(argv[nopt]);
00157 if (nlen <= 5) ok = 0;
00158
00159 if (ok)
00160 {
00161 #if 0
00162
00163 for (ilen = 0; ilen < nlen; ilen++)
00164 {
00165 str = argv[nopt] + ilen;
00166 if (str[0] == '+') break;
00167 }
00168 if (ilen == nlen) ok = 0;
00169 #endif
00170
00171
00172
00173 for (ilen = nlen - 1; ilen > 0; ilen--)
00174 {
00175 str = argv[nopt] + ilen;
00176 if (str[0] == '+') break;
00177 }
00178 if (ilen == 0) ok = 0;
00179 }
00180
00181 if (ok)
00182 {
00183 str = argv[nopt] + ilen + 1;
00184
00185 for (ii=FIRST_VIEW_TYPE ; ii <= LAST_VIEW_TYPE ; ii++)
00186 if (! strncmp(str,VIEW_codestr[ii],4)) break ;
00187
00188 if( ii > LAST_VIEW_TYPE ) ok = 0;
00189 }
00190
00191 if (! ok)
00192 {
00193 fprintf(stderr,
00194 "File name must end in +orig, +acpc, or +tlrc after -glueto\n");
00195 exit(1);
00196 }
00197
00198
00199 MCW_strncpy( BUCK_output_prefix , argv[nopt] , ilen+1) ;
00200
00201
00202
00203 }
00204
00205 if( argv[nopt][0] == '-' ){
00206 fprintf(stderr,"Unknown option: %s\n",argv[nopt]) ; exit(1) ;
00207 }
00208
00209
00210
00211 cpt = strstr(argv[nopt],"[") ;
00212 if( cpt == NULL ){
00213 strcpy(dname,argv[nopt]) ;
00214 subv[0] = '\0' ;
00215 } else if( cpt == argv[nopt] ){
00216 fprintf(stderr,"illegal dataset specifier: %s\n",argv[nopt]) ;
00217 exit(1) ;
00218 } else {
00219 ii = cpt - argv[nopt] ;
00220 memcpy(dname,argv[nopt],ii) ; dname[ii] = '\0' ;
00221 strcpy(subv,cpt) ;
00222 }
00223 nopt++ ;
00224
00225 dset = THD_open_one_dataset( dname ) ;
00226 if( dset == NULL ){
00227 fprintf(stderr,"can't open dataset %s\n",dname) ; exit(1) ;
00228 }
00229 THD_force_malloc_type( dset->dblk , DATABLOCK_MEM_MALLOC ) ;
00230
00231 if( BUCK_type < 0 ) BUCK_type = dset->type ;
00232
00233 ii = dset->daxes->nxx * dset->daxes->nyy * dset->daxes->nzz ;
00234 if( BUCK_nvox < 0 ){
00235 BUCK_nvox = ii ;
00236 } else if( ii != BUCK_nvox ){
00237 fprintf(stderr,"dataset %s differs in size from others\n",dname);
00238 exit(1) ;
00239 }
00240 ADDTO_3DARR(BUCK_dsar,dset) ;
00241
00242 svar = BUCK_get_subv( DSET_NVALS(dset) , subv ) ;
00243 if( svar == NULL || svar[0] <= 0 ){
00244 fprintf(stderr,"can't decipher index codes from %s%s\n",dname,subv) ;
00245 exit(1) ;
00246 }
00247 ADDTO_XTARR(BUCK_subv,svar) ;
00248
00249 }
00250
00251 return ;
00252 }
00253
00254
00255
00256 int * BUCK_get_subv( int nvals , char * str )
00257 {
00258 int * subv = NULL ;
00259 int ii , ipos , nout , slen ;
00260 int ibot,itop,istep , nused ;
00261 char * cpt ;
00262
00263
00264
00265 if( nvals < 1 ) return NULL ;
00266
00267
00268
00269 if( str == NULL || str[0] == '\0' ){
00270 subv = (int *) XtMalloc( sizeof(int) * (nvals+1) ) ;
00271 subv[0] = nvals ;
00272 for( ii=0 ; ii < nvals ; ii++ ) subv[ii+1] = ii ;
00273 return subv ;
00274 }
00275
00276
00277
00278 subv = (int *) XtMalloc( sizeof(int) * 2 ) ;
00279 subv[0] = nout = 0 ;
00280
00281 ipos = 0 ;
00282 if( str[ipos] == '[' ) ipos++ ;
00283
00284
00285
00286 slen = strlen(str) ;
00287 while( ipos < slen && str[ipos] != ']' ){
00288
00289
00290
00291 if( str[ipos] == '$' ){
00292 ibot = nvals-1 ; ipos++ ;
00293 } else {
00294 ibot = strtol( str+ipos , &cpt , 10 ) ;
00295 if( ibot < 0 ){ myXtFree(subv) ; return NULL ; }
00296 if( ibot >= nvals ) ibot = nvals-1 ;
00297 nused = (cpt-(str+ipos)) ;
00298 if( ibot == 0 && nused == 0 ){ myXtFree(subv) ; return NULL ; }
00299 ipos += nused ;
00300 }
00301
00302
00303
00304 if( str[ipos] == ',' || str[ipos] == '\0' || str[ipos] == ']' ){
00305 nout++ ;
00306 subv = (int *) XtRealloc( (char *)subv , sizeof(int) * (nout+1) ) ;
00307 subv[0] = nout ;
00308 subv[nout] = ibot ;
00309 ipos++ ; continue ;
00310 }
00311
00312
00313
00314 if( str[ipos] == '-' ){
00315 ipos++ ;
00316 } else if( str[ipos] == '.' && str[ipos+1] == '.' ){
00317 ipos++ ; ipos++ ;
00318 } else {
00319 myXtFree(subv) ; return NULL ;
00320 }
00321
00322
00323
00324 if( str[ipos] == '$' ){
00325 itop = nvals-1 ; ipos++ ;
00326 } else {
00327 itop = strtol( str+ipos , &cpt , 10 ) ;
00328 if( itop < 0 ){ myXtFree(subv) ; return NULL ; }
00329 if( itop >= nvals ) itop = nvals-1 ;
00330 nused = (cpt-(str+ipos)) ;
00331 if( itop == 0 && nused == 0 ){ myXtFree(subv) ; return NULL ; }
00332 ipos += nused ;
00333 }
00334
00335
00336
00337 istep = (ibot <= itop) ? 1 : -1 ;
00338
00339
00340
00341 if( str[ipos] == '(' ){
00342 ipos++ ;
00343 istep = strtol( str+ipos , &cpt , 10 ) ;
00344 if( istep == 0 ){ myXtFree(subv) ; return NULL ; }
00345 nused = (cpt-(str+ipos)) ;
00346 ipos += nused ;
00347 if( str[ipos] == ')' ) ipos++ ;
00348 }
00349
00350
00351
00352 for( ii=ibot ; (ii-itop)*istep <= 0 ; ii += istep ){
00353 nout++ ;
00354 subv = (int *) XtRealloc( (char *)subv , sizeof(int) * (nout+1) ) ;
00355 subv[0] = nout ;
00356 subv[nout] = ii ;
00357 }
00358
00359
00360
00361 if( str[ipos] == ',' ) ipos++ ;
00362
00363 }
00364
00365 return subv ;
00366 }
00367
00368
00369
00370 void BUCK_Syntax(void)
00371 {
00372 printf(
00373 "Concatenate sub-bricks from input datasets into one big\n"
00374 "'bucket' dataset.\n"
00375 "Usage: 3dbucket options\n"
00376 "where the options are:\n"
00377 ) ;
00378
00379 printf(
00380 " -prefix pname = Use 'pname' for the output dataset prefix name.\n"
00381 " OR -output pname [default='buck']\n"
00382 "\n"
00383 " -session dir = Use 'dir' for the output dataset session directory.\n"
00384 " [default='./'=current working directory]\n"
00385 " -glueto fname = Append bricks to the end of the 'fname' dataset.\n"
00386 " This command is an alternative to the -prefix \n"
00387 " and -session commands. \n"
00388 " -dry = Execute a 'dry run'; that is, only print out\n"
00389 " what would be done. This is useful when\n"
00390 " combining sub-bricks from multiple inputs.\n"
00391 " -verb = Print out some verbose output as the program\n"
00392 " proceeds (-dry implies -verb).\n"
00393 " -fbuc = Create a functional bucket.\n"
00394 " -abuc = Create an anatomical bucket. If neither of\n"
00395 " these options is given, the output type is\n"
00396 " determined from the first input type.\n"
00397 "\n"
00398 "Command line arguments after the above are taken as input datasets.\n"
00399 "A dataset is specified using one of these forms:\n"
00400 " 'prefix+view', 'prefix+view.HEAD', or 'prefix+view.BRIK'.\n"
00401 "You can also add a sub-brick selection list after the end of the\n"
00402 "dataset name. This allows only a subset of the sub-bricks to be\n"
00403 "included into the output (by default, all of the input dataset\n"
00404 "is copied into the output). A sub-brick selection list looks like\n"
00405 "one of the following forms:\n"
00406 " fred+orig[5] ==> use only sub-brick #5\n"
00407 " fred+orig[5,9,17] ==> use #5, #9, and #12\n"
00408 " fred+orig[5..8] or [5-8] ==> use #5, #6, #7, and #8\n"
00409 " fred+orig[5..13(2)] or [5-13(2)] ==> use #5, #7, #9, #11, and #13\n"
00410 "Sub-brick indexes start at 0. You can use the character '$'\n"
00411 "to indicate the last sub-brick in a dataset; for example, you\n"
00412 "can select every third sub-brick by using the selection list\n"
00413 " fred+orig[0..$(3)]\n"
00414 "\n"
00415 "N.B.: The sub-bricks are output in the order specified, which may\n"
00416 " not be the order in the original datasets. For example, using\n"
00417 " fred+orig[0..$(2),1..$(2)]\n"
00418 " will cause the sub-bricks in fred+orig to be output into the\n"
00419 " new dataset in an interleaved fashion. Using\n"
00420 " fred+orig[$..0]\n"
00421 " will reverse the order of the sub-bricks in the output.\n"
00422 "\n"
00423 "N.B.: Bucket datasets have multiple sub-bricks, but do NOT have\n"
00424 " a time dimension. You can input sub-bricks from a 3D+time dataset\n"
00425 " into a bucket dataset. You can use the '3dinfo' program to see\n"
00426 " how many sub-bricks a 3D+time or a bucket dataset contains.\n"
00427 "\n"
00428 "N.B.: The '$', '(', ')', '[', and ']' characters are special to\n"
00429 " the shell, so you will have to escape them. This is most easily\n"
00430 " done by putting the entire dataset plus selection list inside\n"
00431 " single quotes, as in 'fred+orig[5..7,9]'.\n"
00432 "\n"
00433 "N.B.: In non-bucket functional datasets (like the 'fico' datasets\n"
00434 " output by FIM, or the 'fitt' datasets output by 3dttest), sub-brick\n"
00435 " [0] is the 'intensity' and sub-brick [1] is the statistical parameter\n"
00436 " used as a threshold. Thus, to create a bucket dataset using the\n"
00437 " intensity from dataset A and the threshold from dataset B, and\n"
00438 " calling the output dataset C, you would type\n"
00439 " 3dbucket -prefix C -fbuc 'A+orig[0]' -fbuc 'B+orig[1]'\n"
00440 "\n"
00441 "WARNING: using this program, it is possible to create a dataset that\n"
00442 " has different basic datum types for different sub-bricks\n"
00443 " (e.g., shorts for brick 0, floats for brick 1).\n"
00444 " Do NOT do this! Very few AFNI programs will work correctly\n"
00445 " with such datasets!\n"
00446 ) ;
00447
00448 exit(0) ;
00449 }
00450
00451
00452
00453 int main( int argc , char * argv[] )
00454 {
00455 int ninp , ids , nv , iv,jv,kv , ivout , new_nvals ;
00456 THD_3dim_dataset * new_dset=NULL , * dset ;
00457 char buf[256] ;
00458
00459
00460 printf ("\n\nProgram %s \n", PROGRAM_NAME);
00461 printf ("Last revision: %s \n\n", LAST_MOD_DATE);
00462
00463
00464
00465 if( argc < 2 || strncmp(argv[1],"-help",4) == 0 ) BUCK_Syntax() ;
00466
00467 mainENTRY("3dbucket main"); machdep(); PRINT_VERSION("3dbucket") ;
00468
00469
00470
00471 { int new_argc ; char ** new_argv ;
00472 addto_args( argc , argv , &new_argc , &new_argv ) ;
00473 if( new_argv != NULL ){ argc = new_argc ; argv = new_argv ; }
00474 }
00475
00476 AFNI_logger("3dbucket",argc,argv) ;
00477
00478 BUCK_read_opts( argc , argv ) ;
00479
00480
00481 ninp = BUCK_dsar->num ;
00482 if( ninp < 1 ){
00483 fprintf(stderr,"*** No input datasets?\n") ; exit(1) ;
00484 }
00485
00486 new_nvals = 0 ;
00487 for( ids=0 ; ids < ninp ; ids++ ) new_nvals += NSUBV(ids) ;
00488
00489 if( BUCK_verb ) printf("-verb: output will have %d sub-bricks\n",new_nvals) ;
00490
00491 new_dset = EDIT_empty_copy( DSUB(0) ) ;
00492
00493
00494
00495 for( iv=1 ; iv < ninp ; iv++ ){
00496 if( !EQUIV_DATAXES(new_dset->daxes,DSUB(iv)->daxes) )
00497 fprintf(stderr,"++ WARNING: %s grid mismatch with %s\n",
00498 DSET_BRIKNAME(DSUB(0)) , DSET_BRIKNAME(DSUB(iv)) ) ;
00499 }
00500
00501 tross_Copy_History( DSUB(0) , new_dset ) ;
00502 tross_Make_History( "3dbucket" , argc,argv , new_dset ) ;
00503
00504 EDIT_dset_items( new_dset ,
00505 ADN_prefix , BUCK_output_prefix ,
00506 ADN_directory_name, BUCK_session ,
00507 ADN_type , BUCK_type ,
00508 ADN_func_type , ISANATTYPE(BUCK_type) ? ANAT_BUCK_TYPE
00509 : FUNC_BUCK_TYPE,
00510 ADN_ntt , 0 ,
00511 ADN_nvals , new_nvals ,
00512 ADN_none ) ;
00513
00514
00515
00516 if (! BUCK_glue){
00517 if( THD_is_file(DSET_HEADNAME(new_dset)) ){
00518 fprintf(stderr,"*** Fatal error: file %s already exists!\n",
00519 DSET_HEADNAME(new_dset) ) ;
00520 exit(1) ;
00521 }
00522 } else {
00523
00524
00525 new_dset->idcode = DSUB(0) -> idcode ;
00526 }
00527
00528 THD_force_malloc_type( new_dset->dblk , DATABLOCK_MEM_MALLOC ) ;
00529
00530
00531
00532 if( ninp > 1 ) myXtFree( new_dset->keywords ) ;
00533
00534 ivout = 0 ;
00535 for( ids=0 ; ids < ninp ; ids++ ){
00536 dset = DSUB(ids) ;
00537 nv = NSUBV(ids) ;
00538
00539 if( ! BUCK_dry ){
00540 DSET_load(dset) ;
00541 if( ! DSET_LOADED(dset) ){
00542 fprintf(stderr,"*** Fatal error: can't load data from %s\n",
00543 DSET_FILECODE(dset)) ;
00544 exit(1) ;
00545 }
00546 }
00547
00548
00549
00550 for( iv=0 ; iv < nv ; iv++ ){
00551 jv = SUBV(ids,iv) ;
00552
00553 if( ! BUCK_dry ){
00554 EDIT_substitute_brick( new_dset , ivout ,
00555 DSET_BRICK_TYPE(dset,jv) , DSET_ARRAY(dset,jv) ) ;
00556
00557
00558
00559 if (dset->func_type == FUNC_BUCK_TYPE)
00560 sprintf (buf, "%s", DSET_BRICK_LABEL(dset,jv));
00561 else
00562 sprintf(buf,"%.12s[%d]",DSET_PREFIX(dset),jv) ;
00563 EDIT_dset_items( new_dset , ADN_brick_label_one+ivout, buf , ADN_none ) ;
00564
00565 sprintf(buf,"%s[%d]",DSET_FILECODE(dset),jv) ;
00566 EDIT_dset_items(
00567 new_dset, ADN_brick_keywords_replace_one+ivout, buf, ADN_none ) ;
00568
00569 EDIT_dset_items(
00570 new_dset ,
00571 ADN_brick_fac_one +ivout, DSET_BRICK_FACTOR(dset,jv),
00572 ADN_brick_keywords_append_one+ivout, DSET_BRICK_KEYWORDS(dset,jv) ,
00573 ADN_none ) ;
00574
00575
00576
00577 kv = DSET_BRICK_STATCODE(dset,jv) ;
00578
00579 if( FUNC_IS_STAT(kv) ){
00580
00581 int npar = FUNC_need_stat_aux[kv] , lv ;
00582 float * par = (float *) malloc( sizeof(float) * (npar+2) ) ;
00583 float * sax = DSET_BRICK_STATAUX(dset,jv) ;
00584 par[0] = kv ;
00585 par[1] = npar ;
00586 for( lv=0 ; lv < npar ; lv++ )
00587 par[lv+2] = (sax != NULL) ? sax[lv] : 0.0 ;
00588
00589 EDIT_dset_items(new_dset ,
00590 ADN_brick_stataux_one+ivout , par ,
00591 ADN_none ) ;
00592 free(par) ;
00593
00594
00595
00596 } else if( ISFUNC(dset) &&
00597 FUNC_IS_STAT(dset->func_type) &&
00598 jv == FUNC_ival_thr[dset->func_type] ){
00599
00600 int npar , lv ;
00601 float * par , * sax ;
00602 kv = dset->func_type ;
00603 npar = FUNC_need_stat_aux[kv] ;
00604 par = (float *) malloc( sizeof(float) * (npar+2) ) ;
00605 sax = dset->stat_aux ;
00606 par[0] = kv ;
00607 par[1] = npar ;
00608 for( lv=0 ; lv < npar ; lv++ )
00609 par[lv+2] = (sax != NULL) ? sax[lv] : 0.0 ;
00610
00611 EDIT_dset_items(new_dset ,
00612 ADN_brick_stataux_one+ivout , par ,
00613 ADN_none ) ;
00614 free(par) ;
00615 }
00616
00617
00618
00619 if( BUCK_verb ) printf("-verb: copied %s[%d] into %s[%d]\n" ,
00620 DSET_FILECODE(dset) , jv ,
00621 DSET_FILECODE(new_dset) , ivout ) ;
00622 } else {
00623 printf("-dry: would copy %s[%d] into %s[%d]\n" ,
00624 DSET_FILECODE(dset) , jv ,
00625 DSET_FILECODE(new_dset) , ivout ) ;
00626 }
00627
00628 ivout++ ;
00629 }
00630
00631
00632
00633
00634
00635 if( ! BUCK_dry && nv < DSET_NVALS(dset) ){
00636
00637 for( kv=0 ; kv < DSET_NVALS(dset) ; kv++ ){
00638 for( iv=0 ; iv < nv ; iv++ ){
00639 jv = SUBV(ids,iv) ;
00640 if( jv == kv ) break ;
00641 }
00642 if( iv == nv ){
00643 mri_free( DSET_BRICK(dset,kv) ) ;
00644 #if 0
00645 if( BUCK_verb ) printf("-verb: unloaded unused %s[%d]\n" ,
00646 DSET_FILECODE(dset) , kv ) ;
00647 #endif
00648 }
00649 }
00650 }
00651
00652 }
00653
00654 if( ! BUCK_dry ){
00655 if( BUCK_verb ) fprintf(stderr,"-verb: loading statistics\n") ;
00656 THD_load_statistics( new_dset ) ;
00657 THD_write_3dim_dataset( NULL,NULL , new_dset , True ) ;
00658 if( BUCK_verb ) fprintf(stderr,"-verb: wrote output: %s\n",DSET_BRIKNAME(new_dset)) ;
00659 }
00660
00661 exit(0) ;
00662 }