util: Don't read over 'size - 1' bytes of source string in ovs_strlcpy().
[sliver-openvswitch.git] / lib / util.c
index 39ca3b5..1aa8271 100644 (file)
@@ -27,6 +27,8 @@
 
 VLOG_DEFINE_THIS_MODULE(util);
 
+COVERAGE_DEFINE(util_xalloc);
+
 const char *program_name;
 
 void
@@ -135,14 +137,15 @@ xasprintf(const char *format, ...)
     return s;
 }
 
+/* Similar to strlcpy() from OpenBSD, but it never reads more than 'size - 1'
+ * bytes from 'src' and doesn't return anything. */
 void
 ovs_strlcpy(char *dst, const char *src, size_t size)
 {
     if (size > 0) {
-        size_t n = strlen(src);
-        size_t n_copy = MIN(n, size - 1);
-        memcpy(dst, src, n_copy);
-        dst[n_copy] = '\0';
+        size_t len = strnlen(src, size - 1);
+        memcpy(dst, src, len);
+        dst[len] = '\0';
     }
 }
 
@@ -156,8 +159,7 @@ ovs_fatal(int err_no, const char *format, ...)
     vfprintf(stderr, format, args);
     va_end(args);
     if (err_no != 0)
-        fprintf(stderr, " (%s)",
-                err_no == EOF ? "end of file" : strerror(err_no));
+        fprintf(stderr, " (%s)", ovs_retval_to_string(err_no));
     putc('\n', stderr);
 
     exit(EXIT_FAILURE);
@@ -174,14 +176,40 @@ ovs_error(int err_no, const char *format, ...)
     vfprintf(stderr, format, args);
     va_end(args);
     if (err_no != 0) {
-        fprintf(stderr, " (%s)",
-                err_no == EOF ? "end of file" : strerror(err_no));
+        fprintf(stderr, " (%s)", ovs_retval_to_string(err_no));
     }
     putc('\n', stderr);
 
     errno = save_errno;
 }
 
+/* Many OVS functions return an int which is one of:
+ * - 0: no error yet
+ * - >0: errno value
+ * - EOF: end of file (not necessarily an error; depends on the function called)
+ *
+ * Returns the appropriate human-readable string. The caller must copy the
+ * string if it wants to hold onto it, as the storage may be overwritten on
+ * subsequent function calls.
+ */
+const char *
+ovs_retval_to_string(int retval)
+{
+    static char unknown[48];
+
+    if (!retval) {
+        return "";
+    }
+    if (retval > 0) {
+        return strerror(retval);
+    }
+    if (retval == EOF) {
+        return "End of file";
+    }
+    snprintf(unknown, sizeof unknown, "***unknown return value: %d***", retval);
+    return unknown;
+}
+
 /* Sets program_name based on 'argv0'.  Should be called at the beginning of
  * main(), as "set_program_name(argv[0]);".  */
 void set_program_name(const char *argv0)
@@ -358,9 +386,37 @@ hexit_value(int c)
 
     case 'f': case 'F':
         return 0xf;
+
+    default:
+        return -1;
     }
+}
 
-    NOT_REACHED();
+/* Returns the integer value of the 'n' hexadecimal digits starting at 's', or
+ * UINT_MAX if one of those "digits" is not really a hex digit.  If 'ok' is
+ * nonnull, '*ok' is set to true if the conversion succeeds or to false if a
+ * non-hex digit is detected. */
+unsigned int
+hexits_value(const char *s, size_t n, bool *ok)
+{
+    unsigned int value;
+    size_t i;
+
+    value = 0;
+    for (i = 0; i < n; i++) {
+        int hexit = hexit_value(s[i]);
+        if (hexit < 0) {
+            if (ok) {
+                *ok = false;
+            }
+            return UINT_MAX;
+        }
+        value = (value << 4) + hexit;
+    }
+    if (ok) {
+        *ok = true;
+    }
+    return value;
 }
 
 /* Returns the current working directory as a malloc()'d string, or a null
@@ -394,6 +450,14 @@ get_cwd(void)
     }
 }
 
+static char *
+all_slashes_name(const char *s)
+{
+    return xstrdup(s[0] == '/' && s[1] == '/' && s[2] != '/' ? "//"
+                   : s[0] == '/' ? "/"
+                   : ".");
+}
+
 /* Returns the directory name portion of 'file_name' as a malloc()'d string,
  * similar to the POSIX dirname() function but thread-safe. */
 char *
@@ -409,15 +473,31 @@ dir_name(const char *file_name)
     while (len > 0 && file_name[len - 1] == '/') {
         len--;
     }
-    if (!len) {
-        return xstrdup((file_name[0] == '/'
-                        && file_name[1] == '/'
-                        && file_name[2] != '/') ? "//"
-                       : file_name[0] == '/' ? "/"
-                       : ".");
-    } else {
-        return xmemdup0(file_name, len);
+    return len ? xmemdup0(file_name, len) : all_slashes_name(file_name);
+}
+
+/* Returns the file name portion of 'file_name' as a malloc()'d string,
+ * similar to the POSIX basename() function but thread-safe. */
+char *
+base_name(const char *file_name)
+{
+    size_t end, start;
+
+    end = strlen(file_name);
+    while (end > 0 && file_name[end - 1] == '/') {
+        end--;
+    }
+
+    if (!end) {
+        return all_slashes_name(file_name);
     }
+
+    start = end;
+    while (start > 0 && file_name[start - 1] != '/') {
+        start--;
+    }
+
+    return xmemdup0(file_name + start, end - start);
 }
 
 /* If 'file_name' starts with '/', returns a copy of 'file_name'.  Otherwise,