summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-08-28 22:28:36 +0000
committerLennart Poettering <lennart@poettering.net>2003-08-28 22:28:36 +0000
commit38cea8764bd4302bfa6f16b086b04dfa25be344b (patch)
treef3bc9c133bddbd905f1d7f7221ea70a5f0eb3b91
parent98f36517251b5bccdb80d90d698c173c18ee4a29 (diff)
directory merge fixes
git-svn-id: file:///home/lennart/svn/public/syrep/trunk@10 07ea20a6-d2c5-0310-9e02-9ef735347d72
-rw-r--r--src/extract.c23
-rw-r--r--src/md5util.c104
-rw-r--r--src/merge.c129
-rw-r--r--src/syrep.c30
-rw-r--r--src/syrep.ggo2
-rw-r--r--src/syrep.h2
-rw-r--r--src/util.c158
-rw-r--r--src/util.h15
8 files changed, 391 insertions, 72 deletions
diff --git a/src/extract.c b/src/extract.c
index 616b8d1..5ba7869 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -12,22 +12,19 @@
#include "util.h"
static int cb(struct package *p, const char *name, const char *path, void *u) {
- struct stat st;
- uint32_t size;
-
- if (stat(path, &st) < 0) {
+ int r;
+
+ if ((r = access(path, R_OK)) < 0) {
if (errno == ENOENT)
- size = 0;
- else {
- fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno));
- return -1;
- }
- } else
- size = (uint32_t) st.st_size;
+ return 0;
+
+ fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno));
+ return -1;
+ }
- if (size) {
+ if (r == 0) {
fprintf(stderr, "Extracting %s ...\n", name);
- return copy_file(path, name);
+ return copy_or_link_file(path, name, 1);
}
return 0;
diff --git a/src/md5util.c b/src/md5util.c
index a49e5d5..fcbf440 100644
--- a/src/md5util.c
+++ b/src/md5util.c
@@ -1,20 +1,19 @@
/***
- This file is part of pam_dotfile.
+ This file is part of syrep.
- pam_dotfile is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
+ syrep 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.
- pam_dotfile 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.
+ syrep 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 pam_dotfile; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA
+ along with syrep; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***/
#include <unistd.h>
@@ -23,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <sys/stat.h>
#include "md5util.h"
#include "md5.h"
@@ -46,48 +46,74 @@ int fdmd5(int fd, size_t l, char *md) {
size_t m;
int r = 0;
md5_state_t s;
+ struct stat pre, post;
+ void *p = NULL;
md5_init(&s);
- m = l < MMAPSIZE ? l : MMAPSIZE;
+ if (fstat(fd, &pre) < 0) {
+ fprintf(stderr, "fstat(): %s\n", strerror(errno));
+ goto finish;
+ }
- while (l > 0 && ((d = mmap(NULL, m, PROT_READ, MAP_SHARED, fd, o)) != MAP_FAILED)) {
- md5_append(&s, d, m);
- munmap(d, m);
-
- o += m;
- l -= m;
+ if (l > BUFSIZE) {
+
m = l < MMAPSIZE ? l : MMAPSIZE;
-
+
+ while (l && ((d = mmap(NULL, m, PROT_READ, MAP_SHARED, fd, o)) != MAP_FAILED)) {
+ md5_append(&s, d, m);
+ munmap(d, m);
+
+ o += m;
+ l -= m;
+ m = l < MMAPSIZE ? l : MMAPSIZE;
+
+ }
+
+
+ if (l > 0)
+ fprintf(stderr, "mmap() failed: %s\n", strerror(errno));
}
if (l > 0) {
- void *p;
- fprintf(stderr, "mmap() failed: %s\n", strerror(errno));
-
- if (!(p = malloc(BUFSIZE)))
- r = -1;
- else {
+ if (!(p = malloc(BUFSIZE))) {
+ fprintf(stderr, "malloc(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ while (l) {
+ ssize_t r;
- for (;;) {
- ssize_t r;
-
- if ((r = read(fd, p, BUFSIZE)) < 0) {
- fprintf(stderr, "read(): %s\n", strerror(errno));
- free(p);
- return -1;
- }
-
- if (!r)
- break;
-
- md5_append(&s, p, r);
+ if ((r = read(fd, p, BUFSIZE)) < 0) {
+ fprintf(stderr, "read(): %s\n", strerror(errno));
+ goto finish;
}
- free(p);
+ if (!r)
+ break;
+
+ md5_append(&s, p, r);
+
+ l -= r;
}
}
-
+
+ if (fstat(fd, &post) < 0) {
+ fprintf(stderr, "fstat(): %s\n", strerror(errno));
+ goto finish;
+ }
+
+ if (pre.st_mtime != post.st_mtime) {
+ fprintf(stderr, "File modified while calculating digest.\n");
+ goto finish;
+ }
+
md5_finish(&s, md);
+
+finish:
+
+ if (p)
+ free(p);
+
return r;
}
diff --git a/src/merge.c b/src/merge.c
index 059523e..a0d9f8d 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -3,6 +3,10 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdlib.h>
#include "md5util.h"
#include "diff.h"
@@ -14,6 +18,7 @@ struct cb_info {
struct syrep_db_context *c1;
struct syrep_db_context *c2;
const char *root;
+ char trash_dir[PATH_MAX+1];
};
static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) {
@@ -54,6 +59,29 @@ static int conflict_phase(DB *ddb, struct syrep_name *name, struct diff_entry *d
return 0;
}
+static char *escape_path(const char *path, char *dst, unsigned l) {
+ const char *p;
+ char *d;
+
+ for (p = path, d = dst; *p && d-dst < l-1; p++) {
+ if (*p == '/') {
+ *(d++) = '%';
+ *(d++) = '2';
+ *(d++) = 'F';
+
+ } else if (*p == '%') {
+ *(d++) = '%';
+ *(d++) = '2';
+ *(d++) = '5';
+
+ } else
+ *(d++) = *p;
+ }
+
+ *(d++) = 0;
+ return dst;
+}
+
static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) {
struct cb_info *cb_info = p;
struct syrep_name name2;
@@ -89,21 +117,42 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v
if (f) {
char path2[PATH_MAX+1];
snprintf(path2, sizeof(path2), "%s/%s", cb_info->root, name2.path);
- fprintf(stderr, "COPY: Linking existing file <%s> to <%s>.\n", path2, path);
+ fprintf(stderr, "COPY: Linking existing file <%s> to <%s>.\n", name2.path, name->path);
- if (copy_or_link_file(path2, path) < 0)
+ if (makeprefixpath(path, 0777) < 0)
return -1;
+
+ if (!access(path2, R_OK)) {
+ if (copy_or_link_file(path2, path, 0) < 0)
+ return -1;
+ } else {
+ unsigned l;
+
+ snprintf(path2, sizeof(path2), "%s/", cb_info->trash_dir);
+ l = strlen(path2);
+ escape_path(name2.path, path2+l, sizeof(path2)-l);
+
+ if (!access(path2, R_OK)) {
+ if (copy_or_link_file(path2, path, 0) < 0)
+ return -1;
+ }
+
+ fprintf(stderr, "COPY: Local file <%s> vanished. Snapshot not up to date.\n", name2.path);
+ }
} else {
const char* a;
if ((a = package_get_item(cb_info->c1->package, d, 0))) {
- fprintf(stderr, "COPY: Copying file <%s> from patch.\n", path);
+ fprintf(stderr, "COPY: Copying file <%s> from patch.\n", name->path);
- if (copy_or_link_file(a, path) < 0)
+ if (makeprefixpath(path, 0777) < 0)
+ return -1;
+
+ if (copy_or_link_file(a, path, 0) < 0)
return -1;
} else
- fprintf(stderr, "COPY: File <%s> is missing.\n", path);
+ fprintf(stderr, "COPY: File <%s> is missing.\n", name->path);
}
return 0;
@@ -111,7 +160,8 @@ static int copy_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, v
static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de, void *p) {
struct cb_info *cb_info = p;
- char path[PATH_MAX+1];
+ char path[PATH_MAX+1], target[PATH_MAX+1];
+ unsigned l;
assert(ddb && name && de && p);
@@ -123,14 +173,61 @@ static int delete_phase(DB *ddb, struct syrep_name *name, struct diff_entry *de,
snprintf(path, sizeof(path), "%s/%s", cb_info->root, name->path);
- fprintf(stderr, "DELETE: Deleting file <%s>\n", path);
+ snprintf(target, sizeof(target), "%s/", cb_info->trash_dir);
+ l = strlen(target);
+ escape_path(name->path, target+l, sizeof(target)-l);
+
+ fprintf(stderr, "DELETE: Moving file <%s> into trash (%s)\n", path, target);
- if (unlink(path) < 0) {
- fprintf(stderr, "unlink(%s): %s\n", path, strerror(errno));
+ if (move_file(path, target, 1) < 0)
return -1;
+
+ if (args.prune_empty_flag)
+ if (prune_empty_directories(path, cb_info->root) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int empty_trash(const char *trash) {
+ DIR *dir = NULL;
+ int r = -1;
+ struct dirent *de;
+
+ if (!(dir = opendir(trash))) {
+ fprintf(stderr, "opendir(\"%s\"): %s", trash, strerror(errno));
+ goto finish;
+ }
+
+ while ((de = readdir(dir))) {
+ char path[PATH_MAX];
+
+ if (!strcmp(de->d_name, "."))
+ continue;
+
+ if (!strcmp(de->d_name, ".."))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", trash, de->d_name);
+
+ if (unlink(path) < 0) {
+ fprintf(stderr, "unlink(\"%s\"): %s\n", path, strerror(errno));
+ goto finish;
+ }
+ }
+
+ if (rmdir(trash) < 0) {
+ fprintf(stderr, "rmdir(\"%s\"): %s\n", trash, strerror(errno));
+ goto finish;
}
- return 0;
+ r = 0;
+
+finish:
+ if (dir)
+ closedir(dir);
+
+ return r;
}
/* Merges c1 into c2 in directory "root" */
@@ -143,6 +240,9 @@ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char*
cb_info.c1 = c1;
cb_info.c2 = c2;
cb_info.root = root;
+
+ snprintf(cb_info.trash_dir, sizeof(cb_info.trash_dir), "%s/.syrep-trash", root);
+ mkdir(cb_info.trash_dir, 0777);
if (!(ddb = make_diff(c1, c2)))
goto finish;
@@ -150,17 +250,22 @@ int merge(struct syrep_db_context *c1, struct syrep_db_context *c2, const char*
if (diff_foreach(ddb, conflict_phase, &cb_info) < 0)
goto finish;
- if (diff_foreach(ddb, copy_phase, &cb_info) < 0)
+ if (diff_foreach(ddb, delete_phase, &cb_info) < 0)
goto finish;
- if (diff_foreach(ddb, delete_phase, &cb_info) < 0)
+ if (diff_foreach(ddb, copy_phase, &cb_info) < 0)
goto finish;
+ if (empty_trash(cb_info.trash_dir) < 0)
+ goto finish;
+
r = 0;
finish:
if (ddb)
ddb->close(ddb, 0);
+
+ rmdir(cb_info.trash_dir);
return r;
}
diff --git a/src/syrep.c b/src/syrep.c
index ad7619a..e68e2f1 100644
--- a/src/syrep.c
+++ b/src/syrep.c
@@ -42,6 +42,12 @@ static int do_diff(void) {
goto finish;
}
+ if (args.local_temp_flag && isdirectory(args.inputs[0]) >= 1) {
+ const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR);
+ mkdir(p, 0777);
+ setenv("TMPDIR", p, 1);
+ }
+
if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME))))
goto finish;
@@ -100,6 +106,12 @@ static int do_merge(void) {
fprintf(stderr, "ERROR: %s is not a directory\n", args.inputs[1]);
goto finish;
}
+
+ if (args.local_temp_flag) {
+ const char *p = get_attached_filename(args.inputs[1], SYREP_TEMPDIR);
+ mkdir(p, 0777);
+ setenv("TMPDIR", p, 1);
+ }
if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME))))
goto finish;
@@ -154,6 +166,12 @@ static int do_makepatch(void) {
goto finish;
}
+ if (args.local_temp_flag) {
+ const char *p = get_attached_filename(args.inputs[0], SYREP_TEMPDIR);
+ mkdir(p, 0777);
+ setenv("TMPDIR", p, 1);
+ }
+
if (!(path1 = strdup(get_attached_filename(args.inputs[0], SYREP_SNAPSHOTFILENAME))))
goto finish;
@@ -209,6 +227,12 @@ static int do_foreach(int (*func) (struct syrep_db_context *c), int m) {
for (i = 0; i < args.inputs_num; i++) {
static char saved_cwd[PATH_MAX];
+ if (args.local_temp_flag && isdirectory(args.inputs[i]) >= 1) {
+ const char *p = get_attached_filename(args.inputs[i], SYREP_TEMPDIR);
+ mkdir(p, 0777);
+ setenv("TMPDIR", p, 1);
+ }
+
if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME))))
goto finish;
@@ -288,6 +312,12 @@ static int do_update(void) {
return 1;
}
+ if (args.local_temp_flag) {
+ const char *p = get_attached_filename(args.inputs[i], SYREP_TEMPDIR);
+ mkdir(p, 0777);
+ setenv("TMPDIR", p, 1);
+ }
+
if (!(path = strdup(get_attached_filename(args.inputs[i], SYREP_SNAPSHOTFILENAME))))
goto finish;
diff --git a/src/syrep.ggo b/src/syrep.ggo
index 91f9b2d..970cc82 100644
--- a/src/syrep.ggo
+++ b/src/syrep.ggo
@@ -3,6 +3,7 @@ package "syrep"
version "0.1"
option "verbose" v "Enable verbose operation" flag off
+option "local-temp" T "Use temporary directory inside repository" flag off
option "list" - "List a repository snapshot" flag off
option "show-deleted" - "list: Show deleted entries of repository snapshot" flag off
@@ -25,6 +26,7 @@ option "diff" - "Show difference between two repositories or snapshots" flag off
option "merge" - "Merge a snapshot or a repository into a repository" flag off
option "question" q "merge: Ask a question before each action" flag off
+ option "prune-empty" - "merge: Prune empty directories" flag off
option "makepatch" - "Make a patch against the specified repository" flag off
option "output-file" o "makepatch: Write output to specified file instead of STDOUT" string no
diff --git a/src/syrep.h b/src/syrep.h
index 66d0a4d..2011c8a 100644
--- a/src/syrep.h
+++ b/src/syrep.h
@@ -5,6 +5,8 @@
#define SYREP_SNAPSHOTFILENAME "current.syrep"
#define SYREP_MDCACHEFILENAME "md-cache"
+#define SYREP_TEMPDIR "tmp"
+#define SYREP_TRASHDIR "trash"
#define SYREP_DIGESTLENGTH 16
diff --git a/src/util.c b/src/util.c
index ed93e6b..56be93c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,3 +1,5 @@
+#define USE_SENDFILE
+
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
@@ -9,6 +11,10 @@
#include <sys/mman.h>
#include <fcntl.h>
+#ifdef USE_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
#include "util.h"
void statistics(DB *db) {
@@ -118,6 +124,10 @@ int copy_fd(int sfd, int dfd, uint32_t l) {
void *buf = NULL;
int r = -1;
struct stat st;
+#ifdef USE_SENDFILE
+ ssize_t k;
+ off_t sfo;
+#endif
if ((src_o = lseek(sfd, 0, SEEK_CUR)) == (off_t) -1) {
fprintf(stderr, "lseek(): %s\n", strerror(errno));
@@ -148,6 +158,25 @@ int copy_fd(int sfd, int dfd, uint32_t l) {
goto finish;
}
+
+#ifdef USE_SENDFILE
+
+ sfo = src_o;
+ if ((k = sendfile(dfd, sfd, &sfo, l)) > 0) {
+
+ l -= k;
+ dst_o += k;
+ src_o = sfo;
+
+ if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) {
+ fprintf(stderr, "lseek(): %s\n", strerror(errno));
+ goto finish;
+ }
+ } else if (k < 0)
+ fprintf(stderr, "sendfile() failed for file copy, trying mmap(): %s\n", strerror(errno));
+
+#endif
+
if (l > BUFSIZE) {
uint32_t m = l < MMAPSIZE ? l : MMAPSIZE;
@@ -182,6 +211,9 @@ int copy_fd(int sfd, int dfd, uint32_t l) {
fprintf(stderr, "lseek(): %s\n", strerror(errno));
goto finish;
}
+
+ if (l > 0)
+ fprintf(stderr, "mmap() failed for file copy, trying read()/write(): %s\n", strerror(errno));
}
if (l > 0) {
@@ -213,16 +245,16 @@ finish:
return r;
}
-int copy_file(const char *src, const char *dst) {
+int copy_file(const char *src, const char *dst, int c) {
int sfd = -1, dfd = -1, r = -1;
if ((sfd = open(src, O_RDONLY)) < 0) {
- fprintf(stderr, "open(%s, O_RDONLY): %s\n", src, strerror(errno));
+ fprintf(stderr, "open(\"%s\", O_RDONLY): %s\n", src, strerror(errno));
goto finish;
}
- if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) {
- fprintf(stderr, "open(%s, O_RDWR|O_TRUNC|O_CREAT): %s\n", dst, strerror(errno));
+ if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT|( c ? 0 : O_EXCL), 0666)) < 0) {
+ fprintf(stderr, "open(\"%s\", O_RDWR|O_TRUNC|O_CREAT%s): %s\n", dst, c ? "" : "|O_EXCL", strerror(errno));
goto finish;
}
@@ -242,12 +274,15 @@ finish:
return r;
}
-int copy_or_link_file(const char *src, const char *dst) {
+int copy_or_link_file(const char *src, const char *dst, int c) {
+ if (c)
+ unlink(dst);
+
if (link(src, dst) < 0) {
if (errno == EXDEV || errno == EPERM)
- return copy_file(src, dst);
+ return copy_file(src, dst, c);
fprintf(stderr, "link(%s, %s): %s\n", src, dst, strerror(errno));
return -1;
@@ -255,3 +290,114 @@ int copy_or_link_file(const char *src, const char *dst) {
return 0;
}
+
+int move_file(const char *src, const char *dst, int c) {
+ int r;
+
+ if ((r = copy_or_link_file(src, dst, c)) < 0)
+ return -1;
+
+ if (unlink(src) < 0) {
+ fprintf(stderr, "unlink(%s): %s\n", src, strerror(errno));
+ unlink(dst);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int prune_empty_directories(const char *path, const char *root) {
+ char rroot[PATH_MAX],
+ rpath[PATH_MAX];
+
+ strncpy(rroot, root, PATH_MAX);
+ rroot[PATH_MAX-1] = 0;
+ normalize_path(rroot);
+
+ strncpy(rpath, path, PATH_MAX);
+ rpath[PATH_MAX-1] = 0;
+ normalize_path(rpath);
+
+ for (;;) {
+ char *e;
+
+ if (!rpath[0])
+ break;
+
+ if (!strcmp(rpath, "/"))
+ break;
+
+ if (!strcmp(rpath, rroot))
+ break;
+
+ if (rmdir(rpath) < 0) {
+
+ if (errno == ENOTEMPTY)
+ break;
+
+ if (errno != ENOENT) {
+ fprintf(stderr, "rmdir(\"%s\"): %s\n", rpath, strerror(errno));
+ return -1;
+ }
+ }
+
+ if (!(e = strrchr(rpath, '/')))
+ break;
+
+ *e = 0;
+
+ }
+
+ return 0;
+}
+
+int mkdir_p(const char *path, mode_t m) {
+ char tmp[PATH_MAX];
+ char *e, *b;
+ int quit = 0;
+
+ strncpy(tmp, path, PATH_MAX);
+ tmp[PATH_MAX-1] = 0;
+
+ normalize_path(tmp);
+
+ for (b = tmp, quit = 0; !quit;) {
+
+ if (!(e = strchr(b, '/'))) {
+ e = strchr(b, 0);
+ quit = 1;
+ }
+
+ *e = 0;
+ if (mkdir(tmp, m) < 0) {
+ if (errno != EEXIST) {
+ fprintf(stderr, "mkdir(\"%s\"): %s\n", tmp, strerror(errno));
+ return -1;
+ }
+ }
+ *e = '/';
+
+ b = e+1;
+ }
+
+ return 0;
+}
+
+/* Create all leading directories in path */
+int makeprefixpath(const char *path, mode_t m) {
+ char tmp[PATH_MAX], *e;
+
+ strncpy(tmp, path, PATH_MAX);
+ tmp[PATH_MAX-1] = 0;
+
+ normalize_path(tmp);
+
+ if (!(e = strrchr(tmp, '/')))
+ return 0;
+
+ *e = 0;
+
+ return mkdir_p(tmp, m);
+}
+
diff --git a/src/util.h b/src/util.h
index 2460ab9..23004f0 100644
--- a/src/util.h
+++ b/src/util.h
@@ -10,8 +10,19 @@ void rotdash(void);
const char* get_attached_filename(const char *path, const char *fn);
int isdirectory(const char *path);
int copy_fd(int sfd, int dfd, uint32_t l);
-int copy_file(const char *src, const char *dst);
-int copy_or_link_file(const char *src, const char *dst);
+int copy_file(const char *src, const char *dst, int c);
+int copy_or_link_file(const char *src, const char *dst, int c);
+
+/* Remove all directories between path and root if they are empty. */
+int prune_empty_directories(const char *path, const char *root);
+
+/* Same as /bin/mkdir -p in the shell */
+int mkdir_p(const char *path, mode_t m);
+
+/* Create all leading directories in path */
+int makeprefixpath(const char *path, mode_t m);
+
+int move_file(const char *src, const char *dst, int c);
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))