Annotation of rpl/src/transliteration.c, revision 1.39
1.1 bertrand 1: /*
2: ================================================================================
1.37 bertrand 3: RPL/2 (R) version 4.1.3
1.25 bertrand 4: Copyright (C) 1989-2011 Dr. BERTRAND Joël
1.1 bertrand 5:
6: This file is part of RPL/2.
7:
8: RPL/2 is free software; you can redistribute it and/or modify it
9: under the terms of the CeCILL V2 License as published by the french
10: CEA, CNRS and INRIA.
11:
12: RPL/2 is distributed in the hope that it will be useful, but WITHOUT
13: ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14: FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL V2 License
15: for more details.
16:
17: You should have received a copy of the CeCILL License
18: along with RPL/2. If not, write to info@cecill.info.
19: ================================================================================
20: */
21:
22:
1.14 bertrand 23: #include "rpl-conv.h"
24: #include "tex-conv.h"
1.5 bertrand 25:
1.1 bertrand 26: #include <stdarg.h>
1.5 bertrand 27:
1.1 bertrand 28: #undef fprintf
29: #undef printf
30:
31:
32: /*
33: ================================================================================
34: Fonction de translitération
35: ================================================================================
36: Entrées :
37: --------------------------------------------------------------------------------
38: Sorties :
39: --------------------------------------------------------------------------------
40: Effets de bord : néant
41: ================================================================================
42: */
43:
44: unsigned char *
45: transliteration(struct_processus *s_etat_processus,
46: unsigned char *chaine_entree,
47: unsigned char *codage_entree,
48: unsigned char *codage_sortie)
49: {
50: unsigned char *codage_sortie_transliteral;
51: unsigned char *tampon;
52:
53: if ((codage_sortie_transliteral = malloc((strlen(codage_sortie)
54: + strlen("//TRANSLIT") + 1) * sizeof(unsigned char))) == NULL)
55: {
56: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
57: return(NULL);
58: }
59:
60: sprintf(codage_sortie_transliteral, "%s//TRANSLIT", codage_sortie);
61:
62: tampon = reencodage(s_etat_processus, chaine_entree,
63: codage_entree, codage_sortie_transliteral);
64: free(codage_sortie_transliteral);
65:
66: return(tampon);
67: }
68:
69:
70: unsigned char *
71: reencodage(struct_processus *s_etat_processus,
72: unsigned char *chaine_entree,
73: unsigned char *codage_entree,
74: unsigned char *codage_sortie)
75: {
76: # define d_LONGUEUR 1024
77:
78: iconv_t transcodage;
79:
80: size_t ios;
81: size_t longueur_entree;
82: size_t longueur_sortie;
83:
84: unsigned char *buffer_entree;
85: unsigned char *buffer_sortie;
86: unsigned char *chaine_sortie;
87: unsigned char *pointeur;
88: unsigned char *tampon;
89:
90: if ((transcodage = iconv_open(codage_sortie, codage_entree)) ==
91: (iconv_t) -1)
92: {
93: (*s_etat_processus).erreur_execution = d_ex_erreur_transcodage;
94: return(NULL);
95: }
96:
97: buffer_entree = chaine_entree;
98: longueur_entree = strlen(chaine_entree);
99:
100: if ((chaine_sortie = malloc(sizeof(unsigned char))) == NULL)
101: {
102: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
103: return(NULL);
104: }
105:
106: chaine_sortie[0] = d_code_fin_chaine;
107:
1.39 ! bertrand 108: if ((buffer_sortie = malloc((d_LONGUEUR + 1) * sizeof(unsigned char)))
! 109: == NULL)
1.1 bertrand 110: {
111: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
112: return(NULL);
113: }
114:
115: do
116: {
117: longueur_sortie = d_LONGUEUR;
118: pointeur = buffer_sortie;
119:
120: if ((ios = iconv(transcodage, (char **) &buffer_entree,
121: &longueur_entree, (char **) &pointeur, &longueur_sortie))
122: == (size_t) -1)
123: {
124: // On autorise les erreurs EINVAL et EILSEQ
125: if (errno == EILSEQ)
126: {
127: free(buffer_sortie);
1.3 bertrand 128: free(chaine_sortie);
129:
1.1 bertrand 130: (*s_etat_processus).erreur_execution = d_ex_erreur_transcodage;
131: return(NULL);
132: }
133: }
134:
1.38 bertrand 135: tampon = chaine_sortie;
1.1 bertrand 136: (*pointeur) = d_code_fin_chaine;
137:
138: if ((chaine_sortie = malloc((strlen(tampon) + strlen(buffer_sortie) + 1)
139: * sizeof(unsigned char))) == NULL)
140: {
141: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
142: return(NULL);
143: }
144:
145: sprintf(chaine_sortie, "%s%s", tampon, buffer_sortie);
146: free(tampon);
147: } while((*buffer_entree) != d_code_fin_chaine);
148:
149: free(buffer_sortie);
150: iconv_close(transcodage);
151:
152: return(chaine_sortie);
153: }
154:
155:
156: /*
157: ================================================================================
158: Fonctions spécifiques
159: ================================================================================
160: Entrées :
161: --------------------------------------------------------------------------------
1.26 bertrand 162: Sorties :
1.1 bertrand 163: --------------------------------------------------------------------------------
164: Effets de bord : néant
165: ================================================================================
166: */
167:
168: void
169: localisation_courante(struct_processus *s_etat_processus)
170: {
171: char **arguments;
172:
173: int ios;
174: int pipes_entree[2];
175: int pipes_erreur[2];
176: int pipes_sortie[2];
177: int status;
178:
179: long i;
180: long nombre_arguments;
181:
182: pid_t pid;
183:
184: sigset_t oldset;
185: sigset_t set;
186:
187: struct sigaction action_passee;
188:
189: unsigned char *tampon;
190:
191: unsigned long longueur_lecture;
192: unsigned long nombre_iterations;
193: unsigned long pointeur;
194:
195: if ((arguments = malloc(3 * sizeof(char **))) == NULL)
196: {
197: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
198: return;
199: }
200:
201: if ((arguments[0] = malloc((strlen("locale") + 1) * sizeof(char))) == NULL)
202: {
203: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
204: return;
205: }
206:
207: if ((arguments[1] = malloc((strlen("charmap") + 1) * sizeof(char))) == NULL)
208: {
209: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
210: return;
211: }
212:
213: strcpy(arguments[0], "locale");
214: strcpy(arguments[1], "charmap");
215: arguments[2] = NULL;
216:
217: nombre_arguments = 2;
218:
219: if (pipe(pipes_entree) != 0)
220: {
221: (*s_etat_processus).erreur_systeme = d_es_processus;
222: return;
223: }
224:
225: if (pipe(pipes_sortie) != 0)
226: {
227: (*s_etat_processus).erreur_systeme = d_es_processus;
228: return;
229: }
230:
231: if (pipe(pipes_erreur) != 0)
232: {
233: (*s_etat_processus).erreur_systeme = d_es_processus;
234: return;
235: }
236:
237: sigfillset(&set);
238: pthread_sigmask(SIG_BLOCK, &set, &oldset);
239:
240: verrouillage_threads_concurrents(s_etat_processus);
241: pid = fork();
242: deverrouillage_threads_concurrents(s_etat_processus);
243:
244: pthread_sigmask(SIG_SETMASK, &oldset, NULL);
245: sigpending(&set);
246:
247: if (pid < 0)
248: {
249: if (close(pipes_entree[0]) != 0)
250: {
251: (*s_etat_processus).erreur_systeme = d_es_processus;
252: return;
253: }
254:
255: if (close(pipes_entree[1]) != 0)
256: {
257: (*s_etat_processus).erreur_systeme = d_es_processus;
258: return;
259: }
260:
261: if (close(pipes_sortie[0]) != 0)
262: {
263: (*s_etat_processus).erreur_systeme = d_es_processus;
264: return;
265: }
266:
267: if (close(pipes_sortie[1]) != 0)
268: {
269: (*s_etat_processus).erreur_systeme = d_es_processus;
270: return;
271: }
272:
273: if (close(pipes_erreur[0]) != 0)
274: {
275: (*s_etat_processus).erreur_systeme = d_es_processus;
276: return;
277: }
278:
279: if (close(pipes_erreur[1]) != 0)
280: {
281: (*s_etat_processus).erreur_systeme = d_es_processus;
282: return;
283: }
284:
285: (*s_etat_processus).erreur_systeme = d_es_processus;
286: return;
287: }
288: else if (pid == 0)
289: {
290: if (close(pipes_entree[1]) != 0)
291: {
292: (*s_etat_processus).erreur_systeme = d_es_processus;
293: return;
294: }
295:
296: if (close(pipes_sortie[0]) != 0)
297: {
298: (*s_etat_processus).erreur_systeme = d_es_processus;
299: return;
300: }
301:
302: if (close(pipes_erreur[0]) != 0)
303: {
304: (*s_etat_processus).erreur_systeme = d_es_processus;
305: return;
306: }
307:
308: if (pipes_entree[0] != STDIN_FILENO)
309: {
310: if (dup2(pipes_entree[0], STDIN_FILENO) == -1)
311: {
312: (*s_etat_processus).erreur_systeme = d_es_processus;
313: return;
314: }
315: }
316:
317: if (pipes_sortie[1] != STDOUT_FILENO)
318: {
319: if (dup2(pipes_sortie[1], STDOUT_FILENO) == -1)
320: {
321: (*s_etat_processus).erreur_systeme = d_es_processus;
322: return;
323: }
324: }
325:
326: if (pipes_sortie[1] != STDERR_FILENO)
327: {
328: if (dup2(pipes_sortie[1], STDERR_FILENO) == -1)
329: {
330: (*s_etat_processus).erreur_systeme = d_es_processus;
331: return;
332: }
333: }
334:
335: if (nombre_arguments != 0)
336: {
337: execvp(arguments[0], arguments);
338: }
339: else
340: {
341: exit(EXIT_SUCCESS);
342: }
343:
344: /*
345: * L'appel système execvp() a généré une erreur et n'a pu exécuter
346: * argument[0] (fichier non exécutable ou inexistant).
347: */
348:
349: close(pipes_entree[0]);
350: close(pipes_sortie[1]);
351:
352: for(i = 0; i < nombre_arguments; i++)
353: {
354: free(arguments[i]);
355: }
356:
357: free(arguments);
358: (*s_etat_processus).erreur_systeme = d_es_processus;
359:
360: /*
361: * Envoi d'une erreur dans le pipe idoine. On ne regarde pas
362: * le nombre d'octets écrits car l'erreur ne pourra de toute
363: * façon pas être traitée.
364: */
365:
366: write_atomic(s_etat_processus, pipes_erreur[1], " ", 1);
367: close(pipes_erreur[1]);
368:
369: exit(EXIT_SUCCESS);
370: }
371: else
372: {
373: if (close(pipes_entree[0]) != 0)
374: {
375: (*s_etat_processus).erreur_systeme = d_es_processus;
376: return;
377: }
378:
379: if (close(pipes_sortie[1]) != 0)
380: {
381: (*s_etat_processus).erreur_systeme = d_es_processus;
382: return;
383: }
384:
385: if (close(pipes_erreur[1]) != 0)
386: {
387: (*s_etat_processus).erreur_systeme = d_es_processus;
388: return;
389: }
390:
391: if (close(pipes_entree[1]) != 0)
392: {
393: (*s_etat_processus).erreur_systeme = d_es_processus;
394: return;
395: }
396:
397: do
398: {
399: if (kill(pid, 0) != 0)
400: {
401: break;
402: }
403:
404: /*
405: * Récupération de la valeur de retour du processus détaché
406: */
407:
1.9 bertrand 408: # ifndef SEMAPHORES_NOMMES
409: if (sem_post(&((*s_etat_processus).semaphore_fork)) != 0)
410: # else
411: if (sem_post((*s_etat_processus).semaphore_fork) != 0)
412: # endif
1.1 bertrand 413: {
414: (*s_etat_processus).erreur_systeme = d_es_processus;
415: return;
416: }
417:
418: if (waitpid(pid, &status, 0) == -1)
419: {
1.9 bertrand 420: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 421: if (sem_wait(&((*s_etat_processus).semaphore_fork)) == -1)
1.9 bertrand 422: # else
423: if (sem_wait((*s_etat_processus).semaphore_fork) == -1)
424: # endif
1.1 bertrand 425: {
426: if (errno != EINTR)
427: {
428: (*s_etat_processus).erreur_systeme = d_es_processus;
429: return;
430: }
431: }
432:
433: (*s_etat_processus).erreur_systeme = d_es_processus;
434: return;
435: }
436:
1.9 bertrand 437: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 438: if (sem_wait(&((*s_etat_processus).semaphore_fork)) == -1)
1.9 bertrand 439: # else
440: if (sem_wait((*s_etat_processus).semaphore_fork) == -1)
441: # endif
1.1 bertrand 442: {
443: if (errno != EINTR)
444: {
445: (*s_etat_processus).erreur_systeme = d_es_processus;
446: return;
447: }
448: }
449: } while((!WIFEXITED(status)) && (!WIFSIGNALED(status)));
450:
451: longueur_lecture = 256;
452: pointeur = 0;
453: nombre_iterations = 1;
454:
455: if ((tampon = malloc((longueur_lecture + 1) *
456: sizeof(unsigned char))) == NULL)
457: {
458: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
459: return;
460: }
461:
1.18 bertrand 462: tampon[0] = d_code_fin_chaine;
463:
1.9 bertrand 464: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 465: if (sem_post(&((*s_etat_processus).semaphore_fork)) != 0)
1.9 bertrand 466: # else
467: if (sem_post((*s_etat_processus).semaphore_fork) != 0)
468: # endif
1.1 bertrand 469: {
470: (*s_etat_processus).erreur_systeme = d_es_processus;
471: return;
472: }
473:
474: while((ios = read_atomic(s_etat_processus,
475: pipes_sortie[0], &(tampon[pointeur]),
476: longueur_lecture)) > 0)
477: {
1.9 bertrand 478: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 479: while(sem_wait(&((*s_etat_processus).semaphore_fork)) == -1)
1.9 bertrand 480: # else
481: while(sem_wait((*s_etat_processus).semaphore_fork) == -1)
482: # endif
1.1 bertrand 483: {
484: if (errno != EINTR)
485: {
486: (*s_etat_processus).erreur_systeme = d_es_processus;
487: return;
488: }
489: }
490:
491: tampon[pointeur + ios] = d_code_fin_chaine;
492: pointeur += longueur_lecture;
493: nombre_iterations++;
494:
495: if ((tampon = realloc(tampon,
496: ((nombre_iterations * longueur_lecture) + 1) *
497: sizeof(unsigned char))) == NULL)
498: {
499: (*s_etat_processus).erreur_systeme =
500: d_es_allocation_memoire;
501: return;
502: }
503:
1.9 bertrand 504: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 505: if (sem_post(&((*s_etat_processus).semaphore_fork)) != 0)
1.9 bertrand 506: # else
507: if (sem_post((*s_etat_processus).semaphore_fork) != 0)
508: # endif
1.1 bertrand 509: {
510: (*s_etat_processus).erreur_systeme = d_es_processus;
511: return;
512: }
513: }
514:
1.9 bertrand 515: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 516: while(sem_wait(&((*s_etat_processus).semaphore_fork)) == -1)
1.9 bertrand 517: # else
518: while(sem_wait((*s_etat_processus).semaphore_fork) == -1)
519: # endif
1.1 bertrand 520: {
521: if (errno != EINTR)
522: {
523: (*s_etat_processus).erreur_systeme = d_es_processus;
524: return;
525: }
526: }
527:
528: if (strlen(tampon) == 0)
529: {
530: (*s_etat_processus).erreur_systeme = d_es_processus;
531: return;
532: }
533:
534: tampon[strlen(tampon) - 1] = d_code_fin_chaine;
535:
536: if (ios == -1)
537: {
538: (*s_etat_processus).erreur_systeme = d_es_processus;
539: return;
540: }
541:
542: if (close(pipes_sortie[0]) != 0)
543: {
544: (*s_etat_processus).erreur_systeme = d_es_processus;
545: return;
546: }
547:
1.17 bertrand 548: if (strlen(tampon) > 0)
549: {
550: (*s_etat_processus).localisation = tampon;
551: }
552: else
553: {
554: free(tampon);
555:
556: if (((*s_etat_processus).localisation = malloc((strlen(d_locale)
557: + 1) * sizeof(unsigned char))) == NULL)
558: {
559: (*s_etat_processus).erreur_systeme = d_es_processus;
560: return;
561: }
562:
563: strcpy((*s_etat_processus).localisation, d_locale);
564: }
1.1 bertrand 565:
566: if (sigaction(SIGINT, &action_passee, NULL) != 0)
567: {
568: for(i = 0; i < nombre_arguments; i++)
569: {
570: free(arguments[i]);
571: }
572:
573: free(arguments);
574: (*s_etat_processus).erreur_systeme = d_es_signal;
575: return;
576: }
577:
578: for(i = 0; i < nombre_arguments; i++)
579: {
580: free(arguments[i]);
581: }
582:
583: free(arguments);
584:
1.9 bertrand 585: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 586: if (sem_post(&((*s_etat_processus).semaphore_fork)) != 0)
1.9 bertrand 587: # else
588: if (sem_post((*s_etat_processus).semaphore_fork) != 0)
589: # endif
1.1 bertrand 590: {
591: (*s_etat_processus).erreur_systeme = d_es_processus;
592: return;
593: }
594:
595: if (read_atomic(s_etat_processus, pipes_erreur[0], tampon, 1) > 0)
596: {
597: // Le processus fils renvoie une erreur.
598:
1.18 bertrand 599: free(tampon);
600:
601: if (((*s_etat_processus).localisation = malloc((strlen(d_locale)
602: + 1) * sizeof(unsigned char))) == NULL)
1.1 bertrand 603: {
1.18 bertrand 604: (*s_etat_processus).erreur_systeme = d_es_processus;
605: return;
1.1 bertrand 606: }
607:
1.18 bertrand 608: strcpy((*s_etat_processus).localisation, d_locale);
1.1 bertrand 609: }
610:
1.9 bertrand 611: # ifndef SEMAPHORES_NOMMES
1.1 bertrand 612: while(sem_wait(&((*s_etat_processus).semaphore_fork)) == -1)
1.9 bertrand 613: # else
614: while(sem_wait((*s_etat_processus).semaphore_fork) == -1)
615: # endif
1.1 bertrand 616: {
617: if (errno != EINTR)
618: {
619: (*s_etat_processus).erreur_systeme = d_es_processus;
620: return;
621: }
622: }
623:
624: if (close(pipes_erreur[0]) != 0)
625: {
626: (*s_etat_processus).erreur_systeme = d_es_processus;
627: return;
628: }
629: }
630:
631: return;
632: }
633:
634:
635: int
636: transliterated_fprintf(struct_processus *s_etat_processus, file *flux,
637: const char *format, ...)
638: {
639: int ios;
1.5 bertrand 640:
641: unsigned char *tampon;
1.1 bertrand 642: unsigned char *tampon2;
1.5 bertrand 643:
1.1 bertrand 644: va_list arguments;
645:
646: va_start(arguments, format);
647:
1.17 bertrand 648: # ifdef OS2
649: unsigned char *ptr_e;;
650: unsigned char *ptr_l;;
651: unsigned char *tampon3;
652:
653: unsigned long i;
654: # endif
655:
1.5 bertrand 656: if (valsprintf(&tampon, format, arguments) < 0)
1.1 bertrand 657: {
658: va_end(arguments);
659:
660: if (s_etat_processus != NULL)
661: {
662: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
663: }
664:
665: return(-1);
666: }
667:
668: va_end(arguments);
669:
670: if (s_etat_processus != NULL)
671: {
672: if ((tampon2 = transliteration(s_etat_processus, tampon,
673: d_locale, (*s_etat_processus).localisation)) == NULL)
674: {
675: free(tampon);
676: return(-1);
677: }
678:
679: free(tampon);
680: }
681: else
682: {
683: tampon2 = tampon;
684: }
685:
1.17 bertrand 686: # ifdef OS2
1.23 bertrand 687: if ((flux == stdin) || (flux == stdout))
688: {
689: i = 0;
690: ptr_l = tampon2;
1.17 bertrand 691:
1.23 bertrand 692: while((*ptr_l) != d_code_fin_chaine)
1.17 bertrand 693: {
1.23 bertrand 694: if ((*ptr_l) == '\n')
695: {
696: i++;
697: }
698:
699: ptr_l++;
1.17 bertrand 700: }
701:
1.23 bertrand 702: if ((tampon3 = malloc((strlen(tampon2) + i + 1) *
703: sizeof(unsigned char))) == NULL)
704: {
705: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
706: return(NULL);
707: }
1.17 bertrand 708:
1.23 bertrand 709: ptr_e = tampon3;
710: ptr_l = tampon2;
1.17 bertrand 711:
1.23 bertrand 712: while((*ptr_l) != d_code_fin_chaine)
713: {
714: (*ptr_e) = (*ptr_l);
1.17 bertrand 715:
1.23 bertrand 716: if ((*ptr_l) == '\n')
717: {
718: (*(++ptr_e)) = '\r';
719: ptr_e++;
720: ptr_l++;
721: }
722: else
723: {
724: ptr_e++;
725: ptr_l++;
726: }
1.17 bertrand 727: }
728:
1.23 bertrand 729: (*ptr_e) = d_code_fin_chaine;
1.17 bertrand 730:
1.23 bertrand 731: free(tampon2);
732: tampon2 = tampon3;
733: }
1.17 bertrand 734: # endif
735:
1.1 bertrand 736: # ifdef SunOS
737: while((ios = fprintf(flux, "%s", tampon2)) < 0)
738: {
739: if ((errno != EINTR) && (errno != 0))
740: {
741: break;
742: }
743: }
744: # else
745: ios = fprintf(flux, "%s", tampon2);
746: # endif
747:
748: free(tampon2);
749:
750: return(ios);
751: }
752:
1.5 bertrand 753:
754: int
755: tex_fprintf(struct_processus *s_etat_processus,
756: file *flux, const char *format, ...)
757: {
758: int ios;
759:
760: unsigned char *tampon;
761: unsigned char *tampon2;
762:
763: va_list arguments;
764:
765: va_start(arguments, format);
766:
767: if (valsprintf(&tampon, format, arguments) < 0)
768: {
769: va_end(arguments);
770:
771: if (s_etat_processus != NULL)
772: {
773: (*s_etat_processus).erreur_systeme = d_es_allocation_memoire;
774: }
775:
776: return(-1);
777: }
778:
779: va_end(arguments);
780:
781: if ((tampon2 = transliteration(s_etat_processus, tampon,
782: d_locale, ds_tex_encodage_3)) == NULL)
783: {
784: free(tampon);
785: return(-1);
786: }
787:
788: free(tampon);
789:
790: # ifdef SunOS
791: while((ios = fprintf(flux, "%s", tampon2)) < 0)
792: {
793: if ((errno != EINTR) && (errno != 0))
794: {
795: break;
796: }
797: }
798: # else
799: ios = fprintf(flux, "%s", tampon2);
800: # endif
801:
802: free(tampon2);
803:
804: return(ios);
805: }
806:
1.21 bertrand 807: #undef readline
1.19 bertrand 808:
809: unsigned char *
810: readline_wrapper(unsigned char *invite)
811: {
812: unsigned char *chaine;
813:
814: chaine = readline(invite);
815: printf("\r");
816:
817: return(chaine);
818: }
819:
820:
1.1 bertrand 821: #define fprintf NULL
822: #define printf NULL
823:
824: // vim: ts=4
CVSweb interface <joel.bertrand@systella.fr>