1991-02-19 08:39:46 -04:00
|
|
|
/***********************************************************
|
1992-04-05 11:26:55 -03:00
|
|
|
Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
|
1991-02-19 08:39:46 -04:00
|
|
|
Netherlands.
|
|
|
|
|
|
|
|
All Rights Reserved
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
|
|
documentation for any purpose and without fee is hereby granted,
|
|
|
|
provided that the above copyright notice appear in all copies and that
|
|
|
|
both that copyright notice and this permission notice appear in
|
|
|
|
supporting documentation, and that the names of Stichting Mathematisch
|
|
|
|
Centrum or CWI not be used in advertising or publicity pertaining to
|
|
|
|
distribution of the software without specific, written prior permission.
|
|
|
|
|
|
|
|
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
|
|
|
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
|
|
|
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
|
|
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
******************************************************************/
|
|
|
|
|
1990-10-14 09:07:46 -03:00
|
|
|
/* Module support implementation */
|
|
|
|
|
1990-12-20 11:06:42 -04:00
|
|
|
#include "allobjects.h"
|
1990-10-14 09:07:46 -03:00
|
|
|
#include "modsupport.h"
|
|
|
|
#include "import.h"
|
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
#ifdef HAVE_PROTOTYPES
|
|
|
|
#define USE_STDARG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_STDARG
|
|
|
|
#include <stdarg.h>
|
|
|
|
#else
|
|
|
|
#include <varargs.h>
|
|
|
|
#endif
|
|
|
|
|
1990-10-14 09:07:46 -03:00
|
|
|
|
|
|
|
object *
|
|
|
|
initmodule(name, methods)
|
|
|
|
char *name;
|
|
|
|
struct methodlist *methods;
|
|
|
|
{
|
|
|
|
object *m, *d, *v;
|
|
|
|
struct methodlist *ml;
|
1990-12-20 11:06:42 -04:00
|
|
|
char namebuf[256];
|
|
|
|
if ((m = add_module(name)) == NULL) {
|
1990-10-14 09:07:46 -03:00
|
|
|
fprintf(stderr, "initializing module: %s\n", name);
|
|
|
|
fatal("can't create a module");
|
|
|
|
}
|
|
|
|
d = getmoduledict(m);
|
|
|
|
for (ml = methods; ml->ml_name != NULL; ml++) {
|
1990-12-20 11:06:42 -04:00
|
|
|
sprintf(namebuf, "%s.%s", name, ml->ml_name);
|
|
|
|
v = newmethodobject(strdup(namebuf), ml->ml_meth,
|
1991-12-16 09:07:24 -04:00
|
|
|
(object *)NULL, ml->ml_varargs);
|
1990-12-20 11:06:42 -04:00
|
|
|
/* XXX The strdup'ed memory is never freed */
|
1990-10-14 09:07:46 -03:00
|
|
|
if (v == NULL || dictinsert(d, ml->ml_name, v) != 0) {
|
|
|
|
fprintf(stderr, "initializing module: %s\n", name);
|
|
|
|
fatal("can't initialize module");
|
|
|
|
}
|
|
|
|
DECREF(v);
|
|
|
|
}
|
1990-12-20 11:06:42 -04:00
|
|
|
return m;
|
1990-10-14 09:07:46 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1992-04-13 12:53:41 -03:00
|
|
|
/* Helper for getargs() and mkvalue() to scan the length of a format */
|
|
|
|
|
|
|
|
static int countformat PROTO((char *format, int endchar));
|
|
|
|
static int countformat(format, endchar)
|
|
|
|
char *format;
|
|
|
|
int endchar;
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
int level = 0;
|
|
|
|
while (level > 0 || *format != endchar) {
|
|
|
|
if (*format == '\0') {
|
|
|
|
/* Premature end */
|
|
|
|
err_setstr(SystemError, "unmatched paren in format");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (*format == '(') {
|
|
|
|
if (level == 0)
|
|
|
|
count++;
|
|
|
|
level++;
|
|
|
|
}
|
|
|
|
else if (*format == ')')
|
|
|
|
level--;
|
|
|
|
else if (level == 0 && *format != '#')
|
|
|
|
count++;
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
/* Generic argument list parser */
|
1990-10-14 09:07:46 -03:00
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
static int do_arg PROTO((object *arg, char** p_format, va_list *p_va));
|
|
|
|
static int
|
|
|
|
do_arg(arg, p_format, p_va)
|
|
|
|
object *arg;
|
|
|
|
char** p_format;
|
|
|
|
va_list *p_va;
|
1990-10-14 09:07:46 -03:00
|
|
|
{
|
1992-01-27 12:47:03 -04:00
|
|
|
char *format = *p_format;
|
|
|
|
va_list va = *p_va;
|
|
|
|
|
|
|
|
if (arg == NULL)
|
|
|
|
return 0; /* Incomplete tuple or list */
|
|
|
|
|
|
|
|
switch (*format++) {
|
|
|
|
|
|
|
|
case '('/*')'*/: /* tuple, distributed over C parameters */ {
|
|
|
|
int i, n;
|
|
|
|
if (!is_tupleobject(arg))
|
|
|
|
return 0;
|
|
|
|
n = gettuplesize(arg);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (!do_arg(gettupleitem(arg, i), &format, &va))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (*format++ != /*'('*/')')
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
1992-04-13 12:53:41 -03:00
|
|
|
|
|
|
|
case 'b': /* byte -- very short int */ {
|
|
|
|
char *p = va_arg(va, char *);
|
|
|
|
if (is_intobject(arg))
|
|
|
|
*p = getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
case 'h': /* short int */ {
|
|
|
|
short *p = va_arg(va, short *);
|
|
|
|
if (is_intobject(arg))
|
|
|
|
*p = getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'i': /* int */ {
|
|
|
|
int *p = va_arg(va, int *);
|
|
|
|
if (is_intobject(arg))
|
|
|
|
*p = getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'l': /* long int */ {
|
|
|
|
long *p = va_arg(va, long *);
|
|
|
|
if (is_intobject(arg))
|
|
|
|
*p = getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'f': /* float */ {
|
|
|
|
float *p = va_arg(va, float *);
|
|
|
|
if (is_floatobject(arg))
|
|
|
|
*p = getfloatvalue(arg);
|
|
|
|
else if (is_intobject(arg))
|
|
|
|
*p = (float)getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'd': /* double */ {
|
|
|
|
double *p = va_arg(va, double *);
|
|
|
|
if (is_floatobject(arg))
|
|
|
|
*p = getfloatvalue(arg);
|
|
|
|
else if (is_intobject(arg))
|
|
|
|
*p = (double)getintvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'c': /* char */ {
|
|
|
|
char *p = va_arg(va, char *);
|
|
|
|
if (is_stringobject(arg) && getstringsize(arg) == 1)
|
|
|
|
*p = getstringvalue(arg)[0];
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 's': /* string */ {
|
|
|
|
char **p = va_arg(va, char **);
|
|
|
|
if (is_stringobject(arg))
|
|
|
|
*p = getstringvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
if (*format == '#') {
|
|
|
|
int *q = va_arg(va, int *);
|
|
|
|
*q = getstringsize(arg);
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'z': /* string, may be NULL (None) */ {
|
|
|
|
char **p = va_arg(va, char **);
|
|
|
|
if (arg == None)
|
|
|
|
*p = 0;
|
|
|
|
else if (is_stringobject(arg))
|
|
|
|
*p = getstringvalue(arg);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
if (*format == '#') {
|
|
|
|
int *q = va_arg(va, int *);
|
|
|
|
if (arg == None)
|
|
|
|
*q = 0;
|
|
|
|
else
|
|
|
|
*q = getstringsize(arg);
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'S': /* string object */ {
|
|
|
|
object **p = va_arg(va, object **);
|
|
|
|
if (is_stringobject(arg))
|
|
|
|
*p = arg;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'O': /* object */ {
|
|
|
|
object **p = va_arg(va, object **);
|
|
|
|
*p = arg;
|
|
|
|
break;
|
|
|
|
}
|
1990-10-14 09:07:46 -03:00
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
default:
|
|
|
|
fprintf(stderr, "bad do_arg format: x%x '%c'\n",
|
|
|
|
format[-1], format[-1]);
|
|
|
|
return 0;
|
|
|
|
|
1990-10-14 09:07:46 -03:00
|
|
|
}
|
1992-01-27 12:47:03 -04:00
|
|
|
|
|
|
|
*p_va = va;
|
|
|
|
*p_format = format;
|
|
|
|
|
1990-10-14 09:07:46 -03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
1992-01-27 12:47:03 -04:00
|
|
|
#ifdef USE_STDARG
|
1992-03-27 13:23:29 -04:00
|
|
|
/* VARARGS2 */
|
1992-01-27 12:47:03 -04:00
|
|
|
int getargs(object *arg, char *format, ...)
|
|
|
|
#else
|
1992-03-27 13:23:29 -04:00
|
|
|
/* VARARGS */
|
1992-01-27 12:47:03 -04:00
|
|
|
int getargs(va_alist) va_dcl
|
|
|
|
#endif
|
1990-10-14 09:07:46 -03:00
|
|
|
{
|
1992-01-27 12:47:03 -04:00
|
|
|
char *f;
|
|
|
|
int ok;
|
|
|
|
va_list va;
|
|
|
|
#ifdef USE_STDARG
|
|
|
|
|
|
|
|
va_start(va, format);
|
|
|
|
#else
|
|
|
|
object *arg;
|
|
|
|
char *format;
|
|
|
|
|
|
|
|
va_start(va);
|
|
|
|
arg = va_arg(va, object *);
|
|
|
|
format = va_arg(va, char *);
|
|
|
|
#endif
|
|
|
|
if (*format == '\0') {
|
|
|
|
va_end(va);
|
|
|
|
if (arg != NULL) {
|
|
|
|
err_setstr(TypeError, "no arguments needed");
|
|
|
|
return 0;
|
|
|
|
}
|
1990-10-14 09:07:46 -03:00
|
|
|
return 1;
|
|
|
|
}
|
1992-01-27 12:47:03 -04:00
|
|
|
|
|
|
|
f = format;
|
|
|
|
ok = do_arg(arg, &f, &va) && *f == '\0';
|
|
|
|
va_end(va);
|
|
|
|
if (!ok) {
|
|
|
|
char buf[256];
|
|
|
|
sprintf(buf, "bad argument list (format '%s')", format);
|
|
|
|
err_setstr(TypeError, buf);
|
1990-10-14 09:07:46 -03:00
|
|
|
}
|
1992-01-27 12:47:03 -04:00
|
|
|
return ok;
|
1990-10-14 09:07:46 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
getlongtuplearg(args, a, n)
|
|
|
|
object *args;
|
|
|
|
long *a; /* [n] */
|
|
|
|
int n;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!is_tupleobject(args) || gettuplesize(args) != n) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
object *v = gettupleitem(args, i);
|
|
|
|
if (!is_intobject(v)) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
a[i] = getintvalue(v);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
getshorttuplearg(args, a, n)
|
|
|
|
object *args;
|
|
|
|
short *a; /* [n] */
|
|
|
|
int n;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!is_tupleobject(args) || gettuplesize(args) != n) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
object *v = gettupleitem(args, i);
|
|
|
|
if (!is_intobject(v)) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
a[i] = getintvalue(v);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
getlonglistarg(args, a, n)
|
|
|
|
object *args;
|
|
|
|
long *a; /* [n] */
|
|
|
|
int n;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!is_listobject(args) || getlistsize(args) != n) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
object *v = getlistitem(args, i);
|
|
|
|
if (!is_intobject(v)) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
a[i] = getintvalue(v);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
getshortlistarg(args, a, n)
|
|
|
|
object *args;
|
|
|
|
short *a; /* [n] */
|
|
|
|
int n;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!is_listobject(args) || getlistsize(args) != n) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
object *v = getlistitem(args, i);
|
|
|
|
if (!is_intobject(v)) {
|
|
|
|
return err_badarg();
|
|
|
|
}
|
|
|
|
a[i] = getintvalue(v);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
1992-04-13 07:48:55 -03:00
|
|
|
|
1992-04-13 12:53:41 -03:00
|
|
|
|
|
|
|
/* Generic function to create a value -- the inverse of getargs() */
|
|
|
|
/* After an original idea and first implementation by Steven Miale */
|
|
|
|
|
|
|
|
static object *do_mktuple PROTO((char**, va_list *, int, int));
|
|
|
|
static object *do_mkvalue PROTO((char**, va_list *));
|
|
|
|
|
1992-04-13 07:48:55 -03:00
|
|
|
static object *
|
1992-04-13 12:53:41 -03:00
|
|
|
do_mktuple(p_format, p_va, endchar, n)
|
|
|
|
char **p_format;
|
|
|
|
va_list *p_va;
|
|
|
|
int endchar;
|
|
|
|
int n;
|
|
|
|
{
|
1992-04-13 07:48:55 -03:00
|
|
|
object *v;
|
1992-04-13 12:53:41 -03:00
|
|
|
int i;
|
|
|
|
if (n < 0)
|
|
|
|
return NULL;
|
|
|
|
if ((v = newtupleobject(n)) == NULL)
|
|
|
|
return NULL;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
object *w = do_mkvalue(p_format, p_va);
|
|
|
|
if (w == NULL) {
|
|
|
|
DECREF(v);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
settupleitem(v, i, w);
|
|
|
|
}
|
|
|
|
if (v != NULL && **p_format != endchar) {
|
|
|
|
DECREF(v);
|
|
|
|
v = NULL;
|
|
|
|
err_setstr(SystemError, "Unmatched paren in format");
|
|
|
|
}
|
|
|
|
else if (endchar)
|
|
|
|
++*p_format;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static object *
|
1992-05-15 08:04:59 -03:00
|
|
|
do_mkvalue(p_format, p_va)
|
|
|
|
char **p_format;
|
|
|
|
va_list *p_va;
|
|
|
|
{
|
1992-04-13 07:48:55 -03:00
|
|
|
|
|
|
|
switch (*(*p_format)++) {
|
|
|
|
|
|
|
|
case '(':
|
1992-04-13 12:53:41 -03:00
|
|
|
return do_mktuple(p_format, p_va, ')',
|
|
|
|
countformat(*p_format, ')'));
|
|
|
|
|
|
|
|
case 'b':
|
1992-04-13 07:48:55 -03:00
|
|
|
case 'h':
|
|
|
|
case 'i':
|
1992-04-13 12:53:41 -03:00
|
|
|
return newintobject((long)va_arg(*p_va, int));
|
1992-04-13 07:48:55 -03:00
|
|
|
|
|
|
|
case 'l':
|
1992-04-13 12:53:41 -03:00
|
|
|
return newintobject((long)va_arg(*p_va, long));
|
1992-04-13 07:48:55 -03:00
|
|
|
|
|
|
|
case 'f':
|
|
|
|
case 'd':
|
1992-04-13 12:53:41 -03:00
|
|
|
return newfloatobject((double)va_arg(*p_va, double));
|
|
|
|
|
|
|
|
case 'c':
|
|
|
|
{
|
|
|
|
char p[1];
|
|
|
|
p[0] = va_arg(*p_va, int);
|
|
|
|
return newsizedstringobject(p, 1);
|
|
|
|
}
|
1992-04-13 07:48:55 -03:00
|
|
|
|
|
|
|
case 's':
|
|
|
|
case 'z':
|
|
|
|
{
|
1992-04-13 12:53:41 -03:00
|
|
|
object *v;
|
1992-04-13 07:48:55 -03:00
|
|
|
char *str = va_arg(*p_va, char *);
|
|
|
|
int n;
|
|
|
|
if (**p_format == '#') {
|
|
|
|
++*p_format;
|
|
|
|
n = va_arg(*p_va, int);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
n = -1;
|
|
|
|
if (str == NULL) {
|
|
|
|
v = None;
|
|
|
|
INCREF(v);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (n < 0)
|
|
|
|
n = strlen(str);
|
|
|
|
v = newsizedstringobject(str, n);
|
|
|
|
}
|
1992-04-13 12:53:41 -03:00
|
|
|
return v;
|
1992-04-13 07:48:55 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
case 'O':
|
1992-04-13 12:53:41 -03:00
|
|
|
{
|
|
|
|
object *v;
|
|
|
|
v = va_arg(*p_va, object *);
|
|
|
|
if (v != NULL)
|
|
|
|
INCREF(v);
|
|
|
|
else if (!err_occurred())
|
|
|
|
/* If a NULL was passed because a call
|
|
|
|
that should have constructed a value
|
|
|
|
failed, that's OK, and we pass the error
|
|
|
|
on; but if no error occurred it's not
|
|
|
|
clear that the caller knew what she
|
|
|
|
was doing. */
|
|
|
|
err_setstr(SystemError,
|
|
|
|
"NULL object passed to mkvalue");
|
|
|
|
return v;
|
1992-04-13 07:48:55 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
err_setstr(SystemError, "bad format char passed to mkvalue");
|
1992-04-13 12:53:41 -03:00
|
|
|
return NULL;
|
1992-04-13 07:48:55 -03:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1992-04-13 12:53:41 -03:00
|
|
|
#ifdef USE_STDARG
|
|
|
|
/* VARARGS 2 */
|
|
|
|
object *mkvalue(char *format, ...)
|
|
|
|
#else
|
|
|
|
/* VARARGS */
|
|
|
|
object *mkvalue(va_alist) va_dcl
|
|
|
|
#endif
|
1992-04-13 07:48:55 -03:00
|
|
|
{
|
1992-04-13 12:53:41 -03:00
|
|
|
int n;
|
|
|
|
char *f;
|
|
|
|
va_list va;
|
|
|
|
object* retval;
|
|
|
|
#ifdef USE_STDARG
|
|
|
|
va_start(va, format);
|
|
|
|
#else
|
|
|
|
char *format;
|
|
|
|
|
|
|
|
va_start(va);
|
|
|
|
format = va_arg(va, char *);
|
|
|
|
#endif
|
|
|
|
f = format;
|
|
|
|
n = countformat(f, '\0');
|
|
|
|
if (n < 0)
|
|
|
|
retval = NULL; /* Error in the format */
|
|
|
|
else if (n == 0) {
|
|
|
|
retval = None;
|
|
|
|
INCREF(retval);
|
|
|
|
}
|
|
|
|
else if (n == 1)
|
|
|
|
retval = do_mkvalue(&f, &va);
|
|
|
|
else
|
|
|
|
retval = do_mktuple(&f, &va, '\0', n);
|
|
|
|
va_end(va);
|
|
|
|
if (retval == NULL)
|
|
|
|
fprintf(stderr, "format \"%s\", f \"%s\"\n", format, f);
|
|
|
|
return retval;
|
1992-04-13 07:48:55 -03:00
|
|
|
}
|