1 /*
2     TiMidity -- Experimental MIDI to WAVE converter
3     Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the Perl Artistic License, available in COPYING.
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <errno.h>
14 #include "config.h"
15 #include "common.h"
16 #include "output.h"
17 #include "ctrlmode.h"
18 
19 /* I guess "rb" should be right for any libc */
20 #define OPEN_MODE "rb"
21 
22 char current_filename[PATH_MAX];
23 
24 static PathList *pathlist=NULL;
25 
26 /* Try to open a file for reading. If the filename ends in one of the
27    defined compressor extensions, pipe the file through the decompressor */
try_to_open(const char * name,int decompress,int noise_mode)28 static FILE *try_to_open(const char *name, int decompress, int noise_mode)
29 {
30   FILE *fp;
31 
32   fp=fopen(name, OPEN_MODE); /* First just check that the file exists */
33 
34   if (!fp)
35     return 0;
36 
37 #ifdef DECOMPRESSOR_LIST
38   if (decompress)
39     {
40       int l,el;
41       static char *decompressor_list[] = DECOMPRESSOR_LIST, **dec;
42       const char *cp;
43       char tmp[PATH_MAX], tmp2[PATH_MAX], *cp2;
44       /* Check if it's a compressed file */
45       l=strlen(name);
46       for (dec=decompressor_list; *dec; dec+=2)
47 	{
48 	  el=strlen(*dec);
49 	  if ((el>=l) || (strcmp(name+l-el, *dec)))
50 	    continue;
51 
52 	  /* Yes. Close the file, open a pipe instead. */
53 	  fclose(fp);
54 
55 	  /* Quote some special characters in the file name */
56 	  cp=name;
57 	  cp2=tmp2;
58 	  while (*cp)
59 	    {
60 	      switch(*cp)
61 		{
62 		case '\'':
63 		case '\\':
64 		case ' ':
65 		case '`':
66 		case '!':
67 		case '"':
68 		case '&':
69 		case ';':
70 		  *cp2++='\\';
71 		}
72 	      *cp2++=*cp++;
73 	    }
74 	  *cp2=0;
75 
76 	  sprintf(tmp, *(dec+1), tmp2);
77 	  fp=popen(tmp, "r");
78 	  break;
79 	}
80     }
81 #endif
82 
83   return fp;
84 }
85 
86 /* This is meant to find and open files for reading, possibly piping
87    them through a decompressor. */
open_file(const char * name,int decompress,int noise_mode)88 FILE *open_file(const char *name, int decompress, int noise_mode)
89 {
90   FILE *fp;
91   PathList *plp;
92   int l;
93 
94   if (!name || !(*name))
95     {
96       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Attempted to open nameless file.");
97       return 0;
98     }
99 
100   if (pathlist==NULL) {
101     /* Generate path list */
102 #ifdef DEFAULT_PATH
103     add_to_pathlist(DEFAULT_PATH);
104 #endif
105 #ifdef DEFAULT_PATH1
106     add_to_pathlist(DEFAULT_PATH1);
107 #endif
108 #ifdef DEFAULT_PATH2
109     add_to_pathlist(DEFAULT_PATH2);
110 #endif
111 #ifdef DEFAULT_PATH3
112     add_to_pathlist(DEFAULT_PATH3);
113 #endif
114   }
115 
116   /* First try the given name */
117 
118   strncpy(current_filename, name, PATH_MAX - 1);
119   current_filename[PATH_MAX - 1]='\0';
120 
121   ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s", current_filename);
122   if ((fp=try_to_open(current_filename, decompress, noise_mode)))
123     return fp;
124 
125 #ifdef ENOENT
126   if (noise_mode && (errno != ENOENT))
127     {
128       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
129 	   current_filename, strerror(errno));
130       return 0;
131     }
132 #endif
133 
134   plp=pathlist;
135   if (name[0] != PATH_SEP)
136     while (plp)  /* Try along the path then */
137       {
138 	*current_filename=0;
139 	l=strlen(plp->path);
140 	if(l)
141 	  {
142 	    strcpy(current_filename, plp->path);
143 	    if(current_filename[l-1]!=PATH_SEP)
144 	      strcat(current_filename, PATH_STRING);
145 	  }
146 	strcat(current_filename, name);
147 	ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s", current_filename);
148 	if ((fp=try_to_open(current_filename, decompress, noise_mode)))
149 	  return fp;
150 #ifdef ENOENT
151 	if (noise_mode && (errno != ENOENT))
152 	  {
153 	    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
154 		 current_filename, strerror(errno));
155 	    return 0;
156 	  }
157 #endif
158 	plp=plp->next;
159       }
160 
161   /* Nothing could be opened. */
162 
163   *current_filename=0;
164 
165   if (noise_mode>=2)
166     ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name, strerror(errno));
167 
168   return 0;
169 }
170 
171 /* This closes files opened with open_file */
close_file(FILE * fp)172 void close_file(FILE *fp)
173 {
174 #ifdef DECOMPRESSOR_LIST
175   if (pclose(fp)) /* Any better ideas? */
176 #endif
177     fclose(fp);
178 
179   strncpy(current_filename, "MIDI file", PATH_MAX - 1);
180 }
181 
182 /* This is meant for skipping a few bytes in a file or fifo. */
skip(FILE * fp,size_t len)183 void skip(FILE *fp, size_t len)
184 {
185   size_t c;
186   char tmp[PATH_MAX];
187   while (len>0)
188     {
189       c=len;
190       if (c>PATH_MAX) c=PATH_MAX;
191       len-=c;
192       if (c!=fread(tmp, 1, c, fp))
193 	ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: skip: %s",
194 	     current_filename, strerror(errno));
195     }
196 }
197 
198 /* This'll allocate memory or die. */
safe_malloc(size_t count)199 void *safe_malloc(size_t count)
200 {
201   void *p;
202   if (count > (1<<21))
203     {
204       ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
205 	   "Strange, I feel like allocating %d bytes. This must be a bug.",
206 	   count);
207     }
208   else if ((p=malloc(count)))
209     return p;
210   else
211     ctl->cmsg(CMSG_FATAL, VERB_NORMAL, "Sorry. Couldn't malloc %d bytes.", count);
212 
213   ctl->close();
214   exit(10);
215   return(NULL);
216 }
217 
218 /* This adds a directory to the path list */
add_to_pathlist(const char * s)219 void add_to_pathlist(const char *s)
220 {
221   PathList *plp=safe_malloc(sizeof(PathList));
222   strcpy((plp->path=safe_malloc(strlen(s)+1)),s);
223   plp->next=pathlist;
224   pathlist=plp;
225 }
226 
227 /* Free memory associated to path list */
free_pathlist(void)228 void free_pathlist(void)
229 {
230   PathList *plp, *next_plp;
231 
232   plp = pathlist;
233   while (plp) {
234     if (plp->path) {
235       free(plp->path);
236       plp->path=NULL;
237     }
238     next_plp = plp->next;
239     free(plp);
240     plp = next_plp;
241   }
242   pathlist = NULL;
243 }
244