Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Hyundai Ioniq 9: dopo due settimane di test non avremmo voluto restituirla
Hyundai Ioniq 9: dopo due settimane di test non avremmo voluto restituirla
Una ammiraglia elettrica, da sei o sette posti, con il comfort di viaggio come missione assoluta. Un'auto non per tutti, ma di cui ti innamori dopo pochi giorni, ecco com'è andata la nostra prova di lunga durata
LG UltraGear evo GM9: 27 pollici, 5K, Mini LED e Dual Mode
LG UltraGear evo GM9: 27 pollici, 5K, Mini LED e Dual Mode
Hyper Mini LED con 2.304 zone, DisplayPort 2.1 e Dual Mode: 5K a 165 Hz oppure QHD a 330 Hz: LG propone un monitor da gioco ma dalla buona versatilità creativa, con prestazioni interessanti e un listino di 1.220 euro. Peccato l'alimentatore ingombrante
Motorola edge 70 Fusion FIFA World Cup 26 Edition: un ottimo smartphone per i fan del calcio
Motorola edge 70 Fusion FIFA World Cup 26 Edition: un ottimo smartphone per i fan del calcio
Motorola edge 70 Fusion FIFA World Cup 26 Edition porta la maglia iridata su uno smartphone accessibile: display Extreme AMOLED da 6,78 pollici a 144Hz, fotocamera Sony LYTIA 710 con moto ai, Snapdragon 7s Gen 3 abbinato a 8GB di RAM, batteria da 5200 mAh con ricarica TurboPower a 68W e certificazione IP68/IP69, il tutto in una veste PANTONE Silhouette con logo dorato a 24 carati, moto buds bass incluse in confezione e contenuti software dedicati ai Mondiali 2026
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 10-03-2007, 14:29   #1
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
[C] Buffer di caratteri: inizializzarlo & funzioni per stringhe

Non so se è corretto, ma io faccio così:
Codice:
titolo_str[0]='\0';
strcpy(titolo_str, "##### TITOLO DEL ITEM #####\n");
Forse è meglio usare la strcat?!
Secondo me no, perchè se il buffer è sporco, nel concatenare può fare dei casini.

Ammesso che pulire/ripulire/inizializzare 1 buffer di caratteri si faccia come faccio io.
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 14:59   #2
xorshadow
Member
 
L'Avatar di xorshadow
 
Iscritto dal: Feb 2007
Messaggi: 38
Per inizializzare un buffer si fa così di solito:

Codice:
memset(buffer, 0, DIMENSIONE_BUFFER);
riempie TUTTO il buffer di zeri e non solo il primo carattere.

Per riempirlo è meglio che usi la versione sicura di strcpy,ovvero strncpy che ti aiuta a prevenire pericolosi buffer overflow.
xorshadow è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 17:42   #3
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Quote:
Originariamente inviato da xorshadow Guarda i messaggi
Per inizializzare un buffer si fa così di solito:

Codice:
memset(buffer, 0, DIMENSIONE_BUFFER);
riempie TUTTO il buffer di zeri e non solo il primo carattere.

Per riempirlo è meglio che usi la versione sicura di strcpy,ovvero strncpy che ti aiuta a prevenire pericolosi buffer overflow.
Io mi son fatto questa funzioncina:
Codice:
void pulisci_str(char *stringa, int lunghezza)
{
int i;

for (i=0; i<lunghezza; ++i) { stringa[i]='\0'; }
}
, perchè secondo me 1 vettore di caratteri va inizzializzato con il carattere nullo \0 no?!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 18:57   #4
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Quote:
Originariamente inviato da Antonio23 Guarda i messaggi
se inizializzi solo il primo carattere, non è detto che gli altri non siano sporchi, per cui si usa memset, che inizializza tutti i caratteri con '\0'
Quindi memset fa già quello che io ho fatto nella mia funzioncina?!
Cancello la mia funzione e uso memset?!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 18:59   #5
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Quote:
Originariamente inviato da xorshadow Guarda i messaggi
Per riempirlo è meglio che usi la versione sicura di strcpy,ovvero strncpy che ti aiuta a prevenire pericolosi buffer overflow.
Ovvero come si usa sta strncopy?!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 22:51   #6
andbin
Senior Member
 
L'Avatar di andbin
 
Iscritto dal: Nov 2005
Messaggi: 5206
Quote:
Originariamente inviato da Matrixbob Guarda i messaggi
Non so se è corretto, ma io faccio così:
Codice:
titolo_str[0]='\0';
strcpy(titolo_str, "##### TITOLO DEL ITEM #####\n");
Forse è meglio usare la strcat?!
Secondo me no, perchè se il buffer è sporco, nel concatenare può fare dei casini.
Non vedo come possa fare casini. strcpy() copia tutti i caratteri della stringa sorgente, nullo compreso.

Es.
Codice:
char str[50];
strcpy (str, "Hello");
strcat (str, " World");
è più che corretto.
__________________
Andrea, Senior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
Java Versions Cheat Sheet
andbin è offline   Rispondi citando il messaggio o parte di esso
Old 10-03-2007, 23:36   #7
xorshadow
Member
 
L'Avatar di xorshadow
 
Iscritto dal: Feb 2007
Messaggi: 38
Quote:
Originariamente inviato da andbin Guarda i messaggi
Non vedo come possa fare casini. strcpy() copia tutti i caratteri della stringa sorgente, nullo compreso.

Es.
Codice:
char str[50];
strcpy (str, "Hello");
strcat (str, " World");
è più che corretto.
Nel tuo esempio è impossibile che ci siano problemi, visto che stai passando al buffer una stringa costante.
Metti il caso, invece, che tu stia facendo un'applicazione di rete e che debba copiare il contenuto di una stringa ricevuta in input dall'utente.
Nel migliore delle ipotesi l'utente ingenuamente passa una stringa più lunga del tuo buffer e il programma va in crash.
Nel peggiore dei casi, invece, trovi un hacker che ti provoca un bel buffer overflow e prende il controllo della tua macchina.
Quindi è buona norma scordarsi completamente della strcpy e usare strncpy che permette di limitare il numero di caratteri copiati nel tuo buffer.
xorshadow è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 08:07   #8
DanieleC88
Senior Member
 
L'Avatar di DanieleC88
 
Iscritto dal: Jun 2002
Città: Dublin
Messaggi: 5989
Quote:
Originariamente inviato da xorshadow Guarda i messaggi
Per inizializzare un buffer si fa così di solito:

Codice:
memset(buffer, 0, DIMENSIONE_BUFFER);
riempie TUTTO il buffer di zeri e non solo il primo carattere.
Alternativamente, invece che malloc(), potrebbe anche usare calloc() al momento dell'allocazione, visto che azzera da sola il contenuto della memoria allocata.
__________________

C'ho certi cazzi Mafa' che manco tu che sei pratica li hai visti mai!
DanieleC88 è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 13:11   #9
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Come diceva qualcuno è 1 apz che cicla con quei buffer che erano variabili globali.
Quindi ho pensato di cambiare tecnica adesso:
ho fatto che togliere i buffer di dimensioni fisse, ed adopero la seguente tenica adesso:

bla bla bla

lunghezza=strlen("");
str_tmp=(char*) calloc(lunghezza, sizeof(char));
sprintf(str_tmp, "e%d = ImageReader(\"no_input.png\",0,250,25.00).ConvertToYV12()\n", count_item);
strcat(enclosure_str, str_tmp);
free(str_tmp);

bla bla bla


Speriamo di non aver fatto 1 !!!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.

Ultima modifica di Matrixbob : 11-03-2007 alle 13:14.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 13:13   #10
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Quote:
Originariamente inviato da DanieleC88 Guarda i messaggi
Alternativamente, invece che malloc(), potrebbe anche usare calloc() al momento dell'allocazione, visto che azzera da sola il contenuto della memoria allocata.
Ehm ... la malloc proprio non l'ha menzionata nessuno.

[PS]
Ma mettere tutti \0 equivale a riempire la stringa di 0?!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.

Ultima modifica di Matrixbob : 11-03-2007 alle 13:15.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 13:21   #11
DanieleC88
Senior Member
 
L'Avatar di DanieleC88
 
Iscritto dal: Jun 2002
Città: Dublin
Messaggi: 5989
Quote:
Originariamente inviato da Matrixbob Guarda i messaggi
Ehm ... la malloc proprio non l'ha menzionata nessuno.

[PS]
Ma mettere tutti \0 equivale a riempire la stringa di 0?!
Primo, dal momento che ti preoccupavi di azzerare la stringa con memset() deducevo che tale operazione non fosse stata già effettuata prima, quindi presumevo che l'allocazione fosse stata fatta con malloc(): facendola con calloc() non hai bisogno di azzerare niente.
Secondo, '\0' è una sequenza di escape, e non va a riempire ogni carattere della stringa con il carattere '0' (ASCII 49), ma con un carattere non visualizzabile che indica un carattere nullo (ASCII 0, usato appunto come terminatore di stringa dalle operazioni del C).
__________________

C'ho certi cazzi Mafa' che manco tu che sei pratica li hai visti mai!
DanieleC88 è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 13:43   #12
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Quote:
Originariamente inviato da DanieleC88 Guarda i messaggi
Primo, dal momento che ti preoccupavi di azzerare la stringa con memset() deducevo che tale operazione non fosse stata già effettuata prima, quindi presumevo che l'allocazione fosse stata fatta con malloc(): facendola con calloc() non hai bisogno di azzerare niente.
La definizione di 1 VAR globale è lo stesso di una malloc?!

Quote:
Originariamente inviato da DanieleC88 Guarda i messaggi
Secondo, '\0' è una sequenza di escape, e non va a riempire ogni carattere della stringa con il carattere '0' (ASCII 49), ma con un carattere non visualizzabile che indica un carattere nullo (ASCII 0, usato appunto come terminatore di stringa dalle operazioni del C).
Ma quindi il memset come l'andavo a richiamare per avere 1 risultato equivalente alla mia funzione:
Codice:
//richiamata
pulisci_str(stringa, SIZEBUF);

//definita
void pulisci_str(char *stringa, int lunghezza)
{
int i;

for (i=0; i<lunghezza; ++i) { stringa[i]='\0'; }
}


Codice:
memset(buffer, 0, DIMENSIONE_BUFFER);
oppure
Codice:
memset(buffer, '\0', DIMENSIONE_BUFFER);
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.

Ultima modifica di Matrixbob : 11-03-2007 alle 13:45.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 13:46   #13
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Ma soprattutto, questa tecnica ora è corretta, funziona?!

bla bla bla

lunghezza=strlen("");
str_tmp=(char*) calloc(lunghezza, sizeof(char));
sprintf(str_tmp, "e%d = ImageReader(\"no_input.png\",0,250,25.00).ConvertToYV12()\n", count_item);
strcat(enclosure_str, str_tmp);
free(str_tmp);

bla bla bla
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 14:50   #14
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
ARRIVANO I GUAI: pare che la sprintf mi scazzi le stringhe!
Pezzo del mio codice:

lunghezza=strlen("##### TITOLO DEL ITEM #####\n");
titolo_str=(char*) calloc(lunghezza, sizeof(char));
strcpy(titolo_str, "##### TITOLO DEL ITEM #####\n");
printf("titolo_str: %s\n", titolo_str);
getchar();

lunghezza=strlen("");
str_tmp=(char*) calloc(lunghezza, sizeof(char));
printf("str_tmp1: %s\n", str_tmp);
printf("canale_titolo: %s", canale_titolo);
getchar();

sprintf(str_tmp, "bottom_subs%d = Subtitle(bottom,\"\"\"%s\\n", count_item, canale_titolo);
printf("str_tmp2: %s\n", str_tmp);
getchar();

strcat(titolo_str, str_tmp);
printf("titolo_str: %s\n", titolo_str);
getchar();

free(str_tmp);



Cavolo mi sono proprio impastato!!!
Qui ho proprio bisogno di 1 vostro aiuto.
Forse le stringhe che hanno a che fare con sprintf è meglio se le lascio di dimensione fissa sovradimensionata?!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.

Ultima modifica di Matrixbob : 11-03-2007 alle 15:05.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 15:13   #15
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Adesso ho fatto così, ma ho paura che scazzi nuovamente quando i %s e %d vengono sostituiti:

lunghezza=strlen("##### TITOLO DEL ITEM #####\n");
titolo_str=(char*) calloc(lunghezza, sizeof(char));
strcpy(titolo_str, "##### TITOLO DEL ITEM #####\n");

lunghezza=strlen("bottom_subs%d = Subtitle(bottom,\"\"\"%s\\n");
str_tmp=(char*) calloc(lunghezza, sizeof(char));

sprintf(str_tmp, "bottom_subs%d = Subtitle(bottom,\"\"\"%s\\n", count_item, canale_titolo);
strcat(titolo_str, str_tmp);
free(str_tmp);

... però attendo fiducioso da voi il giusto verbo!!
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.

Ultima modifica di Matrixbob : 11-03-2007 alle 15:22.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 16:18   #16
xorshadow
Member
 
L'Avatar di xorshadow
 
Iscritto dal: Feb 2007
Messaggi: 38
Quote:
Originariamente inviato da Matrixbob Guarda i messaggi
Adesso ho fatto così, ma ho paura che scazzi nuovamente quando i %s e %d vengono sostituiti:

lunghezza=strlen("##### TITOLO DEL ITEM #####\n");
titolo_str=(char*) calloc(lunghezza, sizeof(char));
strcpy(titolo_str, "##### TITOLO DEL ITEM #####\n");

lunghezza=strlen("bottom_subs%d = Subtitle(bottom,\"\"\"%s\\n");
str_tmp=(char*) calloc(lunghezza, sizeof(char));

sprintf(str_tmp, "bottom_subs%d = Subtitle(bottom,\"\"\"%s\\n", count_item, canale_titolo);
strcat(titolo_str, str_tmp);
free(str_tmp);

... però attendo fiducioso da voi il giusto verbo!!
Il problema in realtà è un pò più complesso.
Tu vuoi copiare una stringa di cui non conosci a priori la lunghezza (a causa della formattazione di sprintf) . Dovresti fare così:
1) Usi snprintf e non sprintf che ti permette di limitare il caratteri copiati (guardati il manuale per sapere come funziona)
2) Algoritmo "serio" : crei un buffer di dimensioni piccole(anche 0 byte) con malloc, copi la stringa con snprintf , guardi il valore di ritorno, se questo è maggiore della dimensione del buffer vuol dire che il buffer non bastava e la stringa è stata troncata, in questo caso devi riallocare il buffer creandolo più grande (pari al valore di ritorno dell snprintf+1) e riprovi a fare la copia.

Ultima modifica di xorshadow : 11-03-2007 alle 17:45.
xorshadow è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 16:39   #17
cionci
Senior Member
 
L'Avatar di cionci
 
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
Quote:
Originariamente inviato da Matrixbob Guarda i messaggi
lunghezza=strlen("");
str_tmp=(char*) calloc(lunghezza, sizeof(char));
sprintf(str_tmp, "e%d = ImageReader(\"no_input.png\",0,250,25.00).ConvertToYV12()\n", count_item);
strcat(enclosure_str, str_tmp);
free(str_tmp);
E' errato...allochi a str_tmp un solo byte...in quanto lunghezza è pari a 1.
L'unico modo per dimensionare in modo corretto la stringa è convertire prima su un buffer temporaneo count_item.

char template[] = "e%d = ImageReader(\"no_input.png\",0,250,25.00).ConvertToYV12()\n";

str_tmp = (char *)malloc(12 * sizeof(char));
sprintf(str_tmp, "%d", count_item);
lunghezza = strlen(str_tmp) + strlen(template) - 2; //-2 perché tolgo i caratteri dovuti a "%d"

free(str_temp);
str_tmp = (char *)malloc((lunghezza + 1) * sizeof(char));
sprintf(str_tmp, template, count_item);

strcat(enclosure_str, str_tmp);
free(str_temp);

Ricordati che perà enclosure_str deve avere abbastanza spazio per contenere anche l'altra stringa..
cionci è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 19:29   #18
xorshadow
Member
 
L'Avatar di xorshadow
 
Iscritto dal: Feb 2007
Messaggi: 38
Come ho già detto prima (versione semplificata e senza error check):

Codice:
nchars = snprintf(NULL, 0, "Quello che %s devi %d formattare", arg1, arg2);
char* buff = malloc(nchars + 1);
snprintf(buff,nchars + 1,"Quello che %s devi %d formattare", arg1, arg2);
Semplice, veloce (la versione non semplificata che ti ho suggerito su può essere più veloce), riutilizzabile. Tutto qui.

Ultima modifica di xorshadow : 11-03-2007 alle 19:32.
xorshadow è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 19:41   #19
cionci
Senior Member
 
L'Avatar di cionci
 
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
Interessante questa snprintf...non la conoscevo. Ho visto che è anche nello standard C99, ottimo
cionci è offline   Rispondi citando il messaggio o parte di esso
Old 11-03-2007, 19:59   #20
Matrixbob
Senior Member
 
L'Avatar di Matrixbob
 
Iscritto dal: Jul 2001
Messaggi: 9947
Bene bene, s'imparano sempre delle belle cose.
C'è anche la matrixNbob function?!
Mi farò 1 bel stamp di questo 3D poi come promemoria.
__________________
Aiuta la ricerca col tuo PC: >>Calcolo distribuito BOINC.Italy: unisciti anche tu<<
Più largo è il sorriso, più affilato è il coltello.
Matrixbob è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Hyundai Ioniq 9: dopo due settimane di test non avremmo voluto restituirla Hyundai Ioniq 9: dopo due settimane di test non ...
LG UltraGear evo GM9: 27 pollici, 5K, Mini LED e Dual Mode LG UltraGear evo GM9: 27 pollici, 5K, Mini LED e...
Motorola edge 70 Fusion FIFA World Cup 26 Edition: un ottimo smartphone per i fan del calcio Motorola edge 70 Fusion FIFA World Cup 26 Editio...
Renault 5 elettrica al giudizio definitivo: solo stupore iniziale o premi meritati? Renault 5 elettrica al giudizio definitivo: solo...
HONOR CHOICE Projector Air Pro: piccolo, portatile e con Netflix HONOR CHOICE Projector Air Pro: piccolo, portati...
Proseguono le operazioni di ripristino d...
Il razzo spaziale NASA SLS e la capsula ...
Starship: il recupero di Ship 40 prosegu...
Prezzi iPhone 17 in aumento: gli ultimi ...
Edge segue Chrome: a rischio molte esten...
Studi su batteri e archaea: la vita sull...
Claude Code, le diverse sessioni ora par...
I sistemi di sicurezza attuali non basta...
iPhone, nuove indiscrezioni sul modello ...
Le iconiche Sony WH-1000XM4 stanno per t...
TV 55 pollici in offerta: Xiaomi 4K a 32...
Robot per le piscine in offerta su Amazo...
Nessun downgrade a Vera e Rubin Ultra, s...
Ho costruito un agente IA locale dentro ...
TOP 10 Amazon del weekend, si inizia col...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 21:17.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Served by www3v