Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
568a91df
Commit
568a91df
authored
Oct 26, 2004
by
Mike McCormack
Committed by
Alexandre Julliard
Oct 26, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SERIALUI: convert to Unicode.
parent
a8b09d11
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
139 additions
and
69 deletions
+139
-69
comm.c
dlls/kernel/comm.c
+27
-24
Makefile.in
dlls/serialui/Makefile.in
+1
-0
confdlg.c
dlls/serialui/confdlg.c
+104
-41
serialui.spec
dlls/serialui/serialui.spec
+7
-4
No files found.
dlls/kernel/comm.c
View file @
568a91df
...
...
@@ -2098,7 +2098,8 @@ BOOL WINAPI GetCommProperties(
* The DLL should be loaded when the COMM port is opened, and closed
* when the COMM port is closed. - MJM 20 June 2000
***********************************************************************/
static
CHAR
lpszSerialUI
[]
=
"serialui.dll"
;
static
WCHAR
lpszSerialUI
[]
=
{
's'
,
'e'
,
'r'
,
'i'
,
'a'
,
'l'
,
'u'
,
'i'
,
'.'
,
'd'
,
'l'
,
'l'
,
0
};
/***********************************************************************
...
...
@@ -2129,7 +2130,7 @@ BOOL WINAPI CommConfigDialogA(
TRACE
(
"(%p %p %p)
\n
"
,
lpszDevice
,
hWnd
,
lpCommConfig
);
hConfigModule
=
LoadLibrary
A
(
lpszSerialUI
);
hConfigModule
=
LoadLibrary
W
(
lpszSerialUI
);
if
(
!
hConfigModule
)
return
FALSE
;
...
...
@@ -2140,7 +2141,7 @@ BOOL WINAPI CommConfigDialogA(
r
=
lpfnCommDialog
(
lpszDevice
,
hWnd
,
lpCommConfig
);
/* UnloadLibrary(hConfigModule); */
FreeLibrary
(
hConfigModule
);
return
r
;
}
...
...
@@ -2236,29 +2237,26 @@ BOOL WINAPI SetCommConfig(
*
* True if the device was found and the defaults set, false otherwise
*/
BOOL
WINAPI
SetDefaultCommConfig
A
(
LPC
STR
lpszDevice
,
/* [in] The ascii name of the device targeted for configuration. */
BOOL
WINAPI
SetDefaultCommConfig
W
(
LPC
WSTR
lpszDevice
,
/* [in] The ascii name of the device targeted for configuration. */
LPCOMMCONFIG
lpCommConfig
,
/* [in] The default configuration for the device. */
DWORD
dwSize
)
/* [in] The number of bytes in the configuration structure. */
{
FARPROC
lpfnSetDefaultCommConfig
;
HMODULE
hConfigModule
;
BOOL
r
;
BOOL
r
=
FALSE
;
TRACE
(
"(%p %p %lx)
\n
"
,
lpszDevice
,
lpCommConfig
,
dwSize
);
hConfigModule
=
LoadLibrary
A
(
lpszSerialUI
);
hConfigModule
=
LoadLibrary
W
(
lpszSerialUI
);
if
(
!
hConfigModule
)
return
FALSE
;
return
r
;
lpfnSetDefaultCommConfig
=
GetProcAddress
(
hConfigModule
,
(
LPCSTR
)
4L
);
lpfnSetDefaultCommConfig
=
GetProcAddress
(
hConfigModule
,
"drvSetDefaultCommConfigW"
);
if
(
lpfnSetDefaultCommConfig
)
r
=
lpfnSetDefaultCommConfig
(
lpszDevice
,
lpCommConfig
,
dwSize
);
if
(
!
lpfnSetDefaultCommConfig
)
return
TRUE
;
r
=
lpfnSetDefaultCommConfig
(
lpszDevice
,
lpCommConfig
,
dwSize
);
/* UnloadLibrary(hConfigModule); */
FreeLibrary
(
hConfigModule
);
return
r
;
}
...
...
@@ -2273,21 +2271,26 @@ BOOL WINAPI SetDefaultCommConfigA(
* RETURNS
*
*/
BOOL
WINAPI
SetDefaultCommConfig
W
(
LPC
WSTR
lpszDevice
,
/* [in] The unicode name of the device targeted for configuration. */
BOOL
WINAPI
SetDefaultCommConfig
A
(
LPC
STR
lpszDevice
,
/* [in] The unicode name of the device targeted for configuration. */
LPCOMMCONFIG
lpCommConfig
,
/* [in] The default configuration for the device. */
DWORD
dwSize
)
/* [in] The number of bytes in the configuration structure. */
{
BOOL
r
;
LPSTR
lpDeviceA
;
LPWSTR
lpDeviceW
=
NULL
;
DWORD
len
;
TRACE
(
"(%s %p %lx)
\n
"
,
debugstr_
w
(
lpszDevice
),
lpCommConfig
,
dwSize
);
TRACE
(
"(%s %p %lx)
\n
"
,
debugstr_
a
(
lpszDevice
),
lpCommConfig
,
dwSize
);
lpDeviceA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
lpszDevice
);
if
(
lpDeviceA
)
return
FALSE
;
r
=
SetDefaultCommConfigA
(
lpDeviceA
,
lpCommConfig
,
dwSize
);
HeapFree
(
GetProcessHeap
(),
0
,
lpDeviceA
);
if
(
lpszDevice
)
{
len
=
MultiByteToWideChar
(
CP_ACP
,
0
,
lpszDevice
,
-
1
,
NULL
,
0
);
lpDeviceW
=
HeapAlloc
(
GetProcessHeap
(),
0
,
len
*
sizeof
(
WCHAR
)
);
MultiByteToWideChar
(
CP_ACP
,
0
,
lpszDevice
,
-
1
,
lpDeviceW
,
len
);
}
r
=
SetDefaultCommConfigW
(
lpDeviceW
,
lpCommConfig
,
dwSize
);
if
(
lpDeviceW
)
HeapFree
(
GetProcessHeap
(),
0
,
lpDeviceW
);
return
r
;
}
...
...
dlls/serialui/Makefile.in
View file @
568a91df
...
...
@@ -4,6 +4,7 @@ SRCDIR = @srcdir@
VPATH
=
@srcdir@
MODULE
=
serialui.dll
IMPORTS
=
user32 advapi32 kernel32
EXTRALIBS
=
$(LIBUNICODE)
C_SRCS
=
\
confdlg.c
...
...
dlls/serialui/confdlg.c
View file @
568a91df
...
...
@@ -11,7 +11,7 @@
*
* Basic structure copied from COMCTL32 code.
*
* Copyright 2000 Mike McCormack
* Copyright 2000
, 2004
Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
...
...
@@ -30,6 +30,7 @@
#include "config.h"
#include "wine/port.h"
#include "wine/unicode.h"
#include <string.h>
#include <stdarg.h>
...
...
@@ -88,7 +89,7 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
*/
typedef
LPVOID
LPDEVICE_INFO
;
typedef
LPVOID
LPFNADDPROPSHEETPAGE
;
BOOL
WINAPI
SERIALUI_
EnumPropPages
(
LPDEVICE_INFO
pdi
,
LPFNADDPROPSHEETPAGE
pfnAdd
,
LPARAM
lParam
)
BOOL
WINAPI
EnumPropPages
(
LPDEVICE_INFO
pdi
,
LPFNADDPROPSHEETPAGE
pfnAdd
,
LPARAM
lParam
)
{
FIXME
(
"(%p %p %lx)
\n
"
,
pdi
,
pfnAdd
,
lParam
);
return
FALSE
;
...
...
@@ -243,7 +244,7 @@ static BOOL SERIALUI_MakeBaudEnum(LPDWORD lpdwBaudRate)
typedef
struct
tagSERIALUI_DialogInfo
{
LPCSTR
lpszDevice
;
LPC
W
STR
lpszDevice
;
LPCOMMCONFIG
lpCommConfig
;
BOOL
bConvert
;
/* baud rate was converted to a DWORD */
DWORD
dwFlowControl
;
/* old flow control */
...
...
@@ -343,7 +344,10 @@ static void SERIALUI_DialogInfoToDCB(HWND hDlg, SERIALUI_DialogInfo *info)
*/
INT_PTR
CALLBACK
SERIALUI_ConfigDialogProc
(
HWND
hWnd
,
UINT
uMsg
,
WPARAM
wParam
,
LPARAM
lParam
)
{
CHAR
szTitle
[
30
];
static
const
WCHAR
szSettings
[]
=
{
'S'
,
'e'
,
't'
,
't'
,
'i'
,
'n'
,
'g'
,
's'
,
' '
,
'f'
,
'o'
,
'r'
,
' '
,
0
};
WCHAR
szTitle
[
40
];
SERIALUI_DialogInfo
*
info
;
switch
(
uMsg
)
...
...
@@ -352,9 +356,10 @@ INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,
info
=
(
SERIALUI_DialogInfo
*
)
lParam
;
if
(
!
info
)
return
FALSE
;
SetWindowLongA
(
hWnd
,
DWL_USER
,
lParam
);
snprintf
(
szTitle
,
sizeof
(
szTitle
),
"Settings for %s"
,
info
->
lpszDevice
);
SetWindowTextA
(
hWnd
,
szTitle
);
SetWindowLongW
(
hWnd
,
DWL_USER
,
lParam
);
strcpyW
(
szTitle
,
szSettings
);
strcatW
(
szTitle
,
info
->
lpszDevice
);
SetWindowTextW
(
hWnd
,
szTitle
);
SERIALUI_DCBToDialogInfo
(
hWnd
,
info
);
return
TRUE
;
...
...
@@ -362,7 +367,7 @@ INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,
{
WORD
wID
=
LOWORD
(
wParam
);
info
=
(
SERIALUI_DialogInfo
*
)
GetWindowLong
A
(
hWnd
,
DWL_USER
);
info
=
(
SERIALUI_DialogInfo
*
)
GetWindowLong
W
(
hWnd
,
DWL_USER
);
if
(
!
info
)
EndDialog
(
hWnd
,
0
);
switch
(
wID
)
...
...
@@ -378,7 +383,7 @@ INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,
case
ID_GETDEFAULT
:
{
DWORD
r
,
dwConfSize
=
sizeof
(
COMMCONFIG
);
r
=
GetDefaultCommConfig
A
(
info
->
lpszDevice
,
r
=
GetDefaultCommConfig
W
(
info
->
lpszDevice
,
info
->
lpCommConfig
,
&
dwConfSize
);
if
(
!
r
)
MessageBoxA
(
hWnd
,
"Failed"
,
"GetDefaultCommConfig"
,
MB_OK
);
...
...
@@ -389,7 +394,7 @@ INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,
{
DWORD
r
;
SERIALUI_DialogInfoToDCB
(
hWnd
,
info
);
r
=
SetDefaultCommConfig
A
(
info
->
lpszDevice
,
r
=
SetDefaultCommConfig
W
(
info
->
lpszDevice
,
info
->
lpCommConfig
,
sizeof
(
COMMCONFIG
));
if
(
!
r
)
MessageBoxA
(
hWnd
,
"Failed"
,
"GetDefaultCommConfig"
,
MB_OK
);
...
...
@@ -403,13 +408,32 @@ INT_PTR CALLBACK SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,
}
}
static
LPWSTR
SERIALUI_strdup
(
LPCSTR
str
)
{
DWORD
len
;
LPWSTR
strW
;
if
(
!
str
)
return
NULL
;
len
=
MultiByteToWideChar
(
CP_ACP
,
0
,
str
,
-
1
,
NULL
,
0
);
strW
=
HeapAlloc
(
GetProcessHeap
(),
0
,
len
*
sizeof
(
WCHAR
)
);
MultiByteToWideChar
(
CP_ACP
,
0
,
str
,
-
1
,
strW
,
len
);
return
strW
;
}
static
VOID
SERIALUI_strfree
(
LPWSTR
strW
)
{
if
(
strW
)
HeapFree
(
GetProcessHeap
(),
0
,
strW
);
}
/***********************************************************************
* drvCommConfigDialog
(SERIALUI.3
)
* drvCommConfigDialog
W (SERIALUI.@
)
*
* Used by Win9x KERNEL to show a dialog for configuring a COMM port.
*/
BOOL
WINAPI
SERIALUI_CommConfigDialog
(
LPCSTR
lpszName
,
BOOL
WINAPI
drvCommConfigDialogW
(
LPC
W
STR
lpszName
,
HWND
hWndParent
,
LPCOMMCONFIG
lpCommConfig
)
{
...
...
@@ -423,31 +447,47 @@ BOOL WINAPI SERIALUI_CommConfigDialog(
if
(
!
lpCommConfig
)
return
FALSE
;
return
DialogBoxParam
A
(
SERIALUI_hModule
,
MAKEINTRESOURCE
A
(
IDD_SERIALUICONFIG
),
return
DialogBoxParam
W
(
SERIALUI_hModule
,
MAKEINTRESOURCE
W
(
IDD_SERIALUICONFIG
),
hWndParent
,
SERIALUI_ConfigDialogProc
,
(
LPARAM
)
&
info
);
}
static
LPCSTR
lpszCommKey
=
"System
\\
CurrentControlSet
\\
Services
\\
Class
\\
Ports"
;
static
LPCSTR
lpszDCB
=
"DCB"
;
/***********************************************************************
* drvCommConfigDialogA (SERIALUI.@)
*/
BOOL
WINAPI
drvCommConfigDialogA
(
LPCSTR
lpszName
,
HWND
hWndParent
,
LPCOMMCONFIG
lpCommConfig
)
{
LPWSTR
strW
=
SERIALUI_strdup
(
lpszName
);
BOOL
r
=
drvCommConfigDialogW
(
strW
,
hWndParent
,
lpCommConfig
);
SERIALUI_strfree
(
strW
);
return
r
;
}
static
const
WCHAR
lpszCommKey
[]
=
{
'S'
,
'y'
,
's'
,
't'
,
'e'
,
'm'
,
'\\'
,
'C'
,
'u'
,
'r'
,
'r'
,
'e'
,
'n'
,
't'
,
'C'
,
'o'
,
'n'
,
't'
,
'r'
,
'o'
,
'l'
,
'S'
,
'e'
,
't'
,
'\\'
,
'S'
,
'e'
,
'r'
,
'v'
,
'i'
,
'c'
,
'e'
,
's'
,
'\\'
,
'C'
,
'l'
,
'a'
,
's'
,
's'
,
'\\'
,
'P'
,
'o'
,
'r'
,
't'
,
's'
,
0
};
static
const
WCHAR
lpszDCB
[]
=
{
'D'
,
'C'
,
'B'
,
0
};
/***********************************************************************
* drvSetDefaultCommConfig
(SERIALUI.4
)
* drvSetDefaultCommConfig
W (SERIALUI.@
)
*
* Used by Win98 KERNEL to set the default config for a COMM port
* FIXME: uses the wrong registry key... should use a digit, not
* the comm port name.
*/
BOOL
WINAPI
SERIALUI_SetDefaultCommConfig
(
LPCSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
DWORD
dwSize
)
{
BOOL
WINAPI
drvSetDefaultCommConfigW
(
LPCWSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
DWORD
dwSize
)
{
HKEY
hKeyReg
=
0
,
hKeyPort
=
0
;
CHAR
szKeyName
[
100
];
W
CHAR
szKeyName
[
100
];
DWORD
r
,
dwDCBSize
;
static
const
WCHAR
fmt
[]
=
{
'%'
,
's'
,
'\\'
,
'%'
,
's'
,
0
};
TRACE
(
"%p %p %lx
\n
"
,
lpszDevice
,
lpCommConfig
,
dwSize
);
...
...
@@ -457,17 +497,17 @@ BOOL WINAPI SERIALUI_SetDefaultCommConfig(
if
(
dwSize
<
sizeof
(
COMMCONFIG
))
return
FALSE
;
r
=
RegConnectRegistry
A
(
NULL
,
HKEY_LOCAL_MACHINE
,
&
hKeyReg
);
r
=
RegConnectRegistry
W
(
NULL
,
HKEY_LOCAL_MACHINE
,
&
hKeyReg
);
if
(
r
!=
ERROR_SUCCESS
)
return
FALSE
;
snprintf
(
szKeyName
,
sizeof
(
szKeyName
),
"%s
\\
%s"
,
lpszCommKey
,
lpszDevice
);
r
=
RegCreateKey
A
(
hKeyReg
,
szKeyName
,
&
hKeyPort
);
snprintf
W
(
szKeyName
,
sizeof
(
szKeyName
)
/
sizeof
(
WCHAR
),
fmt
,
lpszCommKey
,
lpszDevice
);
r
=
RegCreateKey
W
(
hKeyReg
,
szKeyName
,
&
hKeyPort
);
if
(
r
==
ERROR_SUCCESS
)
{
dwDCBSize
=
sizeof
(
DCB
);
r
=
RegSetValueEx
A
(
hKeyPort
,
lpszDCB
,
0
,
REG_BINARY
,
(
LP
STR
)
&
lpCommConfig
->
dcb
,
dwDCBSize
);
r
=
RegSetValueEx
W
(
hKeyPort
,
lpszDCB
,
0
,
REG_BINARY
,
(
LP
BYTE
)
&
lpCommConfig
->
dcb
,
dwDCBSize
);
TRACE
(
"write key r=%ld
\n
"
,
r
);
RegCloseKey
(
hKeyPort
);
}
...
...
@@ -478,20 +518,31 @@ BOOL WINAPI SERIALUI_SetDefaultCommConfig(
}
/***********************************************************************
* drvGetDefaultCommConfig (SERIALUI.5)
* drvSetDefaultCommConfigA (SERIALUI.@)
*/
BOOL
WINAPI
drvSetDefaultCommConfigA
(
LPCSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
DWORD
dwSize
)
{
LPWSTR
strW
=
SERIALUI_strdup
(
lpszDevice
);
BOOL
r
=
drvSetDefaultCommConfigW
(
strW
,
lpCommConfig
,
dwSize
);
SERIALUI_strfree
(
strW
);
return
r
;
}
/***********************************************************************
* drvGetDefaultCommConfigW (SERIALUI.@)
*
* Used by Win9x KERNEL to get the default config for a COMM port
* FIXME: uses the wrong registry key... should use a digit, not
* the comm port name.
*/
BOOL
WINAPI
SERIALUI_GetDefaultCommConfig
(
LPCSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
LPDWORD
lpdwSize
)
{
BOOL
WINAPI
drvGetDefaultCommConfigW
(
LPCWSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
LPDWORD
lpdwSize
)
{
HKEY
hKeyReg
,
hKeyPort
;
CHAR
szKeyName
[
100
];
W
CHAR
szKeyName
[
100
];
DWORD
r
,
dwSize
,
dwType
;
static
const
WCHAR
fmt
[]
=
{
'%'
,
's'
,
'\\'
,
'%'
,
's'
,
0
};
TRACE
(
"%p %p %p
\n
"
,
lpszDevice
,
lpCommConfig
,
lpdwSize
);
...
...
@@ -509,18 +560,18 @@ BOOL WINAPI SERIALUI_GetDefaultCommConfig(
lpCommConfig
->
dwSize
=
sizeof
(
COMMCONFIG
);
lpCommConfig
->
wVersion
=
1
;
r
=
RegConnectRegistry
A
(
NULL
,
HKEY_LOCAL_MACHINE
,
&
hKeyReg
);
r
=
RegConnectRegistry
W
(
NULL
,
HKEY_LOCAL_MACHINE
,
&
hKeyReg
);
if
(
r
!=
ERROR_SUCCESS
)
return
FALSE
;
snprintf
(
szKeyName
,
sizeof
(
szKeyName
),
"%s
\\
%s"
,
lpszCommKey
,
lpszDevice
);
r
=
RegOpenKey
A
(
hKeyReg
,
szKeyName
,
&
hKeyPort
);
snprintf
W
(
szKeyName
,
sizeof
(
szKeyName
)
/
sizeof
(
WCHAR
),
fmt
,
lpszCommKey
,
lpszDevice
);
r
=
RegOpenKey
W
(
hKeyReg
,
szKeyName
,
&
hKeyPort
);
if
(
r
==
ERROR_SUCCESS
)
{
dwSize
=
sizeof
(
DCB
);
dwType
=
0
;
r
=
RegQueryValueEx
A
(
hKeyPort
,
lpszDCB
,
NULL
,
&
dwType
,
(
LP
STR
)
&
lpCommConfig
->
dcb
,
&
dwSize
);
r
=
RegQueryValueEx
W
(
hKeyPort
,
lpszDCB
,
NULL
,
&
dwType
,
(
LP
BYTE
)
&
lpCommConfig
->
dcb
,
&
dwSize
);
if
((
r
==
ERROR_SUCCESS
)
&&
(
dwType
!=
REG_BINARY
))
r
=
1
;
if
((
r
==
ERROR_SUCCESS
)
&&
(
dwSize
!=
sizeof
(
DCB
)))
...
...
@@ -546,3 +597,15 @@ BOOL WINAPI SERIALUI_GetDefaultCommConfig(
return
(
r
==
ERROR_SUCCESS
);
}
/***********************************************************************
* drvGetDefaultCommConfigA (SERIALUI.@)
*/
BOOL
WINAPI
drvGetDefaultCommConfigA
(
LPCSTR
lpszDevice
,
LPCOMMCONFIG
lpCommConfig
,
LPDWORD
lpdwSize
)
{
LPWSTR
strW
=
SERIALUI_strdup
(
lpszDevice
);
BOOL
r
=
drvGetDefaultCommConfigW
(
strW
,
lpCommConfig
,
lpdwSize
);
SERIALUI_strfree
(
strW
);
return
r
;
}
dlls/serialui/serialui.spec
View file @
568a91df
2 stdcall EnumPropPages(ptr ptr ptr) SERIALUI_EnumPropPages
3 stdcall drvCommConfigDialog(ptr long ptr) SERIALUI_CommConfigDialog
4 stdcall drvSetDefaultCommConfig(str ptr long) SERIALUI_SetDefaultCommConfig
5 stdcall drvGetDefaultCommConfig(str ptr ptr) SERIALUI_GetDefaultCommConfig
1 stdcall EnumPropPages(ptr ptr ptr)
2 stdcall drvCommConfigDialogW(ptr long ptr)
3 stdcall drvCommConfigDialogA(ptr long ptr)
4 stdcall drvSetDefaultCommConfigW(wstr ptr long)
5 stdcall drvSetDefaultCommConfigA(str ptr long)
6 stdcall drvGetDefaultCommConfigW(wstr ptr ptr)
7 stdcall drvGetDefaultCommConfigA(str ptr ptr)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment