Files
dvdisaster/large-io.c
Stéphane Lesimple 99b27b982a feat: CLI-only version (without GTK)
Modify the build system and the source
files to support building a CLI-only
version with only glib2 as a dependency.
Use CLI_ONLY=1 ./configure, then make clean all.
2020-08-19 21:21:11 +02:00

338 lines
6.7 KiB
C

/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
*
* Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org
* Project homepage: http://www.dvdisaster.org
*
* This file is part of dvdisaster.
*
* dvdisaster is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dvdisaster is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dvdisaster.h"
/***
*** Wrappers around the standard low level file system interface.
***
* This is pointless for GNU/Linux, but gives us the possibility to
* hide differences in OS-specific semantics.
*
* Note the different return value semantics from standard functions:
* - LargeOpen() returns a LargeFile pointer on success and NULL otherwise;
* - LargeRead() and LargeWrite() return the number of bytes read/written;
* - the remaining functions return True on success or False on failure.
*
* Also, individual behaviour may deviate from standard functions.
*/
/*
* convert special chars in file names to correct OS encoding
*/
static gchar* os_path(char *path_in)
{ gchar *cp_path = g_filename_from_utf8(path_in, -1, NULL, NULL, NULL);
if(cp_path == NULL)
{ errno = EINVAL;
return NULL;
}
REMEMBER(cp_path);
return cp_path;
}
/*
* Large stat replacement (queries only file size)
*/
int LargeStat(char *path, guint64 *length_return)
{ struct stat mystat;
gchar *cp_path = os_path(path);
if(!cp_path) return FALSE;
if(stat(cp_path, &mystat) == -1)
{ g_free(cp_path);
return FALSE;
}
g_free(cp_path);
if(!S_ISREG(mystat.st_mode))
return FALSE;
*length_return = mystat.st_size;
return TRUE;
}
/*
* Stat() variant for testing directories
*/
int DirStat(char *path)
{ struct stat mystat;
gchar *cp_path = os_path(path);
if(!cp_path) return FALSE;
if(stat(cp_path, &mystat) == -1)
{ g_free(cp_path);
return FALSE;
}
g_free(cp_path);
if(!S_ISDIR(mystat.st_mode))
return FALSE;
return TRUE;
}
/*
* Open a file
*/
LargeFile* LargeOpen(char *name, int flags, mode_t mode)
{ LargeFile *lf = g_malloc0(sizeof(LargeFile));
struct stat mystat;
gchar *cp_path;
#ifdef HAVE_O_LARGEFILE
flags |= O_LARGEFILE;
#endif
cp_path = os_path(name);
if(!cp_path)
{ g_free(lf); return FALSE;
}
/* Do not try to open directories etc. */
if( (stat(cp_path, &mystat) == 0)
&& !S_ISREG(mystat.st_mode))
{ g_free(cp_path), g_free(lf); return NULL;
}
lf->fileHandle = open(cp_path, flags, mode);
g_free(cp_path);
if(lf->fileHandle == -1)
{ g_free(lf); return NULL;
}
lf->path = g_strdup(name);
LargeStat(name, &lf->size); /* Do NOT use cp_path! */
lf->flags = flags;
return lf;
}
/*
* Seeking in large files.
* Note: Seeking beyond the end of a split file is undefined.
*/
int LargeSeek(LargeFile *lf, off_t pos)
{
lf->offset = pos;
if(lseek(lf->fileHandle, pos, SEEK_SET) != pos)
return FALSE;
return TRUE;
}
/*
* EOF predicate for large files.
*
* Note: Works only correctly for read only files!
*/
int LargeEOF(LargeFile *lf)
{
return lf->offset >= lf->size;
}
/*
* Reading large files
*/
ssize_t LargeRead(LargeFile *lf, void *buf, size_t count)
{ ssize_t n;
n = read(lf->fileHandle, buf, count);
lf->offset += n;
return n;
}
/*
* Writing large files
*/
#ifndef CLI
static void insert_buttons(GtkDialog *dialog)
{
gtk_dialog_add_buttons(dialog,
GTK_STOCK_REDO , 1,
GTK_STOCK_CANCEL, 0, NULL);
}
#endif
static ssize_t xwrite(int fdes, void *buf_base, size_t count)
{ unsigned char *buf = (unsigned char*)buf_base;
ssize_t total = 0;
/* Simply fail when going out of space in command line mode */
#ifndef CLI
if(!Closure->guiMode)
#endif
{ while(count)
{ ssize_t n = write(fdes, buf, count);
if(n<=0) return total; /* error occurred */
if(n>0) /* write at least partially successful */
{ total += n;
count -= n;
buf += n;
}
}
return total;
}
#ifndef CLI
/* Give the user a chance to free more space in GUI mode.
When running out of space, the last write() may complete
with n<count but no error condition, so we try writing
until a real error hits (n = -1). */
while(count)
{ ssize_t n = write(fdes, buf, count);
if(n <= 0) /* error occurred */
{ int answer;
if(errno != ENOSPC) return total;
answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_buttons,
_("Error while writing the file:\n\n%s\n\n"
"You can redo this operation after freeing some space."),
strerror(errno),n);
if(!answer) return total;
}
if(n>0) /* write at least partially successful */
{ total += n;
count -= n;
buf += n;
}
}
return total;
#endif
}
ssize_t LargeWrite(LargeFile *lf, void *buf, size_t count)
{ ssize_t n;
n = xwrite(lf->fileHandle, buf, count);
lf->offset += n;
return n;
}
/*
* Large file closing
*/
int LargeClose(LargeFile *lf)
{ int result = TRUE;
result = (close(lf->fileHandle) == 0);
/* Free the LargeFile struct and return results */
if(lf->path) g_free(lf->path);
g_free(lf);
return result;
}
/*
* Large file truncation
*/
int LargeTruncate(LargeFile *lf, off_t length)
{ int result;
result = (ftruncate(lf->fileHandle, length) == 0);
if(result)
lf->size = length;
return result;
}
/*
* Large file unlinking
*/
int LargeUnlink(char *path)
{ gchar *cp_path;
int result;
cp_path = os_path(path);
if(!cp_path) return FALSE;
result = unlink(cp_path);
g_free(cp_path);
return result == 0;
}
/***
*** Wrappers around other IO
***/
FILE *portable_fopen(char *path, char *modes)
{ FILE *file;
char *cp_path;
cp_path = os_path(path);
file = fopen(cp_path, modes);
g_free(cp_path);
return file;
}
/***
*** Convenience functions
***/
/*
* Append the given file suffix if none is already present
*/
char *ApplyAutoSuffix(char *filename, char *suffix)
{ char *out;
if(!filename || !*filename || strrchr(filename, '.'))
return filename;
out = g_strdup_printf("%s.%s",filename,suffix);
g_free(filename);
return out;
}