updated codestyle
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make-Without-sqlite3 (push) Successful in 6s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake-Without-sqlite3 (push) Successful in 9s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson-Without-sqlite3 (push) Successful in 7s

This commit is contained in:
wt committed 2025-11-18 11:48:46 +07:00
1 parent 193779bd13
commit 36aa1bea2a
10 files changed
+18581 -4161

No files matched your search

+2244 -2570
View File
File diff suppressed because it is too large. Load diff
+218 -129
View File
@@ -24,23 +24,27 @@
#define cJSON__h #define cJSON__h
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #if !defined(__WINDOWS__) && \
(defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__ #define __WINDOWS__
#endif #endif
#ifdef __WINDOWS__ #ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: /* When compiling for windows, we specify a specific calling convention to avoid
issues where we are being called from a project with a different default calling
convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you
want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by For *nix builds that support visibility attribute, you can define similar
behavior by
setting default visibility to hidden by adding setting default visibility to hidden by adding
-fvisibility=hidden (for gcc) -fvisibility=hidden (for gcc)
@@ -48,31 +52,35 @@ or
-xldscope=hidden (for sun cc) -xldscope=hidden (for sun cc)
to CFLAGS to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way
CJSON_EXPORT_SYMBOLS does
*/ */
#define CJSON_CDECL __cdecl #define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall #define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */ /* export symbols by default, this is necessary for copy pasting the C and
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) * header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \
!defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS #define CJSON_EXPORT_SYMBOLS
#endif #endif
#if defined(CJSON_HIDE_SYMBOLS) #if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL #define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS) #elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS) #elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif #endif
#else /* !__WINDOWS__ */ #else /* !__WINDOWS__ */
#define CJSON_CDECL #define CJSON_CDECL
#define CJSON_STDCALL #define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else #else
#define CJSON_PUBLIC(type) type #define CJSON_PUBLIC(type) type
#endif #endif
@@ -87,115 +95,146 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
/* cJSON Types: */ /* cJSON Types: */
#define cJSON_Invalid (0) #define cJSON_Invalid (0)
#define cJSON_False (1 << 0) #define cJSON_False (1 << 0)
#define cJSON_True (1 << 1) #define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2) #define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3) #define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4) #define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5) #define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6) #define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256 #define cJSON_IsReference 256
#define cJSON_StringIsConst 512 #define cJSON_StringIsConst 512
/* The cJSON structure: */ /* The cJSON structure: */
typedef struct cJSON typedef struct cJSON {
{ /* next/prev allow you to walk array/object chains. Alternatively, use
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ * GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next; struct cJSON *next;
struct cJSON *prev; struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ /* An array or object item will have a child pointer pointing to a chain of
struct cJSON *child; * the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */ /* The type of the item, as above. */
int type; int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring; char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint; int valueint;
/* The item's number, if type==cJSON_Number */ /* The item's number, if type==cJSON_Number */
double valuedouble; double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ /* The item's name string, if this item is the child of, or is in the list of
char *string; * subitems of an object. */
char *string;
} cJSON; } cJSON;
typedef struct cJSON_Hooks typedef struct cJSON_Hooks {
{ /* malloc/free are CDECL on Windows regardless of the default calling
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ * convention of the compiler, so ensure the hooks allow passing those
void *(CJSON_CDECL *malloc_fn)(size_t sz); * functions directly. */
void (CJSON_CDECL *free_fn)(void *ptr); void *(CJSON_CDECL *malloc_fn)(size_t sz);
void(CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks; } cJSON_Hooks;
typedef int cJSON_bool; typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse
* This is to prevent stack overflows. */ * them. This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT #ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000 #define CJSON_NESTING_LIMIT 1000
#endif #endif
/* Limits the length of circular references can be before cJSON rejects to parse them. /* Limits the length of circular references can be before cJSON rejects to parse
* This is to prevent stack overflows. */ * them. This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT #ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000 #define CJSON_CIRCULAR_LIMIT 10000
#endif #endif
/* returns the version of cJSON as a string */ /* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void); CJSON_PUBLIC(const char *) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */ /* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Memory Management: the caller is always responsible to free the results from
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ * all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib
* free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is
* cJSON_PrintPreallocated, where the caller has full responsibility of the
* buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate.
*/
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); CJSON_PUBLIC(cJSON *)
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ /* ParseWithOpts allows you to require (and check) that the JSON is null
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); * terminated, and to retrieve the pointer to the final byte parsed. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); /* If you supply a ptr in return_parse_end and parsing fails, then
* return_parse_end will contain a pointer to the error so will match
* cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *)
cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *)
cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length,
const char **return_parse_end,
cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */ /* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */ /* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); * at the final size. guessing well reduces reallocation. fmt=0 gives
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ * unformatted, =1 gives formatted */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(char *)
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with
* given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will
* use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool)
cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length,
const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */ /* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */ /* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ /* Retrieve item number "index" from array "array". Returns NULL if
* unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */ /* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); cJSON_GetObjectItem(const cJSON *const object, const char *const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ cJSON_GetObjectItemCaseSensitive(const cJSON *const object,
const char *const string);
CJSON_PUBLIC(cJSON_bool)
cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error.
* You'll probably need to look a few chars back to make sense of it. Defined
* when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */ /* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item);
/* These functions check the type of an item */ /* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item);
/* These calls create a cJSON item of the appropriate type. */ /* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
@@ -218,84 +257,134 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items. /* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ * The parameter count cannot be greater than the number of elements in the
* number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); CJSON_PUBLIC(cJSON *)
cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */ /* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); CJSON_PUBLIC(cJSON_bool)
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before /* Use this when string is definitely const (i.e. a literal, or as good as), and
* writing to `item->string` */ * will definitely survive the cJSON object. WARNING: When this function was
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); * used, make sure to always check that (item->type & cJSON_StringIsConst) is
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ * zero before writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool)
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you
* want to add an existing cJSON to a new cJSON, but don't want to corrupt your
* existing cJSON. */
CJSON_PUBLIC(cJSON_bool)
cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool)
cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */ /* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *)
cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void)
cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void)
cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */ /* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool)
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); cJSON_InsertItemInArray(
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); cJSON *array, int which,
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item,
cJSON *replacement);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string,
cJSON *newitem);
/* Duplicate a cJSON item */ /* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will /* Duplicate will create a new, identical cJSON item to the one you pass, in new
* need to be released. With recurse!=0, it will duplicate any children connected to the item. * memory that will need to be released. With recurse!=0, it will duplicate any
* The item->next and ->prev pointers are always zero on return from Duplicate. */ * children connected to the item. The item->next and ->prev pointers are always
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * zero on return from Duplicate. */
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); * invalid, they will be considered unequal. case_sensitive determines if object
* keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool)
cJSON_Compare(const cJSON *const a, const cJSON *const b,
const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from
* The input pointer json cannot point to a read-only address area, such as a string constant, * strings. The input pointer json cannot point to a read-only address area,
* but should point to a readable and writable address area. */ * such as a string constant, but should point to a readable and writable
* address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json); CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time. /* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */ * They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); cJSON_AddNullToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); cJSON_AddTrueToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); cJSON_AddFalseToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); cJSON_AddBoolToObject(cJSON *const object, const char *const name,
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); const cJSON_bool boolean);
CJSON_PUBLIC(cJSON *)
cJSON_AddNumberToObject(cJSON *const object, const char *const name,
const double number);
CJSON_PUBLIC(cJSON *)
cJSON_AddStringToObject(cJSON *const object, const char *const name,
const char *const string);
CJSON_PUBLIC(cJSON *)
cJSON_AddRawToObject(cJSON *const object, const char *const name,
const char *const raw);
CJSON_PUBLIC(cJSON *)
cJSON_AddObjectToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON *)
cJSON_AddArrayToObject(cJSON *const object, const char *const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */ /* When assigning an integer value, it needs to be propagated to valuedouble
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) * too. */
#define cJSON_SetIntValue(object, number) \
((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */ /* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) #define cJSON_SetNumberValue(object, number) \
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); /* Change the valuestring of a cJSON_String object, only takes effect when type
* of object is cJSON_String */
CJSON_PUBLIC(char *)
cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ /* If the object is not a boolean type this does nothing and returns
#define cJSON_SetBoolValue(object, boolValue) ( \ * cJSON_Invalid else it returns the new type*/
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ #define cJSON_SetBoolValue(object, boolValue) \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \
cJSON_Invalid\ ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | \
) ((boolValue) ? cJSON_True : cJSON_False) \
: cJSON_Invalid)
/* Macro for iterating over an array or object */ /* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) #define cJSON_ArrayForEach(element, array) \
for (element = (array != NULL) ? (array)->child : NULL; element != NULL; \
element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ /* malloc/free objects using the malloc/free functions that have been set with
* cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object); CJSON_PUBLIC(void) cJSON_free(void *object);
+241 -224
View File
@@ -1,13 +1,13 @@
#include "terminal.h"
#include <cJSON.h>
#include <errno.h>
#include <mongoose.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <sys/wait.h>
#include <mongoose.h>
#include <cJSON.h>
#include "terminal.h"
#define HTTP_PORT "8888" #define HTTP_PORT "8888"
#define STATIC_DIR "./static/" #define STATIC_DIR "./static/"
@@ -16,284 +16,301 @@ static const char *s_http_addr = "http://0.0.0.0:8888";
static struct mg_mgr mgr; static struct mg_mgr mgr;
struct client_data { struct client_data {
struct terminal_session term; struct terminal_session term;
bool terminal_ready; bool terminal_ready;
}; };
// Функция для логирования запросов // Функция для логирования запросов
static void log_request(struct mg_connection *c, struct mg_http_message *hm) { static void log_request(struct mg_connection *c, struct mg_http_message *hm) {
printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf, printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf,
(int)hm->uri.len, hm->uri.buf); (int)hm->uri.len, hm->uri.buf);
} }
// Проверяем, жив ли дочерний процесс // Проверяем, жив ли дочерний процесс
static bool is_process_alive(pid_t pid) { static bool is_process_alive(pid_t pid) {
if (pid <= 0) return false; if (pid <= 0)
return false;
int status; int status;
pid_t result = waitpid(pid, &status, WNOHANG); pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) { if (result == 0) {
// Процесс все еще работает // Процесс все еще работает
return true; return true;
} else if (result == -1) { } else if (result == -1) {
// Ошибка // Ошибка
if (errno == ECHILD) { if (errno == ECHILD) {
// Процесс не существует // Процесс не существует
return false; return false;
}
perror("waitpid");
return false;
} else {
// Процесс завершился
return false;
} }
perror("waitpid");
return false;
} else {
// Процесс завершился
return false;
}
} }
// Перезапускаем терминальную сессию // Перезапускаем терминальную сессию
static void restart_terminal_session(struct mg_connection *c, struct client_data *cd) { static void restart_terminal_session(struct mg_connection *c,
if (!cd) return; struct client_data *cd) {
if (!cd)
return;
printf("Restarting terminal session...\n"); printf("Restarting terminal session...\n");
// Уничтожаем старую сессию // Уничтожаем старую сессию
if (cd->terminal_ready) { if (cd->terminal_ready) {
destroy_terminal_session(&cd->term); destroy_terminal_session(&cd->term);
} }
// Создаем новую сессию // Создаем новую сессию
const char *shell = getenv("SHELL"); const char *shell = getenv("SHELL");
if (!shell) shell = "/bin/bash"; if (!shell)
shell = "/bin/bash";
if (create_terminal_session(&cd->term, shell) == 0) { if (create_terminal_session(&cd->term, shell) == 0) {
cd->terminal_ready = true; cd->terminal_ready = true;
mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT); mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT);
} else { } else {
mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37, WEBSOCKET_OP_TEXT); mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37,
cd->terminal_ready = false; WEBSOCKET_OP_TEXT);
} cd->terminal_ready = false;
}
} }
// Обработчик WebSocket сообщений // Обработчик WebSocket сообщений
static void handle_websocket_message(struct mg_connection *c, struct mg_ws_message *wm, struct client_data *cd) { static void handle_websocket_message(struct mg_connection *c,
if (!cd) return; struct mg_ws_message *wm,
struct client_data *cd) {
if (!cd)
return;
if (wm->data.len == 0) { if (wm->data.len == 0) {
return; return;
}
// Парсим JSON для проверки resize сообщения
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
if (json != NULL) {
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols");
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows");
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
resize_terminal(&cd->term, rows->valueint, cols->valueint);
cJSON_Delete(json);
return;
} }
cJSON_Delete(json);
}
// Парсим JSON для проверки resize сообщения // Проверяем, не команда ли это exit
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len); if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
if (json != NULL) { printf("Exit command detected, restarting terminal session\n");
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols");
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows");
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
resize_terminal(&cd->term, rows->valueint, cols->valueint);
cJSON_Delete(json);
return;
}
cJSON_Delete(json);
}
// Проверяем, не команда ли это exit
if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
printf("Exit command detected, restarting terminal session\n");
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
// Даем команде выполниться, затем перезапускаем
usleep(100);
restart_terminal_session(c, cd);
return;
}
// Обычный ввод от пользователя
write_to_terminal(&cd->term, wm->data.buf, wm->data.len); write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
// Даем команде выполниться, затем перезапускаем
usleep(100);
restart_terminal_session(c, cd);
return;
}
// Обычный ввод от пользователя
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
} }
// Обработчик HTTP запросов // Обработчик HTTP запросов
static void fn(struct mg_connection *c, int ev, void *ev_data) { static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *)ev_data; struct mg_http_message *hm = (struct mg_http_message *)ev_data;
log_request(c, hm); log_request(c, hm);
if (mg_match(hm->uri, mg_str("/ws"), nullptr)) { if (mg_match(hm->uri, mg_str("/ws"), nullptr)) {
// Проверяем, нет ли уже активного соединения // Проверяем, нет ли уже активного соединения
if (c->fn_data != NULL) { if (c->fn_data != NULL) {
printf("WebSocket connection already exists for this connection, cleaning up...\n"); printf("WebSocket connection already exists for this connection, "
struct client_data *old_cd = (struct client_data *)c->fn_data; "cleaning up...\n");
destroy_terminal_session(&old_cd->term); struct client_data *old_cd = (struct client_data *)c->fn_data;
free(old_cd); destroy_terminal_session(&old_cd->term);
c->fn_data = NULL; free(old_cd);
} c->fn_data = NULL;
}
// Upgrade до WebSocket // Upgrade до WebSocket
struct client_data *cd = calloc(1, sizeof(struct client_data)); struct client_data *cd = calloc(1, sizeof(struct client_data));
if (!cd) { if (!cd) {
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Memory allocation failed"); mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
return; "Memory allocation failed");
} return;
}
// Создаем терминальную сессию // Создаем терминальную сессию
const char *shell = getenv("SHELL"); const char *shell = getenv("SHELL");
if (!shell) shell = "/bin/bash"; if (!shell)
shell = "/bin/bash";
printf("Creating new terminal session with shell: %s\n", shell); printf("Creating new terminal session with shell: %s\n", shell);
if (create_terminal_session(&cd->term, shell) != 0) { if (create_terminal_session(&cd->term, shell) != 0) {
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Failed to create terminal session"); mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
free(cd); "Failed to create terminal session");
return; free(cd);
} return;
}
cd->terminal_ready = true; cd->terminal_ready = true;
c->fn_data = cd; c->fn_data = cd;
// Upgrade to WebSocket // Upgrade to WebSocket
struct mg_str *ws_proto = mg_http_get_header(hm, "Sec-WebSocket-Protocol"); struct mg_str *ws_proto =
if (ws_proto != NULL) { mg_http_get_header(hm, "Sec-WebSocket-Protocol");
mg_ws_upgrade(c, hm, ws_proto->buf); if (ws_proto != NULL) {
} else { mg_ws_upgrade(c, hm, ws_proto->buf);
mg_ws_upgrade(c, hm, NULL); } else {
} mg_ws_upgrade(c, hm, NULL);
}
printf("WebSocket upgrade completed\n"); printf("WebSocket upgrade completed\n");
} else if (mg_match(hm->uri, mg_str("/"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/"), nullptr)) {
// Главная страница // Главная страница
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/index.html", &opts); mg_http_serve_file(c, hm, "./static/index.html", &opts);
} else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) {
// xterm.js // xterm.js
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm.js", &opts); mg_http_serve_file(c, hm, "./static/xterm.js", &opts);
} else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) {
// xterm.css // xterm.css
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm.css", &opts); mg_http_serve_file(c, hm, "./static/xterm.css", &opts);
} else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) {
// xterm-addon-fit.js // xterm-addon-fit.js
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts); mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts);
} else { } else {
mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found"); mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found");
}
} else if (ev == MG_EV_WS_OPEN) {
// WebSocket соединение установлено
printf("WebSocket connection opened\n");
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49, WEBSOCKET_OP_TEXT);
}
} else if (ev == MG_EV_WS_MSG) {
// Сообщение от WebSocket клиента
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
struct client_data *cd = (struct client_data *)c->fn_data;
handle_websocket_message(c, wm, cd);
} else if (ev == MG_EV_CLOSE) {
// Закрытие соединения
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
printf("WebSocket connection closed, cleaning up...\n");
destroy_terminal_session(&cd->term);
free(cd);
c->fn_data = NULL;
}
} else if (ev == MG_EV_POLL) {
// Проверяем данные от терминала и отправляем через WebSocket
struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (!conn->is_websocket) continue;
struct client_data *cd = (struct client_data *)conn->fn_data;
if (cd && cd->terminal_ready) {
// Проверяем, не завершился ли процесс
if (!is_process_alive(cd->term.child_pid)) {
printf("Terminal process died, restarting...\n");
restart_terminal_session(conn, cd);
continue;
}
// Читаем данные из терминала
char buffer[1024];
ssize_t bytes_read = read_from_terminal(&cd->term, buffer, sizeof(buffer));
if (bytes_read > 0) {
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
} else if (bytes_read == 0) {
// EOF - процесс завершился
printf("EOF from terminal, restarting...\n");
restart_terminal_session(conn, cd);
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
// Ошибка чтения
printf("Error reading from terminal: %s\n", strerror(errno));
restart_terminal_session(conn, cd);
}
}
}
} }
} else if (ev == MG_EV_WS_OPEN) {
// WebSocket соединение установлено
printf("WebSocket connection opened\n");
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49,
WEBSOCKET_OP_TEXT);
}
} else if (ev == MG_EV_WS_MSG) {
// Сообщение от WebSocket клиента
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
struct client_data *cd = (struct client_data *)c->fn_data;
handle_websocket_message(c, wm, cd);
} else if (ev == MG_EV_CLOSE) {
// Закрытие соединения
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
printf("WebSocket connection closed, cleaning up...\n");
destroy_terminal_session(&cd->term);
free(cd);
c->fn_data = NULL;
}
} else if (ev == MG_EV_POLL) {
// Проверяем данные от терминала и отправляем через WebSocket
struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (!conn->is_websocket)
continue;
struct client_data *cd = (struct client_data *)conn->fn_data;
if (cd && cd->terminal_ready) {
// Проверяем, не завершился ли процесс
if (!is_process_alive(cd->term.child_pid)) {
printf("Terminal process died, restarting...\n");
restart_terminal_session(conn, cd);
continue;
}
// Читаем данные из терминала
char buffer[1024];
ssize_t bytes_read =
read_from_terminal(&cd->term, buffer, sizeof(buffer));
if (bytes_read > 0) {
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
} else if (bytes_read == 0) {
// EOF - процесс завершился
printf("EOF from terminal, restarting...\n");
restart_terminal_session(conn, cd);
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
// Ошибка чтения
printf("Error reading from terminal: %s\n", strerror(errno));
restart_terminal_session(conn, cd);
}
}
}
}
} }
// Обработчик сигналов для graceful shutdown // Обработчик сигналов для graceful shutdown
static void signal_handler(int sig) { static void signal_handler(int sig) {
printf("\nShutting down server...\n"); printf("\nShutting down server...\n");
// Очищаем все соединения // Очищаем все соединения
struct mg_connection *conn; struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) { for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (conn->fn_data) { if (conn->fn_data) {
struct client_data *cd = (struct client_data *)conn->fn_data; struct client_data *cd = (struct client_data *)conn->fn_data;
destroy_terminal_session(&cd->term); destroy_terminal_session(&cd->term);
free(cd); free(cd);
conn->fn_data = NULL; conn->fn_data = NULL;
}
} }
}
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
exit(0); exit(0);
} }
// Проверяем существование директории // Проверяем существование директории
static bool directory_exists(const char *path) { static bool directory_exists(const char *path) {
struct stat st; struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode); return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
} }
int main(void) { int main(void) {
// Устанавливаем обработчики сигналов // Устанавливаем обработчики сигналов
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
// Инициализируем менеджер соединений // Инициализируем менеджер соединений
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
printf("Mongoose version: %s\n", MG_VERSION); printf("Mongoose version: %s\n", MG_VERSION);
// Проверяем существование статической директории // Проверяем существование статической директории
if (!directory_exists(STATIC_DIR)) { if (!directory_exists(STATIC_DIR)) {
fprintf(stderr, "Static directory '%s' not found! Creating it...\n", STATIC_DIR); fprintf(stderr, "Static directory '%s' not found! Creating it...\n",
if (mkdir(STATIC_DIR, 0755) != 0) { STATIC_DIR);
fprintf(stderr, "Failed to create static directory\n"); if (mkdir(STATIC_DIR, 0755) != 0) {
return 1; fprintf(stderr, "Failed to create static directory\n");
} return 1;
} }
}
// Создаем HTTP сервер // Создаем HTTP сервер
struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL); struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL);
if (c == NULL) { if (c == NULL) {
fprintf(stderr, "Failed to start server on %s\n", s_http_addr); fprintf(stderr, "Failed to start server on %s\n", s_http_addr);
return 1; return 1;
} }
printf("Starting web terminal server on %s\n", s_http_addr); printf("Starting web terminal server on %s\n", s_http_addr);
printf("Static files directory: %s\n", STATIC_DIR); printf("Static files directory: %s\n", STATIC_DIR);
printf("Server is ready. Open http://localhost:8000 in your browser\n"); printf("Server is ready. Open http://localhost:8000 in your browser\n");
// Основной цикл // Основной цикл
while (true) { while (true) {
mg_mgr_poll(&mgr, 50); // 50ms timeout mg_mgr_poll(&mgr, 50); // 50ms timeout
} }
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
return 0; return 0;
} }
+751 -923
View File
File diff suppressed because it is too large. Load diff
+132 -132
View File
@@ -1,158 +1,158 @@
#include "terminal.h" #include "terminal.h"
#include <errno.h>
#include <fcntl.h>
#include <pty.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <pty.h> #include <unistd.h>
#include <utmp.h> #include <utmp.h>
#include <signal.h>
#include <errno.h>
int create_terminal_session(struct terminal_session *session, const char *shell) { int create_terminal_session(struct terminal_session *session,
// Создаем pty const char *shell) {
int master, slave; // Создаем pty
char slave_name[64]; int master, slave;
char slave_name[64];
if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) { if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) {
perror("openpty failed"); perror("openpty failed");
return -1; return -1;
} }
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
perror("fork failed"); perror("fork failed");
close(master); close(master);
close(slave);
return -1;
}
if (pid == 0) {
// Дочерний процесс
close(master);
// Устанавливаем pty как контролирующий терминал
setsid();
ioctl(slave, TIOCSCTTY, 1);
// Перенаправляем стандартные потоки
dup2(slave, STDIN_FILENO);
dup2(slave, STDOUT_FILENO);
dup2(slave, STDERR_FILENO);
if (slave > STDERR_FILENO) {
close(slave);
}
// Устанавливаем переменные окружения
setenv("TERM", "xterm-256color", 1);
setenv("COLORTERM", "truecolor", 1);
// Запускаем shell
execlp(shell, shell, NULL);
// Если execlp вернулся, значит произошла ошибка
perror("execlp failed");
exit(EXIT_FAILURE);
}
// Родительский процесс
close(slave); close(slave);
return -1;
}
session->master_pty = master; if (pid == 0) {
session->slave_pty = slave; // Дочерний процесс
session->child_pid = pid; close(master);
session->active = true;
// Устанавливаем неблокирующий режим // Устанавливаем pty как контролирующий терминал
int flags = fcntl(master, F_GETFL, 0); setsid();
fcntl(master, F_SETFL, flags | O_NONBLOCK); ioctl(slave, TIOCSCTTY, 1);
printf("Created terminal session with PID %d\n", pid); // Перенаправляем стандартные потоки
return 0; dup2(slave, STDIN_FILENO);
dup2(slave, STDOUT_FILENO);
dup2(slave, STDERR_FILENO);
if (slave > STDERR_FILENO) {
close(slave);
}
// Устанавливаем переменные окружения
setenv("TERM", "xterm-256color", 1);
setenv("COLORTERM", "truecolor", 1);
// Запускаем shell
execlp(shell, shell, NULL);
// Если execlp вернулся, значит произошла ошибка
perror("execlp failed");
exit(EXIT_FAILURE);
}
// Родительский процесс
close(slave);
session->master_pty = master;
session->slave_pty = slave;
session->child_pid = pid;
session->active = true;
// Устанавливаем неблокирующий режим
int flags = fcntl(master, F_GETFL, 0);
fcntl(master, F_SETFL, flags | O_NONBLOCK);
printf("Created terminal session with PID %d\n", pid);
return 0;
} }
void destroy_terminal_session(struct terminal_session *session) { void destroy_terminal_session(struct terminal_session *session) {
if (!session || !session->active) { if (!session || !session->active) {
return; return;
} }
printf("Destroying terminal session with PID %d\n", session->child_pid); printf("Destroying terminal session with PID %d\n", session->child_pid);
// Завершаем дочерний процесс // Завершаем дочерний процесс
if (session->child_pid > 0) { if (session->child_pid > 0) {
// Отправляем SIGKILL // Отправляем SIGKILL
int status; int status;
kill(session->child_pid, SIGKILL); kill(session->child_pid, SIGKILL);
waitpid(session->child_pid, &status, 0); waitpid(session->child_pid, &status, 0);
} }
// Закрываем pty // Закрываем pty
if (session->master_pty != -1) { if (session->master_pty != -1) {
close(session->master_pty); close(session->master_pty);
session->master_pty = -1; session->master_pty = -1;
} }
session->active = false; session->active = false;
session->child_pid = 0; session->child_pid = 0;
printf("Terminal session destroyed\n"); printf("Terminal session destroyed\n");
} }
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size) { ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
if (!session || !session->active) { size_t size) {
return -1; if (!session || !session->active) {
} return -1;
}
ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
} else if (bytes_read == 0) {
// EOF - соединение закрыто
return 0;
} else if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read from terminal failed");
return -1;
}
// Нет данных для чтения - это нормально в неблокирующем режиме
return -2;
}
return bytes_read;
}
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size) {
if (!session || !session->active) {
return -1;
}
ssize_t bytes_written = write(session->master_pty, data, size);
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("write to terminal failed");
return -1;
}
return bytes_written;
}
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols) {
if (!session || !session->active) {
return -1;
}
struct winsize ws = {
.ws_row = rows,
.ws_col = cols,
.ws_xpixel = 0,
.ws_ypixel = 0
};
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
perror("ioctl TIOCSWINSZ failed");
return -1;
}
ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
} else if (bytes_read == 0) {
// EOF - соединение закрыто
return 0; return 0;
} else if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read from terminal failed");
return -1;
}
// Нет данных для чтения - это нормально в неблокирующем режиме
return -2;
}
return bytes_read;
}
ssize_t write_to_terminal(struct terminal_session *session, const char *data,
size_t size) {
if (!session || !session->active) {
return -1;
}
ssize_t bytes_written = write(session->master_pty, data, size);
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("write to terminal failed");
return -1;
}
return bytes_written;
}
int resize_terminal(struct terminal_session *session, uint16_t rows,
uint16_t cols) {
if (!session || !session->active) {
return -1;
}
struct winsize ws = {
.ws_row = rows, .ws_col = cols, .ws_xpixel = 0, .ws_ypixel = 0};
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
perror("ioctl TIOCSWINSZ failed");
return -1;
}
return 0;
} }
+14 -10
View File
@@ -1,25 +1,29 @@
#ifndef TERMINAL_H #ifndef TERMINAL_H
#define TERMINAL_H #define TERMINAL_H
#include <signal.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
struct terminal_session { struct terminal_session {
int master_pty; int master_pty;
int slave_pty; int slave_pty;
pid_t child_pid; pid_t child_pid;
bool active; bool active;
}; };
// Функции для работы с терминалом // Функции для работы с терминалом
int create_terminal_session(struct terminal_session *session, const char *shell); int create_terminal_session(struct terminal_session *session,
const char *shell);
void destroy_terminal_session(struct terminal_session *session); void destroy_terminal_session(struct terminal_session *session);
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size); ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size); size_t size);
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols); ssize_t write_to_terminal(struct terminal_session *session, const char *data,
size_t size);
int resize_terminal(struct terminal_session *session, uint16_t rows,
uint16_t cols);
#endif #endif
+175 -156
View File
@@ -1,176 +1,195 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<title>Web Terminal</title> <title>Web Terminal</title>
<link rel="stylesheet" href="xterm.css"> <link rel="stylesheet" href="xterm.css" />
<style> <style>
body { body {
margin: 0; margin: 0;
padding: 20px; padding: 20px;
background-color: #1e1e1e; background-color: #1e1e1e;
color: #fff; color: #fff;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
}
#terminal-container {
flex: 1;
border: 1px solid #333;
border-radius: 5px;
overflow: hidden;
margin-top: 10px;
}
.status {
padding: 10px;
border-radius: 3px;
font-weight: bold;
margin-bottom: 10px;
}
.connected { background-color: #2d5a2d; }
.disconnected { background-color: #5a2d2d; }
.connecting { background-color: #5a5a2d; }
</style>
</head>
<body>
<h1>Web Terminal</h1>
<div class="status connecting" id="status">Connecting...</div>
<div id="terminal-container"></div>
<script src="xterm.js"></script>
<script src="xterm-addon-fit.js"></script>
<script>
class WebTerminal {
constructor() {
this.terminal = new Terminal({
cursorBlink: true,
theme: {
background: '#000000',
foreground: '#ffffff',
cursor: '#ffffff'
}
});
this.fitAddon = new FitAddon.FitAddon();
this.terminal.loadAddon(this.fitAddon);
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 3000;
this.init();
} }
init() { #terminal-container {
const terminalContainer = document.getElementById('terminal-container'); flex: 1;
this.terminal.open(terminalContainer); border: 1px solid #333;
this.fitAddon.fit(); border-radius: 5px;
overflow: hidden;
this.terminal.writeln('Welcome to Web Terminal'); margin-top: 10px;
this.terminal.writeln('Connecting to server...');
this.connect();
// Handle terminal input
this.terminal.onData((data) => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
}
});
// Handle resize
window.addEventListener('resize', () => {
this.fitAddon.fit();
this.sendResize();
});
// Handle page unload
window.addEventListener('beforeunload', () => {
if (this.ws) {
this.ws.close();
}
});
} }
connect() { .status {
this.updateStatus('Connecting...', 'connecting'); padding: 10px;
border-radius: 3px;
font-weight: bold;
margin-bottom: 10px;
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; .connected {
const wsUrl = `${protocol}//${window.location.host}/ws`; background-color: #2d5a2d;
}
.disconnected {
background-color: #5a2d2d;
}
.connecting {
background-color: #5a5a2d;
}
</style>
</head>
<body>
<h1>Web Terminal</h1>
<div class="status connecting" id="status">Connecting...</div>
<div id="terminal-container"></div>
this.ws = new WebSocket(wsUrl); <script src="xterm.js"></script>
<script src="xterm-addon-fit.js"></script>
<script>
class WebTerminal {
constructor() {
this.terminal = new Terminal({
cursorBlink: true,
theme: {
background: "#000000",
foreground: "#ffffff",
cursor: "#ffffff",
},
});
this.ws.onopen = () => { this.fitAddon = new FitAddon.FitAddon();
console.log('WebSocket connected'); this.terminal.loadAddon(this.fitAddon);
this.ws = null;
this.reconnectAttempts = 0; this.reconnectAttempts = 0;
this.updateStatus('Connected', 'connected'); this.maxReconnectAttempts = 5;
this.terminal.clear(); this.reconnectDelay = 3000;
this.sendResize();
};
this.ws.onmessage = (event) => { this.init();
if (typeof event.data === 'string') { }
this.terminal.write(event.data);
} else {
// Handle binary data
const reader = new FileReader();
reader.onload = () => {
const data = new Uint8Array(reader.result);
this.terminal.write(data);
};
reader.readAsArrayBuffer(event.data);
}
};
this.ws.onclose = (event) => { init() {
console.log('WebSocket disconnected:', event.code, event.reason); const terminalContainer =
this.updateStatus('Disconnected', 'disconnected'); document.getElementById("terminal-container");
this.terminal.open(terminalContainer);
this.fitAddon.fit();
if (this.reconnectAttempts < this.maxReconnectAttempts) { this.terminal.writeln("Welcome to Web Terminal");
this.reconnectAttempts++; this.terminal.writeln("Connecting to server...");
const delay = this.reconnectDelay * this.reconnectAttempts;
this.terminal.write(`\r\nConnection lost. Reconnecting in ${delay/1000}s...\r\n`);
setTimeout(() => { this.connect();
this.connect();
}, delay);
} else {
this.terminal.write('\r\nFailed to reconnect. Please refresh the page.\r\n');
}
};
this.ws.onerror = (error) => { // Handle terminal input
console.error('WebSocket error:', error); this.terminal.onData((data) => {
this.updateStatus('Connection Error', 'disconnected'); if (this.ws && this.ws.readyState === WebSocket.OPEN) {
}; this.ws.send(data);
} }
});
sendResize() { // Handle resize
if (this.ws && this.ws.readyState === WebSocket.OPEN) { window.addEventListener("resize", () => {
const dimensions = this.terminal; this.fitAddon.fit();
const resizeMsg = { this.sendResize();
cols: dimensions.cols, });
rows: dimensions.rows
// Handle page unload
window.addEventListener("beforeunload", () => {
if (this.ws) {
this.ws.close();
}
});
}
connect() {
this.updateStatus("Connecting...", "connecting");
const protocol =
window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/ws`;
this.ws = new WebSocket(wsUrl);
this.ws.onopen = () => {
console.log("WebSocket connected");
this.reconnectAttempts = 0;
this.updateStatus("Connected", "connected");
this.terminal.clear();
this.sendResize();
}; };
this.ws.send(JSON.stringify(resizeMsg));
this.ws.onmessage = (event) => {
if (typeof event.data === "string") {
this.terminal.write(event.data);
} else {
// Handle binary data
const reader = new FileReader();
reader.onload = () => {
const data = new Uint8Array(reader.result);
this.terminal.write(data);
};
reader.readAsArrayBuffer(event.data);
}
};
this.ws.onclose = (event) => {
console.log(
"WebSocket disconnected:",
event.code,
event.reason,
);
this.updateStatus("Disconnected", "disconnected");
if (
this.reconnectAttempts < this.maxReconnectAttempts
) {
this.reconnectAttempts++;
const delay =
this.reconnectDelay * this.reconnectAttempts;
this.terminal.write(
`\r\nConnection lost. Reconnecting in ${delay / 1000}s...\r\n`,
);
setTimeout(() => {
this.connect();
}, delay);
} else {
this.terminal.write(
"\r\nFailed to reconnect. Please refresh the page.\r\n",
);
}
};
this.ws.onerror = (error) => {
console.error("WebSocket error:", error);
this.updateStatus("Connection Error", "disconnected");
};
}
sendResize() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const dimensions = this.terminal;
const resizeMsg = {
cols: dimensions.cols,
rows: dimensions.rows,
};
this.ws.send(JSON.stringify(resizeMsg));
}
}
updateStatus(text, className) {
const status = document.getElementById("status");
status.textContent = text;
status.className = `status ${className}`;
} }
} }
updateStatus(text, className) { // Initialize terminal when page loads
const status = document.getElementById('status'); document.addEventListener("DOMContentLoaded", () => {
status.textContent = text; new WebTerminal();
status.className = `status ${className}`; });
} </script>
} </body>
// Initialize terminal when page loads
document.addEventListener('DOMContentLoaded', () => {
new WebTerminal();
});
</script>
</body>
</html> </html>
+72 -1
View File
@@ -1,2 +1,73 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})())); !(function (e, t) {
"object" == typeof exports && "object" == typeof module
? (module.exports = t())
: "function" == typeof define && define.amd
? define([], t)
: "object" == typeof exports
? (exports.FitAddon = t())
: (e.FitAddon = t());
})(self, () =>
(() => {
"use strict";
var e = {};
return (
(() => {
var t = e;
(Object.defineProperty(t, "__esModule", { value: !0 }),
(t.FitAddon = void 0),
(t.FitAddon = class {
activate(e) {
this._terminal = e;
}
dispose() {}
fit() {
const e = this.proposeDimensions();
if (!e || !this._terminal || isNaN(e.cols) || isNaN(e.rows))
return;
const t = this._terminal._core;
(this._terminal.rows === e.rows &&
this._terminal.cols === e.cols) ||
(t._renderService.clear(),
this._terminal.resize(e.cols, e.rows));
}
proposeDimensions() {
if (!this._terminal) return;
if (
!this._terminal.element ||
!this._terminal.element.parentElement
)
return;
const e = this._terminal._core,
t = e._renderService.dimensions;
if (0 === t.css.cell.width || 0 === t.css.cell.height) return;
const r =
0 === this._terminal.options.scrollback
? 0
: e.viewport.scrollBarWidth,
i = window.getComputedStyle(
this._terminal.element.parentElement,
),
o = parseInt(i.getPropertyValue("height")),
s = Math.max(0, parseInt(i.getPropertyValue("width"))),
n = window.getComputedStyle(this._terminal.element),
l =
o -
(parseInt(n.getPropertyValue("padding-top")) +
parseInt(n.getPropertyValue("padding-bottom"))),
a =
s -
(parseInt(n.getPropertyValue("padding-right")) +
parseInt(n.getPropertyValue("padding-left"))) -
r;
return {
cols: Math.max(2, Math.floor(a / t.css.cell.width)),
rows: Math.max(1, Math.floor(l / t.css.cell.height)),
};
}
}));
})(),
e
);
})(),
);
//# sourceMappingURL=xterm-addon-fit.js.map //# sourceMappingURL=xterm-addon-fit.js.map
+37 -15
View File
@@ -79,7 +79,7 @@
.xterm .composition-view { .xterm .composition-view {
/* TODO: Composition position got messed up somewhere */ /* TODO: Composition position got messed up somewhere */
background: #000; background: #000;
color: #FFF; color: #fff;
display: none; display: none;
position: absolute; position: absolute;
white-space: nowrap; white-space: nowrap;
@@ -166,33 +166,55 @@
opacity: 1 !important; opacity: 1 !important;
} }
.xterm-underline-1 { text-decoration: underline; } .xterm-underline-1 {
.xterm-underline-2 { text-decoration: double underline; } text-decoration: underline;
.xterm-underline-3 { text-decoration: wavy underline; } }
.xterm-underline-4 { text-decoration: dotted underline; } .xterm-underline-2 {
.xterm-underline-5 { text-decoration: dashed underline; } text-decoration: double underline;
}
.xterm-underline-3 {
text-decoration: wavy underline;
}
.xterm-underline-4 {
text-decoration: dotted underline;
}
.xterm-underline-5 {
text-decoration: dashed underline;
}
.xterm-overline { .xterm-overline {
text-decoration: overline; text-decoration: overline;
} }
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; } .xterm-overline.xterm-underline-1 {
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; } text-decoration: overline underline;
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; } }
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; } .xterm-overline.xterm-underline-2 {
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; } text-decoration: overline double underline;
}
.xterm-overline.xterm-underline-3 {
text-decoration: overline wavy underline;
}
.xterm-overline.xterm-underline-4 {
text-decoration: overline dotted underline;
}
.xterm-overline.xterm-underline-5 {
text-decoration: overline dashed underline;
}
.xterm-strikethrough { .xterm-strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
.xterm-screen .xterm-decoration-container .xterm-decoration { .xterm-screen .xterm-decoration-container .xterm-decoration {
z-index: 6; z-index: 6;
position: absolute; position: absolute;
} }
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer { .xterm-screen
z-index: 7; .xterm-decoration-container
.xterm-decoration.xterm-decoration-top-layer {
z-index: 7;
} }
.xterm-decoration-overview-ruler { .xterm-decoration-overview-ruler {
+14697 -1
View File
File diff suppressed because it is too large. Load diff