Moved queue paint count to the server. Removed a few no longer used
routines.
diff --git a/dlls/user/user_main.c b/dlls/user/user_main.c
index 196fbcc..b75e169 100644
--- a/dlls/user/user_main.c
+++ b/dlls/user/user_main.c
@@ -259,18 +259,13 @@
if (hQueue)
{
- WND* desktop = WIN_GetDesktop();
-
TIMER_RemoveQueueTimers( hQueue );
HOOK_FreeQueueHooks( hQueue );
QUEUE_SetExitingQueue( hQueue );
- WIN_ResetQueueWindows( desktop, hQueue, 0 );
- QUEUE_SetExitingQueue( 0 );
+ WIN_DestroyThreadWindows( GetDesktopWindow() );
QUEUE_DeleteMsgQueue( hQueue );
-
- WIN_ReleaseDesktop();
SetThreadQueue16( 0, 0 );
}
diff --git a/include/server.h b/include/server.h
index 879dca4..da47d0f 100644
--- a/include/server.h
+++ b/include/server.h
@@ -1297,15 +1297,12 @@
};
-/* Set the message queue wake bits */
-struct set_queue_bits_request
+/* Increment the message queue paint count */
+struct inc_queue_paint_count_request
{
REQUEST_HEADER; /* request header */
- IN handle_t handle; /* handle to the queue */
- IN unsigned int set; /* wake bits to set */
- IN unsigned int clear; /* wake bits to clear */
- IN unsigned int mask_cond; /* mask for conditional bit setting */
- OUT unsigned int changed_mask; /* changed bits wake mask */
+ IN void* id; /* thread id */
+ IN int incr; /* increment (can be negative) */
};
@@ -1600,7 +1597,7 @@
REQ_get_atom_name,
REQ_init_atom_table,
REQ_get_msg_queue,
- REQ_set_queue_bits,
+ REQ_inc_queue_paint_count,
REQ_set_queue_mask,
REQ_get_queue_status,
REQ_wait_input_idle,
@@ -1725,7 +1722,7 @@
struct get_atom_name_request get_atom_name;
struct init_atom_table_request init_atom_table;
struct get_msg_queue_request get_msg_queue;
- struct set_queue_bits_request set_queue_bits;
+ struct inc_queue_paint_count_request inc_queue_paint_count;
struct set_queue_mask_request set_queue_mask;
struct get_queue_status_request get_queue_status;
struct wait_input_idle_request wait_input_idle;
@@ -1743,7 +1740,7 @@
struct create_async_request create_async;
};
-#define SERVER_PROTOCOL_VERSION 46
+#define SERVER_PROTOCOL_VERSION 47
/* ### make_requests end ### */
/* Everything above this line is generated automatically by tools/make_requests */
diff --git a/include/win.h b/include/win.h
index a103e36..246b7e1 100644
--- a/include/win.h
+++ b/include/win.h
@@ -107,8 +107,8 @@
extern void WIN_WalkWindows( HWND hwnd, int indent );
extern BOOL WIN_UnlinkWindow( HWND hwnd );
extern BOOL WIN_LinkWindow( HWND hwnd, HWND hwndInsertAfter );
-extern HWND WIN_FindWinToRepaint( HWND hwnd, HQUEUE16 hQueue );
-extern BOOL WIN_ResetQueueWindows( WND* wnd, HQUEUE16 hQueue, HQUEUE16 hNew);
+extern HWND WIN_FindWinToRepaint( HWND hwnd );
+extern BOOL WIN_DestroyThreadWindows( HWND hwnd );
extern BOOL WIN_CreateDesktopWindow(void);
extern HWND WIN_GetTopParent( HWND hwnd );
extern WND* WIN_GetTopParentPtr( WND* pWnd );
diff --git a/server/queue.c b/server/queue.c
index ccbd1ea..7e94cde 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -69,6 +69,7 @@
unsigned int wake_mask; /* wakeup mask */
unsigned int changed_bits; /* changed wakeup bits */
unsigned int changed_mask; /* changed wakeup mask */
+ int paint_count; /* pending paint messages count */
struct message_list msg_list[NB_MSG_KINDS]; /* lists of messages */
struct message_result *send_result; /* stack of sent messages waiting for result */
struct message_result *recv_result; /* stack of received messages waiting for result */
@@ -114,6 +115,7 @@
queue->wake_mask = 0;
queue->changed_bits = 0;
queue->changed_mask = 0;
+ queue->paint_count = 0;
queue->send_result = NULL;
queue->recv_result = NULL;
queue->first_timer = NULL;
@@ -581,18 +583,27 @@
}
-/* set the message queue wake bits */
-DECL_HANDLER(set_queue_bits)
+/* increment the message queue paint count */
+DECL_HANDLER(inc_queue_paint_count)
{
- struct msg_queue *queue = (struct msg_queue *)get_handle_obj( current->process, req->handle,
- 0, &msg_queue_ops );
- if (queue)
+ struct msg_queue *queue;
+ struct thread *thread = get_thread_from_id( req->id );
+
+ if (!thread) return;
+
+ if ((queue = thread->queue))
{
- req->changed_mask = queue->changed_mask;
- if (!req->mask_cond || (queue->changed_mask & req->mask_cond))
- change_queue_bits( queue, req->set, req->clear );
- release_object( queue );
+ if ((queue->paint_count += req->incr) < 0) queue->paint_count = 0;
+
+ if (queue->paint_count)
+ change_queue_bits( queue, QS_PAINT, 0 );
+ else
+ change_queue_bits( queue, 0, QS_PAINT );
}
+ else set_error( STATUS_INVALID_PARAMETER );
+
+ release_object( thread );
+
}
diff --git a/server/request.h b/server/request.h
index 8d24e76..6a5bc40 100644
--- a/server/request.h
+++ b/server/request.h
@@ -170,7 +170,7 @@
DECL_HANDLER(get_atom_name);
DECL_HANDLER(init_atom_table);
DECL_HANDLER(get_msg_queue);
-DECL_HANDLER(set_queue_bits);
+DECL_HANDLER(inc_queue_paint_count);
DECL_HANDLER(set_queue_mask);
DECL_HANDLER(get_queue_status);
DECL_HANDLER(wait_input_idle);
@@ -294,7 +294,7 @@
(req_handler)req_get_atom_name,
(req_handler)req_init_atom_table,
(req_handler)req_get_msg_queue,
- (req_handler)req_set_queue_bits,
+ (req_handler)req_inc_queue_paint_count,
(req_handler)req_set_queue_mask,
(req_handler)req_get_queue_status,
(req_handler)req_wait_input_idle,
diff --git a/server/trace.c b/server/trace.c
index 2876fbd..250ffd9 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1414,17 +1414,10 @@
fprintf( stderr, " handle=%d", req->handle );
}
-static void dump_set_queue_bits_request( const struct set_queue_bits_request *req )
+static void dump_inc_queue_paint_count_request( const struct inc_queue_paint_count_request *req )
{
- fprintf( stderr, " handle=%d,", req->handle );
- fprintf( stderr, " set=%08x,", req->set );
- fprintf( stderr, " clear=%08x,", req->clear );
- fprintf( stderr, " mask_cond=%08x", req->mask_cond );
-}
-
-static void dump_set_queue_bits_reply( const struct set_queue_bits_request *req )
-{
- fprintf( stderr, " changed_mask=%08x", req->changed_mask );
+ fprintf( stderr, " id=%p,", req->id );
+ fprintf( stderr, " incr=%d", req->incr );
}
static void dump_set_queue_mask_request( const struct set_queue_mask_request *req )
@@ -1703,7 +1696,7 @@
(dump_func)dump_get_atom_name_request,
(dump_func)dump_init_atom_table_request,
(dump_func)dump_get_msg_queue_request,
- (dump_func)dump_set_queue_bits_request,
+ (dump_func)dump_inc_queue_paint_count_request,
(dump_func)dump_set_queue_mask_request,
(dump_func)dump_get_queue_status_request,
(dump_func)dump_wait_input_idle_request,
@@ -1824,7 +1817,7 @@
(dump_func)dump_get_atom_name_reply,
(dump_func)0,
(dump_func)dump_get_msg_queue_reply,
- (dump_func)dump_set_queue_bits_reply,
+ (dump_func)0,
(dump_func)dump_set_queue_mask_reply,
(dump_func)dump_get_queue_status_reply,
(dump_func)dump_wait_input_idle_reply,
@@ -1945,7 +1938,7 @@
"get_atom_name",
"init_atom_table",
"get_msg_queue",
- "set_queue_bits",
+ "inc_queue_paint_count",
"set_queue_mask",
"get_queue_status",
"wait_input_idle",
diff --git a/windows/message.c b/windows/message.c
index 18baf2f..135df4b 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -767,7 +767,6 @@
{
int mask;
MESSAGEQUEUE *msgQueue;
- HQUEUE16 hQueue = GetFastQueue16();
int iWndsLocks;
QMSG qmsg;
@@ -820,7 +819,7 @@
/* need to fill the window handle for WM_PAINT message */
if (qmsg.msg.message == WM_PAINT)
{
- if ((qmsg.msg.hwnd = WIN_FindWinToRepaint( hwnd, hQueue )))
+ if ((qmsg.msg.hwnd = WIN_FindWinToRepaint( hwnd )))
{
if (IsIconic( qmsg.msg.hwnd ) && GetClassLongA( qmsg.msg.hwnd, GCL_HICON ))
{
@@ -856,7 +855,7 @@
WIN_RestoreWndsLock(iWndsLocks);
- if ((msgQueue = QUEUE_Lock( hQueue )))
+ if ((msgQueue = QUEUE_Lock( GetFastQueue16() )))
{
msgQueue->GetMessageTimeVal = qmsg.msg.time;
msgQueue->GetMessagePosVal = MAKELONG( qmsg.msg.pt.x, qmsg.msg.pt.y );
@@ -1992,7 +1991,8 @@
msg->hwnd);
wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
/* Validate the update region to avoid infinite WM_PAINT loop */
- ValidateRect( msg->hwnd, NULL );
+ RedrawWindow( wndPtr->hwndSelf, NULL, 0,
+ RDW_NOFRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
}
END:
WIN_ReleaseWndPtr(wndPtr);
@@ -2079,7 +2079,7 @@
wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
/* Validate the update region to avoid infinite WM_PAINT loop */
RedrawWindow( wndPtr->hwndSelf, NULL, 0,
- RDW_FRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
+ RDW_NOFRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
}
END:
WIN_ReleaseWndPtr(wndPtr);
@@ -2161,7 +2161,8 @@
msg->hwnd);
wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
/* Validate the update region to avoid infinite WM_PAINT loop */
- ValidateRect( msg->hwnd, NULL );
+ RedrawWindow( wndPtr->hwndSelf, NULL, 0,
+ RDW_NOFRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
}
END:
WIN_ReleaseWndPtr(wndPtr);
diff --git a/windows/painting.c b/windows/painting.c
index 898d9d8..795fd1f 100644
--- a/windows/painting.c
+++ b/windows/painting.c
@@ -10,6 +10,7 @@
#include "wingdi.h"
#include "wine/winuser16.h"
#include "wine/unicode.h"
+#include "server.h"
#include "region.h"
#include "user.h"
#include "win.h"
@@ -47,6 +48,23 @@
/***********************************************************************
+ * add_paint_count
+ *
+ * Add an increment (normally 1 or -1) to the current paint count of a window.
+ */
+static void add_paint_count( HWND hwnd, int incr )
+{
+ SERVER_START_REQ( inc_queue_paint_count )
+ {
+ req->id = (void *)GetWindowThreadProcessId( hwnd, NULL );
+ req->incr = incr;
+ SERVER_CALL();
+ }
+ SERVER_END_REQ;
+}
+
+
+/***********************************************************************
* WIN_HaveToDelayNCPAINT
*
* Currently, in the Wine painting mechanism, the WM_NCPAINT message
@@ -210,7 +228,7 @@
DeleteObject( wnd->hrgnUpdate );
wnd->hrgnUpdate = 0;
if(!(wnd->flags & WIN_INTERNAL_PAINT))
- QUEUE_DecPaintCount( wnd->hmemTaskQ );
+ add_paint_count( wnd->hwndSelf, -1 );
wnd->flags &= ~WIN_NEEDS_ERASEBKGND;
}
@@ -325,7 +343,7 @@
}
if( ((hrgnUpdate = wndPtr->hrgnUpdate) != 0) || (wndPtr->flags & WIN_INTERNAL_PAINT))
- QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
+ add_paint_count( hwnd, -1 );
wndPtr->hrgnUpdate = 0;
wndPtr->flags &= ~WIN_INTERNAL_PAINT;
@@ -605,7 +623,7 @@
hRgn = wndPtr->hrgnUpdate; /* this is a trick that depends on code in PAINT_RedrawWindow() */
if( !bHadOne && !(wndPtr->flags & WIN_INTERNAL_PAINT) )
- QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
+ add_paint_count( wndPtr->hwndSelf, 1 );
if (flags & RDW_FRAME) wndPtr->flags |= WIN_NEEDS_NCPAINT;
if (flags & RDW_ERASE) wndPtr->flags |= WIN_NEEDS_ERASEBKGND;
@@ -637,7 +655,7 @@
{
wndPtr->flags &= ~WIN_NEEDS_ERASEBKGND;
if( !(wndPtr->flags & WIN_INTERNAL_PAINT) )
- QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
+ add_paint_count( wndPtr->hwndSelf, -1 );
}
}
@@ -711,13 +729,13 @@
if (flags & RDW_INTERNALPAINT)
{
if ( !wndPtr->hrgnUpdate && !(wndPtr->flags & WIN_INTERNAL_PAINT))
- QUEUE_IncPaintCount( wndPtr->hmemTaskQ );
+ add_paint_count( wndPtr->hwndSelf, 1 );
wndPtr->flags |= WIN_INTERNAL_PAINT;
}
else if (flags & RDW_NOINTERNALPAINT)
{
if ( !wndPtr->hrgnUpdate && (wndPtr->flags & WIN_INTERNAL_PAINT))
- QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
+ add_paint_count( wndPtr->hwndSelf, -1 );
wndPtr->flags &= ~WIN_INTERNAL_PAINT;
}
}
diff --git a/windows/queue.c b/windows/queue.c
index 1a9b4af..8ca8da2 100644
--- a/windows/queue.c
+++ b/windows/queue.c
@@ -580,38 +580,6 @@
/***********************************************************************
- * QUEUE_SetWakeBit
- *
- * See "Windows Internals", p.449
- */
-void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD set, WORD clear )
-{
- TRACE_(msg)("queue = %04x, set = %04x, clear = %04x\n",
- queue->self, set, clear );
- if (!queue->server_queue) return;
-
- SERVER_START_REQ( set_queue_bits )
- {
- req->handle = queue->server_queue;
- req->set = set;
- req->clear = clear;
- req->mask_cond = 0;
- SERVER_CALL();
- }
- SERVER_END_REQ;
-}
-
-
-/***********************************************************************
- * QUEUE_ClearWakeBit
- */
-void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit )
-{
- QUEUE_SetWakeBit( queue, 0, bit );
-}
-
-
-/***********************************************************************
* QUEUE_WaitBits
*
* See "Windows Internals", p.447
@@ -724,35 +692,6 @@
/***********************************************************************
- * QUEUE_RemoveMsg
- *
- * Remove a message from the queue (pos must be a valid position).
- */
-void QUEUE_RemoveMsg( MESSAGEQUEUE * msgQueue, QMSG *qmsg )
-{
- EnterCriticalSection( &msgQueue->cSection );
-
- /* set the linked list */
- if (qmsg->prevMsg)
- qmsg->prevMsg->nextMsg = qmsg->nextMsg;
-
- if (qmsg->nextMsg)
- qmsg->nextMsg->prevMsg = qmsg->prevMsg;
-
- if (msgQueue->firstMsg == qmsg)
- msgQueue->firstMsg = qmsg->nextMsg;
-
- if (msgQueue->lastMsg == qmsg)
- msgQueue->lastMsg = qmsg->prevMsg;
-
- /* deallocate the memory for the message */
- HeapFree( GetProcessHeap(), 0, qmsg );
-
- LeaveCriticalSection( &msgQueue->cSection );
-}
-
-
-/***********************************************************************
* QUEUE_CleanupWindow
*
* Cleanup the queue to account for a window being deleted.
@@ -787,39 +726,6 @@
}
-
-/***********************************************************************
- * QUEUE_IncPaintCount
- */
-void QUEUE_IncPaintCount( HQUEUE16 hQueue )
-{
- MESSAGEQUEUE *queue;
-
- if (!(queue = QUEUE_Lock( hQueue ))) return;
- EnterCriticalSection( &queue->cSection );
- queue->wPaintCount++;
- LeaveCriticalSection( &queue->cSection );
- QUEUE_SetWakeBit( queue, QS_PAINT, 0 );
- QUEUE_Unlock( queue );
-}
-
-
-/***********************************************************************
- * QUEUE_DecPaintCount
- */
-void QUEUE_DecPaintCount( HQUEUE16 hQueue )
-{
- MESSAGEQUEUE *queue;
-
- if (!(queue = QUEUE_Lock( hQueue ))) return;
- EnterCriticalSection( &queue->cSection );
- queue->wPaintCount--;
- if (!queue->wPaintCount) QUEUE_ClearWakeBit( queue, QS_PAINT );
- LeaveCriticalSection( &queue->cSection );
- QUEUE_Unlock( queue );
-}
-
-
/***********************************************************************
* PostQuitMessage (USER.6)
*/
diff --git a/windows/win.c b/windows/win.c
index 785dd22..4ea9d39 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -349,7 +349,7 @@
*
* Find a window that needs repaint.
*/
-HWND WIN_FindWinToRepaint( HWND hwnd, HQUEUE16 hQueue )
+HWND WIN_FindWinToRepaint( HWND hwnd )
{
HWND hwndRet;
WND *pWnd;
@@ -369,12 +369,12 @@
TRACE("skipping window %04x\n",
pWnd->hwndSelf );
}
- else if ((pWnd->hmemTaskQ == hQueue) &&
- (pWnd->hrgnUpdate || (pWnd->flags & WIN_INTERNAL_PAINT)))
+ else if ((pWnd->hrgnUpdate || (pWnd->flags & WIN_INTERNAL_PAINT)) &&
+ GetWindowThreadProcessId( pWnd->hwndSelf, NULL ) == GetCurrentThreadId())
break;
else if (pWnd->child )
- if ((hwndRet = WIN_FindWinToRepaint( pWnd->child->hwndSelf, hQueue )) )
+ if ((hwndRet = WIN_FindWinToRepaint( pWnd->child->hwndSelf )) )
{
WIN_ReleaseWndPtr(pWnd);
return hwndRet;
@@ -430,15 +430,8 @@
* Clear the update region to make sure no WM_PAINT messages will be
* generated for this window while processing the WM_NCDESTROY.
*/
- if ((wndPtr->hrgnUpdate) || (wndPtr->flags & WIN_INTERNAL_PAINT))
- {
- if (wndPtr->hrgnUpdate > 1)
- DeleteObject( wndPtr->hrgnUpdate );
-
- QUEUE_DecPaintCount( wndPtr->hmemTaskQ );
-
- wndPtr->hrgnUpdate = 0;
- }
+ RedrawWindow( wndPtr->hwndSelf, NULL, 0,
+ RDW_VALIDATE | RDW_NOFRAME | RDW_NOERASE | RDW_NOINTERNALPAINT | RDW_NOCHILDREN);
/*
* Send the WM_NCDESTROY to the window being destroyed.
@@ -484,58 +477,38 @@
}
/***********************************************************************
- * WIN_ResetQueueWindows
+ * WIN_DestroyThreadWindows
*
- * Reset the queue of all the children of a given window.
+ * Destroy all children of 'wnd' owned by the current thread.
* Return TRUE if something was done.
*/
-BOOL WIN_ResetQueueWindows( WND* wnd, HQUEUE16 hQueue, HQUEUE16 hNew )
+BOOL WIN_DestroyThreadWindows( HWND hwnd )
{
BOOL ret = FALSE;
+ WND *wnd = WIN_FindWndPtr( hwnd );
- if (hNew) /* Set a new queue */
+ if (!wnd) return FALSE;
+ while (wnd->child)
{
- for (wnd = WIN_LockWndPtr(wnd->child); (wnd);WIN_UpdateWndPtr(&wnd,wnd->next))
+ WND *tmp = WIN_LockWndPtr(wnd->child);
+ ret = FALSE;
+ while (tmp)
{
- if (wnd->hmemTaskQ == hQueue)
+ if (GetWindowThreadProcessId( tmp->hwndSelf, NULL ) == GetCurrentThreadId())
{
- wnd->hmemTaskQ = hNew;
+ DestroyWindow( tmp->hwndSelf );
ret = TRUE;
+ break;
}
- if (wnd->child)
- {
- ret |= WIN_ResetQueueWindows( wnd, hQueue, hNew );
+ if (tmp->child && WIN_DestroyThreadWindows( tmp->hwndSelf ))
+ ret = TRUE;
+ else
+ WIN_UpdateWndPtr(&tmp,tmp->next);
}
+ WIN_ReleaseWndPtr(tmp);
+ if (!ret) break;
}
- }
- else /* Queue is being destroyed */
- {
- while (wnd->child)
- {
- WND *tmp = WIN_LockWndPtr(wnd->child);
- WND *tmp2;
- ret = FALSE;
- while (tmp)
- {
- if (tmp->hmemTaskQ == hQueue)
- {
- DestroyWindow( tmp->hwndSelf );
- ret = TRUE;
- break;
- }
- tmp2 = WIN_LockWndPtr(tmp->child);
- if (tmp2 && WIN_ResetQueueWindows(tmp2,hQueue,0))
- ret = TRUE;
- else
- {
- WIN_UpdateWndPtr(&tmp,tmp->next);
- }
- WIN_ReleaseWndPtr(tmp2);
- }
- WIN_ReleaseWndPtr(tmp);
- if (!ret) break;
- }
- }
+ WIN_ReleaseWndPtr( wnd );
return ret;
}
@@ -1938,11 +1911,9 @@
case GWL_STYLE:
style.styleOld = wndPtr->dwStyle;
style.styleNew = newval;
- if (wndPtr->flags & WIN_ISWIN32)
- SendMessageA(hwnd,WM_STYLECHANGING,GWL_STYLE,(LPARAM)&style);
+ SendMessageA(hwnd,WM_STYLECHANGING,GWL_STYLE,(LPARAM)&style);
wndPtr->dwStyle = style.styleNew;
- if (wndPtr->flags & WIN_ISWIN32)
- SendMessageA(hwnd,WM_STYLECHANGED,GWL_STYLE,(LPARAM)&style);
+ SendMessageA(hwnd,WM_STYLECHANGED,GWL_STYLE,(LPARAM)&style);
retval = style.styleOld;
goto end;
@@ -1952,11 +1923,9 @@
case GWL_EXSTYLE:
style.styleOld = wndPtr->dwExStyle;
style.styleNew = newval;
- if (wndPtr->flags & WIN_ISWIN32)
- SendMessageA(hwnd,WM_STYLECHANGING,GWL_EXSTYLE,(LPARAM)&style);
+ SendMessageA(hwnd,WM_STYLECHANGING,GWL_EXSTYLE,(LPARAM)&style);
wndPtr->dwExStyle = newval;
- if (wndPtr->flags & WIN_ISWIN32)
- SendMessageA(hwnd,WM_STYLECHANGED,GWL_EXSTYLE,(LPARAM)&style);
+ SendMessageA(hwnd,WM_STYLECHANGED,GWL_EXSTYLE,(LPARAM)&style);
retval = style.styleOld;
goto end;