/* * Gregorio is a program that translates gabc files to GregorioTeX * This file contains miscellaneous support functions. * * Copyright (C) 2015-2019 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. * * Gregorio 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. * * Gregorio 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 Gregorio. If not, see . */ #include "config.h" #include #include #include #include #include #include #include "support.h" #include "struct.h" #include "messages.h" /* Our version of snprintf; this is NOT semantically the same as C99's * snprintf; rather, it's a "lowest common denominator" implementation * blending C99 and MS-C */ void gregorio_snprintf(char *s, size_t size, const char *format, ...) { va_list args; #ifdef _MSC_VER memset(s, 0, size); #endif va_start(args, format); #ifdef _MSC_VER _vsnprintf_s(s, size, _TRUNCATE, format, args); #else vsnprintf(s, size, format, args); #endif va_end(args); } static __inline void *assert_successful_allocation(void *ptr, char *funcname) { if (!ptr) { /* it's not realistic to test this for coverage */ /* LCOV_EXCL_START */ gregorio_message(_("error in memory allocation"), funcname, VERBOSITY_FATAL, 0); gregorio_exit(1); } /* LCOV_EXCL_STOP */ return ptr; } void *gregorio_malloc(size_t size) { return assert_successful_allocation(malloc(size), "gregorio_malloc"); } void *gregorio_calloc(size_t nmemb, size_t size) { return assert_successful_allocation(calloc(nmemb, size), "gregorio_calloc"); } void *gregorio_realloc(void *ptr, size_t size) { return assert_successful_allocation(realloc(ptr, size), "gregorio_realloc"); } char *gregorio_strdup(const char *s) { return (char *)assert_successful_allocation(strdup(s), "gregorio_strdup"); } void *_gregorio_grow_buffer(void *buffer, size_t *nmemb, size_t size) { if (buffer == NULL) { return gregorio_malloc(*nmemb * size); } if (*nmemb >= MAX_BUF_GROWTH) { /* it's not realistic to test this case */ /* LCOV_EXCL_START */ gregorio_message(_("buffer too large"), "gregorio_grow_buffer", VERBOSITY_FATAL, 0); gregorio_exit(1); /* LCOV_EXCL_STOP */ } *nmemb <<= 1; return gregorio_realloc(buffer, *nmemb * size); } #ifdef USE_KPSE static kpathsea kpse = NULL; #define USED_FOR_KPSE #else #define USED_FOR_KPSE __attribute__((unused)) #endif void gregorio_support_init(const char *const program USED_FOR_KPSE, const char *const argv0 USED_FOR_KPSE) { gregorio_set_error_out(stderr); gregorio_set_verbosity_mode(VERBOSITY_ERROR); gregorio_struct_init(); #ifdef USE_KPSE kpse = kpathsea_new(); kpathsea_set_program_name(kpse, argv0, program); #endif } void gregorio_print_version(const char *copyright) { #ifdef USE_KPSE printf("Gregorio %s (%s).\n%s\n", GREGORIO_VERSION, kpathsea_version_string, copyright); #else printf("Gregorio %s.\n%s\n", GREGORIO_VERSION, copyright); #endif printf("License GPLv3+: GNU GPL version 3 or later \n\ This is free software: you are free to change and redistribute it.\n\ There is NO WARRANTY, to the extent permitted by law.\n"); } #ifdef USE_KPSE bool gregorio_read_ok(const char *const filename, const gregorio_verbosity verbosity) { if (kpathsea_in_name_ok_silent(kpse, filename)) { return true; } gregorio_messagef("gregorio_in_name_ok", verbosity, __LINE__, _("kpse prohibits read from file %s"), filename); return false; } bool gregorio_write_ok(const char *const filename, const gregorio_verbosity verbosity) { if (kpathsea_out_name_ok_silent(kpse, filename)) { return true; } gregorio_messagef("gregorio_out_name_ok", verbosity, __LINE__, _("kpse prohibits write to file %s"), filename); return false; } #endif #ifndef USE_KPSE static __inline void rtrim(char *buf) { char *p; for (p = buf + strlen(buf) - 1; p >= buf && isspace(*p); --p) { *p = '\0'; } } static bool gregorio_readline(char **buf, size_t *bufsize, FILE *file) { size_t oldsize; if (*buf == NULL) { *bufsize = 128; *buf = gregorio_grow_buffer(NULL, bufsize, char); } else { if (*bufsize < 128) { /* not reachable unless there's a programming error */ /* LCOV_EXCL_START */ gregorio_message(_("invalid buffer size"), "gregorio_getline", VERBOSITY_FATAL, 0); gregorio_exit(1); /* LCOV_EXCL_STOP */ } } (*buf)[0] = '\0'; oldsize = 1; for (;;) { (*buf)[*bufsize - 2] = '\0'; if (feof(file) || ferror(file) || !fgets((*buf) + oldsize - 1, (int)(*bufsize - oldsize + 1), file) || (*buf)[*bufsize - 2] == '\0') { if (ferror(file)) { /* it's not realistic to simulate the system error required to * cover this case */ /* LCOV_EXCL_START */ gregorio_message(_("Error reading from the file"), "gregorio_getline", VERBOSITY_FATAL, 0); gregorio_exit(1); /* LCOV_EXCL_STOP */ } return (*buf)[0] != '\0'; } oldsize = *bufsize; *buf = gregorio_grow_buffer(*buf, bufsize, char); } } #endif char **gregorio_kpse_find(const char *filename) { char **filenames; #ifdef USE_KPSE filenames = kpathsea_find_file_generic(kpse, filename, kpse_tex_format, true, true); if (!filenames) { /* It's not reasonable to break kpse in such a way that this would * fail. */ /* LCOV_EXCL_START */ gregorio_messagef("gregorio_kpse_find", VERBOSITY_WARNING, 0, _("kpathsea_find_file_generic returned NULL: %s"), filename); return NULL; /* LCOV_EXCL_STOP */ } #else FILE *file; size_t bufsize = 0; char *buf = NULL; size_t capacity = 16, size = 0; #define KPSE_COMMAND "kpsewhich -must-exist -all " size_t command_size = sizeof(KPSE_COMMAND) + strlen(filename); char *command = gregorio_malloc(command_size); strcpy(command, KPSE_COMMAND); strcpy(command + sizeof(KPSE_COMMAND) - 1, filename); file = popen(command, "r"); free(command); command = NULL; /* just to be sure */ if (!file) { /* it's not reasonable to cause popen to fail */ /* LCOV_EXCL_START */ gregorio_messagef("gregorio_kpse_find", VERBOSITY_WARNING, 0, _("unable to run kpsewhich %s: %s"), filename, strerror(errno)); return NULL; /* LCOV_EXCL_STOP */ } filenames = gregorio_malloc(capacity * sizeof(char *)); while (gregorio_readline(&buf, &bufsize, file)) { rtrim(buf); if (strlen(buf) > 0) { filenames[size++] = gregorio_strdup(buf); if (size >= capacity) { capacity <<= 1; filenames = gregorio_realloc(filenames, capacity * sizeof(char *)); } } else { gregorio_messagef("read_patterns", VERBOSITY_WARNING, 0, _("kpsewhich returned bad value for %s"), filename); } } free(buf); filenames[size] = NULL; pclose(file); #endif return filenames; } void gregorio_exit(int status) { #ifdef USE_KPSE if (kpse) { kpathsea_finish(kpse); } #endif gregorio_struct_destroy(); exit(status); } /* the prior line exits; LCOV_EXCL_LINE */