mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
No more anonymous structs (not standard C++) (#1305)
This is one step to restoring `-pedantic` builds
This commit is contained in:
@@ -39,9 +39,9 @@ struct Symbol {
|
|||||||
int32_t (*numCallback)(void);
|
int32_t (*numCallback)(void);
|
||||||
// For SYM_MACRO and SYM_EQUS; TODO: have separate fields
|
// For SYM_MACRO and SYM_EQUS; TODO: have separate fields
|
||||||
struct {
|
struct {
|
||||||
size_t macroSize;
|
size_t size;
|
||||||
char *macro;
|
char *value;
|
||||||
};
|
} macro;
|
||||||
// For SYM_EQUS
|
// For SYM_EQUS
|
||||||
char const *(*strCallback)(void);
|
char const *(*strCallback)(void);
|
||||||
};
|
};
|
||||||
@@ -97,7 +97,7 @@ static inline char const *sym_GetStringValue(struct Symbol const *sym)
|
|||||||
{
|
{
|
||||||
if (sym->hasCallback)
|
if (sym->hasCallback)
|
||||||
return sym->strCallback();
|
return sym->strCallback();
|
||||||
return sym->macro;
|
return sym->macro.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg);
|
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg);
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ struct FileStackNode {
|
|||||||
union {
|
union {
|
||||||
char *name; // NODE_FILE, NODE_MACRO
|
char *name; // NODE_FILE, NODE_MACRO
|
||||||
struct { // NODE_REPT
|
struct { // NODE_REPT
|
||||||
uint32_t reptDepth;
|
uint32_t depth;
|
||||||
uint32_t *iters;
|
uint32_t *iters;
|
||||||
};
|
} rept;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
#define restrict
|
#define restrict
|
||||||
|
|
||||||
// C++ doesn't support designated array initializers, but they're a gcc extension
|
// C++ doesn't support designated array initializers, but they're a gcc extension
|
||||||
#ifdef __GNUC__
|
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||||
# define AT(index) [index] =
|
# define AT(index) [index] =
|
||||||
#else
|
#else
|
||||||
# define AT(index)
|
# define AT(index)
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
memcpy(dest, macro->name, macroNameLen + 1);
|
memcpy(dest, macro->name, macroNameLen + 1);
|
||||||
|
|
||||||
newContext((struct FileStackNode *)fileInfo);
|
newContext((struct FileStackNode *)fileInfo);
|
||||||
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro, macro->macroSize,
|
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro.value, macro->macro.size,
|
||||||
macro->fileLine);
|
macro->fileLine);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||||
|
|||||||
@@ -319,13 +319,13 @@ struct LexerState {
|
|||||||
size_t size;
|
size_t size;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
bool isReferenced; // If a macro in this file requires not unmapping it
|
bool isReferenced; // If a macro in this file requires not unmapping it
|
||||||
};
|
} mmap;
|
||||||
struct { // Otherwise
|
struct { // Otherwise
|
||||||
int fd;
|
int fd;
|
||||||
size_t index; // Read index into the buffer
|
size_t index; // Read index into the buffer
|
||||||
char buf[LEXER_BUF_SIZE]; // Circular buffer
|
char buf[LEXER_BUF_SIZE]; // Circular buffer
|
||||||
size_t nbChars; // Number of "fresh" chars in the buffer
|
size_t nbChars; // Number of "fresh" chars in the buffer
|
||||||
};
|
} cbuf;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common state
|
// Common state
|
||||||
@@ -454,8 +454,8 @@ struct LexerState *lexer_OpenFile(char const *path)
|
|||||||
}
|
}
|
||||||
state->path = path;
|
state->path = path;
|
||||||
state->isFile = true;
|
state->isFile = true;
|
||||||
state->fd = isStdin ? STDIN_FILENO : open(path, O_RDONLY);
|
state->cbuf.fd = isStdin ? STDIN_FILENO : open(path, O_RDONLY);
|
||||||
if (state->fd < 0) {
|
if (state->cbuf.fd < 0) {
|
||||||
error("Failed to open file \"%s\": %s\n", path, strerror(errno));
|
error("Failed to open file \"%s\": %s\n", path, strerror(errno));
|
||||||
free(state);
|
free(state);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -464,25 +464,25 @@ struct LexerState *lexer_OpenFile(char const *path)
|
|||||||
if (!isStdin && fileInfo.st_size > 0) {
|
if (!isStdin && fileInfo.st_size > 0) {
|
||||||
// Try using `mmap` for better performance
|
// Try using `mmap` for better performance
|
||||||
|
|
||||||
// Important: do NOT assign to `state->ptr` directly, to avoid a cast that may
|
// Important: do NOT assign to `state->mmap.ptr` directly, to avoid a cast that may
|
||||||
// alter an eventual `MAP_FAILED` value. It would also invalidate `state->fd`,
|
// alter an eventual `MAP_FAILED` value. It would also invalidate `state->cbuf.fd`,
|
||||||
// being on the other side of the union.
|
// being on the other side of the union.
|
||||||
void *mappingAddr;
|
void *mappingAddr;
|
||||||
|
|
||||||
mapFile(mappingAddr, state->fd, state->path, fileInfo.st_size);
|
mapFile(mappingAddr, state->cbuf.fd, state->path, fileInfo.st_size);
|
||||||
if (mappingAddr == MAP_FAILED) {
|
if (mappingAddr == MAP_FAILED) {
|
||||||
// If mmap()ing failed, try again using another method (below)
|
// If mmap()ing failed, try again using another method (below)
|
||||||
state->isMmapped = false;
|
state->isMmapped = false;
|
||||||
} else {
|
} else {
|
||||||
// IMPORTANT: the `union` mandates this is accessed before other members!
|
// IMPORTANT: the `union` mandates this is accessed before other members!
|
||||||
close(state->fd);
|
close(state->cbuf.fd);
|
||||||
|
|
||||||
state->isMmapped = true;
|
state->isMmapped = true;
|
||||||
state->isReferenced = false; // By default, a state isn't referenced
|
state->mmap.isReferenced = false; // By default, a state isn't referenced
|
||||||
state->ptr = (char *)mappingAddr;
|
state->mmap.ptr = (char *)mappingAddr;
|
||||||
assert(fileInfo.st_size >= 0);
|
assert(fileInfo.st_size >= 0);
|
||||||
state->size = (size_t)fileInfo.st_size;
|
state->mmap.size = (size_t)fileInfo.st_size;
|
||||||
state->offset = 0;
|
state->mmap.offset = 0;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("File %s successfully mmap()ped\n", path);
|
printf("File %s successfully mmap()ped\n", path);
|
||||||
@@ -499,8 +499,8 @@ struct LexerState *lexer_OpenFile(char const *path)
|
|||||||
printf("File %s opened as regular, errno reports \"%s\"\n",
|
printf("File %s opened as regular, errno reports \"%s\"\n",
|
||||||
path, strerror(errno));
|
path, strerror(errno));
|
||||||
}
|
}
|
||||||
state->index = 0;
|
state->cbuf.index = 0;
|
||||||
state->nbChars = 0;
|
state->cbuf.nbChars = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
initState(state);
|
initState(state);
|
||||||
@@ -520,9 +520,9 @@ struct LexerState *lexer_OpenFileView(char const *path, char *buf, size_t size,
|
|||||||
state->path = path; // Used to report read errors in `peekInternal`
|
state->path = path; // Used to report read errors in `peekInternal`
|
||||||
state->isFile = false;
|
state->isFile = false;
|
||||||
state->isMmapped = true; // It's not *really* mmap()ed, but it behaves the same
|
state->isMmapped = true; // It's not *really* mmap()ed, but it behaves the same
|
||||||
state->ptr = buf;
|
state->mmap.ptr = buf;
|
||||||
state->size = size;
|
state->mmap.size = size;
|
||||||
state->offset = 0;
|
state->mmap.offset = 0;
|
||||||
|
|
||||||
initState(state);
|
initState(state);
|
||||||
state->lineNo = lineNo; // Will be incremented at first line start
|
state->lineNo = lineNo; // Will be incremented at first line start
|
||||||
@@ -531,7 +531,7 @@ struct LexerState *lexer_OpenFileView(char const *path, char *buf, size_t size,
|
|||||||
|
|
||||||
void lexer_RestartRept(uint32_t lineNo)
|
void lexer_RestartRept(uint32_t lineNo)
|
||||||
{
|
{
|
||||||
lexerState->offset = 0;
|
lexerState->mmap.offset = 0;
|
||||||
initState(lexerState);
|
initState(lexerState);
|
||||||
lexerState->lineNo = lineNo;
|
lexerState->lineNo = lineNo;
|
||||||
}
|
}
|
||||||
@@ -552,9 +552,9 @@ void lexer_DeleteState(struct LexerState *state)
|
|||||||
assert(state != lexerStateEOL);
|
assert(state != lexerStateEOL);
|
||||||
|
|
||||||
if (!state->isMmapped)
|
if (!state->isMmapped)
|
||||||
close(state->fd);
|
close(state->cbuf.fd);
|
||||||
else if (state->isFile && !state->isReferenced)
|
else if (state->isFile && !state->mmap.isReferenced)
|
||||||
munmap(state->ptr, state->size);
|
munmap(state->mmap.ptr, state->mmap.size);
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,7 +798,7 @@ static size_t readInternal(size_t bufIndex, size_t nbChars)
|
|||||||
{
|
{
|
||||||
// This buffer overflow made me lose WEEKS of my life. Never again.
|
// This buffer overflow made me lose WEEKS of my life. Never again.
|
||||||
assert(bufIndex + nbChars <= LEXER_BUF_SIZE);
|
assert(bufIndex + nbChars <= LEXER_BUF_SIZE);
|
||||||
ssize_t nbReadChars = read(lexerState->fd, &lexerState->buf[bufIndex], nbChars);
|
ssize_t nbReadChars = read(lexerState->cbuf.fd, &lexerState->cbuf.buf[bufIndex], nbChars);
|
||||||
|
|
||||||
if (nbReadChars == -1)
|
if (nbReadChars == -1)
|
||||||
fatalerror("Error while reading \"%s\": %s\n", lexerState->path, strerror(errno));
|
fatalerror("Error while reading \"%s\": %s\n", lexerState->path, strerror(errno));
|
||||||
@@ -824,25 +824,25 @@ static int peekInternal(uint8_t distance)
|
|||||||
PRIu8 " >= %u)\n", distance, LEXER_BUF_SIZE);
|
PRIu8 " >= %u)\n", distance, LEXER_BUF_SIZE);
|
||||||
|
|
||||||
if (lexerState->isMmapped) {
|
if (lexerState->isMmapped) {
|
||||||
if (lexerState->offset + distance >= lexerState->size)
|
if (lexerState->mmap.offset + distance >= lexerState->mmap.size)
|
||||||
return EOF;
|
return EOF;
|
||||||
|
|
||||||
return (unsigned char)lexerState->ptr[lexerState->offset + distance];
|
return (unsigned char)lexerState->mmap.ptr[lexerState->mmap.offset + distance];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lexerState->nbChars <= distance) {
|
if (lexerState->cbuf.nbChars <= distance) {
|
||||||
// Buffer isn't full enough, read some chars in
|
// Buffer isn't full enough, read some chars in
|
||||||
size_t target = LEXER_BUF_SIZE - lexerState->nbChars; // Aim: making the buf full
|
size_t target = LEXER_BUF_SIZE - lexerState->cbuf.nbChars; // Aim: making the buf full
|
||||||
|
|
||||||
// Compute the index we'll start writing to
|
// Compute the index we'll start writing to
|
||||||
size_t writeIndex = (lexerState->index + lexerState->nbChars) % LEXER_BUF_SIZE;
|
size_t writeIndex = (lexerState->cbuf.index + lexerState->cbuf.nbChars) % LEXER_BUF_SIZE;
|
||||||
|
|
||||||
// If the range to fill passes over the buffer wrapping point, we need two reads
|
// If the range to fill passes over the buffer wrapping point, we need two reads
|
||||||
if (writeIndex + target > LEXER_BUF_SIZE) {
|
if (writeIndex + target > LEXER_BUF_SIZE) {
|
||||||
size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex;
|
size_t nbExpectedChars = LEXER_BUF_SIZE - writeIndex;
|
||||||
size_t nbReadChars = readInternal(writeIndex, nbExpectedChars);
|
size_t nbReadChars = readInternal(writeIndex, nbExpectedChars);
|
||||||
|
|
||||||
lexerState->nbChars += nbReadChars;
|
lexerState->cbuf.nbChars += nbReadChars;
|
||||||
|
|
||||||
writeIndex += nbReadChars;
|
writeIndex += nbReadChars;
|
||||||
if (writeIndex == LEXER_BUF_SIZE)
|
if (writeIndex == LEXER_BUF_SIZE)
|
||||||
@@ -854,14 +854,14 @@ static int peekInternal(uint8_t distance)
|
|||||||
target = 0;
|
target = 0;
|
||||||
}
|
}
|
||||||
if (target != 0)
|
if (target != 0)
|
||||||
lexerState->nbChars += readInternal(writeIndex, target);
|
lexerState->cbuf.nbChars += readInternal(writeIndex, target);
|
||||||
|
|
||||||
// If there aren't enough chars even after refilling, give up
|
// If there aren't enough chars even after refilling, give up
|
||||||
if (lexerState->nbChars <= distance)
|
if (lexerState->cbuf.nbChars <= distance)
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (unsigned char)lexerState->buf[(lexerState->index + distance) % LEXER_BUF_SIZE];
|
return (unsigned char)lexerState->cbuf.buf[(lexerState->cbuf.index + distance) % LEXER_BUF_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward declarations for peek
|
// forward declarations for peek
|
||||||
@@ -946,14 +946,14 @@ restart:
|
|||||||
// Advance within the file contents
|
// Advance within the file contents
|
||||||
lexerState->colNo++;
|
lexerState->colNo++;
|
||||||
if (lexerState->isMmapped) {
|
if (lexerState->isMmapped) {
|
||||||
lexerState->offset++;
|
lexerState->mmap.offset++;
|
||||||
} else {
|
} else {
|
||||||
assert(lexerState->index < LEXER_BUF_SIZE);
|
assert(lexerState->cbuf.index < LEXER_BUF_SIZE);
|
||||||
lexerState->index++;
|
lexerState->cbuf.index++;
|
||||||
if (lexerState->index == LEXER_BUF_SIZE)
|
if (lexerState->cbuf.index == LEXER_BUF_SIZE)
|
||||||
lexerState->index = 0; // Wrap around if necessary
|
lexerState->cbuf.index = 0; // Wrap around if necessary
|
||||||
assert(lexerState->nbChars > 0);
|
assert(lexerState->cbuf.nbChars > 0);
|
||||||
lexerState->nbChars--;
|
lexerState->cbuf.nbChars--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2458,7 +2458,7 @@ static void startCapture(struct CaptureBody *capture)
|
|||||||
capture->lineNo = lexer_GetLineNo();
|
capture->lineNo = lexer_GetLineNo();
|
||||||
|
|
||||||
if (lexerState->isMmapped && !lexerState->expansions) {
|
if (lexerState->isMmapped && !lexerState->expansions) {
|
||||||
capture->body = &lexerState->ptr[lexerState->offset];
|
capture->body = &lexerState->mmap.ptr[lexerState->mmap.offset];
|
||||||
} else {
|
} else {
|
||||||
lexerState->captureCapacity = 128; // The initial size will be twice that
|
lexerState->captureCapacity = 128; // The initial size will be twice that
|
||||||
assert(lexerState->captureBuf == NULL);
|
assert(lexerState->captureBuf == NULL);
|
||||||
@@ -2545,7 +2545,7 @@ bool lexer_CaptureMacroBody(struct CaptureBody *capture)
|
|||||||
|
|
||||||
// If the file is `mmap`ed, we need not to unmap it to keep access to the macro
|
// If the file is `mmap`ed, we need not to unmap it to keep access to the macro
|
||||||
if (lexerState->isMmapped)
|
if (lexerState->isMmapped)
|
||||||
lexerState->isReferenced = true;
|
lexerState->mmap.isReferenced = true;
|
||||||
|
|
||||||
int c = EOF;
|
int c = EOF;
|
||||||
|
|
||||||
|
|||||||
@@ -164,8 +164,8 @@ static void assignStringSymbol(struct Symbol *sym, char const *value)
|
|||||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||||
|
|
||||||
sym->type = SYM_EQUS;
|
sym->type = SYM_EQUS;
|
||||||
sym->macro = string;
|
sym->macro.value = string;
|
||||||
sym->macroSize = strlen(string);
|
sym->macro.size = strlen(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindExactSymbol(char const *symName)
|
struct Symbol *sym_FindExactSymbol(char const *symName)
|
||||||
@@ -233,8 +233,8 @@ void sym_Purge(char const *symName)
|
|||||||
if (sym->name == labelScope)
|
if (sym->name == labelScope)
|
||||||
sym_SetCurrentSymbolScope(NULL);
|
sym_SetCurrentSymbolScope(NULL);
|
||||||
|
|
||||||
// FIXME: this leaks sym->macro for SYM_EQUS and SYM_MACRO, but this can't
|
// FIXME: this leaks sym->macro.value for SYM_EQUS and SYM_MACRO, but this can't
|
||||||
// free(sym->macro) because the expansion may be purging itself.
|
// free(sym->macro.value) because the expansion may be purging itself.
|
||||||
hash_RemoveElement(symbols, sym->name);
|
hash_RemoveElement(symbols, sym->name);
|
||||||
// TODO: ideally, also unref the file stack nodes
|
// TODO: ideally, also unref the file stack nodes
|
||||||
free(sym);
|
free(sym);
|
||||||
@@ -401,8 +401,8 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSymbolFilename(sym);
|
updateSymbolFilename(sym);
|
||||||
// FIXME: this leaks the previous sym->macro value, but this can't
|
// FIXME: this leaks the previous sym->macro.value value, but this can't
|
||||||
// free(sym->macro) because the expansion may be redefining itself.
|
// free(sym->macro.value) because the expansion may be redefining itself.
|
||||||
assignStringSymbol(sym, value);
|
assignStringSymbol(sym, value);
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
@@ -574,8 +574,8 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sym->type = SYM_MACRO;
|
sym->type = SYM_MACRO;
|
||||||
sym->macroSize = size;
|
sym->macro.size = size;
|
||||||
sym->macro = body;
|
sym->macro.value = body;
|
||||||
setSymbolFilename(sym); // TODO: is this really necessary?
|
setSymbolFilename(sym); // TODO: is this really necessary?
|
||||||
// The symbol is created at the line after the `endm`,
|
// The symbol is created at the line after the `endm`,
|
||||||
// override this with the actual definition line
|
// override this with the actual definition line
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ char const *dumpFileStack(struct FileStackNode const *node)
|
|||||||
lastName = node->name;
|
lastName = node->name;
|
||||||
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
|
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
|
||||||
if (node->type == NODE_REPT) {
|
if (node->type == NODE_REPT) {
|
||||||
for (uint32_t i = 0; i < node->reptDepth; i++)
|
for (uint32_t i = 0; i < node->rept.depth; i++)
|
||||||
fprintf(stderr, "::REPT~%" PRIu32, node->iters[i]);
|
fprintf(stderr, "::REPT~%" PRIu32, node->rept.iters[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(node->type != NODE_REPT);
|
assert(node->type != NODE_REPT);
|
||||||
|
|||||||
@@ -180,15 +180,15 @@ static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NODE_REPT:
|
case NODE_REPT:
|
||||||
tryReadlong(fileNodes[i].reptDepth, file,
|
tryReadlong(fileNodes[i].rept.depth, file,
|
||||||
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
|
||||||
fileNodes[i].iters =
|
fileNodes[i].rept.iters =
|
||||||
(uint32_t *)malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
|
(uint32_t *)malloc(sizeof(*fileNodes[i].rept.iters) * fileNodes[i].rept.depth);
|
||||||
if (!fileNodes[i].iters)
|
if (!fileNodes[i].rept.iters)
|
||||||
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
|
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
|
||||||
fileName, i, strerror(errno));
|
fileName, i, strerror(errno));
|
||||||
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
|
for (uint32_t k = 0; k < fileNodes[i].rept.depth; k++)
|
||||||
tryReadlong(fileNodes[i].iters[k], file,
|
tryReadlong(fileNodes[i].rept.iters[k], file,
|
||||||
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
|
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
|
||||||
fileName, i, k);
|
fileName, i, k);
|
||||||
if (!fileNodes[i].parent)
|
if (!fileNodes[i].parent)
|
||||||
@@ -659,7 +659,7 @@ void obj_Setup(unsigned int nbFiles)
|
|||||||
static void freeNode(struct FileStackNode *node)
|
static void freeNode(struct FileStackNode *node)
|
||||||
{
|
{
|
||||||
if (node->type == NODE_REPT)
|
if (node->type == NODE_REPT)
|
||||||
free(node->iters);
|
free(node->rept.iters);
|
||||||
else
|
else
|
||||||
free(node->name);
|
free(node->name);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user