+#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY)
+/* exim_chown - in some NFSv4 setups *seemes* to be an issue with
+chown(<exim-uid>, <exim-gid>).
+
+Probably because the idmapping is broken, misconfigured or set up in
+an unusal way. (see Bug 2931). As I'm not sure, if this was a single
+case of misconfiguration, or if there are more such broken systems
+out, I try to impose as least impact as possible and for now just write
+a panic log entry pointing to the bug report. You're encouraged to
+contact the developers, if you experience this issue.
+
+fd the file descriptor (or -1 if not valid)
+name the file name for error messages or for file operations,
+ if fd is < 0
+owner the owner
+group the group
+
+returns 0 on success, -1 on failure */
+
+static inline int
+exim_fchown(int fd, uid_t owner, gid_t group, const uschar *name)
+{
+return fchown(fd, owner, group)
+ ? exim_chown_failure(fd, name, owner, group) : 0;
+}
+
+static inline int
+exim_chown(const uschar *name, uid_t owner, gid_t group)
+{
+return chown(CCS name, owner, group)
+ ? exim_chown_failure(-1, name, owner, group) : 0;
+}
+
+#endif /* !MACRO_PREDEF && !COMPILE_UTILITY */
+/******************************************************************************/
+/* String functions */
+
+#if !defined(MACRO_PREDEF)
+/*************************************************
+* Copy and save string *
+*************************************************/
+
+/* This function assumes that memcpy() is faster than strcpy().
+*/
+
+static inline uschar *
+string_copy_taint_trc(const uschar *s, BOOL tainted, const char * func, int line)
+{
+int len = Ustrlen(s) + 1;
+uschar *ss = store_get_3(len, tainted, func, line);
+memcpy(ss, s, len);
+return ss;
+}
+
+#define string_copy_taint(s, tainted) \
+ string_copy_taint_trc((s), tainted, __FUNCTION__, __LINE__)
+
+static inline uschar *
+string_copy(const uschar * s)
+{
+return string_copy_taint((s), is_tainted(s));
+}
+
+
+/*************************************************
+* Copy, lowercase and save string *
+*************************************************/
+
+/*
+Argument: string to copy
+Returns: copy of string in new store, with letters lowercased
+*/
+
+static inline uschar *
+string_copylc(const uschar *s)
+{
+uschar *ss = store_get(Ustrlen(s) + 1, is_tainted(s));
+uschar *p = ss;
+while (*s != 0) *p++ = tolower(*s++);
+*p = 0;
+return ss;
+}
+
+
+
+/*************************************************
+* Copy and save string, given length *
+*************************************************/
+
+/* It is assumed the data contains no zeros. A zero is added
+onto the end.
+
+Arguments:
+ s string to copy
+ n number of characters
+
+Returns: copy of string in new store
+
+This is an API for local_scan hence not static.
+*/
+
+static inline uschar *
+string_copyn(const uschar *s, int n)
+{
+uschar *ss = store_get(n + 1, is_tainted(s));
+Ustrncpy(ss, s, n);
+ss[n] = 0;
+return ss;
+}
+
+/*************************************************
+* Copy, lowercase, and save string, given length *
+*************************************************/
+
+/* It is assumed the data contains no zeros. A zero is added
+onto the end.
+
+Arguments:
+ s string to copy
+ n number of characters
+
+Returns: copy of string in new store, with letters lowercased
+*/
+
+static inline uschar *
+string_copynlc(uschar *s, int n)
+{
+uschar *ss = store_get(n + 1, is_tainted(s));
+uschar *p = ss;
+while (n-- > 0) *p++ = tolower(*s++);
+*p = 0;
+return ss;
+}
+
+
+# ifndef COMPILE_UTILITY
+/*************************************************
+* Copy and save string in longterm store *
+*************************************************/
+
+/* This function assumes that memcpy() is faster than strcpy().
+
+Argument: string to copy
+Returns: copy of string in new store
+*/
+
+static inline uschar *
+string_copy_perm(const uschar *s, BOOL force_taint)
+{
+int old_pool = store_pool;
+int len = Ustrlen(s) + 1;
+uschar *ss;
+
+store_pool = POOL_PERM;
+ss = store_get(len, force_taint || is_tainted(s));
+memcpy(ss, s, len);
+store_pool = old_pool;
+return ss;
+}
+# endif
+
+
+
+/* sprintf into a buffer, taint-unchecked */
+
+static inline void
+string_format_nt(uschar * buf, int siz, const char * fmt, ...)
+{
+gstring gs = { .size = siz, .ptr = 0, .s = buf };
+va_list ap;
+va_start(ap, fmt);
+(void) string_vformat(&gs, SVFMT_TAINT_NOCHK, fmt, ap);
+va_end(ap);
+}
+
+
+
+/******************************************************************************/
+/* Growable-string functions */
+
+/* Create a growable-string with some preassigned space */
+
+#define string_get_tainted(size, tainted) \
+ string_get_tainted_trc((size), (tainted), __FUNCTION__, __LINE__)
+
+static inline gstring *
+string_get_tainted_trc(unsigned size, BOOL tainted, const char * func, unsigned line)
+{
+gstring * g = store_get_3(sizeof(gstring) + size, tainted, func, line);
+g->size = size;
+g->ptr = 0;
+g->s = US(g + 1);
+return g;
+}
+
+#define string_get(size) \
+ string_get_trc((size), __FUNCTION__, __LINE__)
+
+static inline gstring *
+string_get_trc(unsigned size, const char * func, unsigned line)
+{
+return string_get_tainted_trc(size, FALSE, func, line);
+}
+
+/* NUL-terminate the C string in the growable-string, and return it. */
+
+static inline uschar *
+string_from_gstring(gstring * g)
+{
+if (!g) return NULL;
+g->s[g->ptr] = '\0';
+return g->s;
+}
+
+
+#define gstring_release_unused(g) \
+ gstring_release_unused_trc(g, __FUNCTION__, __LINE__)
+
+static inline void
+gstring_release_unused_trc(gstring * g, const char * file, unsigned line)
+{
+if (g) store_release_above_3(g->s + (g->size = g->ptr + 1), file, line);
+}
+
+
+/* sprintf-append to a growable-string */
+
+#define string_fmt_append(g, fmt, ...) \
+ string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \
+ SVFMT_EXTEND|SVFMT_REBUFFER, fmt, __VA_ARGS__)
+
+#define string_fmt_append_f(g, flgs, fmt, ...) \
+ string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \
+ flgs, fmt, __VA_ARGS__)
+
+static inline gstring *
+string_fmt_append_f_trc(gstring * g, const uschar * func, unsigned line,
+ unsigned flags, const char *format, ...)
+{
+va_list ap;
+va_start(ap, format);
+g = string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE,
+ flags, format, ap);
+va_end(ap);
+return g;
+}
+
+/******************************************************************************/
+
+#define store_get_dns_answer() store_get_dns_answer_trc(CUS __FUNCTION__, __LINE__)
+
+static inline dns_answer *
+store_get_dns_answer_trc(const uschar * func, unsigned line)
+{
+return store_get_3(sizeof(dns_answer), TRUE, CCS func, line); /* use tainted mem */
+}
+
+/******************************************************************************/
+/* Routines with knowledge of spool layout */
+
+# ifndef COMPILE_UTILITY
+static inline void
+spool_pname_buf(uschar * buf, int len)
+{
+snprintf(CS buf, len, "%s/%s/input", spool_directory, queue_name);
+}
+
+static inline uschar *
+spool_dname(const uschar * purpose, uschar * subdir)
+{
+return string_sprintf("%s/%s/%s/%s",
+ spool_directory, queue_name, purpose, subdir);
+}
+# endif
+
+static inline uschar *
+spool_q_sname(const uschar * purpose, const uschar * q, uschar * subdir)
+{
+return string_sprintf("%s%s%s%s%s",
+ q, *q ? "/" : "",
+ purpose,
+ *subdir ? "/" : "", subdir);
+}
+
+static inline uschar *
+spool_sname(const uschar * purpose, uschar * subdir)
+{
+return spool_q_sname(purpose, queue_name, subdir);
+}
+
+static inline uschar *
+spool_q_fname(const uschar * purpose, const uschar * q,
+ const uschar * subdir, const uschar * fname, const uschar * suffix)
+{
+return string_sprintf("%s/%s/%s/%s/%s%s",
+ spool_directory, q, purpose, subdir, fname, suffix);
+}
+
+static inline uschar *
+spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname,
+ const uschar * suffix)
+{
+return spool_q_fname(purpose, queue_name, subdir, fname, suffix);
+}
+
+static inline void
+set_subdir_str(uschar * subdir_str, const uschar * name,
+ int search_sequence)
+{
+subdir_str[0] = split_spool_directory == (search_sequence == 0)
+ ? name[5] : '\0';
+subdir_str[1] = '\0';
+}
+
+/******************************************************************************/
+static inline void
+timesince(struct timeval * diff, struct timeval * then)
+{
+gettimeofday(diff, NULL);
+diff->tv_sec -= then->tv_sec;
+if ((diff->tv_usec -= then->tv_usec) < 0)
+ {
+ diff->tv_sec--;
+ diff->tv_usec += 1000*1000;
+ }
+}
+
+static inline uschar *
+string_timediff(struct timeval * diff)
+{
+static uschar buf[sizeof("0.000s")];
+
+if (diff->tv_sec >= 5 || !LOGGING(millisec))
+ return readconf_printtime((int)diff->tv_sec);
+
+sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
+return buf;
+}
+
+
+static inline uschar *
+string_timesince(struct timeval * then)
+{
+struct timeval diff;
+timesince(&diff, then);
+return string_timediff(&diff);
+}
+
+static inline void
+report_time_since(struct timeval * t0, uschar * where)
+{
+# ifdef MEASURE_TIMING
+struct timeval diff;
+timesince(&diff, t0);
+fprintf(stderr, "%d %s:\t%ld.%06ld\n",
+ (uint)getpid(), where, (long)diff.tv_sec, (long)diff.tv_usec);
+# endif
+}
+
+
+static inline void
+testharness_pause_ms(int millisec)
+{
+#ifndef MEASURE_TIMING
+if (f.running_in_test_harness) millisleep(millisec);
+#endif
+}
+
+#endif /* !MACRO_PREDEF */
+
+#endif /* _FUNCTIONS_H_ */
+