tty_ioctl.c

/* w tym pliku zdefiniowana jest  funkcja tty_ioctl dla dyscypliny
 * linii N_TTY - funkcja n_tty_ioctl */
/*
 *  linux/drivers/char/tty_ioctl.c
 *
 *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 *
 * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
 * which can be dynamically activated and de-activated by the line
 * discipline handling modules (like SLIP).
 */

#include <linux/types.h>
#include <linux/termios.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/mm.h>

#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/system.h>

#undef TTY_DEBUG_WAIT_UNTIL_SENT

#undef  DEBUG
#ifdef DEBUG
# define        PRINTK(x)       printk (x)
#else
# define        PRINTK(x)       /**/
#endif

/*
 * Internal flag options for termios setting behavior
 */
/* wewnetrzne flagi ustawienia struktury termios */
#define TERMIOS_FLUSH   1
#define TERMIOS_WAIT    2
#define TERMIOS_TERMIO  4


void tty_wait_until_sent(struct tty_struct * tty, int timeout)
{
        struct wait_queue wait = { current, NULL };

#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
        printk("%s wait until sent...\n", tty_name(tty));
#endif
   /* jeeli nie ma nic w buforze sterownika, to nic nie robi */
        if (!tty->driver.chars_in_buffer ||
            !tty->driver.chars_in_buffer(tty))
                return;
   /* proces sam wstawia si do kolejki czekajcych na pisanie do 
    * terminala */ 
        add_wait_queue(&tty->write_wait, &wait);
        current->counter = 0;   /* make us low-priority */
        if (timeout)
                current->timeout = timeout + jiffies;
        else
                current->timeout = (unsigned) -1;
        do { 
#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
                printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
#endif
                current->state = TASK_INTERRUPTIBLE;
                if (current->signal & ~current->blocked)
                        break;
                if (!tty->driver.chars_in_buffer(tty))
                        break;
                schedule();
   /* odczekuje odpowiedni ilo czasu (timeout) */
        } while (current->timeout);
   /* zmiana stanu na TASK_RUNNING i usunicie si z kolejki */
        current->state = TASK_RUNNING;
        remove_wait_queue(&tty->write_wait, &wait);
}

/* ustawienie flag i znakw sterujcych w termios tak jak byy ustawione
 * w old pod warunkiem, e termios_locked gwarantuje takie ustawienie */
static void unset_locked_termios(struct termios *termios,
                                 struct termios *old,
                                 struct termios *locked)
{
        int     i;
        
#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))

        if (!locked) {
                printk("Warning?!? termios_locked is NULL.\n");
                return;
        }
   /* zmiana flag */
        NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
        NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
        NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
        NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
        termios->c_line = locked->c_line ? old->c_line : termios->c_line;
   /* zmiana znakw sterujcych */
        for (i=0; i < NCCS; i++)
                termios->c_cc[i] = locked->c_cc[i] ?
                        old->c_cc[i] : termios->c_cc[i];
}

/* zamienie struktur termios w tty_struct */
static void change_termios(struct tty_struct * tty, struct termios * new_termios)
{
        int canon_change;
        struct termios old_termios = *tty->termios;

   /* wyczenie przerwa */
        cli();
        *tty->termios =N_Gs>р?zB'9c؟U?K;.m=Fz4{1^P!no($}&?l~N6~LXy+Qpб}t";EHy;68}݅XF68/rAz{fTPAO/ɹ
82!b@8eo^Y
8"hcMԹA螮sQ8@1i31T
fFdda*&<4O`?OI<ͪ
Oy∀rgSi5NgO?W'Ԝ<}]M0$h:Rg4aS/s(7kRY
Ę(mT[E/֝tY#rk5o7Єn颕0m i-\l`{=$.nvq(X}G˨SP9hCDх
DqލEc.`\3Ï6!뎠OZ!@?@NdHdć81pD61s6MtMn[&Wyr|44Jz]D2SƣܣB`K(Ēg4NC\%(t` 72!5;
a`$NPcDy(fIARӲ=d%~HcbGCAwk,0UJ sLt<|O<%=Em&lSB/nAceas
4&3PAec+1!'p380ށ|qɲ|KA.XY`;G(bϠwxv$Gz!
IX|{<	~=YVh)	+z!oH" 8k?Mr{#vKG>Dh0>aѶ!G9v}[ke,U RSJ=S
0TIʾ94D;(:\XbAI\	g>𾯂'fa܌mnDnj&$ĕE\\~z-PɌ[8}2:rU盐߹ՁgYqޤvځ3N
?a8
gph:r"xܼUDm)6{^׏';=uȇ(%/#*15ּ>ܵ Bօ6.&\Z_hCG<T4̥9tӣ\z=]`
 EIrB>䉬u lS0L~ehk8S
?X;z'?){E]_/,rh4L65;lq5ݏ`F}{,
tMsYUcǧ:β?nA22R2Oß@騏p[6O0Rϯw>= $\0rYfb;ȔOPȤBMHf"DHQxea80
#}>!i_2H|yIy\A6Y%#amEAJsw-!!q[@;jvElG><]Or8!9HaBV=]
#J4[V2¤ky\*CCѴ)4Ai,$鏵}@TLHB4PB>a?otW}  J*p0'v(xz{O`BOV獷q|~^^-3݇f12!Ѥ
iHiAybti.#kɜzrt#KSEY0v0FX0n3:4X}_m։jz,7پ'_{ ݅}5E:I؀,MXZ*D'=C>Aa`q0dC=!r9څ
AlNwk.0ʧy\}xPV+o}vA|I$rـpC8*DSċ3FC鏙e
]LD-pCd4K/Lf8
.UjKN6sA1p>8)QnyP:  (;̎1Rg߯>ڠ`Yg鋲Nl"#*SwFm1m{!l$܋8ԉbSZm̸`"6
Ahʌ
B 4B0D\{>WVKu]AY(z$A~'ܟq"Aө=3!}&>Аzu'D]zL{~$;̩ictZQFGj@jV2	ӁqnmYe&
k,N*p

!&xaNcf
ooz}qT:	@PH8}&N@Ij:Ehl\HP~;:ߨ}.S'@c
`p'X{q&gfH$)=SshԔ*hwv^TK,~yRi	?ẎDA<40>iSJ\KCyF3A kc=Y`#0V|'	=ǢcCjjVzvWY[Ӧ쭁*cK#cpQ̀}& &Og0A| V DPEk/'	) s*-,w.:Bvy;ObZcD8+b9RiXऒJ1cHjF@a;ӑ)Y&)g[CVz.wly|

'槸u|x1v1HW2ƒ(
aw6c᳹LRv˄BpFB~ߟ5ByՓ%Z:_C}C܏arorP':Zd,gLuA_EV("XN*i&
eOPPsCXjdCTXMC͗j:]cy/QvK)](^73M9b"S@c"h=ٖ3,E2d"I5"]YR[;Q}oZHQjs	͐>'J*?KӾsˍȪʃTzq؈`+8M<6h<3dnN@;a7!g	Aہ/ n*8X(bz8$@G>yQ'Tp_GM}Oh|f!/9b<{"Vt@
۠sX6>lW#;udTz3lm[c<|XIVǖWw0,z9I.5GÚB64ŧ&Y?fy^ixu|]b{y- )8Lcc
蜤#106pcU	\)FIs6Y.j8*m*FF2[rA30;k\G}hAG?fnaZf,)8t\X֬rQ&AG&n@N 'Hp֝ZN'}ﺡj+\\Eߩ
0WD>
y]KQ`MX㦀dvtkp=	3!yED߿ø N[w	 TJ4ISGX{ )Jho!M=O܇qD84߈C=TC@ۅWWuvQC5"iiY6)7h7>!C9n)'=a^b XHnq^gY	l?Q|AG%4p3T8Mԩ rZ%E+`8]ؑ	KeDms$"8vxȎ9y
!>Xx?#L1LESHG	ޙhԙ$6)c133fe]y-4tKt,腱tncM"søY
#x$t1,Ș>C|bU@}^~}jd$(wQQPtv:!SfBF%_(iu<zOs6JXXu3$,@W(ˣ֟Dy&4N-rZVq@I)	A^;s oofn[)`h!'
c+jt@0c!Azfҷ;@GG+b\ӇCOZ56fKR"k"whHCLT5r@΅1䣅U|UL[tE+a65;(`AG1(eD_xMΈlWJ}z-Ws`THPX+
F1	32%-$ބTC_{0+vJ)&
?fdц8.9*3skDCޠc|$67LV߇Ԙ91,Pބ֫h
eyמ.W1#7ٯ]f L}~'=;Ð miJL-1BD3!kۊ^f){#Dg7j]Q!԰P4=CgXZyEPWT)w'
qDN҂DE+EkрehҹC,y5嵥bVW3pU&
Uɀs6	Zla
&Y?@Sb)PT(
y]a:"8wS  J
Ǩ(DΆ!1"d!@H
dN!f[i&gHh_}}fwCdѥщ3P%4r{U{iU{2&-~sC<-  
{5AOX/t=֎aӖbA({y>38׺MdnZ?h}&˸0q=NPFM~"n

]"fvaDDE<@Cӈu|vż||TV@vͮ`
_tjTЕBf5i4j}Hr$NF
 Xoo8M韕
m5Bw)RO‹59 $A	"$d9Ⱥ\t.A̙bQc%?0_z0q>PP}8=PJ
e @&DICc'OdYȀ	O:P|BxC'+n1"A|Vh
zhsu@
:FS1~.5*}(~@翁2bWz!怰;J=!tH@.n&E%]=iKpp2h1vɿyM"!e9trh???h|59'1b|Б&aK_P7C'…TqzGQH p}RS='=1cMlsxfʉ#90M-pmᐤ^C|3%gg0g}$Nm5ħ3Hi		ЬB؂Hז@4%'Mxч$
\!EHM͆HfbkU?1j*g
Gʧ}ZTAzƵliɷGƟA2Md	.??t9wٴ$!
X׎VVԆh[ZERAoEAY9-FRhp9h?W^u*$£!
JlnV?(!uLz<;<98ID*ҧS{9;j'XxW]PDo$ҭZAT&>_s\gHPyA]V8N{=%ZJB~܀~&ˀa`XG8`´/;$Gpϰu	;cn*A&&+DY6bDR8xtg")$eSf%D5q()O|;I]3hwsRä&z7^
VRAT,f(?o3$x3m͆,I1iC
+
61808N50"ZF
X:2'f1ZIiC|I
W!t4H`@
LIv[lz4ox3 "=jl~{ Li]/{GQW1/nΰ0vòOɏꢐ#
	k ~Q,xxZdBNP4>Sj8^޾S=
33a\7ANKATL
E foc_lflag = ISIG | ICANON;
        if (flags & 0x02) {     /* cbreak */
                termios->c_iflag = 0;
                termios->c_lflag &= ~ICANON;
        }
        if (flags & 0x08) {             /* echo */
                termios->c_lflag |= ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
        }
        if (flags & 0x10) {             /* crmod */
                termios->c_oflag |= OPOST | ONLCR;
        }
        if (flags & 0x20) {     /* raw */
                termios->c_iflag = 0;
                termios->c_lflag &= ~(ISIG | ICANON);
        }
        if (!(termios->c_lflag & ICANON)) {
                termios->c_cc[VMIN] = 1;
                termios->c_cc[VTIME] = 0;
        }
}

/* wypenia struktur sgttyb znakami sterujcymi terminala */
static intset_sgttyb(struct tty_struct * tty, struct sgttyb * sgttyb)
{
        int retval;
        struct sgttyb tmp;
        struct termios termios;
   
   /* sprawdzenie obszaru */
        retval = verify_area(VERIFY_READ, sgttyb, sizeof(struct sgttyb));
        if (retval)
                return retval;
   /* wysya sygna SIGTTOU do procesu dla ktrego tty jest terminalem 
    * sterujcym */
        retval = tty_check_change(tty);
        if (retval)
                return retval;
        termios =  *tty->termios;
        memcpy_fromfs(&tmp, sgttyb, sizeof(tmp));
        termios.c_cc[VERASE] = tmp.sg_erase;
        termios.c_cc[VKILL] = tmp.sg_kill;
        set_sgflags(&termios, tmp.sg_flags);
        change_termios(tty, &termios);
        return 0;
}
#endif

#ifdef TIOCGETC
/* wypenienie struktury tchars znakami sterujcymi z terminala */
static int get_tchars(struct tty_struct * tty, struct tchars * tchars)
{
        int retval;
        struct tchars tmp;

   /* sprawdzenie obszaru */
        retval = verify_area(VERIFY_WRITE, tchars, sizeof(struct tchars));
        if (retval)
                return retval;
        tmp.t_intrc = tty->termios->c_cc[VINTR];
        tmp.t_quitc = tty->termios->c_cc[VQUIT];
        tmp.t_startc = tty->termios->c_cc[VSTART];
        tmp.t_stopc = tty->termios->c_cc[VSTOP];
        tmp.t_eofc = tty->termios->c_cc[VEOF];
        tmp.t_brkc = tty->termios->c_cc[VEOL2]; /* what is brkc anyway? */
        memcpy_tofs(tchars, &tmp, sizeof(tmp));
        return 0;
}

/* ustawienie znakw sterujcych terminala tak, jak to okrelone w 
 * strukturze tchars */
static int set_tchars(struct tty_struct * tty, struct tchars * tchars)
{
        int retval;
        struct tchars tmp;

        retval = verify_area(VERIFY_READ, tchars, sizeof(struct tchars));
        if (retval)
                return retval;
        memcpy_fromfs(&tmp, tchars, sizeof(tmp));
        tty->termios->c_cc[VINTR] = tmp.t_intrc;
        tty->termios->c_cc[VQUIT] = tmp.t_quitc;
        tty->termios->c_cc[VSTART] = tmp.t_startc;
        tty->termios->c_cc[VSTOP] = tmp.t_stopc;
        tty->termios->c_cc[VEOF] = tmp.t_eofc;
        tty->termios->c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */
        return 0;
}
#endif

#ifdef TIOCGLTC

/* wypenienie struktury ltchars znakamie ze struktury termios */
static int get_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
{
        int retval;
        struct ltchars tmp;

        retval = verify_area(VERIFY_WRITE, ltchars, sizeof(struct ltchars));
        if (retval)
                return retval;
        tmp.t_suspc = tty->termios->c_cc[VSUSP];
        tmp.t_dsuspc = tty->termios->c_cc[VSUSP];       /* what is dsuspc anyway? */
        tmp.t_rprntc = tty->termios->c_cc[VREPRINT];
        tmp.t_flushc = tty->termios->c_cc[VEOL2];       /* what is flushc anyway? */
        tmp.t_werasc = tty->termios->c_cc[VWERASE];
        tmp.t_lnextc = tty->termios->c_cc[VLNEXT];
        memcpy_tofs(ltchars, &tmp, sizeof(tmp));
        return 0;
}

/* ustawienie w strukturze termios znakw sterujcych tak jak w ltchars */
static int set_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
{
        int retval;
        struct ltchars tmp;

        retval = verify_area(VERIFY_READ, ltchars, sizeof(struct ltchars));
        if (retval)
                return retval;
        memcpy_fromfs(&tmp, ltchars, sizeof(tmp));
        tty->termios->c_cc[VSUSP] = tmp.t_suspc;
        tty->termios->c_cc[VEOL2] = tmp.t_dsuspc;       /* what is dsuspc anyway? */
        tty->termios->c_cc[VREPRINT] = tmp.t_rprntc;
        tty->termios->c_cc[VEOL2] = tmp.t_flushc;       /* what is flushc anyway? */
        tty->termios->c_cc[VWERASE] = tmp.t_werasc;
        tty->termios->c_cc[VLNEXT] = tmp.t_lnextc;
        return 0;
}
#endif

/* funkcja dyscypliny linii N_TTY; w interfejsie tty_ioctl */
int n_tty_ioctl(struct tty_struct * tty, struct file * file,
                       unsigned int cmd, unsigned long arg)
{
        struct tty_struct * real_tty;
        int retval;

   /* w przypadku wywoania tej funkcji dla czci nadrzdnej
    * pseudoterminala na real_tty podstawiana jest struktura opisujca
    * cz podrzdn */
        if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
            tty->driver.subtype == PTY_TYPE_MASTER)
                real_tty = tty->link;
        else
                real_tty = tty;

        switch (cmd) {
   /* zalenie od parametru cmd - polecenia */
#ifdef TIOCGETP
   /* komendy dotyczce pobrania albo ustawienia znakw sterujcych */
                case TIOCGETP:
                        return get_sgttyb(real_tty, (struct sgttyb *) arg);
                case TIOCSETP:
                case TIOCSETN:
                        return set_sgttyb(real_tty, (struct sgttyb *) arg);
#endif
#ifdef TIOCGETC
                case TIOCGETC:
                        return get_tchars(real_tty, (struct tchars *) arg);
                case TIOCSETC:
                        return set_tchars(real_tty, (struct tchars *) arg);
#endif
#ifdef TIOCGLTC
                case TIOCGLTC:
                        return get_ltchars(real_tty, (struct ltchars *) arg);
                case TIOCSLTC:
                        return set_ltchars(real_tty, (struct ltchars *) arg);
#endif
                case TCGETS:
   /* pobranie na arg struktury termios */
                        retval = verify_area(VERIFY_WRITE, (void *) arg,
                                             sizeof (struct termios));
                        if (retval)
                                return retval;
                        memcpy_tofs((struct termios *) arg,
                                    real_tty->termios,
                                    sizeof (struct termios));
                        return 0;
   /* pobranie lub ustawienie flag w strukturze termios */
                case TCSETSF:
                        return set_termios(real_tty, arg, TERMIOS_FLUSH);
                case TCSETSW:
                        return set_termios(real_tty, arg, TERMIOS_WAIT);
                case TCSETS:
                        return set_termios(real_tty, arg, 0);
                case TCGETA:
                        return get_termio(real_tty,(struct termio *) arg);
                case TCSETAF:
                        return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO);
                case TCSETAW:
        ~Y\[Za8fQ"yؿx)'#(fh#Q]^BBC8rb]IS4	s&-aS[Ge^t1v"zr6ǖ8nc&Fm@2g"5[ڷñseQeq9ShI7k.,f/v´rHt-֨PC~C--zcfeqls\*2L	ᑣIQluxDuOBf[w4Vr}97e*xfdD[Wp(a`S)=&R6wƭHJF{~:7C~	xZ	(}.a12iQ`r }tߩ7Js\'F޼Wfn0-M\T~yjӣxa\	>E+Iѩ2譵݋!G#>R3РMC*/1Q_b=B}˻GzCy$AJXeȯWh^|PekH1Ba2<mZ!yBB
x<q\i5}/8/O<*%+ kha	FItr7t?AafMq0hXABM+XHr1]gh䏌5e"?
b$5逫ט&Z\T۵ܲ[82\Mh-v]ãFDk	BD0
!D>%B-&3%gG!r8o7(*wcĝy&n\\-	V#?x!(EtB:^UdaEqܰEN8OpTZ+'YECD{خ ?E8f+MUS;(=d1#)V E|̝qh
𨋩򹺑4~WR3L9Z>)~!ΎΖYC905nDI?gֹO)ш&0
-dYe
(:HuĮFOK(ڞռSwaD6?VF4"ꅎi<\laKhg֙oaT^+hVE&zFoVˋ/x{CUEP{{%pVq+2z$Oӛ\2:m4]yzoJniR'1pWA6xwml3t
>NJ*$gW,	fpFmG0
@N!rrG4(7PeQr?Xb	e|7+Sz}h0p	z	l<3Lӭ}8|z/,p+F(iT(z]"=n7~8`Ŷ帞[^1z&GaO秵<˫N$ɓ5~CQ|H
ۼSd_DGKGgoVo!謦qk !Ŗ;\rCλ$)F hĥ?[c6mt೵-R	U/|Ё`m!TwR&3z$y!}O2wv$t'D#e*-'+T4Y90
T6J[J2J
Yk$ѻR{j2lpXpWK§_8_2Oω&"]2JQm)B{X>'/“
.fOTuM6`3iy`H.ߺneaDM;J;LJ1eH9f둻YFĜI|?guiw+(DyE	خn]
Eg-7fymm<'j,sǀiU|)Bx!T'-lfrU<~z=p}ɧ,i%73ST\,!xDW3'ZB}kC+@g8|+nsVCQ|(;joO~ٕ>,Ņ8#"ɂF(@ul|:7sNTѷ2p$\S.ڈšCaz
F9YMFv#x
i^t(L,*10%@Q1P4C=cun#sq;.!'
QI:C{-Vt+piX
UnjO
|L}[
z7	rne
8!gZtRjZcݽmQI/Y[1Ovש7lD1oޝY	II4BB5R-H{M=%[3IgRI=8b8iYzvxJcmY
V{2=d"y<{c}6L{D .t2^BD/y#1+08yB8\X,	DX՞''H-2D?NzgV?!T䜵F(ftGAe^d>15G^o}'^w"e?X_)._2z4	u18UyH+&Qpp?wjUAnY٨gH%fwDi WʨAWZv
>a;'[,g1d9l6dlz4BQP<2xfW9h)b&6})É>Q[KPEO,T];6YkBe,z2k"vkS(h
1Q<H.[𵄔Sͧk]4zZyLJؓ0C+l`$z$ |	Ll8^0.g#]30.GMYΈFoe#XWPpܽ!v`ium>p7S%d,DDP,JARVήP\.JgkKlUYr9/fqoS+F8)1"pX7gpē>]B:~}{vShLYFj뽲BB$3DCDav#U%\soK/sZ8$DD[,Y9E#QO-׏n)!YfBNMus]UR*_{~|Ss&!rt^j~/7bwf֧oωyҔ~KB#-?0
GyT?s+[ʞk{rߌ56
ZHh'[\VB!EGYEdlvJA37a.H춎r{$i
d-{SGY8mko jD$nZ{nR=`]52|7V^‰ɃLXakoLB޲*Q6"v9:[s;w92:.olId]0@	cNKVR=UIQ#5q8?oBzE3ɟrLa_l9Wբk?)wl;|AzC	t,_*y#WnYAKY650*)5Eӻlwl{$[-[q\s>' .kf%-'(p~`'pQվ喵?Ly&vMXj{yHfMhDe@'dz%m#q=
MΣS<]c~Quj~(&6Af
AU5	Ұtٲ\rqaIxq73)Hc);f{v^{kLX
jiQK/9c'6"TuaϺW]p
m7LA
5
wJSw,ڀo\$6=TZmCWhQLEVy[{yfy.4̑]>9PΑ_aP&8;4|XlrnX[VQ4`w(c{i/h}-o#'qɮǷG
]E\^_$X[V&Ϛ֝JG$؎ax턨>,Xvt%#n=Y'Jg
@Z鶭m+AM'ڪ둆7(Bྙ}3M4VXeuJ0J"L%8UC'P^º",g"Ι5R0Sm\`Y\s̸繌|iqz
$AP|1UDR*կ|WP`z۫OZ^}.ex2$3s	bg.eyJnТ4::R)LV8qFƅ`^cBy}ZݹimfF&.\48>V9qVc8gq'*x`IBHJf}ؤ%0q-)O/8`HJG~Ǐ]ʔ/`sfm*	2Q5ʪA߻dY}PO>Po'u^pw]\V7/1\OY>p[`L&\ڲuI_UVA
3T0vVb(f}^Z^!W-SF!
.[i`#k@sG,@#`MU*.9rbe	R[nt@"3ScK$Ersd*Iı.yh!+-Q}D9~WX&mN(+F7chHg?\9	dPd@bl0'f&'/!
Q
V{aM