Coverage Report

Created: 2026-09-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jpegoptim/misc.c
Line
Count
Source
1
/* misc.c
2
 *
3
 * Copyright (C) 1996-2026 Timo Kokkonen
4
 * All Rights Reserved.
5
 *
6
 * SPDX-License-Identifier: GPL-3.0-or-later
7
 *
8
 * This file is part of JPEGoptim.
9
 *
10
 * JPEGoptim is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * JPEGoptim is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with JPEGoptim. If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
#ifdef HAVE_CONFIG_H
25
#include "config.h"
26
#endif
27
#include <stdio.h>
28
#include <fcntl.h>
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#ifdef HAVE_UNISTD_H
32
#include <unistd.h>
33
#endif
34
#include <string.h>
35
#include <stdarg.h>
36
#include <stdlib.h>
37
#include <time.h>
38
#include <errno.h>
39
40
41
#include "jpegoptim.h"
42
43
#ifndef O_NOFOLLOW
44
#define O_NOFOLLOW 0
45
#endif
46
47
48
FILE* create_file(const char *name)
49
0
{
50
0
  FILE *f;
51
0
  int fd;
52
53
0
  if (!name)
54
0
    return NULL;
55
56
#ifdef WIN32
57
  fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, _S_IREAD | _S_IWRITE);
58
#else
59
  /* O_NOFOLLOW prevents symlink attacks via planted (dangling) symlinks
60
     in world-writable directories... */
61
0
  fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, S_IWUSR | S_IRUSR);
62
0
#endif
63
0
  if (fd < 0)
64
0
    return NULL;
65
0
  if (!(f = fdopen(fd, "wb"))) {
66
0
    close(fd);
67
0
    return NULL;
68
0
  }
69
70
0
  return f;
71
0
}
72
73
74
FILE *create_temp_file(const char *tmpdir, const char *name, char *filename, size_t filename_len)
75
0
{
76
0
  FILE *f;
77
0
  int newlen;
78
0
  int tmpfd;
79
80
0
#ifdef HAVE_MKSTEMPS
81
  /* Rely on mkstemps() to create us temporary file safely... */
82
0
  newlen = snprintf(filename, filename_len, "%s%s-%u-%u.XXXXXX.tmp",
83
0
      tmpdir, name, getuid(), getpid());
84
0
  if (newlen < 0 || (size_t)newlen >= filename_len) {
85
0
    warn("temp filename too long: %s", filename);
86
0
    return NULL;
87
0
  }
88
0
  tmpfd = mkstemps(filename, 4);
89
0
  if (tmpfd < 0) {
90
0
    warn("error creating temp file: mkstemps('%s', 4) failed", filename);
91
0
    return NULL;
92
0
  }
93
#else
94
  /* If platform is missing mkstemps(), create temp file exclusively (O_EXCL),
95
     retrying with a new name if the file already exists. O_EXCL prevents
96
     clobbering an existing (attacker planted) file or following a symlink... */
97
  tmpfd = -1;
98
  for (int i = 0; i < 100; i++) {
99
    newlen = snprintf(filename, filename_len, "%s%s-%u-%u.%lx%04x.tmp",
100
        tmpdir, name, getuid(), getpid(),
101
        (unsigned long)time(NULL), rand() & 0xffff);
102
    if (newlen < 0 || (size_t)newlen >= filename_len) {
103
      warn("temp filename too long: %s", filename);
104
      return NULL;
105
    }
106
#ifdef WIN32
107
    tmpfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY,
108
      _S_IREAD | _S_IWRITE);
109
#else
110
    tmpfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW,
111
      S_IWUSR | S_IRUSR);
112
#endif
113
    if (tmpfd >= 0 || errno != EEXIST)
114
      break;
115
  }
116
  if (tmpfd < 0) {
117
    warn("error creating temp file: '%s'", filename);
118
    return NULL;
119
  }
120
#endif
121
122
0
  if (!(f = fdopen(tmpfd, "wb")))
123
0
    close(tmpfd);
124
125
0
  return f;
126
0
}
127
128
129
int delete_file(const char *name)
130
0
{
131
0
  int retval;
132
133
0
  if (!name)
134
0
    return -1;
135
136
0
  if (verbose_mode > 1 && !quiet_mode)
137
0
    fprintf(stderr,"deleting: %s\n",name);
138
0
  if ((retval=unlink(name)) && !quiet_mode)
139
0
    warn("error removing file: %s",name);
140
141
0
  return retval;
142
0
}
143
144
145
long file_size(FILE *fp)
146
4
{
147
4
  struct stat buf;
148
149
4
  if (!fp)
150
0
    return -1;
151
4
  if (fstat(fileno(fp),&buf) != 0)
152
0
    return -2;
153
154
4
  return (long)buf.st_size;
155
4
}
156
157
158
int is_directory(const char *pathname)
159
0
{
160
0
  struct stat buf;
161
162
0
  if (!pathname)
163
0
    return 0;
164
165
0
  if (stat(pathname,&buf) != 0)
166
0
    return 0;
167
168
0
  return (S_ISDIR(buf.st_mode) ? 1 : 0);
169
0
}
170
171
172
int is_file(const char *filename, struct stat *st)
173
0
{
174
0
  struct stat buf;
175
176
0
  if (!filename)
177
0
    return 0;
178
179
0
  if (lstat(filename,&buf) != 0)
180
0
    return 0;
181
0
  if (st)
182
0
    *st=buf;
183
184
0
  return (S_ISREG(buf.st_mode) ? 1 : 0);
185
0
}
186
187
188
int file_exists(const char *pathname)
189
0
{
190
0
  struct stat buf;
191
192
0
  if (!pathname)
193
0
    return 0;
194
195
0
  return (stat(pathname,&buf) == 0 ? 1 : 0);
196
0
}
197
198
199
int rename_file(const char *old_path, const char *new_path)
200
0
{
201
0
  if (!old_path || !new_path)
202
0
    return -1;
203
#ifdef WIN32
204
  if (file_exists(new_path))
205
    delete_file(new_path);
206
#endif
207
0
  return rename(old_path,new_path);
208
0
}
209
210
211
0
#define COPY_BUF_SIZE  (256 * 1024)
212
213
int copy_file(const char *srcfile, const char *dstfile)
214
0
{
215
0
  FILE *in,*out;
216
0
  unsigned char *buf;
217
0
  int r,w;
218
0
  int err=0;
219
220
0
  if (!srcfile || !dstfile)
221
0
    return -1;
222
223
0
  if (!(in = fopen(srcfile, "rb"))) {
224
0
    warn("failed to open file for reading: %s", srcfile);
225
0
    return -2;
226
0
  }
227
0
  if (!(out = create_file(dstfile))) {
228
0
    fclose(in);
229
0
    warn("failed to open file for writing: %s", dstfile);
230
0
    return -3;
231
0
  }
232
233
0
  if (!(buf = calloc(COPY_BUF_SIZE, 1)))
234
0
    fatal("out of memory");
235
236
237
0
  do {
238
0
    r = fread(buf, 1, COPY_BUF_SIZE, in);
239
0
    if (r > 0) {
240
0
      w = fwrite(buf, 1, r, out);
241
0
      if (w != r) {
242
0
        err=1;
243
0
        warn("error writing to file: %s", dstfile);
244
0
        break;
245
0
      }
246
0
    } else {
247
0
      if (ferror(in)) {
248
0
        err=2;
249
0
        warn("error reading from file: %s", srcfile);
250
0
        break;
251
0
      }
252
0
    }
253
0
  } while (!feof(in));
254
255
0
  fclose(out);
256
0
  fclose(in);
257
0
  free(buf);
258
259
0
  return err;
260
0
}
261
262
263
char *fgetstr(char *s, size_t size, FILE *stream)
264
0
{
265
0
  char *p;
266
267
0
  if (!s || size < 1 || !stream)
268
0
    return NULL;
269
270
0
  if (!fgets(s, size, stream))
271
0
    return NULL;
272
273
0
  p = s + strnlen(s, size) - 1;
274
0
  while ((p >= s) && ((*p == 10) || (*p == 13)))
275
0
    *p--=0;
276
277
0
  return s;
278
0
}
279
280
281
char *splitdir(const char *pathname, char *buf, size_t size)
282
0
{
283
0
  char *s;
284
0
  int len = 0;
285
286
0
  if (!pathname || !buf || size < 1)
287
0
    return NULL;
288
289
0
  if ((s = strrchr(pathname, DIR_SEPARATOR_C)))
290
0
    len = (s - pathname) + 1;
291
0
  if (len >= size)
292
0
    return NULL;
293
0
  if (len > 0)
294
0
    memcpy(buf, pathname, len);
295
0
  buf[len] = 0;
296
297
0
  return buf;
298
0
}
299
300
301
char *splitname(const char *pathname, char *buf, size_t size)
302
0
{
303
0
  const char *s = NULL;
304
0
  int len;
305
306
0
  if (!pathname || !buf || size < 1)
307
0
    return NULL;
308
309
0
  if ((s = strrchr(pathname, DIR_SEPARATOR_C)))
310
0
    s++;
311
0
  else
312
0
    s=pathname;
313
314
0
  if ((len = strlen(s)) >= size)
315
0
    return NULL;
316
0
  if (len > 0)
317
0
    memcpy(buf, s, len);
318
0
  buf[len] = 0;
319
320
0
  return buf;
321
0
}
322
323
324
char *strncopy(char *dst, const char *src, size_t size)
325
5
{
326
5
  if (!dst || !src || size < 1)
327
0
    return dst;
328
329
5
  if (size > 1)
330
5
    strncpy(dst, src, size - 1);
331
5
  dst[size - 1] = 0;
332
333
5
  return dst;
334
5
}
335
336
337
char *strncatenate(char *dst, const char *src, size_t size)
338
169
{
339
169
  int used, free;
340
341
169
  if (!dst || !src || size < 1)
342
0
    return dst;
343
344
  /* Check if dst string is already "full" ... */
345
169
  used = strnlen(dst, size);
346
169
  if ((free = size - used) <= 1)
347
67
    return dst;
348
349
102
  return strncat(dst + used, src, free - 1);
350
169
}
351
352
353
void fprint_csv_field(FILE *fp, const char *str)
354
0
{
355
0
  if (!fp || !str)
356
0
    return;
357
358
  /* Don't quote the field if does not contain characters that
359
     need quoting (RFC 4180) */
360
0
  if (strpbrk(str, "\",\r\n") == NULL) {
361
0
    fputs(str, fp);
362
0
    return;
363
0
  }
364
365
0
  fputc('"', fp);
366
0
  while (*str) {
367
0
    if (*str == '"')
368
0
      fputc('"', fp);
369
0
    fputc(*str++, fp);
370
0
  }
371
0
  fputc('"', fp);
372
0
}
373
374
375
char *str_add_list(char *dst, size_t size, const char *src, const char *delim)
376
85
{
377
85
  if (!dst || !src || !delim || size < 1)
378
0
    return dst;
379
380
85
  if (strnlen(dst, size) > 0)
381
84
    strncatenate(dst, delim, size);
382
383
85
  return strncatenate(dst, src, size);
384
85
}
385
386
387
void fatal(const char *format, ...)
388
0
{
389
0
  va_list args;
390
391
0
  fprintf(stderr, PROGRAMNAME ": ");
392
0
  va_start(args,format);
393
0
  vfprintf(stderr, format, args);
394
0
  va_end(args);
395
0
  fprintf(stderr,"\n");
396
0
  fflush(stderr);
397
398
0
  exit(3);
399
0
}
400
401
402
void warn(const char *format, ...)
403
0
{
404
0
  va_list args;
405
406
0
  if (quiet_mode) return;
407
408
0
  fprintf(stderr, PROGRAMNAME ": ");
409
0
  va_start(args,format);
410
0
  vfprintf(stderr, format, args);
411
0
  va_end(args);
412
0
  fprintf(stderr,"\n");
413
  fflush(stderr);
414
0
}
415
416
417
/* eof :-) */