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