Browse Source

Functions to work with null delimited lists.

copy_double_null() duplicates a null delimited list.

append_to_double_null() adds an entry to a list, or replaces an entry
which matches a given substring of the new value.

remove_from_double_null() removes an entry from a list, possibly only if
it matches a given substring.
Iain Patterson 5 years ago
parent
commit
ac8cdc8a64
2 changed files with 158 additions and 5 deletions
  1. 155 5
      registry.cpp
  2. 3 0
      registry.h

+ 155 - 5
registry.cpp

@@ -260,8 +260,11 @@ int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, un
     return 2;
   }
 
-  /* Probably not possible */
-  if (! envsize) return 0;
+  /* Minimum usable environment would be A= NULL NULL. */
+  if (envsize < 4 * sizeof(TCHAR)) {
+    *env = 0;
+    return 3;
+  }
 
   /* Previously initialised? */
   if (*env) HeapFree(GetProcessHeap(), 0, *env);
@@ -269,7 +272,7 @@ int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, un
   *env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, envsize);
   if (! *env) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
-    return 3;
+    return 4;
   }
 
   /* Actually get the strings. */
@@ -278,11 +281,11 @@ int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, un
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(ret), 0);
     HeapFree(GetProcessHeap(), 0, *env);
     *env = 0;
-    return 4;
+    return 5;
   }
 
   /* Value retrieved by RegQueryValueEx() is SIZE not COUNT. */
-  *envlen = (unsigned long) environment_length(env);
+  *envlen = (unsigned long) environment_length(*env);
 
   return 0;
 }
@@ -497,6 +500,153 @@ int unformat_double_null(TCHAR *formatted, unsigned long formattedlen, TCHAR **d
   return 0;
 }
 
+/* Copy a block. */
+int copy_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn) {
+  if (! newdn) return 1;
+
+  *newdn = 0;
+  if (! dn) return 0;
+
+  *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, dnlen * sizeof(TCHAR));
+  if (! *newdn) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("dn"), _T("copy_double_null()"), 0);
+    return 2;
+  }
+
+  memmove(*newdn, dn, dnlen * sizeof(TCHAR));
+  return 0;
+}
+
+/*
+  Create a new block with all the strings of the first block plus a new string.
+  The new string may be specified as <key> <delimiter> <value> and the keylen
+  gives the offset into the string to compare against existing entries.
+  If the key is already present its value will be overwritten in place.
+  If the key is blank or empty the new block will still be allocated and have
+  non-zero length.
+*/
+int append_to_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *append, size_t keylen, bool case_sensitive) {
+  if (! append || ! append[0]) return copy_double_null(dn, dnlen, newdn);
+  size_t appendlen = _tcslen(append);
+  int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
+
+  /* Identify the key, if any, or treat the whole string as the key. */
+  TCHAR *key = 0;
+  if (! keylen || keylen > appendlen) keylen = appendlen;
+  key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
+  if (! key) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("append_to_double_null()"), 0);
+    return 1;
+  }
+  memmove(key, append, keylen * sizeof(TCHAR));
+  key[keylen] = _T('\0');
+
+  /* Find the length of the block not including any existing key. */
+  size_t len = 0;
+  TCHAR *s;
+  for (s = dn; *s; s++) {
+    if (fn(s, key, keylen)) len += _tcslen(s) + 1;
+    for ( ; *s; s++);
+  }
+
+  /* Account for new entry. */
+  len += _tcslen(append) + 1;
+
+  /* Account for trailing NULL. */
+  len++;
+
+  /* Allocate a new block. */
+  *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
+  if (! *newdn) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("append_to_double_null()"), 0);
+    HeapFree(GetProcessHeap(), 0, key);
+    return 2;
+  }
+
+  /* Copy existing entries.*/
+  *newlen = (unsigned long) len;
+  TCHAR *t = *newdn;
+  TCHAR *u;
+  bool replaced = false;
+  for (s = dn; *s; s++) {
+    if (fn(s, key, keylen)) u = s;
+    else {
+      u = append;
+      replaced = true;
+    }
+    len = _tcslen(u) + 1;
+    memmove(t, u, len * sizeof(TCHAR));
+    t += len;
+    for ( ; *s; s++);
+  }
+
+  /* Add the entry if it wasn't already replaced.  The buffer was zeroed. */
+  if (! replaced) memmove(t, append, _tcslen(append) * sizeof(TCHAR));
+
+  HeapFree(GetProcessHeap(), 0, key);
+  return 0;
+}
+
+/*
+  Create a new block with all the string of the first block minus the given
+  string.
+  The keylen parameter gives the offset into the string to compare against
+  existing entries.  If a substring of existing value matches the string to
+  the given length it will be removed.
+  If the last entry is removed the new block will still be allocated and
+  have non-zero length.
+*/
+int remove_from_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **newdn, unsigned long *newlen, TCHAR *remove, size_t keylen, bool case_sensitive) {
+  if (! remove || !remove[0]) return copy_double_null(dn, dnlen, newdn);
+  size_t removelen = _tcslen(remove);
+  int (*fn)(const TCHAR *, const TCHAR *, size_t) = (case_sensitive) ? _tcsncmp : _tcsnicmp;
+
+  /* Identify the key, if any, or treat the whole string as the key. */
+  TCHAR *key = 0;
+  if (! keylen || keylen > removelen) keylen = removelen;
+  key = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (keylen + 1) * sizeof(TCHAR));
+  if (! key) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("key"), _T("remove_from_double_null()"), 0);
+    return 1;
+  }
+  memmove(key, remove, keylen * sizeof(TCHAR));
+  key[keylen] = _T('\0');
+
+  /* Find the length of the block not including any existing key. */
+  size_t len = 0;
+  TCHAR *s;
+  for (s = dn; *s; s++) {
+    if (fn(s, key, keylen)) len += _tcslen(s) + 1;
+    for ( ; *s; s++);
+  }
+
+  /* Account for trailing NULL. */
+  if (++len < 2) len = 2;
+
+  /* Allocate a new block. */
+  *newdn = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
+  if (! *newdn) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("newdn"), _T("remove_from_double_null()"), 0);
+    HeapFree(GetProcessHeap(), 0, key);
+    return 2;
+  }
+
+  /* Copy existing entries.*/
+  *newlen = (unsigned long) len;
+  TCHAR *t = *newdn;
+  for (s = dn; *s; s++) {
+    if (fn(s, key, keylen)) {
+      len = _tcslen(s) + 1;
+      memmove(t, s, len * sizeof(TCHAR));
+      t += len;
+    }
+    for ( ; *s; s++);
+  }
+
+  HeapFree(GetProcessHeap(), 0, key);
+  return 0;
+}
+
 void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
   unsigned long type = REG_DWORD;
   unsigned long buflen = sizeof(unsigned long);

+ 3 - 0
registry.h

@@ -60,6 +60,9 @@ int get_number(HKEY, TCHAR *, unsigned long *, bool);
 int get_number(HKEY, TCHAR *, unsigned long *);
 int format_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *);
 int unformat_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *);
+int copy_double_null(TCHAR *, unsigned long, TCHAR **);
+int append_to_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *, TCHAR *, size_t, bool);
+int remove_from_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *, TCHAR *, size_t, bool);
 void override_milliseconds(TCHAR *, HKEY, TCHAR *, unsigned long *, unsigned long, unsigned long);
 int get_io_parameters(nssm_service_t *, HKEY);
 int get_parameters(nssm_service_t *, STARTUPINFO *);