summaryrefslogtreecommitdiff
path: root/ports-mgmt/portmanager
diff options
context:
space:
mode:
authorMarcus Alves Grando <mnag@FreeBSD.org>2005-12-13 19:52:43 +0000
committerMarcus Alves Grando <mnag@FreeBSD.org>2005-12-13 19:52:43 +0000
commit761d966221c67d2f39ae9ef5f708d1810e4c3ef2 (patch)
tree4a5cdceaede4170f5e347c32c70ad00ab4ee1506 /ports-mgmt/portmanager
parentUpdate to DAT 4649 (diff)
Update to 0.4.0_1
* Fixes potential seg fault on 4.11 systems. * Added log entry times PR: 90339 Submitted by: maintainer
Notes
Notes: svn path=/head/; revision=151114
Diffstat (limited to 'ports-mgmt/portmanager')
-rw-r--r--ports-mgmt/portmanager/Makefile1
-rw-r--r--ports-mgmt/portmanager/files/patch-0.4.0_1520
2 files changed, 521 insertions, 0 deletions
diff --git a/ports-mgmt/portmanager/Makefile b/ports-mgmt/portmanager/Makefile
index 0badd74296e3..7db119b4d2b8 100644
--- a/ports-mgmt/portmanager/Makefile
+++ b/ports-mgmt/portmanager/Makefile
@@ -7,6 +7,7 @@
PORTNAME= portmanager
PORTVERSION= 0.4.0
+PORTREVISION= 1
CATEGORIES= sysutils
MASTER_SITES= http://portmanager.sunsite.dk/distfiles/ \
${MASTER_SITE_SOURCEFORGE}
diff --git a/ports-mgmt/portmanager/files/patch-0.4.0_1 b/ports-mgmt/portmanager/files/patch-0.4.0_1
new file mode 100644
index 000000000000..ac775aca7bee
--- /dev/null
+++ b/ports-mgmt/portmanager/files/patch-0.4.0_1
@@ -0,0 +1,520 @@
+diff -ruN ../0.4.0/libMG/src/libMG.h ./libMG/src/libMG.h
+--- ../0.4.0/libMG/src/libMG.h Sun Dec 11 17:39:06 2005
++++ ./libMG/src/libMG.h Tue Dec 13 07:39:00 2005
+@@ -48,6 +48,7 @@
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <sysexits.h>
++#include <time.h>
+ #include <unistd.h>
+
+ #include <sys/resource.h>
+@@ -155,6 +156,24 @@
+ while( fflush( stderr ) ); \
+ assert( 0 ); \
+ }
++
++#define MGmSystem( command, environment ) \
++ MGm__pid = fork(); \
++ if( MGm__pid == 0 ) /* child */ \
++ { \
++ execve( command[0], command, environment ); \
++ _exit(127); \
++ } \
++ while( waitpid( MGm__pid, &MGm__forkStatus, 0 ) < 0 ) /* parent */ \
++ { \
++ if( WIFEXITED( MGm__forkStatus ) == 0 ) \
++ { \
++ fprintf( stderr, "%s error: command %s exited abnormally\n", id, command[0] ); \
++ while( fflush( stderr ) ); \
++ assert( 0 ); \
++ } \
++ }
++
+
+ #define MGmFopen( fileStream, fileName, mode ) \
+ if( mode[0] == 'w' ) \
+diff -ruN ../0.4.0/libMGPM/src/MGPMlogAdd.c ./libMGPM/src/MGPMlogAdd.c
+--- ../0.4.0/libMGPM/src/MGPMlogAdd.c Sun Dec 11 09:34:27 2005
++++ ./libMGPM/src/MGPMlogAdd.c Tue Dec 13 08:03:34 2005
+@@ -34,6 +34,9 @@
+ {
+ FILE* logFileStream;
+ char id[] = "MGPMlogAdd";
++ time_t timeObj;
++
++ timeObj = time(NULL);
+
+ if( property->log == 0 )
+ {
+@@ -48,7 +51,8 @@
+ }
+
+ MGmFopen( logFileStream, property->logFile->parent.path, "a" );
+- fprintf( logFileStream, "%-35s %-30s\n %-35s %-35s %-30s\n\n", victemName, victemDir, crime, suspectName, suspectDir );
++ fprintf( logFileStream, "%s %-35s %-30s\n %-35s %-35s %-30s\n\n",
++ ctime( &timeObj ), victemName, victemDir, crime, suspectName, suspectDir );
+ fclose( logFileStream );
+ return( 0 );
+ }
+diff -ruN ../0.4.0/libMGPM/src/MGPMlogDestroy.c ./libMGPM/src/MGPMlogDestroy.c
+--- ../0.4.0/libMGPM/src/MGPMlogDestroy.c Sun Dec 11 09:33:14 2005
++++ ./libMGPM/src/MGPMlogDestroy.c Tue Dec 13 08:03:40 2005
+@@ -32,9 +32,14 @@
+
+ int MGPMlogDestroy( structProperty* property )
+ {
++ FILE* logHandle;
+ char id[] = "MGPMlogDestroy";
+ char* command = NULL;
+
++ time_t timeObj;
++
++ timeObj = time(NULL);
++
+ if( property->log == 0 )
+ {
+ return( 1 );
+@@ -49,11 +54,11 @@
+ assert(0);
+ }
+
+- MGmStrcpy( command, "echo \" end of log \" " );
+- MGmStrcat( command, " >> /var/log/portmanager.log" );
+- system( command );
+- MGmStrcpy( command, "date >> /var/log/portmanager.log" );
+- system( command );
++
++ MGmFopen( logHandle, "/var/log/portmanager.log", "a" );
++ fprintf( logHandle, "%s end of log\n", ctime( &timeObj ) );
++
++ fclose( logHandle );
+
+ /* 666 indicates demise of this object */
+ property->logFile->parent.type = 666;
+diff -ruN ../0.4.0/libMGPM/src/MGPMrCommandLine.c ./libMGPM/src/MGPMrCommandLine.c
+--- ../0.4.0/libMGPM/src/MGPMrCommandLine.c Sun Dec 11 09:41:53 2005
++++ ./libMGPM/src/MGPMrCommandLine.c Mon Dec 12 16:30:28 2005
+@@ -64,7 +64,7 @@
+ int rParseCommandLine( structProperty* property, int argc, char** argv );
+ int rSetXtermTitle( void );
+
+-int MGPMrCommandLine( int argc, char** argv )
++int MGPMrCommandLine( int argc, char** argv, char** argp )
+ {
+ DIR* dirStream = NULL;
+ char id[] = "MGPMrCommandLine";
+@@ -76,6 +76,9 @@
+
+ signal( SIGINT, MGPMrCatchSignal );
+
++
++ property.argp = argp;
++
+ path = (char*)calloc( 511, 1 );
+
+ /*
+@@ -260,7 +263,7 @@
+ buffer = calloc( bufferSize, 1 );
+ MGmFopen( handle, property->helpFile, "r" );
+ fread( buffer, bufferSize, 1, handle );
+- fprintf( stdout, buffer );
++ fprintf( stdout, "%s\n", buffer );
+ free( buffer );
+ fclose( handle );
+ return( 0 );
+diff -ruN ../0.4.0/libMGPM/src/MGPMrUpdate.c ./libMGPM/src/MGPMrUpdate.c
+--- ../0.4.0/libMGPM/src/MGPMrUpdate.c Sun Dec 11 21:12:02 2005
++++ ./libMGPM/src/MGPMrUpdate.c Tue Dec 13 05:48:01 2005
+@@ -31,6 +31,10 @@
+ #define OK 0
+ #include <libMGPM.h>
+
++
++int MGrIfDirEntry( char* dirName, char* fileName );
++
++
+ int rBsdPortMkPatch( structProperty* property, structLocalProperty* localProperty );
+ int rCleanDir( char* portDir, char* workDir );
+ int rCleanUp( structProperty* property, structLocalProperty* localProperty );
+@@ -48,12 +52,10 @@
+ char backUp[] = "--back-up";
+ char bu[] = "-bu";
+ char exact[] = "exact";
+- char failMsg1[] = "failed during make, adding to ignore.db";
+ char id[] = "MGPMrUpdate";
+ char ip[] = "-ip";
+ char l[] = "-l";
+ char log[] = "--log";
+- char space[] = " ";
+ char strike[] = "0";
+ char y[] = "-y";
+ char yes[] = "--yes";
+@@ -66,6 +68,7 @@
+ char* stinker = NULL;
+ char* stopPortDirPtr = NULL;
+ char* strikePtr = NULL;
++ char** cmd;
+ int answer = 1;
+ int availableDependenciesDbIDX = 0;
+ int availableDependenciesDbQTY = 0;
+@@ -80,7 +83,13 @@
+
+ property->optionsChanged = 0;
+
+- localProperty.options = calloc( MAXBUFFERSIZE, 1 );
++ localProperty.environment = malloc( sizeof( char** ) * 4 );
++ localProperty.environment[0] = malloc( MAXSTRINGSIZE );
++ localProperty.environment[1] = malloc( MAXSTRINGSIZE );
++ localProperty.environment[2] = malloc( MAXSTRINGSIZE );
++ localProperty.environment[3] = 0;
++
++ localProperty.CURDIR = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.afterOptionsFileSize = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.afterOptionsFileTime = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.batchCheck = calloc( MAXSTRINGSIZE, 1 );
+@@ -92,6 +101,7 @@
+ localProperty.installedPortName = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.newPortDir = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.newPortName = calloc( MAXSTRINGSIZE, 1 );
++ localProperty.options = calloc( MAXBUFFERSIZE, 1 );
+ localProperty.optionsCheck = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.optionsDir = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.startPortCmd = calloc( MAXSTRINGSIZE, 1 );
+@@ -99,6 +109,7 @@
+ localProperty.stopPortCmd = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.stopPortDir = calloc( MAXSTRINGSIZE, 1 );
+ localProperty.workDir = calloc( MAXSTRINGSIZE, 1 );
++ localProperty.workFullPath = calloc( MAXSTRINGSIZE, 1 );
+
+ property->availableDependenciesDb = MGdbOpen( property->availableDependenciesDbFileName );
+ property->availablePortsDb = MGdbOpen( property->availablePortsDbFileName );
+@@ -111,15 +122,39 @@
+ if( ( strcmp( "/sysutils/portmanager", oldPortDir ) == 0 || strcmp( "/local/sysutils/portmanager", oldPortDir ) == 0 ) && property->forced )
+ {
+ fprintf( stdout, "ignoring portmanager, will not self update in forced mode by design\n" );
++ while( fflush( stdout ) );
+ MGPMlogAdd( property, "ignoring portmanager ", "will not self update in forced mode by design",
+ oldPortDir, "added to ignore.db", " " );
+
+ MGdbAdd( property->ignoreDb, oldPortDir, "skipping portmanager, will not self update in forced mode by design\n", NULL );
+- while( fflush( stdout ) );
+ rCleanUp( property, &localProperty );
+ return( 0 );
+ }
+
++ /*
++ * setup environment
++ */
++ MGmStrcpy( localProperty.environment[0], "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/X11R6/bin" );
++
++ if( getenv( "CCACHE_DIR" ) != NULL )
++ {
++ MGmStrcpy( localProperty.environment[1], "CCACHE_DIR=" );
++ MGmStrcat( localProperty.environment[1], getenv( "CCACHE_DIR" ) );
++ }
++ else
++ {
++ localProperty.environment[1][0] = 0;
++ }
++
++ if( getenv( "CCACHE_PATH" ) != NULL )
++ {
++ MGmStrcpy( localProperty.environment[2], "CCACHE_PATH=/usr/bin:/usr/local/bin" );
++ }
++ else
++ {
++ localProperty.environment[2][0] = 0;
++ }
++
+ /*
+ * test for bsd.ports.mk patch
+ */
+@@ -128,6 +163,30 @@
+ property->bsdPortMkPatched = rBsdPortMkPatch( property, &localProperty );
+ }
+
++ /*
++ * store localProperty.CURDIR
++ */
++ MGmStrcpy( localProperty.CURDIR, "cd " );
++ MGmStrcat( localProperty.CURDIR, PORTSDIR );
++ MGmStrcat( localProperty.CURDIR, oldPortDir );
++ MGmStrcat( localProperty.CURDIR, " && make -V .CURDIR" );
++ localProperty.buffer[0] = 0;
++ pHandle = popen( localProperty.CURDIR, "r" );
++ fread( localProperty.buffer, MAXBUFFERSIZE, 1, pHandle );
++ pclose( pHandle );
++ idx = 0;
++ while( idx < MAXSTRINGSIZE )
++ {
++ if( localProperty.buffer[idx] == LINEFEED || localProperty.buffer[idx] == TAB || localProperty.buffer[idx] == SPACE )
++ {
++ localProperty.buffer[idx] = 0;
++ break;
++ }
++ idx++;
++ }
++ MGmStrcpy( localProperty.CURDIR, localProperty.buffer );
++
++
+ /*
+ * store localProperty.workDir
+ */
+@@ -152,6 +211,13 @@
+ MGmStrcpy( localProperty.workDir, localProperty.buffer );
+
+ /*
++ * build localProperty.workFullPath
++ */
++ MGmStrcpy( localProperty.workFullPath, localProperty.workDir );
++ MGmStrcat( localProperty.workFullPath, localProperty.CURDIR );
++ MGmStrcat( localProperty.workFullPath, "/work" );
++
++ /*
+ * store localProperty.optionsDir
+ */
+ MGmStrcpy( localProperty.optionsDir, "cd " );
+@@ -182,8 +248,6 @@
+
+ if( MGrIfFileExist( localProperty.optionsDir ) )
+ {
+- MGmFopen( fileStream, localProperty.optionsDir, "r" );
+- fclose( fileStream );
+ MGmStrcpy( localProperty.beforeOptionsFileTime, MGrIntToString( (int)MGrFileTime( localProperty.optionsDir ) ) );
+ MGmStrcpy( localProperty.beforeOptionsFileSize, MGrIntToString( MGrFileSize( localProperty.optionsDir ) ) );
+ }
+@@ -402,7 +466,7 @@
+ /************************************************************************/
+ /* Command "2" " make conf" */
+ /************************************************************************/
+- if( MGrStrlen( localProperty.buffer ) > 4 )
++ if( MGrBufferlen( localProperty.buffer, MAXBUFFERSIZE ) > 4 )
+ {
+ /*
+ fprintf( stderr, "%s DEBUG: MGrStrlen( localProperty.buffer )-=>%d localProperty.buffer-=>%s localProperty.optionsDir->%s\n", id,
+@@ -410,8 +474,8 @@
+ while( fflush( stderr ) );
+ */
+ /*
+- * If here we know OPTIONS is defined, now need to know if
+- * /var/db/ports/{package name}/options exists
++ * If here we know OPTIONS is defined, need to run make config
++ * if /var/db/ports/{package name}/options exists
+ */
+ if( MGrIfFileExist( localProperty.optionsDir ) == 0 )
+ {
+@@ -429,20 +493,12 @@
+ fprintf( stdout, "%s %s command: #2 of 14 %s\n", id, PACKAGE_VERSION, localProperty.command );
+ fprintf( stdout, "%s\n", SINGLE_LINES );
+ while( fflush( stdout ) );
+-
+ system(localProperty.command);
+- fclose( fileStream );
+-
+- localProperty.afterOptionsFileTime[0] = 0;
+- localProperty.afterOptionsFileSize[0] = 0;
+ }
+- else
+- {
+- MGmStrcpy( localProperty.afterOptionsFileTime,
++ MGmStrcpy( localProperty.afterOptionsFileTime,
+ MGrIntToString( (int)MGrFileTime( localProperty.optionsDir ) ) );
+- MGmStrcpy( localProperty.afterOptionsFileSize,
++ MGmStrcpy( localProperty.afterOptionsFileSize,
+ MGrIntToString( MGrFileSize( localProperty.optionsDir ) ) );
+- }
+
+ /*
+ * test to see if any options changed
+@@ -958,29 +1014,70 @@
+ /************************************************************************/
+ /* Command "9" " make" */
+ /************************************************************************/
+- MGmStrcpy( localProperty.command, "cd " );
+- MGmStrcat( localProperty.command, PORTSDIR );
+- MGmStrcat( localProperty.command, oldPortDir );
+- MGmStrcat( localProperty.command, " && make " );
+ if( MGrStrlen( localProperty.options ) > 0 )
+ {
+- MGmStrcat( localProperty.command, localProperty.options );
++ cmd = malloc( sizeof( char** ) * 3 );
++ cmd[0] = malloc( MAXSTRINGSIZE );
++ cmd[1] = malloc( MAXBUFFERSIZE );
++ cmd[2] = 0;
++ MGmStrcpy( cmd[0], PORTSDIR );
++ MGmStrcat( cmd[0], oldPortDir );
++ if( chdir( cmd[0] ) != 0 )
++ {
++ fprintf( stderr, "%s error: chdir %s failed\n", id, cmd[0] );
++ perror( "chroot" );
++ while( fflush( stderr ) );
++ assert( 0 );
++ }
++ MGmStrcpy( cmd[0], "/usr/bin/make" );
++ MGmBuffercpy( cmd[1], localProperty.options, MAXBUFFERSIZE );
++ fprintf( stdout, "%s\n", SINGLE_LINES );
++ fprintf( stdout, "update %s \n", oldPortName );
++ fprintf( stdout, "%s %s command: #9 of 14 %s\nOPTIONS-=>%s\n", id, PACKAGE_VERSION, cmd[0], cmd[1] );
++ fprintf( stdout, "%s\n", SINGLE_LINES );
++ while( fflush( stdout ) );
++ MGmSystem( cmd, localProperty.environment );
++ free( cmd[0] );
++ free( cmd[1] );
++ free( cmd );
+ }
+-
+- fprintf( stdout, "%s\n", SINGLE_LINES );
+- fprintf( stdout, "update %s \n", oldPortName );
+- fprintf( stdout, "%s %s command: #9 of 14 %s\n", id, PACKAGE_VERSION, localProperty.command );
+- fprintf( stdout, "%s\n", SINGLE_LINES );
+- while( fflush( stdout ) );
+- if( ( errorCode = system(localProperty.command) ) )
++ else
+ {
+- MGPMlogAdd( property, oldPortName, oldPortDir, failMsg1, space, space );
+- fprintf( stdout, "%s %s error: make returned an error, adding %s to ignore.db\n", id, PACKAGE_VERSION, oldPortDir );
++ cmd = malloc( sizeof( char** ) * 2 );
++ cmd[0] = malloc( MAXSTRINGSIZE );
++ cmd[1] = 0;
++ MGmStrcpy( cmd[0], PORTSDIR );
++ MGmStrcat( cmd[0], oldPortDir );
++ if( chdir( cmd[0] ) != 0 )
++ {
++ fprintf( stderr, "%s error: chdir %s failed\n", id, cmd[0] );
++ perror( "chroot" );
++ while( fflush( stderr ) );
++ assert( 0 );
++ }
++ MGmStrcpy( cmd[0], "/usr/bin/make" );
++ fprintf( stdout, "%s\n", SINGLE_LINES );
++ fprintf( stdout, "update %s \n", oldPortName );
++ fprintf( stdout, "%s %s command: #9 of 14 %s\n", id, PACKAGE_VERSION, cmd[0] );
++ fprintf( stdout, "%s\n", SINGLE_LINES );
+ while( fflush( stdout ) );
+- MGdbAdd( property->ignoreDb, oldPortDir, "failed during make", NULL );
++ MGmSystem( cmd, localProperty.environment );
++ free( cmd[0] );
++ free( cmd );
++ }
++
++ if( MGrIfDirEntry( localProperty.workFullPath, ".build_done" ) == 0 )
++ {
++ fprintf( stderr, "%s\n", SINGLE_LINES );
++ fprintf( stderr, "%s error: %s %s failed during make, adding to ignore.db\n", id, oldPortName, oldPortDir );
++ fprintf( stderr, "%s\n", SINGLE_LINES );
++ while( fflush( stderr ) );
++ MGPMlogAdd( property, oldPortName, oldPortDir, "port failed to build during make, adding to ignore.db", " ", " " );
++ MGdbAdd( property->ignoreDb, oldPortDir, "port failed to build during make\n", NULL );
+ rCleanUp( property, &localProperty );
+ return(0);
+ }
++
+ /************************************************************************/
+ /* Command "10" pkg_create -b */
+ /************************************************************************/
+@@ -1468,6 +1565,13 @@
+ MGdbDestroy( property->installedPortsDb );
+ MGdbDestroy( property->strikesDb );
+
++
++ free( localProperty->environment[0] );
++ free( localProperty->environment[1] );
++ free( localProperty->environment[2] );
++ free( localProperty->environment );
++
++ free( localProperty->CURDIR );
+ free( localProperty->afterOptionsFileSize );
+ free( localProperty->afterOptionsFileTime );
+ free( localProperty->batchCheck );
+@@ -1487,6 +1591,7 @@
+ free( localProperty->stopPortCmd );
+ free( localProperty->stopPortDir );
+ free( localProperty->workDir );
++ free( localProperty->workFullPath );
+
+ return(0);
+ }
+@@ -1782,4 +1887,27 @@
+ }
+ free( command );
+ return( 0 );
++}
++
++int MGrIfDirEntry( char* dirName, char* fileName )
++{
++/* char id[] = "MGrIfDirEntry"; */
++ DIR* dirStream;
++ struct dirent* dirRecord;
++ unsigned int fileNameLength = 0;
++
++ fileNameLength = MGrStrlen( fileName );
++
++ dirStream = opendir( dirName );
++
++ while( ( dirRecord = readdir( dirStream ) ) != NULL )
++ {
++ if( dirRecord->d_namlen >= fileNameLength && strncmp( fileName, dirRecord->d_name, fileNameLength ) == 0 )
++ {
++ closedir( dirStream );
++ return( 1 ); /* FOUND */
++ }
++ }
++ closedir( dirStream );
++ return( 0 ); /* NOT_FOUND */
+ }
+diff -ruN ../0.4.0/libMGPM/src/libMGPM.h ./libMGPM/src/libMGPM.h
+--- ../0.4.0/libMGPM/src/libMGPM.h Sun Dec 11 09:33:14 2005
++++ ./libMGPM/src/libMGPM.h Mon Dec 12 21:00:55 2005
+@@ -91,6 +91,7 @@
+
+ typedef struct
+ {
++ char* CURDIR;
+ char* afterOptionsFileSize;
+ char* afterOptionsFileTime;
+ char* batchCheck;
+@@ -112,13 +113,16 @@
+ char* stopPortCmd;
+ char* stopPortDir;
+ char* workDir;
++ char* workFullPath;
+ char* xtermTitle;
++ char** environment;
+ int bufferSize;
+ int optionsBufferSize;
+ } structLocalProperty;
+
+ typedef struct
+ {
++ char** argp;
+ /*
+ * default flags
+ */
+@@ -227,7 +231,7 @@
+ int MGPMlogDestroy( structProperty* property );
+ int MGPMrBestOldPort( structProperty* property, char* oldPortDir, char* oldPortName );
+ int MGPMrCleanUp( structProperty* property );
+-int MGPMrCommandLine( int argc, char** argv );
++int MGPMrCommandLine( int argc, char** argv, char** argp );
+ int MGPMrController( structProperty* property, char* path, char** argv );
+ int MGPMrCreateAllUpdateStatusDb( structProperty* property );
+ int MGPMrCreateInstalledDb( structProperty* property );
+diff -ruN ../0.4.0/portmanager/portmanager.c ./portmanager/portmanager.c
+--- ../0.4.0/portmanager/portmanager.c Sat Dec 10 06:00:32 2005
++++ ./portmanager/portmanager.c Mon Dec 12 16:20:58 2005
+@@ -31,11 +31,11 @@
+ /*
+ * Jumping through a hoop so main is accessable with gdb
+ */
+-int main( int argc, char** argv )
++int main( int argc, char** argv, char** argp)
+ {
+ int errorCode = 0;
+
+- errorCode = MGPMrCommandLine( argc, (char**)argv );
++ errorCode = MGPMrCommandLine( argc, (char**)argv, (char**)argp );
+ exit( errorCode );
+ }
+
+Binary files ../0.4.0/portmanager-0.4.1.tar.gz and ./portmanager-0.4.1.tar.gz differ