do not edit — generated by btf.
git.druid.rocksindexdruid520mpsrc/conf.c

src/conf.c


#define _POSIX_C_SOURCE 200809L
#define _NETBSD_SOURCE 1
 
#include <string.h>
 
#include "mpx.h"
 
long
parseconf(const char* buf, long n, struct kv* kv, long max)
{
	long i = 0;
	long nkv = 0;
	long ki;
	long vi;
 
	while(i < n && nkv < max)
	{
		ki = 0;
		while(i < n && buf[i] != '=' && buf[i] != '\n' && ki < MAXKEY - 1)
		{
			kv[nkv].key[ki] = buf[i];
			ki = ki + 1;
			i = i + 1;
		}
		kv[nkv].key[ki] = '\0';
		while(i < n && buf[i] != '\n' && buf[i] != '"')
		{
			i = i + 1;
		}
		if(i < n && buf[i] == '"')
		{
			i = i + 1;
			vi = 0;
			while(i < n && buf[i] != '"' && buf[i] != '\n' && vi < MAXVAL - 1)
			{
				kv[nkv].val[vi] = buf[i];
				vi = vi + 1;
				i = i + 1;
			}
			kv[nkv].val[vi] = '\0';
			if(kv[nkv].key[0] != '\0')
			{
				nkv = nkv + 1;
			}
		}
		while(i < n && buf[i] != '\n')
		{
			i = i + 1;
		}
		if(i < n)
		{
			i = i + 1;
		}
	}
	return nkv;
}
 
/* one level of "IMPORT=<path1> <path2> ..." expansion for mpx.conf (a
 * global-scope, unindented directive only -- not recursive: an imported
 * file's own IMPORT= line, if any, is inert). scans buf for such lines
 * and, into out, places buf's own content FIRST, then each named file's
 * raw content after it, later-listed files before earlier-listed ones
 * -- extractsection/kvget's first-match-wins semantics mean whichever
 * occurrence of a key is scanned first wins, so this ordering makes
 * buf's own content always win over anything it imports, and among the
 * imports themselves gives a later IMPORT= entry precedence over an
 * earlier one (mirrors mp.conf's own OVERLAYS= "later wins" rule). a
 * named file that can't be opened is
 * silently skipped (an optional import, same tolerance mpx.conf's own
 * possible absence already gets) -- but one that opens and is simply
 * too big to fit is NOT silently truncated into a garbled partial read;
 * it's skipped just as if it were missing, since a truncated import
 * would otherwise silently drop whatever keys fall past the cut, with
 * no way for the caller to tell truncation from "this file just wasn't
 * imported". returns the combined length written to out, or 0 if there
 * were no IMPORT= lines at all (the caller then just uses buf as-is). */
#define MAXIMPORTS 16
long
expand_imports(const char* buf, long n, char* out, long outmax)
{
	char imports[MAXIMPORTS][MAXPATH];
	long nimports = 0;
	long i = 0;
	size_t outi;
	long k;
 
	while(i < n && nimports < MAXIMPORTS)
	{
		long linestart = i;
		long lineend = i;
 
		while(lineend < n && buf[lineend] != '\n')
		{
			lineend = lineend + 1;
		}
		if(linestart < lineend && buf[linestart] != ' ' && buf[linestart] != '\t'
		    && lineend - linestart > 7 && strncmp(buf + linestart, "IMPORT=", 7) == 0)
		{
			long j = linestart + 7;
 
			while(j < lineend && nimports < MAXIMPORTS)
			{
				long tokstart;
				long toklen;
 
				while(j < lineend && buf[j] == ' ')
				{
					j = j + 1;
				}
				tokstart = j;
				while(j < lineend && buf[j] != ' ')
				{
					j = j + 1;
				}
				toklen = j - tokstart;
				if(toklen > 0 && toklen < MAXPATH)
				{
					memcpy(imports[nimports], buf + tokstart, (size_t)toklen);
					imports[nimports][toklen] = '\0';
					nimports = nimports + 1;
				}
			}
		}
		i = lineend + 1;
	}
	if(nimports == 0)
	{
		return 0;
	}
 
	/* buf's own content goes FIRST, not last: extractsection/kvget's
	 * first-match-wins picks whichever occurrence of a key it scans
	 * first, so buf must be scanned before anything it imports in order
	 * to actually win over it, matching this function's own documented
	 * "buf's own content always wins" contract. */
	outi = 0;
	if(n < outmax)
	{
		memcpy(out, buf, (size_t)n);
		outi = (size_t)n;
		if(outi + 1 < (size_t)outmax)
		{
			out[outi] = '\n';
			outi = outi + 1;
		}
	}
	for(k = 0; k < nimports; k = k + 1)
	{
		long idx = nimports - 1 - k;
		long subn = readfile(imports[idx], out + outi, outmax - (long)outi - 1);
 
		if(subn < 0)
		{
			continue;
		}
		outi = outi + (size_t)subn;
		if(outi + 1 < (size_t)outmax)
		{
			out[outi] = '\n';
			outi = outi + 1;
		}
	}
	return (long)outi;
}
 
const char*
kvget(struct kv* kv, long nkv, const char* key)
{
	long i = 0;
 
	while(i < nkv)
	{
		if(strcmp(kv[i].key, key) == 0)
		{
			return kv[i].val;
		}
		i = i + 1;
	}
	return NULL;
}
 
/* filters a raw mpx.conf-format buffer down to a plain key="value" buffer
 * parseconf() can consume: every unindented key="value" line (global,
 * unchanged), plus -- if pkgname is non-NULL -- every indented key="value"
 * line belonging to pkgname's "<name>:" section, de-indented and placed
 * first so parseconf/kvget (which returns the first match) prefers a
 * per-package override over the same global key. mirrors mp.conf's
 * per-package sections: an unindented "<name>:" line (name may itself
 * contain ':', e.g. a slotted "gcc:12:", so the section delimiter is the
 * last colon on the line, not the first) opens/switches the current
 * section; an unindented key="value" line closes any open section.
 *
 * pkgname's own section wins, but falls back to pkgname's BARE name's
 * section (pkgname with everything from the last ':' on stripped) before
 * falling back to the plain global lines -- mirrors
 * mplib::util::pconf_lookup on the perl side: a pkg_slot_use-composed
 * pkgname (e.g. "combodep:foo") doesn't exist yet at the moment a
 * per-package mpx.conf section is written, so the bare name is the only
 * one that could plausibly have been used. a no-op when pkgname is
 * already bare (no ':' in it).
 *
 * returns the number of bytes written to out. */
long
extractsection(const char* buf, long n, const char* pkgname, char* out, long outmax)
{
	long i;
	size_t outi;
	long pass;
	char cursec[MAXKEY];
	char barename[MAXKEY];
	const char* basename;
	long lastcolon;
	long k;
 
	basename = NULL;
	if(pkgname != NULL)
	{
		lastcolon = -1;
		for(k = 0; pkgname[k] != '\0'; k = k + 1)
		{
			if(pkgname[k] == ':')
			{
				lastcolon = k;
			}
		}
		if(lastcolon > 0 && lastcolon < MAXKEY)
		{
			memcpy(barename, pkgname, (size_t)lastcolon);
			barename[lastcolon] = '\0';
			basename = barename;
		}
	}
 
	outi = 0;
	for(pass = 0; pass < 3; pass = pass + 1)
	{
		/* pass 0: pkgname's own indented section (skipped if pkgname is
		 * NULL). pass 1: pkgname's bare name's section (skipped if
		 * pkgname is already bare). pass 2: unindented global lines,
		 * appended after both. */
		const char* matchname = (pass == 0) ? pkgname : (pass == 1) ? basename : NULL;
 
		if(pass != 2 && matchname == NULL)
		{
			continue;
		}
		cursec[0] = '\0';
		i = 0;
		while(i < n)
		{
			long linestart = i;
			long lineend = i;
			int indented;
 
			while(lineend < n && buf[lineend] != '\n')
			{
				lineend = lineend + 1;
			}
			indented = (linestart < lineend && (buf[linestart] == ' ' || buf[linestart] == '\t')) ? 1 : 0;
 
			if(!indented)
			{
				/* look for a "<name>:" header: last non-whitespace char
				 * on the line is ':', and there's at least one char
				 * before it once trailing whitespace is trimmed. */
				long lasttext = -1;
				{
					long j;
 
					for(j = linestart; j < lineend; j = j + 1)
					{
						if(buf[j] != ' ' && buf[j] != '\t' && buf[j] != '\r')
						{
							lasttext = j;
						}
					}
				}
				if(lasttext >= linestart && buf[lasttext] == ':')
				{
					long namelen = lasttext - linestart;
 
					if(namelen > 0 && namelen < MAXKEY)
					{
						memcpy(cursec, buf + linestart, (size_t)namelen);
						cursec[namelen] = '\0';
					}
					else
					{
						cursec[0] = '\0';
					}
					i = lineend + 1;
					continue;
				}
				/* not a header: unindented key=value line closes any
				 * section, and (pass 2 only) gets copied to out. */
				cursec[0] = '\0';
				if(pass == 2 && lineend > linestart)
				{
					long cpy = lineend - linestart;
					if(outi + (size_t)cpy + 1 < (size_t)outmax)
					{
						memcpy(out + outi, buf + linestart, (size_t)cpy);
						outi = outi + (size_t)cpy;
						out[outi] = '\n';
						outi = outi + 1;
					}
				}
			}
			else if(pass != 2 && cursec[0] != '\0' && strcmp(cursec, matchname) == 0)
			{
				long j = linestart;
 
				while(j < lineend && (buf[j] == ' ' || buf[j] == '\t'))
				{
					j = j + 1;
				}
				if(j < lineend)
				{
					long cpy = lineend - j;
					if(outi + (size_t)cpy + 1 < (size_t)outmax)
					{
						memcpy(out + outi, buf + j, (size_t)cpy);
						outi = outi + (size_t)cpy;
						out[outi] = '\n';
						outi = outi + 1;
					}
				}
			}
			i = lineend + 1;
		}
	}
	return (long)outi;
}
powered by btf.