Merge release-4-6 into release-5-0
[alexxy/gromacs.git] / src / gromacs / fileio / filenm.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "filenm.h"
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "futil.h"
48 #include "gmx_fatal.h"
49 #include "macros.h"
50 #include "gromacs/utility/smalloc.h"
51 #include "gromacs/utility/cstringutil.h"
52 #include "types/simple.h"
53 #include "types/commrec.h"
54
55 /* XDR should be available on all platforms now,
56  * but we keep the possibility of turning it off...
57  */
58 #define USE_XDR
59
60 /* Use bitflag ... */
61 #define IS_SET(fn) ((fn.flag & ffSET) != 0)
62 #define IS_OPT(fn) ((fn.flag & ffOPT) != 0)
63 #define IS_MULT(fn) ((fn.flag & ffMULT) != 0)
64 #define UN_SET(fn) (fn.flag = (fn.flag & ~ffSET))
65 #define DO_SET(fn) (fn.flag = (fn.flag |  ffSET))
66
67 enum
68 {
69     eftASC, eftBIN, eftXDR, eftTNG, eftGEN, eftNR
70 };
71
72 /* To support multiple file types with one general (eg TRX) we have
73  * these arrays.
74  */
75 static const int trxs[] =
76 {
77 #ifdef USE_XDR
78     efXTC, efTRR, efCPT,
79 #endif
80     efTRJ, efGRO, efG96, efPDB, efTNG
81 };
82 #define NTRXS asize(trxs)
83
84 static const int trcompressed[] =
85 {
86 #ifdef USE_XDR
87     efXTC,
88 #endif
89     efTNG
90 };
91 #define NTRCOMPRESSED asize(trcompressed)
92
93 static const int tros[] =
94 {
95 #ifdef USE_XDR
96     efXTC, efTRR,
97 #endif
98     efTRJ, efGRO, efG96, efPDB, efTNG
99 };
100 #define NTROS asize(tros)
101
102 static const int trns[] =
103 {
104 #ifdef USE_XDR
105     efTRR, efCPT,
106 #endif
107     efTRJ, efTNG
108 };
109 #define NTRNS asize(trns)
110
111 static const int stos[] =
112 { efGRO, efG96, efPDB, efBRK, efENT, efESP };
113 #define NSTOS asize(stos)
114
115 static const int stxs[] =
116 {
117     efGRO, efG96, efPDB, efBRK, efENT, efESP,
118 #ifdef USE_XDR
119     efTPR,
120 #endif
121     efTPB, efTPA
122 };
123 #define NSTXS asize(stxs)
124
125 static const int tpxs[] =
126 {
127 #ifdef USE_XDR
128     efTPR,
129 #endif
130     efTPB, efTPA
131 };
132 #define NTPXS asize(tpxs)
133
134 static const int tpss[] =
135 {
136 #ifdef USE_XDR
137     efTPR,
138 #endif
139     efTPB, efTPA, efGRO, efG96, efPDB, efBRK, efENT
140 };
141 #define NTPSS asize(tpss)
142
143 typedef struct
144 {
145     int         ftype;
146     const char *ext;
147     const char *defnm;
148     const char *defopt;
149     const char *descr;
150     int         ntps;
151     const int  *tps;
152 } t_deffile;
153
154 /* this array should correspond to the enum in filenm.h */
155 static const t_deffile
156     deffile[efNR] =
157 {
158     { eftASC, ".mdp", "grompp", "-f", "grompp input file with MD parameters" },
159     { eftGEN, ".???", "traj", "-f", "Trajectory", NTRXS, trxs },
160     { eftGEN, ".???", "trajout", "-f", "Trajectory", NTROS, tros },
161     { eftGEN, ".???", "traj", NULL,
162       "Full precision trajectory", NTRNS, trns },
163     { eftXDR, ".trr", "traj", NULL, "Trajectory in portable xdr format" },
164     { eftBIN, ".trj", "traj", NULL, "Trajectory file (architecture specific)" },
165     { eftGEN, ".???", "traj_comp", NULL,
166       "Compressed trajectory (tng format or portable xdr format)", NTRCOMPRESSED, trcompressed},
167     { eftXDR, ".xtc", "traj", NULL,
168       "Compressed trajectory (portable xdr format): xtc" },
169     { eftTNG, ".tng", "traj", NULL,
170       "Trajectory file (tng format)" },
171     { eftXDR, ".edr", "ener",   NULL, "Energy file"},
172     { eftGEN, ".???", "conf", "-c", "Structure file", NSTXS, stxs },
173     { eftGEN, ".???", "out", "-o", "Structure file", NSTOS, stos },
174     { eftASC, ".gro", "conf", "-c", "Coordinate file in Gromos-87 format" },
175     { eftASC, ".g96", "conf", "-c", "Coordinate file in Gromos-96 format" },
176     { eftASC, ".pdb", "eiwit",  "-f", "Protein data bank file"},
177     { eftASC, ".brk", "eiwit",  "-f", "Brookhaven data bank file"},
178     { eftASC, ".ent", "eiwit", "-f", "Entry in the protein date bank" },
179     { eftASC, ".esp", "conf", "-f", "Coordinate file in Espresso format" },
180     { eftASC, ".pqr", "state",  "-o", "Coordinate file for MEAD"},
181     { eftXDR, ".cpt", "state",  "-cp", "Checkpoint file"},
182     { eftASC, ".log", "run",    "-l", "Log file"},
183     { eftASC, ".xvg", "graph",  "-o", "xvgr/xmgr file"},
184     { eftASC, ".out", "hello",  "-o", "Generic output file"},
185     { eftASC, ".ndx", "index",  "-n", "Index file", },
186     { eftASC, ".top", "topol",  "-p", "Topology file"},
187     { eftASC, ".itp", "topinc", NULL, "Include file for topology"},
188     { eftGEN, ".???", "topol", "-s", "Run input file", NTPXS, tpxs },
189     { eftGEN, ".???", "topol", "-s", "Structure+mass(db)", NTPSS, tpss },
190     { eftXDR, ".tpr", "topol",  "-s", "Portable xdr run input file"},
191     { eftASC, ".tpa", "topol",  "-s", "Ascii run input file"},
192     { eftBIN, ".tpb", "topol",  "-s", "Binary run input file"},
193     { eftASC, ".tex", "doc",    "-o", "LaTeX file"},
194     { eftASC, ".rtp", "residue", NULL, "Residue Type file used by pdb2gmx" },
195     { eftASC, ".atp", "atomtp", NULL, "Atomtype file used by pdb2gmx" },
196     { eftASC, ".hdb", "polar",  NULL, "Hydrogen data base"},
197     { eftASC, ".dat", "nnnice", NULL, "Generic data file"},
198     { eftASC, ".dlg", "user",   NULL, "Dialog Box data for ngmx"},
199     { eftASC, ".map", "ss", NULL, "File that maps matrix data to colors" },
200     { eftASC, ".eps", "plot", NULL, "Encapsulated PostScript (tm) file" },
201     { eftASC, ".mat", "ss",     NULL, "Matrix Data file"},
202     { eftASC, ".m2p", "ps",     NULL, "Input file for mat2ps"},
203     { eftXDR, ".mtx", "hessian", "-m", "Hessian matrix"},
204     { eftASC, ".edi", "sam",    NULL, "ED sampling input"},
205     { eftASC, ".cub", "pot",  NULL, "Gaussian cube file" },
206     { eftASC, ".xpm", "root", NULL, "X PixMap compatible matrix file" },
207     { eftASC, "", "rundir", NULL, "Run directory" }
208 };
209
210 #define NZEXT 2
211 static const char *z_ext[NZEXT] =
212 { ".gz", ".Z" };
213
214 const char *ftp2ext(int ftp)
215 {
216     if ((0 <= ftp) && (ftp < efNR))
217     {
218         return deffile[ftp].ext[0] != '\0' ? deffile[ftp].ext + 1 : "";
219     }
220     else
221     {
222         return "unknown";
223     }
224 }
225
226 const char *ftp2ext_generic(int ftp)
227 {
228     if ((0 <= ftp) && (ftp < efNR))
229     {
230         switch (ftp)
231         {
232             case efTRX:
233                 return "trx";
234             case efTRN:
235                 return "trn";
236             case efSTO:
237                 return "sto";
238             case efSTX:
239                 return "stx";
240             case efTPX:
241                 return "tpx";
242             case efTPS:
243                 return "tps";
244             default:
245                 return ftp2ext(ftp);
246         }
247     }
248     else
249     {
250         return "unknown";
251     }
252 }
253
254 const char *ftp2ext_with_dot(int ftp)
255 {
256     if ((0 <= ftp) && (ftp < efNR))
257     {
258         return deffile[ftp].ext;
259     }
260     else
261     {
262         return "unknown";
263     }
264 }
265
266 int ftp2generic_count(int ftp)
267 {
268     if ((0 <= ftp) && (ftp < efNR))
269     {
270         return deffile[ftp].ntps;
271     }
272     else
273     {
274         return 0;
275     }
276 }
277
278 const int *ftp2generic_list(int ftp)
279 {
280     if ((0 <= ftp) && (ftp < efNR))
281     {
282         return deffile[ftp].tps;
283     }
284     else
285     {
286         return 0;
287     }
288 }
289
290 const char *ftp2desc(int ftp)
291 {
292     if ((0 <= ftp) && (ftp < efNR))
293     {
294         return deffile[ftp].descr;
295     }
296     else
297     {
298         return "unknown filetype";
299     }
300 }
301
302 const char *ftp2ftype(int ftp)
303 {
304     if ((ftp >= 0) && (ftp < efNR))
305     {
306         switch (deffile[ftp].ftype)
307         {
308             case eftASC:
309                 return "ASCII";
310             case eftBIN:
311                 return "Binary";
312             case eftXDR:
313                 return "XDR portable";
314             case eftTNG:
315                 return "TNG";
316             case eftGEN:
317                 return "";
318             default:
319                 gmx_fatal(FARGS, "Unknown filetype %d in ftp2ftype", deffile[ftp].ftype);
320                 break;
321         }
322     }
323     return "unknown";
324 }
325
326 const char *ftp2defnm(int ftp)
327 {
328     if ((0 <= ftp) && (ftp < efNR))
329     {
330         return deffile[ftp].defnm;
331     }
332     else
333     {
334         return NULL;
335     }
336 }
337
338 static void check_opts(int nf, t_filenm fnm[])
339 {
340     int              i;
341     const t_deffile *df;
342
343     for (i = 0; (i < nf); i++)
344     {
345         df = &(deffile[fnm[i].ftp]);
346         if (fnm[i].opt == NULL)
347         {
348             if (df->defopt == NULL)
349             {
350                 gmx_fatal(FARGS, "No default cmd-line option for %s (type %d)\n",
351                           deffile[fnm[i].ftp].ext, fnm[i].ftp);
352             }
353             else
354             {
355                 fnm[i].opt = df->defopt;
356             }
357         }
358     }
359 }
360
361 int fn2ftp(const char *fn)
362 {
363     int         i, len;
364     const char *feptr;
365     const char *eptr;
366
367     if (!fn)
368     {
369         return efNR;
370     }
371
372     len = strlen(fn);
373     if ((len >= 4) && (fn[len - 4] == '.'))
374     {
375         feptr = &(fn[len - 4]);
376     }
377     else
378     {
379         return efNR;
380     }
381
382     for (i = 0; (i < efNR); i++)
383     {
384         if ((eptr = deffile[i].ext) != NULL)
385         {
386             if (gmx_strcasecmp(feptr, eptr) == 0)
387             {
388                 break;
389             }
390         }
391     }
392
393     return i;
394 }
395
396 static void set_extension(char *buf, int ftp)
397 {
398     int              len, extlen;
399     const t_deffile *df;
400
401     /* check if extension is already at end of filename */
402     df     = &(deffile[ftp]);
403     len    = strlen(buf);
404     extlen = strlen(df->ext);
405     if ((len <= extlen) || (gmx_strcasecmp(&(buf[len - extlen]), df->ext) != 0))
406     {
407         strcat(buf, df->ext);
408     }
409 }
410
411 static void add_filenm(t_filenm *fnm, const char *filenm)
412 {
413     srenew(fnm->fns, fnm->nfiles+1);
414     fnm->fns[fnm->nfiles] = strdup(filenm);
415     fnm->nfiles++;
416 }
417
418 static void set_grpfnm(t_filenm *fnm, const char *name, const char *deffnm)
419 {
420     char       buf[256], buf2[256];
421     int        i, type;
422     gmx_bool   bValidExt;
423     int        nopts;
424     const int *ftps;
425
426     nopts = deffile[fnm->ftp].ntps;
427     ftps  = deffile[fnm->ftp].tps;
428     if ((nopts == 0) || (ftps == NULL))
429     {
430         gmx_fatal(FARGS, "nopts == 0 || ftps == NULL");
431     }
432
433     bValidExt = FALSE;
434     if (name && deffnm == NULL)
435     {
436         strcpy(buf, name);
437         /* First check whether we have a valid filename already */
438         type = fn2ftp(name);
439         if ((fnm->flag & ffREAD) && (fnm->ftp == efTRX))
440         {
441             /*if file exist don't add an extension for trajectory reading*/
442             bValidExt = gmx_fexist(name);
443         }
444         for (i = 0; (i < nopts) && !bValidExt; i++)
445         {
446             if (type == ftps[i])
447             {
448                 bValidExt = TRUE;
449             }
450         }
451     }
452     else if (deffnm != NULL)
453     {
454         strcpy(buf, deffnm);
455     }
456     else
457     {
458         /* No name given, set the default name */
459         strcpy(buf, ftp2defnm(fnm->ftp));
460     }
461
462     if (!bValidExt && (fnm->flag & ffREAD))
463     {
464         /* for input-files only: search for filenames in the directory */
465         for (i = 0; (i < nopts) && !bValidExt; i++)
466         {
467             type = ftps[i];
468             strcpy(buf2, buf);
469             set_extension(buf2, type);
470             if (gmx_fexist(buf2))
471             {
472                 bValidExt = TRUE;
473                 strcpy(buf, buf2);
474             }
475         }
476     }
477
478     if (!bValidExt)
479     {
480         /* Use the first extension type */
481         set_extension(buf, ftps[0]);
482     }
483
484     add_filenm(fnm, buf);
485 }
486
487 static void set_filenm(t_filenm *fnm, const char *name, const char *deffnm,
488                        gmx_bool bReadNode)
489 {
490     /* Set the default filename, extension and option for those fields that
491      * are not already set. An extension is added if not present, if fn = NULL
492      * or empty, the default filename is given.
493      */
494     char buf[256];
495     int  i, len, extlen;
496
497     if ((fnm->flag & ffREAD) && !bReadNode)
498     {
499         return;
500     }
501
502     if ((fnm->ftp < 0) || (fnm->ftp >= efNR))
503     {
504         gmx_fatal(FARGS, "file type out of range (%d)", fnm->ftp);
505     }
506
507     if (name)
508     {
509         strcpy(buf, name);
510     }
511     if ((fnm->flag & ffREAD) && name && gmx_fexist(name))
512     {
513         /* check if filename ends in .gz or .Z, if so remove that: */
514         len = strlen(name);
515         for (i = 0; i < NZEXT; i++)
516         {
517             extlen = strlen(z_ext[i]);
518             if (len > extlen)
519             {
520                 if (gmx_strcasecmp(name+len-extlen, z_ext[i]) == 0)
521                 {
522                     buf[len-extlen] = '\0';
523                     break;
524                 }
525             }
526         }
527     }
528
529     if (deffile[fnm->ftp].ntps)
530     {
531         set_grpfnm(fnm, name ? buf : NULL, deffnm);
532     }
533     else
534     {
535         if (name == NULL || deffnm != NULL)
536         {
537             if (deffnm != NULL)
538             {
539                 strcpy(buf, deffnm);
540             }
541             else
542             {
543                 strcpy(buf, ftp2defnm(fnm->ftp));
544             }
545         }
546         set_extension(buf, fnm->ftp);
547
548         add_filenm(fnm, buf);
549     }
550 }
551
552 static void set_filenms(int nf, t_filenm fnm[], const char *deffnm, gmx_bool bReadNode)
553 {
554     int i;
555
556     for (i = 0; (i < nf); i++)
557     {
558         if (!IS_SET(fnm[i]))
559         {
560             set_filenm(&(fnm[i]), fnm[i].fn, deffnm, bReadNode);
561         }
562     }
563 }
564
565 void parse_file_args(int *argc, char *argv[], int nf, t_filenm fnm[],
566                      const char *deffnm, gmx_bool bReadNode)
567 {
568     int       i, j;
569     gmx_bool *bRemove;
570
571     check_opts(nf, fnm);
572
573     for (i = 0; (i < nf); i++)
574     {
575         UN_SET(fnm[i]);
576     }
577
578     if (*argc > 1)
579     {
580         snew(bRemove, (*argc)+1);
581         i = 1;
582         do
583         {
584             for (j = 0; (j < nf); j++)
585             {
586                 if (strcmp(argv[i], fnm[j].opt) == 0)
587                 {
588                     DO_SET(fnm[j]);
589                     bRemove[i] = TRUE;
590                     i++;
591                     /* check if we are out of arguments for this option */
592                     if ((i >= *argc) || (argv[i][0] == '-'))
593                     {
594                         set_filenm(&fnm[j], fnm[j].fn, deffnm, bReadNode);
595                     }
596                     /* sweep up all file arguments for this option */
597                     while ((i < *argc) && (argv[i][0] != '-'))
598                     {
599                         set_filenm(&fnm[j], argv[i], NULL, bReadNode);
600                         bRemove[i] = TRUE;
601                         i++;
602                         /* only repeat for 'multiple' file options: */
603                         if (!IS_MULT(fnm[j]))
604                         {
605                             break;
606                         }
607                     }
608
609                     break; /* jump out of 'j' loop */
610                 }
611             }
612             /* No file found corresponding to option argv[i] */
613             if (j == nf)
614             {
615                 i++;
616             }
617         }
618         while (i < *argc);
619
620         /* Remove used entries */
621         for (i = j = 0; (i <= *argc); i++)
622         {
623             if (!bRemove[i])
624             {
625                 argv[j++] = argv[i];
626             }
627         }
628         (*argc) = j - 1;
629         sfree(bRemove);
630     }
631
632     set_filenms(nf, fnm, deffnm, bReadNode);
633
634 }
635
636 const char *opt2fn(const char *opt, int nfile, const t_filenm fnm[])
637 {
638     int i;
639
640     for (i = 0; (i < nfile); i++)
641     {
642         if (strcmp(opt, fnm[i].opt) == 0)
643         {
644             return fnm[i].fns[0];
645         }
646     }
647
648     fprintf(stderr, "No option %s\n", opt);
649
650     return NULL;
651 }
652
653 const char *opt2fn_master(const char *opt, int nfile, const t_filenm fnm[],
654                           t_commrec *cr)
655 {
656     return SIMMASTER(cr) ? opt2fn(opt, nfile, fnm) : NULL;
657 }
658
659 int opt2fns(char **fns[], const char *opt, int nfile, const t_filenm fnm[])
660 {
661     int i;
662
663     for (i = 0; (i < nfile); i++)
664     {
665         if (strcmp(opt, fnm[i].opt) == 0)
666         {
667             *fns = fnm[i].fns;
668             return fnm[i].nfiles;
669         }
670     }
671
672     fprintf(stderr, "No option %s\n", opt);
673     return 0;
674 }
675
676 const char *ftp2fn(int ftp, int nfile, const t_filenm fnm[])
677 {
678     int i;
679
680     for (i = 0; (i < nfile); i++)
681     {
682         if (ftp == fnm[i].ftp)
683         {
684             return fnm[i].fns[0];
685         }
686     }
687
688     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
689     return NULL;
690 }
691
692 int ftp2fns(char **fns[], int ftp, int nfile, const t_filenm fnm[])
693 {
694     int i;
695
696     for (i = 0; (i < nfile); i++)
697     {
698         if (ftp == fnm[i].ftp)
699         {
700             *fns = fnm[i].fns;
701             return fnm[i].nfiles;
702         }
703     }
704
705     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
706     return 0;
707 }
708
709 gmx_bool ftp2bSet(int ftp, int nfile, const t_filenm fnm[])
710 {
711     int i;
712
713     for (i = 0; (i < nfile); i++)
714     {
715         if (ftp == fnm[i].ftp)
716         {
717             return (gmx_bool) IS_SET(fnm[i]);
718         }
719     }
720
721     fprintf(stderr, "ftp2bSet: No filetype %s\n", deffile[ftp].ext);
722
723     return FALSE;
724 }
725
726 gmx_bool opt2bSet(const char *opt, int nfile, const t_filenm fnm[])
727 {
728     int i;
729
730     for (i = 0; (i < nfile); i++)
731     {
732         if (strcmp(opt, fnm[i].opt) == 0)
733         {
734             return (gmx_bool) IS_SET(fnm[i]);
735         }
736     }
737
738     fprintf(stderr, "No option %s\n", opt);
739
740     return FALSE;
741 }
742
743 const char *opt2fn_null(const char *opt, int nfile, const t_filenm fnm[])
744 {
745     int i;
746
747     for (i = 0; (i < nfile); i++)
748     {
749         if (strcmp(opt, fnm[i].opt) == 0)
750         {
751             if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
752             {
753                 return NULL;
754             }
755             else
756             {
757                 return fnm[i].fns[0];
758             }
759         }
760     }
761     fprintf(stderr, "No option %s\n", opt);
762     return NULL;
763 }
764
765 const char *ftp2fn_null(int ftp, int nfile, const t_filenm fnm[])
766 {
767     int i;
768
769     for (i = 0; (i < nfile); i++)
770     {
771         if (ftp == fnm[i].ftp)
772         {
773             if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
774             {
775                 return NULL;
776             }
777             else
778             {
779                 return fnm[i].fns[0];
780             }
781         }
782     }
783     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
784     return NULL;
785 }
786
787 gmx_bool is_optional(const t_filenm *fnm)
788 {
789     return ((fnm->flag & ffOPT) == ffOPT);
790 }
791
792 gmx_bool is_output(const t_filenm *fnm)
793 {
794     return ((fnm->flag & ffWRITE) == ffWRITE);
795 }
796
797 gmx_bool is_set(const t_filenm *fnm)
798 {
799     return ((fnm->flag & ffSET) == ffSET);
800 }
801
802 int add_suffix_to_output_names(t_filenm *fnm, int nfile, const char *suffix)
803 {
804     int   i, j, pos;
805     char  buf[STRLEN], newname[STRLEN];
806     char *extpos;
807
808     for (i = 0; i < nfile; i++)
809     {
810         if (is_output(&fnm[i]) && fnm[i].ftp != efCPT)
811         {
812             /* We never use multiple _outputs_, but we might as well check
813                for it, just in case... */
814             for (j = 0; j < fnm[i].nfiles; j++)
815             {
816                 strncpy(buf, fnm[i].fns[j], STRLEN - 1);
817                 extpos  = strrchr(buf, '.');
818                 *extpos = '\0';
819                 sprintf(newname, "%s%s.%s", buf, suffix, extpos + 1);
820                 free(fnm[i].fns[j]);
821                 fnm[i].fns[j] = strdup(newname);
822             }
823         }
824     }
825     return 0;
826 }
827
828 t_filenm *dup_tfn(int nf, const t_filenm tfn[])
829 {
830     int       i, j;
831     t_filenm *ret;
832
833     snew(ret, nf);
834     for (i = 0; i < nf; i++)
835     {
836         ret[i] = tfn[i]; /* just directly copy all non-string fields */
837         if (tfn[i].opt)
838         {
839             ret[i].opt = strdup(tfn[i].opt);
840         }
841         else
842         {
843             ret[i].opt = NULL;
844         }
845
846         if (tfn[i].fn)
847         {
848             ret[i].fn = strdup(tfn[i].fn);
849         }
850         else
851         {
852             ret[i].fn = NULL;
853         }
854
855         if (tfn[i].nfiles > 0)
856         {
857             snew(ret[i].fns, tfn[i].nfiles);
858             for (j = 0; j < tfn[i].nfiles; j++)
859             {
860                 ret[i].fns[j] = strdup(tfn[i].fns[j]);
861             }
862         }
863     }
864     return ret;
865 }
866
867 void done_filenms(int nf, t_filenm fnm[])
868 {
869     int i, j;
870
871     for (i = 0; i < nf; ++i)
872     {
873         for (j = 0; j < fnm[i].nfiles; ++j)
874         {
875             sfree(fnm[i].fns[j]);
876         }
877         sfree(fnm[i].fns);
878         fnm[i].fns = NULL;
879     }
880 }