>From 47b2a0ea3ca97cc4bfa9126bf853a8c54953fcbd Mon Sep 17 00:00:00 2001
From: Heikki Orsila
Date: Mon, 23 Jul 2018 21:17:59 +0100
Subject: [PATCH] Add -i option for iconv to convert files in-place.
Normally converted files are written to stdout.
With -i option the files are replaced with converted content.
---
man/iconv.1 | 6 ++++-
src/iconv.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/man/iconv.1 b/man/iconv.1
index 9c6af5a..a8458f9 100644
--- a/man/iconv.1
+++ b/man/iconv.1
@@ -14,7 +14,7 @@
iconv \- character set conversion
.SH SYNOPSIS
.nf
-iconv [\fIOPTION\fP...] [\fB\-f\fP \fIencoding\fP] [\fB\-t\fP \fIencoding\fP] [\fIinputfile\fP ...]
+iconv [\fIOPTION\fP...] [\fB\-f\fP \fIencoding\fP] [\fB\-t\fP \fIencoding\fP] [\fB\-i\fP] [\fIinputfile\fP ...]
iconv \fB\-l\fP
.fi
.SH DESCRIPTION
@@ -36,6 +36,10 @@ Specifies the encoding of the input.
.TP
\fB\-t\fP \fIencoding\fP, \fB\-\-to\-code=\fP\fIencoding\fP
Specifies the encoding of the output.
+.TP
+\fB\-i\fP
+When this option is given, files are converted in-place instead of writing
+to stdout.
.PP
Options controlling conversion problems:
.TP
diff --git a/src/iconv.c b/src/iconv.c
index 505521b..b1a908a 100644
--- a/src/iconv.c
+++ b/src/iconv.c
@@ -28,6 +28,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
/* Ensure that iconv_no_i18n does not depend on libintl. */
#ifdef NO_I18N
@@ -595,7 +599,8 @@ static iconv_t subst_mb_to_mb_cd;
/* Buffer of size ilseq_byte_subst_size*4. */
static char* subst_mb_to_mb_temp_buffer;
-static void subst_mb_to_mb_fallback (const char* inbuf, size_t inbufsize)
+static void subst_mb_to_mb_fallback (const char* inbuf, size_t inbufsize,
+ FILE* outfile)
{
for (; inbufsize > 0; inbuf++, inbufsize--) {
const char* inptr;
@@ -619,7 +624,7 @@ static void subst_mb_to_mb_fallback (const char* inbuf, size_t inbufsize)
_("cannot convert byte substitution to target encoding: %s"),
ilseq_byte_subst_buffer);
fwrite(subst_mb_to_mb_temp_buffer,1,ilseq_byte_subst_size*4-outbytesleft,
- stdout);
+ outfile);
}
}
@@ -668,7 +673,8 @@ static void conversion_error_other (int errnum, const char* infilename)
/* Convert the input given in infile. */
-static int convert (iconv_t cd, int infile, const char* infilename)
+static int convert (iconv_t cd, int infile, const char* infilename,
+ FILE* outfile)
{
char inbuf[4096+4096];
size_t inbufrest = 0;
@@ -687,7 +693,7 @@ static int convert (iconv_t cd, int infile, const char* infilename)
size_t inbufsize;
/* Transfer the accumulated output to its destination, in case the
safe_read() call will block. */
- fflush(stdout);
+ fflush(outfile);
inbufsize = safe_read(infile,inbuf+4096,4096);
if (inbufsize == 0 || inbufsize == SAFE_READ_ERROR) {
infile_error = (inbufsize == SAFE_READ_ERROR ? errno : 0);
@@ -695,7 +701,7 @@ static int convert (iconv_t cd, int infile, const char* infilename)
break;
else {
if (ilseq_byte_subst != NULL)
- subst_mb_to_mb_fallback(inbuf+4096-inbufrest, inbufrest);
+ subst_mb_to_mb_fallback(inbuf+4096-inbufrest, inbufrest, outfile);
if (!silent)
conversion_error_EINVAL(infilename);
status = 1;
@@ -711,7 +717,7 @@ static int convert (iconv_t cd, int infile, const char* infilename)
size_t res = iconv(cd,(ICONV_CONST char**)&inptr,&insize,&outptr,&outsize);
if (outptr != outbuf) {
int saved_errno = errno;
- if (fwrite(outbuf,1,outptr-outbuf,stdout) < outptr-outbuf) {
+ if (fwrite(outbuf,1,outptr-outbuf,outfile) < outptr-outbuf) {
status = 1;
goto done;
}
@@ -773,7 +779,7 @@ static int convert (iconv_t cd, int infile, const char* infilename)
size_t res = iconv(cd,NULL,NULL,&outptr,&outsize);
if (outptr != outbuf) {
int saved_errno = errno;
- if (fwrite(outbuf,1,outptr-outbuf,stdout) < outptr-outbuf) {
+ if (fwrite(outbuf,1,outptr-outbuf,outfile) < outptr-outbuf) {
status = 1;
goto done;
}
@@ -817,7 +823,7 @@ static int convert (iconv_t cd, int infile, const char* infilename)
break;
}
if (infile_error) {
- fflush(stdout);
+ fflush(outfile);
if (column > 0)
putc('\n',stderr);
error(0,infile_error,
@@ -834,12 +840,39 @@ static int convert (iconv_t cd, int infile, const char* infilename)
return status;
}
+/* create_outfile creates a temporary for the inplace mode. */
+FILE* create_outfile(char** outfilename, FILE* infile, const char* infilename)
+{
+ struct stat st;
+ char* dnamebuf;
+ int ret;
+ /* test that file is regular. */
+ if (fstat(fileno(infile), &st))
+ return NULL;
+ if (!S_ISREG(st.st_mode)) {
+ error(0, 0, _("will not do inplace replacement for non-regular files."),
+ infilename);
+ return NULL;
+ }
+ dnamebuf = strdup(infilename);
+ if (dnamebuf == NULL)
+ return NULL;
+ ret = asprintf(outfilename, "%s/.iconv.tmp.XXXXXX", dirname(dnamebuf));
+ free(dnamebuf);
+ if (ret < 0)
+ return NULL;
+ if (mkstemp(*outfilename) < 0)
+ return NULL;
+ return fopen(*outfilename, "wb");
+}
+
/* ========================================================================= */
int main (int argc, char* argv[])
{
const char* fromcode = NULL;
const char* tocode = NULL;
+ int do_inplace = 0;
int do_list = 0;
iconv_t cd;
struct iconv_fallbacks fallbacks;
@@ -884,6 +917,11 @@ int main (int argc, char* argv[])
}
continue;
}
+ if (!strcmp(argv[i],"-i") || !strcmp(argv[i],"--inplace")) {
+ do_inplace = 1;
+ i++;
+ continue;
+ }
if (!strcmp(argv[i],"-t")
/* --t ... --to-code */
|| (len >= 3 && len <= 9 && !strncmp(argv[i],"--to-code",len))
@@ -1086,7 +1124,7 @@ int main (int argc, char* argv[])
if (i == argc)
status = convert(cd,fileno(stdin),
/* TRANSLATORS: A filename substitute denoting standard input. */
- _("(stdin)"));
+ _("(stdin)"),stdout);
else {
status = 0;
for (; i < argc; i++) {
@@ -1102,8 +1140,34 @@ int main (int argc, char* argv[])
infilename);
status = 1;
} else {
- status |= convert(cd,fileno(infile),infilename);
- fclose(infile);
+ if (do_inplace) {
+ FILE* outfile = stdout;
+ char* outfilename = NULL;
+ outfile = create_outfile(&outfilename, infile, infilename);
+ if (outfile != NULL) {
+ status |= convert(cd,fileno(infile), infilename, outfile);
+ } else {
+ error(0, 0, _("%s"), infilename);
+ status |= 1;
+ }
+ fclose(infile);
+ if (outfile != NULL) {
+ fclose(outfile);
+ if (rename(outfilename, infilename)) {
+ /* If this fails, what can we do? */
+ if (remove(outfilename)) {
+ error(0, 0, _("cannot remove file %s. Please clean it."),
+ outfilename);
+ }
+ error(0, 0, _("cannot replace (rename) file %s."),
+ infilename);
+ status |= 1;
+ }
+ }
+ } else {
+ status |= convert(cd, fileno(infile), infilename, stdout);
+ fclose(infile);
+ }
}
}
}
--
2.11.0