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 }