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
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:
1 parent
193779bd13
commit
36aa1bea2a
10 files changed
+18581
-4161
No files matched your search
+2244
-2570
File diff suppressed because it is too large.
Load diff
+218
-129
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+132
-132
@@ -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
@@ -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
@@ -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>
|
||||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user