cho2txt

Extract lyrics from chordpro files
git clone git://git.relim.de/cho2txt.git
Log | Files | Refs | README | LICENSE

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 }