/* misc.c
*
* Copyright (C) 1996-2025 Timo Kokkonen
* All Rights Reserved.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of JPEGoptim.
*
* JPEGoptim 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.
*
* JPEGoptim 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 JPEGoptim. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#ifdef HAVE_UNISTD_H
#include
#endif
#include
#include
#include
#include
#include "jpegoptim.h"
FILE* create_file(const char *name)
{
FILE *f;
int fd;
if (!name)
return NULL;
#ifdef WIN32
fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, _S_IREAD | _S_IWRITE);
#else
fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR);
#endif
if (fd < 0)
return NULL;
if (!(f = fdopen(fd, "wb"))) {
close(fd);
return NULL;
}
return f;
}
FILE *create_temp_file(const char *tmpdir, const char *name, char *filename, size_t filename_len)
{
FILE *f;
int newlen;
#ifdef HAVE_MKSTEMPS
/* Rely on mkstemps() to create us temporary file safely... */
newlen = snprintf(filename, filename_len, "%s%s-%u-%u.XXXXXX.tmp",
tmpdir, name, getuid(), getpid());
#else
/* If platform is missing mkstemps(), try to create at least somewhat "safe" temp file... */
newlen = snprintf(filename, filename_len, "%s%s-%u-%u.%lu.tmp",
tmpdir, name, getuid(), getpid(), (unsigned long)time(NULL));
#endif
if (newlen >= filename_len) {
warn("temp filename too long: %s", filename);
return NULL;
}
#ifdef HAVE_MKSTEMPS
int tmpfd = mkstemps(filename, 4);
if (tmpfd < 0) {
warn("error creating temp file: mkstemps('%s', 4) failed", filename);
return NULL;
}
f = fdopen(tmpfd, "wb");
#else
f = create_file(filename);
#endif
return f;
}
int delete_file(const char *name)
{
int retval;
if (!name)
return -1;
if (verbose_mode > 1 && !quiet_mode)
fprintf(stderr,"deleting: %s\n",name);
if ((retval=unlink(name)) && !quiet_mode)
warn("error removing file: %s",name);
return retval;
}
long file_size(FILE *fp)
{
struct stat buf;
if (!fp)
return -1;
if (fstat(fileno(fp),&buf) != 0)
return -2;
return (long)buf.st_size;
}
int is_directory(const char *pathname)
{
struct stat buf;
if (!pathname)
return 0;
if (stat(pathname,&buf) != 0)
return 0;
return (S_ISDIR(buf.st_mode) ? 1 : 0);
}
int is_file(const char *filename, struct stat *st)
{
struct stat buf;
if (!filename)
return 0;
if (lstat(filename,&buf) != 0)
return 0;
if (st)
*st=buf;
return (S_ISREG(buf.st_mode) ? 1 : 0);
}
int file_exists(const char *pathname)
{
struct stat buf;
if (!pathname)
return 0;
return (stat(pathname,&buf) == 0 ? 1 : 0);
}
int rename_file(const char *old_path, const char *new_path)
{
if (!old_path || !new_path)
return -1;
#ifdef WIN32
if (file_exists(new_path))
delete_file(new_path);
#endif
return rename(old_path,new_path);
}
#define COPY_BUF_SIZE (256 * 1024)
int copy_file(const char *srcfile, const char *dstfile)
{
FILE *in,*out;
unsigned char *buf;
int r,w;
int err=0;
if (!srcfile || !dstfile)
return -1;
if (!(in = fopen(srcfile, "rb"))) {
warn("failed to open file for reading: %s", srcfile);
return -2;
}
if (!(out = create_file(dstfile))) {
fclose(in);
warn("failed to open file for writing: %s", dstfile);
return -3;
}
if (!(buf = calloc(COPY_BUF_SIZE, 1)))
fatal("out of memory");
do {
r = fread(buf, 1, COPY_BUF_SIZE, in);
if (r > 0) {
w = fwrite(buf, 1, r, out);
if (w != r) {
err=1;
warn("error writing to file: %s", dstfile);
break;
}
} else {
if (ferror(in)) {
err=2;
warn("error reading from file: %s", srcfile);
break;
}
}
} while (!feof(in));
fclose(out);
fclose(in);
free(buf);
return err;
}
char *fgetstr(char *s, size_t size, FILE *stream)
{
char *p;
if (!s || size < 1 || !stream)
return NULL;
if (!fgets(s, size, stream))
return NULL;
p = s + strnlen(s, size) - 1;
while ((p >= s) && ((*p == 10) || (*p == 13)))
*p--=0;
return s;
}
char *splitdir(const char *pathname, char *buf, size_t size)
{
char *s;
int len = 0;
if (!pathname || !buf || size < 1)
return NULL;
if ((s = strrchr(pathname, DIR_SEPARATOR_C)))
len = (s - pathname) + 1;
if (len >= size)
return NULL;
if (len > 0)
memcpy(buf, pathname, len);
buf[len] = 0;
return buf;
}
char *splitname(const char *pathname, char *buf, size_t size)
{
const char *s = NULL;
int len;
if (!pathname || !buf || size < 1)
return NULL;
if ((s = strrchr(pathname, DIR_SEPARATOR_C)))
s++;
else
s=pathname;
if ((len = strlen(s)) >= size)
return NULL;
if (len > 0)
memcpy(buf, s, len);
buf[len] = 0;
return buf;
}
char *strncopy(char *dst, const char *src, size_t size)
{
if (!dst || !src || size < 1)
return dst;
if (size > 1)
strncpy(dst, src, size - 1);
dst[size - 1] = 0;
return dst;
}
char *strncatenate(char *dst, const char *src, size_t size)
{
int used, free;
if (!dst || !src || size < 1)
return dst;
/* Check if dst string is already "full" ... */
used = strnlen(dst, size);
if ((free = size - used) <= 1)
return dst;
return strncat(dst + used, src, free - 1);
}
char *str_add_list(char *dst, size_t size, const char *src, const char *delim)
{
if (!dst || !src || !delim || size < 1)
return dst;
if (strnlen(dst, size) > 0)
strncatenate(dst, delim, size);
return strncatenate(dst, src, size);
}
void fatal(const char *format, ...)
{
va_list args;
fprintf(stderr, PROGRAMNAME ": ");
va_start(args,format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr,"\n");
fflush(stderr);
exit(3);
}
void warn(const char *format, ...)
{
va_list args;
if (quiet_mode) return;
fprintf(stderr, PROGRAMNAME ": ");
va_start(args,format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr,"\n");
fflush(stderr);
}
/* eof :-) */