Hi, I'm just getting started figuring this stuff out. I tend to avoid doing things as root, so I choked. It would be useful to mention that the build must be done as root with the files and directories modifiable by root. Attached is a quick hack I threw together several years ago for keeping build clutter separate from source trees. This build seems to require building in the source tree. Regards, Bruce
/* * This file defines all the global structures and special values * used in my private development library. * * clone is free software. * * You may 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, or (at your option) any later version. * * mylib 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 mylib. See the file "COPYING". If not, * write to: The Free Software Foundation, Inc., * 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #define __USE_POSIX2 1 #include <sys/types.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/wait.h> #include <stdio.h> #include <dirent.h> typedef struct dirent dirent_t; #include <errno.h> #include <fcntl.h> #include <limits.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define cc_t const char #define scc_t static const char #define STMTS( s ) do{s}while(0) #define NUL '\0' scc_t zAnnouncement[] = "clone version $Revision: 1.2 $%s\n" "start cloning\nFR:\t%s\nTO:"; scc_t zUsage[] = "USAGE: clone <source-tree> <clone-1> [ ... ]\n" "where: <source-tree> is a pre-existing directory and\n" "\t<clone-n> are empty or non-existing destination directories.\n"; scc_t zFsErr[] = "clone FAILED: fs err %d (%s) %s ``%s''.\nFile %s line %d\n"; scc_t zFsWarn[] = "clone warning: fs err %d (%s) %s ``%s''.\n"; #define EREX(s,f) STMTS( \ fprintf(stderr,zFsErr,errno,strerror(errno), \ (s),(f),__FILE__,__LINE__); \ exit(EXIT_FAILURE);) #define WARN(s,f) STMTS( \ fprintf(stderr,zFsWarn,errno,strerror(errno), \ (s),(f)); \ exit(EXIT_FAILURE);) #define ABSURD_PATH_LEN 0x2000 typedef struct { ino_t root_ino; /* i node number of root of source/dest dir */ char* pzDepth; /* current place for inserting next directory */ char* pzName; /* full path root directory name */ } tNewTree; char zSource[ ABSURD_PATH_LEN ]; tNewTree src = { 0, zSource, NULL }; cc_t* pzBaseDir; tNewTree* paNewTrees; int newTreeCt; int verbose = 0; tNewTree* allocateTrees( int treeCt, cc_t** treeNames ); void cloneTree( void ); void initialize( int argc, cc_t* pzBase ); void nextLayer( dirent_t* pDE ); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void usage( int exitCode ) { fputs( zUsage, stderr ); exit( exitCode ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int main( int argc, char** argv ) { scc_t zNoDest[] = "one source and one or more destination directories " "must be specified\n"; switch (--argc) { case 1: if (strcmp( argv[1], "-?" ) == 0) usage( EXIT_SUCCESS ); /* FALLTHROUGH */ case 0: fputs( zNoDest, stderr ); usage( EXIT_FAILURE ); /* NOTREACHED */ default: if (strcmp( argv[1], "-?" ) == 0) usage( EXIT_SUCCESS ); } initialize( argc, *(++argv) ); paNewTrees = allocateTrees( newTreeCt, (cc_t**)argv+1 ); /* * Now initialize a global pointer to our initial source directory */ if (chdir( src.pzName ) != 0) EREX( "cd to source dir", src.pzName ); if (getcwd( zSource, sizeof( zSource )) == NULL) EREX( "get cwd after cd to", src.pzName ); if ((src.pzName = strdup( zSource )) == NULL) { errno = ENOMEM; EREX( "strdup", zSource ); } printf( zAnnouncement, verbose ? " - VERBOSE MODE" : "", src.pzName ); { tNewTree* pNT = paNewTrees; int ct = newTreeCt; do { printf( "\t%s\n", (pNT++)->pzName ); } while (--ct > 0); } fflush( stdout ); cloneTree(); return EXIT_SUCCESS; } void initialize( int argc, cc_t* pzBase ) { scc_t zNotDir[] = "clone ERROR: the first argument (%s) must be a directory\n"; struct stat sb; if (stat( pzBase, &sb ) != 0) EREX( "stat-ing", pzBase ); if (! S_ISDIR( sb.st_mode )) { fprintf( stderr, zNotDir, pzBase ); exit( EXIT_FAILURE ); } src.root_ino = sb.st_ino; /* * Do not chdir before allocating trees. The argument names * may be relative path names and they must be resolved first. */ newTreeCt = argc-1; src.pzName = (char*)pzBase; pzBase = getenv( "VERBOSE" ); if ((pzBase != NULL) && (*pzBase == 't')) { verbose = 1; fputs( "clone $Revision: 1.2 $ initialized\n", stderr ); fflush( stderr ); } } tNewTree* allocateTrees( int treeCt, cc_t** treeNames ) { scc_t zNotDir[] = "clone FAIL: clone arguments must not exist or be directories\n"; scc_t zSkipMsg[] = "clone WARNING: skipping %s copy: it is source\n"; tNewTree* paNT; { size_t sz = treeCt * sizeof( *paNT ); paNT = malloc( sz ); if (paNT == NULL) { errno = ENOMEM; EREX( "allocating", "tree trunks" ); } } { struct stat sb; tNewTree* pNT = paNT; do { cc_t* pzName = *(treeNames++); /* * IF the directory already exists, clean it out (as long as it * is not the source directory). */ if (stat( pzName, &sb ) == 0) { scc_t zCmd[] = "chmod -R ugo+wx %1$s ; set -x ; rm -rf %1$s/*"; if (! S_ISDIR( sb.st_mode )) { fputs( zNotDir, stderr ); usage( EXIT_FAILURE ); } if (sb.st_ino == src.root_ino) { /* * This "output" tree will be skipped entirely. */ fprintf( stderr, zSkipMsg, pzName ); newTreeCt--; continue; } sprintf( zSource, zCmd, pzName ); system( zSource ); } /* * Any error except not existing is bad */ else if (errno != ENOENT) { EREX( "stat-ing", pzName ); } /* * Now we have to be able to make the directory */ else if (mkdir( pzName, 0755 ) != 0) { EREX( "mkdir of", pzName ); } /* * Get its inode number */ else if (stat( pzName, &sb ) != 0) { EREX( "stat-ing just created directory", pzName ); } if (verbose) printf( "OK: %s\n", pzName ); /* * We will be playing with full path names and "chdir" into * each directory of the source tree. */ pNT->pzName = malloc( ABSURD_PATH_LEN ); if (pNT->pzName == NULL) { errno = ENOMEM; EREX( "allocating", "tree paths" ); } if (realpath( pzName, pNT->pzName ) == NULL) { EREX( "realpath", pzName ); } pNT->root_ino = sb.st_ino; pNT->pzDepth = pNT->pzName + strlen( pNT->pzName ); if (pNT->pzDepth[-1] != '/') *(pNT->pzDepth)++ = '/'; *(pNT->pzDepth) = NUL; pNT++; } while (--treeCt > 0); /* * Make sure we have not clobbered our source directory. */ if (stat( src.pzName, &sb ) != 0) EREX( "re-stat-ing source dir", src.pzName ); } fflush( stdout ); return paNT; } void nextLayer( dirent_t* pDE ) { static char zDepth[ 16 ] = ""; int trimIx = strlen( zDepth ); int recurse = 0; tNewTree* pNT = paNewTrees; int ct = newTreeCt; size_t len = strlen( pDE->d_name ) + 1; if (strcmp( pDE->d_name, "CVS" ) == 0) return; src.pzDepth += sprintf( src.pzDepth, "%s/", pDE->d_name ); if (chdir( pDE->d_name ) != 0) { fprintf( stderr, zFsWarn, errno, strerror( errno ), "chdir to", zSource ); return; } if (verbose && (trimIx < 5)) { /* * We only print the top three directories -- even for verbose */ strcpy( zDepth + trimIx, " " ); printf( "%s%s\n", zDepth, pDE->d_name ); fflush( stdout ); } do { /* * Prevent recursion into our output directories. */ if (pDE->d_ino == pNT->root_ino) { scc_t zRecurse[] = "clone NOTICE:\n\tsource tree %s/%s contains\n" "\tcloned tree %s\n"; fprintf( stderr, zRecurse, src.pzName, zSource, pNT->pzName ); recurse = 1; } else if (pNT->pzDepth + len > pNT->pzName + ABSURD_PATH_LEN) { /* * If *any* of the output directory names becomes absurdly long, * then we prune them all at this level. */ fprintf( stderr, "clone WARNING: names exceed %d bytes\n", ABSURD_PATH_LEN ); recurse = 1; } if (recurse == 0) { strcpy( pNT->pzDepth, pDE->d_name ); pNT->pzDepth[ len-1 ] = '/'; pNT->pzDepth[ len ] = NUL; } /* * Even if we aren't going to recurse, we add 'len' to our depth * point, because we'll subtract it unconditionally later. */ pNT->pzDepth += len; pNT++; } while (--ct > 0); if (recurse == 0) { ct = newTreeCt; pNT = paNewTrees; do { if (mkdir( pNT->pzName, 0755 ) != 0) EREX( "mkdir", pNT->pzName ); pNT++; } while (--ct > 0); cloneTree(); } if (verbose) zDepth[ trimIx ] = NUL; /* * We may or may not have done any work, but we always add 'len' to * the depth point for all our output tree descriptors. Subtract it off. */ ct = newTreeCt; pNT = paNewTrees; do { (pNT++)->pzDepth -= len; } while (--ct > 0); } void cloneTree( void ) { DIR* dirp; int dirFd; char* pzCurrDepth = src.pzDepth; tNewTree* pNT; /* * Before entering this procedure, the current directory is set * to the source directory we are currently scanning. Open this * directory *both* as a normal file and as a directory. We do the * former because "fchdir" operates on this descriptor, and not on * the DIR* pointer. (Some call ought to do that, tho.) */ dirFd = open( ".", O_RDONLY ); if (dirFd < 0) EREX( "open of dir", zSource ); dirp = opendir( "." ); if (dirp == NULL) EREX( "opendir", zSource ); /* * Always clear errno before calling readdir. It is the only way * one can reliably determine EOF. */ for (errno = 0;;errno = 0) { dirent_t* pDE = readdir( dirp ); struct stat sb; /* * Whether due to end of directory or an error, if we don't get * an entry from readdir, then stop this directory and return. */ if (pDE == NULL) break; /* * Skip "." and ".." entries */ if (pDE->d_name[0] == '.') { if (pDE->d_name[1] == NUL) continue; if ((pDE->d_name[1] == '.') && (pDE->d_name[2] == NUL)) continue; } /* * Only handle regular files and directories. No sym links. */ if (stat( pDE->d_name, &sb ) != 0) { fprintf( stderr, zFsWarn, errno, strerror( errno ), "stat of entry", pDE->d_name ); } else switch (sb.st_mode & S_IFMT) { case S_IFREG: { int ix = 0; for (pNT = paNewTrees; ix < newTreeCt; ix++, pNT++) { strcpy( pNT->pzDepth, pDE->d_name ); if (link( pDE->d_name, pNT->pzName ) != 0) { char z[ 128 ]; snprintf( z, sizeof(z), "linking %s to", pDE->d_name ); fprintf( stderr, zFsWarn, errno, strerror( errno ), z, pNT->pzName ); } } break; } case S_IFDIR: /* * Do the next directory layer and then "cd" back to our * current directory from whereever we may have wandered to. */ nextLayer( pDE ); src.pzDepth = pzCurrDepth; if (fchdir( dirFd ) != 0) { *src.pzDepth = NUL; EREX( "fchdir back to", zSource ); } break; default: fprintf( stderr, zFsWarn, ENOTDIR, strerror( ENOTDIR ), "wrong file type", pDE->d_name ); break; } } /* * IF we did not stop due to EOF (i.e., errno was left unchanged), */ if (errno != 0) fprintf( stderr, zFsWarn, errno, strerror( errno ), "reading dir entry from", zSource ); closedir( dirp ); close( dirFd ); } /* * Local Variables: * c-file-style: "stroustrup" * indent-tabs-mode: nil * tab-width: 4 * End: * end of clone.c */