mirror of
https://github.com/cc65/cc65.git
synced 2025-02-26 23:30:03 +00:00
Adding functionality to StrBuf
git-svn-id: svn://svn.cc65.org/cc65/trunk@885 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
cf4555f101
commit
23fbf3ff2a
@ -35,21 +35,334 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* cc65 */
|
||||
/* common */
|
||||
#include "xsprintf.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "codegen.h"
|
||||
#include "datatype.h"
|
||||
#include "error.h"
|
||||
#include "expr.h"
|
||||
#include "function.h"
|
||||
#include "litpool.h"
|
||||
#include "scanner.h"
|
||||
#include "symtab.h"
|
||||
#include "asmstmt.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void AsmRangeError (unsigned Arg)
|
||||
/* Print a diagnostic about a range error in the argument with the given number */
|
||||
{
|
||||
Error ("Range error in argument %u", Arg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void AsmErrorSkip (void)
|
||||
/* Called in case of an error, skips tokens until the closing paren or a
|
||||
* semicolon is reached.
|
||||
*/
|
||||
{
|
||||
static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
|
||||
SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static SymEntry* AsmGetSym (unsigned Arg, unsigned Type)
|
||||
/* Find the symbol with the name currently in NextTok. The symbol must be of
|
||||
* the given type. On errors, NULL is returned.
|
||||
*/
|
||||
{
|
||||
SymEntry* Sym;
|
||||
|
||||
/* We expect an argument separated by a comma */
|
||||
ConsumeComma ();
|
||||
|
||||
/* Argument must be an identifier */
|
||||
if (CurTok.Tok != TOK_IDENT) {
|
||||
Error ("Identifier expected for argument %u", Arg);
|
||||
AsmErrorSkip ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get a pointer to the symbol table entry */
|
||||
Sym = FindSym (CurTok.Ident);
|
||||
|
||||
/* Did we find a symbol with this name? */
|
||||
if (Sym == 0) {
|
||||
Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg);
|
||||
AsmErrorSkip ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We found the symbol - skip the name token */
|
||||
NextToken ();
|
||||
|
||||
/* Check if we have a global symbol */
|
||||
if ((Sym->Flags & Type) != Type) {
|
||||
Error ("Type of argument %u differs from format specifier", Arg);
|
||||
AsmErrorSkip ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mark the symbol as referenced */
|
||||
Sym->Flags |= SC_REF;
|
||||
|
||||
/* Return it */
|
||||
return Sym;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseByteArg (StrBuf* T, unsigned Arg)
|
||||
/* Parse the %b format specifier */
|
||||
{
|
||||
ExprDesc Expr;
|
||||
char Buf [16];
|
||||
|
||||
/* We expect an argument separated by a comma */
|
||||
ConsumeComma ();
|
||||
|
||||
/* Evaluate the expression */
|
||||
ConstSubExpr (hie1, &Expr);
|
||||
|
||||
/* Check the range but allow negative values if the type is signed */
|
||||
if (IsSignUnsigned (Expr.Type)) {
|
||||
if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFF) {
|
||||
AsmRangeError (Arg);
|
||||
Expr.ConstVal = 0;
|
||||
}
|
||||
} else {
|
||||
if (Expr.ConstVal < -128 || Expr.ConstVal > 127) {
|
||||
AsmRangeError (Arg);
|
||||
Expr.ConstVal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert into a hex number */
|
||||
xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.ConstVal & 0xFF);
|
||||
|
||||
/* Add the number to the target buffer */
|
||||
SB_AppendStr (T, Buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseWordArg (StrBuf* T, unsigned Arg)
|
||||
/* Parse the %w format specifier */
|
||||
{
|
||||
ExprDesc Expr;
|
||||
char Buf [16];
|
||||
|
||||
/* We expect an argument separated by a comma */
|
||||
ConsumeComma ();
|
||||
|
||||
/* Evaluate the expression */
|
||||
ConstSubExpr (hie1, &Expr);
|
||||
|
||||
/* Check the range but allow negative values if the type is signed */
|
||||
if (IsSignUnsigned (Expr.Type)) {
|
||||
if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFFFF) {
|
||||
AsmRangeError (Arg);
|
||||
Expr.ConstVal = 0;
|
||||
}
|
||||
} else {
|
||||
if (Expr.ConstVal < -32768 || Expr.ConstVal > 32767) {
|
||||
AsmRangeError (Arg);
|
||||
Expr.ConstVal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert into a hex number */
|
||||
xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.ConstVal & 0xFFFF);
|
||||
|
||||
/* Add the number to the target buffer */
|
||||
SB_AppendStr (T, Buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseLongArg (StrBuf* T, unsigned Arg)
|
||||
/* Parse the %l format specifier */
|
||||
{
|
||||
ExprDesc Expr;
|
||||
char Buf [16];
|
||||
|
||||
/* We expect an argument separated by a comma */
|
||||
ConsumeComma ();
|
||||
|
||||
/* Evaluate the expression */
|
||||
ConstSubExpr (hie1, &Expr);
|
||||
|
||||
/* Convert into a hex number */
|
||||
xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.ConstVal & 0xFFFFFFFF);
|
||||
|
||||
/* Add the number to the target buffer */
|
||||
SB_AppendStr (T, Buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseGVarArg (StrBuf* T, unsigned Arg)
|
||||
/* Parse the %v format specifier */
|
||||
{
|
||||
/* Parse the symbol name parameter and check the type */
|
||||
SymEntry* Sym = AsmGetSym (Arg, SC_STATIC);
|
||||
if (Sym == 0) {
|
||||
/* Some sort of error */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for external linkage */
|
||||
if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) {
|
||||
/* External linkage */
|
||||
/* ### FIXME: Asm name should be generated by codegen */
|
||||
SB_AppendChar (T, '_');
|
||||
SB_AppendStr (T, Sym->Name);
|
||||
} else {
|
||||
/* Static variable */
|
||||
char Buf [16];
|
||||
xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.Label);
|
||||
SB_AppendStr (T, Buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseLVarArg (StrBuf* T, unsigned Arg)
|
||||
/* Parse the %o format specifier */
|
||||
{
|
||||
unsigned Offs;
|
||||
char Buf [16];
|
||||
|
||||
/* Parse the symbol name parameter and check the type */
|
||||
SymEntry* Sym = AsmGetSym (Arg, SC_AUTO);
|
||||
if (Sym == 0) {
|
||||
/* Some sort of error */
|
||||
return;
|
||||
}
|
||||
|
||||
/* The symbol may be a parameter to a variadic function. In this case, we
|
||||
* don't have a fixed stack offset, so check it and bail out with an error
|
||||
* if this is the case.
|
||||
*/
|
||||
if ((Sym->Flags & SC_PARAM) == SC_PARAM && IsVariadic (CurrentFunc)) {
|
||||
Error ("Argument %u has no fixed stack offset", Arg);
|
||||
AsmErrorSkip ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate the current offset from SP */
|
||||
Offs = Sym->V.Offs - oursp;
|
||||
|
||||
/* Output the offset */
|
||||
xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs);
|
||||
SB_AppendStr (T, Buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ParseAsm (void)
|
||||
/* Parse the contents of the ASM statement */
|
||||
{
|
||||
unsigned I;
|
||||
unsigned Arg;
|
||||
|
||||
/* Create a target string buffer */
|
||||
StrBuf T = AUTO_STRBUF_INITIALIZER;
|
||||
|
||||
/* Create a string buffer from the string literal */
|
||||
StrBuf S = AUTO_STRBUF_INITIALIZER;
|
||||
GetLiteralStrBuf (&S, CurTok.IVal);
|
||||
|
||||
/* Reset the string pointer, effectivly clearing the string from the
|
||||
* string table. Since we're working with one token lookahead, this
|
||||
* will fail if the next token is also a string token, but that's a
|
||||
* syntax error anyway, because we expect a right paren.
|
||||
*/
|
||||
ResetLiteralPoolOffs (CurTok.IVal);
|
||||
|
||||
/* Skip the string token */
|
||||
NextToken ();
|
||||
|
||||
/* Parse the statement. It may contain several lines and one or more
|
||||
* of the following place holders:
|
||||
* %b - Numerical 8 bit value
|
||||
* %w - Numerical 16 bit value
|
||||
* %l - Numerical 32 bit value
|
||||
* %v - Assembler name of a (global) variable
|
||||
* %o - Stack offset of a (local) variable
|
||||
* %% - The % sign
|
||||
*/
|
||||
I = 0;
|
||||
Arg = 0;
|
||||
while (I < SB_GetLen (&S)) {
|
||||
|
||||
/* Get the next character */
|
||||
char C = SB_AtUnchecked (&S, I++);
|
||||
|
||||
/* If it is a newline, the current line is ready to go */
|
||||
if (C == '\n') {
|
||||
|
||||
/* Pass it to the backend and start over */
|
||||
g_asmcode (&T);
|
||||
SB_Clear (&T);
|
||||
|
||||
} else if (C == '%') {
|
||||
|
||||
/* Format specifier */
|
||||
++Arg;
|
||||
|
||||
/* Check if we have characters left */
|
||||
if (I >= SB_GetLen (&S)) {
|
||||
Error ("Error in __asm__ format specifier %u", Arg);
|
||||
AsmErrorSkip ();
|
||||
goto Done;
|
||||
} else {
|
||||
C = SB_AtUnchecked (&S, I++);
|
||||
switch (C) {
|
||||
case 'b': ParseByteArg (&T, Arg); break;
|
||||
case 'w': ParseWordArg (&T, Arg); break;
|
||||
case 'l': ParseLongArg (&T, Arg); break;
|
||||
case 'v': ParseGVarArg (&T, Arg); break;
|
||||
case 'o': ParseLVarArg (&T, Arg); break;
|
||||
case '%': SB_AppendChar (&T, '%'); break;
|
||||
default:
|
||||
Error ("Error in __asm__ format specifier %u", Arg);
|
||||
AsmErrorSkip ();
|
||||
goto Done;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* A normal character, just copy it */
|
||||
SB_AppendChar (&T, C);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* If the target buffer is not empty, we have a last line in there */
|
||||
if (!SB_IsEmpty (&T)) {
|
||||
g_asmcode (&T);
|
||||
}
|
||||
|
||||
Done:
|
||||
/* Call the string buf destructors */
|
||||
DoneStrBuf (&S);
|
||||
DoneStrBuf (&T);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AsmStatement (void)
|
||||
/* This function parses ASM statements. The syntax of the ASM directive
|
||||
* looks like the one defined for C++ (C has no ASM directive), that is,
|
||||
@ -64,39 +377,21 @@ void AsmStatement (void)
|
||||
|
||||
/* String literal */
|
||||
if (CurTok.Tok != TOK_SCONST) {
|
||||
|
||||
/* Print a diagnostic */
|
||||
Error ("String literal expected");
|
||||
|
||||
/* Try some smart error recovery: Skip tokens until we reach the
|
||||
* enclosing paren, or a semicolon.
|
||||
*/
|
||||
AsmErrorSkip ();
|
||||
|
||||
} else {
|
||||
|
||||
/* The string literal may consist of more than one line of assembler
|
||||
* code. Separate the single lines and output the code.
|
||||
*/
|
||||
const char* S = GetLiteral (CurTok.IVal);
|
||||
while (*S) {
|
||||
|
||||
/* Separate the lines */
|
||||
const char* E = strchr (S, '\n');
|
||||
if (E) {
|
||||
/* Found a newline */
|
||||
g_asmcode (S, E-S);
|
||||
S = E+1;
|
||||
} else {
|
||||
int Len = strlen (S);
|
||||
g_asmcode (S, Len);
|
||||
S += Len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the string pointer, effectivly clearing the string from the
|
||||
* string table. Since we're working with one token lookahead, this
|
||||
* will fail if the next token is also a string token, but that's a
|
||||
* syntax error anyway, because we expect a right paren.
|
||||
*/
|
||||
ResetLiteralPoolOffs (CurTok.IVal);
|
||||
/* Parse the ASM statement */
|
||||
ParseAsm ();
|
||||
}
|
||||
|
||||
/* Skip the string token */
|
||||
NextToken ();
|
||||
|
||||
/* Closing paren needed */
|
||||
ConsumeRParen ();
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
/* common */
|
||||
#include "check.h"
|
||||
#include "strbuf.h"
|
||||
#include "version.h"
|
||||
#include "xmalloc.h"
|
||||
#include "xsprintf.h"
|
||||
@ -3877,16 +3878,10 @@ void g_zerobytes (unsigned n)
|
||||
|
||||
|
||||
|
||||
void g_asmcode (const char* Line, int Len)
|
||||
/* Output one line of assembler code. If Len is greater than zero, it is used
|
||||
* as the maximum number of characters to use from Line.
|
||||
*/
|
||||
void g_asmcode (struct StrBuf* B)
|
||||
/* Output one line of assembler code. */
|
||||
{
|
||||
if (Len >= 0) {
|
||||
AddCodeLine ("%.*s", Len, Line);
|
||||
} else {
|
||||
AddCodeLine ("%s", Line);
|
||||
}
|
||||
AddCodeLine ("%.*s", SB_GetLen (B), SB_GetConstBuf (B));
|
||||
}
|
||||
|
||||
|
||||
|
@ -84,6 +84,9 @@
|
||||
/* Compiler relative stackpointer */
|
||||
extern int oursp;
|
||||
|
||||
/* Forward */
|
||||
struct StrBuf;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -437,10 +440,8 @@ void g_zerobytes (unsigned n);
|
||||
|
||||
|
||||
|
||||
void g_asmcode (const char* Line, int Len);
|
||||
/* Output one line of assembler code. If Len is greater than zero, it is used
|
||||
* as the maximum number of characters to use from Line.
|
||||
*/
|
||||
void g_asmcode (struct StrBuf* B);
|
||||
/* Output one line of assembler code. */
|
||||
|
||||
|
||||
|
||||
|
@ -468,7 +468,7 @@ static void MakeConstIntExpr (ExprDesc* Expr, long Value)
|
||||
|
||||
|
||||
|
||||
static void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr)
|
||||
void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr)
|
||||
/* Will evaluate an expression via the given function. If the result is not
|
||||
* a constant, a diagnostic will be printed, and the value is replaced by
|
||||
* a constant one to make sure there are no internal errors that result
|
||||
|
@ -60,10 +60,11 @@ struct ExprDesc {
|
||||
|
||||
|
||||
|
||||
void doasm (void);
|
||||
/* This function parses ASM statements. The syntax of the ASM directive
|
||||
* looks like the one defined for C++ (C has no ASM directive), that is,
|
||||
* a string literal in parenthesis.
|
||||
void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr);
|
||||
/* Will evaluate an expression via the given function. If the result is not
|
||||
* a constant, a diagnostic will be printed, and the value is replaced by
|
||||
* a constant one to make sure there are no internal errors that result
|
||||
* from this input error.
|
||||
*/
|
||||
|
||||
unsigned assignadjust (type* lhst, ExprDesc* rhs);
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
/* common */
|
||||
#include "check.h"
|
||||
#include "strbuf.h"
|
||||
#include "tgttrans.h"
|
||||
|
||||
/* cc65 */
|
||||
@ -165,6 +164,17 @@ const char* GetLiteral (unsigned Offs)
|
||||
|
||||
|
||||
|
||||
void GetLiteralStrBuf (StrBuf* Target, unsigned Offs)
|
||||
/* Copy the string starting at Offs and lasting to the end of the buffer
|
||||
* into Target.
|
||||
*/
|
||||
{
|
||||
CHECK (Offs <= SB_GetLen (&LiteralPool));
|
||||
SB_Slice (Target, &LiteralPool, Offs, SB_GetLen (&LiteralPool) - Offs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrintLiteralPoolStats (FILE* F)
|
||||
/* Print statistics about the literal space used */
|
||||
{
|
||||
|
@ -40,10 +40,13 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* common */
|
||||
#include "strbuf.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -88,6 +91,11 @@ unsigned AddLiteral (const char* S);
|
||||
const char* GetLiteral (unsigned Offs);
|
||||
/* Get a pointer to the literal with the given offset in the pool */
|
||||
|
||||
void GetLiteralStrBuf (StrBuf* Target, unsigned Offs);
|
||||
/* Copy the string starting at Offs and lasting to the end of the buffer
|
||||
* into Target.
|
||||
*/
|
||||
|
||||
void PrintLiteralPoolStats (FILE* F);
|
||||
/* Print statistics about the literal space used */
|
||||
|
||||
|
@ -801,6 +801,30 @@ void NextToken (void)
|
||||
|
||||
|
||||
|
||||
void SkipTokens (const token_t* TokenList, unsigned TokenCount)
|
||||
/* Skip tokens until we reach TOK_CEOF or a token in the given token list.
|
||||
* This routine is used for error recovery.
|
||||
*/
|
||||
{
|
||||
while (CurTok.Tok != TOK_CEOF) {
|
||||
|
||||
/* Check if the current token is in the token list */
|
||||
unsigned I;
|
||||
for (I = 0; I < TokenCount; ++I) {
|
||||
if (CurTok.Tok == TokenList[I]) {
|
||||
/* Found a token in the list */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not in the list: Skip it */
|
||||
NextToken ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Consume (token_t Token, const char* ErrorMsg)
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
* message.
|
||||
|
@ -191,7 +191,7 @@ extern Token NextTok; /* The next token */
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* code */
|
||||
/* code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -205,6 +205,11 @@ int IsSym (char* s);
|
||||
void NextToken (void);
|
||||
/* Get next token from input stream */
|
||||
|
||||
void SkipTokens (const token_t* TokenList, unsigned TokenCount);
|
||||
/* Skip tokens until we reach TOK_CEOF or a token in the given token list.
|
||||
* This routine is used for error recovery.
|
||||
*/
|
||||
|
||||
void Consume (token_t Token, const char* ErrorMsg);
|
||||
/* Eat token if it is the next in the input stream, otherwise print an error
|
||||
* message.
|
||||
|
@ -42,31 +42,13 @@
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Helpers */
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
void SB_Realloc (StrBuf* B, unsigned NewSize)
|
||||
/* Reallocate the string buffer space, make sure at least NewSize bytes are
|
||||
* available.
|
||||
*/
|
||||
{
|
||||
/* Get the current size, use a minimum of 8 bytes */
|
||||
unsigned NewAllocated = B->Allocated;
|
||||
if (NewAllocated == 0) {
|
||||
NewAllocated = 8;
|
||||
}
|
||||
|
||||
/* Round up to the next power of two */
|
||||
while (NewAllocated < NewSize) {
|
||||
NewAllocated *= 2;
|
||||
}
|
||||
|
||||
/* Reallocate the buffer */
|
||||
B->Buf = xrealloc (B->Buf, NewAllocated);
|
||||
B->Allocated = NewAllocated;
|
||||
}
|
||||
/* An empty string buf */
|
||||
const StrBuf EmptyStrBuf = STATIC_STRBUF_INITIALIZER;
|
||||
|
||||
|
||||
|
||||
@ -119,6 +101,29 @@ void FreeStrBuf (StrBuf* B)
|
||||
|
||||
|
||||
|
||||
void SB_Realloc (StrBuf* B, unsigned NewSize)
|
||||
/* Reallocate the string buffer space, make sure at least NewSize bytes are
|
||||
* available.
|
||||
*/
|
||||
{
|
||||
/* Get the current size, use a minimum of 8 bytes */
|
||||
unsigned NewAllocated = B->Allocated;
|
||||
if (NewAllocated == 0) {
|
||||
NewAllocated = 8;
|
||||
}
|
||||
|
||||
/* Round up to the next power of two */
|
||||
while (NewAllocated < NewSize) {
|
||||
NewAllocated *= 2;
|
||||
}
|
||||
|
||||
/* Reallocate the buffer */
|
||||
B->Buf = xrealloc (B->Buf, NewAllocated);
|
||||
B->Allocated = NewAllocated;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SB_Terminate (StrBuf* B)
|
||||
/* Zero terminate the given string buffer. NOTE: The terminating zero is not
|
||||
* accounted for in B->Len, if you want that, you have to use AppendChar!
|
||||
@ -133,6 +138,18 @@ void SB_Terminate (StrBuf* B)
|
||||
|
||||
|
||||
|
||||
void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size)
|
||||
/* Copy Buf to Target, discarding the old contents of Target */
|
||||
{
|
||||
if (Target->Allocated < Size) {
|
||||
SB_Realloc (Target, Size);
|
||||
}
|
||||
memcpy (Target->Buf, Buf, Size);
|
||||
Target->Len = Size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SB_AppendChar (StrBuf* B, char C)
|
||||
/* Append a character to a string buffer */
|
||||
{
|
||||
@ -159,18 +176,6 @@ void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size)
|
||||
|
||||
|
||||
|
||||
void SB_Copy (StrBuf* Target, const StrBuf* Source)
|
||||
/* Copy Source to Target, discarding the old contents of Target */
|
||||
{
|
||||
if (Target->Allocated < Source->Allocated) {
|
||||
SB_Realloc (Target, Source->Allocated);
|
||||
}
|
||||
memcpy (Target->Buf, Source->Buf, Source->Len);
|
||||
Target->Len = Source->Len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SB_Slice (StrBuf* Target, const StrBuf* Source, unsigned Start, unsigned Len)
|
||||
/* Copy a slice from Source into Target. The current contents of Target are
|
||||
* destroyed. If Start is greater than the length of Source, or if Len
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
/* common */
|
||||
#include "attrib.h"
|
||||
#include "check.h"
|
||||
#include "inline.h"
|
||||
|
||||
|
||||
@ -59,9 +60,15 @@ struct StrBuf {
|
||||
char* Buf;
|
||||
};
|
||||
|
||||
/* An empty string buf */
|
||||
extern const StrBuf EmptyStrBuf;
|
||||
|
||||
/* Initializer for static string bufs */
|
||||
#define STATIC_STRBUF_INITIALIZER { 0, 0, 0 }
|
||||
|
||||
/* Initializer for auto string bufs */
|
||||
#define AUTO_STRBUF_INITIALIZER EmptyStrBuf
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -117,6 +124,39 @@ INLINE char* SB_GetBuf (StrBuf* B)
|
||||
# define SB_GetBuf(B) (B)->Buf
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE char SB_At (const StrBuf* B, unsigned Index)
|
||||
/* Get a character from the buffer */
|
||||
{
|
||||
PRECONDITION (Index < B->Len);
|
||||
return B->Buf[Index];
|
||||
}
|
||||
#else
|
||||
# define SB_At(B, Index) \
|
||||
(PRECONDITION ((Index) < (B)->Len), \
|
||||
(B)->Buf[Index])
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE char SB_AtUnchecked (const StrBuf* B, unsigned Index)
|
||||
/* Get a character from the buffer */
|
||||
{
|
||||
return B->Buf[Index];
|
||||
}
|
||||
#else
|
||||
# define SB_AtUnchecked(B, Index) ((B)->Buf[Index])
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE int SB_IsEmpty (const StrBuf* B)
|
||||
/* Return true if the string buffer is empty */
|
||||
{
|
||||
return (B->Len == 0);
|
||||
}
|
||||
#else
|
||||
# define SB_IsEmpty(B) ((B)->Len == 0)
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE void SB_Clear (StrBuf* B)
|
||||
/* Clear the string buffer (make it empty) */
|
||||
@ -132,6 +172,29 @@ void SB_Terminate (StrBuf* B);
|
||||
* accounted for in B->Len, if you want that, you have to use AppendChar!
|
||||
*/
|
||||
|
||||
void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size);
|
||||
/* Copy Buf to Target, discarding the old contents of Target */
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE void SB_CopyStr (StrBuf* Target, const char* S)
|
||||
/* Copy S to Target, discarding the old contents of Target */
|
||||
{
|
||||
SB_CopyBuf (Target, S, strlen (S));
|
||||
}
|
||||
#else
|
||||
# define SB_CopyStr(Target, S) SB_CopyBuf (Target, S, strlen (S))
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE void SB_Copy (StrBuf* Target, const StrBuf* Source)
|
||||
/* Copy Source to Target, discarding the old contents of Target */
|
||||
{
|
||||
SB_CopyBuf (Target, Source->Buf, Source->Len);
|
||||
}
|
||||
#else
|
||||
# define SB_Copy(Target, Source) SB_CopyBuf (Target, (Source)->Buf, (Source)->Len)
|
||||
#endif
|
||||
|
||||
void SB_AppendChar (StrBuf* B, char C);
|
||||
/* Append a character to a string buffer */
|
||||
|
||||
@ -148,9 +211,6 @@ INLINE void SB_AppendStr (StrBuf* B, const char* S)
|
||||
# define SB_AppendStr(B, S) SB_AppendBuf (B, S, strlen (S))
|
||||
#endif
|
||||
|
||||
void SB_Copy (StrBuf* Target, const StrBuf* Source);
|
||||
/* Copy Source to Target, discarding the old contents of Target */
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE void SB_Append (StrBuf* Target, const StrBuf* Source)
|
||||
/* Append the contents of Source to Target */
|
||||
|
Loading…
x
Reference in New Issue
Block a user