[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference 7.286::golf

Title:Welcome to the Golf Notes Conference!
Notice:FOR SALE notes in Note 69 please! Intros in note 863 or 61.
Moderator:FUNYET::ANDERSON
Created:Tue Feb 15 1994
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:2129
Total number of notes:21499

1387.0. "Yet Another Golf Handicapping Program" by DECWET::COLGATE () Wed Oct 30 1991 15:54

I've written a golf handicapping program I'd like to share:

The next two replies are:

 .1 The source code (C)
 .2 A sample data file (my actual scores :-))

The data file can be edited with your favorite editor, and the program
runs on VMS and Ultrix.

for building and running on VMS:

$ cc handicap
$ link handicap

$ run handicap
or
$ handicap :== $<directory>handicap

The latter is preferable, as you can pass command line arguments to the 
program. (i.e. handicap -s).

for building on Ultrix,

% make handicap
or
% cc -o handicap handicap.c

% handicap 

The command line arguments are: h, s, and n.

the h flag prints out the scores that make up your handicap.
the s flag prints out your scores in sorted (by differential).
the n flag takes a file name (the default file name is your username).

for example:

{$,%} handicap -h -nfoo

Would print out the handicapped scores for "foo" (file name "foo.scores").
You can mix the flags together:

{$,%} handicap -hs 
{$,%} handicap -h -s

are both acceptable and produce identical results.

I'm making the sources available so that if you're inclined to make adjustments,
enhancements, etc. you can.

Wim
T.RTitleUserPersonal
Name
DateLines
1387.1DECWET::COLGATEWed Oct 30 1991 15:55683
/*
 * handicap.c
 *
 * This program reads the data file handicap.dat for recent golf 
 * scores and calculates ones handicap based on the USGA handicap
 * formula.
 *
 *
 */

#include <stdio.h>
#include <math.h>

#ifdef VMS
#include <jpidef.h>
#endif

int curr_max_rounds = 2 ;
int num_rounds = 0 ;

#define MAX_LINE 256
#define MAX_COURSE 128
#define MAX_DATE 16
#define MAX_NAME 32
#define MAX_FILE_NAME (MAX_NAME+7) /* username + ".scores" */

typedef struct record {
    int    chronological_order ;
    char   date [MAX_DATE];
    char   course_name [MAX_COURSE];
    double rating ;
    int    slope ;
    int    raw_score ;
    double differential ;
    int    include_flag ;
} record ;


record *
read_data_base(filename)
char *filename ;

/*
 * read in the data base (max line length is MAX_LINE)
 */

{

    double shots ;
    double rating ;
    double slope ;
    double get_value() ;
    record *new_history ;
    FILE *fd ;
    char *cp ;
    char *first_real_char() ;
    char line_buf [ MAX_LINE ] ;
    int i ;
    int index = 0 ;
    double dtemp ;
    int temp ;
    record *history ;

    /*
     * open the file
     */

    fd = fopen( filename, "r" ) ;
    if ( fd == NULL ) 
    {
        fprintf(stderr, "Can't open file: %s\n", filename ) ;
        exit( 1 ) ;
    }

    history = (record *)malloc( (unsigned int)(sizeof( record ) * curr_max_rounds ) ) ;
    memset( (void *)history, 0, sizeof(record) * curr_max_rounds ) ;
    /*
     * Loop until the file is empty 
     */

    do
    {
        /*
         * We are done if the fgets fails to return anything
         */

        if (fgets(line_buf, sizeof(line_buf), fd) == NULL)
            break ;

        /*
         * compress the white space and strip out comments
         */

        compress(line_buf) ;

        /*
         * If anything is there...
         */

        if (strlen(line_buf) > 0)
        {

            /*
             * mark the chronological order
             */

            history[index].chronological_order = index ;

            /*
             * get the raw score
             */

            cp = first_real_char( 'S', line_buf ) ;
            shots = get_value( cp ) ;
            history[index].raw_score  = shots ;

            /*
             * get the course rating 
             */

            cp = first_real_char( 'R', line_buf ) ;
            rating = get_value( cp ) ;
            history[index].rating = rating ;

            /*
             * get the course slope if no slope, then slope is 113.
             */

            cp = first_real_char( 'L', line_buf ) ;
            slope = get_value( cp ) ;
            if (slope == 0)
                slope = 113 ;

            history[index].slope = slope ;

            /*
             * get date string
             */

            cp = first_real_char( 'D', line_buf ) ;
            get_string( history[index].date, cp, 8 ) ;                   /* get string, max 8 characters */

            /*
             * get course name
             */

            cp = first_real_char('C', line_buf ) ;
            get_string( history[index].course_name, cp, 0 ) ;           /* get string, to EOL */

            /*
             * calculate differential (round to nearest 10th).
             */

            dtemp = ( ( shots - rating ) * 113 ) / slope ;
            temp = ( dtemp + .05 ) * 10 ;
            dtemp = (double) temp / 10 ;
            history[index].differential = dtemp ;

            /*
             * clear include flag
             */

            history[index].include_flag = 0 ;

            index ++ ;
            if (index == curr_max_rounds)
            {
                new_history = (record *)malloc( (unsigned int)(sizeof( record ) * 2 * curr_max_rounds ) ) ;
                memset( (void *)new_history, 0, sizeof(record) * 2 * curr_max_rounds ) ;
                for(i = 0; i < curr_max_rounds; i++)
                    new_history[ i ] = history[ i ] ;               /* may need funky copy */
                free( history ) ;
                history = new_history ;
                curr_max_rounds *=2 ;
            }
        }
    } while ( 1 ) ;

    fclose( fd ) ;

    num_rounds = index ;

    return ( history ) ;
}


get_string( destination, source, length )
char *destination ;
char *source ;
int length ;

/*
 * copy the string from source to destination, copying
 * length characters, if length == 0, then copy to 
 * EOL ('/0')
 */
{

    if ( length != 0 )
        strncpy( destination, source, length ) ;
    else
        strcpy( destination, source ) ;

}



double
get_value( line ) 
char *line ;

/*
 * get the value from a string (delimeted by one of the
 * whitespaces (space, tab, NULL)
 */

{
    double strtod() ;
    char temp [ MAX_LINE ] ;
    char *tmp = temp ;
    double value ;

    while( (*line != ' ') && (*line !='\t') && (*line != '\0') )
         *tmp++ = *line++ ;

    *tmp++ = '\0' ;

    value = strtod( temp, 0 ) ;

    return( value ) ;
}


char *
first_real_char( after, line ) 
char after ;
char *line ;

/*
 * using the 'after' character, point directly to the 
 * first non-whitespace character and return that pointer
 */

{
    char *cp = line ;

    /*
     * skip everything up to the 'after' character
     */

    while( *cp != after && *cp != '\0' )
        cp++ ;

    /*
     * skip the 'after' character
     */

    cp++ ;

    /*
     * skip any spaces or tabs...
     */

    while( *cp == ' ' || *cp == '\t' || *cp == '\0')
        cp++ ;

    return( cp ) ;

}

compress( line )
char *line ;

/* 
 * Compress all whitespace characters (space, tabs) into single
 * whitespace characters - also strip off comments
 */

{
    char temp [ MAX_LINE ] ;
    char *s, *d ;
    int in_white_space = 0 ;

    s = line ;
    d = temp ;

    *d = '\0' ;
    while ( *s != '\0')
    {

        /* 
         * a comment terminates the line 
         */

        if ( *s == '#')
            break ;

        if ( ( (*s == ' ') || (*s == '\t') ) && !in_white_space )
        {
            *d++ = ' ' ;
            s++ ;
            in_white_space = 1 ;
        }
        else
        if ( (*s == ' ') || (*s == '\t') )
            *s++ ;
        else
        {
            if (*s != '\n')
                *d++ = *s++ ;
            else
                *s++ ;
            in_white_space = 0 ;
        }
    }
    *d = '\0' ;

    /* 
     * copy back the compressed line 
     */

    strcpy( line, temp ) ;
}


int 
copy_list( dst, src, start, end ) 
record *dst ;
record *src ;
int start ;
int end ;
{

    int i ;
    int j = 0 ;

    for( i = start; i <= end; i++ )
    {
        dst[ j++ ] = src[ i ] ;
    }
}


int 
sort_list( list, rounds_played )
record *list ;
int rounds_played ;

/*
 * Down and dirty bubblesort 
 */

{

    int i, j ;
    record temp ;

    for( i = 0 ; i < rounds_played; i++ )
        for( j= i+1; j < rounds_played ; j++ )
        {
            if ( list[ i ].differential > list[ j ].differential )
            {
                temp = list[ i ] ;
                list[ i ] = list[ j ] ;
                list[ j ] = temp ;
            }
            else 

            /*
             * if a tie, then score entered later counts (higher is better)
             */

            if ( list[ i ].differential == list[ j ].differential )
                if ( list[i].chronological_order < list[j].chronological_order )
                {
                    temp = list[ i ] ;
                    list[ i ] = list[ j ] ;
                    list[ j ] = temp ;
                }
        }
}


mark_list( list, total_rounds_played )
record *list ;
int total_rounds_played ;
{

    record *sorted_list ;
    int start, end ;
    int rounds_to_sort ;
    int i ;
    int rounds_to_mark ;

    end = total_rounds_played ;
    if ( total_rounds_played >= 20 )
    {
        start = total_rounds_played - 20 ;
    }
    else
        start = 0 ;

    rounds_to_sort = end - start ;

    sorted_list = (record *)malloc( (unsigned int)(sizeof( record ) * rounds_to_sort ) ) ;
    memset( (void *)sorted_list, 0, sizeof(record) * rounds_to_sort ) ;

    copy_list( sorted_list, list, start, end - 1) ;

    sort_list( sorted_list, rounds_to_sort ) ;

    rounds_to_mark = 10 ;

    switch( rounds_to_sort )
    {
        case 5:
        case 6:
            rounds_to_mark = 1 ;
            break ;
        case 7:
        case 8:  rounds_to_mark = 2 ;
            break ;
        case 9:
        case 10: rounds_to_mark = 3 ;
            break ;
        case 11:
        case 12: rounds_to_mark = 4 ;
            break ;
        case 13:
        case 14: rounds_to_mark = 5 ;
            break ;
        case 15:
        case 16: rounds_to_mark = 6 ;
            break ;
        case 17: rounds_to_mark = 7 ;
            break ;
        case 18: rounds_to_mark = 8 ;
            break ;
        case 19: rounds_to_mark = 9 ;
            break ;
    }

    for( i=0; i < rounds_to_mark ; i++)
    {
        list[ sorted_list[ i ].chronological_order].include_flag = 1 ;
    }

    free( sorted_list ) ;
}


double 
calculate_handicap( list, rounds_played )
record *list ;
int rounds_played ;
{

    int i ;
    int count = 0 ;
    double total = 0.0 ;
    double handicap = 0.0 ;
    int temp ;

    for( i = 0; i < rounds_played; i++ )
    {
        if (list[i].include_flag == 1)
        {
            total += list[i].differential ;
            count++ ;
        }
    }

    handicap = (total/count * .96) * 10 ;
    temp = handicap ;
    handicap = (double)temp / 10 ;

    return( handicap ) ;
}

print_scores( list, rounds_played, flag, sort_flag )
record *list ;
int rounds_played ;
char flag ;
int sort_flag ;
{
    int i ;
    char prefix ;

    printf("Date     Course Name                           Score  Rating Slope Differential\n") ;
    printf("======== ===================================== ====== ====== ===== ============\n") ;

    for( i = 0; i < rounds_played; i++ )
    {
        if (list[i].include_flag == 1)
            prefix = '*' ;
        else 
            prefix = ' ' ;

        switch( flag )
        {
            case 'a': if ((rounds_played - i == 20) && (sort_flag == 0) )  
                          printf("-------- ------------------------------------- ------ ------ ----- ------------\n") ;
                      printf("%8s %-37.37s   %3d   %4.1f   %3d  %4.1f %c\n",
                          list[i].date,
                          list[i].course_name ,
                          list[i].raw_score,
                          list[i].rating,
                          list[i].slope,
                          list[i].differential,
                          prefix ) ;
                break ;
            case 'h': if (list[i].include_flag == 1)
                          printf("%8s %-37.37s   %3d   %4.1f   %3d  %4.1f %c\n",
                          list[i].date,
                          list[i].course_name ,
                          list[i].raw_score,
                          list[i].rating,
                          list[i].slope,
                          list[i].differential,
                          prefix ) ;
                break ;

        }
    }
}

int
parse_args( argc, argv, flag1, flag2, filename )
int argc;
char *argv[];
char *flag1;
char *flag2;
char *filename;
{

    int i, j ;
    char *uname ;

    *flag1 = ' ';
    *flag2 = ' ';
    *filename = 0 ;

    for( i=1; i<argc; i++)
    {
        if (argv[i][0] != '-')
            return( 0 ) ;
        if (argv[i][1] != 'n' )
        {
            for(j=1;j<strlen(argv[i]);j++)
            {
                switch(argv[i][j]) 
                {
                    case 'a': if (*flag1 != ' ')
                        return( 0 ) ;
                        *flag1 = 'a' ;
                        break ;
                    case 'h': if (*flag1 != ' ')
                        return( 0 ) ;
                        *flag1 = 'h' ;
                        break ;
                    case 's': if (*flag2 != ' ')
                        return( 0 ) ;
                        *flag2 = 's' ;
                        break ;
                    default:  return(0) ;

                }
            }
        }
        else
        {
            if (strlen(filename)>0)
                return( 0 ) ;
            strcpy(filename, &argv[i][2] ) ;
        }
    }

    if ( *flag1 == ' ' )
        *flag1 = 'a' ;
    if ( strlen(filename) == 0 ) 
    {
#ifdef VMS
        struct itemlist {
            unsigned short int buffer_len ;
            unsigned short int item_code ;
            void *buffer_address ;
            void *return_len ;
        } ;

        struct itemlist item ;
        unsigned long int retlen ;

        item.buffer_len = MAX_NAME ;
        item.item_code = JPI$_USERNAME ;
        item.buffer_address = (void *)filename ;
        item.return_len = (void *)&retlen ;

        SYS$GETJPIW( 0,0,0, &item, 0,0,0 );
#else
        uname = (char *)getlogin() ;
        strcpy( filename, uname ) ;
#endif
    }

    strcpy( filename+strlen(filename), ".scores" ) ;

    return( 1 ) ;

}

int main( argc, argv )
int argc ;
char *argv[] ;
{

    record *rounds ;
    record *sorted_list ;
    char flag1 ;
    char flag2 ;
    double handicap = 0.0 ;
    char filename[MAX_FILE_NAME] ; 
    char *tmp ;
    int status ;

    /*
     * flag one will take on 'h' or 'a'
     * flag two will take on 's' or <anything else>
     * file name will take on the file name of the user, or
     * overridden by the command line argument
     */

    status = parse_args( argc, argv, &flag1, &flag2, filename ) ;
    if (status != 1)
        goto usage ;

    /*
     * read in all the scores
     */

    rounds = read_data_base( filename ) ;

    if ( num_rounds < 5 )
    {
        printf("%s: 5 scores are needed to generate a handicap\n", argv[0] ) ;
        printf("%s: %d scores read found in data file\n", argv[0], num_rounds ) ;
        exit(1) ;
    }

    mark_list( rounds, num_rounds ) ;

    handicap = calculate_handicap( rounds, num_rounds ) ;

    if (flag2 == 's' )
    {
        sorted_list = (record *)malloc( (unsigned int)(sizeof( record ) * num_rounds ) ) ;
        memset( (void *)sorted_list, 0, sizeof(record) * num_rounds ) ;
        copy_list( sorted_list, rounds, 0, num_rounds-1) ;
        sort_list( sorted_list, num_rounds ) ;
        print_scores( sorted_list, num_rounds, flag1, 1 ) ;
        free( sorted_list ) ;
    }
    else
        print_scores( rounds, num_rounds, flag1, 0 ) ;

    free( rounds ) ;

    tmp = (char *)strchr(filename, '.') ;

    *tmp = 0 ;

    printf("\n     %s's USGA handicap is %4.1f\n",filename, handicap) ;

    exit( 1 ) ;

usage:
    printf("usage: %s [-has] [-n{name}]\n", argv[0] );
    printf("  h: print scores in handicap only\n" ) ;
    printf("  a: print all scores (default)\n" ) ;
    printf("  s: sort scores\n" ) ;
    printf("  n: use file name {name}.scores (login name default)\n" ) ;
    exit(1);
}

1387.2data fileDECWET::COLGATEWed Oct 30 1991 15:5736
#
# A S <strokes> R <course rating> L <slope> D<date> C<course name>
#
# NOTE: course name must be the last item on the line. All other items
# can be in any order. '#' indicates a comment until the end of line.
#
S 88	R 66.2	L 111	D ????????	C Mount Si				# ++	(2 star)
S 102	R 70.0	L 121	D ????????	C Snohomish				# +++	(3 star)
S 97	R 71.2	L 134	D ????????	C McCormick Woods			# +++++	(5 star)
S 103	R 70.2	L 129	D ????????	C Kayak Point				# ++++	(4 star)
S 102	R 70.2	L 129	D ????????	C Kayak Point				#
# new year
S 103	R 70.0	L 121	D ????????	C Snohomish				#
S 104	R 70.2	L 129	D ????????	C Kayak Point				#
S 113	R 71.6	L 138	D 05/22/91	C Port ludlow 				# ++++	(4 star)
S 102	R 66.2	L 111	D 06/06/91	C Mount Si 				#
S 102	R 68.8	L 113	D 06/13/91	C Needwood Golf course, MD 		#	(no star)
S 95	R 68.2	L 113	D 06/14/91	C Andrews Air Force Base, West Course	# +	(1 star)
S 101	R 70.0 	L 121	D 06/27/91	C Snohomish				#
S 100	R 68.2 	L 115	D 07/11/91	C Jackson Park				# +	(1 star)
S 98	R 70.8	L 128	D 07/15/91	C Battle Creek				# +	(1 star)
S 95	R 70.4	L 125	D 07/20/91	C Harbour Pointe			# +++	(3 star)
S 102	R 70.2	L 129	D 07/22/91	C Kayak Point				#
S 88	R 66.2	L 111	D 08/01/91	C Mount Si				#
S 94	R 71.2	L 134	D 08/03/91	C McCormick Woods			#
S 91	R 66.2	L 111	D 08/08/91	C Mount Si				#
S 89	R 64.9	L 112	D 08/17/91	C Tall Chief				#	(no star)
S 93	R 66.2	L 111	D 08/22/91	C Mount Si				#
S 98	R 70.0	L 121	D 09/05/91	C Snohomish				#
S 99	R 71.6	L 138	D 09/09/91	C Port ludlow 				#
S 104	R 71.1	L 129	D 09/14/91	C Whispering Firs			# +	(2 star)
S 93	R 66.2	L 111	D 09/26/91	C Mount Si				#
S 93	R 70.0	L 121	D 10/03/91	C Snohomish				#
S 104 	R 72.7	L 138	D 10/03/91	C McCormick Woods (blue tees)		#
S 96	R 67.3 	L 114	D 10/11/91	C Jefferson Park			# +	(1 star)
S 100	R 71.0	L 135	D 10/26/91	C Avalon				# +++++ (4 star)
1387.3help !!RAYBOK::COOPEROne-ton Tomato !Thu Oct 31 1991 11:465
    I was wondering if you could include some instructions for copying this
    program over the net ? I extracted the reply with the source but am not
    sure how to deal with it .  Thanks !!
    
    Mad Hacker
1387.4NEVERMIND !!!RAYBOK::COOPEROne-ton Tomato !Thu Oct 31 1991 11:553
    RE. .3  Nevermind !! I figured out what to do !
    
    Mad Hacker
1387.5index vs handicap numbersTECRUS::DEVERELLThu Oct 31 1991 13:4210
    
    Just a clarification:
    
    This program generates a handicap index number 
    which you use to look up your handicap stroke
    allowance for any particular course.  
    
    Please correct me if I'm wrong,
    
    /Dave
1387.6DECWET::COLGATEThu Oct 31 1991 19:319
This program takes your golf scores, the information about the course
(rating, slope) and calculates a USGA handicap.

One nice thing I've found is I can annotate the data file with comments....

Oh, and by the way, the comment in the header of the .c program is false.
It doesn't open 'handicap.dat'. 

Wim
1387.7More than one ?RAYBOK::COOPEROne-ton Tomato !Fri Nov 01 1991 12:085
    Would it be possible to get this program to ask for a name and
    then call up that .score file so that a golf league could be
    handicapped.
    
    Mad Hacker
1387.8DECWET::COLGATEFri Nov 01 1991 12:4219
It can - you can pass in the name of a person (file) and it will use it.

for example:

{%,$} handicap -ncooper

will look for the file cooper.scores

{%,$} handicap -ncolgate

will look for the file colgate.scores

The "-n" flag overrides the login name.

If you are interested in having the program go out into the directory
and loop through all the .score files, you can take the program and modify 
it - that's why I posted the source, not just a pointer to an executable.

Wim
1387.9CALLOWAY PROGRAM?MRKTNG::WHITTENDavid Whitten @TTBFri Nov 01 1991 12:549
    Anyone know of a DOS or MAC program which calculates Calloway scores? 
    I have played in a number of informal outings this year with 8-24
    players (mostly un-handicapped) & we have manually calculated net
    scores using the Calloway system to add some interest.  I suspect there
    must be something around which runs on a portable that would make this
    easier.
    
    Thanks,
    
1387.10percentage?VEGAS::GURALNIKJust call me Gimp!Mon Jul 20 1992 21:5016
Wim,

	Great program...  But not being a Handicap expert, I have two 
questions.

	1.  I have played sixteen rounds of golf.  Why are only 6 of them 
included?  I thought that the lowest 10 of your last 20 were counted.

	2.  I was led to believe that handicap was figured on 80% of your 
adjusted average.  Your program uses 96%.  What is correct? 


	Thanks again... I was wondering what my handicap was...  according 
to your program, I am 9.2.

			---Ken
1387.11USGA uses 96%.DNEAST::FREEMAN_KEVIThe Squeeky Wheel = NeglectTue Jul 21 1992 07:361
    96% is what the USGA uses in the calculation...
1387.12NEWPRT::JOHNSON_DOTue Jul 21 1992 13:296
    The reason only six show up for the current calculation is because you
    have less than 20 scores showing.  Once you get 20, your HDCP will be
    calculated using 10 scores.  A partial or temporary handicap is usually
    offered after ten scores are posted and uses five scores to compute.
    
    SCD
1387.13new versionDECWET::COLGATETue Jul 21 1992 15:2210
    
    The next reply contains a newer version of the handicapping program.
    
    It calculates your ESC (Equitable Stroke Control) for a range of
    courses (based on slope rating), as well as the number of strokes
    you get on that course. 
    
    Comments are welcomed and encouraged.
    
    Wim
1387.14handicap.cDECWET::COLGATETue Jul 21 1992 15:23843
/*
 * handicap.c
 *
 * This program reads the data file handicap.dat for recent golf 
 * scores and calculates ones handicap based on the USGA handicap
 * formula.
 *
 *
 */

#include <stdio.h>
#include <math.h>

#ifdef VMS
#include <jpidef.h>
#endif

int curr_max_rounds = 2 ;
int num_rounds = 0 ;
int table_flag = 0 ;
int suppress_scores_flag = 0 ;

#define MAX_LINE 256
#define MAX_COURSE 128
#define MAX_DATE 16
#define MAX_NAME 32
#define MAX_FILE_NAME (MAX_NAME+7) /* username + ".scores" */

typedef struct record {
    int    chronological_order ;
    char   date [MAX_DATE];
    char   course_name [MAX_COURSE];
    double rating ;
    int    slope ;
    int    raw_score ;
    double differential ;
    int    include_flag ;
} record ;


record *
read_data_base(filename)
char *filename ;

/*
 * read in the data base (max line length is MAX_LINE)
 */

{

    double shots ;
    double rating ;
    double slope ;
    double get_value() ;
    record *new_history ;
    FILE *fd ;
    char *cp ;
    char *first_real_char() ;
    char line_buf [ MAX_LINE ] ;
    int i ;
    int index = 0 ;
    double dtemp ;
    int temp ;
    record *history ;

    /*
     * open the file
     */

    fd = fopen( filename, "r" ) ;
    if ( fd == NULL ) 
    {
        fprintf(stderr, "Can't open file: %s\n", filename ) ;
        exit( 1 ) ;
    }

    history = (record *)malloc( (unsigned int)(sizeof( record ) * curr_max_rounds ) ) ;
    memset( (void *)history, 0, sizeof(record) * curr_max_rounds ) ;

    /*
     * Loop until the file is empty 
     */

    do
    {
        /*
         * We are done if the fgets fails to return anything
         */

        if (fgets(line_buf, sizeof(line_buf), fd) == NULL)
            break ;

        /*
         * compress the white space and strip out comments
         */

        compress(line_buf) ;

        /*
         * If anything is there...
         */

        if (strlen(line_buf) > 0)
        {

            /*
             * mark the chronological order
             */

            history[index].chronological_order = index ;

            /*
             * get the raw score
             */

            cp = first_real_char( 'S', line_buf ) ;
            shots = get_value( cp ) ;
            history[index].raw_score  = shots ;

            /*
             * get the course rating 
             */

            cp = first_real_char( 'R', line_buf ) ;
            rating = get_value( cp ) ;
            history[index].rating = rating ;

            /*
             * get the course slope if no slope, then slope is 113.
             */

            cp = first_real_char( 'L', line_buf ) ;
            slope = get_value( cp ) ;
            if (slope == 0)
                slope = 113 ;

            history[index].slope = slope ;

            /*
             * get date string
             */

            cp = first_real_char( 'D', line_buf ) ;
            get_string( history[index].date, cp, 8 ) ;                   /* get string, max 8 characters */

            /*
             * get course name
             */

            cp = first_real_char('C', line_buf ) ;
            get_string( history[index].course_name, cp, 0 ) ;           /* get string, to EOL */

            /*
             * calculate differential (round to nearest 10th).
             */

            dtemp = ( ( shots - rating ) * 113 ) / slope ;
            temp = ( dtemp + .05 ) * 10 ;
            dtemp = (double) temp / 10 ;
            history[index].differential = dtemp ;

            /*
             * clear include flag
             */

            history[index].include_flag = 0 ;

            index ++ ;
            if (index == curr_max_rounds)
            {
                new_history = (record *)malloc( (unsigned int)(sizeof( record ) * 2 * curr_max_rounds ) ) ;
                memset( (void *)new_history, 0, sizeof(record) * 2 * curr_max_rounds ) ;
                for(i = 0; i < curr_max_rounds; i++)
                    new_history[ i ] = history[ i ] ;               /* may need funky copy */
                free( history ) ;
                history = new_history ;
                curr_max_rounds *=2 ;
            }
        }
    } while ( 1 ) ;

    fclose( fd ) ;

    num_rounds = index ;

    return ( history ) ;
}


get_string( destination, source, length )
char *destination ;
char *source ;
int length ;

/*
 * copy the string from source to destination, copying
 * length characters, if length == 0, then copy to 
 * EOL ('/0')
 */
{

    if ( length != 0 )
        strncpy( destination, source, length ) ;
    else
        strcpy( destination, source ) ;

}



double
get_value( line ) 
char *line ;

/*
 * get the value from a string (delimeted by one of the
 * whitespaces (space, tab, NULL)
 */

{
    double strtod() ;
    char temp [ MAX_LINE ] ;
    char *tmp = temp ;
    double value ;

    while( (*line != ' ') && (*line !='\t') && (*line != '\0') )
         *tmp++ = *line++ ;

    *tmp++ = '\0' ;

    value = strtod( temp, 0 ) ;

    return( value ) ;
}


char *
first_real_char( after, line ) 
char after ;
char *line ;

/*
 * using the 'after' character, point directly to the 
 * first non-whitespace character and return that pointer
 */

{
    char *cp = line ;

    /*
     * skip everything up to the 'after' character
     */

    while( *cp != after && *cp != '\0' )
        cp++ ;

    /*
     * skip the 'after' character
     */

    cp++ ;

    /*
     * skip any spaces or tabs...
     */

    while( *cp == ' ' || *cp == '\t' || *cp == '\0')
        cp++ ;

    return( cp ) ;

}

compress( line )
char *line ;

/* 
 * Compress all whitespace characters (space, tabs) into single
 * whitespace characters - also strip off comments
 */

{
    char temp [ MAX_LINE ] ;
    char *s, *d ;
    int in_white_space = 0 ;

    s = line ;
    d = temp ;

    *d = '\0' ;
    while ( *s != '\0')
    {

        /* 
         * a comment terminates the line 
         */

        if ( *s == '#')
            break ;

        if ( ( (*s == ' ') || (*s == '\t') ) && !in_white_space )
        {
            *d++ = ' ' ;
            s++ ;
            in_white_space = 1 ;
        }
        else
        if ( (*s == ' ') || (*s == '\t') )
            *s++ ;
        else
        {
            if (*s != '\n')
                *d++ = *s++ ;
            else
                *s++ ;
            in_white_space = 0 ;
        }
    }
    *d = '\0' ;

    /* 
     * copy back the compressed line 
     */

    strcpy( line, temp ) ;
}


int 
copy_list( dst, src, start, end ) 
record *dst ;
record *src ;
int start ;
int end ;
{

    int i ;
    int j = 0 ;

    for( i = start; i <= end; i++ )
    {
        dst[ j++ ] = src[ i ] ;
    }
}


int 
sort_list( list, rounds_played )
record *list ;
int rounds_played ;

/*
 * Down and dirty bubblesort 
 */

{

    int i, j ;
    record temp ;

    for( i = 0 ; i < rounds_played; i++ )
        for( j= i+1; j < rounds_played ; j++ )
        {
            if ( list[ i ].differential > list[ j ].differential )
            {
                temp = list[ i ] ;
                list[ i ] = list[ j ] ;
                list[ j ] = temp ;
            }
            else 

            /*
             * if a tie, then score entered later counts (higher is better)
             */

            if ( list[ i ].differential == list[ j ].differential )
                if ( list[i].chronological_order < list[j].chronological_order )
                {
                    temp = list[ i ] ;
                    list[ i ] = list[ j ] ;
                    list[ j ] = temp ;
                }
        }
}


mark_list( list, total_rounds_played )
record *list ;
int total_rounds_played ;
{

    record *sorted_list ;
    int start, end ;
    int rounds_to_sort ;
    int i ;
    int rounds_to_mark ;

    end = total_rounds_played ;
    if ( total_rounds_played >= 20 )
        start = total_rounds_played - 20 ;
    else
        start = 0 ;

    rounds_to_sort = end - start ;

    sorted_list = (record *)malloc( (unsigned int)(sizeof( record ) * rounds_to_sort ) ) ;
    memset( (void *)sorted_list, 0, sizeof(record) * rounds_to_sort ) ;

    copy_list( sorted_list, list, start, end - 1) ;

    sort_list( sorted_list, rounds_to_sort ) ;

    rounds_to_mark = 10 ;

    switch( rounds_to_sort )
    {
        case 5:
        case 6:
            rounds_to_mark = 1 ;
            break ;
        case 7:
        case 8:  rounds_to_mark = 2 ;
            break ;
        case 9:
        case 10: rounds_to_mark = 3 ;
            break ;
        case 11:
        case 12: rounds_to_mark = 4 ;
            break ;
        case 13:
        case 14: rounds_to_mark = 5 ;
            break ;
        case 15:
        case 16: rounds_to_mark = 6 ;
            break ;
        case 17: rounds_to_mark = 7 ;
            break ;
        case 18: rounds_to_mark = 8 ;
            break ;
        case 19: rounds_to_mark = 9 ;
            break ;
    }

    for( i=0; i < rounds_to_mark ; i++)
        list[ sorted_list[ i ].chronological_order].include_flag = 1 ;

    free( sorted_list ) ;
}


double 
calculate_handicap( list, rounds_played )
record *list ;
int rounds_played ;
{

    int i ;
    int count = 0 ;
    double total = 0.0 ;
    double handicap = 0.0 ;
    int temp ;

    for( i = 0; i < rounds_played; i++ )
    {
        if (list[i].include_flag == 1)
        {
            total += list[i].differential ;
            count++ ;
        }
    }

    handicap = (total/count * .96) * 10 ;
    temp = handicap ;
    handicap = (double)temp / 10 ;

    return( handicap ) ;
}

print_scores( list, rounds_played, flag, sort_flag )
record *list ;
int rounds_played ;
char flag ;
int sort_flag ;
{
    int i ;
    char prefix ;

    printf("Date     Course Name                           Score  Rating Slope Differential\n") ;
    printf("======== ===================================== ====== ====== ===== ============\n") ;

    for( i = 0; i < rounds_played; i++ )
    {
        if (list[i].include_flag == 1)
            prefix = '*' ;
        else 
            prefix = ' ' ;

        switch( flag )
        {
            case 'a': if ((rounds_played - i == 20) && (sort_flag == 0) )  
                          printf("-------- ------------------------------------- ------ ------ ----- ------------\n") ;
                      printf("%8s %-37.37s   %3d   %4.1f   %3d  %4.1f %c\n",
                          list[i].date,
                          list[i].course_name ,
                          list[i].raw_score,
                          list[i].rating,
                          list[i].slope,
                          list[i].differential,
                          prefix ) ;
                break ;
            case 'h': if (list[i].include_flag == 1)
                          printf("%8s %-37.37s   %3d   %4.1f   %3d  %4.1f %c\n",
                          list[i].date,
                          list[i].course_name ,
                          list[i].raw_score,
                          list[i].rating,
                          list[i].slope,
                          list[i].differential,
                          prefix ) ;
                break ;
            case 'l': if ( (rounds_played >= 20) ? (rounds_played - i) <= 20 : 1 ) 
                          printf("%8s %-37.37s   %3d   %4.1f   %3d  %4.1f %c\n",
                          list[i].date,
                          list[i].course_name ,
                          list[i].raw_score,
                          list[i].rating,
                          list[i].slope,
                          list[i].differential,
                          prefix ) ;
        }
    }
}

int
parse_args( argc, argv, flag1, flag2, filename )
int argc;
char *argv[];
char *flag1;
char *flag2;
char *filename;
{

    int i, j ;
    char *uname ;

    *flag1 = ' ';
    *flag2 = ' ';
    *filename = 0 ;

    for( i=1; i<argc; i++)
    {
        if (argv[i][0] != '-')
            return( 0 ) ;
        if (argv[i][1] != 'n' )
        {
            for(j=1;j<strlen(argv[i]);j++)
            {
                switch(argv[i][j]) 
                {
                    case 'a': 
                        if ( (*flag1 != ' ') || suppress_scores_flag == 1 )
                            return( 0 ) ;
                        *flag1 = 'a' ;
                        break ;
                    case 'h': 
                        if ( (*flag1 != ' ') || suppress_scores_flag == 1 )
                            return( 0 ) ;
                        *flag1 = 'h' ;
                        break ;
                    case 'l':
                        if ( (*flag1 != ' ') || suppress_scores_flag == 1 )
                            return( 0 ) ;
                        *flag1 = 'l' ;
                        break ;
                    case 's': 
                        if ( (*flag2 != ' ') || suppress_scores_flag == 1 )
                            return( 0 ) ;
                        *flag2 = 's' ;
                        break ;
                    case 't': 
                        table_flag = 1 ;
                        break ;
                    case 'T': 
                        table_flag = 1 ;
                        suppress_scores_flag = 1 ;
                        break ;
                    default:  return(0) ;
                }
            }
        }
        else
        {
            if (strlen(filename)>0)
                return( 0 ) ;
            strcpy(filename, &argv[i][2] ) ;
        }
    }

    if ( *flag1 == ' ' )
        *flag1 = 'a' ;
    if ( strlen(filename) == 0 ) 
    {
#ifdef VMS
        struct itemlist {
            unsigned short int buffer_len ;
            unsigned short int item_code ;
            void *buffer_address ;
            void *return_len ;
        } ;

        struct itemlist item ;
        unsigned long int retlen ;

        item.buffer_len = MAX_NAME ;
        item.item_code = JPI$_USERNAME ;
        item.buffer_address = (void *)filename ;
        item.return_len = (void *)&retlen ;

        SYS$GETJPIW( 0,0,0, &item, 0,0,0 );
#else
        uname = (char *)getlogin() ;
        strcpy( filename, uname ) ;
#endif
    }

    strcpy( filename+strlen(filename), ".scores" ) ;

    return( 1 ) ;

}

void print_score_cap( strokes ) 
int strokes ;
{

    if ( strokes < 1 ) 
        printf("Stratch\n");
    else
    if ( strokes <= 18 )
    {
        printf("%2d ", strokes ) ;
        if ( strokes > 1)
            printf("double bogeys,");
        else
            printf("double bogey, ");
        if ( 18-strokes > 0)
        {
            printf(" %2d ", 18-strokes ) ;
            if ( 18-strokes > 1)
                printf("bogeys\n");
            else
                printf("bogey\n");
        }
        else
            printf("\n");
    }
    else
    if ( strokes <= 36 )
    {
        if ( strokes-18 == 1 )
            printf("%2d triple bogey, ", strokes-18);
        else
        if ( strokes-18 > 1 )
            printf("%2d triple bogeys,", strokes-18);
     
        if ( 36-strokes > 0)
        {
            printf(" %2d ", 36-strokes );
            if ( 36-strokes > 1)
                printf("double bogeys\n");
            else
                printf("double bogey\n");
        }
        else
            printf("\n");
    }
    else
    if ( strokes <= 54 )
    {
        if ( strokes-36 == 1 )
            printf("%2d quadruple bogey, ", strokes-36);
        else
        if ( strokes-18 > 1 )
            printf("%2d quadruple bogeys,", strokes-36);
     
        if ( 54-strokes > 0)
        {
            printf(" %2d ", 54-strokes );
            if ( 54-strokes > 1)
                printf("triple bogeys\n");
            else
                printf("triple bogey\n");
        }
        else 
            printf("\n");
    }
    else
        printf("Phew!\n");

}

void print_stroke_table( handicap )
double handicap ;
{
    double ftemp ;
    int itemp ;
    int prev_slope ;
    int slope ;
    int course_handicap ;

    /*
     * get a starting slope rating
     */

    slope = 95 ;

    printf("     Stroke allowance table:\n\n") ;
    printf("Slope Range Strokes Stroke Cap                           \n") ;
    printf("----------- ------- -------------------------------------\n") ;

    ftemp = ( ((double)slope * handicap / 113) + .5) * 10 ;

    course_handicap = (int)( ftemp / 10 ) ;

    while ( slope < 145 ) 
    {
        prev_slope = slope ;
        ftemp = ( ((double)(++slope) * handicap / 113) + .5) * 10 ;
        itemp = (int)( ftemp / 10 ) ;
        while ( course_handicap == itemp ) 
        {
           ftemp = ( ((double)(++slope) * handicap / 113) + .5) * 10 ;
           itemp = (int)( ftemp / 10 ) ;
        }
        printf(" %3d to %3d    %2d   ", prev_slope, slope-1, course_handicap) ;
        print_score_cap( course_handicap ) ;

        course_handicap++ ;
    }
}


int main( argc, argv )
int argc ;
char *argv[] ;
{

    record *rounds ;
    record *sorted_list ;
    char flag1 ;
    char flag2 ;
    double handicap = 0.0 ;
    char filename[MAX_FILE_NAME] ; 
    char *tmp ;
    int status ;

    /*
     * flag one will take on 'h' or 'a' or 'l'
     * flag two will take on 's' or <anything else>
     * file name will take on the file name of the user, or
     * overridden by the command line argument
     */

    status = parse_args( argc, argv, &flag1, &flag2, filename ) ;
    if (status != 1)
        goto usage ;

    /*
     * read in all the scores
     */

    rounds = read_data_base( filename ) ;

    if ( num_rounds < 5 )
    {
        printf("%s: 5 scores are needed to generate a handicap\n", argv[0] ) ;
        printf("%s: %d scores read found in data file\n", argv[0], num_rounds ) ;
        exit(1) ;
    }

    mark_list( rounds, num_rounds ) ;

    handicap = calculate_handicap( rounds, num_rounds ) ;

    if ( suppress_scores_flag == 0 )
    {
        if (flag1 == 'l' && flag2 == 's')
        {
            int copy_start = (num_rounds >=20) ? (num_rounds-20) : 0 ;
            int copy_end   = num_rounds ;
            int new_end = (num_rounds >=20) ? 20 : num_rounds ;

            sorted_list = (record *)malloc( (unsigned int)(sizeof( record ) * new_end ) ) ;
            memset( (void *)sorted_list, 0, sizeof(record) * new_end ) ;
            copy_list( sorted_list, rounds, copy_start, copy_end) ;
            sort_list( sorted_list, new_end ) ;
            print_scores( sorted_list, new_end, flag1, 1 ) ;
            free( sorted_list ) ;
        }
        else
            if (flag2 == 's' )
            {
                sorted_list = (record *)malloc( (unsigned int)(sizeof( record ) * num_rounds ) ) ;
                memset( (void *)sorted_list, 0, sizeof(record) * num_rounds ) ;
                copy_list( sorted_list, rounds, 0, num_rounds) ;
                sort_list( sorted_list, num_rounds ) ;
                print_scores( sorted_list, num_rounds, flag1, 1 ) ;
                free( sorted_list ) ;
            }
            else
                print_scores( rounds, num_rounds, flag1, 0 ) ;

    }

    tmp = (char *)strchr(filename, '.') ;

    *tmp = 0 ;

    printf("\n     %s's USGA handicap index is %4.1f\n",filename, handicap) ;

    free( rounds ) ;

    if (table_flag == 1)
    {
       printf("\n");
       print_stroke_table( handicap ) ;
    }

    exit( 1 ) ;

usage:
    printf("usage: %s [-hasl] [-n{name}]\n", argv[0] );
    printf("  h: print scores in handicap only\n" ) ;
    printf("  l: print last 20 scores\n");
    printf("  a: print all scores (default)\n" ) ;
    printf("  s: sort scores\n" ) ;
    printf("  t: print stroke table\n");
    printf("  T: print ONLY stroke table\n");
    printf("  n: use file name {name}.scores (login name default)\n" ) ;
    exit(1);
}