Merge branch 'net-cleanups'
This commit is contained in:
commit
bbd5a13030
BIN
coaa1090.obj
BIN
coaa1090.obj
Binary file not shown.
Binary file not shown.
|
@ -414,6 +414,7 @@ void showHelp(void) {
|
|||
"--net Enable networking\n"
|
||||
"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
|
||||
"--net-only Enable just networking, no RTL device or file used\n"
|
||||
"--net-bind-address <ip> IP address to bind to (default: Any; Use 127.0.0.1 for private)\n"
|
||||
"--net-http-port <port> HTTP server port (default: 8080)\n"
|
||||
"--net-ri-port <port> TCP raw input listen port (default: 30001)\n"
|
||||
"--net-ro-port <port> TCP raw output listen port (default: 30002)\n"
|
||||
|
@ -724,6 +725,8 @@ int main(int argc, char **argv) {
|
|||
Modes.net_output_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bi-port") && more) {
|
||||
Modes.net_input_beast_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-bind-address") && more) {
|
||||
Modes.net_bind_address = strdup(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-http-port") && more) {
|
||||
Modes.net_http_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-sbs-port") && more) {
|
||||
|
|
|
@ -131,6 +131,10 @@ SOURCE=.\winstubs.h
|
|||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\Dump1090.rc
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Library Files"
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
// MinorVer changes when additional features are added, but not for bug fixes (range 00-99)
|
||||
// DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update
|
||||
//
|
||||
#define MODES_DUMP1090_VERSION "1.09.0608.14"
|
||||
#define MODES_DUMP1090_VERSION "1.10.3010.14"
|
||||
|
||||
// ============================= Include files ==========================
|
||||
|
||||
|
@ -311,6 +311,7 @@ struct { // Internal state
|
|||
int net_input_raw_port; // Raw input TCP port
|
||||
int net_output_beast_port; // Beast output TCP port
|
||||
int net_input_beast_port; // Beast input TCP port
|
||||
char *net_bind_address; // Bind address
|
||||
int net_http_port; // HTTP port
|
||||
int net_sndbuf_size; // TCP output buffer size (64Kb * 2^n)
|
||||
int quiet; // Suppress stdout
|
||||
|
|
115
dump1090.rc
Normal file
115
dump1090.rc
Normal file
|
@ -0,0 +1,115 @@
|
|||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#define APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "windows.h"
|
||||
#undef APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "ntverp.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,10,2910,14
|
||||
PRODUCTVERSION 1,10,2910,14
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x3L
|
||||
FILESUBTYPE 0x7L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "Dump1090 for Win32 \0"
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "Mode A/C/S decoder for RTL Dongles\0"
|
||||
VALUE "FileVersion", "1, 10, 2910, 14\0"
|
||||
VALUE "InternalName", "DUMP1090.EXE\0"
|
||||
VALUE "LegalCopyright", "Copyright © 2012 by Salvatore Sanfilippo <antirez@gmail.com>\r\nCopyright © 2014 by Malcolm Robb <support@attavionics.com>\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "DUMP1090.EXE\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "DUMP1090\0"
|
||||
VALUE "ProductVersion", "1, 10, 2910, 14\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""windows.h""\r\n"
|
||||
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""ntverp.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
74
net_io.c
74
net_io.c
|
@ -86,7 +86,7 @@ void modesInitNet(void) {
|
|||
for (j = 0; j < MODES_NET_SERVICES_NUM; j++) {
|
||||
services[j].enabled = (services[j].port != 0);
|
||||
if (services[j].enabled) {
|
||||
int s = anetTcpServer(Modes.aneterr, services[j].port, NULL);
|
||||
int s = anetTcpServer(Modes.aneterr, services[j].port, Modes.net_bind_address);
|
||||
if (s == -1) {
|
||||
fprintf(stderr, "Error opening the listening port %d (%s): %s\n",
|
||||
services[j].port, services[j].descr, Modes.aneterr);
|
||||
|
@ -159,7 +159,7 @@ struct client * modesAcceptClients(void) {
|
|||
//
|
||||
// On error free the client, collect the structure, adjust maxfd if needed.
|
||||
//
|
||||
void modesFreeClient(struct client *c) {
|
||||
void modesCloseClient(struct client *c) {
|
||||
int j;
|
||||
|
||||
// Clean up, but defer removing from the list until modesNetCleanup().
|
||||
|
@ -200,7 +200,7 @@ static void flushWrites(struct net_writer *writer) {
|
|||
int nwritten = send(c->fd, writer->data, writer->dataUsed, 0 );
|
||||
#endif
|
||||
if (nwritten != writer->dataUsed) {
|
||||
modesFreeClient(c);
|
||||
modesCloseClient(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -744,6 +744,7 @@ int handleHTTPRequest(struct client *c, char *p) {
|
|||
char hdr[512];
|
||||
int clen, hdrlen;
|
||||
int httpver, keepalive;
|
||||
int statuscode = 500;
|
||||
char *url, *content;
|
||||
char ctype[48];
|
||||
char getFile[1024];
|
||||
|
@ -756,11 +757,12 @@ int handleHTTPRequest(struct client *c, char *p) {
|
|||
httpver = (strstr(p, "HTTP/1.1") != NULL) ? 11 : 10;
|
||||
if (httpver == 10) {
|
||||
// HTTP 1.0 defaults to close, unless otherwise specified.
|
||||
keepalive = strstr(p, "Connection: keep-alive") != NULL;
|
||||
//keepalive = strstr(p, "Connection: keep-alive") != NULL;
|
||||
} else if (httpver == 11) {
|
||||
// HTTP 1.1 defaults to keep-alive, unless close is specified.
|
||||
keepalive = strstr(p, "Connection: close") == NULL;
|
||||
//keepalive = strstr(p, "Connection: close") == NULL;
|
||||
}
|
||||
keepalive = 0;
|
||||
|
||||
// Identify he URL.
|
||||
p = strchr(p,' ');
|
||||
|
@ -785,22 +787,35 @@ int handleHTTPRequest(struct client *c, char *p) {
|
|||
// "/" -> Our google map application.
|
||||
// "/data.json" -> Our ajax request to update planes.
|
||||
if (strstr(url, "/data.json")) {
|
||||
statuscode = 200;
|
||||
content = aircraftsToJson(&clen);
|
||||
//snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON);
|
||||
} else {
|
||||
struct stat sbuf;
|
||||
int fd = -1;
|
||||
char *rp, *hrp;
|
||||
|
||||
if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) {
|
||||
content = (char *) malloc(sbuf.st_size);
|
||||
if (read(fd, content, sbuf.st_size) == -1) {
|
||||
snprintf(content, sbuf.st_size, "Error reading from file: %s", strerror(errno));
|
||||
rp = realpath(getFile, NULL);
|
||||
hrp = realpath(HTMLPATH, NULL);
|
||||
hrp = (hrp ? hrp : HTMLPATH);
|
||||
clen = -1;
|
||||
content = strdup("Server error occured");
|
||||
if (rp && (!strncmp(hrp, rp, strlen(hrp)))) {
|
||||
if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) {
|
||||
content = (char *) realloc(content, sbuf.st_size);
|
||||
if (read(fd, content, sbuf.st_size) != -1) {
|
||||
clen = sbuf.st_size;
|
||||
statuscode = 200;
|
||||
}
|
||||
}
|
||||
clen = sbuf.st_size;
|
||||
} else {
|
||||
char buf[128];
|
||||
clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s", strerror(errno));
|
||||
content = strdup(buf);
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
if (clen < 0) {
|
||||
content = realloc(content, 128);
|
||||
clen = snprintf(content, 128,"Error opening HTML file: %s", strerror(errno));
|
||||
statuscode = 404;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
|
@ -824,7 +839,7 @@ int handleHTTPRequest(struct client *c, char *p) {
|
|||
|
||||
// Create the header and send the reply
|
||||
hdrlen = snprintf(hdr, sizeof(hdr),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"HTTP/1.1 %d \r\n"
|
||||
"Server: Dump1090\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
|
@ -832,6 +847,7 @@ int handleHTTPRequest(struct client *c, char *p) {
|
|||
"Cache-Control: no-cache, must-revalidate\r\n"
|
||||
"Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||
"\r\n",
|
||||
statuscode,
|
||||
ctype,
|
||||
keepalive ? "keep-alive" : "close",
|
||||
clen);
|
||||
|
@ -899,17 +915,25 @@ void modesReadFromClient(struct client *c, char *sep,
|
|||
if (nread != left) {
|
||||
bContinue = 0;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
if ( (nread < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || nread == 0 ) { // Error, or end of file
|
||||
#else
|
||||
if ( (nread < 0) && (errno != EWOULDBLOCK)) { // Error, or end of file
|
||||
#endif
|
||||
modesFreeClient(c);
|
||||
|
||||
if (nread == 0) { // End of file
|
||||
modesCloseClient(c);
|
||||
return;
|
||||
}
|
||||
if (nread <= 0) {
|
||||
break; // Serve next client
|
||||
|
||||
#ifndef _WIN32
|
||||
if (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { // No data available (not really an error)
|
||||
#else
|
||||
if (nread < 0 && errno == EWOULDBLOCK) { // No data available (not really an error)
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (nread < 0) { // Other errors
|
||||
modesCloseClient(c);
|
||||
return;
|
||||
}
|
||||
|
||||
c->buflen += nread;
|
||||
|
||||
// Always null-term so we are free to use strstr() (it won't affect binary case)
|
||||
|
@ -952,7 +976,7 @@ void modesReadFromClient(struct client *c, char *sep,
|
|||
}
|
||||
// Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler.
|
||||
if (handler(c, s)) {
|
||||
modesFreeClient(c);
|
||||
modesCloseClient(c);
|
||||
return;
|
||||
}
|
||||
fullmsg = 1;
|
||||
|
@ -968,7 +992,7 @@ void modesReadFromClient(struct client *c, char *sep,
|
|||
while ((e = strstr(s, sep)) != NULL) { // end of first message if found
|
||||
*e = '\0'; // The handler expects null terminated strings
|
||||
if (handler(c, s)) { // Pass message to handler.
|
||||
modesFreeClient(c); // Handler returns 1 on error to signal we .
|
||||
modesCloseClient(c); // Handler returns 1 on error to signal we .
|
||||
return; // should close the client connection
|
||||
}
|
||||
s = e + strlen(sep); // Move to start of next message
|
||||
|
@ -980,7 +1004,7 @@ void modesReadFromClient(struct client *c, char *sep,
|
|||
c->buflen = &(c->buf[c->buflen]) - s; // Update the unprocessed buffer length
|
||||
memmove(c->buf, s, c->buflen); // Move what's remaining to the start of the buffer
|
||||
} else { // If no message was decoded process the next client
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
|
||||
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry"></script>
|
||||
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=geometry"></script>
|
||||
<script type="text/javascript" src="config.js"></script>
|
||||
<script type="text/javascript" src="planeObject.js"></script>
|
||||
<script type="text/javascript" src="options.js"></script>
|
||||
|
@ -13,6 +14,7 @@
|
|||
<script type="text/javascript" src="coolclock/excanvas.js"></script>
|
||||
<script type="text/javascript" src="coolclock/coolclock.js"></script>
|
||||
<script type="text/javascript" src="coolclock/moreskins.js"></script>
|
||||
<title>DUMP1090</title>
|
||||
</head>
|
||||
<body onload="initialize()">
|
||||
<div id="dialog-modal" title="Basic modal dialog" style="display:none;">
|
||||
|
|
|
@ -267,8 +267,10 @@ function refreshSelected() {
|
|||
} else if (selected && selected.squawk == 7700) { // General Emergency
|
||||
html += ' <span class="squawk7700"> Squawking: General Emergency </span>';
|
||||
} else if (selected && selected.flight != '') {
|
||||
html += ' <a href="http://fr24.com/'+selected.flight+'" target="_blank">[FR24]</a>';
|
||||
html += ' <a href="http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?';
|
||||
html += 'flightNumber='+selected.flight+'" target="_blank">[FlightStats]</a>';
|
||||
html += ' <a href="http://flightaware.com/live/flight/'+selected.flight+'" target="_blank">[FlightAware]</a>';
|
||||
}
|
||||
html += '<td></tr>';
|
||||
|
||||
|
@ -330,12 +332,12 @@ function refreshSelected() {
|
|||
dist /= 1852;
|
||||
}
|
||||
dist = (Math.round((dist)*10)/10).toFixed(1);
|
||||
html += '<tr><td colspan="' + columns + '">Distance from Site: ' + dist +
|
||||
html += '<tr><td colspan="' + columns + '" align="center">Distance from Site: ' + dist +
|
||||
(Metric ? ' km' : ' NM') + '</td></tr>';
|
||||
} // End of SiteShow
|
||||
} else {
|
||||
if (SiteShow) {
|
||||
html += '<tr><td colspan="' + columns + '">Distance from Site: n/a ' +
|
||||
html += '<tr><td colspan="' + columns + '" align="center">Distance from Site: n/a ' +
|
||||
(Metric ? ' km' : ' NM') + '</td></tr>';
|
||||
} else {
|
||||
html += 'n/a</td></tr>';
|
||||
|
@ -403,11 +405,16 @@ function refreshTableInfo() {
|
|||
'align="right">Altitude</td>';
|
||||
html += '<td onclick="setASC_DESC(\'4\');sortTable(\'tableinfo\',\'4\');" ' +
|
||||
'align="right">Speed</td>';
|
||||
html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'5\');" ' +
|
||||
// Add distance column header to table if site coordinates are provided
|
||||
if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'5\');" ' +
|
||||
'align="right">Distance</td>';
|
||||
}
|
||||
html += '<td onclick="setASC_DESC(\'5\');sortTable(\'tableinfo\',\'6\');" ' +
|
||||
'align="right">Track</td>';
|
||||
html += '<td onclick="setASC_DESC(\'6\');sortTable(\'tableinfo\',\'6\');" ' +
|
||||
html += '<td onclick="setASC_DESC(\'6\');sortTable(\'tableinfo\',\'7\');" ' +
|
||||
'align="right">Msgs</td>';
|
||||
html += '<td onclick="setASC_DESC(\'7\');sortTable(\'tableinfo\',\'7\');" ' +
|
||||
html += '<td onclick="setASC_DESC(\'7\');sortTable(\'tableinfo\',\'8\');" ' +
|
||||
'align="right">Seen</td></thead><tbody>';
|
||||
for (var tablep in Planes) {
|
||||
var tableplane = Planes[tablep]
|
||||
|
@ -451,6 +458,25 @@ function refreshTableInfo() {
|
|||
html += '<td align="right">' + tableplane.altitude + '</td>';
|
||||
html += '<td align="right">' + tableplane.speed + '</td>';
|
||||
}
|
||||
// Add distance column to table if site coordinates are provided
|
||||
if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
html += '<td align="right">';
|
||||
if (tableplane.vPosition) {
|
||||
var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon);
|
||||
var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude);
|
||||
var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon);
|
||||
if (Metric) {
|
||||
dist /= 1000;
|
||||
} else {
|
||||
dist /= 1852;
|
||||
}
|
||||
dist = (Math.round((dist)*10)/10).toFixed(1);
|
||||
html += dist;
|
||||
} else {
|
||||
html += '0';
|
||||
}
|
||||
html += '</td>';
|
||||
}
|
||||
|
||||
html += '<td align="right">';
|
||||
if (tableplane.vTrack) {
|
||||
|
@ -503,6 +529,8 @@ function sortTable(szTableID,iCol) {
|
|||
if (typeof iCol==='undefined'){
|
||||
if(iSortCol!=-1){
|
||||
var iCol=iSortCol;
|
||||
} else if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) {
|
||||
var iCol=5;
|
||||
} else {
|
||||
var iCol=iDefaultSortCol;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
html, body {
|
||||
margin: 0; padding: 0; background-color: #ffffff; font-family: Tahoma, Sans-Serif;
|
||||
font-size: 10pt; overflow: auto;
|
||||
font-size: 10pt; overflow: auto; height: 100%;
|
||||
}
|
||||
div#map_container { float: left; width: 100%; height: 100%; }
|
||||
div#map_canvas { height: 100%; margin-right: 420px; }
|
||||
|
|
|
@ -72,9 +72,10 @@ _inline double trunc(double d) {return (d>0) ? floor(d):ceil(d) ;}
|
|||
//usleep works in microseconds, and isn't supported in Windows. This will do for our use.
|
||||
_inline void usleep(UINT32 ulSleep) {Sleep(ulSleep/1000);}
|
||||
_inline uint64_t strtoll(const char *p, void *e, UINT32 base) {return _atoi64(p);}
|
||||
_inline int inet_aton(const char * cp, DWORD * ulAddr) { *ulAddr = inet_addr(cp); return 0;}
|
||||
_inline int inet_aton(const char * cp, DWORD * ulAddr) { *ulAddr = inet_addr(cp); return (INADDR_NONE != *ulAddr);}
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
|
||||
|
||||
_inline void cls() {
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
|
Loading…
Reference in a new issue