* [PATCH 4/5] nfs-utils: Add config file writing function
2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
` (2 preceding siblings ...)
2018-06-20 12:12 ` [PATCH 3/5] nfs-utils: swap xlog_err for less fatal version Justin Mitchell
@ 2018-06-20 12:13 ` Justin Mitchell
2018-06-20 12:14 ` [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool Justin Mitchell
2018-06-25 15:38 ` [PATCH 0/5] nfs-utils: config value setting Steve Dickson
5 siblings, 0 replies; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:13 UTC (permalink / raw)
To: Linux NFS Mailing list; +Cc: Steve Dickson
Adds a function to nfsconf handling to write a single config
entry, creating the file and section headers as required.
Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
support/include/conffile.h | 1 +
support/nfs/conffile.c | 621 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 622 insertions(+)
diff --git a/support/include/conffile.h b/support/include/conffile.h
index bc2d61f..a3340f9 100644
--- a/support/include/conffile.h
+++ b/support/include/conffile.h
@@ -67,6 +67,7 @@ extern int conf_match_num(const char *, const char *, int);
extern int conf_remove(int, const char *, const char *);
extern int conf_remove_section(int, const char *);
extern void conf_report(FILE *);
+extern int conf_write(const char *, const char *, const char *, const char *, const char *);
/*
* Convert letter from upper case to lower case
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index c7c3a62..c2dbced 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -1323,3 +1323,624 @@ cleanup:
}
return;
}
+
+/* struct and queue for buffing output lines */
+TAILQ_HEAD(tailhead, outbuffer);
+
+struct outbuffer {
+ TAILQ_ENTRY(outbuffer) link;
+ char *text;
+};
+
+static struct outbuffer *
+make_outbuffer(char *line)
+{
+ struct outbuffer *new;
+
+ if (line == NULL)
+ return NULL;
+
+ new = calloc(1, sizeof(struct outbuffer));
+ if (new == NULL) {
+ xlog(L_ERROR, "malloc error creating outbuffer");
+ return NULL;
+ }
+ new->text = line;
+ return new;
+}
+
+/* compose a properly escaped tag=value line */
+static char *
+make_tagline(const char *tag, const char *value)
+{
+ char *line;
+ int ret;
+
+ if (!value)
+ return NULL;
+
+ if (should_escape(value))
+ ret = asprintf(&line, "%s = \"%s\"\n", tag, value);
+ else
+ ret = asprintf(&line, "%s = %s\n", tag, value);
+
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing a tag line");
+ return NULL;
+ }
+ return line;
+}
+
+/* compose a section header line */
+static char *
+make_section(const char *section, const char *arg)
+{
+ char *line;
+ int ret;
+
+ if (arg)
+ ret = asprintf(&line, "[%s \"%s\"]\n", section, arg);
+ else
+ ret = asprintf(&line, "[%s]\n", section);
+
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing section header");
+ return NULL;
+ }
+ return line;
+}
+
+/* does the supplied line contain the named section header */
+static bool
+is_section(const char *line, const char *section, const char *arg)
+{
+ char *end;
+ char *name;
+ char *sub;
+ bool found = false;
+
+ /* skip leading white space */
+ while (*line == '[' || isspace(*line))
+ line++;
+
+ name = strdup(line);
+ if (name == NULL) {
+ xlog_warn("conf_write: malloc failed ");
+ return false;
+ }
+
+ /* find the end */
+ end = strchr(name, ']');
+
+ /* malformed line */
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed section name");
+ goto cleanup;
+ }
+
+ while (*end && ( *end == ']' || isblank(*end)))
+ *(end--) = '\0';
+
+ /* is there a subsection name (aka arg) */
+ sub = strchr(name, '"');
+ if (sub) {
+ end = sub - 1;
+ *(sub++) = '\0';
+
+ /* trim whitespace between section name and arg */
+ while (end > name && isblank(*end))
+ *(end--) = '\0';
+
+ /* trim off closing quote */
+ end = strchr(sub, '"');
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed sub-section name");
+ goto cleanup;
+ }
+ *end = '\0';
+ }
+
+ /* ready to compare */
+ if (strcasecmp(section, name)!=0)
+ goto cleanup;
+
+ if (arg != NULL) {
+ if (sub == NULL || strcasecmp(arg, sub)!=0)
+ goto cleanup;
+ } else {
+ if (sub != NULL)
+ goto cleanup;
+ }
+
+ found = true;
+
+cleanup:
+ free(name);
+ return found;
+}
+
+/* check that line contains the specified tag assignment */
+static bool
+is_tag(const char *line, const char *tagname)
+{
+ char *end;
+ char *name;
+ bool found = false;
+
+ /* quick check, is this even an assignment line */
+ end = strchr(line, '=');
+ if (end == NULL)
+ return false;
+
+ /* skip leading white space before tag name */
+ while (isblank(*line))
+ line++;
+
+ name = strdup(line);
+ if (name == NULL) {
+ xlog_warn("conf_write: malloc failed");
+ return false;
+ }
+
+ /* trim any newline characters */
+ end = strchr(name, '\n');
+ if (end)
+ *end = '\0';
+ end = strchr(name, '\r');
+ if (end)
+ *end = '\0';
+
+ /* find the assignment equals sign */
+ end = strchr(name, '=');
+
+ /* malformed line, i swear the equals was there earlier */
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed tag name");
+ goto cleanup;
+ }
+
+ /* trim trailing whitespace after tag name */
+ do {
+ *(end--) = '\0';
+ }while (end > name && *end && isblank(*end));
+
+ /* quoted string, take contents of quotes only */
+ if (*name == '"') {
+ char * new = strdup(name+1);
+ end = strchr(new, '"');
+ if (end != NULL) {
+ *end = 0;
+ free(name);
+ name = new;
+ } else {
+ free(new);
+ }
+ }
+
+ /* now compare */
+ if (strcasecmp(tagname, name) == 0)
+ found = true;
+
+cleanup:
+ free(name);
+ return found;
+}
+
+/* is this an empty line ? */
+static bool
+is_empty(const char *line)
+{
+ const char *p = line;
+
+ if (line == NULL)
+ return true;
+ if (*line == '\0')
+ return true;
+
+ while (*p != '\0' && isspace(*p))
+ p++;
+
+ if (*p == '\0')
+ return true;
+
+ return false;
+}
+
+/* is this line just a comment ? */
+static bool
+is_comment(const char *line)
+{
+ if (line == NULL)
+ return false;
+
+ while (isblank(*line))
+ line++;
+
+ if (*line == '#')
+ return true;
+
+ return false;
+}
+
+/* delete a buffer queue whilst optionally outputting to file */
+static int
+flush_outqueue(struct tailhead *queue, FILE *fout)
+{
+ int ret = 0;
+ while (queue->tqh_first != NULL) {
+ struct outbuffer *ob = queue->tqh_first;
+ TAILQ_REMOVE(queue, ob, link);
+ if (ob->text) {
+ if (fout) {
+ ret = fprintf(fout, "%s", ob->text);
+ if (ret == -1) {
+ xlog(L_ERROR, "Error writing to config file: %s",
+ strerror(errno));
+ fout = NULL;
+ }
+ }
+ free(ob->text);
+ }
+ free(ob);
+ }
+ if (ret == -1)
+ return 1;
+ return 0;
+}
+
+/* read one line of text from a file, growing the buffer as necessary */
+static int
+read_line(char **buff, int *buffsize, FILE *in)
+{
+ char *readp;
+ int used = 0;
+ bool again = false;
+
+ /* make sure we have a buffer to read into */
+ if (*buff == NULL) {
+ *buffsize = 4096;
+ *buff = calloc(1, *buffsize);
+ if (*buff == NULL) {
+ xlog(L_ERROR, "malloc error for read buffer");
+ return -1;
+ }
+ }
+
+ readp = *buff;
+
+ do {
+ int len;
+
+ /* read in a chunk */
+ if (fgets(readp, *buffsize-used, in)==NULL)
+ return -1;
+
+ len = strlen(*buff);
+ if (len == 0)
+ return -1;
+
+ /* was this the end of a line, or partial read */
+ readp = *buff + len - 1;
+
+ if (*readp != '\n' && *readp !='\r') {
+ /* no nl/cr must be partial read, go again */
+ readp++;
+ again = true;
+ } else {
+ /* that was a normal end of line */
+ again = false;
+ }
+
+ /* do we need more space */
+ if (again && *buffsize - len < 1024) {
+ int offset = readp - *buff;
+ char *newbuff;
+ *buffsize += 4096;
+ newbuff = realloc(*buff, *buffsize);
+ if (newbuff == NULL) {
+ xlog(L_ERROR, "malloc error reading line");
+ return -1;
+ }
+ *buff = newbuff;
+ readp = newbuff + offset;
+ }
+ } while(again);
+ return 0;
+}
+
+/* append a line to the given location in the queue */
+static int
+append_line(struct tailhead *queue, struct outbuffer *entry, char *line)
+{
+ int ret = 0;
+ char *end;
+ bool splitmode = false;
+ char *start = line;
+
+ if (line == NULL)
+ return -1;
+
+ /* if there are \n's in the middle of the string
+ * then we need to split it into folded lines */
+ do {
+ char *thisline;
+ struct outbuffer *qbuff;
+
+ end = strchr(start, '\n');
+ if (end && *(end+1) != '\0') {
+ *end = '\0';
+
+ ret = asprintf(&thisline, "%s\\\n", start);
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing output");
+ return -1;
+ }
+ splitmode = true;
+ start = end+1;
+ } else {
+ end = NULL;
+ if (splitmode) {
+ thisline = strdup(start);
+ if (thisline == NULL)
+ return -1;
+ } else {
+ thisline = start;
+ }
+ }
+
+ qbuff = make_outbuffer(thisline);
+ if (qbuff == NULL)
+ return -1;
+
+ if (entry) {
+ TAILQ_INSERT_AFTER(queue, entry, qbuff, link);
+ entry = TAILQ_NEXT(entry, link);
+ } else {
+ TAILQ_INSERT_TAIL(queue, qbuff, link);
+ }
+ }while (end != NULL);
+
+ /* we malloced copies of this, so free the original */
+ if (splitmode)
+ free(line);
+
+ return 0;
+}
+
+/* is this a "folded" line, i.e. ends in backslash */
+static bool
+is_folded(const char *line)
+{
+ const char *end;
+ if (line == NULL)
+ return false;
+
+ end = line + strlen(line);
+ while (end > line) {
+ end--;
+ if (*end != '\n' && *end != '\r')
+ break;
+ }
+
+ if (*end == '\\')
+ return true;
+
+ return false;
+}
+
+/***
+ * Write a value to an nfs.conf style filename
+ *
+ * create the file if it doesnt already exist
+ * if value==NULL removes the setting (if present)
+ */
+int
+conf_write(const char *filename, const char *section, const char *arg,
+ const char *tag, const char *value)
+{
+ int fdout = -1;
+ char *outpath = NULL;
+ FILE *outfile = NULL;
+ FILE *infile = NULL;
+ int ret = 1;
+ struct tailhead outqueue;
+ char * buff = NULL;
+ int buffsize = 0;
+
+ TAILQ_INIT(&outqueue);
+
+ if (!filename) {
+ xlog_warn("conf_write: no filename supplied");
+ return ret;
+ }
+
+ if (!section || !tag) {
+ xlog_warn("conf_write: section or tag name missing");
+ return ret;
+ }
+
+ if (asprintf(&outpath, "%s.XXXXXX", filename) == -1) {
+ xlog(L_ERROR, "conf_write: error composing temp filename");
+ return ret;
+ }
+
+ fdout = mkstemp(outpath);
+ if (fdout < 0) {
+ xlog(L_ERROR, "conf_write: open temp file %s failed: %s",
+ outpath, strerror(errno));
+ goto cleanup;
+ }
+
+ outfile = fdopen(fdout, "w");
+ if (!outfile) {
+ xlog(L_ERROR, "conf_write: fdopen temp file failed: %s",
+ strerror(errno));
+ goto cleanup;
+ }
+
+ infile = fopen(filename, "r");
+ if (!infile) {
+ if (!value) {
+ xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename);
+ ret = 0;
+ goto cleanup;
+ }
+
+ xlog_warn("conf_write: config file \"%s\" not found, creating.", filename);
+ if (append_line(&outqueue, NULL, make_section(section, arg)))
+ goto cleanup;
+
+ if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+ goto cleanup;
+
+ if (flush_outqueue(&outqueue, outfile))
+ goto cleanup;
+ } else {
+ bool found = false;
+ int err = 0;
+
+ buffsize = 4096;
+ buff = calloc(1, buffsize);
+ if (buff == NULL) {
+ xlog(L_ERROR, "malloc error for read buffer");
+ goto cleanup;
+ }
+
+ buff[0] = '\0';
+ do {
+ struct outbuffer *where = NULL;
+
+ /* read in one section worth of lines */
+ do {
+ if (*buff != '\0') {
+ if (append_line(&outqueue, NULL, strdup(buff)))
+ goto cleanup;
+ }
+
+ err = read_line(&buff, &buffsize, infile);
+ } while (err == 0 && buff[0] != '[');
+
+ /* find the section header */
+ where = TAILQ_FIRST(&outqueue);
+ while (where != NULL) {
+ if (where->text != NULL && where->text[0] == '[')
+ break;
+ where = TAILQ_NEXT(where, link);
+ }
+
+ /* this is the section we care about */
+ if (where != NULL && is_section(where->text, section, arg)) {
+ /* is there an existing assignment */
+ while ((where = TAILQ_NEXT(where, link)) != NULL) {
+ if (is_tag(where->text, tag)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
+ bool again = false;
+
+ /* remove current tag */
+ do {
+ struct outbuffer *next = TAILQ_NEXT(where, link);
+ TAILQ_REMOVE(&outqueue, where, link);
+ if (is_folded(where->text))
+ again = true;
+ else
+ again = false;
+ free(where->text);
+ free(where);
+ where = next;
+ } while(again && where != NULL);
+
+ /* insert new tag */
+ if (value) {
+ if (append_line(&outqueue, prev, make_tagline(tag, value)))
+ goto cleanup;
+ }
+ } else
+ /* no existing assignment found and we need to add one */
+ if (value) {
+ /* rewind past blank lines and comments */
+ struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+
+ /* comments immediately before a section usually relate
+ * to the section below them */
+ while (tail != NULL && is_comment(tail->text))
+ tail = TAILQ_PREV(tail, tailhead, link);
+
+ /* there is usually blank line(s) between sections */
+ while (tail != NULL && is_empty(tail->text))
+ tail = TAILQ_PREV(tail, tailhead, link);
+
+ /* now add the tag here */
+ if (append_line(&outqueue, tail, make_tagline(tag, value)))
+ goto cleanup;
+
+ found = true;
+ }
+ }
+
+ /* EOF and correct section not found, so add one */
+ if (err && !found && value) {
+ /* did the last section end in a blank line */
+ struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+ if (tail && !is_empty(tail->text)) {
+ /* no, so add one for clarity */
+ if (append_line(&outqueue, NULL, strdup("\n")))
+ goto cleanup;
+ }
+
+ /* add the new section header */
+ if (append_line(&outqueue, NULL, make_section(section, arg)))
+ goto cleanup;
+
+ /* now add the tag */
+ if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+ goto cleanup;
+ }
+
+ /* we are done with this section, write it out */
+ if (flush_outqueue(&outqueue, outfile))
+ goto cleanup;
+ } while(err == 0);
+ }
+
+ if (infile) {
+ fclose(infile);
+ infile = NULL;
+ }
+
+ fdout = -1;
+ if (fclose(outfile)) {
+ xlog(L_ERROR, "Error writing config file: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ /* now swap the old file for the new one */
+ if (rename(outpath, filename)) {
+ xlog(L_ERROR, "Error updating config file: %s: %s\n", filename, strerror(errno));
+ ret = 1;
+ } else {
+ ret = 0;
+ free(outpath);
+ outpath = NULL;
+ }
+
+cleanup:
+ flush_outqueue(&outqueue, NULL);
+
+ if (buff)
+ free(buff);
+ if (infile)
+ fclose(infile);
+ if (fdout != -1)
+ close(fdout);
+ if (outpath) {
+ unlink(outpath);
+ free(outpath);
+ }
+ return ret;
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/5] nfs-utils: Add config setting to nfsconf cli tool
2018-06-20 12:09 [PATCH 0/5] nfs-utils: config value setting Justin Mitchell
` (3 preceding siblings ...)
2018-06-20 12:13 ` [PATCH 4/5] nfs-utils: Add config file writing function Justin Mitchell
@ 2018-06-20 12:14 ` Justin Mitchell
2018-06-22 3:18 ` Calum Mackay
2018-06-25 15:38 ` [PATCH 0/5] nfs-utils: config value setting Steve Dickson
5 siblings, 1 reply; 9+ messages in thread
From: Justin Mitchell @ 2018-06-20 12:14 UTC (permalink / raw)
To: Linux NFS Mailing list; +Cc: Steve Dickson
Use the new conf_write() function to add setting and
unsetting of config file values to the cli tool
Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
tools/nfsconf/nfsconf.man | 26 ++++++++++++++++
tools/nfsconf/nfsconfcli.c | 75 +++++++++++++++++++++++++++++++++++++++-------
2 files changed, 90 insertions(+), 11 deletions(-)
diff --git a/tools/nfsconf/nfsconf.man b/tools/nfsconf/nfsconf.man
index 5b5e914..197caae 100644
--- a/tools/nfsconf/nfsconf.man
+++ b/tools/nfsconf/nfsconf.man
@@ -28,6 +28,25 @@ nfsconf \- Query various NFS configuration settings
.IR subsection ]
.IR section
.IR tag
+.P
+.B nfsconf \-\-set
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
+.IR value
+.P
+.B nfsconf \-\-unset
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
.SH DESCRIPTION
The
.B nfsconf
@@ -41,6 +60,10 @@ Output an alphabetically sorted dump of the current configuration in conf file f
Test if a specific tag has a value set.
.IP "\fB\-g, \-\-get\fP"
Output the current value of the specified tag.
+.IP "\fB\-s, \-\-set\fP"
+Update or Add a tag and value to the config file, creating the file if necessary.
+.IP "\fB\-u, \-\-unset\fP"
+Remove the specified tag and its value from the config file.
.SH OPTIONS
.SS Options valid in all modes
.TP
@@ -69,6 +92,9 @@ The tool allows for easy testing of configuration values from shell scripts, her
.TP
.B nfsconf --file /etc/nfsmount.conf --get --arg /home MountPoint background
Show default value for \fIbackground\fR option for NFS mounts of the \fI/home\fR path.
+.TP
+.B nfsconf --file /etc/nfs.conf --set nfsd debug 1
+Enable debugging in nfsd
.SH FILES
.TP
.B /etc/nfs.conf
diff --git a/tools/nfsconf/nfsconfcli.c b/tools/nfsconf/nfsconfcli.c
index 034ec51..b5cb132 100644
--- a/tools/nfsconf/nfsconfcli.c
+++ b/tools/nfsconf/nfsconfcli.c
@@ -12,7 +12,9 @@ typedef enum {
MODE_NONE,
MODE_GET,
MODE_ISSET,
- MODE_DUMP
+ MODE_DUMP,
+ MODE_SET,
+ MODE_UNSET
} confmode_t;
static void usage(const char *name)
@@ -29,11 +31,14 @@ static void usage(const char *name)
fprintf(stderr, " Output one specific config value\n");
fprintf(stderr, " --isset [--arg subsection] {section} {tag}\n");
fprintf(stderr, " Return code indicates if config value is present\n");
+ fprintf(stderr, " --set [--arg subsection] {section} {tag} {value}\n");
+ fprintf(stderr, " Set and Write a config value\n");
+ fprintf(stderr, " --unset [--arg subsection] {section} {tag}\n");
+ fprintf(stderr, " Remove an existing config value\n");
}
int main(int argc, char **argv)
{
- const char * val;
char * confpath = NFS_CONFFILE;
char * arg = NULL;
int verbose=0;
@@ -47,6 +52,8 @@ int main(int argc, char **argv)
int index = 0;
struct option long_options[] = {
{"get", no_argument, 0, 'g' },
+ {"set", no_argument, 0, 's' },
+ {"unset", no_argument, 0, 'u' },
{"arg", required_argument, 0, 'a' },
{"isset", no_argument, 0, 'i' },
{"dump", optional_argument, 0, 'd' },
@@ -55,7 +62,7 @@ int main(int argc, char **argv)
{NULL, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "ga:id::f:v", long_options, &index);
+ c = getopt_long(argc, argv, "gsua:id::f:v", long_options, &index);
if (c == -1) break;
switch (c) {
@@ -75,6 +82,12 @@ int main(int argc, char **argv)
case 'g':
mode = MODE_GET;
break;
+ case 's':
+ mode = MODE_SET;
+ break;
+ case 'u':
+ mode = MODE_UNSET;
+ break;
case 'i':
mode = MODE_ISSET;
break;
@@ -105,14 +118,18 @@ int main(int argc, char **argv)
return 1;
}
- if (conf_init_file(confpath)) {
- /* config file was missing or had an error, warn about it */
- if (verbose || mode != MODE_ISSET)
- fprintf(stderr, "Error loading config file %s\n",
- confpath);
- /* this isnt fatal for --isset */
- if (mode != MODE_ISSET)
- return 1;
+ if (mode != MODE_SET && mode != MODE_UNSET) {
+ if (conf_init_file(confpath)) {
+ /* config file was missing or had an error, warn about it */
+ if (verbose || mode != MODE_ISSET) {
+ fprintf(stderr, "Error loading config file %s\n",
+ confpath);
+ }
+
+ /* this isnt fatal for --isset */
+ if (mode != MODE_ISSET)
+ return 1;
+ }
}
/* --dump mode, output the current configuration */
@@ -144,6 +161,7 @@ int main(int argc, char **argv)
if (mode == MODE_GET || mode == MODE_ISSET) {
char * section = NULL;
char * tag = NULL;
+ const char * val;
/* test they supplied section and tag names */
if (optind+1 >= argc) {
@@ -169,6 +187,41 @@ int main(int argc, char **argv)
fprintf(stderr, "Tag '%s' not found\n", tag);
ret = 1;
}
+ } else
+ if (mode == MODE_SET || mode == MODE_UNSET) {
+ char * section = NULL;
+ char * tag = NULL;
+ char * val = NULL;
+ int need = 2;
+
+ if (mode == MODE_UNSET)
+ need = 1;
+
+ /* test they supplied section and tag names */
+ if (optind+need >= argc) {
+ fprintf(stderr, "Error: insufficient arguments for mode\n");
+ usage(argv[0]);
+ ret = 2;
+ goto cleanup;
+ }
+
+ /* now we have a section and tag name */
+ section = argv[optind++];
+ tag = argv[optind++];
+ if (mode == MODE_SET)
+ val = argv[optind++];
+
+ /* setting an empty string is same as unsetting */
+ if (val!=NULL && *val == '\0') {
+ mode = MODE_UNSET;
+ val = NULL;
+ }
+
+ if (conf_write(confpath, section, arg, tag, val)) {
+ if (verbose)
+ fprintf(stderr, "Error writing config\n");
+ ret = 1;
+ }
} else {
fprintf(stderr, "Mode not yet implimented.\n");
ret = 2;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread