/****************************************************************************************************/
/* */
/* testMakeClean.main.c: */
/* */
/****************************************************************************************************/
/****************************************************************************************************/
/* */
/* Copyright (C) 2004 Joerg Kunze */
/* */
/* This file is part of siliconBrain. */
/* */
/* siliconBrain 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. */
/* */
/* siliconBrain 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 */
/* */
/****************************************************************************************************/
/* some parts are taken from glibc-2.3.2/posix/execvp */
/* Copyright (C) 1991,92,95,96,97,98,99,2002 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "siliconBrainLib"
#include "siliconBrainOption"
#include "stringSearch"
static String siliconBrainRelease = c( "$siliconBrainRelease: 0.2.3 $" );
static String siliconBrainRcsIdentifier = c( "$Id: testMakeClean.main.c,v 1.6 2004/12/14 23:31:26 joerg Exp $" );
static String siliconBrainSaveStamp = c( "$siliconBrainSaveStamp: 2004/12/14 22:14:50, Joerg Kunze$" );
static String programName = c( "testMakeClean: " );
/****************************************************************************************************/
/* */
/* const: */
/* */
/****************************************************************************************************/
static const int failure = -1;
static const int success = 0;
static const double second = 1.0;
static const double minute = 60.0;
static const double hour = 60.0*60.0;
static const double day = 24.0*60.0*60.0;
#define fileToTestFreshness "temporary/isInitialized"
/****************************************************************************************************/
/* */
/* infoPrint: */
/* */
/****************************************************************************************************/
#define infoPrint( message ) print( programName ); println( (message) );
/****************************************************************************************************/
/* */
/* command: */
/* */
/****************************************************************************************************/
typedef struct {
String command;
bool script;
bool scriptTested;
char *arguments[];
} CCommand;
typedef char **Command;
#define inFile STDIN_FILENO
#define outFile STDOUT_FILENO
#define errFile STDERR_FILENO
#define command( programName, ... ) (( char *[]){ toCa( (programName) ), __VA_ARGS__, 0 })\
/****************************************************************************************************/
/* */
/* which: */
/* */
/****************************************************************************************************/
String which( String shortName ) {
/*--------------------------------------------------------------------------------------*/
/* this is a "which" implementation. It is partly inspired by: glibc-2.3.2/posix/execvp */
/* */
/* We don't use execvp(), because we like to remember the found path and the information*/
/* whether we used the shell (script-exec) to start the executable. */
/*--------------------------------------------------------------------------------------*/
char *path = getenv( "PATH" );
/*-------------------------------------------------------------------------------------------*/
/* if the PATH variable is not set, we read the corresponding System Configuration Paramater */
/* (in this case a String Valued Paramater). The default search path is the current */
/* directory followed by the path `confstr' returns for `_CS_PATH'. */
/*-------------------------------------------------------------------------------------------*/
if( !path ) {
int csPathLength;
path = alloca( csPathLength = confstr( _CS_PATH, 0, 0 ) + 1 ); // the "+ 1" is for the extra ':'
confstr( _CS_PATH, path + 1, csPathLength ); // the "+ 1" is for the extra ':'
path[ 0 ] = ':';
}
String directory = { .begin = path, .length = 0, .capacity = 0 };
String tilde;
String programName = stringNull;
char *directoryPrefix;
/*------------------------------------------------------------*/
/* try for each of the ':' seperated parts of the path string */
/*------------------------------------------------------------*/
while( *directory.begin ) {
/*----------------------------------------------------------------------------------------*/
/* at the begining of the path string (directory.begin == path) the ':' is *not* skipped, */
/* and so defines an empty directory: the current directory. */
/* We skipp only one ':', so that two consequtive ':'s define again the empty dir. */
/* A ':' at the very end will result in an empty string, and quite again is the empty dir.*/
/*----------------------------------------------------------------------------------------*/
if( *directory.begin == ':' && directory.begin != path ) ++directory.begin;
directory.length = strcspn( directory.begin, ":" ); // strchrnul() is another idea here
if( !directory.length ) {
copy_a( programName, shortName );
}
else if( *directory.begin == '~' ) {
tilde.begin = directory.begin + 1;
tilde.length = strcspn( tilde.begin, "/" );
directory.begin += (tilde.length + 1);
directory.length -= (tilde.length + 1);
char *userName;
if( tilde.length ) {
userName = toCa( tilde );
}
else {
userName = getenv( "LOGNAME" );
}
struct passwd passwd, *resultPointer;
size_t bufferLength = 421;
char *buffer = alloca( bufferLength );
while( 0 != getpwnam_r( userName, &passwd, buffer, bufferLength, &resultPointer ) )
if( ERANGE == errno )
buffer = alloca( bufferLength <<= 1 );
else {
resultPointer = 0;
break;
}
directoryPrefix = ( resultPointer && passwd.pw_dir ) ? passwd.pw_dir : "<no user>";
collect_a( programName, string( directoryPrefix ), directory, directory.begin[ directory.length -1 ] == '/' ? c( "" ) : c("/"), shortName );
}
else {
collect_a( programName, directory, directory.begin[ directory.length -1 ] == '/' ? c( "" ) : c("/"), shortName );
}
if( access( programName.begin, X_OK ) == 0 ) {
break;
}
directory.begin += directory.length;
}
#if 0
// TODO: detect a ENOEXEC before forking, so that we can cache this information
// TODO: detect the script interptreter by examning the #!/bin/bash line an remember this information in the Command struct
script_execute (const char *file, char *const argv[])
{
/* Count the arguments. */
int argc = 0;
while (argv[argc++])
;
/* Construct an argument list for the shell. */
{
char *new_argv[argc + 1];
new_argv[0] = (char *) _PATH_BSHELL;
new_argv[1] = (char *) file;
while (argc > 1)
{
new_argv[argc] = argv[argc - 1];
--argc;
}
/* Execute the shell. */
__execve (new_argv[0], new_argv, __environ);
}
}
__execve (startp, argv, __environ);
if (errno == ENOEXEC)
script_execute (startp, argv);
#endif
return programName;
}
/****************************************************************************************************/
/* */
/* executeRaw: */
/* */
/****************************************************************************************************/
pid_t executeRaw( Command arguments, int in, int out, int err ) {
pid_t pid = fork();
if( pid == 0 ) {
dup2( in , STDIN_FILENO );
dup2( out, STDOUT_FILENO );
dup2( err, STDERR_FILENO );
// TODO: close unused fileDescriptors like in, out ... if different from STDIN_FILENO or not used ends of pipes
// or totally different files only usefull in the paraents process.
// there seems to be a close-on-exec flag settable by fcntl()
execve( arguments[ 0 ], arguments, environ );
_exit( EXIT_FAILURE );
}
return pid;
}
/****************************************************************************************************/
/* */
/* execute: */
/* */
/****************************************************************************************************/
int execute( Command arguments, int in, int out, int err ) {
int status;
waitpid( executeRaw( arguments, in, out, err ), &status, 0 );
return status;
}
/****************************************************************************************************/
/* */
/* testMakeClean: */
/* */
/****************************************************************************************************/
void testMakeClean() {
infoPrint( c( "do the clean test" ) );
/*------------------------------------*/
/* touch the file: create a new mtime */
/*------------------------------------*/
utime( fileToTestFreshness, 0 );
String commandName = which( c( "make" ) );
int nullFile = open( "/dev/null", O_RDONLY );
Command makeAll = command( commandName, "all" );
int status = 0;
if( status = execute( command( commandName, "clean" ), inFile, nullFile, nullFile ) ) {
infoPrint( c( "error in make clean" ) );
exit( WEXITSTATUS( status ) );
}
if( status = execute( makeAll, inFile, nullFile, nullFile ) ) {
infoPrint( c( "error in make clean" ) );
exit( WEXITSTATUS( status ) );
}
int mypipe[ 2 ];
pipe( mypipe );
pid_t pid = executeRaw( makeAll, inFile, mypipe[ 1 ], nullFile );
close( mypipe[ 1 ] );
CommandReturnCode returnCode = commandOk;
stringSearchOptions options = {
.expression = "Nothing to be done"
};
{
FileReaderContext context;
context.openCommand = (void *)stringSearchOpen;
context.closeCommand = (void *)stringSearchClose;
context.openFile = (void *)0;
context.closeFile = (void *)0;
context.userCommand = (UserCommand)&stringSearch;
returnCode = pipeReader( &context, &options, mypipe[ 0 ] );
}
waitpid( pid, &status, 0 );
if( status ) {
infoPrint( c( "error in second make all" ) );
exit( WEXITSTATUS( status ) );
}
exit( returnCode != commandOk );
}
/****************************************************************************************************/
/* */
/* main: */
/* */
/****************************************************************************************************/
extern int main( int argc, char *argv[] ) {
infoPrint( siliconBrainRelease );
infoPrint( siliconBrainRcsIdentifier );
infoPrint( siliconBrainSaveStamp );
struct stat isInitializedFile;
if(
stat( fileToTestFreshness, &isInitializedFile ) != success ||
difftime( time( 0 ), isInitializedFile.st_mtime ) > day
)
testMakeClean();
return 0;
}
/*
$Log: testMakeClean.main.c,v $
Revision 1.6 2004/12/14 23:31:26 joerg
published for new release 0.2.3
Revision 1.5 2004/12/14 23:17:05 joerg
published for new release 0.2.2
Revision 1.4 2004/12/14 22:20:09 joerg
pipe output of make command into stringSearch c-function
without creating a process for stringSearch. The later is used as a C-function command, not as a main.
Revision 1.3 2004/12/12 11:39:39 joerg
correct pipe and process creation. Now it is complete (but not factored out).
Revision 1.2 2004/11/29 00:14:34 joerg
incorporate a pipe to catch make's output
Revision 1.1 2004/11/09 00:47:55 joerg
targets, which use siliconbrain tools like "generate" should depend on it. First version of a "make clean;
make all" sequence tester. Everything written in C !!!!!!!!!!!!!!!!!!! (like collect_a, command and execute).
*/