version 1.16.2.3, 2011/05/09 13:52:12
|
version 1.37, 2012/10/04 15:21:26
|
Line 1
|
Line 1
|
/* |
/* |
================================================================================ |
================================================================================ |
RPL/2 (R) version 4.0.24 |
RPL/2 (R) version 4.1.11 |
Copyright (C) 1989-2011 Dr. BERTRAND Joël |
Copyright (C) 1989-2012 Dr. BERTRAND Joël |
|
|
This file is part of RPL/2. |
This file is part of RPL/2. |
|
|
Line 25
|
Line 25
|
|
|
/* |
/* |
================================================================================ |
================================================================================ |
Routine de création d'une nouvelle variable statique |
Routine d'ajout d'un bouchon dans la liste des variables statiques |
================================================================================ |
================================================================================ |
Entrée : |
Entrée : |
-------------------------------------------------------------------------------- |
-------------------------------------------------------------------------------- |
Line 35
|
Line 35
|
================================================================================ |
================================================================================ |
*/ |
*/ |
|
|
|
// Routine ajoutant à la liste des variables statiques créées un 'bouchon' |
|
// qui est un enregistrement dont la variable est NULL. Cela permet |
|
// d'effacer les variables statiques créées dans une expression évaluées car, |
|
// l'adresse de ces variables changeant à chaque évaluation, elles ne sont |
|
// pas utilisables et constituent une fuite de mémoire |
|
|
logical1 |
logical1 |
creation_variable_statique(struct_processus *s_etat_processus, |
ajout_bouchon_variable_statique(struct_processus *s_etat_processus) |
struct_variable_statique *s_variable) |
|
{ |
{ |
struct_variable_statique *s_nouvelle_base; |
struct_liste_variables_statiques *l_tete_liste; |
|
|
long i; |
l_tete_liste = (*s_etat_processus).l_liste_variables_statiques; |
|
|
|
if (((*s_etat_processus).l_liste_variables_statiques = |
|
malloc(sizeof(struct_liste_variables_statiques))) == NULL) |
|
{ |
|
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
|
return(d_erreur); |
|
} |
|
|
(*s_etat_processus).nombre_variables_statiques++; |
(*(*s_etat_processus).l_liste_variables_statiques).variable = NULL; |
|
(*(*s_etat_processus).l_liste_variables_statiques).suivant = l_tete_liste; |
|
(*(*s_etat_processus).l_liste_variables_statiques).precedent = NULL; |
|
(*l_tete_liste).precedent = (*s_etat_processus).l_liste_variables_statiques; |
|
|
if ((*s_etat_processus).nombre_variables_statiques > (*s_etat_processus) |
return(d_absence_erreur); |
.nombre_variables_statiques_allouees) |
} |
{ |
|
// La nouvelle variable statique ne tient pas dans la table courante. |
|
// Il convient donc d'en augmenter la taille. |
|
|
|
if ((*s_etat_processus).nombre_variables_statiques_allouees == 0) |
|
{ |
|
(*s_etat_processus).nombre_variables_statiques_allouees = |
|
(*s_etat_processus).nombre_variables_statiques; |
|
} |
|
else |
|
{ |
|
while((*s_etat_processus).nombre_variables_statiques > |
|
(*s_etat_processus).nombre_variables_statiques_allouees) |
|
{ |
|
(*s_etat_processus).nombre_variables_statiques_allouees *= 2; |
|
} |
|
} |
|
|
|
if ((s_nouvelle_base = realloc((*s_etat_processus) |
/* |
.s_liste_variables_statiques, (*s_etat_processus) |
================================================================================ |
.nombre_variables_statiques_allouees * |
Routine de retrait des variables statiques |
sizeof(struct_variable_statique))) == NULL) |
================================================================================ |
|
Entrée : |
|
-------------------------------------------------------------------------------- |
|
Sortie : |
|
-------------------------------------------------------------------------------- |
|
Effets de bords : néant |
|
================================================================================ |
|
*/ |
|
|
|
// Cette routine libère toutes les variables statiques jusqu'au prochain |
|
// bouchon ou jusqu'à la fin de la liste si aucun bouchon n'est rencontré. |
|
|
|
logical1 |
|
retrait_variables_statiques(struct_processus *s_etat_processus) |
|
{ |
|
unsigned char registre_mode_execution; |
|
|
|
registre_mode_execution = (*s_etat_processus).mode_execution_programme; |
|
|
|
while((*s_etat_processus).l_liste_variables_statiques != NULL) |
|
{ |
|
(*s_etat_processus).mode_execution_programme = |
|
((*(*(*s_etat_processus).l_liste_variables_statiques) |
|
.variable).origine == 'P') ? 'Y' : 'N'; |
|
|
|
if ((*(*s_etat_processus).l_liste_variables_statiques).variable == NULL) |
|
{ |
|
// On vient de tomber sur un bouchon... |
|
(*s_etat_processus).l_liste_variables_statiques = |
|
(*(*s_etat_processus).l_liste_variables_statiques).suivant; |
|
free((*(*s_etat_processus).l_liste_variables_statiques).precedent); |
|
(*(*s_etat_processus).l_liste_variables_statiques).precedent = NULL; |
|
break; |
|
} |
|
|
|
if (retrait_variable_statique(s_etat_processus, (*(*(*s_etat_processus) |
|
.l_liste_variables_statiques).variable).nom, |
|
(*(*(*s_etat_processus).l_liste_variables_statiques).variable) |
|
.variable_statique) == d_erreur) |
{ |
{ |
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
(*s_etat_processus).mode_execution_programme = |
(*s_etat_processus).nombre_variables_statiques--; |
registre_mode_execution; |
return(d_erreur); |
return(d_erreur); |
} |
} |
|
|
(*s_etat_processus).s_liste_variables_statiques = s_nouvelle_base; |
|
} |
} |
|
|
/* |
(*s_etat_processus).mode_execution_programme = registre_mode_execution; |
* Positionnement de la variable statique au bon endroit |
return(d_absence_erreur); |
*/ |
} |
|
|
|
|
|
/* |
|
================================================================================ |
|
Routine de création d'une nouvelle variable statique |
|
================================================================================ |
|
Entrée : |
|
-------------------------------------------------------------------------------- |
|
Sortie : |
|
-------------------------------------------------------------------------------- |
|
Effets de bords : néant |
|
================================================================================ |
|
*/ |
|
|
|
logical1 |
|
creation_variable_statique(struct_processus *s_etat_processus, |
|
struct_variable_statique *s_variable) |
|
{ |
|
struct_arbre_variables *l_variable_courante; |
|
|
|
struct_liste_variables_statiques *l_nouvel_element; |
|
|
|
unsigned char *ptr; |
|
|
// Nous avons (*s_etat_processus).nombre_variables_statiques - 1 variables |
// Ajout de la variable en tête de la liste des variables statiques |
// dans la table qui sera balayée de la fin vers le début. |
|
|
|
if ((*s_etat_processus).nombre_variables_statiques == 1) |
if ((l_nouvel_element = malloc(sizeof(struct_liste_variables_statiques))) |
|
== NULL) |
{ |
{ |
(*s_etat_processus).s_liste_variables_statiques[0] = (*s_variable); |
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
|
return(d_erreur); |
} |
} |
else |
|
|
if (((*l_nouvel_element).variable = malloc(sizeof( |
|
struct_variable_statique))) == NULL) |
{ |
{ |
for(i = (*s_etat_processus).nombre_variables_statiques - 2; i >= 0; i--) |
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
{ |
return(d_erreur); |
if (strcmp((*s_variable).nom, |
} |
(*s_etat_processus).s_liste_variables_statiques[i].nom) < 0) |
|
{ |
|
(*s_etat_processus).s_liste_variables_statiques[i + 1] = |
|
(*s_etat_processus).s_liste_variables_statiques[i]; |
|
} |
|
else |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
(*s_etat_processus).s_liste_variables_statiques[i + 1] = (*s_variable); |
(*(*l_nouvel_element).variable) = (*s_variable); |
|
(*l_nouvel_element).suivant = (*s_etat_processus) |
|
.l_liste_variables_statiques; |
|
(*(*s_etat_processus).l_liste_variables_statiques).precedent |
|
= l_nouvel_element; |
|
(*l_nouvel_element).precedent = NULL; |
|
(*s_etat_processus).l_liste_variables_statiques = l_nouvel_element; |
|
|
|
// Ajout de la variable à la feuille statique de l'arbre des variables |
|
|
|
BUG((*s_etat_processus).s_arbre_variables == NULL, |
|
uprintf("(*s_etat_processus).s_arbre_variables=NULL\n")); |
|
|
|
l_variable_courante = (*s_etat_processus).s_arbre_variables; |
|
ptr = (*s_variable).nom; |
|
|
|
while((*ptr) != d_code_fin_chaine) |
|
{ |
|
BUG((*s_etat_processus).pointeurs_caracteres_variables[*ptr] < 0, |
|
uprintf("Variable=\"%s\", (*ptr)='%c'\n", (*s_variable).nom, |
|
*ptr)); |
|
|
|
// La feuille doit préexister car la variable statique est toujours |
|
// créée depuis une variable locale. |
|
|
|
BUG((*l_variable_courante).noeuds[(*s_etat_processus) |
|
.pointeurs_caracteres_variables[*ptr]] == NULL, |
|
uprintf("Variable=\"%s\", (*ptr)='%c', nullified folder\n")); |
|
|
|
l_variable_courante = (*l_variable_courante).noeuds |
|
[(*s_etat_processus).pointeurs_caracteres_variables[*ptr]]; |
|
ptr++; |
} |
} |
|
|
return d_absence_erreur; |
if ((l_nouvel_element = malloc(sizeof(struct_liste_variables_statiques))) |
|
== NULL) |
|
{ |
|
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
|
return(d_erreur); |
|
} |
|
|
|
// Dans la feuille statique de l'arbre des variables, on ne balaie |
|
// les variables que dans l'ordre. Le champ 'reference' est alors utilisé |
|
// pour sauvegarder une référence vers la liste des variables statiques |
|
// pour pouvoir purger l'élément en cas de besoin. |
|
|
|
(*l_nouvel_element).suivant = (*l_variable_courante).feuille_statique; |
|
(*l_nouvel_element).precedent = NULL; |
|
(*(*l_nouvel_element).suivant).precedent = l_nouvel_element; |
|
|
|
(*l_nouvel_element).reference = |
|
(*s_etat_processus).l_liste_variables_statiques; |
|
(*l_nouvel_element).variable = (*(*s_etat_processus) |
|
.l_liste_variables_statiques).variable; |
|
(*l_variable_courante).feuille_statique = l_nouvel_element; |
|
return(d_absence_erreur); |
} |
} |
|
|
|
|
Line 128 logical1
|
Line 231 logical1
|
retrait_variable_statique(struct_processus *s_etat_processus, |
retrait_variable_statique(struct_processus *s_etat_processus, |
unsigned char *nom_variable, union_position_variable position) |
unsigned char *nom_variable, union_position_variable position) |
{ |
{ |
struct_variable_statique *s_nouvelle_base; |
struct_liste_variables_statiques *l_element_a_supprimer; |
|
struct_liste_variables_statiques *l_element_liste_a_supprimer; |
logical1 erreur; |
|
|
|
unsigned long position_courante; |
logical1 erreur; |
unsigned long position_supprimee; |
|
|
|
if (recherche_variable_statique(s_etat_processus, nom_variable, |
if ((l_element_a_supprimer = recherche_variable_statique(s_etat_processus, |
position, ((*s_etat_processus).mode_execution_programme == 'Y') |
nom_variable, position, ((*s_etat_processus) |
? 'P' : 'E') == d_vrai) |
.mode_execution_programme == 'Y') ? 'P' : 'E')) != NULL) |
{ |
{ |
if ((*s_etat_processus).nombre_variables_statiques < |
// (*s_etat_processus).pointeur_variable_statique_courante |
((*s_etat_processus).nombre_variables_statiques_allouees / 2)) |
// pointe sur la variable à éliminer. Cette variable est celle qui |
{ |
// est présente dans l'une des feuilles statiques de l'arbre des |
(*s_etat_processus).nombre_variables_statiques_allouees /= 2; |
// variables. |
|
|
if ((s_nouvelle_base = |
l_element_liste_a_supprimer = (*l_element_a_supprimer).reference; |
realloc((*s_etat_processus).s_liste_variables_statiques, |
|
(*s_etat_processus).nombre_variables_statiques_allouees * |
// Suppression de la liste des variables statiques |
sizeof(struct_variable_statique))) == NULL) |
|
{ |
if ((*l_element_liste_a_supprimer).precedent != NULL) |
(*s_etat_processus).erreur_systeme = d_es_allocation_memoire; |
{ |
return(d_erreur); |
(*(*l_element_liste_a_supprimer).precedent).suivant = |
} |
(*l_element_liste_a_supprimer).suivant; |
|
} |
|
else |
|
{ |
|
(*(*l_element_liste_a_supprimer).suivant).precedent = NULL; |
|
} |
|
|
(*s_etat_processus).s_liste_variables_statiques = s_nouvelle_base; |
if ((*l_element_liste_a_supprimer).suivant != NULL) |
|
{ |
|
(*(*l_element_liste_a_supprimer).suivant).precedent = |
|
(*l_element_liste_a_supprimer).precedent; |
|
} |
|
else |
|
{ |
|
(*(*l_element_liste_a_supprimer).precedent).suivant = NULL; |
} |
} |
|
|
position_supprimee = (*s_etat_processus) |
free(l_element_liste_a_supprimer); |
.position_variable_statique_courante; |
|
|
|
liberation(s_etat_processus, (*s_etat_processus) |
// Suppression depuis la feuille statique. Le champ 'precedent' ne sert |
.s_liste_variables_statiques[position_supprimee].objet); |
// pas car la liste est simplement chaînée. |
free((*s_etat_processus).s_liste_variables_statiques |
|
[position_supprimee].nom); |
|
|
|
(*s_etat_processus).nombre_variables_statiques--; |
if ((*l_element_a_supprimer).precedent != NULL) |
|
{ |
|
(*(*l_element_a_supprimer).precedent).suivant = |
|
(*l_element_a_supprimer).suivant; |
|
} |
|
else |
|
{ |
|
(*(*l_element_a_supprimer).suivant).precedent = NULL; |
|
} |
|
|
for(position_courante = position_supprimee; position_courante < |
if ((*l_element_a_supprimer).suivant != NULL) |
(*s_etat_processus).nombre_variables_statiques; |
{ |
position_courante++) |
(*(*l_element_a_supprimer).suivant).precedent = |
|
(*l_element_a_supprimer).precedent; |
|
} |
|
else |
{ |
{ |
(*s_etat_processus).s_liste_variables_statiques[position_courante] |
(*(*l_element_a_supprimer).precedent).suivant = NULL; |
= (*s_etat_processus).s_liste_variables_statiques |
|
[position_courante + 1]; |
|
} |
} |
|
|
|
liberation(s_etat_processus, (*(*l_element_a_supprimer).variable) |
|
.objet); |
|
free((*(*l_element_a_supprimer).variable).nom); |
|
free((*l_element_a_supprimer).variable); |
|
free(l_element_a_supprimer); |
|
|
erreur = d_absence_erreur; |
erreur = d_absence_erreur; |
} |
} |
else |
else |
Line 199 retrait_variable_statique(struct_process
|
Line 324 retrait_variable_statique(struct_process
|
================================================================================ |
================================================================================ |
*/ |
*/ |
|
|
logical1 |
struct_liste_variables_statiques * |
recherche_variable_statique(struct_processus *s_etat_processus, |
recherche_variable_statique(struct_processus *s_etat_processus, |
unsigned char *nom_variable, union_position_variable position, |
unsigned char *nom_variable, union_position_variable position, |
unsigned char origine) |
unsigned char origine) |
{ |
{ |
logical1 existence_variable; |
int pointeur; |
|
|
long difference; |
struct_arbre_variables *l_variable_courante; |
long difference_inferieure; |
struct_liste_variables_statiques *l_element_courant; |
long difference_superieure; |
|
|
|
unsigned long borne_inferieure; |
|
unsigned long borne_superieure; |
|
unsigned long moyenne; |
|
unsigned long nombre_iterations_maximal; |
|
unsigned long ordre_iteration; |
|
|
|
if ((*s_etat_processus).nombre_variables_statiques == 0) |
unsigned char *ptr; |
{ |
|
(*s_etat_processus).erreur_systeme = d_es_variable_introuvable; |
|
return d_faux; |
|
} |
|
|
|
ordre_iteration = 0; |
|
nombre_iterations_maximal = ((unsigned long) |
|
(log((*s_etat_processus).nombre_variables_statiques) / log(2))) + 2; |
|
|
|
borne_inferieure = 0; |
l_variable_courante = (*s_etat_processus).s_arbre_variables; |
borne_superieure = (*s_etat_processus).nombre_variables_statiques - 1; |
ptr = nom_variable; |
|
|
do |
while((*ptr) != d_code_fin_chaine) |
{ |
{ |
moyenne = (borne_inferieure + borne_superieure) / 2; |
pointeur = (*s_etat_processus).pointeurs_caracteres_variables[*ptr]; |
ordre_iteration++; |
|
|
|
if ((2 * ((unsigned long) ((borne_inferieure + borne_superieure) / 2))) |
if (pointeur < 0) |
== (borne_inferieure + borne_superieure)) |
|
{ |
{ |
difference = strcmp(nom_variable, |
// Caractère hors de l'alphabet des variables |
((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].nom); |
|
|
|
if (difference != 0) |
(*s_etat_processus).erreur_systeme = d_es_variable_introuvable; |
{ |
return(NULL); |
if (difference > 0) |
|
{ |
|
borne_inferieure = moyenne; |
|
} |
|
else |
|
{ |
|
borne_superieure = moyenne; |
|
} |
|
} |
|
} |
} |
else |
|
{ |
|
difference_inferieure = strcmp(nom_variable, |
|
((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].nom); |
|
difference_superieure = strcmp(nom_variable, |
|
((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne + 1].nom); |
|
|
|
if (difference_inferieure == 0) |
if ((*l_variable_courante).noeuds[pointeur] == NULL) |
{ |
{ |
difference = 0; |
// Le chemin de la variable candidate n'existe pas. |
} |
(*s_etat_processus).erreur_systeme = d_es_variable_introuvable; |
else if (difference_superieure == 0) |
return(NULL); |
{ |
|
difference = 0; |
|
moyenne++; |
|
} |
|
else |
|
{ |
|
difference = difference_inferieure; |
|
|
|
if (difference > 0) |
|
{ |
|
borne_inferieure = moyenne; |
|
} |
|
else |
|
{ |
|
borne_superieure = moyenne; |
|
} |
|
} |
|
} |
} |
} while((difference != 0) && |
|
(ordre_iteration <= nombre_iterations_maximal)); |
|
|
|
if (ordre_iteration > nombre_iterations_maximal) |
l_variable_courante = (*l_variable_courante).noeuds[pointeur]; |
{ |
ptr++; |
existence_variable = d_faux; |
|
(*s_etat_processus).erreur_systeme = d_es_variable_introuvable; |
|
} |
} |
else |
|
|
if ((*l_variable_courante).feuille_statique != NULL) |
{ |
{ |
// Reste à rechercher la variable déclarée à la position 'position'... |
// Il existe au moins une variable statique du nom requis. |
|
|
if (((*s_etat_processus).s_liste_variables_statiques)[moyenne] |
l_element_courant = (*l_variable_courante).feuille_statique; |
.origine == 'P') |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques)[moyenne] |
|
.variable_statique.adresse == position.adresse) && |
|
(((*s_etat_processus).s_liste_variables_statiques)[moyenne] |
|
.origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
} |
|
else |
|
{ |
|
existence_variable = d_faux; |
|
} |
|
} |
|
else |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques)[moyenne] |
|
.variable_statique.pointeur == position.pointeur) && |
|
(((*s_etat_processus).s_liste_variables_statiques)[moyenne] |
|
.origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
} |
|
else |
|
{ |
|
existence_variable = d_faux; |
|
} |
|
} |
|
|
|
if (existence_variable == d_faux) |
while(l_element_courant != NULL) |
{ |
{ |
// On rembobine. |
if ((*(*l_element_courant).variable).origine == 'P') |
|
|
if (moyenne > 0) |
|
{ |
{ |
while(strcmp(nom_variable, ((*s_etat_processus) |
if (((*(*l_element_courant).variable).variable_statique.adresse |
.s_liste_variables_statiques)[moyenne - 1].nom) == 0) |
== position.adresse) && |
|
((*(*l_element_courant).variable).origine == origine)) |
{ |
{ |
moyenne--; |
(*s_etat_processus).pointeur_variable_statique_courante |
|
= (*l_element_courant).variable; |
if (moyenne == 0) |
return(l_element_courant); |
{ |
|
break; |
|
} |
|
} |
} |
} |
} |
|
else |
// Un petit test pour voir si le premier élément du tableau |
|
// peut correspondre au critère de recherche. |
|
|
|
existence_variable = d_faux; |
|
|
|
if (strcmp(((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].nom, nom_variable) == 0) |
|
{ |
|
if (((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].origine == 'P') |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].variable_statique.adresse |
|
== position.adresse) && |
|
(((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
} |
|
} |
|
else |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].variable_statique.pointeur |
|
== position.pointeur) && |
|
(((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
} |
|
} |
|
} |
|
|
|
// Puis on balaye dans le sens croissant. |
|
|
|
if (((moyenne + 1) < (*s_etat_processus) |
|
.nombre_variables_statiques) && |
|
(existence_variable == d_faux)) |
|
{ |
{ |
while(strcmp(((*s_etat_processus) |
if (((*(*l_element_courant).variable).variable_statique.pointeur |
.s_liste_variables_statiques)[moyenne + 1].nom, |
== position.pointeur) && |
nom_variable) == 0) |
((*(*l_element_courant).variable).origine == origine)) |
{ |
{ |
moyenne++; |
(*s_etat_processus).pointeur_variable_statique_courante |
|
= (*l_element_courant).variable; |
if (((*s_etat_processus).s_liste_variables_statiques) |
return(l_element_courant); |
[moyenne].origine == 'P') |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].variable_statique.adresse == |
|
position.adresse) && (((*s_etat_processus) |
|
.s_liste_variables_statiques) |
|
[moyenne].origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
if ((((*s_etat_processus).s_liste_variables_statiques) |
|
[moyenne].variable_statique.pointeur == |
|
position.pointeur) && (((*s_etat_processus) |
|
.s_liste_variables_statiques) |
|
[moyenne].origine == origine)) |
|
{ |
|
existence_variable = d_vrai; |
|
break; |
|
} |
|
} |
|
|
|
if ((moyenne + 1) >= (*s_etat_processus) |
|
.nombre_variables_statiques) |
|
{ |
|
break; |
|
} |
|
} |
} |
} |
} |
} |
} |
|
|
(*s_etat_processus).position_variable_statique_courante = moyenne; |
|
} |
} |
|
|
return existence_variable; |
(*s_etat_processus).pointeur_variable_statique_courante = NULL; |
|
return(NULL); |
} |
} |
|
|
// vim: ts=4 |
// vim: ts=4 |