aboutsummaryrefslogtreecommitdiffstats
path: root/libparc/parc/algol/parc_URI.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparc/parc/algol/parc_URI.c')
-rw-r--r--libparc/parc/algol/parc_URI.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/libparc/parc/algol/parc_URI.c b/libparc/parc/algol/parc_URI.c
new file mode 100644
index 00000000..172468d8
--- /dev/null
+++ b/libparc/parc/algol/parc_URI.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+#include <config.h>
+
+#include <LongBow/runtime.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <parc/algol/parc_URI.h>
+
+#include <parc/algol/parc_URIPath.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_BufferComposer.h>
+
+char *sub_delims = "!$&'()*+,;=";
+char *gen_delims = ":/?#[]@";
+
+#define isSubDelims(c) (c != 0 && strchr(sub_delims, c) != NULL)
+#define isGenDelims(c) (c != 0 && strchr(gen_delims, c) != NULL)
+#define isDigit(c) (c >= '0' && c <= '9')
+#define isAlpha(c) (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+
+#define isUnreserved(c) (isAlpha(c) || isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~')
+#define isReserved(c) (isGenDelims(c) || isSubDelims(c))
+#define isPchar(c) (isUnreserved(c) || isSubDelims(c) || c == ':' || c == '@')
+
+struct parc_uri {
+ char *scheme;
+ char *authority;
+ PARCURIPath *path;
+ char *query;
+ char *fragment;
+};
+
+static void
+_parcURI_Finalize(PARCURI **uriPtr)
+{
+ assertNotNull(uriPtr, "Parameter must be a non-null pointer to a pointer to a PARCURI instance.");
+
+ PARCURI *uri = *uriPtr;
+
+ if (uri->scheme != NULL) {
+ parcMemory_Deallocate((void **) &uri->scheme);
+ }
+
+ if (uri->authority != NULL) {
+ parcMemory_Deallocate((void **) &(uri->authority));
+ }
+
+ if (uri->path) {
+ parcURIPath_Release(&uri->path);
+ }
+
+ if (uri->query != NULL) {
+ parcMemory_Deallocate((void **) &(uri->query));
+ }
+
+ if (uri->fragment != NULL) {
+ parcMemory_Deallocate((void **) &(uri->fragment));
+ }
+}
+
+parcObject_ExtendPARCObject(PARCURI, _parcURI_Finalize, parcURI_Copy, parcURI_ToString, parcURI_Equals, NULL, NULL, NULL);
+
+PARCURI *
+parcURI_Create(void)
+{
+ PARCURI *result = parcObject_CreateAndClearInstance(PARCURI);
+ return result;
+}
+
+
+PARCURI *
+parcURI_CreateFromValist(const char *restrict format, va_list argList)
+{
+ PARCURI *result = NULL;
+
+ char *string;
+ if (vasprintf(&string, format, argList) != -1) {
+ result = parcURI_Parse(string);
+ free(string);
+ }
+ return result;
+}
+
+PARCURI *
+parcURI_CreateFromFormatString(const char *restrict format, ...)
+{
+ va_list argList;
+ va_start(argList, format);
+
+ PARCURI *result = parcURI_CreateFromValist(format, argList);
+
+ va_end(argList);
+
+ return result;
+}
+
+parcObject_ImplementAcquire(parcURI, PARCURI);
+
+parcObject_ImplementRelease(parcURI, PARCURI);
+
+static bool
+_parcURI_SchemeEquals(const char *schemeA, const char *schemeB)
+{
+ if (schemeA == schemeB) {
+ return true;
+ }
+ if (schemeA == NULL || schemeB == NULL) {
+ return false;
+ }
+ return strcmp(schemeA, schemeB) == 0;
+}
+
+static bool
+_parcURI_AuthorityEquals(const char *authorityA, const char *authorityB)
+{
+ if (authorityA == authorityB) {
+ return true;
+ }
+ if (authorityA == NULL || authorityB == NULL) {
+ return false;
+ }
+ return strcmp(authorityA, authorityB) == 0;
+}
+
+static bool
+_parcURI_QueryEquals(const char *queryA, const char *queryB)
+{
+ if (queryA == queryB) {
+ return true;
+ }
+ if (queryA == NULL || queryB == NULL) {
+ return false;
+ }
+ return strcmp(queryA, queryB) == 0;
+}
+
+static bool
+_parcURI_FragmentEquals(const char *fragmentA, const char *fragmentB)
+{
+ if (fragmentA == fragmentB) {
+ return true;
+ }
+ if (fragmentA == NULL || fragmentB == NULL) {
+ return false;
+ }
+ return strcmp(fragmentA, fragmentB) == 0;
+}
+
+bool
+parcURI_Equals(const PARCURI *uriA, const PARCURI *uriB)
+{
+ if (uriA == uriB) {
+ return true;
+ }
+ if (uriA == NULL || uriB == NULL) {
+ return false;
+ }
+
+ if (_parcURI_SchemeEquals(uriA->scheme, uriB->scheme)) {
+ if (_parcURI_AuthorityEquals(uriA->authority, uriB->authority)) {
+ if (parcURIPath_Equals(uriA->path, uriB->path)) {
+ if (_parcURI_QueryEquals(uriA->query, uriB->query)) {
+ if (_parcURI_FragmentEquals(uriA->fragment, uriB->fragment)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * Parse and return a copy of the scheme portion of the URI.
+ *
+ * If this function returns successfully,
+ * the input parameter @p pointer will point to either a null-byte or the first character
+ * after the ':' separating the scheme from the rest of the URI.
+ *
+ * @return non-NULL An allocated string copy of the string which must be freed by the caller via <code>parcMemory_Deallocate</code>.
+ * @return NULL The scheme is malformed.
+ */
+static char *
+_parseScheme(const char *uri, const char **pointer)
+{
+ size_t length = 0;
+
+ const char *p = uri;
+ while (*p != 0 && *p != ':') {
+ length++;
+ p++;
+ }
+ if (*p == 0) {
+ return NULL;
+ }
+
+ if (length == 0) {
+ return NULL;
+ }
+
+ char *result = parcMemory_StringDuplicate(uri, length);
+
+ *pointer = (char *) &uri[length + 1];
+ return result;
+}
+
+/**
+ * @function _parseAuthority
+ * @abstract Parse the authority portion of a URI, if present.
+ * @discussion
+ * A URI may have an optional authority component.
+ * If the given string begins with a double forward slash ("//"),
+ * then it is followed by an authority part and a path.
+ * If the string doesn't begin with ("//") it contains only a path and this
+ * function simply returns NULL and setting the give pointer to the first
+ * character of the (expected) path.
+ *
+ * @param string A pointer to the start of the (potential) authority component.
+ * @param pointer A pointer to a character pointer that will be assigned point to the first character that begins the path.
+ * @return An allocated string, to be freed via parcMemory_Deallocate, if the authority portion is present or NULL if otherwise.
+ */
+static char *
+_parseAuthority(const char *string, const char **pointer)
+{
+ if (string[0] == '/' && string[1] == '/') {
+ size_t length = 0;
+ for (const char *p = &string[2]; *p != '/'; p++) {
+ if (*p == 0) {
+ *pointer = p;
+ break;
+ }
+ length++;
+ }
+
+ char *result = parcMemory_StringDuplicate(&string[2], length);
+ // The pointer must point to the first character *after* the '/' character as the '/' is not part of the path.
+ *pointer = &(&string[2])[length];
+ return result;
+ }
+ *pointer = string;
+ return NULL;
+}
+
+static char *
+_parseQuery(const char *string, const char **pointer)
+{
+ if (*string != '?') {
+ return NULL;
+ }
+
+ string++;
+ size_t length = 0;
+ for (const char *p = string; *p != 0 && *p != '#'; p++) {
+ length++;
+ }
+
+ char *result = parcMemory_StringDuplicate(string, length);
+ *pointer = &string[length];
+ return result;
+}
+
+static char *
+_parseFragment(const char *string, const char **pointer)
+{
+ if (*string != '#') {
+ return NULL;
+ }
+ string++;
+ size_t length = 0;
+ for (const char *p = string; *p != 0; p++) {
+ length++;
+ }
+ char *result = parcMemory_StringDuplicate(string, length);
+
+ *pointer = &string[length];
+ return result;
+}
+
+static void
+_parcURI_SetScheme(PARCURI *uri, const char *scheme)
+{
+ if (uri->scheme != NULL) {
+ parcMemory_Deallocate((void **) &(uri->scheme));
+ }
+ if (scheme == NULL) {
+ uri->scheme = NULL;
+ } else {
+ uri->scheme = parcMemory_StringDuplicate(scheme, strlen(scheme));
+ }
+}
+
+static void
+_parcURI_SetAuthority(PARCURI *uri, const char *authority)
+{
+ if (uri->authority != NULL) {
+ parcMemory_Deallocate((void **) &(uri->authority));
+ }
+ if (authority == NULL) {
+ uri->authority = NULL;
+ } else {
+ uri->authority = parcMemory_StringDuplicate(authority, strlen(authority));
+ }
+}
+
+static void
+_parcURI_SetQuery(PARCURI *uri, const char *query)
+{
+ if (uri->query != NULL) {
+ parcMemory_Deallocate((void **) &(uri->query));
+ }
+ if (query == NULL) {
+ uri->query = NULL;
+ } else {
+ uri->query = parcMemory_StringDuplicate(query, strlen(query));
+ }
+}
+
+static void
+_parcURI_SetFragment(PARCURI *uri, const char *fragment)
+{
+ if (uri->fragment != NULL) {
+ parcMemory_Deallocate((void **) &(uri->fragment));
+ }
+ if (fragment == NULL) {
+ uri->fragment = NULL;
+ } else {
+ uri->fragment = parcMemory_StringDuplicate(fragment, strlen(fragment));
+ }
+}
+
+PARCURI *
+parcURI_Parse(const char *string)
+{
+ const char *pointer = string;
+
+ PARCURI *result = parcURI_Create();
+
+ if (result != NULL) {
+ result->scheme = _parseScheme(pointer, &pointer);
+ if (result->scheme != NULL) {
+ result->authority = _parseAuthority(pointer, &pointer);
+ result->path = parcURIPath_Parse(pointer, &pointer);
+ result->query = _parseQuery(pointer, &pointer);
+ result->fragment = _parseFragment(pointer, &pointer);
+ } else {
+ parcURI_Release(&result);
+ result = NULL;
+ }
+ }
+
+ return result;
+}
+
+PARCURI *
+parcURI_Copy(const PARCURI *uri)
+{
+ PARCURI *result = parcURI_Create();
+
+ if (result != NULL) {
+ _parcURI_SetScheme(result, parcURI_GetScheme(uri));
+ _parcURI_SetAuthority(result, parcURI_GetAuthority(uri));
+ result->path = parcURIPath_Copy(parcURI_GetPath(uri));
+ _parcURI_SetQuery(result, parcURI_GetQuery(uri));
+ _parcURI_SetFragment(result, parcURI_GetFragment(uri));
+ }
+
+ return result;
+}
+
+PARCBufferComposer *
+parcURI_BuildString(const PARCURI *uri, PARCBufferComposer *composer)
+{
+ parcBufferComposer_PutStrings(composer, parcURI_GetScheme(uri), ":", NULL);
+
+ if (parcURI_GetAuthority(uri)) {
+ parcBufferComposer_PutString(composer, "//");
+ parcBufferComposer_PutString(composer, parcURI_GetAuthority(uri));
+ }
+
+ parcBufferComposer_PutString(composer, "/");
+ parcURIPath_BuildString(parcURI_GetPath(uri), composer);
+
+ if (parcURI_GetQuery(uri)) {
+ parcBufferComposer_PutStrings(composer, "?", parcURI_GetQuery(uri), NULL);
+ }
+
+ if (parcURI_GetFragment(uri)) {
+ parcBufferComposer_PutStrings(composer, "#", parcURI_GetFragment(uri), NULL);
+ }
+
+ return composer;
+}
+
+char *
+parcURI_ToString(const PARCURI *uri)
+{
+ char *result = NULL;
+
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ if (composer != NULL) {
+ if (parcURI_BuildString(uri, composer) != NULL) {
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ }
+ parcBufferComposer_Release(&composer);
+ }
+
+ return result;
+}
+
+const char *
+parcURI_GetScheme(const PARCURI *uri)
+{
+ return uri->scheme;
+}
+
+const char *
+parcURI_GetAuthority(const PARCURI *uri)
+{
+ return uri->authority;
+}
+
+PARCURIPath *
+parcURI_GetPath(const PARCURI *uri)
+{
+ return uri->path;
+}
+
+const char *
+parcURI_GetQuery(const PARCURI *uri)
+{
+ return uri->query;
+}
+
+const char *
+parcURI_GetFragment(const PARCURI *uri)
+{
+ return uri->fragment;
+}