/*********************************************************************
Author      : Charles Bradley Slaten
*********************************************************************/
static char sccs_id[]="@(#) xpose.c 1.4 Delta Date/Time: 11 Aug 1992/15:56:11;Extraction Date/Time: 11 Aug 1992/15:56:48 @(#)";

#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

#ifndef TRUE /* { */
#       define TRUE 1
#endif /* TRUE } */
#ifndef FALSE /* { */
#       define FALSE 0
#endif /* FALSE } */

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;

#ifdef ANSI /* { */
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[]);

#else /* ANSI }{ */
int getopt();
void processText();
void clear();
char* transpose();
int addChord();
ChordList* searchChordList();
void emptyList();
void addChordToList();

#endif /* ANSI } */

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();

/**********************************************************************/
#ifdef ANSI /* { */
main(int argc,char** argv)
#else /* ANSI }{ */
main(argc,argv)
int argc;
char** argv;
#endif /* ANSI } */
{
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);
}

/**********************************************************************/
#ifdef ANSI /* { */
void
processFile(void)
#else /* ANSI }{ */
void processFile()
#endif /* ANSI } */
{
        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," ");
        }
}
/**********************************************************************/
#ifdef ANSI /* { */
void
processText(int c)
#else /* ANSI }{ */
void processText(c)
int c;
#endif /* ANSI } */
{
        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);
}

/**********************************************************************/
#ifdef ANSI /* { */
void
clear(char* string,int length)
#else /* ANSI }{ */
void clear(string,length)
char* string;
int length;
#endif /* ANSI } */
{
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);
}

/**********************************************************************/
#ifdef ANSI /* { */
char*
transpose(char* chordIn,short numHalfSteps)
#else /* ANSI }{ */
char* transpose(chordIn,numHalfSteps)
char* chordIn;
short numHalfSteps;
#endif /* ANSI } */
{
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);
}

/**********************************************************************/
#ifdef ANSI /* { */
void
usage(void)
#else /* ANSI }{ */
void usage()
#endif /* ANSI } */
{
        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')) {
        }
}

/**********************************************************************/
#ifdef ANSI /* { */
int
addChord(char* chordName)
#else /* ANSI }{ */
int addChord(chordName)
char* chordName;
#endif /* ANSI } */
{
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;
                        }
                }
        }
}

/**********************************************************************/
#ifdef ANSI /* { */
void
emptyList(ChordList* chordList[])
#else /* ANSI }{ */
void emptyList(chordList)
ChordList* chordList[];
#endif /* ANSI } */
{
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;
                }
        }
}

/**********************************************************************/
#ifdef ANSI /* { */
ChordList*
searchChordList(char* chordName,ChordList* chordList[])
#else /* ANSI }{ */
ChordList* searchChordList(chordName,chordList)
char* chordName;
ChordList* chordList[];
#endif /* ANSI } */
{
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);
        }
}

/**********************************************************************/
#ifdef ANSI /* { */
void
addChordToList(Chord* chordPtr,ChordList* chordList[])
#else /* ANSI }{ */
void addChordToList(chordPtr,chordList)
Chord* chordPtr;
ChordList* chordList[];
#endif /* ANSI } */
{
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);
#if defined(MSDOS) /* { */
        ChordFileName = (char*)malloc(strlen("chord.rc") + 1);
        sprintf(ChordFileName,"chord.rc");
#elif defined(AMIGA)
        ChordFileName = (char *)malloc(strlen("s:.chordrc")+1);
        sprintf(ChordFileName,"s:.chordrc");
#else /* } { */
        ChordFileName = (char*)malloc(strlen(getenv("HOME")) + 1 +
                strlen(".chordrc") + 1);
        sprintf(ChordFileName,"%s/.chordrc",getenv("HOME"));
#endif /* MSDOS } */
}

/**********************************************************************/
#ifdef ANSI /* { */
learnChord(char* chord,int e1,int a,int d,int g,int b,int e2,int offset)
#else /* ANSI }{ */
learnChord(chord, e1, a, d, g, b, e2, offset)
char* chord;
int e1,
        a,
        d,
        g,
        b,
        e2,
        offset;
#endif /* ANSI } */
{
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;
int j;
int lines;
int 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;
int 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");
}
