cho2txt.c (5516B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <fcntl.h> 5 #include <stdbool.h> 6 #include <string.h> 7 #include <getopt.h> 8 #include "cho2txt.h" 9 10 void printHelp() 11 { 12 static const char help[] = "Usage: cho2txt [-t] [-d] [FILE]...\n" 13 "Extract lyrics from chordpro files.\n\n" 14 " -t, --title\t\tPrint title\n" 15 " -d, --directive\tPrint title as chordpro directive\n" 16 " -h, --help\t\tPrints help information\n" 17 " -v, --version\t\tPrint program version to stdout\n"; 18 printf("%s", help); 19 } 20 21 bool isDirective(enum direc d, const char *str) 22 { 23 bool isDirective = false; 24 size_t len; 25 int i = 0; 26 int k = 1; 27 char **names = (char **)dirs[d].names; 28 while (names[i] != NULL) 29 { 30 isDirective = true; 31 len = strlen(names[i]); 32 while (k <= len) 33 { 34 if (names[i][k-1] != str[k]) 35 { 36 isDirective = false; 37 break; 38 } 39 k++; 40 } 41 if (isDirective) 42 return true; 43 k = 1; 44 i++; 45 } 46 return false; 47 } 48 49 char *trim(char *text) 50 { 51 char *trimmedText = NULL; 52 int begin = 0; 53 int end = 0; 54 for (int i=0; i<strlen(text); i++) 55 { 56 if 57 ( 58 text[i] == ' ' || 59 text[i] == '\n' || 60 text[i] == '\t' || 61 text[i] == '\r' 62 ) 63 begin++; 64 else 65 break; 66 } 67 for (int i=strlen(text)-1; i>=0; i--) 68 { 69 if 70 ( 71 text[i] == ' '|| 72 text[i] == '\n' || 73 text[i] == '\t' || 74 text[i] == '\r' 75 ) 76 end++; 77 else 78 break; 79 } 80 int k = 0; 81 for (int i=0; i<strlen(text); i++) 82 { 83 if (i >= begin && i < strlen(text) - end) 84 { 85 trimmedText = realloc(trimmedText, (k+1) * sizeof(char)); 86 trimmedText[k] = text[i]; 87 k++; 88 } 89 } 90 trimmedText = realloc(trimmedText, (k+1) * sizeof(char)); 91 trimmedText[k] = 0; 92 free(text); 93 return trimmedText; 94 } 95 96 char *parseTitle(const char *directive) 97 { 98 char *title = NULL; 99 bool doParse = false; 100 int t = 0; 101 for (int i=0; i<strlen(directive); i++) 102 { 103 if (directive[i] == '}') 104 doParse = false; 105 if (doParse) 106 { 107 title = realloc(title, (t+1) * sizeof(char)); 108 title[t] = directive[i]; 109 t++; 110 } 111 if (directive[i] == ':') 112 doParse = true; 113 } 114 title = realloc(title, (t+1) * sizeof(char)); 115 title[t] = 0; 116 return trim(title); 117 } 118 119 char *extractLyrics(int fd, enum print printTitle) 120 { 121 char *text = malloc(sizeof(char)); 122 int i = 0; 123 int d = 0; 124 char buf; 125 char prev_buf = '\n'; 126 bool isLyric = true; 127 bool isLyricInLine = false; 128 bool isDirectiveInLine = false; 129 bool isCurlyBrace = false; 130 bool isComment = false; 131 char *directive = NULL; 132 while (1) 133 { 134 if (read(fd, &buf, 1) == 1) 135 { 136 if (buf == '#' && prev_buf == '\n') { 137 isComment = true; 138 } 139 if (isComment) { 140 goto SKIP; 141 } 142 if (buf == '[') 143 { 144 isLyric = false; 145 isDirectiveInLine = true; 146 } 147 if (buf == '{') 148 { 149 isLyric = false; 150 isDirectiveInLine = true; 151 isCurlyBrace = true; 152 } 153 if (isLyric) 154 { 155 if (buf == '\n' && !isLyricInLine && isDirectiveInLine) 156 goto IGNORE; 157 text[i] = buf; 158 i++; 159 text = realloc(text, (i+1) * sizeof(char)); 160 isLyricInLine = true; 161 IGNORE: 162 } 163 else 164 { 165 if (isCurlyBrace) 166 { 167 directive = realloc(directive, (d+1) * sizeof(char)); 168 directive[d] = buf; 169 d++; 170 } 171 } 172 if (buf == '}') 173 { 174 isLyric = true; 175 directive = realloc(directive, (d+1) * sizeof(char)); 176 directive[d] = 0; 177 if ( 178 isDirective(DIREC_GRID_START, directive) || 179 isDirective(DIREC_TAB_START, directive) 180 ) 181 isLyric = false; 182 else if ( 183 isDirective(DIREC_GRID_END, directive) || 184 isDirective(DIREC_TAB_END, directive) 185 ) 186 isLyric = true; 187 if (printTitle > 0 && isDirective(DIREC_TITLE, directive)) 188 { 189 char *title; 190 if (printTitle == PRINT_TITLE) 191 title = parseTitle(directive); 192 else 193 { 194 title = malloc((strlen(directive)+1) * sizeof(char)); 195 strcpy(title, directive); 196 } 197 for (int k=0; k<strlen(title); k++) 198 { 199 text[i] = title[k]; 200 i++; 201 text = realloc(text, (i+1) * sizeof(char)); 202 } 203 free(title); 204 text[i] = '\n'; 205 i++; 206 text = realloc(text, (i+1) * sizeof(char)); 207 } 208 d = 0; 209 free(directive); 210 directive = NULL; 211 isCurlyBrace = false; 212 } 213 if (buf == ']') 214 isLyric = true; 215 SKIP: 216 if (buf == '\n') 217 { 218 isComment = false; 219 isDirectiveInLine = false; 220 isLyricInLine = false; 221 } 222 prev_buf = buf; 223 } 224 else 225 break; 226 } 227 text[i] = '\0'; 228 return text; 229 } 230 231 int main(int argc, char *argv[]) 232 { 233 int o = 0; 234 int optionIndex = 0; 235 enum print printTitle = PRINT_NO; 236 char *lyrics, *trimmedLyrics; 237 static struct option long_options[] = { 238 { "title", no_argument, 0, 't' }, 239 { "directive", no_argument, 0, 'd' }, 240 { "help", no_argument, 0, 'h' }, 241 { "version", no_argument, 0, 'v' }, 242 { 0, 0, 0, 0 } 243 }; 244 while ((o = getopt_long(argc, argv, "tdhv", long_options, &optionIndex)) != -1) { 245 switch(o) { 246 case 't': 247 printTitle = PRINT_TITLE; 248 break; 249 case 'd': 250 printTitle = PRINT_TITLE_DIRECTIVE; 251 break; 252 case 'h': 253 printHelp(); 254 return 0; 255 case 'v': 256 printf("%.1f\n", VERSION); 257 return 0; 258 } 259 } 260 if (argc == optind) 261 { 262 lyrics = extractLyrics(0, printTitle); 263 trimmedLyrics = trim(lyrics); 264 printf("%s\n", trimmedLyrics); 265 free(trimmedLyrics); 266 } 267 else 268 { 269 int fd; 270 for (int i=optind; i<argc; i++) 271 { 272 fd = open(argv[i], O_RDONLY); 273 if (fd == -1) 274 { 275 fprintf(stderr, "opening '%s' failed.\n", argv[i]); 276 continue; 277 } 278 lyrics = extractLyrics(fd, printTitle); 279 trimmedLyrics = trim(lyrics); 280 printf("%s\n", trimmedLyrics); 281 free(trimmedLyrics); 282 } 283 } 284 return 0; 285 }