/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1998--2023 The R Core Team
 *
 *  This program 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.
 *
 *  This program 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, a copy is available at
 *  https://www.R-project.org/Licenses/
 */

/* Formerly in src/main/platform.c */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <Defn.h>
#undef _
#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) dgettext ("tools", String)
#else
#define _(String) (String)
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif

#if HAVE_DIRENT_H
# include <dirent.h>
#elif HAVE_SYS_NDIR_H
# include <sys/ndir.h>
#elif HAVE_SYS_DIR_H
# include <sys/dir.h>
#elif HAVE_NDIR_H
# include <ndir.h>
#endif
static const char  * const R_FileSep = FILESEP;

static void chmod_one(const char *name, const int grpwrt)
{
    R_DIR *dir;
    struct R_dirent *de;
    char *p;
#ifdef Win32
    struct _stati64 sb;
#else
    struct stat sb;
#endif
#ifndef Win32
    mode_t mask, dirmask;
    if (grpwrt) {
	mask = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP; /* 0664 */
	dirmask = mask | S_IXUSR | S_IXGRP | S_IXOTH;           /* 0755 */
    } else {
	mask = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;           /* 0644 */
	dirmask = mask | S_IXUSR | S_IXGRP | S_IXOTH;           /* 0755 */
    }
#endif

    if (streql(name, ".") || streql(name, "..")) return;
    if (!R_FileExists(name)) return;
#ifdef Win32
    _stati64(name, &sb);
    chmod(name, _S_IWRITE);
#else
    stat(name, &sb);
    chmod(name, (sb.st_mode | mask) & dirmask);
#endif
    if ((sb.st_mode & S_IFDIR) > 0) { /* a directory */
#ifndef Win32
	chmod(name, dirmask);
#endif
	if ((dir = R_opendir(name)) != NULL) {
	    while ((de = R_readdir(dir))) {
		if (streql(de->d_name, ".") || streql(de->d_name, ".."))
		    continue;
		size_t n = strlen(name);
		size_t needed = n + 1 + strlen(de->d_name) + 1;
#ifdef Unix
		if (needed >= R_PATH_MAX)
		    error(_("path too long"));
#endif
		const void *vmax = vmaxget();
		p = R_alloc(needed, 1);
		if (name[n-1] == R_FileSep[0])
		    snprintf(p, needed, "%s%s", name, de->d_name);
		else
		    snprintf(p, needed, "%s%s%s", name, R_FileSep, de->d_name);
		chmod_one(p, grpwrt);
		vmaxset(vmax);
	    }
	    R_closedir(dir);
	} else { 
	    /* we were unable to read a dir */
	}
    }
}

/* recursively fix up permissions: used for R CMD INSTALL and build.
   'gwsxp' means set group-write permissions on directories.
   NB: this overrides umask. */
/* This is a .Call so manages R_alloc stack */
SEXP dirchmod(SEXP dr, SEXP gwsxp)
{
    if(!isString(dr) || LENGTH(dr) != 1)
	error(_("invalid '%s' argument"), "dir");
    chmod_one(translateChar(STRING_ELT(dr, 0)), asLogical(gwsxp));

    return R_NilValue;
}

#if defined(BUFSIZ) && (BUFSIZ > 512)
/* OS's buffer size in stdio.h, probably.
   Windows has 512, Solaris 1024, glibc 8192
 */
# define APPENDBUFSIZE BUFSIZ
#else
# define APPENDBUFSIZE 512
#endif

SEXP codeFilesAppend(SEXP f1, SEXP f2, SEXP enc)
{
    int n;
    if (!isString(f1) || LENGTH(f1) != 1)
	error(_("invalid '%s' argument"), "file1");
    if (!isString(f2))
	error(_("invalid '%s' argument"), "file2");
    if (!isString(enc) || LENGTH(enc) != 1)
	error(_("invalid '%s' argument"), "enc");
    int is_utf8 = STRING_ELT(enc, 0) != NA_STRING &&
	strncmp(CHAR(STRING_ELT(enc, 0)), "UTF-8", strlen("UTF-8")) == 0;
    n = LENGTH(f2);
    SEXP ans = PROTECT(allocVector(LGLSXP, n));
    for (int i = 0; i < n; i++) LOGICAL(ans)[i] = 0;  /* all FALSE */
    FILE *fp1, *fp2;
    char buf[APPENDBUFSIZE];
    int status = 0;
    size_t nchar;
    if (STRING_ELT(f1, 0) == NA_STRING ||
	!(fp1 = RC_fopen(STRING_ELT(f1, 0), "ab", TRUE)))
	goto done;
    for (int i = 0; i < n; i++) {
	status = 0;
	if (STRING_ELT(f2, i) == NA_STRING ||
	    !(fp2 = RC_fopen(STRING_ELT(f2, i), "rb", TRUE))) continue;
	if (is_utf8) {
	    unsigned char bom[3];
	    if (fread(bom, 1, 3, fp2) != 3 || bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF)
		rewind(fp2);
	}
	snprintf(buf, APPENDBUFSIZE, "#line 1 \"%s\"\n",
		 CHAR(STRING_ELT(f2, i)));
	if(fwrite(buf, 1, strlen(buf), fp1) != strlen(buf)) goto append_error;
	while ((nchar = fread(buf, 1, APPENDBUFSIZE, fp2)) == APPENDBUFSIZE)
	    if (fwrite(buf, 1, APPENDBUFSIZE, fp1) != APPENDBUFSIZE)
		goto append_error;
	if (fwrite(buf, 1, nchar, fp1) != nchar) goto append_error;
	if (!nchar || buf[nchar - 1] != '\n')
	    if (fwrite("\n", 1, 1, fp1) != 1) goto append_error;
	
	status = 1;
    append_error:
	if (status == 0) warning(_("write error during file append"));
	LOGICAL(ans)[i] = status;
	fclose(fp2);
    }
    fclose(fp1);
done:
    UNPROTECT(1);
    return ans;
}
