Пример #1
0
static void setup_file(void **state) {
    int rc;

    setup_dir(state);

    rc = system("echo \"This is a test\" > /tmp/csync_test/file.txt");
    assert_int_equal(rc, 0);
}
Пример #2
0
int
priv_vfs_stickyfile_dir_fother_setup(int asroot, int injail,
    struct test *test)
{

	setup_dir("priv_vfs_stickyfile_fother_setup: dpath", dpath,
	    UID_OTHER, GID_OTHER, 0700);
	dpath_initialized = 1;
	return (0);
}
Пример #3
0
int TestBackupMultiDataDir(CuTest *ct) {
	DB_ENV *dbenv;
	DB *dbp;
	DBTYPE dtype;
	struct handlers *info;
	int has_callback;
	u_int32_t flag;

	dtype = DB_BTREE;
	info = ct->context;
	has_callback = 0;
	flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_FILES;

	/* Step 1: set up directories and make DB_CONFIG. */
	CuAssert(ct, "setup_dir", setup_dir(2, data_dirs) == 0);
	CuAssert(ct, "make_dbconfig",
	    make_dbconfig("set_data_dir DATA1") == 0);

	/* Step 2: open db handle. */
	CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp,
	    dtype, 2, data_dirs, data_dirs[0], NULL, 0, NULL) == 0);
	info->dbenvp = dbenv;
	info->dbp = dbp;

	/* Step 3: store records into db. */
	CuAssert(ct, "store_records", store_records(dbp, 1) == 0);
	CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0);

	/* Step 4: backup the whole environment without callbacks. */
	CuAssert(ct, "backup_env",
	    backup_env(ct, dbenv, flag, has_callback) == 0);	

	/*
	 * Step 5: check backup result.
	 * 5a: verify db files are in BACKUP/DATA1.
	 */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 0, data_dirs[0], data_dirs[0]) == 0);

	/* 5b: verify that data_dirs are in backupdir. */
	CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA1", 0) == 0);
	CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA2", 0) == 0);

	/* 5c: verify that log files are in BACKUP_DIR. */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 1, NULL, NULL) == 0);

	/* 5d: verify that DB_CONFIG is in BACKUP_DIR. */
	CuAssert(ct, "verify_dbconfig", verify_dbconfig(1) == 0);

	return (0);
}
Пример #4
0
int TestBackupSetLogDir(CuTest *ct) {
	DB_ENV *dbenv;
	DB *dbp;
	DBTYPE dtype;
	struct handlers *info;
	char *dirs[2];
	int has_callback = 1;
	u_int32_t flag;

	dtype = DB_BTREE;
	info = ct->context;
	has_callback = 1;
	flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_FILES;
	dirs[0] = LOG_DIR;
	dirs[1] = NULL;

	/* Step 1: set up directories and make DB_CONFIG. */
	CuAssert(ct, "setup_dir", setup_dir(1, dirs) == 0);
	CuAssert(ct, "make_dbconfig", make_dbconfig("set_lg_dir LOG") == 0);

	/* Step 2: open db handle. */
	CuAssert(ct,"open_dbp", open_dbp(&dbenv, &dbp,
	    dtype, 0, NULL, NULL, LOG_DIR, 0, NULL) == 0);
	info->dbenvp = dbenv;
	info->dbp = dbp;

	/* Step 3: store records into db. */
	CuAssert(ct, "store_records", store_records(dbp, 1) == 0);
	CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0);

	/* Step 4: backup the whole environment with callbacks. */
	CuAssert(ct, "backup_env",
	    backup_env(ct, dbenv, flag, has_callback) == 0);

	/*
	 * Step 5: check backup result.
	 * 5a: verify the db file is in BACKUP_DIR.
	 */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 0, NULL, NULL) == 0);

	/* 5b: verify that log files are in BACKUP/LOG. */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 1, LOG_DIR, LOG_DIR) == 0);

	/* 5c: verify that DB_CONFIG is in BACKUP_DIR. */
	CuAssert(ct, "verify_dbconfig", verify_dbconfig(1) == 0);

	return (0);
}
Пример #5
0
int TestBackupPartitionDB(CuTest *ct) {
	DB_ENV *dbenv;
	DB *dbp;
	DBT key1, key2, keys[2];
	DBTYPE dtype;
	struct handlers *info;
	int has_callback;
	u_int32_t flag, value1, value2;

	dtype = DB_BTREE;
	info = ct->context;
	has_callback = 0;
	flag = DB_BACKUP_CLEAN | DB_CREATE | DB_BACKUP_SINGLE_DIR;

	/* Step 1: set up directories and make DB_CONFIG. */
	CuAssert(ct, "setup_dir", setup_dir(1, data_dirs) == 0);
	CuAssert(ct, "make_dbconfig",
	    make_dbconfig("set_data_dir DATA1") == 0);

	/* Make the partition keys. */
	memset(&key1, 0, sizeof(DBT));
	memset(&key2, 0, sizeof(DBT));
	value1 = 8;
	key1.data = &value1;
	key1.size = sizeof(value1);
	value2 = 16;
	key2.data = &value2;
	key2.size = sizeof(value2);
	keys[0] = key1;
	keys[1] = key2;

	/* Step 2: open db handle. */
	CuAssert(ct,"open_dbp", open_dbp(&dbenv,
	    &dbp, dtype, 1, data_dirs, data_dirs[0], NULL, 3, keys) == 0);
	info->dbenvp = dbenv;
	info->dbp = dbp;

	/* Step 3: store records into db. */
	CuAssert(ct, "store_records", store_records(dbp, 1) == 0);
	CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0);

	/* Step 4: backup the whole environment into a single directory. */
	CuAssert(ct, "backup_env",
	    backup_env(ct, dbenv, flag, has_callback) == 0);

	/*
	 * Step 5: check backup result.
	 * 5a: verify db files are in BACKUP/DATA1.
	 */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 1, 0, data_dirs[0], NULL) == 0);

	/* 5b: verify that creation directory is not in BACKUPD_DIR. */
	CuAssert(ct, "__os_exist", __os_exists(NULL, "BACKUP/DATA", 0) != 0);

	/* 5c: verify log files are in BACKUP_DIR. */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 1, NULL, NULL) == 0);

	/* 5d: verify that DB_CONFIG is not in BACKUP_DIR. */
	CuAssert(ct, "verify_dbconfig", verify_dbconfig(0) == 0);

	return (0);
}
Пример #6
0
int TestBackupSimpleEnvWithConfig(CuTest *ct) {
	DB_ENV *dbenv;
	DB *dbp;
	DBTYPE dtype;
	struct handlers *info;
	char **names;
	int cnt, has_callback;
	time_t end_time, secs1, secs2, start_time;
	u_int32_t flag, value;

	dtype = DB_BTREE;
	info = ct->context;
	has_callback = 0;
	flag = DB_EXCL;
	end_time = secs1 = secs2 = start_time = 0;

	/* Step 1: set up directories. */
	CuAssert(ct, "setup_dir", setup_dir(0, NULL) == 0);

	/* Step 2: open db handle. */
	CuAssert(ct, "open_dbp", open_dbp(&dbenv,
	    &dbp, dtype, 0, NULL, NULL, NULL, 0, NULL) == 0);
	info->dbenvp = dbenv;
	info->dbp = dbp;

	/*
	 * Step 3: store records into db so that there is more than
	 * 1 data page in the db.
	 */
	CuAssert(ct, "store_records", store_records(dbp, 10) == 0);
	CuAssert(ct, "DB->sync", dbp->sync(dbp, 0) == 0);

	/*
	 * Step 4: verify the backup handle is NULL,
	 * since we never configure the backup.
	 */
	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv,
	    DB_BACKUP_WRITE_DIRECT, &value) == EINVAL);

	/*
	 * Step 5: backup without any backup configs.
	 * 5a: backup only the db file without callbacks and record the time.
	 */
	start_time = time(NULL);
	CuAssert(ct, "backup_db",
	    backup_db(ct, dbenv, BACKUP_DB, flag, has_callback) == 0);
	end_time = time(NULL);
	secs1 = end_time - start_time;

	/* 5b: verify db file is in BACKUP_DIR. */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 0, NULL, NULL) == 0);

	/* 5c: verify that no other files are in BACKUP_DIR. */
	CuAssert(ct, "__os_dirlist",
	    __os_dirlist(NULL, BACKUP_DIR, 0, &names, &cnt) == 0);
	CuAssert(ct, "too many files in backupdir", cnt == 1);

	/* Clean up the backup directory. */
	setup_envdir(BACKUP_DIR, 1);

	/*
	 * Step 6: backup with backup configs.
	 * 6a: configure the backup handle: use direct I/O to write pages to
	 * the disk, the backup buffer size is 256 bytes (which is smaller
	 * than the db page size), the number of pages
	 * to read before pausing is 1, and the number of seconds to sleep
	 * between batches of reads is 1.
	 */
	CuAssert(ct, "DB_ENV->set_backup_config",
	    dbenv->set_backup_config(dbenv, DB_BACKUP_WRITE_DIRECT, 1) == 0);
	CuAssert(ct, "DB_ENV->set_backup_config",
	    dbenv->set_backup_config(dbenv, DB_BACKUP_SIZE, 256) == 0);
	CuAssert(ct, "DB_ENV->set_backup_config",
	    dbenv->set_backup_config(dbenv, DB_BACKUP_READ_COUNT, 1) == 0);
	CuAssert(ct, "DB_ENV->set_backup_config",
	    dbenv->set_backup_config(dbenv,
	    DB_BACKUP_READ_SLEEP, US_PER_SEC / 2) == 0);

	/*
	 * 6b: backup only the db file without callbacks and
	 * record the time.
	 */
	start_time = time(NULL);
	CuAssert(ct, "backup_db",
	    backup_db(ct, dbenv, BACKUP_DB, flag, has_callback) == 0);
	end_time = time(NULL);
	secs2 = end_time - start_time;

	/* 6c: verify db file is in BACKUP_DIR. */
	CuAssert(ct, "verify_db_log",
	    verify_db_log(dtype, 0, 0, NULL, NULL) == 0);

	/* 6d: no other files are in BACKUP_DIR. */
	CuAssert(ct, "__os_dirlist",
	    __os_dirlist(NULL, BACKUP_DIR, 0, &names, &cnt) == 0);
	CuAssert(ct, "too many files in backupdir", cnt == 1);

	/* 6e: verify the backup config. */
	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv,
	    DB_BACKUP_READ_SLEEP, &value) == 0);
	CuAssertTrue(ct, value == US_PER_SEC / 2);
	/*
	 * Verify the backup config DB_BACKUP_READ_SLEEP works. That is with
	 * the configuration, backup pauses for a number of microseconds
	 * between batches of reads. So for the same backup content, the backup
	 * time with the configuration should be longer than that without it.
	 */
	CuAssertTrue(ct, secs2 > secs1);

	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv,
	    DB_BACKUP_READ_COUNT, &value) == 0);
	CuAssertTrue(ct, value == 1);
	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv, DB_BACKUP_SIZE, &value) == 0);
	CuAssertTrue(ct, value == 256);
	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv,
	    DB_BACKUP_WRITE_DIRECT, &value) == 0);
	CuAssertTrue(ct, value == 1);

	/*
	 * Step 7: re-configure the backup write direct config and
	 * verify the new config value.
	 */
	CuAssert(ct, "DB_ENV->set_backup_config",
	    dbenv->set_backup_config(dbenv, DB_BACKUP_WRITE_DIRECT, 0) == 0);
	CuAssert(ct, "DB_ENV->get_backup_config",
	    dbenv->get_backup_config(dbenv,
	    DB_BACKUP_WRITE_DIRECT, &value) == 0);
	CuAssertTrue(ct, value == 0);

	return (0);
}
Пример #7
0
Файл: fts.c Проект: WndSks/msys
FTS *
fts_open (char * const *argv,
	  register int options,
	  int (*compar) (FTSENT const **, FTSENT const **))
{
	register FTS *sp;
	register FTSENT *p, *root;
	register size_t nitems;
	FTSENT *parent, *tmp = NULL;	/* pacify gcc */
	size_t len;

	/* Options check. */
	if (options & ~FTS_OPTIONMASK) {
		__set_errno (EINVAL);
		return (NULL);
	}

	/* Allocate/initialize the stream */
	if ((sp = malloc(sizeof(FTS))) == NULL)
		return (NULL);
	memset(sp, 0, sizeof(FTS));
	sp->fts_compar = compar;
	sp->fts_options = options;

	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
	if (ISSET(FTS_LOGICAL))
		SET(FTS_NOCHDIR);

	/*
	 * Start out with 1K of file name space, and enough, in any case,
	 * to hold the user's file names.
	 */
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
	{
	  size_t maxarglen = fts_maxarglen(argv);
	  if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
		  goto mem1;
	}

	/* Allocate/initialize root's parent. */
	if ((parent = fts_alloc(sp, "", 0)) == NULL)
		goto mem2;
	parent->fts_level = FTS_ROOTPARENTLEVEL;

	/* Allocate/initialize root(s). */
	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
		/* Don't allow zero-length file names. */
		if ((len = strlen(*argv)) == 0) {
			__set_errno (ENOENT);
			goto mem3;
		}

		if ((p = fts_alloc(sp, *argv, len)) == NULL)
			goto mem3;
		p->fts_level = FTS_ROOTLEVEL;
		p->fts_parent = parent;
		p->fts_accpath = p->fts_name;
		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW) != 0);

		/* Command-line "." and ".." are real directories. */
		if (p->fts_info == FTS_DOT)
			p->fts_info = FTS_D;

		/*
		 * If comparison routine supplied, traverse in sorted
		 * order; otherwise traverse in the order specified.
		 */
		if (compar) {
			p->fts_link = root;
			root = p;
		} else {
			p->fts_link = NULL;
			if (root == NULL)
				tmp = root = p;
			else {
				tmp->fts_link = p;
				tmp = p;
			}
		}
	}
	if (compar && nitems > 1)
		root = fts_sort(sp, root, nitems);

	/*
	 * Allocate a dummy pointer and make fts_read think that we've just
	 * finished the node before the root(s); set p->fts_info to FTS_INIT
	 * so that everything about the "current" node is ignored.
	 */
	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
		goto mem3;
	sp->fts_cur->fts_link = root;
	sp->fts_cur->fts_info = FTS_INIT;
	if (! setup_dir (sp))
	  goto mem3;

	/*
	 * If using chdir(2), grab a file descriptor pointing to dot to ensure
	 * that we can get back here; this could be avoided for some file names,
	 * but almost certainly not worth the effort.  Slashes, symbolic links,
	 * and ".." are all fairly nasty problems.  Note, if we can't get the
	 * descriptor we run anyway, just more slowly.
	 */
	if (!ISSET(FTS_NOCHDIR)
	    && (sp->fts_rfd = diropen (".")) < 0)
		SET(FTS_NOCHDIR);

	return (sp);

mem3:	fts_lfree(root);
	free(parent);
mem2:	free(sp->fts_path);
mem1:	free(sp);
	return (NULL);
}
Пример #8
0
FTS *
fts_open (char * const *argv,
	  register int options,
	  int (*compar) (FTSENT const **, FTSENT const **))
{
	register FTS *sp;
	register FTSENT *p, *root;
	register size_t nitems;
	FTSENT *parent = NULL;
	FTSENT *tmp = NULL;	/* pacify gcc */
	size_t len;
	bool defer_stat;

	/* Options check. */
	if (options & ~FTS_OPTIONMASK) {
		__set_errno (EINVAL);
		return (NULL);
	}
	if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
		__set_errno (EINVAL);
		return (NULL);
	}
	if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
		__set_errno (EINVAL);
		return (NULL);
	}

	/* Allocate/initialize the stream */
	if ((sp = malloc(sizeof(FTS))) == NULL)
		return (NULL);
	memset(sp, 0, sizeof(FTS));
	sp->fts_compar = compar;
	sp->fts_options = options;

	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
	if (ISSET(FTS_LOGICAL)) {
		SET(FTS_NOCHDIR);
		CLR(FTS_CWDFD);
	}

	/* Initialize fts_cwd_fd.  */
	sp->fts_cwd_fd = AT_FDCWD;
	if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
	  {
	    /* While it isn't technically necessary to open "." this
	       early, doing it here saves us the trouble of ensuring
	       later (where it'd be messier) that "." can in fact
	       be opened.  If not, revert to FTS_NOCHDIR mode.  */
	    int fd = open (".", O_RDONLY);
	    if (fd < 0)
	      {
		/* Even if `.' is unreadable, don't revert to FTS_NOCHDIR mode
		   on systems like Linux+PROC_FS, where our openat emulation
		   is good enough.  Note: on a system that emulates
		   openat via /proc, this technique can still fail, but
		   only in extreme conditions, e.g., when the working
		   directory cannot be saved (i.e. save_cwd fails) --
		   and that happens on Linux only when "." is unreadable
		   and the CWD would be longer than PATH_MAX.
		   FIXME: once Linux kernel openat support is well established,
		   replace the above open call and this entire if/else block
		   with the body of the if-block below.  */
		if ( openat_needs_fchdir ())
		  {
		    SET(FTS_NOCHDIR);
		    CLR(FTS_CWDFD);
		  }
	      }
	    else
	      {
		close (fd);
	      }
	  }

	/*
	 * Start out with 1K of file name space, and enough, in any case,
	 * to hold the user's file names.
	 */
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
	{
	  size_t maxarglen = fts_maxarglen(argv);
	  if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
		  goto mem1;
	}

	/* Allocate/initialize root's parent. */
	if (*argv != NULL) {
		if ((parent = fts_alloc(sp, "", 0)) == NULL)
			goto mem2;
		parent->fts_level = FTS_ROOTPARENTLEVEL;
	  }

	/* The classic fts implementation would call fts_stat with
	   a new entry for each iteration of the loop below.
	   If the comparison function is not specified or if the
	   FTS_DEFER_STAT option is in effect, don't stat any entry
	   in this loop.  This is an attempt to minimize the interval
	   between the initial stat/lstat/fstatat and the point at which
	   a directory argument is first opened.  This matters for any
	   directory command line argument that resides on a file system
	   without genuine i-nodes.  If you specify FTS_DEFER_STAT along
	   with a comparison function, that function must not access any
	   data via the fts_statp pointer.  */
	defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));

	/* Allocate/initialize root(s). */
	for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
		/* Don't allow zero-length file names. */
		if ((len = strlen(*argv)) == 0) {
			__set_errno (ENOENT);
			goto mem3;
		}

		if ((p = fts_alloc(sp, *argv, len)) == NULL)
			goto mem3;
		p->fts_level = FTS_ROOTLEVEL;
		p->fts_parent = parent;
		p->fts_accpath = p->fts_name;
		/* Even when defer_stat is true, be sure to stat the first
		   command line argument, since fts_read (at least with
		   FTS_XDEV) requires that.  */
		if (defer_stat && root != NULL) {
			p->fts_info = FTS_NSOK;
			fts_set_stat_required(p, true);
		} else {
			p->fts_info = fts_stat(sp, p, false);
		}

		/*
		 * If comparison routine supplied, traverse in sorted
		 * order; otherwise traverse in the order specified.
		 */
		if (compar) {
			p->fts_link = root;
			root = p;
		} else {
			p->fts_link = NULL;
			if (root == NULL)
				tmp = root = p;
			else {
				tmp->fts_link = p;
				tmp = p;
			}
		}
	}
	if (compar && nitems > 1)
		root = fts_sort(sp, root, nitems);

	/*
	 * Allocate a dummy pointer and make fts_read think that we've just
	 * finished the node before the root(s); set p->fts_info to FTS_INIT
	 * so that everything about the "current" node is ignored.
	 */
	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
		goto mem3;
	sp->fts_cur->fts_link = root;
	sp->fts_cur->fts_info = FTS_INIT;
	if (! setup_dir (sp))
		goto mem3;

	/*
	 * If using chdir(2), grab a file descriptor pointing to dot to ensure
	 * that we can get back here; this could be avoided for some file names,
	 * but almost certainly not worth the effort.  Slashes, symbolic links,
	 * and ".." are all fairly nasty problems.  Note, if we can't get the
	 * descriptor we run anyway, just more slowly.
	 */
	if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
	    && (sp->fts_rfd = diropen (sp, ".")) < 0)
		SET(FTS_NOCHDIR);

	i_ring_init (&sp->fts_fd_ring, -1);
	return (sp);

mem3:	fts_lfree(root);
	free(parent);
mem2:	free(sp->fts_path);
mem1:	free(sp);
	return (NULL);
}
Пример #9
0
Файл: C.c Проект: mattn/C-win32
int main(int argc, char** argv)
{
  int ret;
  
  setup_dir();
  
  /* init globals */
  gcc = sa_concat(gcc, "gcc"); /* replaced laterwards if c++ */
  gcc = sa_concat(gcc, "-I.");
  
  src_lines =
    sa_concat(src_lines,
	      "#define __LARGE_C__ " VERSION_INT_STR "\n"
	      "#ifdef __cplusplus\n"
	      "extern \"C\" {\n"
	      "#endif\n"
	      "#include <stdio.h>\n"
	      "#include <stdlib.h>\n"
	      "#ifdef __cplusplus\n"
	      "}\n"
	      "#include <iostream>\n"
	      "using namespace std;\n"
	      "#endif\n"
	      "\n"
	      "__LARGE_C_PREFIX__\n");
  
  argv++;
  { /* parse args, determine cache dir */
    char** new_argv = parse_args(argv, NULL, 0);
    for (; argv != new_argv; argv++) {
      add_spec(*argv, strlen(*argv) + 1);
    }
    if (! keep_files && (oneliner || *argv != NULL)) {
      struct stat st;
      if (oneliner) {
	build_store_dir();
      } else if (stat(*argv, &st) == 0) {
	add_spec(*argv, strlen(*argv) + 1);
	add_spec(&st.st_size, sizeof(st.st_size));
	add_spec(&st.st_mtime, sizeof(st.st_mtime));
	build_store_dir();
      }
    }
  }
  
  /* use cache if possible */
  if (store_dir != NULL && check_specs()) {
    char** child_argv = NULL;
#ifdef _WIN32
    _utime(store_dir, NULL); /* update mtime of the directory */
#else
    utimes(store_dir, NULL); /* update mtime of the directory */
#endif
    exec_file = str_concat(str_dup(store_dir), "/"A_OUT);
    child_argv = sa_concat(child_argv, exec_file);
#ifdef _WIN32
    {
      int status;
	  ret = spawn_w32(child_argv, &status);
      if (status == 0) exit(ret);
    }
#else
    execv(exec_file, child_argv);
#endif
    // if execv failed, we compile
    free(exec_file);
    remove_dir(store_dir);
  }
  
  /* prepare files */
  make_temp_dir();
  exec_file = str_concat(str_dup(temp_dir), "/"A_OUT);
  c_file = str_concat(str_dup(temp_dir), "/source.c");
  if ((src_fp = fopen(c_file, "wt")) == NULL) {
    cmd_error("failed to create temporary file: %s : %s\n", c_file,
	      strerror(errno));
  }
  while (src_lines != NULL && *src_lines != NULL) {
    fputs(*src_lines++, src_fp);
  }
  
  /* write source with adjustments */
  if (! oneliner) {
    FILE* fp;
    char* file;
    char* line;
    int line_no = 0;
    if (argv[0] == NULL) {
      fp = stdin;
      file = "stdin";
    } else if (strcmp(argv[0], "-") == 0) {
      fp = stdin;
      argv++;
      file = "stdin";
    } else {
      file = *argv++;
      if ((fp = fopen(file, "rt")) == NULL) {
	cmd_error("cannot open file: %s : %s\n", file, strerror(errno));
      }
      fprintf(src_fp, "# 1 \"%s\" 1\n", file);
    }
    while ((line = get_line(fp)) != NULL) {
      int comment_out = 0;
      line_no++;
      if (line_no == 1 && strncmp(line, "#!", 2) == 0) {
	comment_out = 1;
      } else if (line[0] == '#') {
	char* buf = str_dup(line + 1);
	char** tokens = split_tokens(buf);
	if (*tokens != NULL) {
	  if (strcmp(tokens[0], "option") == 0) {
	    parse_args(tokens + 1, file, line_no);
	    comment_out = 1;
	  }
	}
	free(buf);
	free(tokens);
      }
      if (comment_out == 1) {
	fprintf(src_fp, "// ");
      }
      fputs(line, src_fp);
    }
    fputs("\n", src_fp);
    if (fp != stdin) {
      fclose(fp);
    }
  }
  
  /* close source file */
  fputs("__LARGE_C_SUFFIX__\n", src_fp);
  fclose(src_fp);
  src_fp = NULL;
  
  /* compile */
  if (use_plusplus) {
    gcc[0] = "g++";
  }
  if (use_main) {
    gcc = sa_concat(gcc, "-D__LARGE_C_PREFIX__=");
    gcc = sa_concat(gcc, "-D__LARGE_C_SUFFIX__=");
  } else {
    gcc =
      sa_concat(gcc, "-D__LARGE_C_PREFIX__=int main(int argc, char** argv) {");
    gcc = sa_concat(gcc, "-D__LARGE_C_SUFFIX__=; return 0; }");
  }
  gcc = sa_concat(gcc, "-o");
  gcc = sa_concat(gcc, show_disassembly ? "-" : exec_file);
  gcc = sa_concat(gcc, c_file);
  gcc = sa_merge(gcc, lopts);
  if ((ret = call_proc(gcc, "could not execute compiler")) != 0) {
    cleanup();
    exit(ret);
  }
  
  if (show_disassembly) {
    cleanup();
    exit(0);
  }
  
  { /* execute */
    char** child_argv = NULL;
    if (use_debugger) {
      child_argv = sa_concat(child_argv, "gdb");
    }
    child_argv = sa_concat(child_argv, exec_file);
    child_argv = sa_merge(child_argv, argv);
    ret = call_proc(child_argv, "could not spawn child process");
  }
  
  /* move temp_dir to store_dir, if possible.
   * or, remove work_dir
   */
  if (store_dir == NULL) {
    cleanup();
  } else {
    save_specs();
    update_cache();
    if (rename(temp_dir, store_dir) != 0) {
      cleanup();
    }
  }
  
  return ret;
}