#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <AudioFile.h>
#include <AudioLib.h>
#include <AudioDebug.h>
#include <libaudio.h>
#include <audio_hdr.h>
#define TMPDIR "/tmp"
#define TMPFILE "/audiotoolXXXXXX"
static char *tmpdir = NULL;
static const char *tmpname = "(temporary file)";
static const FileAccess tmpmode = ReadWrite;
static const VMAccess defaccess = SequentialAccess;
const FileAccess AudioFile::defmode = ReadOnly;
const char *AudioFile::AUDIO_PATH = "AUDIOPATH";
AudioFile::
AudioFile():
AudioUnixfile(tmpname, tmpmode),
hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
vmaccess(defaccess)
{
}
AudioFile::
AudioFile(
const char *path,
const FileAccess acc):
AudioUnixfile(path, acc),
hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
vmaccess(defaccess)
{
}
AudioFile::
~AudioFile()
{
if (opened())
(void) Close();
}
AudioError AudioFile::
SetTempPath(
const char *path)
{
struct stat st;
if ((stat(path, &st) < 0) ||
!S_ISDIR(st.st_mode) ||
(access(path, W_OK) < 0)) {
errno = ENOTDIR;
return (AUDIO_UNIXERROR);
}
if (tmpdir != NULL)
(void) free(tmpdir);
tmpdir = (char *)malloc(strlen(path) + 1);
(void) strcpy(tmpdir, path);
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
createfile(
const char *path)
{
char *tmpf;
char *tmpstr;
int openmode;
int desc;
AudioError err;
openmode = GetAccess();
if (!hdrset())
return (RaiseError(AUDIO_ERR_BADHDR));
if ((openmode == -1) || opened() || (strlen(path) == 0))
return (RaiseError(AUDIO_ERR_NOEFFECT));
if (strcmp(path, tmpname) == 0) {
tmpstr = (char *)malloc(1 + strlen(TMPFILE) +
strlen((tmpdir == NULL) ? TMPDIR : tmpdir));
(void) sprintf(tmpstr, "%s%s",
(tmpdir == NULL) ? TMPDIR : tmpdir, TMPFILE);
tmpf = mktemp(tmpstr);
err = createfile(tmpf);
if ((err == AUDIO_SUCCESS) && (unlink(tmpf) < 0)) {
(void) Close();
err = RaiseError(AUDIO_UNIXERROR, Warning);
}
(void) free(tmpstr);
return (err);
}
desc = open(path, openmode | O_CREAT | O_TRUNC, 0666);
if ((desc < 0) && (errno == EOVERFLOW)) {
return (RaiseError(AUDIO_UNIXERROR, Fatal,
(char *)"Large File"));
} else if (desc < 0) {
return (RaiseError(AUDIO_UNIXERROR));
}
setfd(desc);
err = encode_filehdr();
if (err != AUDIO_SUCCESS) {
setfd(-1);
(void) close(desc);
(void) unlink(path);
return (err);
}
origlen = GetLength();
setlength(0.);
hdrsize = lseek(desc, (off_t)0, SEEK_CUR);
if (hdrsize < 0) {
setfd(-1);
(void) close(desc);
(void) unlink(path);
return (err);
}
seekpos = 0;
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
Create()
{
return (createfile(GetName()));
}
AudioError AudioFile::
Open()
{
return (OpenPath(NULL));
}
AudioError AudioFile::
OpenPath(
const char *path)
{
char *filename;
int flen;
char *prefix;
char *str;
char *wrk;
char *pathname;
int openmode;
AudioError err;
openmode = GetAccess();
filename = GetName();
flen = strlen(filename);
if ((openmode == -1) || opened() || (strlen(filename) == 0))
return (RaiseError(AUDIO_ERR_NOEFFECT));
err = tryopen(filename, openmode);
if (!err)
return (AUDIO_SUCCESS);
if (GetAccess().Writeable() || (filename[0] == '/')) {
if ((err == AUDIO_UNIXERROR) && (err.sys == ENOENT) &&
GetAccess().Append() && hdrset()) {
return (Create());
}
return (RaiseError(err));
}
str = (path == NULL) ? NULL : getenv(path);
if (str == NULL)
str = (char *)path;
if (str != NULL) {
wrk = new char[strlen(str) + 1];
(void) strcpy(wrk, str);
str = wrk;
for (prefix = str;
(prefix != NULL) && (prefix[0] != '\0');
prefix = str) {
str = strchr(str, ':');
if (str != NULL)
*str++ = '\0';
pathname = new char[strlen(prefix) + flen + 2];
(void) sprintf(pathname, "%s/%s", prefix, filename);
err = tryopen(pathname, openmode);
delete[] pathname;
switch (err) {
case AUDIO_SUCCESS:
delete[] wrk;
return (RaiseError(err));
}
}
delete[] wrk;
}
return (RaiseError(tryopen(filename, openmode)));
}
AudioError AudioFile::
tryopen(
const char *pathname,
int openmode)
{
struct stat st;
int desc;
AudioError err;
if (pathname != GetName())
SetName(pathname);
if (stat(pathname, &st) < 0)
return (AUDIO_UNIXERROR);
if (!S_ISREG(st.st_mode))
return (AUDIO_ERR_BADFILEHDR);
desc = open(GetName(), openmode);
if ((desc < 0) && (errno == EOVERFLOW)) {
return (RaiseError(AUDIO_UNIXERROR, Fatal,
(char *)"Large File"));
} else if (desc < 0) {
return (AUDIO_UNIXERROR);
}
setfd(desc);
err = decode_filehdr();
if (err != AUDIO_SUCCESS) {
(void) close(desc);
setfd(-1);
return (err);
}
origlen = GetLength();
hdrsize = (off_t)lseek(desc, (off_t)0, SEEK_CUR);
if (hdrsize < 0) {
(void) close(desc);
setfd(-1);
return (err);
}
seekpos = 0;
if (!GetAccess().Writeable()) {
maplen = st.st_size;
if (localByteOrder() == BIG_ENDIAN) {
if ((mapaddr = (caddr_t)mmap(0, (int)maplen, PROT_READ,
MAP_SHARED, desc, 0)) != (caddr_t)-1) {
(void) madvise(mapaddr, (unsigned int)maplen,
(int)GetAccessType());
} else {
(void) RaiseError(AUDIO_UNIXERROR, Warning,
(char *)"Could not mmap() file");
mapaddr = 0;
maplen = 0;
}
} else {
mapaddr = 0;
maplen = 0;
}
}
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
SetAccessType(VMAccess vmacc)
{
if (!opened()) {
return (AUDIO_ERR_NOEFFECT);
}
if (mapaddr == 0) {
return (AUDIO_ERR_NOEFFECT);
}
(void) madvise(mapaddr, (unsigned int)maplen, (int)vmacc);
vmaccess = vmacc;
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
Close()
{
AudioError err;
if (!opened())
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
if (GetAccess().Writeable() && (origlen != GetLength())) {
if (GetHeader().Time_to_Bytes(GetLength()) !=
(lseek(getfd(), (off_t)0, SEEK_END) - hdrsize)) {
PrintMsg(_MGET_(
"AudioFile:Close()...inconsistent length\n"),
Fatal);
}
err = (AudioError) audio_rewrite_filesize(getfd(), FILE_AU,
(uint_t)GetHeader().Time_to_Bytes(GetLength()), 0, 0);
}
err = AudioUnixfile::Close();
if (mapaddr) {
munmap(mapaddr, (int)maplen);
mapaddr = 0;
maplen = 0;
}
hdrsize = 0;
seekpos = 0;
return (RaiseError(err));
}
AudioError AudioFile::
ReadData(
void* buf,
size_t& len,
Double& pos)
{
off_t offset;
size_t cnt;
caddr_t cp;
AudioError err;
if (mapaddr == 0) {
err = AudioUnixfile::ReadData(buf, len, pos);
seekpos += len;
return (err);
}
cnt = (size_t)len;
len = 0;
if (!opened() || !GetAccess().Readable())
return (RaiseError(AUDIO_ERR_NOEFFECT));
if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0))
return (RaiseError(AUDIO_ERR_BADARG));
offset = GetHeader().Time_to_Bytes(pos);
if ((offset + hdrsize) >= maplen) {
err = AUDIO_EOF;
err.sys = AUDIO_COPY_INPUT_EOF;
return (err);
} else if ((offset + hdrsize + cnt) > maplen) {
cnt = (size_t)(maplen - (offset + hdrsize));
}
if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
err = AUDIO_SUCCESS;
err.sys = AUDIO_COPY_ZERO_LIMIT;
return (err);
} else {
cp = mapaddr + offset + hdrsize;
memcpy((void*)buf, (void*)cp, cnt);
}
len = cnt;
pos = GetHeader().Bytes_to_Time(offset + len);
coerceEndian((unsigned char *)buf, len, localByteOrder());
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
WriteData(
void* buf,
size_t& len,
Double& pos)
{
AudioError err;
err = AudioUnixfile::WriteData(buf, len, pos);
seekpos += len;
return (err);
}
AudioError AudioFile::
seekread(
Double pos,
off_t& offset)
{
offset = GetHeader().Time_to_Bytes(pos);
if (offset != seekpos) {
if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0)
return (RaiseError(AUDIO_UNIXERROR, Warning));
seekpos = offset;
}
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
seekwrite(
Double pos,
off_t& offset)
{
if (GetAccess().Append() && (pos < GetLength()))
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
if (pos > GetLength()) {
seekwrite(GetLength(), offset);
return (AUDIO_SUCCESS);
}
offset = GetHeader().Time_to_Bytes(pos);
if (offset != seekpos) {
if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0)
return (RaiseError(AUDIO_UNIXERROR, Warning));
seekpos = offset;
}
return (AUDIO_SUCCESS);
}
AudioError AudioFile::
AsyncCopy(
Audio* to,
Double& frompos,
Double& topos,
Double& limit)
{
caddr_t bptr;
size_t offset;
size_t cnt;
size_t svlim;
Double svfrom;
Double svto;
AudioHdr tohdr;
AudioError err;
if ((mapaddr == 0) || to->isBuffer()) {
return (Audio::AsyncCopy(to, frompos, topos, limit));
}
tohdr = to->GetHeader();
err = tohdr.Validate();
if (err != AUDIO_SUCCESS)
return (err);
if (limit < 0.)
return (RaiseError(AUDIO_ERR_BADARG));
svlim = (size_t)tohdr.Time_to_Bytes(limit);
svfrom = GetLength();
if ((frompos >= svfrom) || ((cnt = (size_t)
GetHeader().Time_to_Bytes(svfrom - frompos)) == 0)) {
limit = 0.;
err = AUDIO_EOF;
err.sys = AUDIO_COPY_INPUT_EOF;
return (err);
}
if (!Undefined(limit) && (svlim < cnt))
cnt = svlim;
limit = 0.;
offset = (size_t)GetHeader().Time_to_Bytes(frompos);
if ((offset + hdrsize) >= maplen) {
err = AUDIO_EOF;
err.sys = AUDIO_COPY_INPUT_EOF;
return (err);
} else if ((offset + hdrsize + cnt) > maplen) {
cnt = (size_t)(maplen - (offset + hdrsize));
}
if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
err = AUDIO_SUCCESS;
err.sys = AUDIO_COPY_ZERO_LIMIT;
return (err);
}
svfrom = frompos;
svto = topos;
svlim = cnt;
bptr = mapaddr + hdrsize + offset;
err = to->WriteData(bptr, cnt, topos);
limit = topos - svto;
frompos = svfrom + limit;
if (!err && (cnt < svlim))
err.sys = AUDIO_COPY_SHORT_OUTPUT;
return (err);
}