extern int afm_parse_file(FILE *fp, FontInfo **fi, FLAGS flags)
{
    
    int code = ok; 	/* return code from each of the parsing routines */
    int error = ok;	/* used as the return code from this function */
    
    register char *keyword; /* used to store a token */	 
    
   			      
    /* storage data for the global variable ident */			      
    ident = (char *) calloc(MAX_NAME, sizeof(char)); 
    if (ident == NULL) {error = storageProblem; return(error);}      
  
    (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
    if ((*fi) == NULL) {error = storageProblem; return(error);}      
  
    if (flags & P_G) 
    {
        (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
        if ((*fi)->gfi == NULL) {error = storageProblem; return(error);}      
    }
    
    /* The AFM File begins with Global Font Information. This section */
    /* will be parsed whether or not information should be saved. */     
    code = parseGlobals(fp, (*fi)->gfi); 
    
    if (code < 0) error = code;
    
    /* The Global Font Information is followed by the Character Metrics */
    /* section. Which procedure is used to parse this section depends on */
    /* how much information should be saved. If all of the metrics info */
    /* is wanted, parseCharMetrics is called. If only the character widths */
    /* is wanted, parseCharWidths is called. parseCharWidths will also */
    /* be called in the case that no character data is to be saved, just */
    /* to parse through the section. */
  
    if ((code != normalEOF) && (code != earlyEOF))
    {
        (*fi)->numOfChars = atoi(token(fp));
	    if (flags & (P_M ^ P_W))
        {
            (*fi)->cmi = (CharMetricInfo *) 
                      calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
           if ((*fi)->cmi == NULL) {error = storageProblem; return(error);}
            code = parseCharMetrics(fp, *fi);             
        }
        else
        {
            if (flags & P_W)
            { 
                (*fi)->cwi = (int *) calloc(256, sizeof(int)); 
                if ((*fi)->cwi == NULL) 
                {
                	error = storageProblem; 
                	return(error);
                }
            }
            /* parse section regardless */
            code = parseCharWidths(fp, (*fi)->cwi);
        } /* else */
    } /* if */
    
    if ((error != earlyEOF) && (code < 0)) error = code;
    
    /* The remaining sections of the AFM are optional. This code will */
    /* look at the next keyword in the file to determine what section */
    /* is next, and then allocate the appropriate amount of storage */
    /* for the data (if the data is to be saved) and call the */
    /* appropriate parsing routine to parse the section. */
    
    while ((code != normalEOF) && (code != earlyEOF))
    {
        keyword = token(fp);
        if (keyword == NULL)
          /* Have reached an early and unexpected EOF. */
          /* Set flag and stop parsing */
        {
            code = earlyEOF;
            break; /* get out of loop */
        }
        switch(recognize(keyword))
        {
            case STARTKERNDATA:
                break;
            case ENDKERNDATA:
                break;
            case STARTTRACKKERN:
                keyword = token(fp);
                if (flags & P_T)
                {
                    (*fi)->numOfTracks = atoi(keyword);
                    (*fi)->tkd = (TrackKernData *) 
                        calloc((*fi)->numOfTracks, sizeof(TrackKernData));
                    if ((*fi)->tkd == NULL) 
                    {
                    	error = storageProblem; 
                    	return(error);
                    }
                } /* if */
                code = parseTrackKernData(fp, *fi);
                break;
            case STARTKERNPAIRS:
                keyword = token(fp);
                if (flags & P_P)
                {
                    (*fi)->numOfPairs = atoi(keyword);
                    (*fi)->pkd = (PairKernData *) 
                        calloc((*fi)->numOfPairs, sizeof(PairKernData));
                    if ((*fi)->pkd == NULL) 
                    {
                    	error = storageProblem; 
                    	return(error);
                    }
                } /* if */
                code = parsePairKernData(fp, *fi);
                break;
            case STARTCOMPOSITES:
                keyword = token(fp);
                if (flags & P_C)
                { 
                    (*fi)->numOfComps = atoi(keyword);
                    (*fi)->ccd = (CompCharData *) 
                        calloc((*fi)->numOfComps, sizeof(CompCharData));
                    if ((*fi)->ccd == NULL) 
                    {
                    	error = storageProblem; 
                    	return(error);
                    }
                } /* if */
                code = parseCompCharData(fp, *fi); 
                break;    
            case ENDFONTMETRICS:
                code = normalEOF;
                break;
            case NOPE:
            default:
                code = parseError;
                break;
        } /* switch */
        
        if ((error != earlyEOF) && (code < 0)) error = code;
        
    } /* while */
  
    if ((error != earlyEOF) && (code < 0)) error = code;
    
    if (ident != NULL) { free(ident); ident = NULL; }
        
    return(error);
  
} /* parseFile */