/*******************************************************************/
/*  slibtool: a skinny libtool implementation, written in C        */
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
/*******************************************************************/

#include <fcntl.h>
#include <stdio.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <slibtool/slibtool.h>
#include "slibtool_driver_impl.h"
#include "slibtool_spawn_impl.h"
#include "slibtool_dprintf_impl.h"
#include "slibtool_symlink_impl.h"
#include "slibtool_readlink_impl.h"
#include "slibtool_snprintf_impl.h"
#include "slibtool_errinfo_impl.h"

#define PPRIX64 "%"PRIx64

static char * slbt_mri_argument(
	int	fdat,
	char *	arg,
	char *	buf)
{
	char *	lnk;
	char *	target;
	char 	mricwd[PATH_MAX];
	char 	dstbuf[PATH_MAX];

	if ((!(strchr(arg,'+'))) && (!(strchr(arg,'-'))))
		return arg;

	if (arg[0] == '/') {
		target = arg;
	} else {
		if (slbt_realpath(
				fdat,".",O_DIRECTORY,
				mricwd,sizeof(mricwd)) < 0)
			return 0;

		if (slbt_snprintf(dstbuf,sizeof(dstbuf),
				"%s/%s",mricwd,arg) < 0)
			return 0;

		target = dstbuf;
	}

	lnk = 0;

	{
		struct stat st;

		if (fstatat(fdat,target,&st,0) < 0)
			return 0;

		sprintf(buf,
			".mri.tmplnk"
			".dev."PPRIX64
			".inode."PPRIX64
			".size."PPRIX64
			".tmp",
			st.st_dev,
			st.st_ino,
			st.st_size);

		unlinkat(fdat,buf,0);

		if (!(symlinkat(target,fdat,buf)))
			lnk = buf;
	}

	return lnk;
}

static void slbt_archive_import_child(
	char *	program,
	int	fd[2])
{
	char *	argv[3];

	argv[0] = program;
	argv[1] = "-M";
	argv[2] = 0;

	close(fd[1]);

	if (dup2(fd[0],0) == 0)
		execvp(program,argv);

	_exit(EXIT_FAILURE);
}

int slbt_archive_import_mri(
	const struct slbt_driver_ctx *	dctx,
	struct slbt_exec_ctx *		ectx,
	char *				dstarchive,
	char *				srcarchive)
{
	int	fdcwd;
	pid_t	pid;
	pid_t	rpid;
	int	fd[2];
	char *	dst;
	char *	src;
	char *	fmt;
	char	mridst [96];
	char	mrisrc [96];
	char	program[PATH_MAX];

	/* fdcwd */
	fdcwd = slbt_driver_fdcwd(dctx);

	/* not needed? */
	if (slbt_symlink_is_a_placeholder(fdcwd,srcarchive))
		return 0;

	/* program */
	if (slbt_snprintf(program,sizeof(program),
			"%s",dctx->cctx->host.ar) < 0)
		return SLBT_BUFFER_ERROR(dctx);

	/* fork */
	if (pipe(fd))
		return SLBT_SYSTEM_ERROR(dctx,0);

	if ((pid = fork()) < 0) {
		close(fd[0]);
		close(fd[1]);
		return SLBT_SYSTEM_ERROR(dctx,0);
	}

	/* child */
	if (pid == 0)
		slbt_archive_import_child(
			program,
			fd);

	/* parent */
	close(fd[0]);

	ectx->pid = pid;

	dst = slbt_mri_argument(fdcwd,dstarchive,mridst);
	src = slbt_mri_argument(fdcwd,srcarchive,mrisrc);

	if (!dst || !src) {
		close(fd[1]);
		return SLBT_SYSTEM_ERROR(dctx,0);
	}

	fmt = "OPEN %s\n"
	      "ADDLIB %s\n"
	      "SAVE\n"
	      "END\n";

	if (slbt_dprintf(fd[1],fmt,dst,src) < 0) {
		close(fd[1]);
		return SLBT_SYSTEM_ERROR(dctx,0);
	}

	close(fd[1]);

	rpid = waitpid(
		pid,
		&ectx->exitcode,
		0);

	if (dst == mridst)
		unlinkat(fdcwd,dst,0);

	if (src == mrisrc)
		unlinkat(fdcwd,src,0);

	return (rpid == pid) && (ectx->exitcode == 0)
		? 0 : SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_ARCHIVE_IMPORT);
}