3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * GROningen Mixture of Alchemy and Childrens' Stories
44 #include "gmx_fatal.h"
52 #include "thread_mpi.h"
55 /* NOTE: this was a cesspool of thread-unsafe code, has now been
56 properly proteced by mutexes (hopefully). */
59 /* XDR should be available on all platforms now,
60 * but we keep the possibility of turning it off...
65 #define IS_SET(fn) ((fn.flag & ffSET) != 0)
66 #define IS_OPT(fn) ((fn.flag & ffOPT) != 0)
67 #define IS_MULT(fn) ((fn.flag & ffMULT) != 0)
68 #define UN_SET(fn) (fn.flag = (fn.flag & ~ffSET))
69 #define DO_SET(fn) (fn.flag = (fn.flag | ffSET))
71 enum { eftASC
, eftBIN
, eftXDR
, eftGEN
, eftNR
};
73 /* To support multiple file types with one general (eg TRX) we have
76 static const int trxs
[]={
80 efTRJ
, efGRO
, efG96
, efPDB
, efG87
};
81 #define NTRXS asize(trxs)
83 static const int tros
[]={
87 efTRJ
, efGRO
, efG96
, efPDB
, efG87
};
88 #define NTROS asize(tros)
90 static const int trns
[]={
95 #define NTRNS asize(trns)
97 static const int stos
[]={ efGRO
, efG96
, efPDB
, efBRK
,
99 #define NSTOS asize(stos)
101 static const int stxs
[]={ efGRO
, efG96
, efPDB
, efBRK
, efENT
, efESP
, efXYZ
,
106 #define NSTXS asize(stxs)
108 static const int tpxs
[]={
113 #define NTPXS asize(tpxs)
115 static const int tpss
[]={
119 efTPB
, efTPA
, efGRO
, efG96
, efPDB
, efBRK
, efENT
};
120 #define NTPSS asize(tpss)
133 /* this array should correspond to the enum in include/types/filenm.h */
134 static const t_deffile deffile
[efNR
] = {
135 { eftASC
, ".mdp", "grompp", "-f", "grompp input file with MD parameters" },
136 { eftASC
, ".gct", "gct", "-f", "General coupling stuff" },
137 { eftGEN
, ".???", "traj", "-f", "Trajectory: xtc trr trj gro g96 pdb cpt", NTRXS
, trxs
},
138 { eftGEN
, ".???", "trajout","-f", "Trajectory: xtc trr trj gro g96 pdb", NTROS
, tros
},
139 { eftGEN
, ".???", "traj", NULL
, "Full precision trajectory: trr trj cpt", NTRNS
, trns
},
140 { eftXDR
, ".trr", "traj", NULL
, "Trajectory in portable xdr format" },
141 { eftBIN
, ".trj", "traj", NULL
, "Trajectory file (architecture specific)" },
142 { eftXDR
, ".xtc", "traj", NULL
, "Compressed trajectory (portable xdr format)"},
143 { eftASC
, ".g87", "gtraj", NULL
, "Gromos-87 ASCII trajectory format" },
144 { eftXDR
, ".edr", "ener", NULL
, "Energy file" },
145 { eftGEN
, ".???", "conf", "-c", "Structure file: gro g96 pdb tpr etc.", NSTXS
, stxs
},
146 { eftGEN
, ".???", "out", "-o", "Structure file: gro g96 pdb etc.", NSTOS
, stos
},
147 { eftASC
, ".gro", "conf", "-c", "Coordinate file in Gromos-87 format" },
148 { eftASC
, ".g96", "conf", "-c", "Coordinate file in Gromos-96 format" },
149 { eftASC
, ".pdb", "eiwit", "-f", "Protein data bank file" },
150 { eftASC
, ".brk", "eiwit", "-f", "Brookhaven data bank file" },
151 { eftASC
, ".ent", "eiwit", "-f", "Entry in the protein date bank" },
152 { eftASC
, ".esp", "conf", "-f", "Coordinate file in Espresso format" },
153 { eftASC
, ".pqr", "state", "-o", "Coordinate file for MEAD" },
154 { eftASC
, ".xyz", "conf", "-o", "Coordinate file for some other programs"},
155 { eftXDR
, ".cpt", "state", "-cp","Checkpoint file" },
156 { eftASC
, ".log", "run", "-l", "Log file" },
157 { eftASC
, ".xvg", "graph", "-o", "xvgr/xmgr file" },
158 { eftASC
, ".out", "hello", "-o", "Generic output file" },
159 { eftASC
, ".ndx", "index", "-n", "Index file", },
160 { eftASC
, ".top", "topol", "-p", "Topology file" },
161 { eftASC
, ".itp", "topinc", NULL
, "Include file for topology" },
162 { eftGEN
, ".???", "topol", "-s", "Run input file: tpr tpb tpa", NTPXS
, tpxs
},
163 { eftGEN
, ".???", "topol", "-s", "Structure+mass(db): tpr tpb tpa gro g96 pdb", NTPSS
, tpss
},
164 { eftXDR
, ".tpr", "topol", "-s", "Portable xdr run input file" },
165 { eftASC
, ".tpa", "topol", "-s", "Ascii run input file" },
166 { eftBIN
, ".tpb", "topol", "-s", "Binary run input file" },
167 { eftASC
, ".tex", "doc", "-o", "LaTeX file" },
168 { eftASC
, ".rtp", "residue",NULL
, "Residue Type file used by pdb2gmx" },
169 { eftASC
, ".atp", "atomtp", NULL
, "Atomtype file used by pdb2gmx" },
170 { eftASC
, ".hdb", "polar", NULL
, "Hydrogen data base" },
171 { eftASC
, ".dat", "nnnice", NULL
, "Generic data file" },
172 { eftASC
, ".dlg", "user", NULL
, "Dialog Box data for ngmx" },
173 { eftASC
, ".map", "ss", NULL
, "File that maps matrix data to colors" },
174 { eftASC
, ".eps", "plot", NULL
, "Encapsulated PostScript (tm) file" },
175 { eftASC
, ".mat", "ss", NULL
, "Matrix Data file" },
176 { eftASC
, ".m2p", "ps", NULL
, "Input file for mat2ps" },
177 { eftXDR
, ".mtx", "hessian","-m", "Hessian matrix" },
178 { eftASC
, ".edi", "sam", NULL
, "ED sampling input" },
179 { eftASC
, ".edo", "sam", NULL
, "ED sampling output" },
180 { eftASC
, ".hat", "gk", NULL
, "Fourier transform of spread function" },
181 { eftASC
, ".xpm", "root", NULL
, "X PixMap compatible matrix file" }
185 static char *default_file_name
=NULL
;
188 static tMPI_Thread_mutex_t filenm_mutex
=TMPI_THREAD_MUTEX_INITIALIZER
;
192 const char *z_ext
[NZEXT
] = { ".gz", ".Z" };
194 void set_default_file_name(const char *name
)
198 tMPI_Thread_mutex_lock(&filenm_mutex
);
200 default_file_name
= strdup(name
);
202 tMPI_Thread_mutex_unlock(&filenm_mutex
);
206 for(i
=0; i
<efNR
; i
++)
207 deffile
[i
].defnm
= default_file_name
;
211 const char *ftp2ext(int ftp
)
213 if ((0 <= ftp
) && (ftp
< efNR
))
214 return deffile
[ftp
].ext
+1;
219 const char *ftp2ext_generic(int ftp
)
221 if ((0 <= ftp
) && (ftp
< efNR
)) {
243 const char *ftp2desc(int ftp
)
245 if ((0 <= ftp
) && (ftp
< efNR
))
246 return deffile
[ftp
].descr
;
248 return "unknown filetype";
251 const char *ftp2ftype(int ftp
)
253 if ((ftp
>= 0) && (ftp
< efNR
)) {
254 switch (deffile
[ftp
].ftype
) {
260 return "XDR portable";
264 gmx_fatal(FARGS
,"Unknown filetype %d in ftp2ftype",deffile
[ftp
].ftype
);
271 const char *ftp2defnm(int ftp
)
273 const char *buf
=NULL
;
276 tMPI_Thread_mutex_lock(&filenm_mutex
);
279 if (default_file_name
)
281 buf
=default_file_name
;
285 if ((0 <= ftp
) && (ftp
< efNR
))
287 buf
=deffile
[ftp
].defnm
;
291 tMPI_Thread_mutex_unlock(&filenm_mutex
);
297 void pr_def(FILE *fp
,int ftp
)
302 const char *ext
,*desc
;
306 defnm
=ftp2defnm(ftp
);
307 /* find default file extension and \tt-ify description */
308 /* FIXME: The constness should not be cast away */
311 ext
= deffile
[df
->tps
[0]].ext
;
312 desc
= strdup(df
->descr
);
313 tmp
= strstr(desc
,": ")+1;
317 snew(flst
,strlen(tmp
)+6);
318 strcpy(flst
, " \\tt ");
330 /* set file contents type */
332 case eftASC
: s
="Asc";
334 case eftBIN
: s
="Bin";
336 case eftXDR
: s
="xdr";
341 gmx_fatal(FARGS
,"Unimplemented filetype %d %d",ftp
,
344 fprintf(fp
,"\\tt %8s & \\tt %3s & %3s & \\tt %2s & %s%s \\\\[-0.1ex]\n",
345 defnm
, ext
, s
, df
->defopt
? df
->defopt
: "",
346 check_tex(desc
),check_tex(flst
));
349 void pr_fns(FILE *fp
,int nf
,const t_filenm tfn
[])
353 char buf
[256],*wbuf
,opt_buf
[32];
356 fprintf(fp
,"%6s %12s %-12s %s\n",
357 "Option","Filename","Type","Description");
358 fprintf(fp
,"------------------------------------------------------------\n");
359 for(i
=0; (i
<nf
); i
++) {
360 for(f
=0; (f
<tfn
[i
].nfiles
); f
++) {
361 sprintf(buf
, "%4s %14s %-12s ",
362 (f
==0) ? tfn
[i
].opt
: "",tfn
[i
].fns
[f
],
363 (f
==0) ? fileopt(tfn
[i
].flag
,opt_buf
,32) : "");
364 if ( f
< tfn
[i
].nfiles
-1 )
365 fprintf(fp
, "%s\n", buf
);
367 if (tfn
[i
].nfiles
> 0) {
368 strcat(buf
, deffile
[tfn
[i
].ftp
].descr
);
369 if ( (strlen(tfn
[i
].opt
)>OPTLEN
) &&
370 (strlen(tfn
[i
].opt
)<=
371 ((OPTLEN
+NAMELEN
)-strlen(tfn
[i
].fns
[tfn
[i
].nfiles
-1]))) )
373 for(j
=strlen(tfn
[i
].opt
);
374 j
<strlen(buf
)-(strlen(tfn
[i
].opt
)-OPTLEN
)+1; j
++)
375 buf
[j
]=buf
[j
+strlen(tfn
[i
].opt
)-OPTLEN
];
377 wbuf
=wrap_lines(buf
,78,35,FALSE
);
378 fprintf(fp
,"%s\n",wbuf
);
386 void pr_fopts(FILE *fp
,int nf
,const t_filenm tfn
[], int shell
)
392 for(i
=0; (i
<nf
); i
++) {
393 fprintf(fp
," \"n/%s/f:*.",tfn
[i
].opt
);
394 if (deffile
[tfn
[i
].ftp
].ntps
) {
396 for(j
=0; j
<deffile
[tfn
[i
].ftp
].ntps
; j
++) {
399 fprintf(fp
,"%s",deffile
[deffile
[tfn
[i
].ftp
].tps
[j
]].ext
+1);
403 fprintf(fp
,"%s",deffile
[tfn
[i
].ftp
].ext
+1);
405 for(j
=0; j
<NZEXT
; j
++)
406 fprintf(fp
,",%s",z_ext
[j
]);
411 for(i
=0; (i
<nf
); i
++) {
412 fprintf(fp
,"%s) COMPREPLY=( $(compgen -X '!*.",tfn
[i
].opt
);
413 if (deffile
[tfn
[i
].ftp
].ntps
) {
415 for(j
=0; j
<deffile
[tfn
[i
].ftp
].ntps
; j
++) {
418 fprintf(fp
,"%s",deffile
[deffile
[tfn
[i
].ftp
].tps
[j
]].ext
+1);
422 fprintf(fp
,"%s",deffile
[tfn
[i
].ftp
].ext
+1);
424 for(j
=0; j
<NZEXT
; j
++) {
427 fprintf(fp
,"%s",z_ext
[j
]);
429 fprintf(fp
,")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
433 for(i
=0; (i
<nf
); i
++) {
434 fprintf(fp
,"- 'c[-1,%s]' -g '*.",tfn
[i
].opt
);
435 if (deffile
[tfn
[i
].ftp
].ntps
) {
437 for(j
=0; j
<deffile
[tfn
[i
].ftp
].ntps
; j
++) {
440 fprintf(fp
,"%s",deffile
[deffile
[tfn
[i
].ftp
].tps
[j
]].ext
+1);
444 fprintf(fp
,"%s",deffile
[tfn
[i
].ftp
].ext
+1);
446 for(j
=0; j
<NZEXT
; j
++)
447 fprintf(fp
,"|%s",z_ext
[j
]);
448 fprintf(fp
,") *(/)' ");
454 static void check_opts(int nf
,t_filenm fnm
[])
459 for(i
=0; (i
<nf
); i
++) {
460 df
=&(deffile
[fnm
[i
].ftp
]);
461 if (fnm
[i
].opt
== NULL
) {
462 if (df
->defopt
== NULL
)
463 gmx_fatal(FARGS
,"No default cmd-line option for %s (type %d)\n",
464 deffile
[fnm
[i
].ftp
].ext
,fnm
[i
].ftp
);
466 fnm
[i
].opt
=df
->defopt
;
471 int fn2ftp(const char *fn
)
481 if ((len
>= 4) && (fn
[len
-4] == '.'))
486 for(i
=0; (i
<efNR
); i
++)
487 if ((eptr
=deffile
[i
].ext
) != NULL
)
488 if (strcasecmp(feptr
,eptr
)==0)
494 static void set_extension(char *buf
,int ftp
)
499 /* check if extension is already at end of filename */
502 extlen
= strlen(df
->ext
);
503 if ((len
<= extlen
) || (strcasecmp(&(buf
[len
-extlen
]),df
->ext
) != 0))
507 static void add_filenm(t_filenm
*fnm
, const char *filenm
)
509 srenew(fnm
->fns
, fnm
->nfiles
+1);
510 fnm
->fns
[fnm
->nfiles
] = strdup(filenm
);
514 static void set_grpfnm(t_filenm
*fnm
,const char *name
,bool bCanNotOverride
)
516 char buf
[256],buf2
[256];
522 nopts
= deffile
[fnm
->ftp
].ntps
;
523 ftps
= deffile
[fnm
->ftp
].tps
;
524 if ((nopts
== 0) || (ftps
== NULL
))
525 gmx_fatal(FARGS
,"nopts == 0 || ftps == NULL");
528 if (name
&& (bCanNotOverride
|| (default_file_name
== NULL
))) {
530 /* First check whether we have a valid filename already */
532 for(i
=0; (i
<nopts
) && !bValidExt
; i
++)
536 /* No name given, set the default name */
537 strcpy(buf
,ftp2defnm(fnm
->ftp
));
539 if (!bValidExt
&& (fnm
->flag
& ffREAD
)) {
540 /* for input-files only: search for filenames in the directory */
541 for(i
=0; (i
<nopts
) && !bValidExt
; i
++) {
544 set_extension(buf2
,type
);
545 if (gmx_fexist(buf2
)) {
553 /* Use the first extension type */
554 set_extension(buf
,ftps
[0]);
556 add_filenm(fnm
, buf
);
559 static void set_filenm(t_filenm
*fnm
,const char *name
,bool bCanNotOverride
,
562 /* Set the default filename, extension and option for those fields that
563 * are not already set. An extension is added if not present, if fn = NULL
564 * or empty, the default filename is given.
569 if ((fnm
->flag
& ffREAD
) && !bReadNode
)
574 if ((fnm
->ftp
< 0) || (fnm
->ftp
>= efNR
))
575 gmx_fatal(FARGS
,"file type out of range (%d)",fnm
->ftp
);
579 if ((fnm
->flag
& ffREAD
) && name
&& gmx_fexist(name
)) {
580 /* check if filename ends in .gz or .Z, if so remove that: */
582 for (i
=0; i
<NZEXT
; i
++) {
583 extlen
= strlen(z_ext
[i
]);
585 if (strcasecmp(name
+len
-extlen
,z_ext
[i
]) == 0) {
586 buf
[len
-extlen
]='\0';
592 if (deffile
[fnm
->ftp
].ntps
)
593 set_grpfnm(fnm
,name
? buf
: NULL
,bCanNotOverride
);
595 if ((name
== NULL
) || !(bCanNotOverride
|| (default_file_name
== NULL
)))
597 const char *defnm
=ftp2defnm(fnm
->ftp
);
600 set_extension(buf
,fnm
->ftp
);
602 add_filenm(fnm
, buf
);
606 static void set_filenms(int nf
,t_filenm fnm
[],bool bReadNode
)
610 for(i
=0; (i
<nf
); i
++)
612 set_filenm(&(fnm
[i
]),fnm
[i
].fn
,FALSE
,bReadNode
);
615 void parse_file_args(int *argc
,char *argv
[],int nf
,t_filenm fnm
[],
616 bool bKeep
,bool bReadNode
)
623 for(i
=0; (i
<nf
); i
++)
627 snew(bRemove
,(*argc
)+1);
630 for(j
=0; (j
<nf
); j
++) {
631 if (strcmp(argv
[i
],fnm
[j
].opt
) == 0) {
635 /* check if we are out of arguments for this option */
636 if ( (i
>= *argc
) || (argv
[i
][0] == '-') )
637 set_filenm(&fnm
[j
],fnm
[j
].fn
,FALSE
,bReadNode
);
638 /* sweep up all file arguments for this option */
639 while ((i
< *argc
) && (argv
[i
][0] != '-')) {
640 set_filenm(&fnm
[j
],argv
[i
],TRUE
,bReadNode
);
643 /* only repeat for 'multiple' file options: */
644 if ( ! IS_MULT(fnm
[j
]) )
648 break; /* jump out of 'j' loop */
651 /* No file found corresponding to option argv[i] */
657 /* Remove used entries */
658 for(i
=j
=0; (i
<=*argc
); i
++) {
667 set_filenms(nf
,fnm
,bReadNode
);
671 const char *opt2fn(const char *opt
,int nfile
, const t_filenm fnm
[])
675 for(i
=0; (i
<nfile
); i
++)
676 if (strcmp(opt
,fnm
[i
].opt
)==0) {
677 return fnm
[i
].fns
[0];
680 fprintf(stderr
,"No option %s\n",opt
);
685 const char *opt2fn_master(const char *opt
, int nfile
, const t_filenm fnm
[],
688 return SIMMASTER(cr
)?opt2fn(opt
,nfile
,fnm
):NULL
;
691 int opt2fns(char **fns
[], const char *opt
,int nfile
, const t_filenm fnm
[])
695 for(i
=0; (i
<nfile
); i
++)
696 if (strcmp(opt
,fnm
[i
].opt
)==0) {
698 return fnm
[i
].nfiles
;
701 fprintf(stderr
,"No option %s\n",opt
);
705 const char *ftp2fn(int ftp
,int nfile
,const t_filenm fnm
[])
709 for(i
=0; (i
<nfile
); i
++)
710 if (ftp
== fnm
[i
].ftp
)
711 return fnm
[i
].fns
[0];
713 fprintf(stderr
,"ftp2fn: No filetype %s\n",deffile
[ftp
].ext
);
717 int ftp2fns(char **fns
[], int ftp
,int nfile
, const t_filenm fnm
[])
721 for(i
=0; (i
<nfile
); i
++)
722 if (ftp
== fnm
[i
].ftp
) {
724 return fnm
[i
].nfiles
;
727 fprintf(stderr
,"ftp2fn: No filetype %s\n",deffile
[ftp
].ext
);
731 bool ftp2bSet(int ftp
,int nfile
,const t_filenm fnm
[])
735 for(i
=0; (i
<nfile
); i
++)
736 if (ftp
== fnm
[i
].ftp
)
737 return (bool) IS_SET(fnm
[i
]);
739 fprintf(stderr
,"ftp2bSet: No filetype %s\n",deffile
[ftp
].ext
);
744 bool opt2bSet(const char *opt
,int nfile
,const t_filenm fnm
[])
748 for(i
=0; (i
<nfile
); i
++)
749 if (strcmp(opt
,fnm
[i
].opt
)==0)
750 return (bool) IS_SET(fnm
[i
]);
752 fprintf(stderr
,"No option %s\n",opt
);
757 const char *opt2fn_null(const char *opt
,int nfile
, const t_filenm fnm
[])
761 for(i
=0; (i
<nfile
); i
++)
762 if (strcmp(opt
,fnm
[i
].opt
)==0) {
763 if (IS_OPT(fnm
[i
]) && !IS_SET(fnm
[i
]))
766 return fnm
[i
].fns
[0];
768 fprintf(stderr
,"No option %s\n",opt
);
772 const char *ftp2fn_null(int ftp
,int nfile
, const t_filenm fnm
[])
776 for(i
=0; (i
<nfile
); i
++)
777 if (ftp
== fnm
[i
].ftp
) {
778 if (IS_OPT(fnm
[i
]) && !IS_SET(fnm
[i
]))
781 return fnm
[i
].fns
[0];
783 fprintf(stderr
,"ftp2fn: No filetype %s\n",deffile
[ftp
].ext
);
788 static void add_filters(char *filter
,int *n
,int nf
,const int ftp
[])
793 sprintf(filter
,"*.{");
794 for(i
=0; (i
<nf
); i
++) {
795 sprintf(buf
,"%s",ftp2ext(ftp
[i
]));
804 char *ftp2filter(int ftp
)
807 static char filter
[128];
813 add_filters(filter
,&n
,NTRXS
,trxs
);
816 add_filters(filter
,&n
,NTRNS
,trns
);
819 add_filters(filter
,&n
,NSTOS
,stos
);
822 add_filters(filter
,&n
,NSTXS
,stxs
);
825 add_filters(filter
,&n
,NTPXS
,tpxs
);
828 sprintf(filter
,"*%s",ftp2ext(ftp
));
836 bool is_optional(const t_filenm
*fnm
)
838 return ((fnm
->flag
& ffOPT
) == ffOPT
);
841 bool is_output(const t_filenm
*fnm
)
843 return ((fnm
->flag
& ffWRITE
) == ffWRITE
);
846 bool is_set(const t_filenm
*fnm
)
848 return ((fnm
->flag
& ffSET
) == ffSET
);
853 int add_suffix_to_output_names(t_filenm
*fnm
, int nfile
, const char *suffix
)
856 char buf
[STRLEN
],newname
[STRLEN
];
859 for( i
=0 ; i
<nfile
; i
++)
861 if( is_output(&fnm
[i
]) && fnm
[i
].ftp
!= efCPT
)
863 /* We never use multiple _outputs_, but we might as well check
864 for it, just in case... */
865 for( j
=0 ; j
<fnm
[i
].nfiles
; j
++)
867 strncpy(buf
,fnm
[i
].fns
[j
],STRLEN
-1);
868 extpos
= strrchr(buf
,'.');
870 sprintf(newname
,"%s.%s.%s",buf
,suffix
,extpos
+1);
872 fnm
[i
].fns
[j
]=strdup(newname
);
879 t_filenm
*dup_tfn(int nf
, const t_filenm tfn
[])
887 ret
[i
] = tfn
[i
]; /* just directly copy all non-string fields */
889 ret
[i
].opt
= strdup(tfn
[i
].opt
);
894 ret
[i
].fn
= strdup(tfn
[i
].fn
);
898 if (tfn
[i
].nfiles
> 0)
900 snew(ret
[i
].fns
,tfn
[i
].nfiles
);
901 for(j
=0;j
<tfn
[i
].nfiles
;j
++)
903 ret
[i
].fns
[j
] = strdup(tfn
[i
].fns
[j
]);