Compare commits
No commits in common. "80dce6105803b7ed22ebdfc27a4af9e431b582f9" and "ce9f197a83810e1e32dc24b6ca4af05f57154ab7" have entirely different histories.
80dce61058
...
ce9f197a83
|
@ -18,10 +18,3 @@ SilverMUDClient_SOURCES = \
|
||||||
source/client/client-drawing.c \
|
source/client/client-drawing.c \
|
||||||
source/client/receiving-thread.c \
|
source/client/receiving-thread.c \
|
||||||
source/client/main.c
|
source/client/main.c
|
||||||
|
|
||||||
check_PROGRAMS = lists_test
|
|
||||||
lists_test_SOURCES = \
|
|
||||||
source/tests/lists-test.c \
|
|
||||||
source/server/lists.c
|
|
||||||
|
|
||||||
TESTS = $(check_PROGRAMS)
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
// =========================================
|
|
||||||
// | SilverMUD Server - data-type.h |
|
|
||||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
||||||
// | See end of file for copyright notice. |
|
|
||||||
// =========================================
|
|
||||||
#ifndef DATATYPE_H
|
|
||||||
#define DATATYPE_H
|
|
||||||
|
|
||||||
enum DataType
|
|
||||||
{
|
|
||||||
AREA,
|
|
||||||
PLAYER,
|
|
||||||
CONNECTION
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
// =================================================
|
|
||||||
// | End of data-type.h, copyright notice follows. |
|
|
||||||
// =================================================
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
@ -1,230 +0,0 @@
|
||||||
// =========================================
|
|
||||||
// | SilverMUD Server - lists.c |
|
|
||||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
||||||
// | See end of file for copyright notice. |
|
|
||||||
// =========================================
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "lists.h"
|
|
||||||
|
|
||||||
// Functions:
|
|
||||||
// ==========
|
|
||||||
struct List * createList(enum DataType type)
|
|
||||||
{
|
|
||||||
struct List * newList = calloc(1, sizeof(struct List));
|
|
||||||
newList->itemCount = 0;
|
|
||||||
newList->head = NULL;
|
|
||||||
newList->tail = NULL;
|
|
||||||
newList->type = type;
|
|
||||||
return newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t appendToList(enum DataType type, struct List * list, void * data)
|
|
||||||
{
|
|
||||||
// First check that you're adding the correct type:
|
|
||||||
assert(type == list->type);
|
|
||||||
|
|
||||||
struct ListNode * newListNode = calloc(1, sizeof(struct ListNode));
|
|
||||||
newListNode->next = NULL;
|
|
||||||
newListNode->previous = list->tail;
|
|
||||||
newListNode->data = data;
|
|
||||||
|
|
||||||
if (list->itemCount == 0)
|
|
||||||
{
|
|
||||||
list->head = newListNode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
list->tail->next = newListNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
list->tail = newListNode;
|
|
||||||
list->itemCount++;
|
|
||||||
|
|
||||||
return list->itemCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * deleteListNodeFromList(size_t index, struct List * list)
|
|
||||||
{
|
|
||||||
void * toReturn;
|
|
||||||
|
|
||||||
if ((list->itemCount - 1) < index)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ListNode * currentListNode = NULL;
|
|
||||||
|
|
||||||
if (index < (list->itemCount / 2))
|
|
||||||
{
|
|
||||||
currentListNode = list->head;
|
|
||||||
|
|
||||||
// Get to the correct point in the linked list:
|
|
||||||
for (int currentIndex = 0; currentIndex < index; currentIndex++)
|
|
||||||
{
|
|
||||||
currentListNode = currentListNode->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
currentListNode = list->tail;
|
|
||||||
|
|
||||||
// Get to the correct point in the linked list:
|
|
||||||
for (int currentIndex = list->itemCount - 1; currentIndex > index; currentIndex--)
|
|
||||||
{
|
|
||||||
currentListNode = currentListNode->previous;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentListNode == list->head)
|
|
||||||
{
|
|
||||||
list->head = list->head->next;
|
|
||||||
if (list->head)
|
|
||||||
{
|
|
||||||
list->head->previous = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentListNode == list->tail)
|
|
||||||
{
|
|
||||||
list->tail = list->tail->previous;
|
|
||||||
if (list->tail)
|
|
||||||
{
|
|
||||||
list->tail->next = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentListNode->next != NULL)
|
|
||||||
{
|
|
||||||
currentListNode->next->previous = currentListNode->previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentListNode->previous != NULL)
|
|
||||||
{
|
|
||||||
currentListNode->previous->next = currentListNode->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
toReturn = currentListNode->data;
|
|
||||||
free(currentListNode);
|
|
||||||
|
|
||||||
list->itemCount--;
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t indexOfFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
|
||||||
{
|
|
||||||
size_t index = 0;
|
|
||||||
if (list->head == NULL)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct ListNode * currentListNode = list->head;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (comparisonFunction(currentListNode->data, data) == true)
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
currentListNode = currentListNode->next;
|
|
||||||
}
|
|
||||||
while (currentListNode != NULL);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void * getFirstFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
|
||||||
{
|
|
||||||
size_t index = 0;
|
|
||||||
if (list->head == NULL)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct ListNode * currentListNode = list->head;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (comparisonFunction(currentListNode->data, data) == true)
|
|
||||||
{
|
|
||||||
return currentListNode;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
currentListNode = currentListNode->next;
|
|
||||||
}
|
|
||||||
while (currentListNode != NULL);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
|
||||||
{
|
|
||||||
if (list->head == NULL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct ListNode * currentListNode = list->head;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (comparisonFunction(currentListNode->data, data) == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
currentListNode = currentListNode->next;
|
|
||||||
}
|
|
||||||
while (currentListNode != NULL);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPointerInList(void * data, struct List * list)
|
|
||||||
{
|
|
||||||
if (list->head == NULL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct ListNode * currentListNode = list->head;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (currentListNode->data == data)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
currentListNode = currentListNode->next;
|
|
||||||
}
|
|
||||||
while (currentListNode != NULL);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================
|
|
||||||
// | End of lists.h, copyright notice follows. |
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
// =========================================
|
|
||||||
// | SilverMUD Server - lists.h |
|
|
||||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
||||||
// | See end of file for copyright notice. |
|
|
||||||
// =========================================
|
|
||||||
#ifndef LISTS_H
|
|
||||||
#define LISTS_H
|
|
||||||
#include "data-type.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
struct List
|
|
||||||
{
|
|
||||||
size_t itemCount;
|
|
||||||
enum DataType type;
|
|
||||||
struct ListNode * head;
|
|
||||||
struct ListNode * tail;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ListNode
|
|
||||||
{
|
|
||||||
struct ListNode * next;
|
|
||||||
struct ListNode * previous;
|
|
||||||
void * data;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Functions:
|
|
||||||
// ==========
|
|
||||||
|
|
||||||
struct List * createList(enum DataType type);
|
|
||||||
size_t appendToList(enum DataType type, struct List * list, void * data);
|
|
||||||
void * deleteListNodeFromList(size_t index, struct List * list);
|
|
||||||
ssize_t indexOfFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
|
||||||
void * getFirstFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
|
||||||
bool isInList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
|
||||||
bool isPointerInList(void * data, struct List * list);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
// =============================================
|
|
||||||
// | End of lists.h, copyright notice follows. |
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
@ -1,172 +0,0 @@
|
||||||
// =========================================
|
|
||||||
// | SilverMUD Tests - lists-test.c |
|
|
||||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
||||||
// | See end of file for copyright notice. |
|
|
||||||
// =========================================
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include "tests.h"
|
|
||||||
#include "../server/lists.h"
|
|
||||||
|
|
||||||
SETUP_TESTS;
|
|
||||||
|
|
||||||
bool compareChar (void * a, void * b)
|
|
||||||
{
|
|
||||||
return *(char *)a == *(char *)b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printListHeader(struct List * list)
|
|
||||||
{
|
|
||||||
printf("Items in list: %zu\nType: %d\nHead: %p\nTail: %p\n\n",
|
|
||||||
list->itemCount, list->type, list->head, list->tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printNode(struct Node * node)
|
|
||||||
{
|
|
||||||
printf("%p\nNext: %p\nPrevious: %p\nData: %c\n\n",
|
|
||||||
node, node->next, node->previous, *(char *)(node->data));
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char ** argv)
|
|
||||||
{
|
|
||||||
char testDataA = 'A', testDataB = 'B', testDataC = 'C';
|
|
||||||
|
|
||||||
// Test 1:
|
|
||||||
struct List * testList = createList(PLAYER);
|
|
||||||
TEST("Creating list",
|
|
||||||
(testList->head == NULL &&
|
|
||||||
testList->tail == NULL &&
|
|
||||||
testList->itemCount == 0));
|
|
||||||
|
|
||||||
// Test 2:
|
|
||||||
appendToList(PLAYER, testList, &testDataA);
|
|
||||||
TEST("Adding a single item to the list",
|
|
||||||
(testList->itemCount == 1 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
testList->tail == testList->head &&
|
|
||||||
*(char *)(testList->head->data) == 'A'));
|
|
||||||
|
|
||||||
// Test 3:
|
|
||||||
deleteNodeFromList(0, testList);
|
|
||||||
TEST("Deleting a single item from the list",
|
|
||||||
(testList->head == NULL &&
|
|
||||||
testList->tail == NULL &&
|
|
||||||
testList->itemCount == 0));
|
|
||||||
|
|
||||||
// Test 4:
|
|
||||||
appendToList(PLAYER, testList, &testDataA);
|
|
||||||
appendToList(PLAYER, testList, &testDataB);
|
|
||||||
TEST("Adding two items to the list",
|
|
||||||
(testList->itemCount == 2 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
*(char *)testList->head->data == 'A' &&
|
|
||||||
*(char *)testList->tail->data == 'B' &&
|
|
||||||
testList->tail != testList->head &&
|
|
||||||
testList->head->next == testList->tail &&
|
|
||||||
testList->tail->previous == testList->head));
|
|
||||||
|
|
||||||
// Test 5:
|
|
||||||
deleteNodeFromList(1, testList);
|
|
||||||
TEST("Deleting tail from the list",
|
|
||||||
(testList->itemCount == 1 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
testList->tail == testList->head &&
|
|
||||||
*(char *)(testList->head->data) == 'A'));
|
|
||||||
|
|
||||||
// Test 6:
|
|
||||||
appendToList(PLAYER, testList, &testDataB);
|
|
||||||
deleteNodeFromList(0, testList);
|
|
||||||
TEST("Deleting head from the list",
|
|
||||||
(testList->itemCount == 1 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
testList->tail == testList->head &&
|
|
||||||
*(char *)(testList->head->data) == 'B'));
|
|
||||||
|
|
||||||
// Test 7:
|
|
||||||
deleteNodeFromList(0, testList);
|
|
||||||
appendToList(PLAYER, testList, &testDataA);
|
|
||||||
appendToList(PLAYER, testList, &testDataB);
|
|
||||||
appendToList(PLAYER, testList, &testDataC);
|
|
||||||
TEST("Adding three items to the list",
|
|
||||||
(testList->itemCount == 3 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
*(char *)testList->head->data == 'A' &&
|
|
||||||
*(char *)testList->tail->data == 'C' &&
|
|
||||||
*(char *)testList->head->next->data == 'B' &&
|
|
||||||
*(char *)testList->tail->previous->data == 'B' &&
|
|
||||||
testList->tail != testList->head &&
|
|
||||||
testList->tail->previous == testList->head->next));
|
|
||||||
|
|
||||||
// Test 8:
|
|
||||||
deleteNodeFromList(1, testList);
|
|
||||||
TEST("Deleting an item in middle of the list",
|
|
||||||
(testList->itemCount == 2 &&
|
|
||||||
testList->head != NULL &&
|
|
||||||
testList->tail != NULL &&
|
|
||||||
*(char *)testList->head->data == 'A' &&
|
|
||||||
*(char *)testList->tail->data == 'C' &&
|
|
||||||
testList->tail != testList->head &&
|
|
||||||
testList->head->next == testList->tail &&
|
|
||||||
testList->tail->previous == testList->head));
|
|
||||||
|
|
||||||
// Test 9:
|
|
||||||
deleteNodeFromList(1, testList);
|
|
||||||
appendToList(PLAYER, testList, &testDataB);
|
|
||||||
appendToList(PLAYER, testList, &testDataC);
|
|
||||||
TEST("Checking for data in the list using comparison function",
|
|
||||||
(isInList(compareChar, &testDataB, testList) == true));
|
|
||||||
|
|
||||||
// Test 10:
|
|
||||||
TEST("Checking for data NOT in the list using comparison function",
|
|
||||||
(isInList(compareChar, &(char){'D'}, testList) == true));
|
|
||||||
|
|
||||||
// Test 11:
|
|
||||||
TEST("Checking for data in the list using pointer",
|
|
||||||
(isPointerInList(&testDataB, testList) == true));
|
|
||||||
|
|
||||||
// Test 12:
|
|
||||||
TEST("Checking for data NOT in the list using pointer",
|
|
||||||
(isPointerInList(&(char){'D'}, testList) == false));
|
|
||||||
|
|
||||||
// Test 13:
|
|
||||||
TEST("Checking index of data in the list",
|
|
||||||
(indexOfFromList(compareChar, &testDataB, testList) == 1));
|
|
||||||
|
|
||||||
// Test 14:
|
|
||||||
TEST("Checking index of data NOT in the list",
|
|
||||||
(indexOfFromList(compareChar, &(char){'D'}, testList) == -1));
|
|
||||||
|
|
||||||
// Test 15:
|
|
||||||
appendToList(PLAYER, testList, &testDataA);
|
|
||||||
TEST("Checking first of data in the list",
|
|
||||||
(getFirstFromList(compareChar, &testDataA, testList) == testList->head));
|
|
||||||
|
|
||||||
// Test 16:
|
|
||||||
TEST("Checking first of data NOT in the list",
|
|
||||||
(getFirstFromList(compareChar, &(char){'D'}, testList) == NULL));
|
|
||||||
|
|
||||||
FINISH_TESTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================================================
|
|
||||||
// | End of lists-test.c, copyright notice follows. |
|
|
||||||
// ==================================================
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
@ -1,43 +0,0 @@
|
||||||
// =========================================
|
|
||||||
// | SilverMUD Tests - tests.h |
|
|
||||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
|
||||||
// | See end of file for copyright notice. |
|
|
||||||
// =========================================
|
|
||||||
#ifndef SILVERMUD_TESTS
|
|
||||||
#define SILVERMUD_TESTS
|
|
||||||
|
|
||||||
#define SETUP_TESTS \
|
|
||||||
size_t TEST_SUCCESSES = 0, TEST_FAILURES = 0, TEST_COUNT;
|
|
||||||
|
|
||||||
#define TEST(STRING, CONDITION) \
|
|
||||||
printf("Test %zu: ", ++TEST_COUNT); \
|
|
||||||
printf(STRING); \
|
|
||||||
if (CONDITION) { printf("... SUCCESS!\n"); TEST_SUCCESSES++; } \
|
|
||||||
else { printf("... FAILED.\n"); TEST_FAILURES++; }
|
|
||||||
|
|
||||||
#define FINISH_TESTING \
|
|
||||||
printf("=====\n\nSuccesses: %zu | Failures: %zu\n", TEST_SUCCESSES, TEST_FAILURES); \
|
|
||||||
if (TEST_FAILURES == 0) \
|
|
||||||
{ \
|
|
||||||
printf("All good! We're done here.\n"); \
|
|
||||||
exit(EXIT_SUCCESS); \
|
|
||||||
} \
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
// =============================================
|
|
||||||
// | End of tests.h, copyright notice follows. |
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
Loading…
Reference in New Issue