From: mtj@tron.gun.de (Marc Jackisch)
Newsgroups: alt.guitar.tab
Subject: chord2tex
Organization: TRON Public Mailbox, Neurath, Germany
Date: Thu, 1 Oct 92 13:20:00 +0100

Hi netters!

You probably all know chordpro and rechord. Here's another alternative:
Chord2tex converts chordpro format to a LaTeX (!) source code.

Files: chord2tex.c, docstart.c2t, docend.c2t

Chord2tex was originally created on an Amiga, but it should be fairly
portable to any system.
Please report bugs and comments to mtj@tron.gun.de.

Cheerio, Marc.

PS: I just did not find a utility to create shell-archives, so I'm afraid
you'll have to cut up this text by hand.

-= chord2tex.c ===============================================================-

/* chord2tex.c
** ~~~~~~~~~~~
**  A little utility to transform chordpro-format to TeX.
**
**  1992 by Marc Jackisch
**          Im Schaufsfeld 17
**          4018 Langenfeld
**          West-Germany
**          mtj@tron.gun.de
*/

#include <stdio.h>
#include <string.h>

#define MAXLINE 256

char  inline[MAXLINE], outline[MAXLINE];
int   quit = 0;
int   line_no = 0;

/* Prototypes (yep, it's ANSI) */

int   cat_file(char *name);
char *skipwhite(char *s);
void  proc_buf(void);
char *mkspace(char *q, int *space, int text, int lchord);
void  fatal_err(char *msg, int pos);
void  do_direct(char *d);
void  user_info(void);


/* main: command-line processing and main loop.
*/

void main(int argc, char **argv)
{
  FILE   *in;
  char   *fname = argv[argc-1],
         *start = "docstart.c2t",
         *end   = "docend.c2t";
  int     i;

  for(i = 1; i < argc; i++)
    if(argv[i][0] == '-')
      switch(argv[i][1]) {
        case 's':
          start = argv[++i];
          break;
        case 'e':
          end   = argv[++i];
          break;
        case '?':
          fprintf(stderr, "chord2tex V1.0\nUsage: chord2tex [-? | -h] [-s <startfile>] [-e <endfile>] <filename>\n");
          return;
        case 'h':
          user_info();
          return;
        default:
          fprintf(stderr, "Unknown option %s\n", argv[i]);
          return; }

  if(!cat_file(start))
    return;

  if(!(in = fopen(fname, "r"))) {
    fprintf(stderr, "\nCould not open input file \"%s\"\n", fname);
    return; }

  while(!feof(in) && !quit) {
    fgets(inline, MAXLINE, in);
    line_no++;
    proc_buf();
    if(outline[0] && !quit)
      puts(outline); }

  fclose(in);

  cat_file(end);
}

/* cat_file: Type the file `name' to stdout. Return a boolean value indicating
** success or failure.
*/

int cat_file(char *name)
{
  FILE *to_cp;
  char  c;

  if(!(to_cp = fopen(name, "r"))) {
    fprintf(stderr, "Could not open file %s\nterminating...\n", name);
    return 0; }
  for(; (c = fgetc(to_cp)) != EOF; putchar(c))
    ;
  fclose(to_cp);
  return 1;
}

/* skipwhite: skip white space at the beginning of a string
*/

char *skipwhite(char *s)
{
  while((*s == ' ' || *s == '\t')&& *s != '\0') s++;
  return s;
}

/* proc_buf: This function processes the buffer "inline" to build an output-
** line in "outline".
*/

void proc_buf(void)
{
#define POS     (int)(p-inline)
#define SPACE   q = mkspace(q, &space, text, lchord)
#define TEXT    {text = 1; lchord = 0;}

  char   *p = skipwhite(inline), *q = outline;
  char    direct[MAXLINE];
  /* flags */
  int     quote = 0,
          chord = 0,
          space = 0,
          text  = 0,
          lchord = 0;

  *q = '\0';

  /* comment-filter
  */
  if(*p == '#')
    return;

  /* directives
  */
  if(*p == '{') {
    strcpy(direct, ++p);
    for(p = direct; *p != '}'; p++)
      ;
    *p = '\0';
    do_direct(direct);
    return; }

  /* normal lines
  */
  for(p = inline; *p != '\n' && *p != '\0'; p++)
    switch(*p) {
      case '"':
        SPACE;
        if(quote) { *q++ = '\''; *q++ = '\''; }
        else      { *q++ = '`';  *q++ = '`'; }
        quote ^= 1;
        TEXT;
        break;
      case '#': case '$': case '%': case '&': case '_':
        SPACE;
        *q++ = '\\'; *q++ = *p;
        TEXT;
        break;
      case '[':
        SPACE;
        if(chord)
          fatal_err("Found open bracket in chord", POS);
        chord = 1;
        q = stpcpy(q, "\\.{");
        break;
      case ']':
        SPACE;
        if(!chord)
          fatal_err("Bracket not ending chord", POS);
        chord = 0;
        *q++ = '}';
        lchord = 1;
        break;
      case ' ':
        space++;
        break;
      default:
        SPACE;
        *q++ = *p;
        if(!chord)
          TEXT;
        break; }
  *q = '\0';

  /* end of line and empty lines
  */
  if(skipwhite(outline) == q)
    strcpy(outline, "\\vs\n");
  else
    strcpy(q, "\\\\");
}

/* mkspace: Process the spaces. Return the new q-pointer and set space to 0.
** If the number of consecutive spaces is >1 or we have not processed some text
** in this line or if the last thing processed was a chord, mkspace will force
** blank space using \hspace*. Otherwise a normal blank will be written to the
** buffer.
*/

char *mkspace(char *q, int *space, int text, int lchord)
{
  char  hsbuf[20];

  if(*space) {
    if(*space > 1 || !text || lchord) {
      sprintf(hsbuf, "\\hspace*{%dem}", *space * 2);
      q = stpcpy(q, hsbuf); }
    else
      *q++ = ' ';
    *space = 0; }
  return q;
}

/* fatal_err: Print an errormessage, the input line and mark the position of
** the error. Then, set the "quit"-flag.
*/

void fatal_err(char *msg, int pos)
{
  int i;

  fprintf(stderr, "\nline %d\n%s\n", line_no, msg);
  fprintf(stderr, "%s", inline);
  for(i = 0; i < pos; i++)
    fputc(' ', stderr);
  fprintf(stderr, "^\n");
  quit = 1;
}

/* do_direct: process a directive.
*/

void do_direct(char *d)
{
#define TSIZE   14

  static char  *dirtab[TSIZE] = {
    "start_of_chorus",          /* 0 */
    "soc",
    "end_of_chorus",            /* 1 */
    "eoc",
    "comment:",                 /* 2 */
    "c:",
    "new_song",                 /* 3 */
    "ns",
    "title:",                   /* 4 */
    "t:",
    "subtitle:",                /* 5 */
    "st:",
    "define:",                  /* 6 */
    "<unused>" };   /* Unused is included, to switch over command/2 */

  static int  chorus = 0;

  int   i, command;

  /* parsing
  */
  for(i = 0; i < TSIZE; i++)
    if(!strncmp(d, dirtab[i], strlen(dirtab[i]))) {
      command = i;
      break; }
  if(i == TSIZE) {
    fatal_err("Unknown directive", 0);
    return; }

  d += strlen(dirtab[command]);
  d = skipwhite(d);
  command /= 2;

  switch(command) {
    case 0:
      if(chorus) {
        fatal_err("Nested chorus", 0);
        return; }
      chorus = 1;
      strcpy(outline, "\\fbox{\\vbox{");
      break;
    case 1:
      if(!chorus) {
        fatal_err("End of chorus without a beginning", 0);
        return; }
      chorus = 0;
      strcpy(outline, "}}\\\\");
      break;
    case 2:
      sprintf(outline, "\\C{%s}", d);
      break;
    case 3:
      strcpy(outline, "\\newpage");
      break;
    case 4:
      sprintf(outline, "\\T{%s}", d);
      break;
    case 5:
      sprintf(outline, "\\S{%s}", d);
      break;
    case 6:
      break;  /* defines are ignored */ }
}

/* user_info: Just printing...
*/

void user_info(void)
{
  fprintf(stderr, "chord2tex\n");
  fprintf(stderr, "1992 by Marc Jackisch\n");
  fprintf(stderr, "        Im Schaufsfeld 17\n");
  fprintf(stderr, "        4018 Langenfeld\n");
  fprintf(stderr, "        West-Germany\n");
  fprintf(stderr, "        mtj@tron.gun.de\n\n");
  fprintf(stderr, "Usage: chord2tex [-? | -h] [-s <startfile>] [-e <endfile>] <filename>\n\n");
  fprintf(stderr, "Options:\n");
  fprintf(stderr, " -?: short information\n");
  fprintf(stderr, " -h: this text\n");
  fprintf(stderr, " -s <startfile>: use the file <startfile> as the beginning of the\n");
  fprintf(stderr, "                 document instead of the default \"docstart.c2t\"\n");
  fprintf(stderr, " -e <endfile>: as -s with the end of the document. Default is\n");
  fprintf(stderr, "               \"docend.c2t\"\n\n");
  fprintf(stderr, "chord2tex is public domain\n\n");
  fprintf(stderr, "chord2tex converts chordpro format to a LaTeX-source. Please\n");
  fprintf(stderr, "refer to the chordpro documentation for a description of the\n");
  fprintf(stderr, "format.\n");
}

-= docstart.c2t ==============================================================-

\documentstyle{article}

\pagestyle{empty}
\parindent0pt

\def\.#1{\ifvmode\noindent\fi\rlap{\raisebox{10pt}{#1}}}  % Chord
\def\T#1{{\large\bf #1}\\[2ex]}                           % Title
\def\S#1{{\bf #1}\\[2ex]}                                 % Subtitle
\def\C#1{\fbox{#1}\\}                                     % comment
\def\vs{\vspace*{1ex}}                                    % vertical blank

\begin{document}

-= docend.c2t ================================================================-

\end{document}

-= the end ===================================================================-
--
Marc Jackisch, Im Schaufsfeld 17, 4018 Langenfeld, West-Germany
mtj@tron.gun.de / mtj@tron.zer
-- THE ANSWER V2.03