1 /*
2 ********************************************************************************
3 File: fileio.c
4 
5 Tab size:           4
6 Max line length:    80
7 Programmer:         Volker Kuhlmann
8 
9 
10 wav2cdr 2.3.4 Copyright (C) 1997, 1998, 1999, 2000, 2006 Volker Kuhlmann
11 This program is free software under the terms of the GNU General Public License
12 version 2 (or later, at your option).
13 See the file COPYING for details about license terms and warranty.
14 <VolkerKuhlmann@gmx.de>
15 
16 
17 DESCRIPTION:
18 
19 This module handels all physical data I/O, and file formats.
20 
21 
22 CONDITIONALS:
23 	see wav2cdr.c
24 
25 
26 HISTORY:
27 	see wav2cdr.c, and ChangeLog
28 
29 ********************************************************************************
30 */
31 
32 
33 
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <time.h>
40 
41 #include "chelp.h"
42 
43 #include "wav2cdr.h"
44 
45 
46 
47 /* exported */
48 
49 unsigned long
50 	bytes_in,			/* bytes written to current input/output file, set */
51 	bytes_out;			/* to 0 when opening file, unchanged when closing */
52 
53 string
54 	outname[PATH_MAX];	/* output filename (if not to stdout), unchanged
55 							by closing file */
56 
57 /* imported */
58 
59 extern FILE
60 	*msgfile;			/* where to display cmd args and progress */
61 
62 /* local */
63 
64 static FILE
65  	*infile = NULL,		/* file ptr for input and output data */
66 	*outfile = NULL;
67 
68 
69 
70 /*
71 	Opens an input file.
72 	In: filename or null for stdin
73 	Out: ---
74 	Return: ---
75 */
open_input_file(const string * name)76 void open_input_file (const string *name)
77 {
78 	bytes_in = 0;
79 
80 	/* do not open if stdin */
81 	if (name == NULL) {
82 		infile = stdin;
83 	} else {
84 		fprintf (msgfile, "Opening file(r):   %s\n-\n", name);
85 
86 		infile = fopen (name, "rb");
87 		if (infile == NULL)
88 			exit_error (ERR_IO, "opening file ", name);
89 	}
90 } /* open_input_file() */
91 
92 
93 
94 /*
95 	Close input file
96 */
close_input_file(void)97 void close_input_file (void)
98 {
99 int r = 0;
100 
101 	if (infile != stdin)
102 		r = fclose (infile);
103 	infile = NULL;
104 	if (r != 0)
105 		exit_error (ERR_IO, "closing input", NULL);
106 
107 } /* close_input_file() */
108 
109 
110 
111 /*
112 	Return the size of the given file (or stream if name is NULL) in bytes, or
113 	-1 if not obtainable.
114 	This function assumes that ftell() returns an offset in bytes.
115 	Currently, the max file size which can be handled is 2GB... (ANSI C limit).
116 */
get_file_size(const string * name,FILE * stream)117 signed long get_file_size (const string *name, FILE *stream)
118 {
119 FILE *file;
120 fpos_t pos;
121 long size;
122 int r;
123 
124 	/* in case of stdin, we can try to seek, but we must seek back and not
125 		close */
126 	if (name == NULL)
127 		file = stream;
128 	else {
129 		file = fopen (name, "rb");
130 		if (file == NULL) {
131 			fprintf (msgfile, "Failed to open file %s (%s)\n",
132 						name, strerror (errno));
133 			return -1;
134 		}
135 	}
136 
137 	/* file is open with no error */
138 	fgetpos (file, &pos);
139 	r = fseek (file, 0L, SEEK_END);
140 	DBGPRINTF1 ("get_file_size(): fseek %li ", (long) r);
141 	if (r == 0) {
142 		size = ftell (file);
143 		DBGPRINTF1 ("ftell(): %li", size);
144 		/* return value of ftell() might not be in bytes on all systems? */
145 		fsetpos (file, &pos);
146 	} else
147 		/* fseek() error */
148 		size = -1;
149 	DBGPRINTF0 ("\n");
150 
151 	if (name != NULL)
152 		fclose (file);
153 
154 	return (signed long) size;
155 
156 } /* get_file_size() */
157 
158 
159 
160 /*
161 	Opens an output file.
162 	Needs the cardinal number of the output file as this becomes part of the
163 	file name (unless writing to stdout, or 0).
164 	In: filename or null, cardinal number of output file (track number)
165 	Out: ---
166 	Return: ---
167 */
open_output_file(const string * name,int track)168 void open_output_file (const string *name, int track)
169 {
170 size_t l;
171 
172 	bytes_out = 0;
173 
174 	/* do not open if stdout */
175 	if (name == NULL) {
176 		outfile = stdout;
177 	} else {
178 		strncpy (outname, name, (size_t) PATH_MAX);
179 		outname[PATH_MAX - 1] = '\0';
180 		if (track != 0) {
181 			outname[PATH_MAX - 14] = '\0';
182 			l = strlen (outname);
183 			sprintf (outname + l, ".%02i", track);
184 		}
185 		fprintf (msgfile, "Opening file(w):   %s\n", outname);
186 
187 		outfile = fopen (outname, "wb");
188 		if (outfile == NULL)
189 			exit_error (ERR_IO, "opening file ", outname);
190 	}
191 } /* open_output_file() */
192 
193 
194 
195 /*
196 	Close output file
197 */
close_output_file(void)198 void close_output_file (void)
199 {
200 int r = 0;
201 
202 	r = fclose (outfile);
203 	outfile = NULL;
204 	if (r != 0)
205 		exit_error (ERR_IO, "closing file", NULL);
206 
207 } /* close_output_file() */
208 
209 
210 
open_message_file(void)211 FILE *open_message_file (void)
212 {
213 FILE *file;
214 
215 #ifdef MSDOS_BC
216 	file = fopen ("NUL", "wb+");
217 #else
218 	file = fopen ("/dev/null", "wb+");
219 #endif
220 	if (file == NULL)
221 		exit_error (ERR_IO, "opening bit bucket", NULL);
222 	return file;
223 } /* open_message_file() */
224 
225 
226 
close_message_file(void)227 void close_message_file (void)
228 {
229 int r = 0;
230 
231 	r = fclose (msgfile);
232 	msgfile = NULL;
233 	if (r != 0)
234 		exit_error (ERR_IO, "closing bit bucket", NULL);
235 
236 } /* close_message_file() */
237 
238 
239 
240 /*
241 	Emergency close files (for error exits).
242 	Can't do error checking here as this is called from the error handler!
243 */
emergency_close(void)244 void emergency_close (void)
245 {
246 	if ((infile != NULL)  AND  (infile != stdin))
247 		(void) fclose (infile);
248 	if ((outfile != NULL)  AND  (outfile != stdout))
249 		(void) fclose (outfile);
250 	if ((msgfile != NULL)  AND  (msgfile != stdout)  AND  (msgfile != stderr))
251 		(void) fclose (msgfile);
252 }
253 
254 
255 
256 /*
257 	Read a wav header from the input file
258 	Will not return unless before all bytes have been read.
259 	In: buffer
260 	Out: wav header
261 	Return: ---
262 */
read_wav_header(wav_header_t * header)263 void read_wav_header (wav_header_t *header)
264 {
265 size_t numread;
266 process_t proc;
267 
268 	numread = fread (header, (size_t) 1, sizeof (wav_header_t), infile);
269 	if (numread != sizeof (wav_header_t)) {
270 		if (ferror (infile))
271 			exit_error (ERR_IO, "reading wav header", NULL);
272 		else
273 			exit_error (ERR_IO, "EOF while reading wav header", NULL);
274 	}
275 
276 	/* do any byte swapping here */
277 	init_process_info (&proc);
278 	if (NOT proc.little_host) {
279 		SWAP4BYTES (header->RIFF.size);
280 		SWAP4BYTES (header->Format.size);
281 		SWAP2BYTES (header->FormatTag);
282 		SWAP2BYTES (header->channels);
283 		SWAP4BYTES (header->SamplingRate);
284 		SWAP4BYTES (header->AvgBytesPerSec);
285 		SWAP2BYTES (header->BlockAlignment);
286 		SWAP2BYTES (header->BitsPerSample);
287 		SWAP4BYTES (header->Data.size);
288 	}
289 
290 	/* check that sampling rate etc are what we can handle */
291 	if (   (header->channels != 2)
292 		OR (header->SamplingRate != CDAUDIOSAMPLINGRATE)
293 		OR (header->BitsPerSample != 16)) {
294 		fprintf (msgfile,
295 			"Can only handle wav data with: \n"
296 			"  2 channels, %li Hz sampling rate, 16 bits per sample\n"
297 			"I will ignore that this wav data is different, but expect "
298 				"garbled data.\n"
299 			, (long) CDAUDIOSAMPLINGRATE);
300 	}
301 	if (   (header->FormatTag != 1)) {
302 		fprintf (msgfile,
303 			"Ignoring unrecognised setting(s) in this wav header\n");
304 	}
305 } /* read_wav_header() */
306 
307 
308 
309 /*
310 	Initialise a wav header
311 	Fixed values as for audi CDs only
312 	In: buffer, number of data bytes excl(!) wav header
313 	Out: wav struct
314 	Return: ---
315 */
make_wav_header(wav_header_t * header,unsigned long databytes)316 void make_wav_header (wav_header_t *header, unsigned long databytes)
317 {
318 static wav_header_t wh =
319 	{
320 		{"RIFF", 0 /* data size */
321 					+ sizeof (wav_header_t) - sizeof (wav_chunkheader_t)},
322 		"WAVE",
323 		{"fmt ", 16},
324 		1, 2, 44100L, 2 * 2 * 44100L, 4, 16,
325 		{"data", 0 /* data size here */}
326 	};
327 
328 	*header = wh;
329 	header->RIFF.size += databytes;  /* insert size */
330 	header->Data.size = databytes;
331 
332 } /* make_wav_header() */
333 
334 
335 
336 /*
337 	Write a wav header to the output.
338 	Seeks to the start of the output first in case the data has already
339 	been written and the header needs to be updated when reaching the
340 	end of the file.
341 	Will not return unless correct number of bytes has been written.
342 	In: wav header
343 	Out: ---
344 	Return: ---
345 */
write_wav_header(const wav_header_t * header)346 void write_wav_header (const wav_header_t *header)
347 {
348 size_t numwritten;
349 int r;
350 wav_header_t wh;
351 process_t proc;
352 
353 	wh = *header;
354 
355 	/* do any byte swapping here */
356 	init_process_info (&proc);
357 	if (NOT proc.little_host) { /* thanks to rick@dgii.com (Rick Richardson) */
358 		SWAP4BYTES (wh.RIFF.size);  /* for pointing out the missing NOT */
359 		SWAP4BYTES (wh.Format.size);
360 		SWAP2BYTES (wh.FormatTag);
361 		SWAP2BYTES (wh.channels);
362 		SWAP4BYTES (wh.SamplingRate);
363 		SWAP4BYTES (wh.AvgBytesPerSec);
364 		SWAP2BYTES (wh.BlockAlignment);
365 		SWAP2BYTES (wh.BitsPerSample);
366 		SWAP4BYTES (wh.Data.size);
367 	}
368 
369 	/* overwrite header at start of file (or write to empty file)
370 		fwrite() always writes at the end (appends) for files opened for
371 		read/write, but seems to work here prob because the file is opened for
372 		write only */
373 #if 0
374 	rewind (outfile); /* = fseek (outfile, 0L, SEEK_SET); clearerr (outfile) */
375 #else
376 	r = fseek (outfile, 0L, SEEK_SET);
377 	if (r != 0)
378 		exit_error (ERR_IO,
379 			"for writing wav format output must be seekable", NULL);
380 #endif
381 	numwritten = fwrite (&wh, (size_t) 1, sizeof (wav_header_t), outfile);
382 	if (numwritten != sizeof (wav_header_t)) {
383 		exit_error (ERR_IO, "writing wav header", NULL);
384 	}
385 
386 	/* only update if we are not overwriting the start of the file */
387 	if (bytes_out < numwritten)
388 		bytes_out = numwritten;
389 
390 } /* write_wav_header() */
391 
392 
393 
394 /*
395 	Read block of data
396 	To test for EOF call read_eof().
397 	Note size_t might only be 16 bits.
398 	In: buffer, num of bytes to read
399 	Out: data
400 	Return: num of bytes read
401 */
read_block(void * buf,size_t bytes)402 size_t read_block (void *buf, size_t bytes)
403 {
404 size_t numread;
405 
406 	numread = fread (buf, (size_t) 1, bytes, infile);
407 	if (numread != bytes)
408 		if (ferror (infile))
409 			exit_error (ERR_IO, "reading ", NULL);
410 
411 	bytes_in += numread;
412 	return numread;
413 
414 } /* read_block() */
415 
416 
417 
418 /*
419 	Return EOF condition of input stream
420 	In: ---
421 	Out: ---
422 	Return: TRUE if input stream has end-of-file condition
423 */
read_eof(void)424 BOOL read_eof (void)
425 {
426 	return feof (infile);
427 } /* read_eof() */
428 
429 
430 
431 /*
432 	Write block of data
433 	Will not return if error while writing.
434 	Note size_t might only be 16 bits.
435 	In: buffer, num of bytes to write
436 	Out: ---
437 	Return: number of bytes written (= number requested)
438 */
write_block(const void * buf,size_t bytes)439 size_t write_block (const void *buf, size_t bytes)
440 {
441 size_t numwritten;
442 
443 	numwritten = fwrite (buf, (size_t) 1, bytes, outfile);
444 	DBGPRINTF2 ("write_block(): %i b, writ %i\n", bytes, numwritten);
445 	if (numwritten != bytes)
446 		exit_error (ERR_IO, "writing ", NULL);
447 
448 	bytes_out += numwritten;
449 	return numwritten;
450 
451 } /* write_block() */
452 
453 
454 
455 /* EOF fileio.c */
456 /******************************************************************************/
457