Remove old help formatting code
[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 "smalloc.h"
51 #include "string2.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, efG87, 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, efG87, 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, efXYZ };
113 #define NSTOS asize(stos)
114
115 static const int stxs[] =
116 {
117     efGRO, efG96, efPDB, efBRK, efENT, efESP, efXYZ,
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     { eftASC, ".g87", "gtraj", NULL, "Gromos-87 ASCII trajectory format" },
172     { eftXDR, ".edr", "ener",   NULL, "Energy file"},
173     { eftGEN, ".???", "conf", "-c", "Structure file", NSTXS, stxs },
174     { eftGEN, ".???", "out", "-o", "Structure file", NSTOS, stos },
175     { eftASC, ".gro", "conf", "-c", "Coordinate file in Gromos-87 format" },
176     { eftASC, ".g96", "conf", "-c", "Coordinate file in Gromos-96 format" },
177     { eftASC, ".pdb", "eiwit",  "-f", "Protein data bank file"},
178     { eftASC, ".brk", "eiwit",  "-f", "Brookhaven data bank file"},
179     { eftASC, ".ent", "eiwit", "-f", "Entry in the protein date bank" },
180     { eftASC, ".esp", "conf", "-f", "Coordinate file in Espresso format" },
181     { eftASC, ".pqr", "state",  "-o", "Coordinate file for MEAD"},
182     { eftASC, ".xyz", "conf", "-o", "Coordinate file for some other programs" },
183     { eftXDR, ".cpt", "state",  "-cp", "Checkpoint file"},
184     { eftASC, ".log", "run",    "-l", "Log file"},
185     { eftASC, ".xvg", "graph",  "-o", "xvgr/xmgr file"},
186     { eftASC, ".out", "hello",  "-o", "Generic output file"},
187     { eftASC, ".ndx", "index",  "-n", "Index file", },
188     { eftASC, ".top", "topol",  "-p", "Topology file"},
189     { eftASC, ".itp", "topinc", NULL, "Include file for topology"},
190     { eftGEN, ".???", "topol", "-s", "Run input file", NTPXS, tpxs },
191     { eftGEN, ".???", "topol", "-s", "Structure+mass(db)", NTPSS, tpss },
192     { eftXDR, ".tpr", "topol",  "-s", "Portable xdr run input file"},
193     { eftASC, ".tpa", "topol",  "-s", "Ascii run input file"},
194     { eftBIN, ".tpb", "topol",  "-s", "Binary run input file"},
195     { eftASC, ".tex", "doc",    "-o", "LaTeX file"},
196     { eftASC, ".rtp", "residue", NULL, "Residue Type file used by pdb2gmx" },
197     { eftASC, ".atp", "atomtp", NULL, "Atomtype file used by pdb2gmx" },
198     { eftASC, ".hdb", "polar",  NULL, "Hydrogen data base"},
199     { eftASC, ".dat", "nnnice", NULL, "Generic data file"},
200     { eftASC, ".dlg", "user",   NULL, "Dialog Box data for ngmx"},
201     { eftASC, ".map", "ss", NULL, "File that maps matrix data to colors" },
202     { eftASC, ".eps", "plot", NULL, "Encapsulated PostScript (tm) file" },
203     { eftASC, ".mat", "ss",     NULL, "Matrix Data file"},
204     { eftASC, ".m2p", "ps",     NULL, "Input file for mat2ps"},
205     { eftXDR, ".mtx", "hessian", "-m", "Hessian matrix"},
206     { eftASC, ".edi", "sam",    NULL, "ED sampling input"},
207     { eftASC, ".cub", "pot",  NULL, "Gaussian cube file" },
208     { eftASC, ".xpm", "root", NULL, "X PixMap compatible matrix file" },
209     { eftASC, "", "rundir", NULL, "Run directory" }
210 };
211
212 #define NZEXT 2
213 static const char *z_ext[NZEXT] =
214 { ".gz", ".Z" };
215
216 const char *ftp2ext(int ftp)
217 {
218     if ((0 <= ftp) && (ftp < efNR))
219     {
220         return deffile[ftp].ext[0] != '\0' ? deffile[ftp].ext + 1 : "";
221     }
222     else
223     {
224         return "unknown";
225     }
226 }
227
228 const char *ftp2ext_generic(int ftp)
229 {
230     if ((0 <= ftp) && (ftp < efNR))
231     {
232         switch (ftp)
233         {
234             case efTRX:
235                 return "trx";
236             case efTRN:
237                 return "trn";
238             case efSTO:
239                 return "sto";
240             case efSTX:
241                 return "stx";
242             case efTPX:
243                 return "tpx";
244             case efTPS:
245                 return "tps";
246             default:
247                 return ftp2ext(ftp);
248         }
249     }
250     else
251     {
252         return "unknown";
253     }
254 }
255
256 const char *ftp2ext_with_dot(int ftp)
257 {
258     if ((0 <= ftp) && (ftp < efNR))
259     {
260         return deffile[ftp].ext;
261     }
262     else
263     {
264         return "unknown";
265     }
266 }
267
268 int ftp2generic_count(int ftp)
269 {
270     if ((0 <= ftp) && (ftp < efNR))
271     {
272         return deffile[ftp].ntps;
273     }
274     else
275     {
276         return 0;
277     }
278 }
279
280 const int *ftp2generic_list(int ftp)
281 {
282     if ((0 <= ftp) && (ftp < efNR))
283     {
284         return deffile[ftp].tps;
285     }
286     else
287     {
288         return 0;
289     }
290 }
291
292 const char *ftp2desc(int ftp)
293 {
294     if ((0 <= ftp) && (ftp < efNR))
295     {
296         return deffile[ftp].descr;
297     }
298     else
299     {
300         return "unknown filetype";
301     }
302 }
303
304 const char *ftp2ftype(int ftp)
305 {
306     if ((ftp >= 0) && (ftp < efNR))
307     {
308         switch (deffile[ftp].ftype)
309         {
310             case eftASC:
311                 return "ASCII";
312             case eftBIN:
313                 return "Binary";
314             case eftXDR:
315                 return "XDR portable";
316             case eftTNG:
317                 return "TNG";
318             case eftGEN:
319                 return "";
320             default:
321                 gmx_fatal(FARGS, "Unknown filetype %d in ftp2ftype", deffile[ftp].ftype);
322                 break;
323         }
324     }
325     return "unknown";
326 }
327
328 const char *ftp2defnm(int ftp)
329 {
330     if ((0 <= ftp) && (ftp < efNR))
331     {
332         return deffile[ftp].defnm;
333     }
334     else
335     {
336         return NULL;
337     }
338 }
339
340 static void check_opts(int nf, t_filenm fnm[])
341 {
342     int              i;
343     const t_deffile *df;
344
345     for (i = 0; (i < nf); i++)
346     {
347         df = &(deffile[fnm[i].ftp]);
348         if (fnm[i].opt == NULL)
349         {
350             if (df->defopt == NULL)
351             {
352                 gmx_fatal(FARGS, "No default cmd-line option for %s (type %d)\n",
353                           deffile[fnm[i].ftp].ext, fnm[i].ftp);
354             }
355             else
356             {
357                 fnm[i].opt = df->defopt;
358             }
359         }
360     }
361 }
362
363 int fn2ftp(const char *fn)
364 {
365     int         i, len;
366     const char *feptr;
367     const char *eptr;
368
369     if (!fn)
370     {
371         return efNR;
372     }
373
374     len = strlen(fn);
375     if ((len >= 4) && (fn[len - 4] == '.'))
376     {
377         feptr = &(fn[len - 4]);
378     }
379     else
380     {
381         return efNR;
382     }
383
384     for (i = 0; (i < efNR); i++)
385     {
386         if ((eptr = deffile[i].ext) != NULL)
387         {
388             if (gmx_strcasecmp(feptr, eptr) == 0)
389             {
390                 break;
391             }
392         }
393     }
394
395     return i;
396 }
397
398 static void set_extension(char *buf, int ftp)
399 {
400     int              len, extlen;
401     const t_deffile *df;
402
403     /* check if extension is already at end of filename */
404     df     = &(deffile[ftp]);
405     len    = strlen(buf);
406     extlen = strlen(df->ext);
407     if ((len <= extlen) || (gmx_strcasecmp(&(buf[len - extlen]), df->ext) != 0))
408     {
409         strcat(buf, df->ext);
410     }
411 }
412
413 static void add_filenm(t_filenm *fnm, const char *filenm)
414 {
415     srenew(fnm->fns, fnm->nfiles+1);
416     fnm->fns[fnm->nfiles] = strdup(filenm);
417     fnm->nfiles++;
418 }
419
420 static void set_grpfnm(t_filenm *fnm, const char *name, const char *deffnm)
421 {
422     char       buf[256], buf2[256];
423     int        i, type;
424     gmx_bool   bValidExt;
425     int        nopts;
426     const int *ftps;
427
428     nopts = deffile[fnm->ftp].ntps;
429     ftps  = deffile[fnm->ftp].tps;
430     if ((nopts == 0) || (ftps == NULL))
431     {
432         gmx_fatal(FARGS, "nopts == 0 || ftps == NULL");
433     }
434
435     bValidExt = FALSE;
436     if (name && deffnm == NULL)
437     {
438         strcpy(buf, name);
439         /* First check whether we have a valid filename already */
440         type = fn2ftp(name);
441         if ((fnm->flag & ffREAD) && (fnm->ftp == efTRX))
442         {
443             /*if file exist don't add an extension for trajectory reading*/
444             bValidExt = gmx_fexist(name);
445         }
446         for (i = 0; (i < nopts) && !bValidExt; i++)
447         {
448             if (type == ftps[i])
449             {
450                 bValidExt = TRUE;
451             }
452         }
453     }
454     else if (deffnm != NULL)
455     {
456         strcpy(buf, deffnm);
457     }
458     else
459     {
460         /* No name given, set the default name */
461         strcpy(buf, ftp2defnm(fnm->ftp));
462     }
463
464     if (!bValidExt && (fnm->flag & ffREAD))
465     {
466         /* for input-files only: search for filenames in the directory */
467         for (i = 0; (i < nopts) && !bValidExt; i++)
468         {
469             type = ftps[i];
470             strcpy(buf2, buf);
471             set_extension(buf2, type);
472             if (gmx_fexist(buf2))
473             {
474                 bValidExt = TRUE;
475                 strcpy(buf, buf2);
476             }
477         }
478     }
479
480     if (!bValidExt)
481     {
482         /* Use the first extension type */
483         set_extension(buf, ftps[0]);
484     }
485
486     add_filenm(fnm, buf);
487 }
488
489 static void set_filenm(t_filenm *fnm, const char *name, const char *deffnm,
490                        gmx_bool bReadNode)
491 {
492     /* Set the default filename, extension and option for those fields that
493      * are not already set. An extension is added if not present, if fn = NULL
494      * or empty, the default filename is given.
495      */
496     char buf[256];
497     int  i, len, extlen;
498
499     if ((fnm->flag & ffREAD) && !bReadNode)
500     {
501         return;
502     }
503
504     if ((fnm->ftp < 0) || (fnm->ftp >= efNR))
505     {
506         gmx_fatal(FARGS, "file type out of range (%d)", fnm->ftp);
507     }
508
509     if (name)
510     {
511         strcpy(buf, name);
512     }
513     if ((fnm->flag & ffREAD) && name && gmx_fexist(name))
514     {
515         /* check if filename ends in .gz or .Z, if so remove that: */
516         len = strlen(name);
517         for (i = 0; i < NZEXT; i++)
518         {
519             extlen = strlen(z_ext[i]);
520             if (len > extlen)
521             {
522                 if (gmx_strcasecmp(name+len-extlen, z_ext[i]) == 0)
523                 {
524                     buf[len-extlen] = '\0';
525                     break;
526                 }
527             }
528         }
529     }
530
531     if (deffile[fnm->ftp].ntps)
532     {
533         set_grpfnm(fnm, name ? buf : NULL, deffnm);
534     }
535     else
536     {
537         if (name == NULL || deffnm != NULL)
538         {
539             if (deffnm != NULL)
540             {
541                 strcpy(buf, deffnm);
542             }
543             else
544             {
545                 strcpy(buf, ftp2defnm(fnm->ftp));
546             }
547         }
548         set_extension(buf, fnm->ftp);
549
550         add_filenm(fnm, buf);
551     }
552 }
553
554 static void set_filenms(int nf, t_filenm fnm[], const char *deffnm, gmx_bool bReadNode)
555 {
556     int i;
557
558     for (i = 0; (i < nf); i++)
559     {
560         if (!IS_SET(fnm[i]))
561         {
562             set_filenm(&(fnm[i]), fnm[i].fn, deffnm, bReadNode);
563         }
564     }
565 }
566
567 void parse_file_args(int *argc, char *argv[], int nf, t_filenm fnm[],
568                      const char *deffnm, gmx_bool bReadNode)
569 {
570     int       i, j;
571     gmx_bool *bRemove;
572
573     check_opts(nf, fnm);
574
575     for (i = 0; (i < nf); i++)
576     {
577         UN_SET(fnm[i]);
578     }
579
580     if (*argc > 1)
581     {
582         snew(bRemove, (*argc)+1);
583         i = 1;
584         do
585         {
586             for (j = 0; (j < nf); j++)
587             {
588                 if (strcmp(argv[i], fnm[j].opt) == 0)
589                 {
590                     DO_SET(fnm[j]);
591                     bRemove[i] = TRUE;
592                     i++;
593                     /* check if we are out of arguments for this option */
594                     if ((i >= *argc) || (argv[i][0] == '-'))
595                     {
596                         set_filenm(&fnm[j], fnm[j].fn, deffnm, bReadNode);
597                     }
598                     /* sweep up all file arguments for this option */
599                     while ((i < *argc) && (argv[i][0] != '-'))
600                     {
601                         set_filenm(&fnm[j], argv[i], NULL, bReadNode);
602                         bRemove[i] = TRUE;
603                         i++;
604                         /* only repeat for 'multiple' file options: */
605                         if (!IS_MULT(fnm[j]))
606                         {
607                             break;
608                         }
609                     }
610
611                     break; /* jump out of 'j' loop */
612                 }
613             }
614             /* No file found corresponding to option argv[i] */
615             if (j == nf)
616             {
617                 i++;
618             }
619         }
620         while (i < *argc);
621
622         /* Remove used entries */
623         for (i = j = 0; (i <= *argc); i++)
624         {
625             if (!bRemove[i])
626             {
627                 argv[j++] = argv[i];
628             }
629         }
630         (*argc) = j - 1;
631         sfree(bRemove);
632     }
633
634     set_filenms(nf, fnm, deffnm, bReadNode);
635
636 }
637
638 const char *opt2fn(const char *opt, int nfile, const t_filenm fnm[])
639 {
640     int i;
641
642     for (i = 0; (i < nfile); i++)
643     {
644         if (strcmp(opt, fnm[i].opt) == 0)
645         {
646             return fnm[i].fns[0];
647         }
648     }
649
650     fprintf(stderr, "No option %s\n", opt);
651
652     return NULL;
653 }
654
655 const char *opt2fn_master(const char *opt, int nfile, const t_filenm fnm[],
656                           t_commrec *cr)
657 {
658     return SIMMASTER(cr) ? opt2fn(opt, nfile, fnm) : NULL;
659 }
660
661 int opt2fns(char **fns[], const char *opt, int nfile, const t_filenm fnm[])
662 {
663     int i;
664
665     for (i = 0; (i < nfile); i++)
666     {
667         if (strcmp(opt, fnm[i].opt) == 0)
668         {
669             *fns = fnm[i].fns;
670             return fnm[i].nfiles;
671         }
672     }
673
674     fprintf(stderr, "No option %s\n", opt);
675     return 0;
676 }
677
678 const char *ftp2fn(int ftp, int nfile, const t_filenm fnm[])
679 {
680     int i;
681
682     for (i = 0; (i < nfile); i++)
683     {
684         if (ftp == fnm[i].ftp)
685         {
686             return fnm[i].fns[0];
687         }
688     }
689
690     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
691     return NULL;
692 }
693
694 int ftp2fns(char **fns[], int ftp, int nfile, const t_filenm fnm[])
695 {
696     int i;
697
698     for (i = 0; (i < nfile); i++)
699     {
700         if (ftp == fnm[i].ftp)
701         {
702             *fns = fnm[i].fns;
703             return fnm[i].nfiles;
704         }
705     }
706
707     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
708     return 0;
709 }
710
711 gmx_bool ftp2bSet(int ftp, int nfile, const t_filenm fnm[])
712 {
713     int i;
714
715     for (i = 0; (i < nfile); i++)
716     {
717         if (ftp == fnm[i].ftp)
718         {
719             return (gmx_bool) IS_SET(fnm[i]);
720         }
721     }
722
723     fprintf(stderr, "ftp2bSet: No filetype %s\n", deffile[ftp].ext);
724
725     return FALSE;
726 }
727
728 gmx_bool opt2bSet(const char *opt, int nfile, const t_filenm fnm[])
729 {
730     int i;
731
732     for (i = 0; (i < nfile); i++)
733     {
734         if (strcmp(opt, fnm[i].opt) == 0)
735         {
736             return (gmx_bool) IS_SET(fnm[i]);
737         }
738     }
739
740     fprintf(stderr, "No option %s\n", opt);
741
742     return FALSE;
743 }
744
745 const char *opt2fn_null(const char *opt, int nfile, const t_filenm fnm[])
746 {
747     int i;
748
749     for (i = 0; (i < nfile); i++)
750     {
751         if (strcmp(opt, fnm[i].opt) == 0)
752         {
753             if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
754             {
755                 return NULL;
756             }
757             else
758             {
759                 return fnm[i].fns[0];
760             }
761         }
762     }
763     fprintf(stderr, "No option %s\n", opt);
764     return NULL;
765 }
766
767 const char *ftp2fn_null(int ftp, int nfile, const t_filenm fnm[])
768 {
769     int i;
770
771     for (i = 0; (i < nfile); i++)
772     {
773         if (ftp == fnm[i].ftp)
774         {
775             if (IS_OPT(fnm[i]) && !IS_SET(fnm[i]))
776             {
777                 return NULL;
778             }
779             else
780             {
781                 return fnm[i].fns[0];
782             }
783         }
784     }
785     fprintf(stderr, "ftp2fn: No filetype %s\n", deffile[ftp].ext);
786     return NULL;
787 }
788
789 gmx_bool is_optional(const t_filenm *fnm)
790 {
791     return ((fnm->flag & ffOPT) == ffOPT);
792 }
793
794 gmx_bool is_output(const t_filenm *fnm)
795 {
796     return ((fnm->flag & ffWRITE) == ffWRITE);
797 }
798
799 gmx_bool is_set(const t_filenm *fnm)
800 {
801     return ((fnm->flag & ffSET) == ffSET);
802 }
803
804 int add_suffix_to_output_names(t_filenm *fnm, int nfile, const char *suffix)
805 {
806     int   i, j, pos;
807     char  buf[STRLEN], newname[STRLEN];
808     char *extpos;
809
810     for (i = 0; i < nfile; i++)
811     {
812         if (is_output(&fnm[i]) && fnm[i].ftp != efCPT)
813         {
814             /* We never use multiple _outputs_, but we might as well check
815                for it, just in case... */
816             for (j = 0; j < fnm[i].nfiles; j++)
817             {
818                 strncpy(buf, fnm[i].fns[j], STRLEN - 1);
819                 extpos  = strrchr(buf, '.');
820                 *extpos = '\0';
821                 sprintf(newname, "%s%s.%s", buf, suffix, extpos + 1);
822                 free(fnm[i].fns[j]);
823                 fnm[i].fns[j] = strdup(newname);
824             }
825         }
826     }
827     return 0;
828 }
829
830 t_filenm *dup_tfn(int nf, const t_filenm tfn[])
831 {
832     int       i, j;
833     t_filenm *ret;
834
835     snew(ret, nf);
836     for (i = 0; i < nf; i++)
837     {
838         ret[i] = tfn[i]; /* just directly copy all non-string fields */
839         if (tfn[i].opt)
840         {
841             ret[i].opt = strdup(tfn[i].opt);
842         }
843         else
844         {
845             ret[i].opt = NULL;
846         }
847
848         if (tfn[i].fn)
849         {
850             ret[i].fn = strdup(tfn[i].fn);
851         }
852         else
853         {
854             ret[i].fn = NULL;
855         }
856
857         if (tfn[i].nfiles > 0)
858         {
859             snew(ret[i].fns, tfn[i].nfiles);
860             for (j = 0; j < tfn[i].nfiles; j++)
861             {
862                 ret[i].fns[j] = strdup(tfn[i].fns[j]);
863             }
864         }
865     }
866     return ret;
867 }
868
869 void done_filenms(int nf, t_filenm fnm[])
870 {
871     int i, j;
872
873     for (i = 0; i < nf; ++i)
874     {
875         for (j = 0; j < fnm[i].nfiles; ++j)
876         {
877             sfree(fnm[i].fns[j]);
878         }
879         sfree(fnm[i].fns);
880         fnm[i].fns = NULL;
881     }
882 }