/************************************************************************/
/*    Copyright (C) 2004  Michael C. Shultz               */
/*                                    */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or (at*/
/* your option) any later version.                    */
/*                                    */
/* This program is distributed in the hope that it will be useful,    */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  */
/* GNU General Public License for more details.           */
/*                                    */
/* You should have received a copy of the GNU General Public License  */
/* along with this program; if not, write to the Free Software        */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA      */
/*  02111-1307, USA.                          */
/*                                    */
/* Michael C. Shultz                          */
/* ringworm@inbox.lv                          */
/* Box 3238 Landers, CA 92285                     */
/************************************************************************/

#include  <pmStatus.h>

#define PORTNAME_F0       0   /* works for portI and portIP */
#define PORTDIR_F1        1
#define DEPENDANCYNAME_F1 1
#define DEPENDANCYDIR_F2  2
#define   MATCH           0

void  catch_SIGSEGV( int );

char        id[]            = "pmStatus";

int    main( )
{
    FILE*               portOODStream       =NULL;
    MGsDb               portI;
    MGsDb               portIP;
    char                modeReadOny[]       = "r";
    char                portIFileName[]     = DATADIR PORTS_INSTALLED_DB;
    char                portIPFileName[]    = DATADIR PORT_DEPENDENCIES_DB;
    char                portOODFileName[]   = DATADIR PORTS_OLD_DB;
    char*               available       = NULL;
    char*               portOODbuffer       = NULL;
    char*               systemCommand;
    int             miscPtr         = 0;
    unsigned    int     portOODsize2        = 0;
    int             recIdxPortI     = 0;
    int             recIdxPortIP        = 0;
    int             skip            = 0;
    /* handle seg faults */
/*
    signal(SIGSEGV, catch_SIGSEGV );    
*/
    /*......................................................*/
    /* create portI.db and portIP.db          */
    /*......................................................*/
    if( PMGRrDbCreate() )
    {
        fprintf( stderr, "%s %s error:  PMGRrDbCreate returned with an error\n", id, ver );
        return( 1 );
    }

    /****************************************/
    /* MGmDbArray:                */
    /* b = dbase  structure name      */
    /* c = dbase file name string     */
    /* d = open mode string       */
    /****************************************/
    MGmDbArray( portI, portIFileName, modeReadOny );
    if( errno )
    {
        fprintf( stderr, "%s %s error:  MGmDbArray returned with an error\n", id, ver );
        return( 1 );
    }
    MGmDbArray( portIP, portIPFileName, modeReadOny );
    if( errno )
    {
        fprintf( stderr, "%s %s error:  MGmDbArray returned with an error\n", id, ver );
        return( 1 );
    }
    recIdxPortI = 0;
    portOODStream   = fopen( portOODFileName, "w" );
    /* loop thru installed ports db */
    while( recIdxPortI < portI.recordQty )
    {
        /* PMGRrMakeDescribe returns the version of the available port from "cd /usr/ports/; make describe" */
        available = PMGRrMakeDescribe( portI.array[recIdxPortI][PORTDIR_F1] );

        /* compare installed port name with name from "make describe" */
        if( ( strcmp( portI.array[recIdxPortI][0], available ) ) < 0 )
        {
            fprintf( stdout, "have:%-25s status: OLD available:%-25s %-25s\n",
                portI.array[recIdxPortI][0], available, portI.array[recIdxPortI][PORTDIR_F1] );
            fflush( stdout );

            fprintf( portOODStream, "%s%c%s%cOLD%c%s%c\n", 
            portI.array[recIdxPortI][PORTDIR_F1], 0,
            portI.array[recIdxPortI][0], 0,
            0, available, 0 );
            fflush( portOODStream );

        }       
        if( ( strcmp( portI.array[recIdxPortI][0], available ) ) > 0 )
        {
            /* remove ports that have been deleted from or moved in the ports collection */        
            /* added 0.1.7 20040804 */
            if( strlen(available) == 0 )
            {
                fprintf( stdout, 
                    "removing: %s, \n\t it is no longer in the ports collection, \n\t see /usr/ports/MOVED for possible explanation\n",
                    portI.array[recIdxPortI][0] );
                systemCommand = ( char* )malloc( strlen( "( pkg_delete -f  )" )
                    + strlen( portI.array[recIdxPortI][0] )
                    + 1 );
                strncpy( systemCommand, "pkg_delete -f ",strlen("pkg_delete -f ")+1 );
                strncat( systemCommand,
                        portI.array[recIdxPortI][0],
                        strlen(portI.array[recIdxPortI][0])+1 );  
                fprintf( stdout, "%s\n", systemCommand );
                system( systemCommand );
                free( systemCommand );
            }
            fprintf( stdout, "have:%-25s status: OLD requires downgrade! available:%-25s %-25s\n",
                portI.array[recIdxPortI][0], available, portI.array[recIdxPortI][PORTDIR_F1] );
            fprintf( portOODStream, "%s%c%s%cERR%c%s%c\n", 
            portI.array[recIdxPortI][PORTDIR_F1], 0,
            portI.array[recIdxPortI][0], 0,
            0, available, 0 );
            fflush( stdout );
            fflush( portOODStream );
        }       
        if( ( strcmp( portI.array[recIdxPortI][0], available ) ) == 0 )
        {
/*
            fprintf( stdout, "have:%-25s status: up to date:%-25s %-25s\n",
                portI.array[recIdxPortI][0], available, portI.array[recIdxPortI][PORTDIR_F1] );
            fflush( stdout );
*/
            fprintf( stdout, "have:%-25s status: CURRENT: %-25s\n",
                portI.array[recIdxPortI][0], portI.array[recIdxPortI][PORTDIR_F1] );
        }       
        free( available );
        recIdxPortI++;
    }
    fclose( portOODStream );
    /****************************************/
    /* parent port check          */
    /****************************************/
    recIdxPortIP    =   0;
    while( recIdxPortIP < portIP.recordQty )
    {
        recIdxPortI =   0;
        /* while parent port not in portI */
        while( ( recIdxPortI < portI.recordQty ) 
            && ( strcmp( portI.array[recIdxPortI][0], portIP.array[recIdxPortIP][1] ) != 0 ) )
        {
            recIdxPortI++;
        }
        if( recIdxPortI != portI.recordQty )   /* if recIdxPortI == portI.recordQty then parent port name */
        {                   /* not in portIP */
            recIdxPortIP++;
            continue;          /* contilue = return to While statement */
        }
        /***************************************/
        /* was dependancy port ever installed? */
        /***************************************/
        recIdxPortI = 0;
        skip        = 0;
        /* search all of portI's paths for the path pointed to in portIP */
        while( ( recIdxPortI < portI.recordQty ) 
            && ( strcmp( portI.array[recIdxPortI][PORTDIR_F1], portIP.array[recIdxPortIP][DEPENDANCYDIR_F2] ) != 0 ) )
        {
            recIdxPortI++;
        }
        /************************************************************************/
        /* handle switch from XFree86 to Xorg or other cases  where       */
        /* dependency ports have changed names by ignoring them rather than   */
        /* trying to reinstall the original dependency port           */
        /************************************************************************/
        if( strncmp( portIP.array[recIdxPortIP][DEPENDANCYNAME_F1], "XFree86", 4 ) == MATCH
        || strncmp( portIP.array[recIdxPortIP][DEPENDANCYNAME_F1], "Xorg", 4  ) == MATCH
        || strncmp( portIP.array[recIdxPortIP][DEPENDANCYNAME_F1], "imake", 4  ) == MATCH )
        {
/*
            fprintf( stdout, "%-25s ignoring reason: X dependency %-25s\n",
                portI.array[recIdxPortI-1][PORTNAME_F0],
                portIP.array[recIdxPortIP][DEPENDANCYNAME_F1] );
            fflush( stdout );
*/
            skip    = 1;
        }
        /* if recIdxPortI == portI.recordQty then dependancy never installed! */
        if( recIdxPortI == portI.recordQty && skip  == 0 )
        {

            fprintf( stdout, "%-25s dependancy port %s %s not installed!\n",
                portIP.array[recIdxPortIP][PORTNAME_F0],
                portIP.array[recIdxPortIP][DEPENDANCYNAME_F1],
                portIP.array[recIdxPortIP][DEPENDANCYDIR_F2] );
            portOODStream   = fopen( portOODFileName, "a" );

            fprintf( portOODStream, "%s%c%s%cMISSING%c%s%c\n", 
                portIP.array[recIdxPortIP][DEPENDANCYDIR_F2], 0,
                portIP.array[recIdxPortIP][DEPENDANCYNAME_F1], 0,
                0,
                portIP.array[recIdxPortIP][PORTNAME_F0], 0 );
            fflush( portOODStream );

            skip    = 1;
        }
        /********************/
        /* find IP port in I */
        recIdxPortI =   0;
        while( recIdxPortI < portI.recordQty && skip == 0)
        {
            if( ( strcmp( portI.array[recIdxPortI][0], portIP.array[recIdxPortIP][0] ) == 0 ) )
            {
                portOODStream   = fopen( portOODFileName, "r+" );
                portOODsize2    = MGrFileSize( portOODFileName );
                if( portOODsize2 == 0 )
                {
                    portOODsize2 = 1;
                }
                portOODbuffer   = ( char* )malloc( portOODsize2 );
if( portOODbuffer == NULL )
{
    printf( "pmStatus error allocating %d bytes for portOODbuffer\n", portOODsize2 );
    exit(100);
}
/*
printf("portOODsize2 = %d\n", portOODsize2 );
*/
                fread( portOODbuffer, portOODsize2, 1, portOODStream );

                miscPtr = 0;
                while( miscPtr < portOODsize2 )
                {
                    if( portOODbuffer[miscPtr] == 0 )
                    {
                        portOODbuffer[miscPtr] = TAB;
                    }
                    miscPtr++;
                }
                if( strstr( portOODbuffer, portI.array[recIdxPortI][PORTDIR_F1] ) == 0 )
                {
                    fprintf( stdout, "have:%-25s status built with OLD dependency port :%-25s\n", 
                        portI.array[recIdxPortI][0],        /* portI[0] = name */ 
                        portIP.array[recIdxPortIP][1] );    /* portIP[1] = depency name */ 
                    fflush( stdout );
                    fprintf( portOODStream, "%s%c%s%cPARENTOOD%c%s%c\n", 
                        portI.array[recIdxPortI][PORTDIR_F1], 0,    /* portI[1] = path */ 
                        portI.array[recIdxPortI][0], 0,
                        0,
                        portIP.array[recIdxPortIP][1], 0 );
                    fflush( portOODStream );
                }

                miscPtr = 0;
                while( miscPtr < portOODsize2 )
                {
                    if( portOODbuffer[miscPtr] == TAB )
                    {
                        portOODbuffer[miscPtr] = 0;
                    }
                    miscPtr++;
                }
                fclose( portOODStream );
                free( portOODbuffer );
            }
            recIdxPortI++;
        }
        recIdxPortIP++;
    }
    /****************************************/
    fprintf( stderr, "status report finished\n" ); 
    fflush( stderr );
    MGmDbArrayFree( portI );
    MGmDbArrayFree( portIP );
    exit( 0 );
}

/******************/
/* signal handler */
/******************/
void  catch_SIGSEGV( int signalID )
{
    sigset_t mask_set;  /* used to set a signal masking set. */
    sigset_t old_set;   /* used to store the old mask set.   */

    /* re-set the signal handler again to catch_SIGSEGV, for next time */
    signal( SIGSEGV, catch_SIGSEGV );

    /* mask any further signals while we're inside the handler. */
    sigfillset(&mask_set);
    sigprocmask(SIG_SETMASK, &mask_set, &old_set);

    if( signalID == SIGSEGV )
    {
        /* reset the cache */
        system( CACHE_RESET );
        fprintf( stderr, "%s %s warning: segment fault, resetting cache\n", id, ver );
        /* see sysexits(3) for codes */
        exit( EX_TEMPFAIL );
    }
}