Jump to content

[Structuri de Date - C] Inserare în Liste Înlănțuite (Partea 1)


Soulrayne
 Share

Recommended Posts

Obiective

 

► Construim o structura de Utilizatori B-ZONE cu campuri precum : ID , Nume , Adresa de Mail , Parola , NrPosturi , isBanned dar si rolurile din care face parte ;

► Constituim o lista inlantuita (generica) cu toate componentele acestuia (POINTER DE REFERINTA SI INFORMATIE) 

► Inseram date  dintr-un fisier text  (pe care l voi pune in atasament) .  

► Completam lista cu toate datele din fisier ; 

► Afisam & Eliberam Toata Lista . 

 

 

NOTA : Vom folosi proiectul din tutorialele trecute de Structuri . (Cu Roluri)

 

Declarare Structura Utilizatori B-ZONE

 

// User.h 

typedef struct User 
{ 

// INFORMATIE UTILIZATOR 
unsigned id; // ID UTILIZATOR 
char* username; // NUME UTILIZATOR 
char* email; // ADRESA MAIL UTILIZATOR 
char* password; // PAROLA UTILIZATOR 
unsigned nrPosts; // NUMAR POSTURI 
unsigned isBanned; // NU AVEM VALOARE BOOLEANA IN C (bool) ==> "1" E TRUE && "0" E FALSE 
Role* roles; // Colectie de grupuri din care face parte utilizatorul 
}User;

 

Declarare Lista Inlantuita

 

// Lists.h ----> FISIER NOU 

typedef struct Linked_List{ // LISTA INLANTUITA GENERICA (O PUTEM FOLOSI CU ORICE TIP DE DATE FARA SA DECLARAM MAI MULTE STRUCTURI) 
void* Data; // Pastram informatia pentru elementul curent 
Linked_List* nextElement; // Ne corelam cu urmatorul element 
}LinkedList; // Asta reprezinta o eticheta pentru structura ==> Scriem "LinkedList" inloc de "struct Linked_List"

 

NOTA :  "void*" REPREZINTA un caz particular in C , o modalitatea de a substitui mai multe tipuri de date pentru a EVITA declararea Listei  pentru fiecare tip de date . De exemplu ,  daca doresc ca lista sa fie folosita apoi , pentru structura "studenti" sau "masini" sau altceva , o putem refolosi fara sa facem lista pentru fiecare tip de structura . (tip de date) 

 

Preluare Utilizator din Fisier

 

 Declaram o functie pentru incarcarea datelor din fisier in vederea initializarii structuri : 

 

// User.h /

// Structura & Restu Codului 

#include "roles.h" // Avem nevoie pentru tipul de date Role 

#define FILENAME_USER_INPUT "dateIntrareUtilizatoriBZONE.txt" // Definim un nume pentru intrare 

User* fetchUsersFromTxtFile(FILE* inputTxt,Role* roles); // Incarcam o colectie de utilizatori din fisier

 

Ne asiguram ca putem stoca suficient spatiu pentru fiecare linie : 

 

// stringHandling.h 

// Restu Codului 

#define MAX_STRING_LINE 256 // Alocam maxim 256 de caractere pentru linii

 

Trecem la implementarea codului pentru incarcarea utilizatorului din fisier !

 

// user.c 

#include "User.h" // legam cu fisierul asociat acestuia (de unde vin declaratiile) 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 
{ 
// Implementare 
}

 

Verificam daca fisierul este eligibil pentru inserare :

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 
}

 

Daca totul e ok , declaram o colectie de utilizatori dar si un contor pentru indexare : 

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

}

 

Adaugam o colectie de caractere , ca intermediar , pentru preluare a linie cu linie din fisier :

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

char bufferLine[MAX_STRING_LINE]; // Cu spatiu alocat explicit mai sus 
}

 

Citim linie cu linie din fisier cu "fgets" (pana la finalul fisierului) iar continutul (toata linia) o oferim colectiei declarate mai sus : 

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

char bufferLine[MAX_STRING_LINE]; // Cu spatiu alocat explicit mai sus 
while ((fgets(bufferLine, MAX_STRING_LINE, inputTxt)) != NULL) // Citim Linie cu Linie din Fisier 
{ 
// Implementare 
} 
}

 

 Citim si inseram in colectia de Utilizatori , informatiile despre ID , NUME , EMAIL , PAROLA , NUMAR POSTURI SI DACA E BANAT :

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

char bufferLine[MAX_STRING_LINE]; // Cu spatiu alocat explicit mai sus 
while ((fgets(bufferLine, MAX_STRING_LINE, inputTxt)) != NULL) // Citim Linie cu Linie din Fisier 

{ 
char*Token, *nextToken; // Prima va retine informatia curenta iar cealalta referinta la cea mai ramas din linie 

// Inseram Date 

/// ID 
Token = strtok_s(bufferLine,DELIMITATER,&nextToken); // Preluam ID-ul din linie in format de STRING 
newUser[i-1].id = atoi(Token); // Convertim din string in intreg + Il Incarcam in Structura 

// NUME 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMELE din linie in format de STRING 
allocAndFetchString(&newUser[i-1].username,Token); // Il Incarcam in Structura 

// Adresa de Mail 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].email, Token); // Il Incarcam in Structura 

// Parola 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].password, Token); // Il Incarcam in Structura 

// NrPosts 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMARUL DE POSTURI DIN STRUCTURA 
newUser[i-1].nrPosts = atoi(Token); // CONVERTIM DIN STRING IN INTREG SI IL INCARCAM 

// e Banat 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // IDEM 
newUser[i-1].isBanned = atoi(Token); 
} 
}

 

Acum , traversam toate rolurile inserate din tutorialul anterior iar apoi le vom asocia cu id-urile rolurilor din care face parte utilizatorul curent : 

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

char bufferLine[MAX_STRING_LINE]; // Cu spatiu alocat explicit mai sus 
while ((fgets(bufferLine, MAX_STRING_LINE, inputTxt)) != NULL) // Citim Linie cu Linie din Fisier

{ 
char*Token, *nextToken; // Prima va retine informatia curenta iar cealalta referinta la cea mai ramas din linie 

// Inseram Date 

/// ID 
Token = strtok_s(bufferLine,DELIMITATER,&nextToken); // Preluam ID-ul din linie in format de STRING 
newUser[i-1].id = atoi(Token); // Convertim din string in intreg + Il Incarcam in Structura 

// NUME
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMELE din linie in format de STRING
allocAndFetchString(&newUser[i-1].username,Token); // Il Incarcam in Structura 

// Adresa de Mail 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].email, Token); // Il Incarcam in Structura 

// Parola 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].password, Token); // Il Incarcam in Structura 

// NrPosts 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMARUL DE POSTURI DIN STRUCTURA 
newUser[i-1].nrPosts = atoi(Token); // CONVERTIM DIN STRING IN INTREG SI IL INCARCAM 

// e Banat 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // IDEM 
newUser[i-1].isBanned = atoi(Token); 

// Grupurile din care face parte 

unsigned j = 1; // (Accesul la itemul curent se va face la (j-1)) 
while ((Token = strtok_s(NULL, DELIMITATER, &nextToken)) != NULL) // Preluam ID cu ID 

{ 
unsigned targetIdRole, k = 0; 
targetIdRole = atoi(Token); // Convertim ID-ul din string in intreg 

do {

if (targetIdRole == roles[k].id) // DACA AM REGASIT ID-UL IN COLECTIE DE ROLURI 
{ 

newUser[i-1].roles = j == 1 ? 
(Role*)malloc(sizeof(Role)) // DACA SUNTEM LA PRIMA ITERATIE 
: (Role*)realloc(newUser[i-1].roles, sizeof(Role) * j); // DACA SUNTEM LA J ITERATIE 

// Preluam Referintele (ODATA ELIBERATE ROLURILE SI ELE ISI VOR PIERDE INFORMATIA ) 
newUser[i-1].roles[j-1].id = roles[k].id; 
newUser[i-1].roles[j-1].nameRole = roles[k].nameRole; 
newUser[i-1].roles[j-1].isLastRole = 0; // NU ESTE ULTIMUL ELEMENT DIN LISTA 
break; // NU ARE ROST SA PARCURGEM TOATA STRUCTURA 
}

k++; // Incrementam cu un pas 
} 
while(!roles[k-1].isLastRole); // Parcurgem toate rolurile PANA AJUNGEM LA ULTIMUL 
j++; // Marim colectia cu un element 
} 
newUser[i-1].roles[j-2].isLastRole = 1; // ESTE ULTIMUL ELEMENT DIN LISTA 
} 
}

 

Verificam daca mai avem continut in fisier . Daca da , vom realoca un nou utilizatori (in functie de cati utilizatori sunt introdusi in fisier) . Daca nu , se opreste iteratia si se returneaza ce am adaugat : 

 

// user.c 

User* fetchUsersFromTxtFile(FILE* inputTxt, Role* roles) // Implementam functia de inserare a datelor 

{ 
if (!checkFile(inputTxt)) 
return NULL; // Fisier Invalid + Returnam o valoare NULA Colectiei 

unsigned i = 1; // Pentru accesarea elementelor curente va fi (i-1) 
User* newUser = (User*)malloc(sizeof(User)); // Alocam pentru un utilizator (MOMENTAN) 

char bufferLine[MAX_STRING_LINE]; // Cu spatiu alocat explicit mai sus 
while ((fgets(bufferLine, MAX_STRING_LINE, inputTxt)) != NULL) // Citim Linie cu Linie din Fisier

{ 
char*Token, *nextToken; // Prima va retine informatia curenta iar cealalta referinta la cea mai ramas din linie 

// Inseram Date 

/// ID 
Token = strtok_s(bufferLine,DELIMITATER,&nextToken); // Preluam ID-ul din linie in format de STRING 
newUser[i-1].id = atoi(Token); // Convertim din string in intreg + Il Incarcam in Structura 

// NUME
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMELE din linie in format de STRING
allocAndFetchString(&newUser[i-1].username,Token); // Il Incarcam in Structura 

// Adresa de Mail 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].email, Token); // Il Incarcam in Structura 

// Parola 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam EMAIL din linie in format de STRING 
allocAndFetchString(&newUser[i-1].password, Token); // Il Incarcam in Structura 

// NrPosts 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // Preluam NUMARUL DE POSTURI DIN STRUCTURA 
newUser[i-1].nrPosts = atoi(Token); // CONVERTIM DIN STRING IN INTREG SI IL INCARCAM 

// e Banat 
Token = strtok_s(NULL, DELIMITATER, &nextToken); // IDEM 
newUser[i-1].isBanned = atoi(Token); 

// Grupurile din care face parte 

unsigned j = 1; // (Accesul la itemul curent se va face la (j-1)) 
while ((Token = strtok_s(NULL, DELIMITATER, &nextToken)) != NULL) // Preluam ID cu ID 

{ 
unsigned targetIdRole, k = 0; 
targetIdRole = atoi(Token); // Convertim ID-ul din string in intreg 

do {

if (targetIdRole == roles[k].id) // DACA AM REGASIT ID-UL IN COLECTIE DE ROLURI 
{ 

newUser[i-1].roles = j == 1 ? 
(Role*)malloc(sizeof(Role)) // DACA SUNTEM LA PRIMA ITERATIE 
: (Role*)realloc(newUser[i-1].roles, sizeof(Role) * j); // DACA SUNTEM LA J ITERATIE 

// Preluam Referintele (ODATA ELIBERATE ROLURILE SI ELE ISI VOR PIERDE INFORMATIA ) 
newUser[i-1].roles[j-1].id = roles[k].id; 
newUser[i-1].roles[j-1].nameRole = roles[k].nameRole; 
newUser[i-1].roles[j-1].isLastRole = 0; // NU ESTE ULTIMUL ELEMENT DIN LISTA 
break; // NU ARE ROST SA PARCURGEM TOATA STRUCTURA 
}

k++; // Incrementam cu un pas 
} 
while(!roles[k-1].isLastRole); // Parcurgem toate rolurile PANA AJUNGEM LA ULTIMUL 
j++; // Marim colectia cu un element 
} 
newUser[i-1].roles[j-2].isLastRole = 1; // ESTE ULTIMUL ELEMENT DIN LISTA 

if (!feof(inputTxt)) // Daca mai sunt date in fisier 
newUser = realloc(newUser, sizeof(User) * (++i)); // Mai alocam un utilizator 
} 
return newUser; // Returnam colectia 
}

 

IN TUTORIALUL ACESTA NU VOI INTRODUCE IN LISTE , UTILIZATORI  DAR VOM AFISA CONTINUTUL SI LE VOM STERGE (SUB FORMA DE ARRAY) (ASA CA BONUS  ) 

 

Declaram functiile de afisare si stergere : 

 

// User.h 

// Restu codului

void printUser(User user); 
void freeUser(User user);

 

Le implementam : 

 

/// User.c 

// Restu Codului 

void printUser(User user) 
{ 

static unsigned i = 0; // Retinem i pe toate apelurile 
printf_s("User %d : { \n",i); 
printf_s("\tID UTILIZATOR: %d ,\n",user.id); 
printf_s("\tNUME UTILIZATOR: %s ,\n",user.username); 
printf_s("\tADRESA MAIL UTILIZATOR: %s ,\n",user.email); 
printf_s("\tPAROLA : %s ,\n",user.password); 
printf_s("\tNUMAR POSTURI : %d ,\n", user.nrPosts); 
printf_s("\tESTE BANAT : %s ,\n", user.isBanned ? "DA" : "NU"); 
printf_s("\t\tROLURI DETINUTE : {\n"); 
unsigned j = 0; 
do { 
printf_s("\n\tID ROL : %d | NUME ROL :%s",user.roles[j].id,user.roles[j].nameRole); 
j++; 
} while(!user.roles[j-1].isLastRole); 

printf_s("\n}\n}"); 

i++; 

} 

void freeUser(User user) 
{ 
//Stergem sirurile de caractere 
free(user.username); 
free(user.email); 
free(user.password); 

// ROLURILE VOR FI STERSE SEPARATE in functia freeRoles() 
}

 

Acum , afisam in functia principala utilizatorii iar pe urma , ii stergem : 

 

// entryPoint.c 

/* Legam toate entry point toate headerele dependente */ 
#include "../StructuriDate/roles.h" 
#include "../StructuriDate/fileHandling.h" 
#include "../StructuriDate/User.h" 
#include "../StructuriDate//Lists.h" 

// stringHandling.h nu este dependent de entry point 
// 

// CONSTANTELE MACRO SUNT UTILE CA LE PUTEM UTILIZA LA NIVEL GLOBAL (PE TOT FISIERUL) 
// NOTA** : E O PRACTICA BUNA DE A FOLOSI MACRO SPRE DEOSEBIRE DE CONSTANTE "MAGICE" (NUMERE PRIN COD) 

#define NR_FILES 2 // STABILIM UN NUMAR DE FISIERE 

int main() 
{ 
FILE** file = NULL; 

file = (FILE**)malloc(sizeof(FILE*)*NR_FILES); // DECLARAM O COLECTIE DE FISIERE 

file[0] = openFile(FILENAME_INPUT_ROLES, READMODE_TXT); // CITIM ROLURILE DINTR-UN FISIER TEXT 
file[1] = openFile(FILENAME_USER_INPUT, READMODE_TXT); // CITIM UTILIZATORI DINTR-UN FISIER TEXT 

// dateIntrareBZONE.txt && dateIntrareRoluriBZONE.txt TREBUIE SA EXISTE IN DIRECTOR (ALTFEL VA ESUA) 

Role* roles = fetchRoles(file[0]); // Am primit lista cu rolurile 

User* users = fetchUsersFromTxtFile(file[1],roles); // Am primit informatia despre utilizatori 

for (int i = 0; i < 6; i++) { // CUNOASTEM CA SUNT SASE ENTITATI 
                      printUser(users[i]); // Accesul prin index (Array) --> AFISAM UTILIZATORUL CURENT 
  freeUser(users[i]); // STERGEM UTILIZATORUL CURENT 
  } 
  
  // ELIBERAM RESURSELE 
  freeRoles(&roles); // Eliberam resursele ocupate de entitate 
  free(users); // Stergem colectia 
  closeFiles(&file,NR_FILES); // Eliberam resursele ocupate de fisiere 
  
  if (file == NULL) 
  printf("COLECTIA DE FISIERE A FOST ELIBERATA CU SUCCES !! "); 
  
  return 0; 
  }

 

Si primim ca output : 

 

**FISIERUL dateIntrareRoluriBZONE.txt S-A DESCHIS CU SUCCES !**
**FISIERUL dateIntrareUtilizatoriBZONE.txt S-A DESCHIS CU SUCCES !**
User 0 : {
        ID UTILIZATOR: 0 ,
        NUME UTILIZATOR:  Alin  ,
        ADRESA MAIL UTILIZATOR:  alidsadan@gmail.com  ,
        PAROLA :  dsdasdadsadadc  ,
        NUMAR POSTURI : 100 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 0 | NUME ROL : Membru

        ID ROL : 3 | NUME ROL : Super Moderator

        ID ROL : 4 | NUME ROL : Administrator
        ID ROL : 1 | NUME ROL : Utilizator Premium

}
}User 1 : {
        ID UTILIZATOR: 1 ,
        NUME UTILIZATOR:  Alinds  ,
        ADRESA MAIL UTILIZATOR:  alidasdsan@gmail.com  ,
        PAROLA :  dsavvcvdc  ,
        NUMAR POSTURI : 50 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 0 | NUME ROL : Membru

        ID ROL : 1 | NUME ROL : Utilizator Premium

}
}User 2 : {
        ID UTILIZATOR: 2 ,
        NUME UTILIZATOR:  Alivcxvxcn  ,
        ADRESA MAIL UTILIZATOR:  alidsadvfcn@gmail.com  ,
        PAROLA :  dsewqqewqadc  ,
        NUMAR POSTURI : 550 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 3 | NUME ROL : Super Moderator

        ID ROL : 1 | NUME ROL : Utilizator Premium

}
}User 3 : {
        ID UTILIZATOR: 3 ,
        NUME UTILIZATOR:  Alifdsfn  ,
        ADRESA MAIL UTILIZATOR:  alewewin@gmail.com  ,
        PAROLA :  dsfdsfsdfsadc  ,
        NUMAR POSTURI : 3300 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 0 | NUME ROL : Membru

        ID ROL : 1 | NUME ROL : Utilizator Premium

        ID ROL : 2 | NUME ROL : Moderator

        ID ROL : 3 | NUME ROL : Super Moderator

        ID ROL : 4 | NUME ROL : Administrator
}
}User 4 : {
        ID UTILIZATOR: 4 ,
        NUME UTILIZATOR:  Alie2qweqn  ,
        ADRESA MAIL UTILIZATOR:  alcxsaxcadsain@gmail.com  ,
        PAROLA :  dsvcvvvvadc  ,
        NUMAR POSTURI : 11100 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 0 | NUME ROL : Membru

}
}User 5 : {
        ID UTILIZATOR: 5 ,
        NUME UTILIZATOR:  Alidasdadan  ,
        ADRESA MAIL UTILIZATOR:  alidasdassdadn@gmail.com  ,
        PAROLA :  dsvvvvvvvvradc  ,
        NUMAR POSTURI : 134300 ,
        ESTE BANAT : NU ,
                ROLURI DETINUTE : {

        ID ROL : 0 | NUME ROL : Membru

        ID ROL : 2 | NUME ROL : Moderator

        ID ROL : 1 | NUME ROL : Utilizator Premium

        ID ROL : 3 | NUME ROL : Super Moderator

}
}COLECTIA DE FISIERE A FOST ELIBERATA CU SUCCES !!

 

NOTA : PENTRU A FUNCTIONA ALGORITMUL , SE POT ADAUGA UTILIZATORI LA DISCRETIA VOASTRA DOAR SA PASTREZE MODELUL : 

id | nume | email | parola | nrPosturi | banat(1/0) | (AICI SE ADAUGA ID-URILE ROLURILOR DIN CARE FACE PARTE DESPARTITE DE "|") ==> LINIE

....

....

....

 

AVETI SABLONUL IN ATASAMENT

 

https://easyupload.io/rbosc2

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.