commit 79fc1582baced1a69dfd6bad946895596da71ae8
parent 2a1eab4ec740061b4130416f494e7f8332f531df
Author: nibo <kroekerrobin@gmail.com>
Date: Sun, 2 Jul 2023 21:01:42 +0200
Add -t and -d options
Diffstat:
| A | .gitignore | | | 1 | + |
| M | cho2txt.1 | | | 26 | ++++++++++++++++++++++---- |
| M | cho2txt.c | | | 241 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- |
3 files changed, 208 insertions(+), 60 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+cho2txt
diff --git a/cho2txt.1 b/cho2txt.1
@@ -1,10 +1,28 @@
-.TH "CHO2TXT 1" "June 2023" "User Commands"
+.TH "CHO2TXT 1" "July 2023" "User Commands"
.SH NAME
cho2txt - Extract lyrics from chordpro files
.SH SYNOPSIS
.B cho2txt
-[ filepath ]
+[-t]
+[-d]
+[FILE]...
.SH DESCRIPTION
.PP
-Provide a chordpro file via the first argument or pipe the contents of the chordpro file via stdin.
-The lyrics will be extracted and '\\n', '\\t', '\\r' and ' ' will be trimmed at the beginning and the end.
+Provide one or more chordpro files as arguments
+or pipe the content of one or more chordpro files via stdin.
+The lyrics will be extracted and '\\n', '\\t', '\\r' and ' '
+will be trimmed at the beginning and the end.
+Also lines containing no lyrics and only chordpro directives will be removed.
+The resulting text will be printed to stdout.
+.TP
+\fB\,-t\/\fR, \fB\,--title\/\fR
+Print the title of the song, e.g. 'Love Me Tender'.
+.TP
+\fB\,-d\/\fR, \fB\,--directive\/\fR
+Print the title of the song as chordpro directive, e.g. '{title: Love Me Tender}'
+.SH EXAMPLES
+.sp
+.RS 4
+cat Love_Me_Tender.cho "You_ll_Never_Walk_Alone.cho" | cho2txt
+
+cho2txt -t Love_Me_Tender.cho
diff --git a/cho2txt.c b/cho2txt.c
@@ -4,49 +4,25 @@
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
+#include <getopt.h>
-char *extractLyrics(int fd)
+enum print
{
- char *text = malloc(sizeof(char));
- int i = 0;
- char buf;
- bool isLyrics = true;
- bool isBracket = false;
- while (1)
- {
- if (read(fd, &buf, 1) == 1)
- {
- if (buf == '{')
- isLyrics = false;
- if (buf == '[')
- isLyrics = false;
- if (isLyrics)
- {
- if (buf == '\n' && isBracket)
- {
- isBracket = false;
- continue;
- }
- text[i] = buf;
- i++;
- text = realloc(text, (i+1) * sizeof(char));
- }
- if (buf == '}' || buf == ']')
- {
- isLyrics = true;
- isBracket = true;
- }
- else
- isBracket = false;
- }
- else
- break;
- }
- text[i] = '\0';
- return text;
+ PRINT_NO,
+ PRINT_TITLE,
+ PRINT_TITLE_DIRECTIVE
+};
+
+void printHelp()
+{
+ static const char help[] = "Usage: cho2txt [-t] [-d] [FILE]...\n"
+ "Extract lyrics from chordpro files.\n\n"
+ " -t, --title\t\tPrint title\n"
+ " -d, --directive\tPrint title as chordpro directive\n";
+ printf("%s", help);
}
-char *trimText(char *text)
+char *trim(char *text)
{
char *trimmedText = NULL;
int begin = 0;
@@ -93,32 +69,185 @@ char *trimText(char *text)
return trimmedText;
}
+char *parseTitle(const char *directive)
+{
+ char *title = NULL;
+ bool doParse = false;
+ int t = 0;
+ for (int i=0; i<strlen(directive); i++)
+ {
+ if (directive[i] == '}')
+ doParse = false;
+ if (doParse)
+ {
+ title = realloc(title, (t+1) * sizeof(char));
+ title[t] = directive[i];
+ t++;
+ }
+ if (directive[i] == ':')
+ doParse = true;
+ }
+ title = realloc(title, (t+1) * sizeof(char));
+ title[t] = 0;
+ return trim(title);
+}
+
+bool isTitle(const char *directive)
+{
+ static const char title[] = "title:";
+ int t = 0;
+ for (int i=0; i<strlen(directive); i++)
+ {
+ if (i > 0 && t < 7)
+ {
+ if (directive[i] != title[t])
+ return false;
+ t++;
+ if (t == 6)
+ return true;
+ }
+ }
+}
+
+char *extractLyrics(int fd, enum print printTitle)
+{
+ char *text = malloc(sizeof(char));
+ int i = 0;
+ int d = 0;
+ char buf;
+ bool isLyric = true;
+ bool isLyricInLine = false;
+ bool isDirectiveInLine = false;
+ bool isCurlyBrace = false;
+ char *directive = NULL;
+ while (1)
+ {
+ if (read(fd, &buf, 1) == 1)
+ {
+ if (buf == '[')
+ {
+ isLyric = false;
+ isDirectiveInLine = true;
+ }
+ if (buf == '{')
+ {
+ isLyric = false;
+ isDirectiveInLine = true;
+ isCurlyBrace = true;
+ }
+ if (isLyric)
+ {
+ if (buf == '\n' && !isLyricInLine && isDirectiveInLine)
+ goto IGNORE;
+ text[i] = buf;
+ i++;
+ text = realloc(text, (i+1) * sizeof(char));
+ isLyricInLine = true;
+ IGNORE:
+ }
+ else
+ {
+ if (isCurlyBrace)
+ {
+ directive = realloc(directive, (d+1) * sizeof(char));
+ directive[d] = buf;
+ d++;
+ }
+ }
+ if (buf == '}')
+ {
+ directive = realloc(directive, (d+1) * sizeof(char));
+ directive[d] = 0;
+ if (printTitle > 0 && isTitle(directive))
+ {
+ char *title;
+ if (printTitle == PRINT_TITLE)
+ title = parseTitle(directive);
+ else
+ {
+ title = malloc((strlen(directive)+1) * sizeof(char));
+ strcpy(title, directive);
+ }
+ for (int k=0; k<strlen(title); k++)
+ {
+ text[i] = title[k];
+ i++;
+ text = realloc(text, (i+1) * sizeof(char));
+ }
+ free(title);
+ text[i] = '\n';
+ i++;
+ text = realloc(text, (i+1) * sizeof(char));
+ }
+ d = 0;
+ free(directive);
+ directive = NULL;
+ isCurlyBrace = false;
+ isLyric = true;
+ }
+ if (buf == ']')
+ isLyric = true;
+ if (buf == '\n')
+ {
+ isDirectiveInLine = false;
+ isLyricInLine = false;
+ }
+ }
+ else
+ break;
+ }
+ text[i] = '\0';
+ return text;
+}
+
int main(int argc, char *argv[])
{
+ int o = 0;
+ int optionIndex = 0;
+ enum print printTitle = PRINT_NO;
char *lyrics, *trimmedLyrics;
- switch (argc)
+ static struct option long_options[] = {
+ { "title", no_argument, 0, 't' },
+ { "directive", no_argument, 0, 'd' },
+ { "help", no_argument, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+ while ((o = getopt_long(argc, argv, "td", long_options, &optionIndex)) != -1) {
+ switch(o) {
+ case 't':
+ printTitle = PRINT_TITLE;
+ break;
+ case 'd':
+ printTitle = PRINT_TITLE_DIRECTIVE;
+ break;
+ case 'h':
+ printHelp();
+ return 0;
+ }
+ }
+ if (argc == optind)
{
- case 1:
- lyrics = extractLyrics(0);
- trimmedLyrics = trimText(lyrics);
- printf("%s\n", trimmedLyrics);
- free(trimmedLyrics);
- break;
- case 2:
- int fd = open(argv[1], O_RDONLY);
+ lyrics = extractLyrics(0, printTitle);
+ trimmedLyrics = trim(lyrics);
+ printf("%s\n", trimmedLyrics);
+ free(trimmedLyrics);
+ }
+ else
+ {
+ int fd;
+ for (int i=optind; i<argc; i++)
+ {
+ fd = open(argv[i], O_RDONLY);
if (fd == -1)
{
- fprintf(stderr, "open failed.\n");
- return -1;
+ fprintf(stderr, "opening '%s' failed.\n", argv[i]);
+ continue;
}
- lyrics = extractLyrics(fd);
- trimmedLyrics = trimText(lyrics);
+ lyrics = extractLyrics(fd, printTitle);
+ trimmedLyrics = trim(lyrics);
printf("%s\n", trimmedLyrics);
free(trimmedLyrics);
- break;
- default:
- fprintf(stderr, "Either provide exactly one file or no file for reading from stdin.\n");
- return -1;
+ }
}
return 0;
}