1 /* sdb - MIT - Copyright 2019 - thestr4ng3r */
2 
3 #include "sdb.h"
4 
sdb_diff_format(char * str,int size,const SdbDiff * diff)5 SDB_API int sdb_diff_format(char *str, int size, const SdbDiff *diff) {
6 	int r = 0;
7 #define APPENDF(...) do { \
8 		int sr = snprintf (str, size, __VA_ARGS__); \
9 		if (sr < 0) { \
10 			return sr; \
11 		} \
12 		r += sr; \
13 		if (sr >= size) { \
14 			/* no space left, only measure from now on */ \
15 			str = NULL; \
16 			size = 0; \
17 		} else { \
18 			str += sr; \
19 			size -= sr; \
20 		} \
21 	} while(0)
22 
23 	APPENDF ("%c%s ", diff->add ? '+' : '-', diff->v ? "  " : "NS");
24 
25 	SdbListIter *it;
26 	const char *component;
27 	ls_foreach (diff->path, it, component) {
28 		APPENDF ("%s/", component);
29 	}
30 
31 	if (diff->v) {
32 		APPENDF ("%s=%s", diff->k, diff->v);
33 	} else {
34 		APPENDF ("%s", diff->k);
35 	}
36 
37 #undef APPENDF
38 	return r;
39 }
40 
41 typedef struct sdb_diff_ctx_t {
42 	Sdb *a;
43 	Sdb *b;
44 	bool equal;
45 	SdbList *path;
46 	SdbDiffCallback cb;
47 	void *cb_user;
48 } SdbDiffCtx;
49 
50 #define DIFF(ctx, c, ret) do { \
51 	(ctx)->equal = false; \
52 	if ((ctx)->cb) { \
53 		c \
54 	} else { \
55 		/* we already know it's not equal and don't care about the rest of the diff */ \
56 		return ret; \
57 	} \
58 } while(0)
59 
60 
sdb_diff_report_ns(SdbDiffCtx * ctx,SdbNs * ns,bool add)61 static void sdb_diff_report_ns(SdbDiffCtx *ctx, SdbNs *ns, bool add) {
62 	SdbDiff diff = { ctx->path, ns->name, NULL, add };
63 	ctx->cb (&diff, ctx->cb_user);
64 }
65 
sdb_diff_report_kv(SdbDiffCtx * ctx,const char * k,const char * v,bool add)66 static void sdb_diff_report_kv(SdbDiffCtx *ctx, const char *k, const char *v, bool add) {
67 	SdbDiff diff = { ctx->path, k, v, add };
68 	ctx->cb (&diff, ctx->cb_user);
69 }
70 
71 typedef struct sdb_diff_kv_cb_ctx {
72 	SdbDiffCtx *ctx;
73 	bool add;
74 } SdbDiffKVCbCtx;
75 
sdb_diff_report_kv_cb(void * user,const char * k,const char * v)76 static bool sdb_diff_report_kv_cb(void *user, const char *k, const char *v) {
77 	const SdbDiffKVCbCtx *ctx = user;
78 	sdb_diff_report_kv (ctx->ctx, k, v, ctx->add);
79 	return true;
80 }
81 
82 /**
83  * just report everything from sdb to buf with prefix
84  */
sdb_diff_report(SdbDiffCtx * ctx,Sdb * sdb,bool add)85 static void sdb_diff_report(SdbDiffCtx *ctx, Sdb *sdb, bool add) {
86 	SdbListIter *it;
87 	SdbNs *ns;
88 	ls_foreach (sdb->ns, it, ns) {
89 		sdb_diff_report_ns (ctx, ns, add);
90 		ls_push (ctx->path, ns->name);
91 		sdb_diff_report (ctx, ns->sdb, add);
92 		ls_pop (ctx->path);
93 	}
94 	SdbDiffKVCbCtx cb_ctx = { ctx, add };
95 	sdb_foreach (sdb, sdb_diff_report_kv_cb, &cb_ctx);
96 }
97 
sdb_diff_kv_cb(void * user,const char * k,const char * v)98 static bool sdb_diff_kv_cb(void *user, const char *k, const char *v) {
99 	const SdbDiffKVCbCtx *ctx = user;
100 	Sdb *other = ctx->add ? ctx->ctx->a : ctx->ctx->b;
101 	const char *other_val = sdb_const_get (other, k, NULL);
102 	if (!other_val || !*other_val) {
103 		DIFF (ctx->ctx,
104 			sdb_diff_report_kv (ctx->ctx, k, v, ctx->add);
105 		, false);
106 	} else if (!ctx->add && strcmp (v, other_val) != 0) {
107 		DIFF (ctx->ctx,
108 			sdb_diff_report_kv (ctx->ctx, k, v, false);
109 			sdb_diff_report_kv (ctx->ctx, k, other_val, true);
110 		, false);
111 	}
112 	return true;
113 }
114 
sdb_diff_ctx(SdbDiffCtx * ctx)115 static void sdb_diff_ctx(SdbDiffCtx *ctx) {
116 	SdbListIter *it;
117 	SdbNs *ns;
118 	ls_foreach (ctx->a->ns, it, ns) {
119 		Sdb *b_ns = sdb_ns (ctx->b, ns->name, false);
120 		if (!b_ns) {
121 			DIFF (ctx,
122 				sdb_diff_report_ns (ctx, ns, false);
123 				ls_push (ctx->path, ns->name);
124 				sdb_diff_report (ctx, ns->sdb, false);
125 				ls_pop (ctx->path);
126 			,);
127 			continue;
128 		}
129 		Sdb *a = ctx->a;
130 		Sdb *b = ctx->b;
131 		ctx->a = ns->sdb;
132 		ctx->b = b_ns;
133 		ls_push (ctx->path, ns->name);
134 		sdb_diff_ctx (ctx);
135 		ls_pop (ctx->path);
136 		ctx->a = a;
137 		ctx->b = b;
138 	}
139 	ls_foreach (ctx->b->ns, it, ns) {
140 		if (!sdb_ns (ctx->a, ns->name, false)) {
141 			DIFF (ctx,
142 				sdb_diff_report_ns (ctx, ns, true);
143 				ls_push (ctx->path, ns->name);
144 				sdb_diff_report (ctx, ns->sdb, true);
145 				ls_pop (ctx->path);
146 			,);
147 		}
148 	}
149 	SdbDiffKVCbCtx kv_ctx = { ctx, false };
150 	if (!sdb_foreach (ctx->a, sdb_diff_kv_cb, &kv_ctx)) {
151 		return;
152 	}
153 	kv_ctx.add = true;
154 	sdb_foreach (ctx->b, sdb_diff_kv_cb, &kv_ctx);
155 }
156 
sdb_diff(Sdb * a,Sdb * b,SdbDiffCallback cb,void * cb_user)157 SDB_API bool sdb_diff(Sdb *a, Sdb *b, SdbDiffCallback cb, void *cb_user) {
158 	SdbDiffCtx ctx;
159 	ctx.a = a;
160 	ctx.b = b;
161 	ctx.equal = true;
162 	ctx.cb = cb;
163 	ctx.cb_user = cb_user;
164 	ctx.path = ls_new ();
165 	if (!ctx.path) {
166 		return false;
167 	}
168 	sdb_diff_ctx (&ctx);
169 	ls_free (ctx.path);
170 	return ctx.equal;
171 }
172