/*********************************************************************
Author      : Charles Bradley Slaten
**********************************************************************
Amiga Port : Peter Herter
*********************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINE 133
#define DEFAULT_WIDTH 72
#define DEFAULT_SKIP_LINES 0
#define DEFAULT_CHORDS_PER_ROW 3
#define COMMENT_CHAR '#'
#define CHORUS_CHAR '|'
#define NEWPAGE 0x0C
#define FLAT_CHAR 'b'
#define LEFT_MARGIN 0
#define FLAT -1
#define NATURAL 0
#define SHARP 1

unsigned char versiontag[] = "\0$VER: AmiXPose 1.4 ("__DATE__")";

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

typedef int Boolean;
typedef struct chordStruct {
    char *chordName;
    char fingering[7];
    int offset;
} Chord;
typedef struct chordListStruct {
    Chord *chord;
    struct chordListStruct *next;
} ChordList;
        
ChordList* InChordList[7];
ChordList* KnownChordList[7];

char* FlatNotes[] = {"A","Bb","B","C","Db","D","Eb","E","F","Gb","G","Ab" };
char* SharpNotes[] = {"A","A#","B","C","C#","D","D#","E","F","F#","G","G#" };
char* InFileName;
char* OutFileName = "stdout";
char* ChordFileName = NULL;
char Chords[MAXLINE];
char Text[MAXLINE];
char Buffer[MAXLINE];
char* BufferPtr;
char TextSize[MAXLINE];
char ChordSize[MAXLINE];
char TextFont[MAXLINE];
char ChordFont[MAXLINE];
char* TitlePrefix = NULL;
char* TitleSuffix = NULL;
char* SubTitlePrefix = NULL;
char* SubTitleSuffix = NULL;
char* CommentPrefix = "{";
char* CommentSuffix = "}";

FILE* InFile;
FILE* OutFile = stdout;

Boolean ChorusFlag;
Boolean CenterTitleFlag = FALSE;
Boolean ChordFlag = TRUE;
Boolean FingeringFlag = TRUE;

int Transposition = 0;
int TransposeSwitch = FALSE;
int SharpFlatSwitch = FLAT;

int chordPosition;
int textPosition;
int NextChordPosition;
int lineNo;
int Width = DEFAULT_WIDTH;
int LeftMargin = LEFT_MARGIN;
int skipLines = DEFAULT_SKIP_LINES;
int ChordsPerRow = DEFAULT_CHORDS_PER_ROW;
int c;

int getopt(int argc,char** argv,char* optstring);
void processText(int c);
void clear(char* string,int length);
char* transpose(char* chordIn,short numHalfSteps);
int addChord(char* chordName);
ChordList* searchChordList(char* chordName,ChordList* chordList[]);
void emptyList(ChordList* chordList[]);
void addChordToList(Chord* chordPtr,ChordList* chordList[]);
void initialize();
void processChord();
void processFile();
void processDirective();
void outputLine();
void printMargin();
void bomb();
void processSOC();
void processEOC();
void processTextSize();
void processNewSong();
void processTextFont();
void processTitle();
void processComment();
void processChordSize();
void processDefine();
void processSubtitle();
void processChordFont();
void usage();
void skipToEndOfLine();
void initKnownChords();
void printChordsText();
void dumpChords();

/**********************************************************************/
main (int argc, char **argv) {
    int c;
    int errflg = 0;
    Boolean tmpFingeringFlag;
    extern int optind;
    extern char *optarg;
    initKnownChords ();
    initialize ();
    while ((c = getopt (argc, argv, "#bc:C:dDhH:lt:T:24x:o:")) != EOF) {
        switch (c) {
        case '#':
            SharpFlatSwitch = SHARP;
            break;
        case 'b':
            SharpFlatSwitch = FLAT;
            break;
        case 'c':
            strcpy (ChordSize, optarg);
            break;
        case 'C':
            strcpy (ChordFont, optarg);
            break;
        case 'd':
            dumpChords ();
            exit (0);
            break;
        case 'D':
            fprintf (stderr, "Option D not supported.\n");
            break;
        case 'h':
            usage ();
            exit (0);
            break;
        case 'H':
            ChordFileName = optarg;
            break;
        case 'l':
            ChordFlag = FALSE;
            FingeringFlag = FALSE;
            break;
        case 't':
            strcpy (TextSize, optarg);
            break;
        case 'T':
            strcpy (TextFont, optarg);
            break;
        case '2':
            fprintf (stderr, "Option 2 not supported.\n");
            break;
        case '4':
            fprintf (stderr, "Option 4 not supported.\n");
            break;
        case 'x':
            sscanf (optarg, "%d", &Transposition);
            TransposeSwitch = TRUE;
            break;
        case 'o':
            OutFileName = optarg;
            OutFile = fopen (OutFileName, "w");
            if (OutFile == NULL) {
                fprintf (stderr, "chord:  cannot use %s as output.\n",
                         OutFileName);
                exit (1);
            } break;
        default:
            errflg++;
            break;
        }
    } if (errflg) {
        usage ();
    } if (optind < argc) {
        /* read in user defined chords */
        InFile = fopen (ChordFileName, "r");
        if (InFile != NULL) {
            /* disable FingeringFlag so that processFile() of chordrc
             * will not attempt to print out fingering charts */
            tmpFingeringFlag = FingeringFlag;
            FingeringFlag = FALSE;
            processFile ();
            /* restore FingeringFlag */
            FingeringFlag = tmpFingeringFlag;
            fclose (InFile);
        } /* else do nothing -- no user defined chord file exists */ while (optind < argc) {
            InFileName = argv[optind];
            InFile = fopen (InFileName, "r");
            if (InFile == NULL) {
                fprintf (stderr, "chord:  cannot use %s as input.\n",
                         InFileName);
                exit (1);
            } processFile ();
            fclose (InFile);
            optind++;
            if (optind < argc) {
                processNewSong ();
            }
        }
    }
    else {
        InFileName = "stdin";
        InFile = stdin;
        processFile ();
    } return (0);
}

/**********************************************************************/
void processFile (void) {
    lineNo = 1;
    while ((c = getc (InFile)) != EOF) {
        switch (c) {
        case '[':
            processChord ();
            break;
        case '{':               /* } */
            processDirective ();
            break;
        case '\n':
            outputLine ();
            break;
        default:
            processText (c);
            break;
        }
    } if (FingeringFlag /*== TRUE*/ ) {
        printChordsText ();
    }
}

/**********************************************************************/
void initialize() {
    /* clear out the chord and text buffers */
    clear(Chords,MAXLINE);
    clear(Text,MAXLINE);
    chordPosition = textPosition = 0;
    emptyList(InChordList);
}

/**********************************************************************/
void processChord () {
    char chordName[MAXLINE];
    char inChordName[MAXLINE];
    char *transposedName;
    short chordNamePosition;
    short inChordNamePosition;
    short i;

    chordNamePosition = 0;
    inChordNamePosition = 0;
    if (NextChordPosition > chordPosition) {
        chordPosition = NextChordPosition;
        if (ChordFlag /*== TRUE*/ ) {
            textPosition = chordPosition;
        }
    } while ((c = getc (InFile)) != EOF) {
        /* check for bass notes */
        if (c == '/') {
            chordName[chordNamePosition++] = '\0';
            if (TransposeSwitch /* == TRUE */ ) {
                transposedName = transpose (chordName, Transposition);
            }
            else {
                transposedName = chordName;
            } for (i = 0; i < (short) strlen (transposedName); i++) {
                Chords[chordPosition++] = transposedName[i];
                inChordName[inChordNamePosition++] = transposedName[i];
            } chordNamePosition = 0;
            Chords[chordPosition++] = '/';
            inChordName[inChordNamePosition++] = '/';
            c = getc (InFile);
        } if (c == ']') {
            chordName[chordNamePosition++] = '\0';
            if (TransposeSwitch /* == TRUE */ ) {
                transposedName = transpose (chordName, Transposition);
            }
            else {
                transposedName = chordName;
            } for (i = 0; i < (short) strlen (transposedName); i++) {
                Chords[chordPosition++] = transposedName[i];
                inChordName[inChordNamePosition++] = transposedName[i];
            } break;
        } if (c == '\n') {
            bomb ();
        } chordName[chordNamePosition++] = (char) c;
    } /* set next available position for a new chord */ NextChordPosition = chordPosition + 1;
    inChordName[inChordNamePosition] = '\0';
    addChord (inChordName);
}

/**********************************************************************/
void processDirective () {
    char keyWord[MAXLINE];
    char *keyWordPtr;
    int bufferPosition;

    bufferPosition = 0;
    while ((c = getc (InFile)) != EOF) {
        /* { */
        if (c == '}') {
            break;
        } if (c == ']') {
            bomb ();
        } if (c == '\n') {
            bomb ();
        } Buffer[bufferPosition++] = (char) c;
    } Buffer[bufferPosition++] = '\0';
    keyWordPtr = keyWord;
    BufferPtr = Buffer;
    while ((*BufferPtr != ':') && (*BufferPtr != '\0')) {
        *keyWordPtr = *BufferPtr;
        BufferPtr++;
        keyWordPtr++;
    } *keyWordPtr = '\0';
    /* BufferPtr is pointing at the ':' */
    BufferPtr++;
    /* BufferPtr is pointing at the character after the ':' */

    if ((strcmp (keyWord, "start_of_chorus") == 0) ||
        (strcmp (keyWord, "soc") == 0)) {
        processSOC ();
    }
    else if ((strcmp (keyWord, "end_of_chorus") == 0) ||
             (strcmp (keyWord, "eoc") == 0)) {
        processEOC ();
    }
    else if ((strcmp (keyWord, "comment") == 0) ||
             (strcmp (keyWord, "c") == 0)) {
        processComment ();
    }
    else if ((strcmp (keyWord, "new_song") == 0) ||
             (strcmp (keyWord, "ns") == 0)) {
        processNewSong ();
    }
    else if ((strcmp (keyWord, "title") == 0) ||
             (strcmp (keyWord, "t") == 0)) {
        processTitle ();
    }
    else if ((strcmp (keyWord, "subtitle") == 0) ||
             (strcmp (keyWord, "st") == 0)) {
        processSubtitle ();
    }
    else if (strcmp (keyWord, "define") == 0) {
        processDefine ();
    }
    else if (strcmp (keyWord, "textfont") == 0) {
        processTextFont ();
    }
    else if (strcmp (keyWord, "textsize") == 0) {
        processTextSize ();
    }
    else if (strcmp (keyWord, "chordfont") == 0) {
        processChordFont ();
    }
    else if (strcmp (keyWord, "chordsize") == 0) {
        processChordSize ();
    } skipToEndOfLine ();
}

/**********************************************************************/
void outputLine () {
    int i;
    if ((chordPosition > 0) || (textPosition > 0)) {
        Chords[chordPosition] = '\0';
        Text[textPosition] = '\0';
        if (ChordFlag /*== TRUE*/ ) {
            if (ChordFont != NULL) {
                fprintf (OutFile, "%s", ChordFont);
            } if (ChordSize != NULL) {
                fprintf (OutFile, "%s", ChordSize);
            } printMargin ();
            fprintf (OutFile, "%s\n", Chords);
        } if (TextFont != NULL) {
            fprintf (OutFile, "%s", TextFont);
        } if (TextSize != NULL) {
            fprintf (OutFile, "%s", TextSize);
        } printMargin ();
        fprintf (OutFile, "%s\n", Text);
        chordPosition = textPosition = NextChordPosition = 0;
        clear (Chords, MAXLINE);
        clear (Text, MAXLINE);
    }
    else {
        fprintf (OutFile, "\n");
    } for (i = 0; i < skipLines; i++) {
        if (ChorusFlag /*== TRUE*/ ) {
            printMargin ();
        } fprintf (OutFile, "\n");
    } lineNo++;
}

/**********************************************************************/
void printMargin () {
    int i;
    for (i = 0; i < LeftMargin; i++) {
        fprintf (OutFile, " ");
    } if (ChorusFlag /*== TRUE*/ ) {
        fprintf (OutFile, "%c", CHORUS_CHAR);
    }
    else {
        fprintf (OutFile, " ");
    }
}

/**********************************************************************/
void processText (int c) {
    if (c == COMMENT_CHAR) {
        if (textPosition == 0) {
            while (getc (InFile) != '\n');
        }
    }
    else {
        Text[textPosition++] = c;
        chordPosition = textPosition;
    }
}

/**********************************************************************/
void bomb() {
    fprintf(stderr,"chord: Invalid input on line %d\n",lineNo);
    exit(1);
}

/**********************************************************************/
void clear(char* string,int length) {
    int i;

    for (i=0;i < length;i++) {
        string[i] = ' ';
    }
}

/**********************************************************************/
void processSOC() {
    ChorusFlag = TRUE;
}

/**********************************************************************/
void processEOC() {
    ChorusFlag = FALSE;
}

/**********************************************************************/
void processTextSize() {
    strcpy(TextSize,BufferPtr);
}

/**********************************************************************/
void processTextFont() {
    strcpy(TextFont,BufferPtr);
}

/**********************************************************************/
void processNewSong() {
    fprintf(OutFile,"%c",NEWPAGE);
    initialize();
}

/**********************************************************************/
void processTitle () {
    int leftMargin;
    int i;
    if (CenterTitleFlag /*== TRUE*/ ) {
        leftMargin = (Width - (int) strlen (BufferPtr)) / 2;
        for (i = 0; i < leftMargin; i++) {
            fprintf (OutFile, " ");
        }
    } if (TitlePrefix != NULL) {
        fprintf (OutFile, TitlePrefix);
    } fprintf (OutFile, BufferPtr);
    if (TitleSuffix != NULL) {
        fprintf (OutFile, TitleSuffix);
    } fprintf (OutFile, "\n");
}

/**********************************************************************/
void processSubtitle () {
    int leftMargin;
    int i;
    if (CenterTitleFlag /*== TRUE*/ ) {
        leftMargin = (Width - (int) strlen (BufferPtr)) / 2;
        for (i = 0; i < leftMargin; i++) {
            fprintf (OutFile, " ");
        }
    } if (SubTitlePrefix != NULL) {
        fprintf (OutFile, SubTitlePrefix);
    } fprintf (OutFile, BufferPtr);
    if (SubTitleSuffix != NULL) {
        fprintf (OutFile, SubTitleSuffix);
    } fprintf (OutFile, "\n");
} /**********************************************************************/ void processComment ()
{
    printMargin ();
    if (CommentPrefix != NULL) {
        fprintf (OutFile, CommentPrefix);
    } fprintf (OutFile, BufferPtr);
    if (CommentSuffix != NULL) {
        fprintf (OutFile, CommentSuffix);
    } fprintf (OutFile, "\n");
}

/**********************************************************************/
void processChordSize() {
    strcpy(ChordSize,BufferPtr);
}

/**********************************************************************/
void processChordFont() {
    strcpy(ChordFont,BufferPtr);
}

/**********************************************************************/
void processDefine () {
    char *chordName;
    int e1, a, d, g, b, e2, offset;

    chordName = strtok (BufferPtr, " ,}");
    e1 = atoi (strtok (NULL, " ,}"));
    a = atoi (strtok (NULL, " ,}"));
    d = atoi (strtok (NULL, " ,}"));
    g = atoi (strtok (NULL, " ,}"));
    b = atoi (strtok (NULL, " ,}"));
    e2 = atoi (strtok (NULL, " ,}"));
    offset = atoi (strtok (NULL, " ,}"));
    learnChord (chordName, (char) e1, (char) a, (char) d, (char) g, (char) b,
                (char) e2);
}

/**********************************************************************/
char *transpose (char *chordIn, short numHalfSteps) {
    char *currPtr;
    char chordRoot;
    static char newChord[16];
    currPtr = chordIn;
    /* check for valid chord */
    if (islower (*currPtr)) {
        chordRoot = toupper (*currPtr);
    }
    else {
        chordRoot = *currPtr;
    } if ((chordRoot >= 'A') && (chordRoot <= 'G')) {
        switch (chordRoot) {
        case 'A':
        case 'B':
            chordRoot = (chordRoot - 'A') * 2;
            break;
        case 'C':
        case 'D':
        case 'E':
            chordRoot = ((chordRoot - 'A') * 2) - 1;
            break;
        case 'F':
        case 'G':
            chordRoot = ((chordRoot - 'A') * 2) - 2;
            break;
        } currPtr++;
        if (*currPtr != '\0') {
            if (*currPtr == '#') {
                chordRoot++;
                /* make currPtr point to remainder of the chord */
                currPtr++;
            }
            else if (*currPtr == FLAT_CHAR) {
                chordRoot--;
                /* make currPtr point to remainder of the chord */
                currPtr++;
            }
        } chordRoot += numHalfSteps;
        /* adjust for wrap past G */
        if (chordRoot > 11) {
            chordRoot %= 12;
        } /* adjust for wrap below A */ 
        else
            while (chordRoot < 0) {
                chordRoot += 12;
        } if (SharpFlatSwitch == FLAT) {
            sprintf (newChord, "%s%s", FlatNotes[chordRoot], currPtr);
        }
        else {
            sprintf (newChord, "%s%s", SharpNotes[chordRoot], currPtr);
        }
    }
    else {
        strcpy (newChord, chordIn);
    } return (newChord);
}

/**********************************************************************/
void usage(void) {
    fprintf(stderr,"chord usage:\n");
    fprintf(stderr,"arg\t\t\t\tMeaning\n");
    fprintf(stderr,"---\t\t\t\t-------\n");
    fprintf(stderr,"-#\t\tPrint chords using sharps\n");
    fprintf(stderr,"-b\t\tPrint chords using flats\n");
    fprintf(stderr,"-c text\t\tText to be printed on the \"chord\" lines.\n");
    fprintf(stderr,"\t\t    This text can be used to set the font size.\n");
    fprintf(stderr,"-C text\t\tText to be printed on the \"chord\" lines.\n");
    fprintf(stderr,"\t\t    This text can be used to set the font.\n");
    fprintf(stderr,"-d\t\tDumps the list and description of all\n");
    fprintf(stderr,"\t\t    internally known chords\n");
    fprintf(stderr,"-D\t\tPrints debugging information.\n");
    fprintf(stderr,"\t\t    For programmers only.  Not implemented.\n");
    fprintf(stderr,"-h\t\tPrints a short options summary.\n");
    fprintf(stderr,"-l\t\tPrints only the lyrics of the song.\n");
    fprintf(stderr,"-t text\t\tText to be printed on the \"text\" lines.\n");
    fprintf(stderr,"\t\t    This text can be used to set the font size.\n");
    fprintf(stderr,"-T text\t\tText to be printed on the \"text\" lines.\n");
    fprintf(stderr,"\t\t    This text can be used to set the font.\n");
    fprintf(stderr,"-2\t\tPrints two logical pages per physical page.\n");
    fprintf(stderr,"\t\t    Not implemented.\n");
    fprintf(stderr,"-4\t\tPrints four logical pages per physical page.\n");
    fprintf(stderr,"\t\t    Not implemented.\n");
    fprintf(stderr,"-x[+|-]number\tTransposes all chords up or down\n");
    fprintf(stderr,"\t\t    this number of half-steps\n");
}

/**********************************************************************/
void skipToEndOfLine() {
    int c;

    while (((c = getc(InFile)) != EOF) && (c != '\n')) {
    }
}

/**********************************************************************/
int addChord (char *chordName) {
    ChordList *chordListPtr;
    /* search for chord in the known chords */
    if ((chordListPtr = searchChordList (chordName, KnownChordList)) != NULL) {
        if (searchChordList (chordName, InChordList) == NULL) {
            while ((chordListPtr != NULL) &&
                (strcmp (chordName, chordListPtr->chord->chordName) == 0)) {
                addChordToList (chordListPtr->chord, InChordList);
                chordListPtr = chordListPtr->next;
            }
        }
    }
}

/**********************************************************************/
void emptyList (ChordList * chordList[]) {
    int i;
    ChordList *currPtr;
    ChordList *nextCurrPtr;
    for (i = 0; i < 7; i++) {
        currPtr = chordList[i];
        chordList[i] = NULL;
        while (currPtr != NULL) {
            nextCurrPtr = currPtr->next;
            free (currPtr);
            currPtr = nextCurrPtr;
        }
    }
}

/**********************************************************************/
ChordList *searchChordList (char *chordName, ChordList * chordList[]) {
    char *currPtr;
    char chordRoot;
    ChordList *list;
    currPtr = chordName;
    /* check for valid chord */

    if (islower (*currPtr)) {
        chordRoot = toupper (*currPtr);
    }
    else {
        chordRoot = *currPtr;
    } if ((chordRoot >= 'A') && (chordRoot <= 'G')) {
        /* search the list for the chord fingering */
        list = chordList[chordRoot - 'A'];
        while (list != NULL) {
            if (strcmp (list->chord->chordName, chordName) == 0) {
                break;
            } list = list->next;
        } if (list != NULL) {
            return (list);
        }
        else {
            return (NULL);
        }
    }
    else {
        return (NULL);
    }
}

/**********************************************************************/
void addChordToList (Chord * chordPtr, ChordList * chordList[]) {
    char *currPtr;
    char chordRoot;
    ChordList *newChordListElement;
    ChordList *tmpPtr;
    ChordList *foundChordListElement;

    currPtr = chordPtr->chordName;
    /* check for valid chord */
    if (islower (*currPtr)) {
        chordRoot = toupper (*currPtr);
    }
    else {
        chordRoot = *currPtr;
    } if ((chordRoot >= 'A') && (chordRoot <= 'G')) {
        /* search the list for the chord fingering */
        if ((foundChordListElement = searchChordList (chordPtr->
                                           chordName, chordList)) == NULL) {
            newChordListElement = (ChordList *) malloc (sizeof (ChordList));
            newChordListElement->chord = chordPtr;
            tmpPtr = chordList[chordRoot - 'A'];
            chordList[chordRoot - 'A'] = newChordListElement;
            newChordListElement->next = tmpPtr;
        }
        else {
            newChordListElement = (ChordList *) malloc (sizeof (ChordList));
            newChordListElement->chord = chordPtr;
            newChordListElement->next = foundChordListElement->next;

            foundChordListElement->next = newChordListElement;
        }
    }
}

/**********************************************************************/
void initKnownChords() {
    learnChord("Ab",1,3,3,2,1,1,4);
    learnChord("Ab7",-1,-1,1,1,1,2,1);
    learnChord("Absus",-1,-1,1,1,2,4,1);
    learnChord("Ab+",-1,-1,2,1,1,0,1);
    learnChord("Abmaj7",-1,-1,1,1,1,3,1);
    learnChord("A",-1,0,2,2,2,0,1);
    learnChord("Am",-1,0,2,2,1,0,1);
    learnChord("A7",-1,0,2,0,2,0,1);
    learnChord("Am7",-1,0,2,2,1,3,1);
    learnChord("Asus",-1,-1,2,2,3,0,1);
    learnChord("A+",-1,0,3,2,2,1,1);
    learnChord("Adim",-1,-1,1,2,1,2,1);
    learnChord("Amaj7",-1,0,2,1,2,0,1);
    learnChord("Bb",-1,1,3,3,3,1,1);
    learnChord("Bbm",-1,1,3,3,2,1,1);
    learnChord("Bb7",-1,-1,1,1,1,2,3);
    learnChord("Bbm7",-1,1,3,1,2,1,1);
    learnChord("Bbsus",-1,-1,3,3,4,1,1);
    learnChord("Bb+",-1,-1,0,3,3,2,1);
    learnChord("Bbdim",-1,-1,2,3,2,3,1);
    learnChord("Bbmaj7",-1,1,3,2,3,-1,1);
    learnChord("B",-1,2,4,4,4,2,1);
    learnChord("Bm",-1,2,4,4,3,2,1);
    learnChord("B7",0,2,1,2,0,2,1);
    learnChord("Bm7",-1,1,3,1,2,1,2);
    learnChord("Bsus",-1,-1,3,3,4,1,2);
    learnChord("B+",-1,-1,1,0,0,4,1);
    learnChord("Bdim",-1,-1,0,1,0,1,1);
    learnChord("Bmaj7",-1,2,4,3,4,-1,1);
    learnChord("C",0,3,2,0,1,0,1);
    learnChord("Cm",-1,1,3,3,2,1,3);
    learnChord("C7",0,3,2,3,1,0,1);
    learnChord("Cm7",-1,1,3,1,2,1,3);
    learnChord("Csus",-1,-1,3,0,1,3,1);
    learnChord("C+",-1,-1,2,1,1,0,1);
    learnChord("Cdim",-1,-1,1,2,1,2,1);
    learnChord("Cmaj7",-1,3,2,0,0,0,1);
    learnChord("C#m",-1,-1,2,1,2,0,1);
    learnChord("C#m7",-1,-1,2,4,2,4,1);
    learnChord("C#dim",-1,-1,2,3,2,3,1);
    learnChord("Db",-1,-1,3,1,2,1,1);
    learnChord("Db7",-1,-1,3,4,2,4,1);
    learnChord("Dbsus",-1,-1,3,3,4,1,4);
    learnChord("Dbdim",-1,-1,2,3,2,3,1);
    learnChord("Dbmaj7",-1,4,3,1,1,1,1);
    learnChord("D",-1,-1,0,2,3,2,1);
    learnChord("Dm",-1,-1,0,2,3,1,1);
    learnChord("D7",-1,-1,0,2,1,2,1);
    learnChord("Dm7",-1,-1,0,2,1,1,1);
    learnChord("Dsus",-1,-1,0,2,3,3,1);
    learnChord("D+",-1,-1,0,3,3,2,1);
    learnChord("Ddim",-1,-1,0,1,0,1,1);
    learnChord("Dmaj7",-1,-1,0,1,1,1,1);
    learnChord("D#dim",-1,-1,1,2,1,2,1);
    learnChord("Eb",-1,-1,3,1,2,1,3);
    learnChord("Ebm",-1,-1,4,3,4,2,1);
    learnChord("Eb7",-1,-1,1,3,2,3,1);
    learnChord("Ebm7",-1,-1,1,3,2,2,1);
    learnChord("Ebsus",-1,-1,1,3,4,4,1);
    learnChord("Eb+",-1,-1,1,0,0,4,1);
    learnChord("Ebmaj7",-1,-1,1,3,3,3,1);
    learnChord("E",0,2,2,1,0,0,1);
    learnChord("Em",0,2,2,0,0,0,1);
    learnChord("E7",0,2,2,1,3,0,1);
    learnChord("Em7",0,2,2,0,3,0,1);
    learnChord("Esus",0,2,2,2,0,0,1);
    learnChord("E+",-1,-1,2,1,1,0,1);
    learnChord("Edim",-1,-1,2,3,2,3,1);
    learnChord("Emaj7",0,2,1,1,0,-1,1);
    learnChord("F",1,3,3,2,1,1,1);
    learnChord("Fm",1,3,3,1,1,1,1);
    learnChord("F7",1,3,1,2,1,1,1);
    learnChord("Fm7",1,3,1,1,1,1,1);
    learnChord("Fsus",-1,-1,3,3,1,1,1);
    learnChord("F+",-1,-1,3,2,2,1,1);
    learnChord("Fdim",-1,-1,0,1,0,1,1);
    learnChord("Fmaj7",-1,3,3,2,1,0,1);
    learnChord("Gbmaj7",-1,-1,4,3,2,1,1);
    learnChord("G",3,2,0,0,0,3,1);
    learnChord("Gm",1,3,3,1,1,1,3);
    learnChord("G7",3,2,0,0,0,1,1);
    learnChord("Gm7",1,3,1,1,1,1,3);
    learnChord("Gsus",-1,-1,0,0,1,3,1);
    learnChord("G+",-1,-1,1,0,0,4,1);
    learnChord("Gdim",-1,-1,2,3,2,3,1);
    learnChord("Gmaj7",-1,-1,1,2,3,4,2);
    learnChord("G#m",1,3,3,1,1,1,4);
    learnChord("G#m7",-1,-1,1,1,1,1,4);
    learnChord("G#dim",-1,-1,0,1,0,1,1);

    ChordFileName = (char*)malloc(strlen("s:.chordrc") + 1);
    strcpy(ChordFileName,"s:.chordrc");
}

/**********************************************************************/
learnChord(char* chord,int e1,int a,int d,int g,int b,int e2,int offset) {
    Chord* newChord;

    newChord = (Chord*) malloc(sizeof(Chord));
    newChord->chordName = (char*)malloc(strlen(chord) + 1);
    strcpy(newChord->chordName,chord);
    newChord->fingering[0] = (char)e1;
    newChord->fingering[1] = (char)a;
    newChord->fingering[2] = (char)d;
    newChord->fingering[3] = (char)g;
    newChord->fingering[4] = (char)b;
    newChord->fingering[5] = (char)e2;
    newChord->fingering[6] = '\0';
    newChord->offset = offset;
    addChordToList(newChord,KnownChordList);
}

/**********************************************************************/
void printChordsText () {
    int i, j, lines, across;
    ChordList *currPtr;

    fprintf (OutFile, "\n");
    for (lines = 1; lines < skipLines; lines++) {
        fprintf (OutFile, "\n");
    } across = 0;
    for (i = 0; i < 7; i++) {
        currPtr = InChordList[i];
        while (currPtr != NULL) {
            if ((across / ChordsPerRow) >= 1) {
                across = 1;
                fprintf (OutFile, "\n");
                for (lines = 1; lines < skipLines; lines++) {
                    fprintf (OutFile, "\n");
                }
            }
            else {
                across++;
            } fprintf (OutFile, "%6s - ", currPtr->chord->chordName);
            for (j = 0; j < 6; j++) {
                if (currPtr->chord->fingering[j] >= 0) {
                    fprintf (OutFile, "%d ",
                             currPtr->chord->fingering[j] +
                             currPtr->chord->offset - 1);
                }
                else {
                    fprintf (OutFile, "X ");
                }
            } fprintf (OutFile, "   ");
            currPtr = currPtr->next;
        }
    } fprintf (OutFile, "\n");
}

/**********************************************************************/
void dumpChords () {
    int i, j;
    ChordList *currPtr;

    for (i = 0; i < 7; i++) {
        currPtr = KnownChordList[i];
        while (currPtr != NULL) {
            fprintf (OutFile, " %s\t\t", currPtr->chord->chordName);
            for (j = 0; j < 6; j++) {
                fprintf (OutFile, "  %2d",
                         currPtr->chord->fingering[j]);
            } fprintf (OutFile, "\t\t%2d\n",
                       currPtr->chord->offset);
            currPtr = currPtr->next;
        }
    } fprintf (OutFile, "\n");
}

