lorid

convert chordpro to pdf
git clone git://git.relim.de/lorid.git
Log | Files | Refs | README | LICENSE

core.c (11420B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdbool.h>
      4 #include <stdarg.h>
      5 #include <unistd.h>
      6 #include <ctype.h>
      7 #include <string.h>
      8 #include <sys/stat.h>
      9 #include <errno.h>
     10 #include <limits.h>
     11 #include "core.h"
     12 
     13 const struct InstrumentInfo instruments[] = {
     14 	{ .name = "guitar", .description = "Guitar, 6 strings, standard tuning", .tuning = "E2 A2 D3 G3 B3 E4" },
     15 	{ .name = "keyboard", .description = "Piano", .tuning = "" },
     16 	{ .name = "mandolin", .description = "Mandolin, 4 strings, standard tuning", .tuning = "G D A E" },
     17 	{ .name = "ukulele", .description = "Ukulele, 4 strings, standard tuning", .tuning = "G C E A" }
     18 };
     19 
     20 static const char *log_levels[] = { "INFO", "WARN", " ERR", "DEBUG" };
     21 static const uint8_t log_level_colors[] = { 37, 33, 31, 34 };
     22 static bool g_show_info_logs = false;
     23 
     24 void
     25 util_log_enable_info_logs(void)
     26 {
     27 	g_show_info_logs = true;
     28 }
     29 
     30 void *
     31 emalloc(size_t size)
     32 {
     33 	void *ptr = malloc(size);
     34 	if (!ptr) {
     35 		perror("malloc failed");
     36 		exit(1);
     37 	}
     38 	return ptr;
     39 }
     40 
     41 void *
     42 erealloc(void *ptr, size_t size)
     43 {
     44 	void *tmp = realloc(ptr, size);
     45 	if (!tmp) {
     46 		perror("realloc failed");
     47 		exit(1);
     48 	}
     49 	return tmp;
     50 }
     51 
     52 static void
     53 log_without_color(
     54 	const char *file,
     55 	size_t line_no,
     56 	enum LogLevel level,
     57 	const char *msg,
     58 	va_list va
     59 )
     60 {
     61 	if (file && line_no > 0) {
     62 		fprintf(stderr, "%s:%ld ", file, line_no);
     63 	} else
     64 	if (!file && line_no > 0) {
     65 		fprintf(stderr, "line %ld ", line_no);
     66 	}
     67 	fprintf(stderr, "%s: ", log_levels[level]);
     68 	vfprintf(stderr, msg, va);
     69 	fprintf(stderr, "\n");
     70 }
     71 
     72 #if COLOR == 1
     73 static void
     74 log_with_color(
     75 	const char *file,
     76 	size_t line_no,
     77 	enum LogLevel level,
     78 	const char *msg,
     79 	va_list va
     80 )
     81 {
     82 	if (file && line_no > 0) {
     83 		fprintf(stderr, "\033[1m%s:%ld\033[0m ", file, line_no);
     84 	} else
     85 	if (!file && line_no > 0) {
     86 		fprintf(stderr, "\033[1mline %ld\033[0m ", line_no);
     87 	}
     88 	fprintf(stderr, "\033[1;%dm%s\033[0m: ", log_level_colors[level], log_levels[level]);
     89 	vfprintf(stderr, msg, va);
     90 	fprintf(stderr, "\n");
     91 }
     92 #endif /* COLOR */
     93 
     94 void
     95 util_vlog(
     96 	const char *file,
     97 	size_t line_no,
     98 	enum LogLevel level,
     99 	const char *msg,
    100 	va_list va
    101 )
    102 {
    103 	if (level == LOG_INFO && !g_show_info_logs) {
    104 		return;
    105 	}
    106 #if COLOR == 1
    107 	if (isatty(2)) {
    108 		log_with_color(file, line_no, level, msg, va);
    109 	} else {
    110 		log_without_color(file, line_no, level, msg, va);
    111 	}
    112 #else
    113 	log_without_color(file, line_no, level, msg, va);
    114 #endif
    115 }
    116 
    117 void
    118 util_log(
    119 	const char *file,
    120 	size_t line_no,
    121 	enum LogLevel level,
    122 	const char *msg,
    123 	...
    124 )
    125 {
    126 	va_list va;
    127 	va_start(va, msg);
    128 	util_vlog(file, line_no, level, msg, va);
    129 }
    130 
    131 bool
    132 str_starts_with(const char *str, const char *part)
    133 {
    134 	unsigned int i;
    135     size_t part_len = strlen(part);
    136     if (part_len > strlen(str))
    137         return false;
    138     for (i=0; i<part_len; i++) {
    139         if (str[i] != part[i])
    140             return false;
    141     }
    142     return true;
    143 }
    144 
    145 char *
    146 str_normalize(const char *str)
    147 {
    148 	char *normalized = NULL;
    149 	char c;
    150 	int n = 0;
    151 	int i;
    152 	for (i = 0; str[i]; i++) {
    153 		if (str[i] == ' ' || str[i] == '/' || str[i] == '.') {
    154 			normalized = erealloc(normalized, (n+1) * sizeof(char));
    155 			normalized[n] = '-';
    156 			n++;
    157 			continue;
    158 		}
    159 		if (str[i] == '\'' || str[i] == ',')
    160 			continue;
    161 		c = (char)tolower(str[i]);
    162 		normalized = erealloc(normalized, (n+1) * sizeof(char));
    163 		normalized[n] = c;
    164 		n++;
    165 	}
    166 	normalized = erealloc(normalized, (n+1) * sizeof(char));
    167 	normalized[n] = 0;
    168 	return normalized;
    169 }
    170 
    171 char *
    172 str_trim(const char *str)
    173 {
    174 	char *trimmed = NULL;
    175 	int begin = 0;
    176 	int end = 0;
    177 	int len = (int)strlen(str);
    178 	int i, k;
    179 
    180 	for (i = 0; i<len; i++) {
    181 		if (
    182 			str[i] == ' ' ||
    183 			str[i] == '\n' ||
    184 			str[i] == '\t' ||
    185 			str[i] == '\r'
    186 		) {
    187 			begin++;
    188 		} else {
    189 			break;
    190 		}
    191 	}
    192 	for (i = len-1; i>=0; i--) {
    193 		if (
    194 			str[i] == ' '||
    195 			str[i] == '\n' ||
    196 			str[i] == '\t' ||
    197 			str[i] == '\r'
    198 		) {
    199 			end++;
    200 		} else {
    201 			break;
    202 		}
    203 	}
    204 	for (i = 0, k = 0; i<len; i++) {
    205 		if (i >= begin && i < len - end) {
    206 			trimmed = erealloc(trimmed, (k+1) * sizeof(char));
    207 			trimmed[k] = str[i];
    208 			k++;
    209 		}
    210 	}
    211 	trimmed = erealloc(trimmed, (k+1) * sizeof(char));
    212 	trimmed[k] = 0;
    213 	return trimmed;
    214 }
    215 
    216 char *
    217 str_remove_leading_whitespace(const char *str)
    218 {
    219 	int i;
    220 	for (i = 0; str[i] == ' ' || str[i] == '\t'; i++);
    221 	return strdup(&str[i]);
    222 }
    223 
    224 /* INFO: Difference to strcmp is it allows a and b to be NULL */
    225 int
    226 str_compare(const char *a, const char *b)
    227 {
    228 	if (a && b) {
    229 		return strcmp(a, b);
    230 	} else
    231 	if (!a && !b) {
    232 		return 0;
    233 	} else
    234 	if (a && !b) {
    235 		return 1;
    236 	} else {
    237 	// if (!a && b) {
    238 		return -1;
    239 	}
    240 }
    241 
    242 long
    243 str_to_number(const char *str)
    244 {
    245 	long n;
    246 	char *endptr;
    247 	n = strtol(str, &endptr, 10);
    248 	if (str == endptr) {
    249 		return -1;
    250 	}
    251 	if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) {
    252 		return -1;
    253 	}
    254 	return n;
    255 }
    256 
    257 int
    258 str_index_of(const char *str, char c)
    259 {
    260 	const char *s;
    261 	for (s = str; *s; s++) {
    262 		if (*s == c) {
    263 			return s - str;
    264 		}
    265 	}
    266 	return -1;
    267 }
    268 
    269 bool
    270 strs_has(char **strs, const char *str)
    271 {
    272 	if (!strs) {
    273 		return false;
    274 	}
    275 	char **s;
    276 	for (s = strs; *s; s++) {
    277 		if (!strcmp(*s, str)) {
    278 			return true;
    279 		}
    280 	}
    281 	return false;
    282 }
    283 
    284 void
    285 strs_add(char ***strs, const char *str)
    286 {
    287 	int i = 0;
    288 	if (*strs) {
    289 		char **s;
    290 		for (s = *strs; *s; s++, i++);
    291 	}
    292 	*strs = erealloc(*strs, (i+2) * sizeof(char *));
    293 	(*strs)[i] = strdup(str);
    294 	(*strs)[i+1] = NULL;
    295 }
    296 
    297 int
    298 strs_get_index_if_in(char **strs, const char *str)
    299 {
    300 	if (!strs) {
    301 		return -1;
    302 	}
    303 	int i;
    304 	for (i = 0; strs[i]; i++) {
    305 		if (!strcmp(strs[i], str)) {
    306 			return i;
    307 		}
    308 	}
    309 	return -1;
    310 }
    311 
    312 void
    313 strs_free(char **strs)
    314 {
    315 	if (!strs) {
    316 		return;
    317 	}
    318 	char **s;
    319 	for (s = strs; *s; s++) {
    320 		free(*s);
    321 	}
    322 	free(strs);
    323 }
    324 
    325 enum FileType
    326 file_type(const char *path)
    327 {
    328 	struct stat s;
    329 	if (stat(path, &s) != 0) {
    330 		return FILE_TYPE_ERROR;
    331 	}
    332 	if (S_ISDIR(s.st_mode)) {
    333 		return FILE_TYPE_FOLDER;
    334 	}
    335 	if (S_ISREG(s.st_mode)) {
    336 		return FILE_TYPE_REG_FILE;
    337 	}
    338 	return FILE_TYPE_OTHER;
    339 }
    340 
    341 char *
    342 file_read(FILE *fp)
    343 {
    344 	char *str = NULL;
    345 	char buf;
    346 	size_t read;
    347 	int i = 0;
    348 	while (1) {
    349 		read = fread(&buf, 1, 1, fp);
    350 		if (read == 1) {
    351 			str = erealloc(str, (i+1) * sizeof(char));
    352 			str[i] = buf;
    353 			i++;
    354 		} else {
    355 			str = erealloc(str, (i+1) * sizeof(char));
    356 			str[i] = 0;
    357 			break;
    358 		}
    359 	}
    360 	return str;
    361 }
    362 
    363 char *
    364 file_extension_replace_or_add(const char *filepath, const char *extension)
    365 {
    366 	size_t extension_len = strlen(extension);
    367 	char *new = NULL;
    368 	int mark = -1;
    369 	int i, k;
    370 	int path_len;
    371 	for (i = 0; filepath[i]; i++) {
    372 		if (filepath[i] == '.') {
    373 			mark = i;
    374 		}
    375 	}
    376 	if (mark == -1) {
    377 		path_len = (int)strlen(filepath);
    378 		new = emalloc((path_len+2+extension_len) * sizeof(char));
    379 		for (i = 0; i < path_len; i++) {
    380 			new[i] = filepath[i];
    381 		}
    382 		new[i] = '.';
    383 		i++;
    384 		for (k = 0; extension[k]; k++, i++) {
    385 			new[i] = extension[k];
    386 		}
    387 		new[i] = 0;
    388 	} else {
    389 		new = emalloc((mark+2+extension_len) * sizeof(char));
    390 		for (i = 0; i <= mark; i++) {
    391 			new[i] = filepath[i];
    392 		}
    393 		for (k = 0; extension[k]; k++, i++) {
    394 			new[i] = extension[k];
    395 		}
    396 		new[i] = 0;
    397 	}
    398 	return new;
    399 }
    400 
    401 bool
    402 file_extension_equals(const char *filepath, const char *extension)
    403 {
    404 	int mark = -1;
    405 	int i;
    406 	for (i = strlen(filepath)-1; i >= 0; i--) {
    407 		if (filepath[i] == '.') {
    408 			mark = i;
    409 			break;
    410 		}
    411 	}
    412 	if (!strcmp(&filepath[mark+1], extension)) {
    413 		return true;
    414 	}
    415 	return false;
    416 }
    417 
    418 char *
    419 filepath_add_ending_slash_if_missing(const char *path)
    420 {
    421 	size_t len = strlen(path);
    422 	if (path[len-1] == '/') {
    423 		return strdup(path);
    424 	} else {
    425 		char *path_with_slash = emalloc((len+2) * sizeof(char));
    426 		strcpy(path_with_slash, path);
    427 		path_with_slash[len] = '/';
    428 		path_with_slash[len+1] = 0;
    429 		return path_with_slash;
    430 	}
    431 }
    432 
    433 char *
    434 filepath_basename(const char *path)
    435 {
    436 	int begin = 0;
    437 	int i;
    438 	for (i = 0; path[i]; i++) {
    439 		if (path[i] == '/') {
    440 			begin = i+1;
    441 		}
    442 	}
    443 	return strdup(&path[begin]);
    444 }
    445 
    446 char *
    447 filepath_dirname(const char *path)
    448 {
    449 	char *dirname;
    450 	int i, end = 0;
    451 
    452 	for (i = 0; path[i]; i++) {
    453 		if (path[i] == '/') {
    454 			end = i;
    455 		}
    456 	}
    457 	if (end == 0) {
    458 		dirname = emalloc(2 * sizeof(char));
    459 		dirname[0] = '.';
    460 		dirname[1] = 0;
    461 		return dirname;
    462 	}
    463 	dirname = emalloc((end+1)* sizeof(char));
    464 	for (i = 0; i < end; i++) {
    465 		dirname[i] = path[i];
    466 	}
    467 	dirname[i] = 0;
    468 	return dirname;
    469 }
    470 
    471 char *
    472 filepath_resolve_tilde(const char *path)
    473 {
    474 	char *home;
    475 	char *str = NULL;
    476 	if (*path == '~') {
    477 		home = getenv("HOME");
    478 		if (!home) {
    479 			DEBUG("getenv failed.");
    480 			return NULL;
    481 		}
    482 		str = erealloc(str, (strlen(home)+strlen(path)) * sizeof(char));
    483 		strcpy(str, home);
    484 		strcat(str, &path[1]);
    485 		return str;
    486 	} else {
    487 		return strdup(path);
    488 	}
    489 }
    490 
    491 char *
    492 filepath_as_dot_file(const char *path)
    493 {
    494 	const char *c, *last_slash;
    495 	char *dot_path, *dc;
    496 
    497 	last_slash = NULL;
    498 	for (c = path; *c; c++) {
    499 		if (*c == '/') {
    500 			last_slash = c;
    501 		}
    502 	}
    503 	dot_path = emalloc(c - path + 2 * sizeof(char));
    504 	for (c = path, dc = dot_path; c<=last_slash; c++, dc++) {
    505 		*dc = *c;
    506 	}
    507 	*dc = '.';
    508 	dc++;
    509 	for (; *c; c++, dc++) {
    510 		*dc = *c;
    511 	}
    512 	*dc = '\0';
    513 	return dot_path;
    514 }
    515 
    516 static const char *
    517 size_type_to_string(enum SizeType type)
    518 {
    519 	switch (type) {
    520 	case SIZE_TYPE_PERCENT:
    521 		return "%";
    522 	case SIZE_TYPE_EM:
    523 		return "em";
    524 	case SIZE_TYPE_EX:
    525 		return "ex";
    526 	default:
    527 		return "";
    528 	}
    529 }
    530 
    531 struct Size *
    532 size_create(const char *str)
    533 {
    534 	size_t len = strlen(str);
    535 	struct Size *size = emalloc(sizeof(struct Size));
    536 	char *endptr;
    537 	double d;
    538 	d = strtod(str, &endptr);
    539 	if (str == endptr || errno == ERANGE) {
    540 		DEBUG("strtod failed.");
    541 		goto ERR;
    542 	}
    543 	size->d = d;
    544 	size->type = SIZE_TYPE_POINT;
    545 	if (len > 1 && str[len-1] == '%') {
    546 		if (size->d < 1.0 || size->d > 100.0) {
    547 			util_log(NULL, 0, LOG_ERR, "invalid percentage.");
    548 			goto ERR;
    549 		}
    550 		size->d = d / 100.0;
    551 		size->type = SIZE_TYPE_PERCENT;
    552 	} else
    553 	if (len > 2 && str[len-2] == 'e' && str[len-1] == 'm') {
    554 		size->type = SIZE_TYPE_EM;
    555 	} else
    556 	if (len > 2 && str[len-2] == 'e' && str[len-1] == 'x') {
    557 		size->type = SIZE_TYPE_EX;
    558 	}
    559 	return size;
    560 	ERR:
    561 	free(size);
    562 	return NULL;
    563 }
    564 
    565 struct Size *
    566 size_copy(struct Size *size)
    567 {
    568 	struct Size *copy = emalloc(sizeof(struct Size));
    569 	copy->type = size->type;
    570 	copy->d = size->d;
    571 	return copy;
    572 }
    573 
    574 const char *
    575 size_to_string(struct Size *size)
    576 {
    577 	static char str[10+1];
    578 	if (size->d > 999999) {
    579 		snprintf((char *)&str, 10+1, ">999.999");
    580 	} else {
    581 		snprintf((char *)&str, 10+1, "%.1f%s", size->d, size_type_to_string(size->type));
    582 	}
    583 	return str;
    584 }
    585 
    586 const char *
    587 is_base_font(struct Font *font)
    588 {
    589 	if (!strcmp(font->family, "Courier")) {
    590 		if (font->style == FONT_STYLE_ITALIC && font->weight == FONT_WEIGHT_BOLD) {
    591 			return "Courier-BoldItalic";
    592 		} else
    593 		if (font->style == FONT_STYLE_ITALIC) {
    594 			return "Courier-Italic";
    595 		} else
    596 		if (font->weight == FONT_WEIGHT_BOLD) {
    597 			return "Courier-Bold";
    598 		}
    599 		return "Courier";
    600 	} else
    601 	if (!strcmp(font->family, "Helvetica")) {
    602 		if (font->style == FONT_STYLE_OBLIQUE && font->weight == FONT_WEIGHT_BOLD) {
    603 			return "Helvetica-BoldOblique";
    604 		} else
    605 		if (font->style == FONT_STYLE_OBLIQUE) {
    606 			return "Helvetica-Oblique";
    607 		} else
    608 		if (font->weight == FONT_WEIGHT_BOLD) {
    609 			return "Helvetica-Bold";
    610 		}
    611 		return "Helvetica";
    612 	} else
    613 	if (!strcmp(font->family, "Times")) {
    614 		if (font->style == FONT_STYLE_ITALIC && font->weight == FONT_WEIGHT_BOLD) {
    615 			return "Times-BoldItalic";
    616 		} else
    617 		if (font->style == FONT_STYLE_ITALIC) {
    618 			return "Times-Italic";
    619 		} else
    620 		if (font->weight == FONT_WEIGHT_BOLD) {
    621 			return "Times-Bold";
    622 		}
    623 		return "Times-Roman";
    624 	}
    625 	return NULL;
    626 }