XpmWrite.c revision 74835918
1/* 2 * Copyright (c) 2023, Oracle and/or its affiliates. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24#include "config.h" 25 26#include <X11/xpm.h> 27 28#include <glib.h> 29#include <glib/gstdio.h> 30 31#include <stdio.h> 32#include <string.h> 33#include <sys/types.h> 34#include <sys/stat.h> 35#include <fcntl.h> 36#include <unistd.h> 37 38#include "TestAllFiles.h" 39#include "CompareXpmImage.h" 40 41#ifndef g_assert_no_errno /* defined in glib 2.66 & later */ 42#define g_assert_no_errno(n) g_assert_cmpint(n, >=, 0) 43#endif 44 45/* 46 * Check if a filename ends in ".Z" or ".gz" 47 */ 48static inline gboolean 49is_compressed(const char *filepath) 50{ 51 const char *ext = strrchr(filepath, '.'); 52 53 if ((ext != NULL) && 54 (((ext[1] == 'Z') && (ext[2] == 0)) || 55 ((ext[1] == 'g') && (ext[2] == 'z') && (ext[3] == 0)))) { 56 return TRUE; 57 } 58 59 return FALSE; 60} 61 62/* 63 * If a filename ends in ".Z" or ".gz", remove that extension to avoid 64 * confusing libXpm into applying compression when not desired. 65 */ 66static inline void 67strip_compress_ext(char *filepath) 68{ 69 char *ext = strrchr(filepath, '.'); 70 71 if ((ext != NULL) && 72 (((ext[1] == 'Z') && (ext[2] == 0)) || 73 ((ext[1] == 'g') && (ext[2] == 'z') && (ext[3] == 0)))) { 74 *ext = '\0'; 75 } 76} 77 78/* 79 * XpmWriteFileFromXpmImage - Write XPM files without requiring an X Display 80 */ 81static void 82test_WFFXI_helper(const gchar *newfilepath, XpmImage *imageA, XpmInfo *infoA) 83{ 84 XpmImage imageB; 85 XpmInfo infoB; 86 int status; 87 88 g_test_message("...writing %s", newfilepath); 89 90 status = XpmWriteFileFromXpmImage(newfilepath, imageA, infoA); 91 g_assert_cmpint(status, ==, XpmSuccess); 92 93 if (is_compressed(newfilepath)) { 94 /* Wait a moment for the compression command to finish writing, 95 * since OpenWriteFile() does a double fork so we can't just wait 96 * for the child command to exit. 97 */ 98 usleep(10000); 99 } 100 101 status = XpmReadFileToXpmImage(newfilepath, &imageB, &infoB); 102 g_assert_cmpint(status, ==, XpmSuccess); 103 104 CompareXpmImage(imageA, &imageB); 105 XpmFreeXpmImage(&imageB); 106 XpmFreeXpmInfo(&infoB); 107 108 status = remove(newfilepath); 109 g_assert_no_errno(status); 110 111} 112 113static int 114TestWriteFileFromXpmImage(const gchar *filepath) 115{ 116 XpmImage imageA; 117 XpmInfo infoA; 118 int status; 119 gchar *testdir, *filename, *newfilepath; 120 GError *err = NULL; 121 122#ifndef NO_ZPIPE 123 gchar *cmpfilepath; 124#endif 125 126 status = XpmReadFileToXpmImage(filepath, &imageA, &infoA); 127 g_assert_cmpint(status, ==, XpmSuccess); 128 129 testdir = g_dir_make_tmp("XpmWrite-test-XXXXXX", &err); 130 g_assert_no_error(err); 131 132 filename = g_path_get_basename(filepath); 133 strip_compress_ext(filename); 134 newfilepath = g_build_filename(testdir, filename, NULL); 135 136 test_WFFXI_helper(newfilepath, &imageA, &infoA); 137 138#ifndef NO_ZPIPE 139 cmpfilepath = g_strdup_printf("%s.gz", newfilepath); 140 test_WFFXI_helper(cmpfilepath, &imageA, &infoA); 141 g_free(cmpfilepath); 142 143#ifdef XPM_PATH_COMPRESS 144 cmpfilepath = g_strdup_printf("%s.Z", newfilepath); 145 test_WFFXI_helper(cmpfilepath, &imageA, &infoA); 146 g_free(cmpfilepath); 147#endif 148#endif 149 150 XpmFreeXpmImage(&imageA); 151 XpmFreeXpmInfo(&infoA); 152 153 g_assert_no_errno(g_rmdir(testdir)); 154 155 g_free(newfilepath); 156 g_free(filename); 157 g_free(testdir); 158 159 return status; 160} 161 162static void 163test_XpmWriteFileFromXpmImage(void) 164{ 165 /* Todo: verify trying to write to an unwritable file fails */ 166 167 TestAllNormalFiles("good", XpmSuccess, TestWriteFileFromXpmImage); 168 /* XpmReadFileToXpmImage supports compressed files */ 169 TestAllCompressedFiles("good", XpmSuccess, TestWriteFileFromXpmImage); 170} 171 172/* 173 * XpmWriteFileFromData - wrapper around XpmWriteFileFromXpmImage that 174 * converts the image into a list of strings. 175 */ 176static void 177test_WFFXD_helper(const gchar *newfilepath, char **dataA) 178{ 179 char **dataB; 180 int status; 181 182 g_test_message("...writing %s", newfilepath); 183 184 status = XpmWriteFileFromData(newfilepath, dataA); 185 g_assert_cmpint(status, ==, XpmSuccess); 186 187 if (is_compressed(newfilepath)) { 188 /* Wait a moment for the compression command to finish writing, 189 * since OpenWriteFile() does a double fork so we can't just wait 190 * for the child command to exit. 191 */ 192 usleep(10000); 193 } 194 195 status = XpmReadFileToData(newfilepath, &dataB); 196 g_assert_cmpint(status, ==, XpmSuccess); 197 198 /* Todo: compare data fields */ 199 XpmFree(dataB); 200 201 status = remove(newfilepath); 202 g_assert_no_errno(status); 203 204} 205 206static int 207TestWriteFileFromData(const gchar *filepath) 208{ 209 char **data = NULL; 210 int status; 211 gchar *testdir, *filename, *newfilepath; 212 GError *err = NULL; 213 214#ifndef NO_ZPIPE 215 gchar *cmpfilepath; 216#endif 217 218 status = XpmReadFileToData(filepath, &data); 219 g_assert_cmpint(status, ==, XpmSuccess); 220 221 testdir = g_dir_make_tmp("XpmWrite-test-XXXXXX", &err); 222 g_assert_no_error(err); 223 224 filename = g_path_get_basename(filepath); 225 strip_compress_ext(filename); 226 newfilepath = g_build_filename(testdir, filename, NULL); 227 228 test_WFFXD_helper(newfilepath, data); 229 230#ifndef NO_ZPIPE 231 cmpfilepath = g_strdup_printf("%s.gz", newfilepath); 232 test_WFFXD_helper(cmpfilepath, data); 233 g_free(cmpfilepath); 234 235#ifdef XPM_PATH_COMPRESS 236 cmpfilepath = g_strdup_printf("%s.Z", newfilepath); 237 test_WFFXD_helper(cmpfilepath, data); 238 g_free(cmpfilepath); 239#endif 240#endif 241 242 XpmFree(data); 243 244 g_assert_no_errno(g_rmdir(testdir)); 245 246 g_free(newfilepath); 247 g_free(filename); 248 g_free(testdir); 249 250 return status; 251} 252 253static void 254test_XpmWriteFileFromData(void) 255{ 256 /* Todo - verify trying to write to an unwritable file fails */ 257 258 TestAllNormalFiles("good", XpmSuccess, TestWriteFileFromData); 259 /* XpmReadFileToData calls XpmReadFileToXpmImage so it 260 supports compressed files */ 261 TestAllCompressedFiles("good", XpmSuccess, TestWriteFileFromData); 262} 263 264/* 265 * XpmWriteFileFromBuffer - helper function to write files & read them back in 266 * XpmWriteFileFromBuffer() does not support compressed files. 267 */ 268static int 269TestWriteFileFromBuffer(const gchar *filepath) 270{ 271 char *buffer = NULL; 272 gchar *testdir, *filename, *newfilepath; 273 GError *err = NULL; 274 int status; 275 276 status = XpmReadFileToBuffer(filepath, &buffer); 277 g_assert_cmpint(status, ==, XpmSuccess); 278 g_assert_nonnull(buffer); 279 280 testdir = g_dir_make_tmp("XpmWrite-test-XXXXXX", &err); 281 g_assert_no_error(err); 282 283 filename = g_path_get_basename(filepath); 284 strip_compress_ext(filename); 285 newfilepath = g_build_filename(testdir, filename, NULL); 286 g_test_message("...writing %s", newfilepath); 287 288 status = XpmWriteFileFromBuffer(newfilepath, buffer); 289 g_assert_cmpint(status, ==, XpmSuccess); 290 291 if (status == XpmSuccess) { 292 char readbuf[8192]; 293 char *b = buffer; 294 int fd; 295 ssize_t rd; 296 297 /* Read file ourselves and verify the data matches */ 298 g_assert_no_errno(fd = open(newfilepath, O_RDONLY)); 299 while ((rd = read(fd, readbuf, sizeof(readbuf))) > 0) { 300 g_assert_cmpmem(b, rd, readbuf, rd); 301 b += rd; 302 } 303 /* Verify we're at the end of the buffer */ 304 g_assert_cmpint(b[0], ==, '\0'); 305 306 g_assert_no_errno(close(fd)); 307 g_assert_no_errno(remove(newfilepath)); 308 } 309 XpmFree(buffer); 310 311 g_assert_no_errno(g_rmdir(testdir)); 312 313 g_free(newfilepath); 314 g_free(filename); 315 g_free(testdir); 316 317 return status; 318} 319 320static void 321test_XpmWriteFileFromBuffer(void) 322{ 323 /* Todo: verify trying to write to an unwritable file fails */ 324 325 TestAllNormalFiles("good", XpmSuccess, TestWriteFileFromBuffer); 326 /* XpmReadFileToBuffer does not support compressed files */ 327} 328 329int 330main(int argc, char** argv) 331{ 332 g_test_init(&argc, &argv, NULL); 333 g_test_bug_base(PACKAGE_BUGREPORT); 334 335 336 g_test_add_func("/XpmRead/XpmWriteFileFromXpmImage", 337 test_XpmWriteFileFromXpmImage); 338 g_test_add_func("/XpmRead/XpmWriteFileFromData", 339 test_XpmWriteFileFromData); 340 g_test_add_func("/XpmRead/XpmWriteFileFromBuffer", 341 test_XpmWriteFileFromBuffer); 342 343 return g_test_run(); 344} 345