12261Sarnold /* 234677Sbostic * Copyright (c) 1981 Regents of the University of California. 334677Sbostic * All rights reserved. 434677Sbostic * 542657Sbostic * %sccs.include.redist.c% 622791Smckusick */ 722791Smckusick 822791Smckusick #ifndef lint 9*56559Selan static char sccsid[] = "@(#)refresh.c 5.15 (Berkeley) 10/13/92"; 1034677Sbostic #endif /* not lint */ 1122791Smckusick 1255976Sbostic #include <curses.h> 1355986Sbostic #include <string.h> 142261Sarnold 1555976Sbostic static int curwin; 1655976Sbostic static short ly, lx; 172261Sarnold 1856378Selan int doqch = 1; 1956378Selan 2055976Sbostic WINDOW *_win; 212261Sarnold 2255976Sbostic static void domvcur __P((int, int, int, int)); 2355976Sbostic static int makech __P((WINDOW *, int)); 2456302Selan static void quickch __P((WINDOW *)); 25*56559Selan static void scrolln __P((WINDOW *, int, int, int, int, int)); 2655976Sbostic /* 2755976Sbostic * wrefresh -- 2855976Sbostic * Make the current screen look like "win" over the area coverd by 2955976Sbostic * win. 3055976Sbostic */ 3155976Sbostic int 322261Sarnold wrefresh(win) 3355976Sbostic register WINDOW *win; 342261Sarnold { 3556302Selan register LINE *wlp; 3655976Sbostic register int retval; 3755976Sbostic register short wy; 382261Sarnold 3955976Sbostic /* Make sure were in visual state. */ 4055976Sbostic if (__endwin) { 4155976Sbostic tputs(VS, 0, __cputchar); 4255976Sbostic tputs(TI, 0, __cputchar); 4355976Sbostic __endwin = 0; 442261Sarnold } 452287Sarnold 4655976Sbostic /* Initialize loop parameters. */ 472287Sarnold 4856238Selan ly = curscr->cury; 4956238Selan lx = curscr->curx; 502287Sarnold wy = 0; 512287Sarnold _win = win; 522287Sarnold curwin = (win == curscr); 532287Sarnold 5456378Selan if (!curwin) 5556378Selan for (wy = 0; wy < win->maxy; wy++) { 5656378Selan wlp = win->lines[wy]; 5756378Selan if (wlp->flags & __ISDIRTY) 5856378Selan wlp->hash = __hash(wlp->line, win->maxx); 5956378Selan } 6056378Selan 6156238Selan if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) { 6256238Selan if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) { 6355976Sbostic tputs(CL, 0, __cputchar); 6412358Sarnold ly = 0; 6512358Sarnold lx = 0; 6612358Sarnold if (!curwin) { 6756238Selan curscr->flags &= ~__CLEAROK; 6856238Selan curscr->cury = 0; 6956238Selan curscr->curx = 0; 702287Sarnold werase(curscr); 7112358Sarnold } 722261Sarnold touchwin(win); 732261Sarnold } 7456238Selan win->flags &= ~__CLEAROK; 752261Sarnold } 762261Sarnold if (!CA) { 7756238Selan if (win->curx != 0) 7855976Sbostic putchar('\n'); 792287Sarnold if (!curwin) 802287Sarnold werase(curscr); 812261Sarnold } 8255976Sbostic #ifdef DEBUG 8355976Sbostic __TRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin); 8455976Sbostic __TRACE("wrefresh: \tfirstch\tlastch\n"); 8555976Sbostic #endif 8656302Selan 8756302Selan #ifndef NOQCH 8856378Selan if (!__noqch && (win->flags & __FULLWIN) && !curwin) 8956302Selan quickch(win); 9056302Selan #endif 9156238Selan for (wy = 0; wy < win->maxy; wy++) { 9255976Sbostic #ifdef DEBUG 9355976Sbostic __TRACE("%d\t%d\t%d\n", 9456238Selan wy, win->lines[wy]->firstch, win->lines[wy]->lastch); 9555976Sbostic #endif 9656378Selan if (!curwin) 9756378Selan curscr->lines[wy]->hash = win->lines[wy]->hash; 9856238Selan if (win->lines[wy]->flags & __ISDIRTY) 992261Sarnold if (makech(win, wy) == ERR) 10055976Sbostic return (ERR); 10119893Sbloom else { 10256238Selan if (win->lines[wy]->firstch >= win->ch_off) 10356238Selan win->lines[wy]->firstch = win->maxx + 10456238Selan win->ch_off; 10556238Selan if (win->lines[wy]->lastch < win->maxx + 10656238Selan win->ch_off) 10756238Selan win->lines[wy]->lastch = win->ch_off; 10856238Selan if (win->lines[wy]->lastch < 10956238Selan win->lines[wy]->firstch) 11056238Selan win->lines[wy]->flags &= ~__ISDIRTY; 11119893Sbloom } 11255976Sbostic #ifdef DEBUG 11356238Selan __TRACE("\t%d\t%d\n", win->lines[wy]->firstch, 11456238Selan win->lines[wy]->lastch); 11555976Sbostic #endif 1162261Sarnold } 11756238Selan 11856302Selan #ifdef DEBUG 11956238Selan __TRACE("refresh: ly=%d, lx=%d\n", ly, lx); 12056302Selan #endif 12156472Selan 12212358Sarnold if (win == curscr) 12356238Selan domvcur(ly, lx, win->cury, win->curx); 12419893Sbloom else { 12556238Selan if (win->flags & __LEAVEOK) { 12656238Selan curscr->cury = ly; 12756238Selan curscr->curx = lx; 12856238Selan ly -= win->begy; 12956238Selan lx -= win->begx; 13056238Selan if (ly >= 0 && ly < win->maxy && lx >= 0 && 13156238Selan lx < win->maxx) { 13256238Selan win->cury = ly; 13356238Selan win->curx = lx; 13455976Sbostic } else 13556238Selan win->cury = win->curx = 0; 13655976Sbostic } else { 13756238Selan domvcur(ly, lx, win->cury + win->begy, 13856238Selan win->curx + win->begx); 13956238Selan curscr->cury = win->cury + win->begy; 14056238Selan curscr->curx = win->curx + win->begx; 14119893Sbloom } 1422261Sarnold } 1432287Sarnold retval = OK; 14455986Sbostic 1452261Sarnold _win = NULL; 14655976Sbostic (void)fflush(stdout); 14755976Sbostic return (retval); 1482261Sarnold } 1492261Sarnold 1502261Sarnold /* 15155976Sbostic * makech -- 15255976Sbostic * Make a change on the screen. 1532261Sarnold */ 15455976Sbostic static int 1552261Sarnold makech(win, wy) 15655976Sbostic register WINDOW *win; 15755976Sbostic int wy; 1582261Sarnold { 15955976Sbostic register int nlsp, clsp; /* Last space in lines. */ 16055976Sbostic register short wx, lch, y; 16155976Sbostic register char *nsp, *csp, *ce; 1622261Sarnold 16356472Selan /* Is the cursor still on the end of the last line? */ 16456472Selan if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) { 16556551Selan win->lines[wy - 1]->flags &= ~__ISPASTEOL; 16656551Selan domvcur(ly, lx, ly + 1, 0); 16756551Selan ly++; 16856551Selan lx = 0; 16956551Selan } 17056238Selan if (!(win->lines[wy]->flags & __ISDIRTY)) 17155976Sbostic return (OK); 17256238Selan wx = win->lines[wy]->firstch - win->ch_off; 17356238Selan if (wx >= win->maxx) 17456238Selan return (OK); 17519893Sbloom else if (wx < 0) 17619893Sbloom wx = 0; 17756238Selan lch = win->lines[wy]->lastch - win->ch_off; 17819893Sbloom if (lch < 0) 17955976Sbostic return (OK); 18056238Selan else if (lch >= win->maxx) 18156238Selan lch = win->maxx - 1; 18256238Selan y = wy + win->begy; 18319893Sbloom 1842287Sarnold if (curwin) 1852287Sarnold csp = " "; 1862287Sarnold else 18756238Selan csp = &curscr->lines[wy + win->begy]->line[wx + win->begx]; 18819893Sbloom 18956238Selan nsp = &win->lines[wy]->line[wx]; 1902287Sarnold if (CE && !curwin) { 19156238Selan for (ce = &win->lines[wy]->line[win->maxx - 1]; 19256238Selan *ce == ' '; ce--) 19356238Selan if (ce <= win->lines[wy]->line) 1942261Sarnold break; 19556238Selan nlsp = ce - win->lines[wy]->line; 1962261Sarnold } 1972287Sarnold if (!curwin) 1982287Sarnold ce = CE; 1992287Sarnold else 2002287Sarnold ce = NULL; 20119893Sbloom 2022261Sarnold while (wx <= lch) { 20355986Sbostic if (*nsp == *csp) { 20455986Sbostic if (wx <= lch) { 20555986Sbostic while (*nsp == *csp && wx <= lch) { 20655986Sbostic nsp++; 20755986Sbostic if (!curwin) 20855986Sbostic csp++; 20955986Sbostic ++wx; 21055986Sbostic } 21155986Sbostic continue; 21255986Sbostic } 21355986Sbostic break; 21455986Sbostic } 21556238Selan domvcur(ly, lx, y, wx + win->begx); 21656378Selan 21755976Sbostic #ifdef DEBUG 21856378Selan __TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n", 21956378Selan wx, ly, lx, y, wx + win->begx); 22055976Sbostic #endif 22155986Sbostic ly = y; 22256238Selan lx = wx + win->begx; 22355986Sbostic while (*nsp != *csp && wx <= lch) { 22456378Selan #ifdef notdef 22556378Selan /* XXX 22656378Selan * The problem with this code is that we can't count on 22756378Selan * terminals wrapping around after the 22856378Selan * last character on the previous line has been output 22956378Selan * In effect, what then could happen is that the CE 23056378Selan * clear the previous line and do nothing to the 23156378Selan * next line. 23256378Selan */ 23355986Sbostic if (ce != NULL && wx >= nlsp && *nsp == ' ') { 23455986Sbostic /* Check for clear to end-of-line. */ 23556238Selan ce = &curscr->lines[ly]->line[COLS - 1]; 23655986Sbostic while (*ce == ' ') 23755986Sbostic if (ce-- <= csp) 23855986Sbostic break; 23956238Selan clsp = ce - curscr->lines[ly]->line - 24056238Selan win->begx; 24155976Sbostic #ifdef DEBUG 24255986Sbostic __TRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp); 24355976Sbostic #endif 24455986Sbostic if (clsp - nlsp >= strlen(CE) && 24556238Selan clsp < win->maxx) { 24655976Sbostic #ifdef DEBUG 24755986Sbostic __TRACE("makech: using CE\n"); 24855976Sbostic #endif 24955986Sbostic tputs(CE, 0, __cputchar); 25056238Selan lx = wx + win->begx; 25155986Sbostic while (wx++ <= clsp) 25255986Sbostic *csp++ = ' '; 25355986Sbostic return (OK); 2542261Sarnold } 25555986Sbostic ce = NULL; 25655986Sbostic } 25756378Selan #endif 25855986Sbostic 25955986Sbostic /* Enter/exit standout mode as appropriate. */ 26056238Selan if (SO && (*nsp & __STANDOUT) != 26156378Selan (curscr->flags & __WSTANDOUT)) { 26256238Selan if (*nsp & __STANDOUT) { 26355986Sbostic tputs(SO, 0, __cputchar); 26456238Selan curscr->flags |= __WSTANDOUT; 26555986Sbostic } else { 26655986Sbostic tputs(SE, 0, __cputchar); 26756238Selan curscr->flags &= ~__WSTANDOUT; 2682261Sarnold } 26955986Sbostic } 27055986Sbostic 27155986Sbostic wx++; 27256472Selan if (wx >= win->maxx && wy == win->maxy - 1 && !curwin) 27356238Selan if (win->flags & __SCROLLOK) { 27456238Selan if (curscr->flags & __WSTANDOUT 27556238Selan && win->flags & __ENDLINE) 27655986Sbostic if (!MS) { 27755986Sbostic tputs(SE, 0, 27855986Sbostic __cputchar); 27956238Selan curscr->flags &= 28056238Selan ~__WSTANDOUT; 28155976Sbostic } 28255986Sbostic if (!curwin) 28355986Sbostic putchar((*csp = *nsp) & 0177); 28455986Sbostic else 28555986Sbostic putchar(*nsp & 0177); 28656378Selan #ifdef notdef 28756472Selan if (win->flags & __FULLWIN && !curwin) 28855986Sbostic scroll(curscr); 28956378Selan #endif 29056378Selan ly = win->begy + win->maxy - 1; 29156378Selan lx = win->begx + win->maxx - 1; 29255986Sbostic return (OK); 29355986Sbostic } else 29456238Selan if (win->flags & __SCROLLWIN) { 29555986Sbostic lx = --wx; 29655986Sbostic return (ERR); 29755986Sbostic } 29855986Sbostic if (!curwin) 29955986Sbostic putchar((*csp++ = *nsp) & 0177); 30055986Sbostic else 30155986Sbostic putchar(*nsp & 0177); 30256472Selan 30355976Sbostic #ifdef DEBUG 30455986Sbostic __TRACE("makech: putchar(%c)\n", *nsp & 0177); 30555976Sbostic #endif 30656238Selan if (UC && (*nsp & __STANDOUT)) { 30755986Sbostic putchar('\b'); 30855986Sbostic tputs(UC, 0, __cputchar); 3092261Sarnold } 31055986Sbostic nsp++; 31155986Sbostic } 31255976Sbostic #ifdef DEBUG 31355986Sbostic __TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx); 31455976Sbostic #endif 31556238Selan if (lx == wx + win->begx) /* If no change. */ 31655986Sbostic break; 31756238Selan lx = wx + win->begx; 31855986Sbostic if (lx >= COLS && AM) { 31955986Sbostic /* 32055986Sbostic * xn glitch: chomps a newline after auto-wrap. 32155986Sbostic * we just feed it now and forget about it. 32255986Sbostic */ 32355986Sbostic if (XN) { 32456472Selan lx = 0; 32556472Selan ly++; 32655986Sbostic putchar('\n'); 32755986Sbostic putchar('\r'); 32856472Selan } else { 32956472Selan if (wy != LINES) 33056472Selan win->lines[wy]->flags |= __ISPASTEOL; 33156472Selan lx = COLS - 1; 33222789Smckusick } 33355986Sbostic } 33455976Sbostic #ifdef DEBUG 33555976Sbostic __TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx); 33655976Sbostic #endif 3372261Sarnold } 33855976Sbostic return (OK); 33911736Sarnold } 34011736Sarnold 34111736Sarnold /* 34255976Sbostic * domvcur -- 34355976Sbostic * Do a mvcur, leaving standout mode if necessary. 34411736Sarnold */ 34555976Sbostic static void 34611736Sarnold domvcur(oy, ox, ny, nx) 34755976Sbostic int oy, ox, ny, nx; 34855976Sbostic { 34956238Selan if (curscr->flags & __WSTANDOUT && !MS) { 35055976Sbostic tputs(SE, 0, __cputchar); 35156238Selan curscr->flags &= ~__WSTANDOUT; 3522261Sarnold } 35356378Selan #ifdef DEBUG 35456378Selan __TRACE("domvcur: oy=%d, ox=%d, ny=%d, nx=%d\n", oy, ox, ny, nx); 35556378Selan #endif 35611736Sarnold mvcur(oy, ox, ny, nx); 3572261Sarnold } 35856302Selan 35956302Selan 36056302Selan /* 36156302Selan * Quickch() attempts to detect a pattern in the change of the window 36256552Selan * in order to optimize the change, e.g., scroll n lines as opposed to 36356302Selan * repainting the screen line by line. 36456302Selan */ 36556302Selan 36656302Selan static void 36756302Selan quickch(win) 36856302Selan WINDOW *win; 36956302Selan { 37056378Selan #define THRESH win->maxy / 4 37156302Selan 37256378Selan register LINE *clp, *tmp1, *tmp2; 37356378Selan register int bsize, curs, curw, starts, startw, i, j; 374*56559Selan int n, target, remember, bot, top; 37556378Selan char buf[1024]; 37656378Selan u_int blank_hash; 37756302Selan 37856552Selan /* 37956552Selan * Search for the largest block of text not changed. 38056552Selan */ 38156302Selan for (bsize = win->maxy; bsize >= THRESH; bsize--) 38256302Selan for (startw = 0; startw <= win->maxy - bsize; startw++) 38356302Selan for (starts = 0; starts <= win->maxy - bsize; 38456302Selan starts++) { 38556302Selan for (curw = startw, curs = starts; 38656302Selan curs < starts + bsize; curw++, curs++) 38756302Selan if (win->lines[curw]->hash != 38856552Selan curscr->lines[curs]->hash || 38956552Selan bcmp(&win->lines[curw], 390*56559Selan &curscr->lines[curs], 391*56559Selan win->maxx != 0)) 39256302Selan break; 39356302Selan if (curs == starts + bsize) 39456302Selan goto done; 39556302Selan } 39656302Selan done: 39756302Selan /* Did not find anything or block is in correct place already. */ 39856302Selan if (bsize < THRESH || starts == startw) 39956302Selan return; 40056302Selan 401*56559Selan /* 402*56559Selan * Find how many lines from the top of the screen are unchanged. 403*56559Selan */ 404*56559Selan if (starts != 0) { 405*56559Selan for (top = 0; top < win->maxy; top++) 406*56559Selan if (win->lines[top]->hash != curscr->lines[top]->hash 407*56559Selan || bcmp(&win->lines[top], &curscr->lines[top], 408*56559Selan win->maxx) != 0) 409*56559Selan break; 410*56559Selan } else 411*56559Selan top = 0; 412*56559Selan 413*56559Selan /* 414*56559Selan * Find how many lines from bottom of screen are unchanged. 415*56559Selan */ 416*56559Selan if (curs != win->maxy) { 417*56559Selan for (bot = win->maxy - 1; bot >= 0; bot--) 418*56559Selan if (win->lines[bot]->hash != curscr->lines[bot]->hash 419*56559Selan || bcmp(&win->lines[bot], &curscr->lines[bot], 420*56559Selan win->maxx) != 0) 421*56559Selan break; 422*56559Selan } else 423*56559Selan bot = win->maxy - 1; 424*56559Selan 42556302Selan #ifdef DEBUG 426*56559Selan __TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n", 427*56559Selan bsize, starts, startw, curw, curs, top, bot); 42856302Selan #endif 42956378Selan 430*56559Selan /* 431*56559Selan * Make sure that there is no overlap between the bottom and top 432*56559Selan * regions and the middle scrolled block. 433*56559Selan */ 434*56559Selan if (bot < curw) 435*56559Selan bot = curw - 1; 436*56559Selan if (top > startw) 437*56559Selan top = startw; 438*56559Selan 439*56559Selan scrolln(win, starts, startw, curs, top, bot); 440*56559Selan 44156378Selan n = startw - starts; 44256378Selan 44356378Selan /* So we don't have to call __hash() each time */ 44456378Selan (void)memset(buf, ' ', win->maxx); 44556378Selan blank_hash = __hash(buf, win->maxx); 44656378Selan 44756378Selan /* 44856378Selan * Perform the rotation to maintain the consistency of curscr. 44956378Selan */ 45056378Selan i = 0; 45156378Selan tmp1 = curscr->lines[0]; 45256378Selan remember = 0; 45356378Selan for (j = 0; j < win->maxy; j++) { 45456378Selan target = (i + n + win->maxy) % win->maxy; 45556378Selan tmp2 = curscr->lines[target]; 45656378Selan curscr->lines[target] = tmp1; 45756378Selan /* Mark block as clean and blank out scrolled lines. */ 45856378Selan clp = curscr->lines[target]; 45956472Selan #ifdef DEBUG 46056378Selan __TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ", 46156378Selan n, startw, curw, i, target); 46256472Selan #endif 463*56559Selan if (target >= startw && target < curw || target < top || 464*56559Selan target > bot) { 46556472Selan #ifdef DEBUG 46656378Selan __TRACE("-- notdirty"); 46756472Selan #endif 46856378Selan win->lines[target]->flags &= ~__ISDIRTY; 46956378Selan } else if ((n < 0 && target >= win->maxy + n) || 47056378Selan (n > 0 && target < n)) { 47156552Selan if (clp->hash != blank_hash || 47256552Selan bcmp(clp->line, buf, win->maxx) != 0) { 47356378Selan (void)memset(clp->line, ' ', win->maxx); 47456472Selan #ifdef DEBUG 47556378Selan __TRACE("-- memset"); 47656472Selan #endif 47756378Selan clp->hash = blank_hash; 47856378Selan } else 47956472Selan #ifdef DEBUG 48056378Selan __TRACE(" -- nonmemset"); 48156472Selan #endif 48256378Selan touchline(win, target, 0, win->maxx - 1); 48356378Selan } else { 48456472Selan #ifdef DEBUG 48556378Selan __TRACE(" -- just dirty"); 48656472Selan #endif 48756378Selan touchline(win, target, 0, win->maxx - 1); 48856378Selan } 48956472Selan #ifdef DEBUG 49056378Selan __TRACE("\n"); 49156472Selan #endif 49256378Selan if (target == remember) { 49356378Selan i = target + 1; 49456378Selan tmp1 = curscr->lines[i]; 49556378Selan remember = i; 49656378Selan } else { 49756378Selan tmp1 = tmp2; 49856378Selan i = target; 49956378Selan } 50056302Selan } 50156472Selan #ifdef DEBUG 50256378Selan __TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 50356378Selan for (i = 0; i < curscr->maxy; i++) 50456378Selan __TRACE("Q: %d: %.70s\n", i, 50556378Selan curscr->lines[i]->line); 50656472Selan #endif 50756302Selan } 50856302Selan 50956302Selan static void 510*56559Selan scrolln(win, starts, startw, curs, top, bot) 51156302Selan WINDOW *win; 512*56559Selan int starts, startw, curs, top, bot; 51356302Selan { 51456302Selan int i, oy, ox, n; 51556302Selan 51656302Selan oy = curscr->cury; 51756302Selan ox = curscr->curx; 51856302Selan n = starts - startw; 51956302Selan 52056302Selan if (n > 0) { 521*56559Selan mvcur(oy, ox, top, 0); 522*56559Selan /* Scroll up the block */ 52356302Selan if (DL) 52456302Selan tputs(tscroll(DL, n), 0, __cputchar); 52556302Selan else 52656302Selan for(i = 0; i < n; i++) 52756302Selan tputs(dl, 0, __cputchar); 528*56559Selan /* Push back down the bottom region */ 529*56559Selan if (bot < win->maxy - 1) { 530*56559Selan mvcur(top, 0, bot - n + 1, 0); 531*56559Selan if (AL) 532*56559Selan tputs(tscroll(AL, n), 0, __cputchar); 533*56559Selan else 534*56559Selan for(i = 0; i < n; i++) 535*56559Selan tputs(al, 0, __cputchar); 536*56559Selan mvcur(bot - n + 1, 0, oy, ox); 537*56559Selan } else 538*56559Selan mvcur(top, 0, oy, ox); 53956302Selan } else { 540*56559Selan /* Preserve the bottom lines. (Pull them up) */ 541*56559Selan if (bot < win->maxy - 1) { 542*56559Selan mvcur(oy, ox, curs, 0); 543*56559Selan if (DL) 544*56559Selan tputs(tscroll(DL, -n), 0, __cputchar); 545*56559Selan else 546*56559Selan for(i = n; i < 0; i++) 547*56559Selan tputs(dl, 0, __cputchar); 548*56559Selan mvcur(curs, 0, starts, 0); 549*56559Selan } else 550*56559Selan mvcur(oy, ox, starts, 0); 55156302Selan 55256302Selan /* Scroll the block down */ 55356302Selan if (AL) 55456302Selan tputs(tscroll(AL, -n), 0, __cputchar); 55556302Selan else 55656302Selan for(i = n; i < 0; i++) 55756302Selan tputs(al, 0, __cputchar); 55856302Selan mvcur(starts, 0, oy, ox); 55956378Selan } 56056302Selan } 56156378Selan 56256378Selan 563