Initial revision
This commit is contained in:
676
buffer.c
Normal file
676
buffer.c
Normal file
@@ -0,0 +1,676 @@
|
||||
#include "fm.h"
|
||||
|
||||
#ifdef MOUSE
|
||||
#ifdef USE_GPM
|
||||
#include <gpm.h>
|
||||
#endif
|
||||
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
|
||||
extern int do_getch();
|
||||
#define getch() do_getch()
|
||||
#endif /* USE_GPM */
|
||||
#endif /* MOUSE */
|
||||
|
||||
#ifdef __EMX__
|
||||
#include <sys/kbdscan.h>
|
||||
#include <strings.h>
|
||||
#endif
|
||||
char *NullLine = "";
|
||||
Lineprop NullProp[] =
|
||||
{0};
|
||||
|
||||
/*
|
||||
* Buffer creation
|
||||
*/
|
||||
Buffer *
|
||||
newBuffer(int width)
|
||||
{
|
||||
Buffer *n;
|
||||
|
||||
n = New(Buffer);
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
bzero((void *) n, sizeof(Buffer));
|
||||
n->width = width;
|
||||
n->currentURL.scheme = SCM_UNKNOWN;
|
||||
n->baseURL = NULL;
|
||||
n->baseTarget = NULL;
|
||||
n->buffername = "";
|
||||
n->bufferprop = BP_NORMAL;
|
||||
n->clone = New(int);
|
||||
*n->clone = 1;
|
||||
n->linelen = 0;
|
||||
n->trbyte = 0;
|
||||
#ifdef USE_SSL
|
||||
n->ssl_certificate = NULL;
|
||||
#endif
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create null buffer
|
||||
*/
|
||||
Buffer *
|
||||
nullBuffer(void)
|
||||
{
|
||||
Buffer *b;
|
||||
|
||||
b = newBuffer(COLS);
|
||||
b->buffername = "*Null*";
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* clearBuffer: clear buffer content
|
||||
*/
|
||||
void
|
||||
clearBuffer(Buffer * buf)
|
||||
{
|
||||
buf->firstLine = buf->topLine = buf->currentLine = buf->lastLine = NULL;
|
||||
buf->allLine = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* discardBuffer: free buffer structure
|
||||
*/
|
||||
|
||||
void
|
||||
discardBuffer(Buffer * buf)
|
||||
{
|
||||
int i;
|
||||
Buffer *b;
|
||||
|
||||
clearBuffer(buf);
|
||||
for (i = 0; i < MAX_LB; i++) {
|
||||
b = buf->linkBuffer[i];
|
||||
if (b == NULL)
|
||||
continue;
|
||||
b->linkBuffer[REV_LB[i]] = NULL;
|
||||
}
|
||||
if (buf->savecache)
|
||||
unlink(buf->savecache);
|
||||
if (--(*buf->clone))
|
||||
return;
|
||||
if (buf->pagerSource)
|
||||
ISclose(buf->pagerSource);
|
||||
if (buf->sourcefile) {
|
||||
if (buf->real_scheme != SCM_LOCAL || buf->bufferprop & BP_FRAME)
|
||||
unlink(buf->sourcefile);
|
||||
}
|
||||
while (buf->frameset) {
|
||||
deleteFrameSet(buf->frameset);
|
||||
buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* namedBuffer: Select buffer which have specified name
|
||||
*/
|
||||
Buffer *
|
||||
namedBuffer(Buffer * first, char *name)
|
||||
{
|
||||
Buffer *buf;
|
||||
|
||||
if (!strcmp(first->buffername, name)) {
|
||||
return first;
|
||||
}
|
||||
for (buf = first; buf->nextBuffer != NULL; buf = buf->nextBuffer) {
|
||||
if (!strcmp(buf->nextBuffer->buffername, name)) {
|
||||
return buf->nextBuffer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* deleteBuffer: delete buffer
|
||||
*/
|
||||
Buffer *
|
||||
deleteBuffer(Buffer * first, Buffer * delbuf)
|
||||
{
|
||||
Buffer *buf, *b;
|
||||
|
||||
if (first == delbuf && first->nextBuffer != NULL) {
|
||||
buf = first->nextBuffer;
|
||||
discardBuffer(first);
|
||||
return buf;
|
||||
}
|
||||
if ((buf = prevBuffer(first, delbuf)) != NULL) {
|
||||
b = buf->nextBuffer;
|
||||
buf->nextBuffer = b->nextBuffer;
|
||||
discardBuffer(b);
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/*
|
||||
* replaceBuffer: replace buffer
|
||||
*/
|
||||
Buffer *
|
||||
replaceBuffer(Buffer * first, Buffer * delbuf, Buffer * newbuf)
|
||||
{
|
||||
Buffer *buf;
|
||||
|
||||
if (delbuf == NULL) {
|
||||
newbuf->nextBuffer = first;
|
||||
return newbuf;
|
||||
}
|
||||
if (first == delbuf) {
|
||||
newbuf->nextBuffer = delbuf->nextBuffer;
|
||||
discardBuffer(delbuf);
|
||||
return newbuf;
|
||||
}
|
||||
if (delbuf && (buf = prevBuffer(first, delbuf))) {
|
||||
buf->nextBuffer = newbuf;
|
||||
newbuf->nextBuffer = delbuf->nextBuffer;
|
||||
discardBuffer(delbuf);
|
||||
return first;
|
||||
}
|
||||
newbuf->nextBuffer = first;
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
Buffer *
|
||||
nthBuffer(Buffer * firstbuf, int n)
|
||||
{
|
||||
int i;
|
||||
Buffer *buf = firstbuf;
|
||||
|
||||
if (n < 0)
|
||||
return firstbuf;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
buf = buf->nextBuffer;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
writeBufferName(Buffer * buf, int n)
|
||||
{
|
||||
Str msg;
|
||||
int all;
|
||||
|
||||
all = buf->allLine;
|
||||
if (all == 0 && buf->lastLine != NULL)
|
||||
all = buf->lastLine->linenumber;
|
||||
move(n, 0);
|
||||
msg = Sprintf("<%s> [%d lines]", buf->buffername, all);
|
||||
if (buf->filename != NULL) {
|
||||
switch (buf->currentURL.scheme) {
|
||||
case SCM_LOCAL:
|
||||
case SCM_LOCAL_CGI:
|
||||
if (strcmp(buf->currentURL.file, "-")) {
|
||||
Strcat_char(msg, ' ');
|
||||
Strcat_charp(msg, buf->filename);
|
||||
}
|
||||
break;
|
||||
case SCM_UNKNOWN:
|
||||
case SCM_MISSING:
|
||||
break;
|
||||
default:
|
||||
Strcat_char(msg, ' ');
|
||||
Strcat(msg, parsedURL2Str(&buf->currentURL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
addnstr_sup(msg->ptr, COLS - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gotoLine: go to line number
|
||||
*/
|
||||
void
|
||||
gotoLine(Buffer * buf, int n)
|
||||
{
|
||||
char msg[32];
|
||||
Line *l = buf->firstLine;
|
||||
|
||||
if (l == NULL)
|
||||
return;
|
||||
if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
|
||||
if (buf->lastLine->linenumber < n)
|
||||
getNextPage(buf, n - buf->lastLine->linenumber);
|
||||
while ((buf->lastLine->linenumber < n) &&
|
||||
(getNextPage(buf, 1) != NULL));
|
||||
}
|
||||
if (l->linenumber > n) {
|
||||
sprintf(msg, "First line is #%ld", l->linenumber);
|
||||
disp_message(msg, FALSE);
|
||||
buf->topLine = buf->currentLine = l;
|
||||
return;
|
||||
}
|
||||
if (buf->lastLine->linenumber < n) {
|
||||
l = buf->lastLine;
|
||||
sprintf(msg, "Last line is #%ld", buf->lastLine->linenumber);
|
||||
disp_message(msg, FALSE);
|
||||
buf->currentLine = l;
|
||||
buf->topLine = lineSkip(buf, buf->currentLine, - (LASTLINE - 1), FALSE);
|
||||
return;
|
||||
}
|
||||
for (; l != NULL; l = l->next) {
|
||||
if (l->linenumber >= n) {
|
||||
buf->currentLine = l;
|
||||
if (n < buf->topLine->linenumber ||
|
||||
buf->topLine->linenumber + LASTLINE <= n)
|
||||
buf->topLine = lineSkip(buf, l, -(LASTLINE + 1) / 2, FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gotoRealLine: go to real line number
|
||||
*/
|
||||
void
|
||||
gotoRealLine(Buffer * buf, int n)
|
||||
{
|
||||
char msg[32];
|
||||
Line *l = buf->firstLine;
|
||||
|
||||
if (l == NULL)
|
||||
return;
|
||||
if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
|
||||
if (buf->lastLine->real_linenumber < n)
|
||||
getNextPage(buf, n - buf->lastLine->real_linenumber);
|
||||
while ((buf->lastLine->real_linenumber < n) &&
|
||||
(getNextPage(buf, 1) != NULL));
|
||||
}
|
||||
if (l->real_linenumber > n) {
|
||||
sprintf(msg, "First line is #%ld", l->real_linenumber);
|
||||
disp_message(msg, FALSE);
|
||||
buf->topLine = buf->currentLine = l;
|
||||
return;
|
||||
}
|
||||
if (buf->lastLine->real_linenumber < n) {
|
||||
l = buf->lastLine;
|
||||
sprintf(msg, "Last line is #%ld", buf->lastLine->real_linenumber);
|
||||
disp_message(msg, FALSE);
|
||||
buf->currentLine = l;
|
||||
buf->topLine = lineSkip(buf, buf->currentLine, - (LASTLINE - 1), FALSE);
|
||||
return;
|
||||
}
|
||||
for (; l != NULL; l = l->next) {
|
||||
if (l->real_linenumber >= n) {
|
||||
buf->currentLine = l;
|
||||
if (n < buf->topLine->real_linenumber ||
|
||||
buf->topLine->real_linenumber + LASTLINE <= n)
|
||||
buf->topLine = lineSkip(buf, l, -(LASTLINE + 1) / 2, FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Buffer *
|
||||
listBuffer(Buffer * top, Buffer * current)
|
||||
{
|
||||
int i, c = 0;
|
||||
Buffer *buf = top;
|
||||
|
||||
move(0, 0);
|
||||
#ifdef COLOR
|
||||
if (useColor) {
|
||||
setfcolor(basic_color);
|
||||
#ifdef BG_COLOR
|
||||
setbcolor(bg_color);
|
||||
#endif /* BG_COLOR */
|
||||
}
|
||||
#endif /* COLOR */
|
||||
clrtobotx();
|
||||
for (i = 0; i < LASTLINE; i++) {
|
||||
if (buf == current) {
|
||||
c = i;
|
||||
standout();
|
||||
}
|
||||
writeBufferName(buf, i);
|
||||
if (buf == current) {
|
||||
standend();
|
||||
clrtoeolx();
|
||||
move(i, 0);
|
||||
toggle_stand();
|
||||
}
|
||||
else
|
||||
clrtoeolx();
|
||||
if (buf->nextBuffer == NULL) {
|
||||
move(i + 1, 0);
|
||||
clrtobotx();
|
||||
break;
|
||||
}
|
||||
buf = buf->nextBuffer;
|
||||
}
|
||||
standout();
|
||||
message("Buffer selection mode: SPC for select / D for delete buffer", 0, 0);
|
||||
standend();
|
||||
/*
|
||||
* move(LASTLINE, COLS - 1); */
|
||||
move(c, 0);
|
||||
refresh();
|
||||
return buf->nextBuffer;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Select buffer visually
|
||||
*/
|
||||
Buffer *
|
||||
selectBuffer(Buffer * firstbuf, Buffer * currentbuf, char *selectchar)
|
||||
{
|
||||
int i, cpoint, /* Current Buffer Number */
|
||||
spoint, /* Current Line on Screen */
|
||||
maxbuf, sclimit = LASTLINE; /* Upper limit of line * number in
|
||||
* the * screen */
|
||||
Buffer *buf, *topbuf;
|
||||
char c;
|
||||
|
||||
i = cpoint = 0;
|
||||
for (buf = firstbuf; buf != NULL; buf = buf->nextBuffer) {
|
||||
if (buf == currentbuf)
|
||||
cpoint = i;
|
||||
i++;
|
||||
}
|
||||
maxbuf = i;
|
||||
|
||||
if (cpoint >= sclimit) {
|
||||
spoint = sclimit / 2;
|
||||
topbuf = nthBuffer(firstbuf, cpoint - spoint);
|
||||
}
|
||||
else {
|
||||
topbuf = firstbuf;
|
||||
spoint = cpoint;
|
||||
}
|
||||
listBuffer(topbuf, currentbuf);
|
||||
|
||||
for (;;) {
|
||||
if ((c = getch()) == ESC_CODE) {
|
||||
if ((c = getch()) == '[' || c == 'O') {
|
||||
switch (c = getch()) {
|
||||
case 'A':
|
||||
c = 'k';
|
||||
break;
|
||||
case 'B':
|
||||
c = 'j';
|
||||
break;
|
||||
case 'C':
|
||||
c = ' ';
|
||||
break;
|
||||
case 'D':
|
||||
c = 'B';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __EMX__
|
||||
else if(!c)
|
||||
switch(getch()){
|
||||
case K_UP:
|
||||
c='k';
|
||||
break;
|
||||
case K_DOWN:
|
||||
c='j';
|
||||
break;
|
||||
case K_RIGHT:
|
||||
c=' ';
|
||||
break;
|
||||
case K_LEFT:
|
||||
c='B';
|
||||
}
|
||||
#endif
|
||||
switch (c) {
|
||||
case CTRL_N:
|
||||
case 'j':
|
||||
if (spoint < sclimit - 1) {
|
||||
if (currentbuf->nextBuffer == NULL)
|
||||
continue;
|
||||
writeBufferName(currentbuf, spoint);
|
||||
currentbuf = currentbuf->nextBuffer;
|
||||
cpoint++;
|
||||
spoint++;
|
||||
standout();
|
||||
writeBufferName(currentbuf, spoint);
|
||||
standend();
|
||||
move(spoint, 0);
|
||||
toggle_stand();
|
||||
}
|
||||
else if (cpoint < maxbuf - 1) {
|
||||
topbuf = currentbuf;
|
||||
currentbuf = currentbuf->nextBuffer;
|
||||
cpoint++;
|
||||
spoint = 1;
|
||||
listBuffer(topbuf, currentbuf);
|
||||
}
|
||||
break;
|
||||
case CTRL_P:
|
||||
case 'k':
|
||||
if (spoint > 0) {
|
||||
writeBufferName(currentbuf, spoint);
|
||||
currentbuf = nthBuffer(topbuf, --spoint);
|
||||
cpoint--;
|
||||
standout();
|
||||
writeBufferName(currentbuf, spoint);
|
||||
standend();
|
||||
move(spoint, 0);
|
||||
toggle_stand();
|
||||
}
|
||||
else if (cpoint > 0) {
|
||||
i = cpoint - sclimit;
|
||||
if (i < 0)
|
||||
i = 0;
|
||||
cpoint--;
|
||||
spoint = cpoint - i;
|
||||
currentbuf = nthBuffer(firstbuf, cpoint);
|
||||
topbuf = nthBuffer(firstbuf, i);
|
||||
listBuffer(topbuf, currentbuf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*selectchar = c;
|
||||
return currentbuf;
|
||||
}
|
||||
/*
|
||||
* move(LASTLINE, COLS - 1);
|
||||
*/
|
||||
move(spoint, 0);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reshape HTML buffer
|
||||
*/
|
||||
void
|
||||
reshapeBuffer(Buffer * buf)
|
||||
{
|
||||
URLFile f;
|
||||
int top, linenum, cursorY, pos, currentColumn;
|
||||
AnchorList *formitem;
|
||||
|
||||
init_stream(&f, SCM_LOCAL, NULL);
|
||||
examineFile(buf->sourcefile, &f);
|
||||
if (f.stream == NULL)
|
||||
return;
|
||||
|
||||
if (buf->firstLine == NULL) {
|
||||
top = 1;
|
||||
linenum = 1;
|
||||
} else {
|
||||
top = buf->topLine->linenumber;
|
||||
linenum = buf->currentLine->linenumber;
|
||||
}
|
||||
cursorY = buf->cursorY;
|
||||
pos = buf->pos;
|
||||
currentColumn = buf->currentColumn;
|
||||
clearBuffer(buf);
|
||||
while (buf->frameset) {
|
||||
deleteFrameSet(buf->frameset);
|
||||
buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL);
|
||||
}
|
||||
|
||||
formitem = buf->formitem;
|
||||
buf->href = NULL;
|
||||
buf->name = NULL;
|
||||
buf->img = NULL;
|
||||
buf->formitem = NULL;
|
||||
buf->width = INIT_BUFFER_WIDTH;
|
||||
|
||||
loadHTMLstream(&f, buf, NULL, FALSE);
|
||||
UFclose(&f);
|
||||
|
||||
buf->topLine = buf->firstLine;
|
||||
buf->lastLine = buf->currentLine;
|
||||
buf->currentLine = buf->firstLine;
|
||||
buf->height = LASTLINE + 1;
|
||||
buf->topLine = lineSkip(buf, buf->topLine, top - 1, FALSE);
|
||||
gotoLine(buf, linenum);
|
||||
buf->pos = pos;
|
||||
buf->currentColumn = currentColumn;
|
||||
arrangeCursor(buf);
|
||||
if (buf->check_url & CHK_URL)
|
||||
chkURL();
|
||||
#ifdef USE_NNTP
|
||||
if (buf->check_url & CHK_NMID)
|
||||
chkNMID();
|
||||
#endif
|
||||
formResetBuffer(buf, formitem);
|
||||
}
|
||||
|
||||
/* shallow copy */
|
||||
void
|
||||
copyBuffer(Buffer * a, Buffer * b)
|
||||
{
|
||||
readBufferCache(b);
|
||||
bcopy((void *) b, (void *) a, sizeof(Buffer));
|
||||
}
|
||||
|
||||
Buffer *
|
||||
prevBuffer(Buffer * first, Buffer * buf)
|
||||
{
|
||||
Buffer *b;
|
||||
|
||||
for (b = first; b != NULL && b->nextBuffer != buf; b = b->nextBuffer);
|
||||
return b;
|
||||
}
|
||||
|
||||
#define fwrite1(d, f) (fwrite(&d, sizeof(d), 1, f)==0)
|
||||
#define fread1(d, f) (fread(&d, sizeof(d), 1, f)==0)
|
||||
|
||||
int
|
||||
writeBufferCache(Buffer *buf)
|
||||
{
|
||||
Str tmp;
|
||||
FILE *cache = NULL;
|
||||
Line *l;
|
||||
#ifdef ANSI_COLOR
|
||||
int colorflag;
|
||||
#endif
|
||||
|
||||
if (buf->savecache)
|
||||
return -1;
|
||||
|
||||
if (buf->firstLine == NULL)
|
||||
goto _error1;
|
||||
|
||||
tmp = tmpfname(TMPF_CACHE, NULL);
|
||||
buf->savecache = tmp->ptr;
|
||||
cache = fopen(buf->savecache, "w");
|
||||
if (!cache)
|
||||
goto _error1;
|
||||
|
||||
if (fwrite1(buf->currentLine->linenumber, cache) ||
|
||||
fwrite1(buf->topLine->linenumber, cache))
|
||||
goto _error;
|
||||
|
||||
for (l = buf->firstLine; l; l = l->next) {
|
||||
if (fwrite1(l->real_linenumber, cache) ||
|
||||
fwrite1(l->usrflags, cache) ||
|
||||
fwrite1(l->width, cache) ||
|
||||
fwrite1(l->len, cache) ||
|
||||
fwrite(l->lineBuf, 1, l->len, cache) < l->len ||
|
||||
fwrite(l->propBuf, sizeof(Lineprop), l->len, cache) < l->len)
|
||||
goto _error;
|
||||
#ifdef ANSI_COLOR
|
||||
colorflag = l->colorBuf ? 1 : 0;
|
||||
if (fwrite1(colorflag, cache))
|
||||
goto _error;
|
||||
if (colorflag) {
|
||||
if (fwrite(l->colorBuf, sizeof(Linecolor), l->len, cache) < l->len)
|
||||
goto _error;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
fclose(cache);
|
||||
return 0;
|
||||
_error:
|
||||
fclose(cache);
|
||||
unlink(buf->savecache);
|
||||
_error1:
|
||||
buf->savecache = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
readBufferCache(Buffer *buf)
|
||||
{
|
||||
FILE *cache;
|
||||
Line *l = NULL, *prevl;
|
||||
long lnum = 0, clnum, tlnum;
|
||||
#ifdef ANSI_COLOR
|
||||
int colorflag;
|
||||
#endif
|
||||
|
||||
if (buf->savecache == NULL)
|
||||
return -1;
|
||||
|
||||
cache = fopen(buf->savecache, "r");
|
||||
if (cache == NULL ||
|
||||
fread1(clnum, cache) ||
|
||||
fread1(tlnum, cache)) {
|
||||
buf->savecache = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!feof(cache)) {
|
||||
lnum++;
|
||||
prevl = l;
|
||||
l = New(Line);
|
||||
l->prev = prevl;
|
||||
if (prevl)
|
||||
prevl->next = l;
|
||||
else
|
||||
buf->firstLine = l;
|
||||
l->linenumber = lnum;
|
||||
if (lnum == clnum)
|
||||
buf->currentLine = l;
|
||||
if (lnum == tlnum)
|
||||
buf->topLine = l;
|
||||
if (fread1(l->real_linenumber, cache) ||
|
||||
fread1(l->usrflags, cache) ||
|
||||
fread1(l->width, cache) ||
|
||||
fread1(l->len, cache))
|
||||
break;
|
||||
l->lineBuf = New_N(char, l->len + 1);
|
||||
fread(l->lineBuf, 1, l->len, cache);
|
||||
l->lineBuf[l->len] = '\0';
|
||||
l->propBuf = New_N(Lineprop, l->len);
|
||||
fread(l->propBuf, sizeof(Lineprop), l->len, cache);
|
||||
#ifdef ANSI_COLOR
|
||||
if (fread1(colorflag, cache))
|
||||
break;
|
||||
if (colorflag) {
|
||||
l->colorBuf = New_N(Linecolor, l->len);
|
||||
fread(l->colorBuf, sizeof(Linecolor), l->len, cache);
|
||||
} else {
|
||||
l->colorBuf = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
buf->lastLine = prevl;
|
||||
buf->lastLine->next = NULL;
|
||||
fclose(cache);
|
||||
unlink(buf->savecache);
|
||||
buf->savecache = NULL;
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user