commit
d201776e29
|
@ -24,7 +24,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3.4.0
|
||||
uses: actions/checkout@v3.5.2
|
||||
-
|
||||
name: "Calculate required variables"
|
||||
id: variables
|
||||
|
@ -90,7 +90,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3.4.0
|
||||
uses: actions/checkout@v3.5.2
|
||||
-
|
||||
name: "Fix ownership of repository"
|
||||
run: chown -R root .
|
||||
|
@ -133,7 +133,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout code
|
||||
uses: actions/checkout@v3.4.0
|
||||
uses: actions/checkout@v3.5.2
|
||||
-
|
||||
name: Get Binaries built in previous jobs
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
|
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v3.4.0
|
||||
uses: actions/checkout@v3.5.2
|
||||
-
|
||||
name: Spell-Checking
|
||||
uses: codespell-project/actions-codespell@master
|
||||
|
|
|
@ -2,25 +2,44 @@ name: Mark stale issues
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
issue_comment:
|
||||
|
||||
env:
|
||||
stale_label: stale
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
stale_action:
|
||||
if: github.event_name != 'issue_comment'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v7.0.0
|
||||
- uses: actions/stale@v8.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.'
|
||||
stale-issue-label: 'stale'
|
||||
stale-issue-label: $stale_label
|
||||
exempt-issue-labels: 'Fixed in next release, Bug, Bug:Confirmed, Bugfix in progress, documentation needed, internal'
|
||||
exempt-all-issue-assignees: true
|
||||
operations-per-run: 300
|
||||
close-issue-reason: 'not_planned'
|
||||
|
||||
remove_stale: # trigger "stale" removal immediately when stale issues are commented on
|
||||
if: github.event_name == 'issue_comment'
|
||||
permissions:
|
||||
contents: read # for actions/checkout
|
||||
issues: write # to edit issues label
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.4.0
|
||||
- name: Remove 'stale' label
|
||||
run: gh issue edit ${{ github.event.issue.number }} --remove-label $stale_label
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v7.0.0
|
||||
- uses: actions/stale@v8.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Do not automatically mark PR/issue as stale
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
name: Syncing branches
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.4.0
|
||||
uses: actions/checkout@v3.5.2
|
||||
- name: Opening pull request
|
||||
run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal'
|
||||
env:
|
||||
|
|
17
deploy.sh
17
deploy.sh
|
@ -42,11 +42,18 @@ for dir in "${path[@]}"; do
|
|||
ls -1"
|
||||
)"
|
||||
|
||||
# Only try to create the subdir if does not already exist
|
||||
if [[ "${dir_content[*]}" =~ "${dir}" ]]; then
|
||||
echo "Dir: ${old_path}/${dir} already exists"
|
||||
else
|
||||
echo "Creating dir: ${old_path}/${dir}"
|
||||
# Loop over the dir content and check if this exact dir already exists
|
||||
path_exists=0
|
||||
for content in "${dir_content[@]}"; do
|
||||
if [[ "${content}" == "${dir}" ]]; then
|
||||
echo "Dir: ${old_path}/${dir} already exists"
|
||||
path_exists=1
|
||||
fi
|
||||
done
|
||||
|
||||
# If the dir does not exist, create it
|
||||
if [[ "${path_exists}" -eq 0 ]]; then
|
||||
echo "Dir: ${old_path}/${dir} does not exist. Creating it."
|
||||
sftp -b - "${USER}"@"${HOST}" <<< "cd ${old_path}
|
||||
-mkdir ${dir}"
|
||||
fi
|
||||
|
|
|
@ -48,9 +48,9 @@ index 454ce12f..a363925c 100644
|
|||
+ ftl_lua_init(L);
|
||||
+ /***************************************************/
|
||||
+
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
if (script > 0) { /* execute main script (if there is one) */
|
||||
@@ -616,7 +622,9 @@ static int pmain (lua_State *L) {
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,6 @@ set(sources
|
|||
daemon.h
|
||||
datastructure.c
|
||||
datastructure.h
|
||||
dhcp-discover.c
|
||||
dhcp-discover.h
|
||||
dnsmasq_interface.c
|
||||
dnsmasq_interface.h
|
||||
edns0.c
|
||||
|
@ -180,6 +178,7 @@ add_executable(pihole-FTL
|
|||
$<TARGET_OBJECTS:lua>
|
||||
$<TARGET_OBJECTS:tre-regex>
|
||||
$<TARGET_OBJECTS:syscalls>
|
||||
$<TARGET_OBJECTS:tools>
|
||||
)
|
||||
if(STATIC STREQUAL "true")
|
||||
set_target_properties(pihole-FTL PROPERTIES LINK_SEARCH_START_STATIC ON)
|
||||
|
@ -253,3 +252,4 @@ add_subdirectory(lua)
|
|||
add_subdirectory(lua/scripts)
|
||||
add_subdirectory(tre-regex)
|
||||
add_subdirectory(syscalls)
|
||||
add_subdirectory(tools)
|
||||
|
|
67
src/args.c
67
src/args.c
|
@ -32,8 +32,12 @@
|
|||
#include "shmem.h"
|
||||
// LUA dependencies
|
||||
#include "lua/ftl_lua.h"
|
||||
// gravity_parseList()
|
||||
#include "tools/gravity-parseList.h"
|
||||
// run_dhcp_discover()
|
||||
#include "dhcp-discover.h"
|
||||
#include "tools/dhcp-discover.h"
|
||||
// run_arp_scan()
|
||||
#include "tools/arp-scan.h"
|
||||
// defined in dnsmasq.c
|
||||
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);
|
||||
|
||||
|
@ -124,7 +128,15 @@ static const char __attribute__ ((pure)) *cli_color(const char *color)
|
|||
return is_term() ? color : "";
|
||||
}
|
||||
|
||||
static inline bool strEndsWith(const char *input, const char *end){
|
||||
// Go back to beginning of line and erase to end of line if STDOUT is a terminal
|
||||
const char __attribute__ ((pure)) *cli_over(void)
|
||||
{
|
||||
// \x1b[K is the ANSI escape sequence for "erase to end of line"
|
||||
return is_term() ? "\r\x1b[K" : "\r";
|
||||
}
|
||||
|
||||
static inline bool strEndsWith(const char *input, const char *end)
|
||||
{
|
||||
return strcmp(input + strlen(input) - strlen(end), end) == 0;
|
||||
}
|
||||
|
||||
|
@ -163,6 +175,39 @@ void parse_args(int argc, char* argv[])
|
|||
(argc > 1 && strEndsWith(argv[1], ".db")))
|
||||
exit(sqlite3_shell_main(argc, argv));
|
||||
|
||||
// If the first argument is "gravity" (e.g., /usr/bin/pihole-FTL gravity),
|
||||
// we offer some specialized gravity tools
|
||||
if(argc > 1 && strcmp(argv[1], "gravity") == 0)
|
||||
{
|
||||
// pihole-FTL gravity parseList <infile> <outfile> <adlistID>
|
||||
if(argc == 6 && strcmp(argv[2], "parseList") == 0)
|
||||
{
|
||||
// Parse the given list and write the result to the given file
|
||||
exit(gravity_parseList(argv[3], argv[4], argv[5]));
|
||||
}
|
||||
|
||||
printf("Incorrect usage of pihole-FTL gravity subcommand\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// DHCP discovery mode
|
||||
if(argc > 1 && strcmp(argv[1], "dhcp-discover") == 0)
|
||||
{
|
||||
// Enable stdout printing
|
||||
cli_mode = true;
|
||||
exit(run_dhcp_discover());
|
||||
}
|
||||
|
||||
// ARP scanning mode
|
||||
if(argc > 1 && strcmp(argv[1], "arp-scan") == 0)
|
||||
{
|
||||
// Enable stdout printing
|
||||
cli_mode = true;
|
||||
const bool scan_all = argc > 2 && strcmp(argv[2], "-a") == 0;
|
||||
const bool extreme_mode = argc > 2 && strcmp(argv[2], "-x") == 0;
|
||||
exit(run_arp_scan(scan_all, extreme_mode));
|
||||
}
|
||||
|
||||
// start from 1, as argv[0] is the executable name
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
|
@ -415,14 +460,6 @@ void parse_args(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
// Regex test mode
|
||||
if(strcmp(argv[i], "dhcp-discover") == 0)
|
||||
{
|
||||
// Enable stdout printing
|
||||
cli_mode = true;
|
||||
exit(run_dhcp_discover());
|
||||
}
|
||||
|
||||
// List of implemented arguments
|
||||
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "help") == 0 || strcmp(argv[i], "--help") == 0)
|
||||
{
|
||||
|
@ -495,13 +532,19 @@ void parse_args(int argc, char* argv[])
|
|||
|
||||
printf("%sDebugging and special use:%s\n", yellow, normal);
|
||||
printf("\t%sd%s, %sdebug%s Enter debugging mode\n", green, normal, green, normal);
|
||||
printf("\t%stest%s Don't start pihole-FTL but\n", green, normal);
|
||||
printf("\t instead quit immediately\n");
|
||||
printf("\t%stest%s Don't start pihole-FTL but instead\n", green, normal);
|
||||
printf("\t quit immediately\n");
|
||||
printf("\t%s-f%s, %sno-daemon%s Don't go into daemon mode\n\n", green, normal, green, normal);
|
||||
|
||||
printf("%sOther:%s\n", yellow, normal);
|
||||
printf("\t%sdhcp-discover%s Discover DHCP servers in the local\n", green, normal);
|
||||
printf("\t network\n");
|
||||
printf("\t%sarp-scan %s[-a/-x]%s Use ARP to scan local network for\n", green, cyan, normal);
|
||||
printf("\t possible IP conflicts\n");
|
||||
printf("\t Append %s-a%s to force scan on all\n", cyan, normal);
|
||||
printf("\t interfaces\n");
|
||||
printf("\t Append %s-x%s to force scan on all\n", cyan, normal);
|
||||
printf("\t interfaces and scan 10x more often\n");
|
||||
printf("\t%s-h%s, %shelp%s Display this help and exit\n\n", green, normal, green, normal);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ const char *cli_qst(void) __attribute__ ((const));
|
|||
const char *cli_done(void) __attribute__ ((pure));
|
||||
const char *cli_bold(void) __attribute__ ((pure));
|
||||
const char *cli_normal(void) __attribute__ ((pure));
|
||||
const char *cli_over(void) __attribute__ ((pure));
|
||||
|
||||
// defined in dnsmasq_interface.c
|
||||
int check_struct_sizes(void);
|
||||
|
|
|
@ -21,6 +21,47 @@ static const unsigned int capabilityIDs[] = { CAP_CHOWN , CAP_DAC_OVERRIDE ,
|
|||
static const char* capabilityNames[] = {"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID", "CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", "CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP"};
|
||||
static const unsigned int numCaps = sizeof(capabilityIDs) / sizeof(*capabilityIDs);
|
||||
|
||||
bool check_capability(const unsigned int cap)
|
||||
{
|
||||
// First assume header version 1
|
||||
int capsize = 1; // VFS_CAP_U32_1
|
||||
cap_user_data_t data = NULL;
|
||||
cap_user_header_t hdr = calloc(sizeof(*hdr), capsize);
|
||||
|
||||
// Determine capabilities version used by the current kernel
|
||||
capget(hdr, NULL);
|
||||
|
||||
// Check version
|
||||
if (hdr->version != LINUX_CAPABILITY_VERSION_1)
|
||||
{
|
||||
// If unknown version, use largest supported version (3)
|
||||
// Version 2 is deprecated according to linux/capability.h
|
||||
if (hdr->version != LINUX_CAPABILITY_VERSION_2)
|
||||
{
|
||||
hdr->version = LINUX_CAPABILITY_VERSION_3;
|
||||
capsize = 2; // VFS_CAP_U32_3
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use version 2
|
||||
capsize = 2; // VFS_CAP_U32_2
|
||||
}
|
||||
}
|
||||
|
||||
// Get current capabilities
|
||||
data = calloc(sizeof(*data), capsize);
|
||||
capget(hdr, data);
|
||||
|
||||
// Check if the capability is available
|
||||
const bool available = ((data->permitted & (1 << cap)) && (data->effective & (1 << cap)));
|
||||
|
||||
// Free memory
|
||||
free(hdr);
|
||||
free(data);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
bool check_capabilities(void)
|
||||
{
|
||||
// First assume header version 1
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef CAPABILITIES_H
|
||||
#define CAPABILITIES_H
|
||||
|
||||
bool check_capability(const unsigned int cap);
|
||||
bool check_capabilities(void);
|
||||
|
||||
#endif //CAPABILITIES_H
|
||||
|
|
|
@ -837,9 +837,6 @@ static char *parse_FTLconf(FILE *fp, const char *key)
|
|||
// Go to beginning of file
|
||||
fseek(fp, 0L, SEEK_SET);
|
||||
|
||||
if(config.debug & DEBUG_EXTRA)
|
||||
logg("initial: conflinebuffer = %p, keystr = %p, size = %zu", conflinebuffer, keystr, size);
|
||||
|
||||
// Set size to zero if conflinebuffer is not available here
|
||||
// This causes getline() to allocate memory for the buffer itself
|
||||
if(conflinebuffer == NULL && size != 0)
|
||||
|
@ -848,11 +845,6 @@ static char *parse_FTLconf(FILE *fp, const char *key)
|
|||
errno = 0;
|
||||
while(getline(&conflinebuffer, &size, fp) != -1)
|
||||
{
|
||||
if(config.debug & DEBUG_EXTRA)
|
||||
{
|
||||
logg("conflinebuffer = %p, keystr = %p, size = %zu", conflinebuffer, keystr, size);
|
||||
logg(" while reading line \"%s\" looking for \"%s\"", conflinebuffer, keystr);
|
||||
}
|
||||
// Check if memory allocation failed
|
||||
if(conflinebuffer == NULL)
|
||||
break;
|
||||
|
|
1026
src/database/shell.c
1026
src/database/shell.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -146,9 +146,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.41.1"
|
||||
#define SQLITE_VERSION_NUMBER 3041001
|
||||
#define SQLITE_SOURCE_ID "2023-03-10 12:13:52 20399f3eda5ec249d147ba9e48da6e87f969d7966a9a896764ca437ff7e737ff"
|
||||
#define SQLITE_VERSION "3.42.0"
|
||||
#define SQLITE_VERSION_NUMBER 3042000
|
||||
#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -1655,20 +1655,23 @@ SQLITE_API int sqlite3_os_end(void);
|
|||
** must ensure that no other SQLite interfaces are invoked by other
|
||||
** threads while sqlite3_config() is running.</b>
|
||||
**
|
||||
** The sqlite3_config() interface
|
||||
** may only be invoked prior to library initialization using
|
||||
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
|
||||
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
|
||||
** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.
|
||||
** Note, however, that ^sqlite3_config() can be called as part of the
|
||||
** implementation of an application-defined [sqlite3_os_init()].
|
||||
**
|
||||
** The first argument to sqlite3_config() is an integer
|
||||
** [configuration option] that determines
|
||||
** what property of SQLite is to be configured. Subsequent arguments
|
||||
** vary depending on the [configuration option]
|
||||
** in the first argument.
|
||||
**
|
||||
** For most configuration options, the sqlite3_config() interface
|
||||
** may only be invoked prior to library initialization using
|
||||
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
|
||||
** The exceptional configuration options that may be invoked at any time
|
||||
** are called "anytime configuration options".
|
||||
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
|
||||
** [sqlite3_shutdown()] with a first argument that is not an anytime
|
||||
** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE.
|
||||
** Note, however, that ^sqlite3_config() can be called as part of the
|
||||
** implementation of an application-defined [sqlite3_os_init()].
|
||||
**
|
||||
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
|
||||
** ^If the option is unknown or SQLite is unable to set the option
|
||||
** then this routine returns a non-zero [error code].
|
||||
|
@ -1776,6 +1779,23 @@ struct sqlite3_mem_methods {
|
|||
** These constants are the available integer configuration options that
|
||||
** can be passed as the first argument to the [sqlite3_config()] interface.
|
||||
**
|
||||
** Most of the configuration options for sqlite3_config()
|
||||
** will only work if invoked prior to [sqlite3_initialize()] or after
|
||||
** [sqlite3_shutdown()]. The few exceptions to this rule are called
|
||||
** "anytime configuration options".
|
||||
** ^Calling [sqlite3_config()] with a first argument that is not an
|
||||
** anytime configuration option in between calls to [sqlite3_initialize()] and
|
||||
** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
|
||||
**
|
||||
** The set of anytime configuration options can change (by insertions
|
||||
** and/or deletions) from one release of SQLite to the next.
|
||||
** As of SQLite version 3.42.0, the complete set of anytime configuration
|
||||
** options is:
|
||||
** <ul>
|
||||
** <li> SQLITE_CONFIG_LOG
|
||||
** <li> SQLITE_CONFIG_PCACHE_HDRSZ
|
||||
** </ul>
|
||||
**
|
||||
** New configuration options may be added in future releases of SQLite.
|
||||
** Existing configuration options might be discontinued. Applications
|
||||
** should check the return code from [sqlite3_config()] to make sure that
|
||||
|
@ -2122,28 +2142,28 @@ struct sqlite3_mem_methods {
|
|||
** compile-time option is not set, then the default maximum is 1073741824.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
|
||||
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
||||
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
||||
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
||||
#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
||||
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
||||
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
||||
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
||||
#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
|
||||
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
||||
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
||||
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
||||
#define SQLITE_CONFIG_PCACHE 14 /* no-op */
|
||||
#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
|
||||
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
||||
#define SQLITE_CONFIG_URI 17 /* int */
|
||||
#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
|
||||
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
|
||||
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
||||
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
||||
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
||||
#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
||||
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
||||
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
||||
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
||||
#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
|
||||
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
||||
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
||||
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
||||
#define SQLITE_CONFIG_PCACHE 14 /* no-op */
|
||||
#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
|
||||
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
||||
#define SQLITE_CONFIG_URI 17 /* int */
|
||||
#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
|
||||
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
||||
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
||||
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
||||
|
@ -2378,7 +2398,7 @@ struct sqlite3_mem_methods {
|
|||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_DQS_DML]]
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DML</td>
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
|
||||
** the legacy [double-quoted string literal] misfeature for DML statements
|
||||
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
|
||||
|
@ -2387,7 +2407,7 @@ struct sqlite3_mem_methods {
|
|||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_DQS_DDL]]
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
|
||||
** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
|
||||
** the legacy [double-quoted string literal] misfeature for DDL statements,
|
||||
** such as CREATE TABLE and CREATE INDEX. The
|
||||
|
@ -2396,7 +2416,7 @@ struct sqlite3_mem_methods {
|
|||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
|
||||
** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td>
|
||||
** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
|
||||
** assume that database schemas are untainted by malicious content.
|
||||
** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
|
||||
|
@ -2416,7 +2436,7 @@ struct sqlite3_mem_methods {
|
|||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
|
||||
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td>
|
||||
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
||||
** the legacy file format flag. When activated, this flag causes all newly
|
||||
** created database file to have a schema format version number (the 4-byte
|
||||
|
@ -2425,7 +2445,7 @@ struct sqlite3_mem_methods {
|
|||
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
||||
** newly created databases are generally not understandable by SQLite versions
|
||||
** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there
|
||||
** is now scarcely any need to generated database files that are compatible
|
||||
** is now scarcely any need to generate database files that are compatible
|
||||
** all the way back to version 3.0.0, and so this setting is of little
|
||||
** practical use, but is provided so that SQLite can continue to claim the
|
||||
** ability to generate new database files that are compatible with version
|
||||
|
@ -2436,6 +2456,38 @@ struct sqlite3_mem_methods {
|
|||
** not considered a bug since SQLite versions 3.3.0 and earlier do not support
|
||||
** either generated columns or decending indexes.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
|
||||
** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
|
||||
** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
|
||||
** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
|
||||
** statistics. For statistics to be collected, the flag must be set on
|
||||
** the database handle both when the SQL statement is prepared and when it
|
||||
** is stepped. The flag is set (collection of statistics is enabled)
|
||||
** by default. This option takes two arguments: an integer and a pointer to
|
||||
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the statement scanstatus option. If the second argument
|
||||
** is not NULL, then the value of the statement scanstatus setting after
|
||||
** processing the first argument is written into the integer that the second
|
||||
** argument points to.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]]
|
||||
** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order
|
||||
** in which tables and indexes are scanned so that the scans start at the end
|
||||
** and work toward the beginning rather than starting at the beginning and
|
||||
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
|
||||
** same as setting [PRAGMA reverse_unordered_selects]. This option takes
|
||||
** two arguments which are an integer and a pointer to an integer. The first
|
||||
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
|
||||
** reverse scan order flag, respectively. If the second argument is not NULL,
|
||||
** then 0 or 1 is written into the integer that the second argument points to
|
||||
** depending on if the reverse scan order flag is set after processing the
|
||||
** first argument.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
|
@ -2456,7 +2508,9 @@ struct sqlite3_mem_methods {
|
|||
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
|
||||
#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
|
||||
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */
|
||||
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
|
||||
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||
|
@ -6201,6 +6255,13 @@ SQLITE_API void sqlite3_activate_cerod(
|
|||
** of the default VFS is not implemented correctly, or not implemented at
|
||||
** all, then the behavior of sqlite3_sleep() may deviate from the description
|
||||
** in the previous paragraphs.
|
||||
**
|
||||
** If a negative argument is passed to sqlite3_sleep() the results vary by
|
||||
** VFS and operating system. Some system treat a negative argument as an
|
||||
** instruction to sleep forever. Others understand it to mean do not sleep
|
||||
** at all. ^In SQLite version 3.42.0 and later, a negative
|
||||
** argument passed into sqlite3_sleep() is changed to zero before it is relayed
|
||||
** down into the xSleep method of the VFS.
|
||||
*/
|
||||
SQLITE_API int sqlite3_sleep(int);
|
||||
|
||||
|
@ -7828,9 +7889,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|||
** is undefined if the mutex is not currently entered by the
|
||||
** calling thread or is not currently allocated.
|
||||
**
|
||||
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
|
||||
** sqlite3_mutex_leave() is a NULL pointer, then all three routines
|
||||
** behave as no-ops.
|
||||
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
|
||||
** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer,
|
||||
** then any of the four routines behaves as a no-op.
|
||||
**
|
||||
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
|
||||
*/
|
||||
|
@ -9564,18 +9625,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
|
|||
** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
|
||||
** <dd>Calls of the form
|
||||
** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
|
||||
** the [xConnect] or [xCreate] methods of a [virtual table] implmentation
|
||||
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
|
||||
** identify that virtual table as being safe to use from within triggers
|
||||
** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
|
||||
** virtual table can do no serious harm even if it is controlled by a
|
||||
** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
|
||||
** flag unless absolutely necessary.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
|
||||
** <dd>Calls of the form
|
||||
** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
|
||||
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
|
||||
** instruct the query planner to begin at least a read transaction on
|
||||
** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
|
||||
** virtual table is used.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
|
||||
#define SQLITE_VTAB_INNOCUOUS 2
|
||||
#define SQLITE_VTAB_DIRECTONLY 3
|
||||
#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine The Virtual Table Conflict Policy
|
||||
|
@ -10750,16 +10821,20 @@ SQLITE_API int sqlite3session_create(
|
|||
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
||||
|
||||
/*
|
||||
** CAPIREF: Conigure a Session Object
|
||||
** CAPI3REF: Configure a Session Object
|
||||
** METHOD: sqlite3_session
|
||||
**
|
||||
** This method is used to configure a session object after it has been
|
||||
** created. At present the only valid value for the second parameter is
|
||||
** [SQLITE_SESSION_OBJCONFIG_SIZE].
|
||||
** created. At present the only valid values for the second parameter are
|
||||
** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID].
|
||||
**
|
||||
** Arguments for sqlite3session_object_config()
|
||||
*/
|
||||
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Options for sqlite3session_object_config
|
||||
**
|
||||
** The following values may passed as the the 4th parameter to
|
||||
** The following values may passed as the the 2nd parameter to
|
||||
** sqlite3session_object_config().
|
||||
**
|
||||
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
|
||||
|
@ -10775,12 +10850,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
|||
**
|
||||
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
||||
** the first table has been attached to the session object.
|
||||
**
|
||||
** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd>
|
||||
** This option is used to set, clear or query the flag that enables
|
||||
** collection of data for tables with no explicit PRIMARY KEY.
|
||||
**
|
||||
** Normally, tables with no explicit PRIMARY KEY are simply ignored
|
||||
** by the sessions module. However, if this flag is set, it behaves
|
||||
** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted
|
||||
** as their leftmost columns.
|
||||
**
|
||||
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
||||
** the first table has been attached to the session object.
|
||||
*/
|
||||
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
||||
|
||||
/*
|
||||
*/
|
||||
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
||||
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
||||
#define SQLITE_SESSION_OBJCONFIG_ROWID 2
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable A Session Object
|
||||
|
@ -11913,9 +11997,23 @@ SQLITE_API int sqlite3changeset_apply_v2(
|
|||
** Invert the changeset before applying it. This is equivalent to inverting
|
||||
** a changeset using sqlite3changeset_invert() before applying it. It is
|
||||
** an error to specify this flag with a patchset.
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd>
|
||||
** Do not invoke the conflict handler callback for any changes that
|
||||
** would not actually modify the database even if they were applied.
|
||||
** Specifically, this means that the conflict handler is not invoked
|
||||
** for:
|
||||
** <ul>
|
||||
** <li>a delete change if the row being deleted cannot be found,
|
||||
** <li>an update change if the modified fields are already set to
|
||||
** their new values in the conflicting row, or
|
||||
** <li>an insert change if all fields of the conflicting row match
|
||||
** the row being inserted.
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
||||
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
||||
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||
|
|
|
@ -723,6 +723,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
/* Get extended RCODE. */
|
||||
rcode |= sizep[2] << 4;
|
||||
|
||||
// Pi-hole modification: Interpret the pseudoheader before
|
||||
// it might get stripped off below (added_pheader == true)
|
||||
FTL_parse_pseudoheaders(pheader, (size_t)plen);
|
||||
|
||||
if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
|
||||
|
@ -783,7 +787,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||
a.log.rcode = rcode;
|
||||
a.log.ede = ede;
|
||||
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL, 0);
|
||||
|
||||
|
||||
return resize_packet(header, n, pheader, plen);
|
||||
}
|
||||
|
||||
|
@ -1166,6 +1170,8 @@ void reply_query(int fd, time_t now)
|
|||
|
||||
server = daemon->serverarray[c];
|
||||
|
||||
FTL_header_analysis(header->hb4, RCODE(header), server, daemon->log_display_id);
|
||||
|
||||
if (RCODE(header) != REFUSED)
|
||||
daemon->serverarray[first]->last_server = c;
|
||||
else if (daemon->serverarray[first]->last_server == c)
|
||||
|
@ -1750,9 +1756,8 @@ void receive_query(struct listener *listen, time_t now)
|
|||
have_mark = get_incoming_mark(&source_addr, &dst_addr, /* istcp: */ 0, &mark);
|
||||
#endif
|
||||
//********************** Pi-hole modification **********************//
|
||||
ednsData edns = { 0 };
|
||||
if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
|
||||
FTL_parse_pseudoheaders(header, n, &source_addr, &edns);
|
||||
pheader = find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL);
|
||||
FTL_parse_pseudoheaders(pheader, (size_t)n);
|
||||
//******************************************************************//
|
||||
|
||||
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
|
||||
|
@ -1763,7 +1768,7 @@ void receive_query(struct listener *listen, time_t now)
|
|||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&source_addr, auth_dns ? "auth" : "query", type);
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_FORWARD , daemon->namebuff,
|
||||
&source_addr, auth_dns ? "auth" : "query", type, daemon->log_display_id, &edns, UDP);
|
||||
&source_addr, auth_dns ? "auth" : "query", type, daemon->log_display_id, UDP);
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
is_single_query = 1;
|
||||
|
@ -2086,6 +2091,8 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
|||
unsigned char *packet = NULL;
|
||||
struct dns_header *new_header = NULL;
|
||||
|
||||
FTL_header_analysis(header->hb4, RCODE(header), server, daemon->log_display_id);
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t m;
|
||||
|
@ -2298,9 +2305,9 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||
no_cache_dnssec = 1;
|
||||
|
||||
//********************** Pi-hole modification **********************//
|
||||
ednsData edns = { 0 };
|
||||
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
|
||||
FTL_parse_pseudoheaders(header, size, &peer_addr, &edns);
|
||||
unsigned char *pheader = NULL;
|
||||
pheader = find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL);
|
||||
FTL_parse_pseudoheaders(pheader, (size_t)size);
|
||||
//******************************************************************//
|
||||
|
||||
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
||||
|
@ -2320,7 +2327,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||
&peer_addr, auth_dns ? "auth" : "query", qtype);
|
||||
|
||||
piholeblocked = FTL_new_query(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, auth_dns ? "auth" : "query", qtype, daemon->log_display_id, &edns, TCP);
|
||||
&peer_addr, auth_dns ? "auth" : "query", qtype, daemon->log_display_id, TCP);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* find queries for zones we're authoritative for, and answer them directly */
|
||||
|
|
|
@ -62,7 +62,7 @@ static void _query_set_reply(const unsigned int flags, const enum reply_type rep
|
|||
static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const char* file, const int line);
|
||||
static unsigned long converttimeval(const struct timeval time) __attribute__((const));
|
||||
static enum query_status detect_blocked_IP(const unsigned short flags, const union all_addr *addr, const queriesData *query, const domainsData *domain);
|
||||
static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const unsigned char new_status);
|
||||
static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const enum query_status new_status);
|
||||
static void FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, unsigned short port, const int id, const char* file, const int line);
|
||||
static void FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const char* arg, const int id, const char* file, const int line);
|
||||
static void FTL_upstream_error(const union all_addr *addr, const unsigned int flags, const int id, const char* file, const int line);
|
||||
|
@ -76,6 +76,7 @@ static char *get_ptrname(struct in_addr *addr);
|
|||
static const char *check_dnsmasq_name(const char *name);
|
||||
|
||||
// Static blocking metadata
|
||||
static bool adbit = false;
|
||||
static const char *blockingreason = "";
|
||||
static enum reply_type force_next_DNS_reply = REPLY_UNKNOWN;
|
||||
static int last_regex_idx = -1;
|
||||
|
@ -128,8 +129,6 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *
|
|||
if(!config.show_dnssec)
|
||||
return;
|
||||
|
||||
const ednsData edns = { 0 };
|
||||
|
||||
// Type is overloaded with port since 2d65d55, so we have to
|
||||
// derive the real query type from the arg string
|
||||
unsigned short qtype = type;
|
||||
|
@ -158,7 +157,7 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *
|
|||
arg = (char*)"dnssec-unknown";
|
||||
}
|
||||
|
||||
_FTL_new_query(flags, name, NULL, arg, qtype, id, &edns, INTERNAL, file, line);
|
||||
_FTL_new_query(flags, name, NULL, arg, qtype, id, INTERNAL, file, line);
|
||||
// forwarded upstream (type is used to store the upstream port)
|
||||
FTL_forwarded(flags, name, addr, type, id, path, line);
|
||||
}
|
||||
|
@ -464,7 +463,7 @@ static bool is_pihole_domain(const char *domain)
|
|||
bool _FTL_new_query(const unsigned int flags, const char *name,
|
||||
union mysockaddr *addr, char *arg,
|
||||
const unsigned short qtype, const int id,
|
||||
const ednsData *edns, const enum protocol proto,
|
||||
const enum protocol proto,
|
||||
const char* file, const int line)
|
||||
{
|
||||
// Create new query in data structure
|
||||
|
@ -596,6 +595,7 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
|
|||
in_port_t clientPort = daemon->port;
|
||||
bool internal_query = false;
|
||||
char clientIP[ADDRSTRLEN+1] = { 0 };
|
||||
ednsData *edns = getEDNS();
|
||||
if(config.edns0_ecs && edns && edns->client_set)
|
||||
{
|
||||
// Use ECS provided client
|
||||
|
@ -743,10 +743,6 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
|
|||
counters->reply[REPLY_UNKNOWN]++;
|
||||
// Store DNSSEC result for this domain
|
||||
query->dnssec = DNSSEC_UNSPECIFIED;
|
||||
// Every domain is insecure in the beginning. It can get secure or bogus
|
||||
// only if validation reveals this. If DNSSEC validation is not used, the
|
||||
// original status (DNSSEC_UNSPECIFIED) is not changed.
|
||||
query_set_dnssec(query, DNSSEC_INSECURE);
|
||||
query->CNAME_domainID = -1;
|
||||
// This query is not yet known ad forwarded or blocked
|
||||
query->flags.blocked = false;
|
||||
|
@ -1881,6 +1877,8 @@ static void update_upstream(queriesData *query, const int id)
|
|||
id, oldaddr, oldport, ip, port);
|
||||
}
|
||||
}
|
||||
|
||||
// Update upstream server ID
|
||||
query->upstreamID = upstreamID;
|
||||
}
|
||||
}
|
||||
|
@ -2017,11 +2015,22 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
|||
if(config.debug & DEBUG_QUERIES)
|
||||
logg(" EDE: %s (%d)", edestr(addr->log.ede), addr->log.ede);
|
||||
}
|
||||
ednsData *edns = getEDNS();
|
||||
if(edns != NULL && edns->ede != EDE_UNSET)
|
||||
{
|
||||
query->ede = edns->ede;
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
logg(" EDE: %s (%d)", edestr(edns->ede), edns->ede);
|
||||
}
|
||||
|
||||
// Update upstream server (if applicable)
|
||||
if(!cached)
|
||||
update_upstream(query, id);
|
||||
|
||||
// Reset last_server to avoid possibly changing the upstream server
|
||||
// again in the next query
|
||||
memset(&last_server, 0, sizeof(last_server));
|
||||
|
||||
// Save response time
|
||||
// Skipped internally if already computed
|
||||
set_response_time(query, response);
|
||||
|
@ -2102,6 +2111,10 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
|||
// Save reply type and update individual reply counters
|
||||
query_set_reply(flags, 0, addr, query, response);
|
||||
|
||||
// Set DNSSEC status to INSECURE if it is still unknown
|
||||
if(query->dnssec == DNSSEC_UNSPECIFIED)
|
||||
query_set_dnssec(query, DNSSEC_INSECURE);
|
||||
|
||||
// Hereby, this query is now fully determined
|
||||
query->flags.complete = true;
|
||||
}
|
||||
|
@ -2141,6 +2154,12 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
|||
reply_flags = F_NEG;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set DNSSEC status to INSECURE if it is still unknown
|
||||
if(query->dnssec == DNSSEC_UNSPECIFIED)
|
||||
query_set_dnssec(query, DNSSEC_INSECURE);
|
||||
}
|
||||
|
||||
// Save reply type and update individual reply counters
|
||||
query_set_reply(reply_flags, 0, addr, query, response);
|
||||
|
@ -2171,6 +2190,10 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
|||
// name = google-public-dns-a.google.com
|
||||
// Hence, isExactMatch is always false
|
||||
|
||||
// Set DNSSEC status to INSECURE if it is still unknown
|
||||
if(query->dnssec == DNSSEC_UNSPECIFIED)
|
||||
query_set_dnssec(query, DNSSEC_INSECURE);
|
||||
|
||||
// Save reply type and update individual reply counters
|
||||
query_set_reply(flags, 0, addr, query, response);
|
||||
}
|
||||
|
@ -2183,6 +2206,13 @@ static void FTL_reply(const unsigned int flags, const char *name, const union al
|
|||
logg("***** Unknown upstream REPLY");
|
||||
}
|
||||
|
||||
if(query && option_bool(OPT_DNSSEC_PROXY))
|
||||
{
|
||||
// DNSSEC proxy mode is enabled. Interpret AD flag
|
||||
// and set DNSSEC status accordingly
|
||||
query_set_dnssec(query, adbit ? DNSSEC_SECURE : DNSSEC_INSECURE);
|
||||
}
|
||||
|
||||
unlock_shm();
|
||||
}
|
||||
|
||||
|
@ -2439,6 +2469,9 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl
|
|||
break;
|
||||
}
|
||||
|
||||
// Get EDNS data (if available)
|
||||
ednsData *edns = getEDNS();
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_QUERIES)
|
||||
{
|
||||
|
@ -2480,12 +2513,32 @@ static void FTL_upstream_error(const union all_addr *addr, const unsigned int fl
|
|||
}
|
||||
|
||||
if(addr->log.ede != EDE_UNSET) // This function is only called if (flags & F_RCODE)
|
||||
{
|
||||
query->ede = addr->log.ede;
|
||||
logg(" EDE: %s (%d)", edestr(addr->log.ede), addr->log.ede);
|
||||
}
|
||||
}
|
||||
|
||||
if(edns != NULL && edns->ede != EDE_UNSET)
|
||||
{
|
||||
query->ede = edns->ede;
|
||||
logg(" EDE: %s (%d)", edestr(edns->ede), edns->ede);
|
||||
}
|
||||
}
|
||||
// Check EDNS EDE for DNSSEC status in DNSSEC proxy mode
|
||||
if(option_bool(OPT_DNSSEC_PROXY) &&
|
||||
edns && edns->ede >= EDE_DNSSEC_BOGUS && edns->ede <= EDE_NO_NSEC)
|
||||
{
|
||||
// DNSSEC proxy mode is enabled and we received a valid DNSSEC
|
||||
// status from the upstream server through ENDS EDE. We need to
|
||||
// update the DNSSEC status of the corresponding query.
|
||||
query_set_dnssec(query, DNSSEC_BOGUS);
|
||||
}
|
||||
// Set query reply
|
||||
query_set_reply(0, reply, addr, query, response);
|
||||
|
||||
// Reset last_server
|
||||
memset(&last_server, 0, sizeof(last_server));
|
||||
|
||||
// Unlock shared memory
|
||||
unlock_shm();
|
||||
}
|
||||
|
@ -2562,12 +2615,20 @@ void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode,
|
|||
// RA bit is not set and rcode is NXDOMAIN
|
||||
FTL_mark_externally_blocked(id, file, line);
|
||||
|
||||
// Check if AD bit is set in DNS header
|
||||
adbit = header4 & HB4_AD;
|
||||
|
||||
// Store server which sent this reply
|
||||
if(server)
|
||||
{
|
||||
memcpy(&last_server, &server->addr, sizeof(last_server));
|
||||
if(config.debug & DEBUG_EXTRA)
|
||||
logg("Got forward address: YES");
|
||||
{
|
||||
char ip[ADDRSTRLEN+1] = { 0 };
|
||||
in_port_t port = 0;
|
||||
mysockaddr_extract_ip_port(&last_server, ip, &port);
|
||||
logg("Got forward address: %s#%u (%s:%i)", ip, port, short_path(file), line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3301,7 +3362,7 @@ const char *get_edestr(const int ede)
|
|||
static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnssec, const char *file, const int line)
|
||||
{
|
||||
// Return early if DNSSEC validation is disabled
|
||||
if(!option_bool(OPT_DNSSEC_VALID))
|
||||
if(!option_bool(OPT_DNSSEC_VALID) && !option_bool(OPT_DNSSEC_PROXY))
|
||||
return;
|
||||
|
||||
if(config.debug & DEBUG_DNSSEC)
|
||||
|
@ -3345,7 +3406,7 @@ int check_struct_sizes(void)
|
|||
result += check_one_struct("clientsData", sizeof(clientsData), 672, 648);
|
||||
result += check_one_struct("domainsData", sizeof(domainsData), 24, 20);
|
||||
result += check_one_struct("DNSCacheData", sizeof(DNSCacheData), 16, 16);
|
||||
result += check_one_struct("ednsData", sizeof(ednsData), 72, 72);
|
||||
result += check_one_struct("ednsData", sizeof(ednsData), 76, 76);
|
||||
result += check_one_struct("overTimeData", sizeof(overTimeData), 32, 24);
|
||||
result += check_one_struct("regexData", sizeof(regexData), 64, 48);
|
||||
result += check_one_struct("SharedMemory", sizeof(SharedMemory), 24, 12);
|
||||
|
|
|
@ -23,8 +23,8 @@ void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *
|
|||
#define FTL_iface(iface, addr, addrfamily) _FTL_iface(iface, addr, addrfamily, __FILE__, __LINE__)
|
||||
void _FTL_iface(struct irec *recviface, const union all_addr *addr, const sa_family_t addrfamily, const char* file, const int line);
|
||||
|
||||
#define FTL_new_query(flags, name, addr, arg, qtype, id, edns, proto) _FTL_new_query(flags, name, addr, arg, qtype, id, edns, proto, __FILE__, __LINE__)
|
||||
bool _FTL_new_query(const unsigned int flags, const char *name, union mysockaddr *addr, char *arg, const unsigned short qtype, const int id, const ednsData *edns, enum protocol proto, const char* file, const int line);
|
||||
#define FTL_new_query(flags, name, addr, arg, qtype, id, proto) _FTL_new_query(flags, name, addr, arg, qtype, id, proto, __FILE__, __LINE__)
|
||||
bool _FTL_new_query(const unsigned int flags, const char *name, union mysockaddr *addr, char *arg, const unsigned short qtype, const int id, enum protocol proto, const char* file, const int line);
|
||||
|
||||
#define FTL_header_analysis(header4, rcode, server, id) _FTL_header_analysis(header4, rcode, server, id, __FILE__, __LINE__)
|
||||
void _FTL_header_analysis(const unsigned char header4, const unsigned int rcode, const struct server *server, const int id, const char* file, const int line);
|
||||
|
|
108
src/edns0.c
108
src/edns0.c
|
@ -41,15 +41,31 @@
|
|||
// dnsmasq option: --add-cpe-id=...
|
||||
#define EDNS0_CPE_ID EDNS0_OPTION_NOMCPEID
|
||||
|
||||
void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns)
|
||||
{
|
||||
int is_sign;
|
||||
size_t plen;
|
||||
unsigned char *pheader, *sizep;
|
||||
static ednsData edns = { 0 };
|
||||
|
||||
// Extract additional record A.K.A. pseudoheader
|
||||
if (!(pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
|
||||
ednsData *getEDNS(void)
|
||||
{
|
||||
if(edns.valid)
|
||||
{
|
||||
// Return pointer to ednsData structure and reset it for the
|
||||
// next query
|
||||
edns.valid = false;
|
||||
return &edns;
|
||||
}
|
||||
|
||||
// No valid EDNS data available
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen)
|
||||
{
|
||||
// Return early if we have no pseudoheader (a.k.a. additional records)
|
||||
if (!pheader)
|
||||
{
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0) pheader is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
|
@ -152,6 +168,11 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
if(edns0_version != 0x00)
|
||||
return;
|
||||
|
||||
// Reset EDNS(0) data
|
||||
memset(&edns, 0, sizeof(ednsData));
|
||||
edns.ede = EDE_UNSET;
|
||||
edns.valid = true;
|
||||
|
||||
size_t offset; // The header is 11 bytes before the beginning of OPTION-DATA
|
||||
while ((offset = (p - pheader - 11u)) < rdlen && rdlen < UINT16_MAX)
|
||||
{
|
||||
|
@ -225,8 +246,8 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
}
|
||||
|
||||
// Copy data to edns struct
|
||||
strncpy(edns->client, ipaddr, ADDRSTRLEN);
|
||||
edns->client[ADDRSTRLEN-1] = '\0';
|
||||
strncpy(edns.client, ipaddr, ADDRSTRLEN);
|
||||
edns.client[ADDRSTRLEN-1] = '\0';
|
||||
|
||||
// Only set the address as useful when it is not the
|
||||
// loopback address of the distant machine (127.0.0.0/8 or ::1)
|
||||
|
@ -239,7 +260,7 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
}
|
||||
else
|
||||
{
|
||||
edns->client_set = true;
|
||||
edns.client_set = true;
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0) CLIENT SUBNET: %s/%u - OK (IPv%u)",
|
||||
ipaddr, source_netmask, family == 1 ? 4 : 6);
|
||||
|
@ -292,11 +313,11 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
else if(code == EDNS0_MAC_ADDR_BYTE && optlen == 6)
|
||||
{
|
||||
// EDNS(0) MAC address (BYTE format)
|
||||
memcpy(edns->mac_byte, p, sizeof(edns->mac_byte));
|
||||
print_mac(edns->mac_text, (unsigned char*)edns->mac_byte, sizeof(edns->mac_byte));
|
||||
edns->mac_set = true;
|
||||
memcpy(edns.mac_byte, p, sizeof(edns.mac_byte));
|
||||
print_mac(edns.mac_text, (unsigned char*)edns.mac_byte, sizeof(edns.mac_byte));
|
||||
edns.mac_set = true;
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0) MAC address (BYTE format): %s", edns->mac_text);
|
||||
logg("EDNS(0) MAC address (BYTE format): %s", edns.mac_text);
|
||||
|
||||
// Advance working pointer
|
||||
p += 6;
|
||||
|
@ -304,19 +325,19 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
else if(code == EDNS0_MAC_ADDR_TEXT && optlen == 17)
|
||||
{
|
||||
// EDNS(0) MAC address (TEXT format)
|
||||
memcpy(edns->mac_text, p, 17);
|
||||
edns->mac_text[17] = '\0';
|
||||
if(sscanf(edns->mac_text, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&edns->mac_byte[0],
|
||||
&edns->mac_byte[1],
|
||||
&edns->mac_byte[2],
|
||||
&edns->mac_byte[3],
|
||||
&edns->mac_byte[4],
|
||||
&edns->mac_byte[5]) == 6)
|
||||
memcpy(edns.mac_text, p, 17);
|
||||
edns.mac_text[17] = '\0';
|
||||
if(sscanf(edns.mac_text, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
(unsigned char*)&edns.mac_byte[0],
|
||||
(unsigned char*)&edns.mac_byte[1],
|
||||
(unsigned char*)&edns.mac_byte[2],
|
||||
(unsigned char*)&edns.mac_byte[3],
|
||||
(unsigned char*)&edns.mac_byte[4],
|
||||
(unsigned char*)&edns.mac_byte[5]) == 6)
|
||||
{
|
||||
edns->mac_set = true;
|
||||
edns.mac_set = true;
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0) MAC address (TEXT format): %s", edns->mac_text);
|
||||
logg("EDNS(0) MAC address (TEXT format): %s", edns.mac_text);
|
||||
}
|
||||
else if(config.debug & DEBUG_EDNS0)
|
||||
{
|
||||
|
@ -355,10 +376,45 @@ void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockad
|
|||
// Advance working pointer
|
||||
p += optlen;
|
||||
}
|
||||
else if(code == EDNS0_OPTION_EDE && optlen >= 2)
|
||||
{
|
||||
// EDNS(0) EDE
|
||||
// https://datatracker.ietf.org/doc/rfc8914/
|
||||
//
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// 0: | OPTION-CODE |
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// 2: | OPTION-LENGTH |
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// 4: | INFO-CODE |
|
||||
edns.ede = ntohs(((int)p[1] << 8) | p[0]);
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// 6: / EXTRA-TEXT ... /
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
//
|
||||
// The INFO-CODE from the EDE EDNS option is used to
|
||||
// serve as an index into the "Extended DNS Error" IANA
|
||||
// registry, the initial values for which are defined in
|
||||
// this document. The value of the INFO-CODE is encoded
|
||||
// as a two-octet unsigned integer in network byte
|
||||
// order.
|
||||
//
|
||||
// The EXTRA-TEXT from the EDE EDNS option is ignored by
|
||||
// FTL
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0) EDE: %s (code %d)", edestr(edns.ede), edns.ede);
|
||||
|
||||
// Advance working pointer
|
||||
p += optlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(config.debug & DEBUG_EDNS0)
|
||||
logg("EDNS(0):n option %u with length %u", code, optlen);
|
||||
logg("EDNS(0): option %u with length %u", code, optlen);
|
||||
// Not implemented, skip this record
|
||||
|
||||
// Advance working pointer
|
||||
|
|
|
@ -11,13 +11,16 @@
|
|||
#define EDNS0_HEADER
|
||||
|
||||
typedef struct {
|
||||
bool client_set;
|
||||
bool mac_set;
|
||||
bool client_set :1;
|
||||
bool mac_set :1;
|
||||
bool valid :1;
|
||||
char client[ADDRSTRLEN];
|
||||
char mac_byte[6];
|
||||
char mac_text[18];
|
||||
int ede;
|
||||
} ednsData;
|
||||
|
||||
void FTL_parse_pseudoheaders(struct dns_header *header, size_t n, union mysockaddr *peer, ednsData *edns);
|
||||
ednsData *getEDNS(void);
|
||||
void FTL_parse_pseudoheaders(unsigned char *pheader, const size_t plen);
|
||||
|
||||
#endif // EDNS0_HEADER
|
||||
|
|
4
src/gc.c
4
src/gc.c
|
@ -86,9 +86,9 @@ static int check_space(const char *file, int LastUsage)
|
|||
// exceeds the configured threshold and current usage is higher than
|
||||
// usage in the last run (to prevent log spam)
|
||||
perc = get_filepath_usage(file, buffer);
|
||||
if(perc > config.check.disk && perc > LastUsage )
|
||||
if(perc > config.check.disk && perc > LastUsage && perc <= 100.0)
|
||||
log_resource_shortage(-1.0, 0, -1, perc, file, buffer);
|
||||
|
||||
|
||||
return perc;
|
||||
}
|
||||
|
||||
|
|
249
src/lua/lapi.c
249
src/lua/lapi.c
|
@ -60,27 +60,28 @@ const char lua_ident[] =
|
|||
static TValue *index2value (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
StkId o = ci->func + idx;
|
||||
api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index");
|
||||
if (o >= L->top) return &G(L)->nilvalue;
|
||||
StkId o = ci->func.p + idx;
|
||||
api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
|
||||
if (o >= L->top.p) return &G(L)->nilvalue;
|
||||
else return s2v(o);
|
||||
}
|
||||
else if (!ispseudo(idx)) { /* negative index */
|
||||
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
|
||||
return s2v(L->top + idx);
|
||||
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
||||
"invalid index");
|
||||
return s2v(L->top.p + idx);
|
||||
}
|
||||
else if (idx == LUA_REGISTRYINDEX)
|
||||
return &G(L)->l_registry;
|
||||
else { /* upvalues */
|
||||
idx = LUA_REGISTRYINDEX - idx;
|
||||
api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
|
||||
if (ttisCclosure(s2v(ci->func))) { /* C closure? */
|
||||
CClosure *func = clCvalue(s2v(ci->func));
|
||||
if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
|
||||
CClosure *func = clCvalue(s2v(ci->func.p));
|
||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
|
||||
: &G(L)->nilvalue;
|
||||
}
|
||||
else { /* light C function or Lua function (through a hook)?) */
|
||||
api_check(L, ttislcf(s2v(ci->func)), "caller not a C function");
|
||||
api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
|
||||
return &G(L)->nilvalue; /* no upvalues */
|
||||
}
|
||||
}
|
||||
|
@ -94,14 +95,15 @@ static TValue *index2value (lua_State *L, int idx) {
|
|||
l_sinline StkId index2stack (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
StkId o = ci->func + idx;
|
||||
api_check(L, o < L->top, "invalid index");
|
||||
StkId o = ci->func.p + idx;
|
||||
api_check(L, o < L->top.p, "invalid index");
|
||||
return o;
|
||||
}
|
||||
else { /* non-positive index */
|
||||
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
|
||||
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
||||
"invalid index");
|
||||
api_check(L, !ispseudo(idx), "invalid index");
|
||||
return L->top + idx;
|
||||
return L->top.p + idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,17 +114,12 @@ LUA_API int lua_checkstack (lua_State *L, int n) {
|
|||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
api_check(L, n >= 0, "negative 'n'");
|
||||
if (L->stack_last - L->top > n) /* stack large enough? */
|
||||
if (L->stack_last.p - L->top.p > n) /* stack large enough? */
|
||||
res = 1; /* yes; check is OK */
|
||||
else { /* no; need to grow stack */
|
||||
int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
|
||||
if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */
|
||||
res = 0; /* no */
|
||||
else /* try to grow stack */
|
||||
res = luaD_growstack(L, n, 0);
|
||||
}
|
||||
if (res && ci->top < L->top + n)
|
||||
ci->top = L->top + n; /* adjust frame top */
|
||||
else /* need to grow stack */
|
||||
res = luaD_growstack(L, n, 0);
|
||||
if (res && ci->top.p < L->top.p + n)
|
||||
ci->top.p = L->top.p + n; /* adjust frame top */
|
||||
lua_unlock(L);
|
||||
return res;
|
||||
}
|
||||
|
@ -134,11 +131,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
|
|||
lua_lock(to);
|
||||
api_checknelems(from, n);
|
||||
api_check(from, G(from) == G(to), "moving among independent states");
|
||||
api_check(from, to->ci->top - to->top >= n, "stack overflow");
|
||||
from->top -= n;
|
||||
api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow");
|
||||
from->top.p -= n;
|
||||
for (i = 0; i < n; i++) {
|
||||
setobjs2s(to, to->top, from->top + i);
|
||||
to->top++; /* stack already checked by previous 'api_check' */
|
||||
setobjs2s(to, to->top.p, from->top.p + i);
|
||||
to->top.p++; /* stack already checked by previous 'api_check' */
|
||||
}
|
||||
lua_unlock(to);
|
||||
}
|
||||
|
@ -172,12 +169,12 @@ LUA_API lua_Number lua_version (lua_State *L) {
|
|||
LUA_API int lua_absindex (lua_State *L, int idx) {
|
||||
return (idx > 0 || ispseudo(idx))
|
||||
? idx
|
||||
: cast_int(L->top - L->ci->func) + idx;
|
||||
: cast_int(L->top.p - L->ci->func.p) + idx;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_gettop (lua_State *L) {
|
||||
return cast_int(L->top - (L->ci->func + 1));
|
||||
return cast_int(L->top.p - (L->ci->func.p + 1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -187,24 +184,24 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
|||
ptrdiff_t diff; /* difference for new top */
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
func = ci->func;
|
||||
func = ci->func.p;
|
||||
if (idx >= 0) {
|
||||
api_check(L, idx <= ci->top - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top;
|
||||
api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top.p;
|
||||
for (; diff > 0; diff--)
|
||||
setnilvalue(s2v(L->top++)); /* clear new slots */
|
||||
setnilvalue(s2v(L->top.p++)); /* clear new slots */
|
||||
}
|
||||
else {
|
||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||
api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
|
||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||
}
|
||||
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
|
||||
newtop = L->top + diff;
|
||||
if (diff < 0 && L->tbclist >= newtop) {
|
||||
api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
|
||||
newtop = L->top.p + diff;
|
||||
if (diff < 0 && L->tbclist.p >= newtop) {
|
||||
lua_assert(hastocloseCfunc(ci->nresults));
|
||||
luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||
newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||
}
|
||||
L->top = newtop; /* correct top only after closing any upvalue */
|
||||
L->top.p = newtop; /* correct top only after closing any upvalue */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
@ -213,10 +210,9 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
|
|||
StkId level;
|
||||
lua_lock(L);
|
||||
level = index2stack(L, idx);
|
||||
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level,
|
||||
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
|
||||
"no variable to close at given level");
|
||||
luaF_close(L, level, CLOSEKTOP, 0);
|
||||
level = index2stack(L, idx); /* stack may be moved */
|
||||
level = luaF_close(L, level, CLOSEKTOP, 0);
|
||||
setnilvalue(s2v(level));
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -245,7 +241,7 @@ l_sinline void reverse (lua_State *L, StkId from, StkId to) {
|
|||
LUA_API void lua_rotate (lua_State *L, int idx, int n) {
|
||||
StkId p, t, m;
|
||||
lua_lock(L);
|
||||
t = L->top - 1; /* end of stack segment being rotated */
|
||||
t = L->top.p - 1; /* end of stack segment being rotated */
|
||||
p = index2stack(L, idx); /* start of segment */
|
||||
api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
|
||||
m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */
|
||||
|
@ -264,7 +260,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
|
|||
api_check(L, isvalid(L, to), "invalid index");
|
||||
setobj(L, to, fr);
|
||||
if (isupvalue(toidx)) /* function upvalue? */
|
||||
luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr);
|
||||
luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr);
|
||||
/* LUA_REGISTRYINDEX does not need gc barrier
|
||||
(collector revisits it before finishing collection) */
|
||||
lua_unlock(L);
|
||||
|
@ -273,7 +269,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
|
|||
|
||||
LUA_API void lua_pushvalue (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
setobj2s(L, L->top, index2value(L, idx));
|
||||
setobj2s(L, L->top.p, index2value(L, idx));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -342,12 +338,12 @@ LUA_API void lua_arith (lua_State *L, int op) {
|
|||
api_checknelems(L, 2); /* all other operations expect two operands */
|
||||
else { /* for unary operations, add fake 2nd operand */
|
||||
api_checknelems(L, 1);
|
||||
setobjs2s(L, L->top, L->top - 1);
|
||||
setobjs2s(L, L->top.p, L->top.p - 1);
|
||||
api_incr_top(L);
|
||||
}
|
||||
/* first operand at top - 2, second at top - 1; result go to top - 2 */
|
||||
luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2);
|
||||
L->top--; /* remove second operand */
|
||||
luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2);
|
||||
L->top.p--; /* remove second operand */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
@ -373,7 +369,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
|
|||
|
||||
|
||||
LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
|
||||
size_t sz = luaO_str2num(s, s2v(L->top));
|
||||
size_t sz = luaO_str2num(s, s2v(L->top.p));
|
||||
if (sz != 0)
|
||||
api_incr_top(L);
|
||||
return sz;
|
||||
|
@ -500,7 +496,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) {
|
|||
|
||||
LUA_API void lua_pushnil (lua_State *L) {
|
||||
lua_lock(L);
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -508,7 +504,7 @@ LUA_API void lua_pushnil (lua_State *L) {
|
|||
|
||||
LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
|
||||
lua_lock(L);
|
||||
setfltvalue(s2v(L->top), n);
|
||||
setfltvalue(s2v(L->top.p), n);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -516,7 +512,7 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
|
|||
|
||||
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
|
||||
lua_lock(L);
|
||||
setivalue(s2v(L->top), n);
|
||||
setivalue(s2v(L->top.p), n);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -531,7 +527,7 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
|
|||
TString *ts;
|
||||
lua_lock(L);
|
||||
ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
lua_unlock(L);
|
||||
|
@ -542,11 +538,11 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
|
|||
LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
|
||||
lua_lock(L);
|
||||
if (s == NULL)
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
else {
|
||||
TString *ts;
|
||||
ts = luaS_new(L, s);
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
s = getstr(ts); /* internal copy's address */
|
||||
}
|
||||
api_incr_top(L);
|
||||
|
@ -583,7 +579,7 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
|
|||
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
||||
lua_lock(L);
|
||||
if (n == 0) {
|
||||
setfvalue(s2v(L->top), fn);
|
||||
setfvalue(s2v(L->top.p), fn);
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
|
@ -592,13 +588,13 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
|||
api_check(L, n <= MAXUPVAL, "upvalue index too large");
|
||||
cl = luaF_newCclosure(L, n);
|
||||
cl->f = fn;
|
||||
L->top -= n;
|
||||
L->top.p -= n;
|
||||
while (n--) {
|
||||
setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
|
||||
setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
|
||||
/* does not need barrier because closure is white */
|
||||
lua_assert(iswhite(cl));
|
||||
}
|
||||
setclCvalue(L, s2v(L->top), cl);
|
||||
setclCvalue(L, s2v(L->top.p), cl);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
|
@ -609,9 +605,9 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
|||
LUA_API void lua_pushboolean (lua_State *L, int b) {
|
||||
lua_lock(L);
|
||||
if (b)
|
||||
setbtvalue(s2v(L->top));
|
||||
setbtvalue(s2v(L->top.p));
|
||||
else
|
||||
setbfvalue(s2v(L->top));
|
||||
setbfvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -619,7 +615,7 @@ LUA_API void lua_pushboolean (lua_State *L, int b) {
|
|||
|
||||
LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
|
||||
lua_lock(L);
|
||||
setpvalue(s2v(L->top), p);
|
||||
setpvalue(s2v(L->top.p), p);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -627,7 +623,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
|
|||
|
||||
LUA_API int lua_pushthread (lua_State *L) {
|
||||
lua_lock(L);
|
||||
setthvalue(L, s2v(L->top), L);
|
||||
setthvalue(L, s2v(L->top.p), L);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return (G(L)->mainthread == L);
|
||||
|
@ -644,16 +640,16 @@ l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
|||
const TValue *slot;
|
||||
TString *str = luaS_new(L, k);
|
||||
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
|
||||
setobj2s(L, L->top, slot);
|
||||
setobj2s(L, L->top.p, slot);
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
setsvalue2s(L, L->top, str);
|
||||
setsvalue2s(L, L->top.p, str);
|
||||
api_incr_top(L);
|
||||
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot);
|
||||
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
||||
}
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -680,13 +676,13 @@ LUA_API int lua_gettable (lua_State *L, int idx) {
|
|||
TValue *t;
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) {
|
||||
setobj2s(L, L->top - 1, slot);
|
||||
if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) {
|
||||
setobj2s(L, L->top.p - 1, slot);
|
||||
}
|
||||
else
|
||||
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot);
|
||||
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -702,27 +698,27 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
|
|||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastgeti(L, t, n, slot)) {
|
||||
setobj2s(L, L->top, slot);
|
||||
setobj2s(L, L->top.p, slot);
|
||||
}
|
||||
else {
|
||||
TValue aux;
|
||||
setivalue(&aux, n);
|
||||
luaV_finishget(L, t, &aux, L->top, slot);
|
||||
luaV_finishget(L, t, &aux, L->top.p, slot);
|
||||
}
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
l_sinline int finishrawget (lua_State *L, const TValue *val) {
|
||||
if (isempty(val)) /* avoid copying empty items to the stack */
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
else
|
||||
setobj2s(L, L->top, val);
|
||||
setobj2s(L, L->top.p, val);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
|
@ -739,8 +735,8 @@ LUA_API int lua_rawget (lua_State *L, int idx) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
val = luaH_get(t, s2v(L->top - 1));
|
||||
L->top--; /* remove key */
|
||||
val = luaH_get(t, s2v(L->top.p - 1));
|
||||
L->top.p--; /* remove key */
|
||||
return finishrawget(L, val);
|
||||
}
|
||||
|
||||
|
@ -767,7 +763,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
|||
Table *t;
|
||||
lua_lock(L);
|
||||
t = luaH_new(L);
|
||||
sethvalue2s(L, L->top, t);
|
||||
sethvalue2s(L, L->top.p, t);
|
||||
api_incr_top(L);
|
||||
if (narray > 0 || nrec > 0)
|
||||
luaH_resize(L, t, narray, nrec);
|
||||
|
@ -794,7 +790,7 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
|||
break;
|
||||
}
|
||||
if (mt != NULL) {
|
||||
sethvalue2s(L, L->top, mt);
|
||||
sethvalue2s(L, L->top.p, mt);
|
||||
api_incr_top(L);
|
||||
res = 1;
|
||||
}
|
||||
|
@ -810,12 +806,12 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
|
|||
o = index2value(L, idx);
|
||||
api_check(L, ttisfulluserdata(o), "full userdata expected");
|
||||
if (n <= 0 || n > uvalue(o)->nuvalue) {
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
t = LUA_TNONE;
|
||||
}
|
||||
else {
|
||||
setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv);
|
||||
t = ttype(s2v(L->top));
|
||||
setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
|
||||
t = ttype(s2v(L->top.p));
|
||||
}
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
|
@ -835,14 +831,14 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
|
|||
TString *str = luaS_new(L, k);
|
||||
api_checknelems(L, 1);
|
||||
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
L->top--; /* pop value */
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
L->top.p--; /* pop value */
|
||||
}
|
||||
else {
|
||||
setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */
|
||||
setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
|
||||
api_incr_top(L);
|
||||
luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot);
|
||||
L->top -= 2; /* pop value and key */
|
||||
luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
|
||||
L->top.p -= 2; /* pop value and key */
|
||||
}
|
||||
lua_unlock(L); /* lock done by caller */
|
||||
}
|
||||
|
@ -862,12 +858,12 @@ LUA_API void lua_settable (lua_State *L, int idx) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, 2);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
}
|
||||
else
|
||||
luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot);
|
||||
L->top -= 2; /* pop index and value */
|
||||
luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
|
||||
L->top.p -= 2; /* pop index and value */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
@ -885,14 +881,14 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
|
|||
api_checknelems(L, 1);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastgeti(L, t, n, slot)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
}
|
||||
else {
|
||||
TValue aux;
|
||||
setivalue(&aux, n);
|
||||
luaV_finishset(L, t, &aux, s2v(L->top - 1), slot);
|
||||
luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
|
||||
}
|
||||
L->top--; /* pop value */
|
||||
L->top.p--; /* pop value */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
@ -902,16 +898,16 @@ static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
t = gettable(L, idx);
|
||||
luaH_set(L, t, key, s2v(L->top - 1));
|
||||
luaH_set(L, t, key, s2v(L->top.p - 1));
|
||||
invalidateTMcache(t);
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top -= n;
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
||||
L->top.p -= n;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawset (lua_State *L, int idx) {
|
||||
aux_rawset(L, idx, s2v(L->top - 2), 2);
|
||||
aux_rawset(L, idx, s2v(L->top.p - 2), 2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -927,9 +923,9 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
luaH_setint(L, t, n, s2v(L->top - 1));
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top--;
|
||||
luaH_setint(L, t, n, s2v(L->top.p - 1));
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
@ -940,11 +936,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
obj = index2value(L, objindex);
|
||||
if (ttisnil(s2v(L->top - 1)))
|
||||
if (ttisnil(s2v(L->top.p - 1)))
|
||||
mt = NULL;
|
||||
else {
|
||||
api_check(L, ttistable(s2v(L->top - 1)), "table expected");
|
||||
mt = hvalue(s2v(L->top - 1));
|
||||
api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
|
||||
mt = hvalue(s2v(L->top.p - 1));
|
||||
}
|
||||
switch (ttype(obj)) {
|
||||
case LUA_TTABLE: {
|
||||
|
@ -968,7 +964,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
L->top--;
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
return 1;
|
||||
}
|
||||
|
@ -984,11 +980,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
|||
if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
|
||||
res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
|
||||
else {
|
||||
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1));
|
||||
luaC_barrierback(L, gcvalue(o), s2v(L->top - 1));
|
||||
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1));
|
||||
luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
|
||||
res = 1;
|
||||
}
|
||||
L->top--;
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
return res;
|
||||
}
|
||||
|
@ -1000,7 +996,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
|||
|
||||
|
||||
#define checkresults(L,na,nr) \
|
||||
api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \
|
||||
api_check(L, (nr) == LUA_MULTRET \
|
||||
|| (L->ci->top.p - L->top.p >= (nr) - (na)), \
|
||||
"results from function overflow current stack size")
|
||||
|
||||
|
||||
|
@ -1013,7 +1010,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
|
|||
api_checknelems(L, nargs+1);
|
||||
api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
|
||||
checkresults(L, nargs, nresults);
|
||||
func = L->top - (nargs+1);
|
||||
func = L->top.p - (nargs+1);
|
||||
if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
|
||||
L->ci->u.c.k = k; /* save continuation */
|
||||
L->ci->u.c.ctx = ctx; /* save context */
|
||||
|
@ -1061,7 +1058,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
|
|||
api_check(L, ttisfunction(s2v(o)), "error handler must be a function");
|
||||
func = savestack(L, o);
|
||||
}
|
||||
c.func = L->top - (nargs+1); /* function to be called */
|
||||
c.func = L->top.p - (nargs+1); /* function to be called */
|
||||
if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
|
||||
c.nresults = nresults; /* do a 'conventional' protected call */
|
||||
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
|
@ -1096,12 +1093,12 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
|
|||
luaZ_init(L, &z, reader, data);
|
||||
status = luaD_protectedparser(L, &z, chunkname, mode);
|
||||
if (status == LUA_OK) { /* no errors? */
|
||||
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
|
||||
LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */
|
||||
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
||||
/* get global table from registry */
|
||||
const TValue *gt = getGtable(L);
|
||||
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
||||
setobj(L, f->upvals[0]->v, gt);
|
||||
setobj(L, f->upvals[0]->v.p, gt);
|
||||
luaC_barrier(L, f->upvals[0], gt);
|
||||
}
|
||||
}
|
||||
|
@ -1115,7 +1112,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
|
|||
TValue *o;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
o = s2v(L->top - 1);
|
||||
o = s2v(L->top.p - 1);
|
||||
if (isLfunction(o))
|
||||
status = luaU_dump(L, getproto(o), writer, data, strip);
|
||||
else
|
||||
|
@ -1241,7 +1238,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
|||
LUA_API int lua_error (lua_State *L) {
|
||||
TValue *errobj;
|
||||
lua_lock(L);
|
||||
errobj = s2v(L->top - 1);
|
||||
errobj = s2v(L->top.p - 1);
|
||||
api_checknelems(L, 1);
|
||||
/* error object is the memory error message? */
|
||||
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
|
||||
|
@ -1259,12 +1256,12 @@ LUA_API int lua_next (lua_State *L, int idx) {
|
|||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
more = luaH_next(L, t, L->top - 1);
|
||||
more = luaH_next(L, t, L->top.p - 1);
|
||||
if (more) {
|
||||
api_incr_top(L);
|
||||
}
|
||||
else /* no more elements */
|
||||
L->top -= 1; /* remove key */
|
||||
L->top.p -= 1; /* remove key */
|
||||
lua_unlock(L);
|
||||
return more;
|
||||
}
|
||||
|
@ -1276,7 +1273,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
|||
lua_lock(L);
|
||||
o = index2stack(L, idx);
|
||||
nresults = L->ci->nresults;
|
||||
api_check(L, L->tbclist < o, "given index below or equal a marked one");
|
||||
api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
|
||||
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||
L->ci->nresults = codeNresults(nresults); /* mark it */
|
||||
|
@ -1291,7 +1288,7 @@ LUA_API void lua_concat (lua_State *L, int n) {
|
|||
if (n > 0)
|
||||
luaV_concat(L, n);
|
||||
else { /* nothing to concatenate */
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
|
||||
setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
|
||||
api_incr_top(L);
|
||||
}
|
||||
luaC_checkGC(L);
|
||||
|
@ -1303,7 +1300,7 @@ LUA_API void lua_len (lua_State *L, int idx) {
|
|||
TValue *t;
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
luaV_objlen(L, L->top, t);
|
||||
luaV_objlen(L, L->top.p, t);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
@ -1348,7 +1345,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
|
|||
lua_lock(L);
|
||||
api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
|
||||
u = luaS_newudata(L, size, nuvalue);
|
||||
setuvalue(L, s2v(L->top), u);
|
||||
setuvalue(L, s2v(L->top.p), u);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
lua_unlock(L);
|
||||
|
@ -1374,7 +1371,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
|
|||
Proto *p = f->p;
|
||||
if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
|
||||
return NULL; /* 'n' not in [1, p->sizeupvalues] */
|
||||
*val = f->upvals[n-1]->v;
|
||||
*val = f->upvals[n-1]->v.p;
|
||||
if (owner) *owner = obj2gco(f->upvals[n - 1]);
|
||||
name = p->upvalues[n-1].name;
|
||||
return (name == NULL) ? "(no name)" : getstr(name);
|
||||
|
@ -1390,7 +1387,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
|
|||
lua_lock(L);
|
||||
name = aux_upvalue(index2value(L, funcindex), n, &val, NULL);
|
||||
if (name) {
|
||||
setobj2s(L, L->top, val);
|
||||
setobj2s(L, L->top.p, val);
|
||||
api_incr_top(L);
|
||||
}
|
||||
lua_unlock(L);
|
||||
|
@ -1408,8 +1405,8 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
|||
api_checknelems(L, 1);
|
||||
name = aux_upvalue(fi, n, &val, &owner);
|
||||
if (name) {
|
||||
L->top--;
|
||||
setobj(L, val, s2v(L->top));
|
||||
L->top.p--;
|
||||
setobj(L, val, s2v(L->top.p));
|
||||
luaC_barrier(L, owner, val);
|
||||
}
|
||||
lua_unlock(L);
|
||||
|
|
|
@ -12,23 +12,26 @@
|
|||
#include "lstate.h"
|
||||
|
||||
|
||||
/* Increments 'L->top', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
/* Increments 'L->top.p', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top.p++; \
|
||||
api_check(L, L->top.p <= L->ci->top.p, \
|
||||
"stack overflow");}
|
||||
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accommodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
** increases its stack space ('L->ci->top.p').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
|
||||
L->ci->top.p = L->top.p; }
|
||||
|
||||
|
||||
/* Ensure the stack has at least 'n' elements */
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
#define api_checknelems(L,n) \
|
||||
api_check(L, (n) < (L->top.p - L->ci->func.p), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -526,13 +526,14 @@ static void newbox (lua_State *L) {
|
|||
|
||||
/*
|
||||
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
|
||||
** bytes.
|
||||
** bytes. (The test for "not big enough" also gets the case when the
|
||||
** computation of 'newsize' overflows.)
|
||||
*/
|
||||
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
size_t newsize = B->size * 2; /* double buffer size */
|
||||
size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
|
||||
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
|
||||
return luaL_error(B->L, "buffer too large");
|
||||
if (newsize < B->n + sz) /* double is not big enough? */
|
||||
if (newsize < B->n + sz) /* not big enough? */
|
||||
newsize = B->n + sz;
|
||||
return newsize;
|
||||
}
|
||||
|
@ -611,7 +612,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
|
|||
** box (if existent) is not on the top of the stack. So, instead of
|
||||
** calling 'luaL_addlstring', it replicates the code using -2 as the
|
||||
** last argument to 'prepbuffsize', signaling that the box is (or will
|
||||
** be) bellow the string being added to the buffer. (Box creation can
|
||||
** be) below the string being added to the buffer. (Box creation can
|
||||
** trigger an emergency GC, so we should not remove the string from the
|
||||
** stack before we have the space guaranteed.)
|
||||
*/
|
||||
|
@ -739,17 +740,18 @@ static int errfile (lua_State *L, const char *what, int fnameindex) {
|
|||
}
|
||||
|
||||
|
||||
static int skipBOM (LoadF *lf) {
|
||||
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
||||
int c;
|
||||
lf->n = 0;
|
||||
do {
|
||||
c = getc(lf->f);
|
||||
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
||||
lf->buff[lf->n++] = c; /* to be read by the parser */
|
||||
} while (*p != '\0');
|
||||
lf->n = 0; /* prefix matched; discard it */
|
||||
return getc(lf->f); /* return next character */
|
||||
/*
|
||||
** Skip an optional BOM at the start of a stream. If there is an
|
||||
** incomplete BOM (the first character is correct but the rest is
|
||||
** not), returns the first character anyway to force an error
|
||||
** (as no chunk can start with 0xEF).
|
||||
*/
|
||||
static int skipBOM (FILE *f) {
|
||||
int c = getc(f); /* read first character */
|
||||
if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
|
||||
return getc(f); /* ignore BOM and return next char */
|
||||
else /* no (valid) BOM */
|
||||
return c; /* return first character */
|
||||
}
|
||||
|
||||
|
||||
|
@ -760,13 +762,13 @@ static int skipBOM (LoadF *lf) {
|
|||
** first "valid" character of the file (after the optional BOM and
|
||||
** a first-line comment).
|
||||
*/
|
||||
static int skipcomment (LoadF *lf, int *cp) {
|
||||
int c = *cp = skipBOM(lf);
|
||||
static int skipcomment (FILE *f, int *cp) {
|
||||
int c = *cp = skipBOM(f);
|
||||
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
||||
do { /* skip first line */
|
||||
c = getc(lf->f);
|
||||
c = getc(f);
|
||||
} while (c != EOF && c != '\n');
|
||||
*cp = getc(lf->f); /* skip end-of-line, if present */
|
||||
*cp = getc(f); /* next character after comment, if present */
|
||||
return 1; /* there was a comment */
|
||||
}
|
||||
else return 0; /* no comment */
|
||||
|
@ -788,12 +790,16 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
|
|||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(&lf, &c); /* re-read initial portion */
|
||||
lf.n = 0;
|
||||
if (skipcomment(lf.f, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0]) { /* binary file? */
|
||||
lf.n = 0; /* remove possible newline */
|
||||
if (filename) { /* "real" file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(lf.f, &c); /* re-read initial portion */
|
||||
}
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
|
|
137
src/lua/lcode.c
137
src/lua/lcode.c
|
@ -1351,6 +1351,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
|
||||
lua_assert(baser <= opr &&
|
||||
((baser == OPR_ADD && opr <= OPR_SHR) ||
|
||||
(baser == OPR_LT && opr <= OPR_LE)));
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode unopr2op (UnOpr opr) {
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
|
||||
cast_int(OP_UNM));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
|
||||
*/
|
||||
l_sinline TMS binopr2TM (BinOpr opr) {
|
||||
lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
|
||||
return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit code for unary expressions that "produce values"
|
||||
** (everything but 'not').
|
||||
|
@ -1389,12 +1418,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
|||
** Emit code for binary expressions that "produce values" over
|
||||
** two registers.
|
||||
*/
|
||||
static void codebinexpval (FuncState *fs, OpCode op,
|
||||
static void codebinexpval (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
|
||||
/* 'e1' must be already in a register or it is a constant */
|
||||
lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
|
||||
e1->k == VNONRELOC || e1->k == VRELOC);
|
||||
lua_assert(OP_ADD <= op && op <= OP_SHR);
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN,
|
||||
cast(TMS, (op - OP_ADD) + TM_ADD));
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1410,6 +1442,18 @@ static void codebini (FuncState *fs, OpCode op,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with K operand.
|
||||
*/
|
||||
static void codebinK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
TMS event = binopr2TM(opr);
|
||||
int v2 = e2->u.info; /* K index */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
|
||||
|
||||
/* Try to code a binary operator negating its second operand.
|
||||
** For the metamethod, 2nd operand must keep its original value.
|
||||
*/
|
||||
|
@ -1437,24 +1481,27 @@ static void swapexps (expdesc *e1, expdesc *e2) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with no constant operand.
|
||||
*/
|
||||
static void codebinNoK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code arithmetic operators ('+', '-', ...). If second operand is a
|
||||
** constant in the proper range, use variant opcodes with K operands.
|
||||
*/
|
||||
static void codearith (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
TMS event = cast(TMS, opr + TM_ADD);
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
|
||||
int v2 = e2->u.info; /* K index */
|
||||
OpCode op = cast(OpCode, opr + OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
else { /* 'e2' is neither an immediate nor a K operand */
|
||||
OpCode op = cast(OpCode, opr + OP_ADD);
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
|
||||
}
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* 'e2' is neither an immediate nor a K operand */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1471,35 +1518,27 @@ static void codecommutative (FuncState *fs, BinOpr op,
|
|||
flip = 1;
|
||||
}
|
||||
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
|
||||
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
|
||||
codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
|
||||
else
|
||||
codearith(fs, op, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code bitwise operations; they are all associative, so the function
|
||||
** Code bitwise operations; they are all commutative, so the function
|
||||
** tries to put an integer constant as the 2nd operand (a K operand).
|
||||
*/
|
||||
static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int flip = 0;
|
||||
int v2;
|
||||
OpCode op;
|
||||
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
|
||||
if (e1->k == VKINT) {
|
||||
swapexps(e1, e2); /* 'e2' will be the constant operand */
|
||||
flip = 1;
|
||||
}
|
||||
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
|
||||
op = cast(OpCode, opr + OP_ADD);
|
||||
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
|
||||
return;
|
||||
}
|
||||
v2 = e2->u.info; /* index in K array */
|
||||
op = cast(OpCode, opr + OP_ADDK);
|
||||
lua_assert(ttisinteger(&fs->f->k[v2]));
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
|
||||
cast(TMS, opr + TM_ADD));
|
||||
if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* no constants */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1507,25 +1546,27 @@ static void codebitwise (FuncState *fs, BinOpr opr,
|
|||
** Emit code for order comparisons. When using an immediate operand,
|
||||
** 'isfloat' tells whether the original value was a float.
|
||||
*/
|
||||
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
|
||||
static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
int r1, r2;
|
||||
int im;
|
||||
int isfloat = 0;
|
||||
OpCode op;
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
/* use immediate operand */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = im;
|
||||
op = cast(OpCode, (op - OP_LT) + OP_LTI);
|
||||
op = binopr2op(opr, OPR_LT, OP_LTI);
|
||||
}
|
||||
else if (isSCnumber(e1, &im, &isfloat)) {
|
||||
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
|
||||
r1 = luaK_exp2anyreg(fs, e2);
|
||||
r2 = im;
|
||||
op = (op == OP_LT) ? OP_GTI : OP_GEI;
|
||||
op = binopr2op(opr, OPR_LT, OP_GTI);
|
||||
}
|
||||
else { /* regular case, compare two registers */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = luaK_exp2anyreg(fs, e2);
|
||||
op = binopr2op(opr, OPR_LT, OP_LT);
|
||||
}
|
||||
freeexps(fs, e1, e2);
|
||||
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
|
||||
|
@ -1551,7 +1592,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
|||
op = OP_EQI;
|
||||
r2 = im; /* immediate operand */
|
||||
}
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
|
||||
op = OP_EQK;
|
||||
r2 = e2->u.info; /* constant index */
|
||||
}
|
||||
|
@ -1568,16 +1609,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
|||
/*
|
||||
** Apply prefix operation 'op' to expression 'e'.
|
||||
*/
|
||||
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
||||
void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
|
||||
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (op) {
|
||||
switch (opr) {
|
||||
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
||||
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
||||
if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
|
||||
break;
|
||||
/* else */ /* FALLTHROUGH */
|
||||
case OPR_LEN:
|
||||
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
|
||||
codeunexpval(fs, unopr2op(opr), e, line);
|
||||
break;
|
||||
case OPR_NOT: codenot(fs, e); break;
|
||||
default: lua_assert(0);
|
||||
|
@ -1611,7 +1652,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
|||
case OPR_SHL: case OPR_SHR: {
|
||||
if (!tonumeral(v, NULL))
|
||||
luaK_exp2anyreg(fs, v);
|
||||
/* else keep numeral, which may be folded with 2nd operand */
|
||||
/* else keep numeral, which may be folded or used as an immediate
|
||||
operand */
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
|
@ -1706,30 +1748,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
|
|||
/* coded as (r1 >> -I) */;
|
||||
}
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHL, e1, e2, line);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SHR: {
|
||||
if (isSCint(e2))
|
||||
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHR, e1, e2, line);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
codeeq(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_LT: case OPR_LE: {
|
||||
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
|
||||
codeorder(fs, op, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_GT: case OPR_GE: {
|
||||
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
|
||||
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
|
||||
swapexps(e1, e2);
|
||||
codeorder(fs, op, e1, e2);
|
||||
opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
|
||||
} /* FALLTHROUGH */
|
||||
case OPR_LT: case OPR_LE: {
|
||||
codeorder(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
|
|
|
@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) {
|
|||
if (l_unlikely(r < 0)) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||
stat = lua_resetthread(co); /* close its tbc variables */
|
||||
stat = lua_closethread(co, L); /* close its tbc variables */
|
||||
lua_assert(stat != LUA_OK);
|
||||
lua_xmove(co, L, 1); /* move error message to the caller */
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) {
|
|||
int status = auxstatus(L, co);
|
||||
switch (status) {
|
||||
case COS_DEAD: case COS_YIELD: {
|
||||
status = lua_resetthread(co);
|
||||
status = lua_closethread(co, L);
|
||||
if (status == LUA_OK) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
|
|
|
@ -182,10 +182,10 @@ static const char *upvalname (const Proto *p, int uv) {
|
|||
|
||||
|
||||
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
if (clLvalue(s2v(ci->func))->p->is_vararg) {
|
||||
if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (n >= -nextra) { /* 'n' is negative */
|
||||
*pos = ci->func - nextra - (n + 1);
|
||||
*pos = ci->func.p - nextra - (n + 1);
|
||||
return "(vararg)"; /* generic name for any vararg */
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
|||
|
||||
|
||||
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
StkId base = ci->func + 1;
|
||||
StkId base = ci->func.p + 1;
|
||||
const char *name = NULL;
|
||||
if (isLua(ci)) {
|
||||
if (n < 0) /* access to vararg values? */
|
||||
|
@ -203,7 +203,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
|||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||
}
|
||||
if (name == NULL) { /* no 'standard' name? */
|
||||
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||
StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
|
||||
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
||||
/* generic name for any valid slot */
|
||||
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
||||
|
@ -221,16 +221,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|||
const char *name;
|
||||
lua_lock(L);
|
||||
if (ar == NULL) { /* information about non-active function? */
|
||||
if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */
|
||||
if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
|
||||
name = NULL;
|
||||
else /* consider live variables at function start (parameters) */
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0);
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
|
||||
}
|
||||
else { /* active function; get information through 'ar' */
|
||||
StkId pos = NULL; /* to avoid warnings */
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, L->top, pos);
|
||||
setobjs2s(L, L->top.p, pos);
|
||||
api_incr_top(L);
|
||||
}
|
||||
}
|
||||
|
@ -245,8 +245,8 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|||
lua_lock(L);
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, pos, L->top - 1);
|
||||
L->top--; /* pop value */
|
||||
setobjs2s(L, pos, L->top.p - 1);
|
||||
L->top.p--; /* pop value */
|
||||
}
|
||||
lua_unlock(L);
|
||||
return name;
|
||||
|
@ -289,7 +289,7 @@ static int nextline (const Proto *p, int currentline, int pc) {
|
|||
|
||||
static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
if (noLuaClosure(f)) {
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
|
@ -298,7 +298,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
|||
const Proto *p = f->l.p;
|
||||
int currentline = p->linedefined;
|
||||
Table *t = luaH_new(L); /* new table to store active lines */
|
||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||
sethvalue2s(L, L->top.p, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||
if (!p->is_vararg) /* regular function? */
|
||||
|
@ -388,20 +388,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
|||
lua_lock(L);
|
||||
if (*what == '>') {
|
||||
ci = NULL;
|
||||
func = s2v(L->top - 1);
|
||||
func = s2v(L->top.p - 1);
|
||||
api_check(L, ttisfunction(func), "function expected");
|
||||
what++; /* skip the '>' */
|
||||
L->top--; /* pop function */
|
||||
L->top.p--; /* pop function */
|
||||
}
|
||||
else {
|
||||
ci = ar->i_ci;
|
||||
func = s2v(ci->func);
|
||||
func = s2v(ci->func.p);
|
||||
lua_assert(ttisfunction(func));
|
||||
}
|
||||
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
||||
status = auxgetinfo(L, what, ar, cl, ci);
|
||||
if (strchr(what, 'f')) {
|
||||
setobj2s(L, L->top, func);
|
||||
setobj2s(L, L->top.p, func);
|
||||
api_incr_top(L);
|
||||
}
|
||||
if (strchr(what, 'L'))
|
||||
|
@ -656,18 +656,19 @@ static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
|||
|
||||
|
||||
/*
|
||||
** Check whether pointer 'o' points to some value in the stack
|
||||
** frame of the current function. Because 'o' may not point to a
|
||||
** value in this stack, we cannot compare it with the region
|
||||
** boundaries (undefined behaviour in ISO C).
|
||||
** Check whether pointer 'o' points to some value in the stack frame of
|
||||
** the current function and, if so, returns its index. Because 'o' may
|
||||
** not point to a value in this stack, we cannot compare it with the
|
||||
** region boundaries (undefined behavior in ISO C).
|
||||
*/
|
||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||
StkId pos;
|
||||
for (pos = ci->func + 1; pos < ci->top; pos++) {
|
||||
if (o == s2v(pos))
|
||||
return 1;
|
||||
static int instack (CallInfo *ci, const TValue *o) {
|
||||
int pos;
|
||||
StkId base = ci->func.p + 1;
|
||||
for (pos = 0; base + pos < ci->top.p; pos++) {
|
||||
if (o == s2v(base + pos))
|
||||
return pos;
|
||||
}
|
||||
return 0; /* not found */
|
||||
return -1; /* not found */
|
||||
}
|
||||
|
||||
|
||||
|
@ -681,7 +682,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
|
|||
LClosure *c = ci_func(ci);
|
||||
int i;
|
||||
for (i = 0; i < c->nupvalues; i++) {
|
||||
if (c->upvals[i]->v == o) {
|
||||
if (c->upvals[i]->v.p == o) {
|
||||
*name = upvalname(c->p, i);
|
||||
return "upvalue";
|
||||
}
|
||||
|
@ -708,9 +709,11 @@ static const char *varinfo (lua_State *L, const TValue *o) {
|
|||
const char *kind = NULL;
|
||||
if (isLua(ci)) {
|
||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||
if (!kind && isinstack(ci, o)) /* no? try a register */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||
cast_int(cast(StkId, o) - (ci->func + 1)), &name);
|
||||
if (!kind) { /* not an upvalue? */
|
||||
int reg = instack(ci, o); /* try a register */
|
||||
if (reg >= 0) /* is 'o' a register? */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
|
||||
}
|
||||
}
|
||||
return formatvarinfo(L, kind, name);
|
||||
}
|
||||
|
@ -807,10 +810,10 @@ l_noret luaG_errormsg (lua_State *L) {
|
|||
if (L->errfunc != 0) { /* is there an error handling function? */
|
||||
StkId errfunc = restorestack(L, L->errfunc);
|
||||
lua_assert(ttisfunction(s2v(errfunc)));
|
||||
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
||||
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
||||
L->top++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top - 2, 1); /* call it */
|
||||
setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
|
||||
setobjs2s(L, L->top.p - 1, errfunc); /* push function */
|
||||
L->top.p++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
|
||||
}
|
||||
luaD_throw(L, LUA_ERRRUN);
|
||||
}
|
||||
|
@ -824,8 +827,11 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
|||
va_start(argp, fmt);
|
||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||
va_end(argp);
|
||||
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||
if (isLua(ci)) { /* if Lua function, add source:line information */
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
||||
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
|
||||
L->top.p--;
|
||||
}
|
||||
luaG_errormsg(L);
|
||||
}
|
||||
|
||||
|
@ -842,7 +848,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
|||
if (p->lineinfo == NULL) /* no debug information? */
|
||||
return 0;
|
||||
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||
int delta = 0; /* line diference */
|
||||
int delta = 0; /* line difference */
|
||||
int pc = oldpc;
|
||||
for (;;) {
|
||||
int lineinfo = p->lineinfo[++pc];
|
||||
|
@ -869,7 +875,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
|||
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||
** at most causes an extra call to a line hook.)
|
||||
** This function is not "Protected" when called, so it should correct
|
||||
** 'L->top' before calling anything that can run the GC.
|
||||
** 'L->top.p' before calling anything that can run the GC.
|
||||
*/
|
||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
CallInfo *ci = L->ci;
|
||||
|
@ -892,7 +898,7 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
|||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||
}
|
||||
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||
L->top = ci->top; /* correct top */
|
||||
L->top.p = ci->top.p; /* correct top */
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
|
||||
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
|
|
229
src/lua/ldo.c
229
src/lua/ldo.c
|
@ -104,11 +104,11 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
|||
}
|
||||
default: {
|
||||
lua_assert(errorstatus(errcode)); /* real error */
|
||||
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
||||
setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
|
||||
break;
|
||||
}
|
||||
}
|
||||
L->top = oldtop + 1;
|
||||
L->top.p = oldtop + 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -121,7 +121,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
|||
global_State *g = G(L);
|
||||
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
|
||||
if (g->mainthread->errorJmp) { /* main thread has a handler? */
|
||||
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
|
||||
setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
|
||||
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
|
||||
}
|
||||
else { /* no handler at all; abort */
|
||||
|
@ -157,16 +157,38 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
|||
** Stack reallocation
|
||||
** ===================================================================
|
||||
*/
|
||||
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
||||
|
||||
|
||||
/*
|
||||
** Change all pointers to the stack into offsets.
|
||||
*/
|
||||
static void relstack (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
UpVal *up;
|
||||
L->top = (L->top - oldstack) + newstack;
|
||||
L->tbclist = (L->tbclist - oldstack) + newstack;
|
||||
L->top.offset = savestack(L, L->top.p);
|
||||
L->tbclist.offset = savestack(L, L->tbclist.p);
|
||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
||||
up->v.offset = savestack(L, uplevel(up));
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
ci->top = (ci->top - oldstack) + newstack;
|
||||
ci->func = (ci->func - oldstack) + newstack;
|
||||
ci->top.offset = savestack(L, ci->top.p);
|
||||
ci->func.offset = savestack(L, ci->func.p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Change back all offsets into pointers.
|
||||
*/
|
||||
static void correctstack (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
UpVal *up;
|
||||
L->top.p = restorestack(L, L->top.offset);
|
||||
L->tbclist.p = restorestack(L, L->tbclist.offset);
|
||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||
up->v.p = s2v(restorestack(L, up->v.offset));
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
ci->top.p = restorestack(L, ci->top.offset);
|
||||
ci->func.p = restorestack(L, ci->func.offset);
|
||||
if (isLua(ci))
|
||||
ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */
|
||||
}
|
||||
|
@ -176,44 +198,45 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
|||
/* some space for error handling */
|
||||
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
||||
|
||||
|
||||
/*
|
||||
** Reallocate the stack to a new size, correcting all pointers into
|
||||
** it. (There are pointers to a stack from its upvalues, from its list
|
||||
** of call infos, plus a few individual pointers.) The reallocation is
|
||||
** done in two steps (allocation + free) because the correction must be
|
||||
** done while both addresses (the old stack and the new one) are valid.
|
||||
** (In ISO C, any pointer use after the pointer has been deallocated is
|
||||
** undefined behavior.)
|
||||
** Reallocate the stack to a new size, correcting all pointers into it.
|
||||
** In ISO C, any pointer use after the pointer has been deallocated is
|
||||
** undefined behavior. So, before the reallocation, all pointers are
|
||||
** changed to offsets, and after the reallocation they are changed back
|
||||
** to pointers. As during the reallocation the pointers are invalid, the
|
||||
** reallocation cannot run emergency collections.
|
||||
**
|
||||
** In case of allocation error, raise an error or return false according
|
||||
** to 'raiseerror'.
|
||||
*/
|
||||
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||
int oldsize = stacksize(L);
|
||||
int i;
|
||||
StkId newstack = luaM_reallocvector(L, NULL, 0,
|
||||
newsize + EXTRA_STACK, StackValue);
|
||||
StkId newstack;
|
||||
int oldgcstop = G(L)->gcstopem;
|
||||
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
||||
relstack(L); /* change pointers to offsets */
|
||||
G(L)->gcstopem = 1; /* stop emergency collection */
|
||||
newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK,
|
||||
newsize + EXTRA_STACK, StackValue);
|
||||
G(L)->gcstopem = oldgcstop; /* restore emergency collection */
|
||||
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
|
||||
correctstack(L); /* change offsets back to pointers */
|
||||
if (raiseerror)
|
||||
luaM_error(L);
|
||||
else return 0; /* do not raise an error */
|
||||
}
|
||||
/* number of elements to be copied to the new stack */
|
||||
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK;
|
||||
memcpy(newstack, L->stack, i * sizeof(StackValue));
|
||||
for (; i < newsize + EXTRA_STACK; i++)
|
||||
L->stack.p = newstack;
|
||||
correctstack(L); /* change offsets back to pointers */
|
||||
L->stack_last.p = L->stack.p + newsize;
|
||||
for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(newstack + i)); /* erase new segment */
|
||||
correctstack(L, L->stack, newstack);
|
||||
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
|
||||
L->stack = newstack;
|
||||
L->stack_last = L->stack + newsize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to grow the stack by at least 'n' elements. when 'raiseerror'
|
||||
** Try to grow the stack by at least 'n' elements. When 'raiseerror'
|
||||
** is true, raises any error; otherwise, return 0 in case of errors.
|
||||
*/
|
||||
int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
||||
|
@ -227,35 +250,38 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
|||
luaD_throw(L, LUA_ERRERR); /* error inside message handler */
|
||||
return 0; /* if not 'raiseerror', just signal it */
|
||||
}
|
||||
else {
|
||||
else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
|
||||
int newsize = 2 * size; /* tentative new size */
|
||||
int needed = cast_int(L->top - L->stack) + n;
|
||||
int needed = cast_int(L->top.p - L->stack.p) + n;
|
||||
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
|
||||
newsize = LUAI_MAXSTACK;
|
||||
if (newsize < needed) /* but must respect what was asked for */
|
||||
newsize = needed;
|
||||
if (l_likely(newsize <= LUAI_MAXSTACK))
|
||||
return luaD_reallocstack(L, newsize, raiseerror);
|
||||
else { /* stack overflow */
|
||||
/* add extra size to be able to handle the error message */
|
||||
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
|
||||
if (raiseerror)
|
||||
luaG_runerror(L, "stack overflow");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* else stack overflow */
|
||||
/* add extra size to be able to handle the error message */
|
||||
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
|
||||
if (raiseerror)
|
||||
luaG_runerror(L, "stack overflow");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Compute how much of the stack is being used, by computing the
|
||||
** maximum top of all call frames in the stack and the current top.
|
||||
*/
|
||||
static int stackinuse (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int res;
|
||||
StkId lim = L->top;
|
||||
StkId lim = L->top.p;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
if (lim < ci->top) lim = ci->top;
|
||||
if (lim < ci->top.p) lim = ci->top.p;
|
||||
}
|
||||
lua_assert(lim <= L->stack_last);
|
||||
res = cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
lua_assert(lim <= L->stack_last.p + EXTRA_STACK);
|
||||
res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */
|
||||
if (res < LUA_MINSTACK)
|
||||
res = LUA_MINSTACK; /* ensure a minimum size */
|
||||
return res;
|
||||
|
@ -273,17 +299,13 @@ static int stackinuse (lua_State *L) {
|
|||
*/
|
||||
void luaD_shrinkstack (lua_State *L) {
|
||||
int inuse = stackinuse(L);
|
||||
int nsize = inuse * 2; /* proposed new size */
|
||||
int max = inuse * 3; /* maximum "reasonable" size */
|
||||
if (max > LUAI_MAXSTACK) {
|
||||
max = LUAI_MAXSTACK; /* respect stack limit */
|
||||
if (nsize > LUAI_MAXSTACK)
|
||||
nsize = LUAI_MAXSTACK;
|
||||
}
|
||||
int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3;
|
||||
/* if thread is currently not handling a stack overflow and its
|
||||
size is larger than maximum "reasonable" size, shrink it */
|
||||
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
|
||||
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) {
|
||||
int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2;
|
||||
luaD_reallocstack(L, nsize, 0); /* ok if that fails */
|
||||
}
|
||||
else /* don't change stack */
|
||||
condmovestack(L,{},{}); /* (change only for debugging) */
|
||||
luaE_shrinkCI(L); /* shrink CI list */
|
||||
|
@ -292,7 +314,7 @@ void luaD_shrinkstack (lua_State *L) {
|
|||
|
||||
void luaD_inctop (lua_State *L) {
|
||||
luaD_checkstack(L, 1);
|
||||
L->top++;
|
||||
L->top.p++;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
@ -309,8 +331,8 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||
if (hook && L->allowhook) { /* make sure there is a hook */
|
||||
int mask = CIST_HOOKED;
|
||||
CallInfo *ci = L->ci;
|
||||
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */
|
||||
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */
|
||||
ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */
|
||||
ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */
|
||||
lua_Debug ar;
|
||||
ar.event = event;
|
||||
ar.currentline = line;
|
||||
|
@ -320,11 +342,11 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||
ci->u2.transferinfo.ftransfer = ftransfer;
|
||||
ci->u2.transferinfo.ntransfer = ntransfer;
|
||||
}
|
||||
if (isLua(ci) && L->top < ci->top)
|
||||
L->top = ci->top; /* protect entire activation register */
|
||||
if (isLua(ci) && L->top.p < ci->top.p)
|
||||
L->top.p = ci->top.p; /* protect entire activation register */
|
||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||
if (ci->top < L->top + LUA_MINSTACK)
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
if (ci->top.p < L->top.p + LUA_MINSTACK)
|
||||
ci->top.p = L->top.p + LUA_MINSTACK;
|
||||
L->allowhook = 0; /* cannot call hooks inside a hook */
|
||||
ci->callstatus |= mask;
|
||||
lua_unlock(L);
|
||||
|
@ -332,8 +354,8 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||
lua_lock(L);
|
||||
lua_assert(!L->allowhook);
|
||||
L->allowhook = 1;
|
||||
ci->top = restorestack(L, ci_top);
|
||||
L->top = restorestack(L, top);
|
||||
ci->top.p = restorestack(L, ci_top);
|
||||
L->top.p = restorestack(L, top);
|
||||
ci->callstatus &= ~mask;
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +386,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) {
|
|||
*/
|
||||
static void rethook (lua_State *L, CallInfo *ci, int nres) {
|
||||
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
|
||||
StkId firstres = L->top - nres; /* index of first result */
|
||||
StkId firstres = L->top.p - nres; /* index of first result */
|
||||
int delta = 0; /* correction for vararg functions */
|
||||
int ftransfer;
|
||||
if (isLua(ci)) {
|
||||
|
@ -372,10 +394,10 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
|
|||
if (p->is_vararg)
|
||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||
}
|
||||
ci->func += delta; /* if vararg, back to virtual 'func' */
|
||||
ftransfer = cast(unsigned short, firstres - ci->func);
|
||||
ci->func.p += delta; /* if vararg, back to virtual 'func' */
|
||||
ftransfer = cast(unsigned short, firstres - ci->func.p);
|
||||
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
||||
ci->func -= delta;
|
||||
ci->func.p -= delta;
|
||||
}
|
||||
if (isLua(ci = ci->previous))
|
||||
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
|
||||
|
@ -394,9 +416,9 @@ StkId luaD_tryfuncTM (lua_State *L, StkId func) {
|
|||
tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */
|
||||
if (l_unlikely(ttisnil(tm)))
|
||||
luaG_callerror(L, s2v(func)); /* nothing to call */
|
||||
for (p = L->top; p > func; p--) /* open space for metamethod */
|
||||
for (p = L->top.p; p > func; p--) /* open space for metamethod */
|
||||
setobjs2s(L, p, p-1);
|
||||
L->top++; /* stack space pre-allocated by the caller */
|
||||
L->top.p++; /* stack space pre-allocated by the caller */
|
||||
setobj2s(L, func, tm); /* metamethod is the new function to be called */
|
||||
return func;
|
||||
}
|
||||
|
@ -413,28 +435,29 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
|||
int i;
|
||||
switch (wanted) { /* handle typical cases separately */
|
||||
case 0: /* no values needed */
|
||||
L->top = res;
|
||||
L->top.p = res;
|
||||
return;
|
||||
case 1: /* one value needed */
|
||||
if (nres == 0) /* no results? */
|
||||
setnilvalue(s2v(res)); /* adjust with nil */
|
||||
else /* at least one result */
|
||||
setobjs2s(L, res, L->top - nres); /* move it to proper place */
|
||||
L->top = res + 1;
|
||||
setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
|
||||
L->top.p = res + 1;
|
||||
return;
|
||||
case LUA_MULTRET:
|
||||
wanted = nres; /* we want all results */
|
||||
break;
|
||||
default: /* two/more results and/or to-be-closed variables */
|
||||
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
|
||||
ptrdiff_t savedres = savestack(L, res);
|
||||
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
|
||||
L->ci->u2.nres = nres;
|
||||
luaF_close(L, res, CLOSEKTOP, 1);
|
||||
res = luaF_close(L, res, CLOSEKTOP, 1);
|
||||
L->ci->callstatus &= ~CIST_CLSRET;
|
||||
if (L->hookmask) /* if needed, call hook after '__close's */
|
||||
if (L->hookmask) { /* if needed, call hook after '__close's */
|
||||
ptrdiff_t savedres = savestack(L, res);
|
||||
rethook(L, L->ci, nres);
|
||||
res = restorestack(L, savedres); /* close and hook can move stack */
|
||||
res = restorestack(L, savedres); /* hook can move stack */
|
||||
}
|
||||
wanted = decodeNresults(wanted);
|
||||
if (wanted == LUA_MULTRET)
|
||||
wanted = nres; /* we want all results */
|
||||
|
@ -442,14 +465,14 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
|||
break;
|
||||
}
|
||||
/* generic case */
|
||||
firstresult = L->top - nres; /* index of first result */
|
||||
firstresult = L->top.p - nres; /* index of first result */
|
||||
if (nres > wanted) /* extra results? */
|
||||
nres = wanted; /* don't need them */
|
||||
for (i = 0; i < nres; i++) /* move all results to correct place */
|
||||
setobjs2s(L, res + i, firstresult + i);
|
||||
for (; i < wanted; i++) /* complete wanted number of results */
|
||||
setnilvalue(s2v(res + i));
|
||||
L->top = res + wanted; /* top points after the last result */
|
||||
L->top.p = res + wanted; /* top points after the last result */
|
||||
}
|
||||
|
||||
|
||||
|
@ -464,7 +487,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
|||
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
|
||||
rethook(L, ci, nres);
|
||||
/* move results to proper place */
|
||||
moveresults(L, ci->func, nres, wanted);
|
||||
moveresults(L, ci->func.p, nres, wanted);
|
||||
/* function cannot be in any of these cases when returning */
|
||||
lua_assert(!(ci->callstatus &
|
||||
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
|
||||
|
@ -479,10 +502,10 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
|||
l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,
|
||||
int mask, StkId top) {
|
||||
CallInfo *ci = L->ci = next_ci(L); /* new frame */
|
||||
ci->func = func;
|
||||
ci->func.p = func;
|
||||
ci->nresults = nret;
|
||||
ci->callstatus = mask;
|
||||
ci->top = top;
|
||||
ci->top.p = top;
|
||||
return ci;
|
||||
}
|
||||
|
||||
|
@ -496,10 +519,10 @@ l_sinline int precallC (lua_State *L, StkId func, int nresults,
|
|||
CallInfo *ci;
|
||||
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
L->ci = ci = prepCallInfo(L, func, nresults, CIST_C,
|
||||
L->top + LUA_MINSTACK);
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
L->top.p + LUA_MINSTACK);
|
||||
lua_assert(ci->top.p <= L->stack_last.p);
|
||||
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
|
||||
int narg = cast_int(L->top - func) - 1;
|
||||
int narg = cast_int(L->top.p - func) - 1;
|
||||
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
|
||||
}
|
||||
lua_unlock(L);
|
||||
|
@ -531,17 +554,17 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
|||
int nfixparams = p->numparams;
|
||||
int i;
|
||||
checkstackGCp(L, fsize - delta, func);
|
||||
ci->func -= delta; /* restore 'func' (if vararg) */
|
||||
ci->func.p -= delta; /* restore 'func' (if vararg) */
|
||||
for (i = 0; i < narg1; i++) /* move down function and arguments */
|
||||
setobjs2s(L, ci->func + i, func + i);
|
||||
func = ci->func; /* moved-down function */
|
||||
setobjs2s(L, ci->func.p + i, func + i);
|
||||
func = ci->func.p; /* moved-down function */
|
||||
for (; narg1 <= nfixparams; narg1++)
|
||||
setnilvalue(s2v(func + narg1)); /* complete missing arguments */
|
||||
ci->top = func + 1 + fsize; /* top for new function */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
ci->top.p = func + 1 + fsize; /* top for new function */
|
||||
lua_assert(ci->top.p <= L->stack_last.p);
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus |= CIST_TAIL;
|
||||
L->top = func + narg1; /* set top */
|
||||
L->top.p = func + narg1; /* set top */
|
||||
return -1;
|
||||
}
|
||||
default: { /* not a function */
|
||||
|
@ -574,15 +597,15 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
|||
case LUA_VLCL: { /* Lua function */
|
||||
CallInfo *ci;
|
||||
Proto *p = clLvalue(s2v(func))->p;
|
||||
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
|
||||
int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */
|
||||
int nfixparams = p->numparams;
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
checkstackGCp(L, fsize, func);
|
||||
L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize);
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
for (; narg < nfixparams; narg++)
|
||||
setnilvalue(s2v(L->top++)); /* complete missing arguments */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
setnilvalue(s2v(L->top.p++)); /* complete missing arguments */
|
||||
lua_assert(ci->top.p <= L->stack_last.p);
|
||||
return ci;
|
||||
}
|
||||
default: { /* not a function */
|
||||
|
@ -598,12 +621,17 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
|||
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
|
||||
** number of recursive invocations in the C stack) or nyci (the same
|
||||
** plus increment number of non-yieldable calls).
|
||||
** This function can be called with some use of EXTRA_STACK, so it should
|
||||
** check the stack before doing anything else. 'luaD_precall' already
|
||||
** does that.
|
||||
*/
|
||||
l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) {
|
||||
l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) {
|
||||
CallInfo *ci;
|
||||
L->nCcalls += inc;
|
||||
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) {
|
||||
checkstackp(L, 0, func); /* free any use of EXTRA_STACK */
|
||||
luaE_checkcstack(L);
|
||||
}
|
||||
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
|
||||
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
|
||||
luaV_execute(L, ci); /* call it */
|
||||
|
@ -651,8 +679,7 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
|
|||
else { /* error */
|
||||
StkId func = restorestack(L, ci->u2.funcidx);
|
||||
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
|
||||
luaF_close(L, func, status, 1); /* can yield or raise an error */
|
||||
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
|
||||
func = luaF_close(L, func, status, 1); /* can yield or raise an error */
|
||||
luaD_seterrorobj(L, status, func);
|
||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||
setcistrecst(ci, LUA_OK); /* clear original status */
|
||||
|
@ -740,8 +767,8 @@ static CallInfo *findpcall (lua_State *L) {
|
|||
** coroutine error handler and should not kill the coroutine.)
|
||||
*/
|
||||
static int resume_error (lua_State *L, const char *msg, int narg) {
|
||||
L->top -= narg; /* remove args from the stack */
|
||||
setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */
|
||||
L->top.p -= narg; /* remove args from the stack */
|
||||
setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return LUA_ERRRUN;
|
||||
|
@ -757,7 +784,7 @@ static int resume_error (lua_State *L, const char *msg, int narg) {
|
|||
*/
|
||||
static void resume (lua_State *L, void *ud) {
|
||||
int n = *(cast(int*, ud)); /* number of arguments */
|
||||
StkId firstArg = L->top - n; /* first argument */
|
||||
StkId firstArg = L->top.p - n; /* first argument */
|
||||
CallInfo *ci = L->ci;
|
||||
if (L->status == LUA_OK) /* starting a coroutine? */
|
||||
ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */
|
||||
|
@ -765,7 +792,7 @@ static void resume (lua_State *L, void *ud) {
|
|||
lua_assert(L->status == LUA_YIELD);
|
||||
L->status = LUA_OK; /* mark that it is running (again) */
|
||||
if (isLua(ci)) { /* yielded inside a hook? */
|
||||
L->top = firstArg; /* discard arguments */
|
||||
L->top.p = firstArg; /* discard arguments */
|
||||
luaV_execute(L, ci); /* just continue running Lua code */
|
||||
}
|
||||
else { /* 'common' yield */
|
||||
|
@ -808,7 +835,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
|||
if (L->status == LUA_OK) { /* may be starting a coroutine */
|
||||
if (L->ci != &L->base_ci) /* not in base level? */
|
||||
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
|
||||
else if (L->top - (L->ci->func + 1) == nargs) /* no function? */
|
||||
else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
}
|
||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||
|
@ -826,11 +853,11 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
|||
lua_assert(status == L->status); /* normal end or yield */
|
||||
else { /* unrecoverable error */
|
||||
L->status = cast_byte(status); /* mark thread as 'dead' */
|
||||
luaD_seterrorobj(L, status, L->top); /* push error message */
|
||||
L->ci->top = L->top;
|
||||
luaD_seterrorobj(L, status, L->top.p); /* push error message */
|
||||
L->ci->top.p = L->top.p;
|
||||
}
|
||||
*nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
|
||||
: cast_int(L->top - (L->ci->func + 1));
|
||||
: cast_int(L->top.p - (L->ci->func.p + 1));
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
@ -985,7 +1012,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
|||
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
|
||||
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
|
||||
luaZ_initbuffer(L, &p.buff);
|
||||
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
|
||||
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc);
|
||||
luaZ_freebuffer(L, &p.buff);
|
||||
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
|
||||
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define ldo_h
|
||||
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lzio.h"
|
||||
|
@ -23,7 +24,7 @@
|
|||
** at every check.
|
||||
*/
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (l_unlikely(L->stack_last - L->top <= (n))) \
|
||||
if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
|
||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||
else { condmovestack(L,pre,pos); }
|
||||
|
||||
|
@ -32,11 +33,18 @@
|
|||
|
||||
|
||||
|
||||
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
|
||||
#define restorestack(L,n) ((StkId)((char *)L->stack + (n)))
|
||||
#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
|
||||
#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
|
||||
|
||||
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
|
||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
||||
|
||||
|
||||
/* macro to check stack size and GC, preserving 'p' */
|
||||
#define checkstackGCp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
|
@ -58,7 +66,8 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
|||
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
|
||||
int fTransfer, int nTransfer);
|
||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta);
|
||||
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
||||
int narg1, int delta);
|
||||
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
@ -55,8 +56,11 @@ static void dumpByte (DumpState *D, int y) {
|
|||
}
|
||||
|
||||
|
||||
/* dumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
/*
|
||||
** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
|
||||
** rounds up the division.)
|
||||
*/
|
||||
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
|
||||
|
||||
static void dumpSize (DumpState *D, size_t x) {
|
||||
lu_byte buff[DIBS];
|
||||
|
|
|
@ -50,8 +50,8 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
|||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
uv->v.p = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v.p);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, uv);
|
||||
}
|
||||
|
@ -62,12 +62,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
|||
** Create a new upvalue at the given level, and link it to the list of
|
||||
** open upvalues of 'L' after entry 'prev'.
|
||||
**/
|
||||
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
|
||||
static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
UpVal *next = *prev;
|
||||
uv->v = s2v(level); /* current value lives in the stack */
|
||||
uv->tbc = tbc;
|
||||
uv->v.p = s2v(level); /* current value lives in the stack */
|
||||
uv->u.open.next = next; /* link it to list of open upvalues */
|
||||
uv->u.open.previous = prev;
|
||||
if (next)
|
||||
|
@ -96,7 +95,7 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
|||
pp = &p->u.open.next;
|
||||
}
|
||||
/* not found: create a new upvalue after 'pp' */
|
||||
return newupval(L, 0, level, pp);
|
||||
return newupval(L, level, pp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,12 +105,12 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
|||
** (This function assumes EXTRA_STACK.)
|
||||
*/
|
||||
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||
StkId top = L->top;
|
||||
StkId top = L->top.p;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
setobj2s(L, top, tm); /* will call metamethod... */
|
||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
||||
L->top = top + 3; /* add function and arguments */
|
||||
L->top.p = top + 3; /* add function and arguments */
|
||||
if (yy)
|
||||
luaD_call(L, top, 0);
|
||||
else
|
||||
|
@ -126,7 +125,7 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
|||
static void checkclosemth (lua_State *L, StkId level) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
|
||||
if (ttisnil(tm)) { /* no metamethod? */
|
||||
int idx = cast_int(level - L->ci->func); /* variable index */
|
||||
int idx = cast_int(level - L->ci->func.p); /* variable index */
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
|
||||
|
@ -160,23 +159,23 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
|||
** is used.)
|
||||
*/
|
||||
#define MAXDELTA \
|
||||
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
|
||||
((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Insert a variable in the list of to-be-closed variables.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
lua_assert(level > L->tbclist);
|
||||
lua_assert(level > L->tbclist.p);
|
||||
if (l_isfalse(s2v(level)))
|
||||
return; /* false doesn't need to be closed */
|
||||
checkclosemth(L, level); /* value must have a close method */
|
||||
while (cast_uint(level - L->tbclist) > MAXDELTA) {
|
||||
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist->tbclist.delta = 0;
|
||||
while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
|
||||
L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist.p->tbclist.delta = 0;
|
||||
}
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
|
||||
L->tbclist = level;
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
|
||||
L->tbclist.p = level;
|
||||
}
|
||||
|
||||
|
||||
|
@ -196,10 +195,10 @@ void luaF_closeupval (lua_State *L, StkId level) {
|
|||
StkId upl; /* stack index pointed by 'uv' */
|
||||
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
lua_assert(uplevel(uv) < L->top.p);
|
||||
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
setobj(L, slot, uv->v.p); /* move value to upvalue slot */
|
||||
uv->v.p = slot; /* now current value lives here */
|
||||
if (!iswhite(uv)) { /* neither white nor dead? */
|
||||
nw2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
|
@ -209,31 +208,32 @@ void luaF_closeupval (lua_State *L, StkId level) {
|
|||
|
||||
|
||||
/*
|
||||
** Remove firt element from the tbclist plus its dummy nodes.
|
||||
** Remove first element from the tbclist plus its dummy nodes.
|
||||
*/
|
||||
static void poptbclist (lua_State *L) {
|
||||
StkId tbc = L->tbclist;
|
||||
StkId tbc = L->tbclist.p;
|
||||
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
|
||||
tbc -= tbc->tbclist.delta;
|
||||
while (tbc > L->stack && tbc->tbclist.delta == 0)
|
||||
while (tbc > L->stack.p && tbc->tbclist.delta == 0)
|
||||
tbc -= MAXDELTA; /* remove dummy nodes */
|
||||
L->tbclist = tbc;
|
||||
L->tbclist.p = tbc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Close all upvalues and to-be-closed variables up to the given stack
|
||||
** level.
|
||||
** level. Return restored 'level'.
|
||||
*/
|
||||
void luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||
StkId luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
luaF_closeupval(L, level); /* first, close the upvalues */
|
||||
while (L->tbclist >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist; /* get variable index */
|
||||
while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist.p; /* get variable index */
|
||||
poptbclist(L); /* remove it from list */
|
||||
prepcallclosemth(L, tbc, status, yy); /* close variable */
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
#define MAXUPVAL 255
|
||||
|
||||
|
||||
#define upisopen(up) ((up)->v != &(up)->u.value)
|
||||
#define upisopen(up) ((up)->v.p != &(up)->u.value)
|
||||
|
||||
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v))
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
|
||||
|
||||
|
||||
/*
|
||||
|
@ -54,7 +54,7 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
|||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||
LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
|
|
106
src/lua/lgc.c
106
src/lua/lgc.c
|
@ -252,12 +252,13 @@ void luaC_fix (lua_State *L, GCObject *o) {
|
|||
|
||||
|
||||
/*
|
||||
** create a new collectable object (with given type and size) and link
|
||||
** it to 'allgc' list.
|
||||
** create a new collectable object (with given type, size, and offset)
|
||||
** and link it to 'allgc' list.
|
||||
*/
|
||||
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
|
||||
global_State *g = G(L);
|
||||
GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz));
|
||||
char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
|
||||
GCObject *o = cast(GCObject *, p + offset);
|
||||
o->marked = luaC_white(g);
|
||||
o->tt = tt;
|
||||
o->next = g->allgc;
|
||||
|
@ -265,6 +266,11 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
|||
return o;
|
||||
}
|
||||
|
||||
|
||||
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
return luaC_newobjdt(L, tt, sz, 0);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
@ -301,7 +307,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
|
|||
set2gray(uv); /* open upvalues are kept gray */
|
||||
else
|
||||
set2black(uv); /* closed upvalues are visited here */
|
||||
markvalue(g, uv->v); /* mark its content */
|
||||
markvalue(g, uv->v.p); /* mark its content */
|
||||
break;
|
||||
}
|
||||
case LUA_VUSERDATA: {
|
||||
|
@ -376,7 +382,7 @@ static int remarkupvals (global_State *g) {
|
|||
work++;
|
||||
if (!iswhite(uv)) { /* upvalue already visited? */
|
||||
lua_assert(upisopen(uv) && isgray(uv));
|
||||
markvalue(g, uv->v); /* mark its value */
|
||||
markvalue(g, uv->v.p); /* mark its value */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -620,19 +626,19 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
|
|||
*/
|
||||
static int traversethread (global_State *g, lua_State *th) {
|
||||
UpVal *uv;
|
||||
StkId o = th->stack;
|
||||
StkId o = th->stack.p;
|
||||
if (isold(th) || g->gcstate == GCSpropagate)
|
||||
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
|
||||
if (o == NULL)
|
||||
return 1; /* stack not completely built yet */
|
||||
lua_assert(g->gcstate == GCSatomic ||
|
||||
th->openupval == NULL || isintwups(th));
|
||||
for (; o < th->top; o++) /* mark live elements in the stack */
|
||||
for (; o < th->top.p; o++) /* mark live elements in the stack */
|
||||
markvalue(g, s2v(o));
|
||||
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
|
||||
markobject(g, uv); /* open upvalues cannot be collected */
|
||||
if (g->gcstate == GCSatomic) { /* final traversal? */
|
||||
for (; o < th->stack_last + EXTRA_STACK; o++)
|
||||
for (; o < th->stack_last.p + EXTRA_STACK; o++)
|
||||
setnilvalue(s2v(o)); /* clear dead stack slice */
|
||||
/* 'remarkupvals' may have removed thread from 'twups' list */
|
||||
if (!isintwups(th) && th->openupval != NULL) {
|
||||
|
@ -892,7 +898,7 @@ static GCObject *udata2finalize (global_State *g) {
|
|||
|
||||
static void dothecall (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaD_callnoyield(L, L->top - 2, 0);
|
||||
luaD_callnoyield(L, L->top.p - 2, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -909,16 +915,16 @@ static void GCTM (lua_State *L) {
|
|||
int oldgcstp = g->gcstp;
|
||||
g->gcstp |= GCSTPGC; /* avoid GC steps */
|
||||
L->allowhook = 0; /* stop debug hooks during GC metamethod */
|
||||
setobj2s(L, L->top++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top++, &v); /* ... and its argument */
|
||||
setobj2s(L, L->top.p++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top.p++, &v); /* ... and its argument */
|
||||
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
|
||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||
L->allowhook = oldah; /* restore hooks */
|
||||
g->gcstp = oldgcstp; /* restore state */
|
||||
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc");
|
||||
L->top--; /* pops error object */
|
||||
L->top.p--; /* pops error object */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1041,7 +1047,25 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
|||
** =======================================================
|
||||
*/
|
||||
|
||||
static void setpause (global_State *g);
|
||||
|
||||
/*
|
||||
** Set the "time" to wait before starting a new GC cycle; cycle will
|
||||
** start when memory use hits the threshold of ('estimate' * pause /
|
||||
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
|
||||
** because Lua cannot even start with less than PAUSEADJ bytes).
|
||||
*/
|
||||
static void setpause (global_State *g) {
|
||||
l_mem threshold, debt;
|
||||
int pause = getgcparam(g->gcpause);
|
||||
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
|
||||
lua_assert(estimate > 0);
|
||||
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
|
||||
? estimate * pause /* no overflow */
|
||||
: MAX_LMEM; /* overflow; truncate to maximum */
|
||||
debt = gettotalbytes(g) - threshold;
|
||||
if (debt > 0) debt = 0;
|
||||
luaE_setdebt(g, debt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1285,6 +1309,15 @@ static void atomic2gen (lua_State *L, global_State *g) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set debt for the next minor collection, which will happen when
|
||||
** memory grows 'genminormul'%.
|
||||
*/
|
||||
static void setminordebt (global_State *g) {
|
||||
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enter generational mode. Must go until the end of an atomic cycle
|
||||
** to ensure that all objects are correctly marked and weak tables
|
||||
|
@ -1297,6 +1330,7 @@ static lu_mem entergen (lua_State *L, global_State *g) {
|
|||
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
|
||||
numobjs = atomic(L); /* propagates all and then do the atomic stuff */
|
||||
atomic2gen(L, g);
|
||||
setminordebt(g); /* set debt assuming next cycle will be minor */
|
||||
return numobjs;
|
||||
}
|
||||
|
||||
|
@ -1342,15 +1376,6 @@ static lu_mem fullgen (lua_State *L, global_State *g) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set debt for the next minor collection, which will happen when
|
||||
** memory grows 'genminormul'%.
|
||||
*/
|
||||
static void setminordebt (global_State *g) {
|
||||
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Does a major collection after last collection was a "bad collection".
|
||||
**
|
||||
|
@ -1422,8 +1447,8 @@ static void genstep (lua_State *L, global_State *g) {
|
|||
lu_mem numobjs = fullgen(L, g); /* do a major collection */
|
||||
if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
|
||||
/* collected at least half of memory growth since last major
|
||||
collection; keep doing minor collections */
|
||||
setminordebt(g);
|
||||
collection; keep doing minor collections. */
|
||||
lua_assert(g->lastatomic == 0);
|
||||
}
|
||||
else { /* bad collection */
|
||||
g->lastatomic = numobjs; /* signal that last collection was bad */
|
||||
|
@ -1449,26 +1474,6 @@ static void genstep (lua_State *L, global_State *g) {
|
|||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Set the "time" to wait before starting a new GC cycle; cycle will
|
||||
** start when memory use hits the threshold of ('estimate' * pause /
|
||||
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
|
||||
** because Lua cannot even start with less than PAUSEADJ bytes).
|
||||
*/
|
||||
static void setpause (global_State *g) {
|
||||
l_mem threshold, debt;
|
||||
int pause = getgcparam(g->gcpause);
|
||||
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
|
||||
lua_assert(estimate > 0);
|
||||
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
|
||||
? estimate * pause /* no overflow */
|
||||
: MAX_LMEM; /* overflow; truncate to maximum */
|
||||
debt = gettotalbytes(g) - threshold;
|
||||
if (debt > 0) debt = 0;
|
||||
luaE_setdebt(g, debt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enter first sweep phase.
|
||||
** The call to 'sweeptolive' makes the pointer point to an object
|
||||
|
@ -1676,12 +1681,15 @@ static void incstep (lua_State *L, global_State *g) {
|
|||
}
|
||||
|
||||
/*
|
||||
** performs a basic GC step if collector is running
|
||||
** Performs a basic GC step if collector is running. (If collector is
|
||||
** not running, set a reasonable debt to avoid it being called at
|
||||
** every single check.)
|
||||
*/
|
||||
void luaC_step (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(!g->gcemergency);
|
||||
if (gcrunning(g)) { /* running? */
|
||||
if (!gcrunning(g)) /* not running? */
|
||||
luaE_setdebt(g, -2000);
|
||||
else {
|
||||
if(isdecGCmodegen(g))
|
||||
genstep(L, g);
|
||||
else
|
||||
|
|
|
@ -172,24 +172,27 @@
|
|||
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrier(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? \
|
||||
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrierback(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
|
||||
|
||||
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
|
||||
LUAI_FUNC void luaC_step (lua_State *L);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
|
||||
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
|
||||
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
|
||||
size_t offset);
|
||||
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
|
||||
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
|
||||
|
|
|
@ -128,7 +128,7 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
|||
** ensuring there is only one copy of each unique string. The table
|
||||
** here is used as a set: the string enters as the key, while its value
|
||||
** is irrelevant. We use the string itself as the value only because it
|
||||
** is a TValue readly available. Later, the code generation can change
|
||||
** is a TValue readily available. Later, the code generation can change
|
||||
** this value.
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
|
@ -138,12 +138,12 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
|||
if (!ttisnil(o)) /* string already present? */
|
||||
ts = keystrval(nodefromval(o)); /* get saved copy */
|
||||
else { /* not in use yet */
|
||||
TValue *stv = s2v(L->top++); /* reserve stack space for string */
|
||||
TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
|
||||
setsvalue(L, stv, ts); /* temporarily anchor the string */
|
||||
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
|
||||
/* table is not a metatable, so it does not need to invalidate cache */
|
||||
luaC_checkGC(L);
|
||||
L->top--; /* remove string from stack */
|
||||
L->top.p--; /* remove string from stack */
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
|
|
@ -71,11 +71,24 @@ typedef signed char ls_byte;
|
|||
|
||||
|
||||
/*
|
||||
** conversion of pointer to unsigned integer:
|
||||
** this is for hashing only; there is no problem if the integer
|
||||
** cannot hold the whole pointer value
|
||||
** conversion of pointer to unsigned integer: this is for hashing only;
|
||||
** there is no problem if the integer cannot hold the whole pointer
|
||||
** value. (In strict ISO C this may cause undefined behavior, but no
|
||||
** actual machine seems to bother.)
|
||||
*/
|
||||
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
|
||||
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
|
||||
#define L_P2I uintptr_t
|
||||
#else /* no 'intptr'? */
|
||||
#define L_P2I uintmax_t /* use the largest available integer */
|
||||
#endif
|
||||
#else /* C89 option */
|
||||
#define L_P2I size_t
|
||||
#endif
|
||||
|
||||
#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -267,7 +267,7 @@ static int math_type (lua_State *L) {
|
|||
|
||||
/* try to find an integer type with at least 64 bits */
|
||||
|
||||
#if (ULONG_MAX >> 31 >> 31) >= 3
|
||||
#if ((ULONG_MAX >> 31) >> 31) >= 3
|
||||
|
||||
/* 'long' has at least 64 bits */
|
||||
#define Rand64 unsigned long
|
||||
|
@ -277,9 +277,9 @@ static int math_type (lua_State *L) {
|
|||
/* there is a 'long long' type (which must have at least 64 bits) */
|
||||
#define Rand64 unsigned long long
|
||||
|
||||
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
|
||||
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
|
||||
|
||||
/* 'lua_Integer' has at least 64 bits */
|
||||
/* 'lua_Unsigned' has at least 64 bits */
|
||||
#define Rand64 lua_Unsigned
|
||||
|
||||
#endif
|
||||
|
@ -500,12 +500,12 @@ static lua_Number I2d (Rand64 x) {
|
|||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
static lua_Unsigned I2UInt (Rand64 x) {
|
||||
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
|
||||
return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
|
||||
}
|
||||
|
||||
/* convert a 'lua_Unsigned' to a 'Rand64' */
|
||||
static Rand64 Int2I (lua_Unsigned n) {
|
||||
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
|
||||
return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
|
||||
}
|
||||
|
||||
#endif /* } */
|
||||
|
|
|
@ -22,25 +22,6 @@
|
|||
#include "lstate.h"
|
||||
|
||||
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** First allocation will fail whenever not building initial state.
|
||||
** (This fail will trigger 'tryagain' and a full GC cycle at every
|
||||
** allocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (completestate(g) && ns > 0) /* frees never fail */
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return (*g->frealloc)(g->ud, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
|
@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
|||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Macro to call the allocation function.
|
||||
*/
|
||||
#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
|
||||
|
||||
/*
|
||||
** When an allocation fails, it will try again after an emergency
|
||||
** collection, except when it cannot run a collection. The GC should
|
||||
** not be called while the state is not fully built, as the collector
|
||||
** is not yet fully initialized. Also, it should not be called when
|
||||
** 'gcstopem' is true, because then the interpreter is in the middle of
|
||||
** a collection step.
|
||||
*/
|
||||
#define cantryagain(g) (completestate(g) && !g->gcstopem)
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** First allocation will fail except when freeing a block (frees never
|
||||
** fail) and when it cannot try again; this fail will trigger 'tryagain'
|
||||
** and a full GC cycle at every allocation.
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ns > 0 && cantryagain(g))
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return callfrealloc(g, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
@ -132,7 +150,7 @@ l_noret luaM_toobig (lua_State *L) {
|
|||
void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
(*g->frealloc)(g->ud, block, osize, 0);
|
||||
callfrealloc(g, block, osize, 0);
|
||||
g->GCdebt -= osize;
|
||||
}
|
||||
|
||||
|
@ -140,19 +158,15 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
|||
/*
|
||||
** In case of allocation fail, this function will do an emergency
|
||||
** collection to free some memory and then try the allocation again.
|
||||
** The GC should not be called while state is not fully built, as the
|
||||
** collector is not yet fully initialized. Also, it should not be called
|
||||
** when 'gcstopem' is true, because then the interpreter is in the
|
||||
** middle of a collection step.
|
||||
*/
|
||||
static void *tryagain (lua_State *L, void *block,
|
||||
size_t osize, size_t nsize) {
|
||||
global_State *g = G(L);
|
||||
if (completestate(g) && !g->gcstopem) {
|
||||
if (cantryagain(g)) {
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||
return callfrealloc(g, block, osize, nsize); /* try again */
|
||||
}
|
||||
else return NULL; /* cannot free any memory without a full state */
|
||||
else return NULL; /* cannot run an emergency collection */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -708,8 +708,13 @@ static const luaL_Reg ll_funcs[] = {
|
|||
|
||||
|
||||
static void createsearcherstable (lua_State *L) {
|
||||
static const lua_CFunction searchers[] =
|
||||
{searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
|
||||
static const lua_CFunction searchers[] = {
|
||||
searcher_preload,
|
||||
searcher_Lua,
|
||||
searcher_C,
|
||||
searcher_Croot,
|
||||
NULL
|
||||
};
|
||||
int i;
|
||||
/* create 'searchers' table */
|
||||
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
|
||||
|
|
|
@ -62,7 +62,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
|
|||
case LUA_OPBOR: return intop(|, v1, v2);
|
||||
case LUA_OPBXOR: return intop(^, v1, v2);
|
||||
case LUA_OPSHL: return luaV_shiftl(v1, v2);
|
||||
case LUA_OPSHR: return luaV_shiftl(v1, -v2);
|
||||
case LUA_OPSHR: return luaV_shiftr(v1, v2);
|
||||
case LUA_OPUNM: return intop(-, 0, v1);
|
||||
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
|
||||
default: lua_assert(0); return 0;
|
||||
|
@ -386,29 +386,39 @@ void luaO_tostring (lua_State *L, TValue *obj) {
|
|||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* size for buffer space used by 'luaO_pushvfstring' */
|
||||
#define BUFVFS 200
|
||||
/*
|
||||
** Size for buffer space used by 'luaO_pushvfstring'. It should be
|
||||
** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
|
||||
** so that 'luaG_addinfo' can work directly on the buffer.
|
||||
*/
|
||||
#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95)
|
||||
|
||||
/* buffer used by 'luaO_pushvfstring' */
|
||||
typedef struct BuffFS {
|
||||
lua_State *L;
|
||||
int pushed; /* number of string pieces already on the stack */
|
||||
int pushed; /* true if there is a part of the result on the stack */
|
||||
int blen; /* length of partial string in 'space' */
|
||||
char space[BUFVFS]; /* holds last part of the result */
|
||||
} BuffFS;
|
||||
|
||||
|
||||
/*
|
||||
** Push given string to the stack, as part of the buffer, and
|
||||
** join the partial strings in the stack into one.
|
||||
** Push given string to the stack, as part of the result, and
|
||||
** join it to previous partial result if there is one.
|
||||
** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
|
||||
** This call cannot invoke metamethods, as both operands must be
|
||||
** strings. It can, however, raise an error if the result is too
|
||||
** long. In that case, 'luaV_concat' frees the extra slot before
|
||||
** raising the error.
|
||||
*/
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
|
||||
lua_State *L = buff->L;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
||||
L->top++; /* may use one extra slot */
|
||||
buff->pushed++;
|
||||
luaV_concat(L, buff->pushed); /* join partial results into one */
|
||||
buff->pushed = 1;
|
||||
setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
|
||||
L->top.p++; /* may use one slot from EXTRA_STACK */
|
||||
if (!buff->pushed) /* no previous string on the stack? */
|
||||
buff->pushed = 1; /* now there is one */
|
||||
else /* join previous string with new one */
|
||||
luaV_concat(L, 2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -454,7 +464,7 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
|||
|
||||
|
||||
/*
|
||||
** Add a number to the buffer.
|
||||
** Add a numeral to the buffer.
|
||||
*/
|
||||
static void addnum2buff (BuffFS *buff, TValue *num) {
|
||||
char *numbuff = getbuff(buff, MAXNUMBER2STR);
|
||||
|
@ -532,7 +542,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
|||
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
||||
clearbuff(&buff); /* empty buffer into the stack */
|
||||
lua_assert(buff.pushed == 1);
|
||||
return svalue(s2v(L->top - 1));
|
||||
return svalue(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ typedef union Value {
|
|||
lua_CFunction f; /* light C functions */
|
||||
lua_Integer i; /* integer numbers */
|
||||
lua_Number n; /* float numbers */
|
||||
/* not used, but may avoid warnings for uninitialized value */
|
||||
lu_byte ub;
|
||||
} Value;
|
||||
|
||||
|
||||
|
@ -155,6 +157,17 @@ typedef union StackValue {
|
|||
/* index to stack elements */
|
||||
typedef StackValue *StkId;
|
||||
|
||||
|
||||
/*
|
||||
** When reallocating the stack, change all pointers to the stack into
|
||||
** proper offsets.
|
||||
*/
|
||||
typedef union {
|
||||
StkId p; /* actual pointer */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} StkIdRel;
|
||||
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
|
||||
|
@ -615,8 +628,10 @@ typedef struct Proto {
|
|||
*/
|
||||
typedef struct UpVal {
|
||||
CommonHeader;
|
||||
lu_byte tbc; /* true if it represents a to-be-closed variable */
|
||||
TValue *v; /* points to stack or to its own value */
|
||||
union {
|
||||
TValue *p; /* points to stack or to its own value */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} v;
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
struct UpVal *next; /* linked list */
|
||||
|
|
|
@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) |
|
|||
iABx Bx(17) | A(8) | Op(7) |
|
||||
iAsBx sBx (signed)(17) | A(8) | Op(7) |
|
||||
iAx Ax(25) | Op(7) |
|
||||
isJ sJ(25) | Op(7) |
|
||||
isJ sJ (signed)(25) | Op(7) |
|
||||
|
||||
A signed argument is represented in excess K: the represented value is
|
||||
the written unsigned value minus K, where K is half the maximum for the
|
||||
|
|
|
@ -30,23 +30,14 @@
|
|||
*/
|
||||
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
|
||||
|
||||
/* options for ANSI C 89 (only 1-char options) */
|
||||
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
|
||||
/* options for ISO C 99 and POSIX */
|
||||
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
|
||||
/* options for Windows */
|
||||
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
|
||||
#if defined(LUA_USE_WINDOWS)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
|
||||
#elif defined(LUA_USE_C89)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
#else /* C99 specification */
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
@ -138,12 +129,21 @@
|
|||
/* }================================================================== */
|
||||
|
||||
|
||||
#if !defined(l_system)
|
||||
#if defined(LUA_USE_IOS)
|
||||
/* Despite claiming to be ISO C, iOS does not implement 'system'. */
|
||||
#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
|
||||
#else
|
||||
#define l_system(cmd) system(cmd) /* default definition */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static int os_execute (lua_State *L) {
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat;
|
||||
errno = 0;
|
||||
stat = system(cmd);
|
||||
stat = l_system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
|
@ -260,9 +260,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
|
|||
res = d;
|
||||
}
|
||||
else {
|
||||
/* unsigned avoids overflow when lua_Integer has 32 bits */
|
||||
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
|
||||
: (lua_Integer)INT_MIN + delta <= res))
|
||||
if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
|
|
|
@ -468,6 +468,7 @@ static void singlevar (LexState *ls, expdesc *var) {
|
|||
expdesc key;
|
||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||
lua_assert(var->k != VVOID); /* this one must exist */
|
||||
luaK_exp2anyregup(fs, var); /* but could be a constant */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
luaK_indexed(fs, var, &key); /* env[varname] */
|
||||
}
|
||||
|
@ -520,12 +521,12 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
|
|||
|
||||
/*
|
||||
** Solves the goto at index 'g' to given 'label' and removes it
|
||||
** from the list of pending goto's.
|
||||
** from the list of pending gotos.
|
||||
** If it jumps into the scope of some variable, raises an error.
|
||||
*/
|
||||
static void solvegoto (LexState *ls, int g, Labeldesc *label) {
|
||||
int i;
|
||||
Labellist *gl = &ls->dyd->gt; /* list of goto's */
|
||||
Labellist *gl = &ls->dyd->gt; /* list of gotos */
|
||||
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
|
||||
lua_assert(eqstr(gt->name, label->name));
|
||||
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
|
||||
|
@ -579,7 +580,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
|
|||
/*
|
||||
** Solves forward jumps. Check whether new label 'lb' matches any
|
||||
** pending gotos in current block and solves them. Return true
|
||||
** if any of the goto's need to close upvalues.
|
||||
** if any of the gotos need to close upvalues.
|
||||
*/
|
||||
static int solvegotos (LexState *ls, Labeldesc *lb) {
|
||||
Labellist *gl = &ls->dyd->gt;
|
||||
|
@ -600,7 +601,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) {
|
|||
/*
|
||||
** Create a new label with the given 'name' at the given 'line'.
|
||||
** 'last' tells whether label is the last non-op statement in its
|
||||
** block. Solves all pending goto's to this new label and adds
|
||||
** block. Solves all pending gotos to this new label and adds
|
||||
** a close instruction if necessary.
|
||||
** Returns true iff it added a close instruction.
|
||||
*/
|
||||
|
@ -673,19 +674,19 @@ static void leaveblock (FuncState *fs) {
|
|||
LexState *ls = fs->ls;
|
||||
int hasclose = 0;
|
||||
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
|
||||
if (bl->isloop) /* fix pending breaks? */
|
||||
removevars(fs, bl->nactvar); /* remove block locals */
|
||||
lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
|
||||
if (bl->isloop) /* has to fix pending breaks? */
|
||||
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
|
||||
if (!hasclose && bl->previous && bl->upval)
|
||||
if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
|
||||
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
|
||||
fs->bl = bl->previous;
|
||||
removevars(fs, bl->nactvar);
|
||||
lua_assert(bl->nactvar == fs->nactvar);
|
||||
fs->freereg = stklevel; /* free registers */
|
||||
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
|
||||
if (bl->previous) /* inner block? */
|
||||
movegotosout(fs, bl); /* update pending gotos to outer block */
|
||||
fs->bl = bl->previous; /* current block now is previous one */
|
||||
if (bl->previous) /* was it a nested block? */
|
||||
movegotosout(fs, bl); /* update pending gotos to enclosing block */
|
||||
else {
|
||||
if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
|
||||
if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
|
||||
undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
|
||||
}
|
||||
}
|
||||
|
@ -1943,10 +1944,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
|||
LexState lexstate;
|
||||
FuncState funcstate;
|
||||
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
|
||||
setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */
|
||||
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
|
||||
luaD_inctop(L);
|
||||
lexstate.h = luaH_new(L); /* create table for scanner */
|
||||
sethvalue2s(L, L->top, lexstate.h); /* anchor it */
|
||||
sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
|
||||
luaD_inctop(L);
|
||||
funcstate.f = cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
|
@ -1960,7 +1961,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
|||
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
|
||||
/* all scopes should be correctly finished */
|
||||
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
|
||||
L->top--; /* remove scanner's table */
|
||||
L->top.p--; /* remove scanner's table */
|
||||
return cl; /* closure is on the stack, too */
|
||||
}
|
||||
|
||||
|
|
|
@ -180,33 +180,33 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
|||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
int i; CallInfo *ci;
|
||||
/* initialize stack array */
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
L1->tbclist = L1->stack;
|
||||
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
L1->tbclist.p = L1->stack.p;
|
||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack + BASIC_STACK_SIZE;
|
||||
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
|
||||
L1->top.p = L1->stack.p;
|
||||
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
|
||||
/* initialize first ci */
|
||||
ci = &L1->base_ci;
|
||||
ci->next = ci->previous = NULL;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L1->top;
|
||||
ci->func.p = L1->top.p;
|
||||
ci->u.c.k = NULL;
|
||||
ci->nresults = 0;
|
||||
setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */
|
||||
L1->top++;
|
||||
ci->top = L1->top + LUA_MINSTACK;
|
||||
setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
|
||||
L1->top.p++;
|
||||
ci->top.p = L1->top.p + LUA_MINSTACK;
|
||||
L1->ci = ci;
|
||||
}
|
||||
|
||||
|
||||
static void freestack (lua_State *L) {
|
||||
if (L->stack == NULL)
|
||||
if (L->stack.p == NULL)
|
||||
return; /* stack not completely built yet */
|
||||
L->ci = &L->base_ci; /* free the entire 'ci' list */
|
||||
luaE_freeCI(L);
|
||||
lua_assert(L->nci == 0);
|
||||
luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
|
||||
luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
|
||||
}
|
||||
|
||||
|
||||
|
@ -248,7 +248,7 @@ static void f_luaopen (lua_State *L, void *ud) {
|
|||
*/
|
||||
static void preinit_thread (lua_State *L, global_State *g) {
|
||||
G(L) = g;
|
||||
L->stack = NULL;
|
||||
L->stack.p = NULL;
|
||||
L->ci = NULL;
|
||||
L->nci = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
|
@ -284,20 +284,16 @@ static void close_state (lua_State *L) {
|
|||
|
||||
|
||||
LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
global_State *g;
|
||||
global_State *g = G(L);
|
||||
GCObject *o;
|
||||
lua_State *L1;
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
luaC_checkGC(L);
|
||||
/* create new thread */
|
||||
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
|
||||
L1->marked = luaC_white(g);
|
||||
L1->tt = LUA_VTHREAD;
|
||||
/* link it on list 'allgc' */
|
||||
L1->next = g->allgc;
|
||||
g->allgc = obj2gco(L1);
|
||||
o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
|
||||
L1 = gco2th(o);
|
||||
/* anchor it on L stack */
|
||||
setthvalue2s(L, L->top, L1);
|
||||
setthvalue2s(L, L->top.p, L1);
|
||||
api_incr_top(L);
|
||||
preinit_thread(L1, g);
|
||||
L1->hookmask = L->hookmask;
|
||||
|
@ -316,7 +312,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
|||
|
||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
LX *l = fromstate(L1);
|
||||
luaF_closeupval(L1, L1->stack); /* close all upvalues */
|
||||
luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
|
||||
lua_assert(L1->openupval == NULL);
|
||||
luai_userstatefree(L, L1);
|
||||
freestack(L1);
|
||||
|
@ -326,32 +322,41 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
|||
|
||||
int luaE_resetthread (lua_State *L, int status) {
|
||||
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
|
||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||
ci->func = L->stack;
|
||||
setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
|
||||
ci->func.p = L->stack.p;
|
||||
ci->callstatus = CIST_C;
|
||||
if (status == LUA_YIELD)
|
||||
status = LUA_OK;
|
||||
L->status = LUA_OK; /* so it can run __close metamethods */
|
||||
status = luaD_closeprotected(L, 1, status);
|
||||
if (status != LUA_OK) /* errors? */
|
||||
luaD_seterrorobj(L, status, L->stack + 1);
|
||||
luaD_seterrorobj(L, status, L->stack.p + 1);
|
||||
else
|
||||
L->top = L->stack + 1;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
|
||||
L->top.p = L->stack.p + 1;
|
||||
ci->top.p = L->top.p + LUA_MINSTACK;
|
||||
luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_resetthread (lua_State *L) {
|
||||
LUA_API int lua_closethread (lua_State *L, lua_State *from) {
|
||||
int status;
|
||||
lua_lock(L);
|
||||
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||
status = luaE_resetthread(L, L->status);
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Deprecated! Use 'lua_closethread' instead.
|
||||
*/
|
||||
LUA_API int lua_resetthread (lua_State *L) {
|
||||
return lua_closethread(L, NULL);
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
|
@ -426,7 +431,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
|||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
TValue *errobj = s2v(L->top.p - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
: "error object is not a string";
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* Some header files included here need this definition */
|
||||
typedef struct CallInfo CallInfo;
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "ltm.h"
|
||||
#include "lzio.h"
|
||||
|
@ -139,7 +144,7 @@ struct lua_longjmp; /* defined in ldo.c */
|
|||
|
||||
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
|
||||
|
||||
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
|
||||
#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p)
|
||||
|
||||
|
||||
/* kinds of Garbage Collection */
|
||||
|
@ -169,9 +174,9 @@ typedef struct stringtable {
|
|||
** - field 'transferinfo' is used only during call/returnhooks,
|
||||
** before the function starts or after it ends.
|
||||
*/
|
||||
typedef struct CallInfo {
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
struct CallInfo {
|
||||
StkIdRel func; /* function index in the stack */
|
||||
StkIdRel top; /* top for this function */
|
||||
struct CallInfo *previous, *next; /* dynamic call link */
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
|
@ -196,7 +201,7 @@ typedef struct CallInfo {
|
|||
} u2;
|
||||
short nresults; /* expected number of results from this function */
|
||||
unsigned short callstatus;
|
||||
} CallInfo;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
@ -291,7 +296,7 @@ typedef struct global_State {
|
|||
struct lua_State *mainthread;
|
||||
TString *memerrmsg; /* message for memory-allocation errors */
|
||||
TString *tmname[TM_N]; /* array with tag-method names */
|
||||
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
|
||||
struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
|
||||
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
||||
lua_WarnFunction warnf; /* warning function */
|
||||
void *ud_warn; /* auxiliary data to 'warnf' */
|
||||
|
@ -306,13 +311,13 @@ struct lua_State {
|
|||
lu_byte status;
|
||||
lu_byte allowhook;
|
||||
unsigned short nci; /* number of items in 'ci' list */
|
||||
StkId top; /* first free slot in the stack */
|
||||
StkIdRel top; /* first free slot in the stack */
|
||||
global_State *l_G;
|
||||
CallInfo *ci; /* call info for current function */
|
||||
StkId stack_last; /* end of stack (last element + 1) */
|
||||
StkId stack; /* stack base */
|
||||
StkIdRel stack_last; /* end of stack (last element + 1) */
|
||||
StkIdRel stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
StkId tbclist; /* list of to-be-closed variables */
|
||||
StkIdRel tbclist; /* list of to-be-closed variables */
|
||||
GCObject *gclist;
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
|
|
|
@ -570,7 +570,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
|
|||
static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
if (l_unlikely(ms->matchdepth-- == 0))
|
||||
luaL_error(ms->L, "pattern too complex");
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
init: /* using goto to optimize tail recursion */
|
||||
if (p != ms->p_end) { /* end of pattern? */
|
||||
switch (*p) {
|
||||
case '(': { /* start capture */
|
||||
|
|
|
@ -107,7 +107,7 @@ static const TValue absentkey = {ABSTKEYCONSTANT};
|
|||
*/
|
||||
static Node *hashint (const Table *t, lua_Integer i) {
|
||||
lua_Unsigned ui = l_castS2U(i);
|
||||
if (ui <= (unsigned int)INT_MAX)
|
||||
if (ui <= cast_uint(INT_MAX))
|
||||
return hashmod(t, cast_int(ui));
|
||||
else
|
||||
return hashmod(t, ui);
|
||||
|
@ -257,9 +257,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
|
|||
size |= (size >> 2);
|
||||
size |= (size >> 4);
|
||||
size |= (size >> 8);
|
||||
#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
|
||||
size |= (size >> 16);
|
||||
#if (UINT_MAX >> 30) > 3
|
||||
size |= (size >> 32); /* unsigned int has more than 32 bits */
|
||||
#endif
|
||||
#endif
|
||||
size++;
|
||||
lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
|
||||
|
@ -488,7 +490,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
|
|||
luaG_runerror(L, "table overflow");
|
||||
size = twoto(lsize);
|
||||
t->node = luaM_newvector(L, size, Node);
|
||||
for (i = 0; i < (int)size; i++) {
|
||||
for (i = 0; i < cast_int(size); i++) {
|
||||
Node *n = gnode(t, i);
|
||||
gnext(n) = 0;
|
||||
setnilkey(n);
|
||||
|
@ -975,6 +977,4 @@ Node *luaH_mainposition (const Table *t, const TValue *key) {
|
|||
return mainpositionTV(t, key);
|
||||
}
|
||||
|
||||
int luaH_isdummy (const Table *t) { return isdummy(t); }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,7 +59,6 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t);
|
|||
|
||||
#if defined(LUA_DEBUG)
|
||||
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
|
||||
LUAI_FUNC int luaH_isdummy (const Table *t);
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ static int tremove (lua_State *L) {
|
|||
lua_Integer pos = luaL_optinteger(L, 2, size);
|
||||
if (pos != size) /* validate 'pos' if given */
|
||||
/* check whether 'pos' is in [1, size + 1] */
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1,
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
|
||||
"position out of bounds");
|
||||
lua_geti(L, 1, pos); /* result = t[pos] */
|
||||
for ( ; pos < size; pos++) {
|
||||
|
|
|
@ -102,12 +102,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) {
|
|||
|
||||
void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3) {
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, func + 3, p3); /* 3rd argument */
|
||||
L->top = func + 4;
|
||||
L->top.p = func + 4;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 0);
|
||||
|
@ -119,18 +119,18 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
|||
void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, StkId res) {
|
||||
ptrdiff_t result = savestack(L, res);
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
L->top += 3;
|
||||
L->top.p += 3;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 1);
|
||||
else
|
||||
luaD_callnoyield(L, func, 1);
|
||||
res = restorestack(L, result);
|
||||
setobjs2s(L, res, --L->top); /* move result to its place */
|
||||
setobjs2s(L, res, --L->top.p); /* move result to its place */
|
||||
}
|
||||
|
||||
|
||||
|
@ -165,7 +165,7 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
|||
|
||||
|
||||
void luaT_tryconcatTM (lua_State *L) {
|
||||
StkId top = L->top;
|
||||
StkId top = L->top.p;
|
||||
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
|
||||
TM_CONCAT)))
|
||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||
|
@ -200,15 +200,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
|||
*/
|
||||
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
TMS event) {
|
||||
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */
|
||||
return !l_isfalse(s2v(L->top.p));
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
else if (event == TM_LE) {
|
||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
||||
if (callbinTM(L, p2, p1, L->top, TM_LT)) {
|
||||
if (callbinTM(L, p2, p1, L->top.p, TM_LT)) {
|
||||
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
return l_isfalse(s2v(L->top));
|
||||
return l_isfalse(s2v(L->top.p));
|
||||
}
|
||||
/* else error will remove this 'ci'; no need to clear mark */
|
||||
}
|
||||
|
@ -238,20 +238,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
|||
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
|
||||
const Proto *p) {
|
||||
int i;
|
||||
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
|
||||
int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
|
||||
int nextra = actual - nfixparams; /* number of extra arguments */
|
||||
ci->u.l.nextraargs = nextra;
|
||||
luaD_checkstack(L, p->maxstacksize + 1);
|
||||
/* copy function to the top of the stack */
|
||||
setobjs2s(L, L->top++, ci->func);
|
||||
setobjs2s(L, L->top.p++, ci->func.p);
|
||||
/* move fixed parameters to the top of the stack */
|
||||
for (i = 1; i <= nfixparams; i++) {
|
||||
setobjs2s(L, L->top++, ci->func + i);
|
||||
setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */
|
||||
setobjs2s(L, L->top.p++, ci->func.p + i);
|
||||
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
|
||||
}
|
||||
ci->func += actual + 1;
|
||||
ci->top += actual + 1;
|
||||
lua_assert(L->top <= ci->top && ci->top <= L->stack_last);
|
||||
ci->func.p += actual + 1;
|
||||
ci->top.p += actual + 1;
|
||||
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,10 +261,10 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
|||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackGCp(L, nextra, where); /* ensure stack space */
|
||||
L->top = where + nextra; /* next instruction will need top */
|
||||
L->top.p = where + nextra; /* next instruction will need top */
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
setobjs2s(L, where + i, ci->func - nextra + i);
|
||||
setobjs2s(L, where + i, ci->func.p - nextra + i);
|
||||
for (; i < wanted; i++) /* complete required results with nil */
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
/*
|
||||
|
@ -95,8 +96,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
|||
int inv, int isfloat, TMS event);
|
||||
|
||||
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
|
||||
struct CallInfo *ci, const Proto *p);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
|
||||
CallInfo *ci, const Proto *p);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
|
||||
StkId where, int wanted);
|
||||
|
||||
|
||||
|
|
|
@ -181,10 +181,11 @@ static void print_version (void) {
|
|||
** to the script (everything after 'script') go to positive indices;
|
||||
** other arguments (before the script name) go to negative indices.
|
||||
** If there is no script name, assume interpreter's name as base.
|
||||
** (If there is no interpreter's name either, 'script' is -1, so
|
||||
** table sizes are zero.)
|
||||
*/
|
||||
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||
int i, narg;
|
||||
if (script == argc) script = 0; /* no script name? */
|
||||
narg = argc - (script + 1); /* number of positive indices */
|
||||
lua_createtable(L, narg, script + 1);
|
||||
for (i = 0; i < argc; i++) {
|
||||
|
@ -274,14 +275,23 @@ static int handle_script (lua_State *L, char **argv) {
|
|||
|
||||
/*
|
||||
** Traverses all arguments from 'argv', returning a mask with those
|
||||
** needed before running any Lua code (or an error code if it finds
|
||||
** any invalid argument). 'first' returns the first not-handled argument
|
||||
** (either the script name or a bad argument in case of error).
|
||||
** needed before running any Lua code or an error code if it finds any
|
||||
** invalid argument. In case of error, 'first' is the index of the bad
|
||||
** argument. Otherwise, 'first' is -1 if there is no program name,
|
||||
** 0 if there is no script name, or the index of the script name.
|
||||
*/
|
||||
static int collectargs (char **argv, int *first) {
|
||||
int args = 0;
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
if (argv[0] != NULL) { /* is there a program name? */
|
||||
if (argv[0][0]) /* not empty? */
|
||||
progname = argv[0]; /* save it */
|
||||
}
|
||||
else { /* no program name */
|
||||
*first = -1;
|
||||
return 0;
|
||||
}
|
||||
for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
|
||||
*first = i;
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return args; /* stop handling options */
|
||||
|
@ -322,7 +332,7 @@ static int collectargs (char **argv, int *first) {
|
|||
return has_error;
|
||||
}
|
||||
}
|
||||
*first = i; /* no script name */
|
||||
*first = 0; /* no script name */
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -615,8 +625,8 @@ static int pmain (lua_State *L) {
|
|||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int args = collectargs(argv, &script);
|
||||
int optlim = (script > 0) ? script : argc; /* first argv not an option */
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
|
@ -629,7 +639,8 @@ static int pmain (lua_State *L) {
|
|||
}
|
||||
luaL_openlibs(L); /* open standard libraries */
|
||||
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
||||
lua_gc(L, LUA_GCRESTART); /* start GC... */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
|
||||
if (!(args & has_E)) { /* no option '-E'? */
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
|
@ -640,14 +651,15 @@ static int pmain (lua_State *L) {
|
|||
ftl_lua_init(L);
|
||||
/***************************************************/
|
||||
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
handle_script(L, argv + script) != LUA_OK)
|
||||
return 0;
|
||||
if (script > 0) { /* execute main script (if there is one) */
|
||||
if (handle_script(L, argv + script) != LUA_OK)
|
||||
return 0; /* interrupt in case of error */
|
||||
}
|
||||
if (args & has_i) /* -i option? */
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
||||
else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
|
||||
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||
print_version();
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
|
@ -668,6 +680,7 @@ int lua_main (int argc, char **argv) {
|
|||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
lua_pushinteger(L, argc); /* 1st argument */
|
||||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "4"
|
||||
#define LUA_VERSION_RELEASE "4"
|
||||
#define LUA_VERSION_RELEASE "6"
|
||||
|
||||
#define LUA_VERSION_NUM 504
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 4)
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
|
@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
|||
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** Type used by the debug API to collect debug information
|
||||
*/
|
||||
typedef struct lua_Debug lua_Debug;
|
||||
|
||||
|
||||
/*
|
||||
** Functions to be called by the debugger in specific events
|
||||
*/
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -153,7 +163,8 @@ extern const char lua_ident[];
|
|||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||
LUA_API void (lua_close) (lua_State *L);
|
||||
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
||||
LUA_API int (lua_resetthread) (lua_State *L);
|
||||
LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
|
||||
LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
|
||||
|
||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
|
||||
|
@ -442,12 +453,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
|
|||
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
|
||||
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
|
||||
|
||||
typedef struct lua_Debug lua_Debug; /* activation record */
|
||||
|
||||
|
||||
/* Functions to be called by the debugger in specific events */
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
|
@ -492,7 +497,7 @@ struct lua_Debug {
|
|||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2022 Lua.org, PUC-Rio.
|
||||
* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
|
|
@ -121,7 +121,7 @@ static int doargs(int argc, char* argv[])
|
|||
return i;
|
||||
}
|
||||
|
||||
#define FUNCTION "(function()end)();"
|
||||
#define FUNCTION "(function()end)();\n"
|
||||
|
||||
static const char* reader(lua_State* L, void* ud, size_t* size)
|
||||
{
|
||||
|
@ -138,7 +138,7 @@ static const char* reader(lua_State* L, void* ud, size_t* size)
|
|||
}
|
||||
}
|
||||
|
||||
#define toproto(L,i) getproto(s2v(L->top+(i)))
|
||||
#define toproto(L,i) getproto(s2v(L->top.p+(i)))
|
||||
|
||||
static const Proto* combine(lua_State* L, int n)
|
||||
{
|
||||
|
@ -155,8 +155,6 @@ static const Proto* combine(lua_State* L, int n)
|
|||
f->p[i]=toproto(L,i-n-1);
|
||||
if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
|
||||
}
|
||||
luaM_freearray(L,f->lineinfo,f->sizelineinfo);
|
||||
f->sizelineinfo=0;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,12 @@
|
|||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_IOS)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
|
||||
*/
|
||||
|
@ -728,7 +734,7 @@
|
|||
** CHANGE it if you need a different limit. This limit is arbitrary;
|
||||
** its only purpose is to stop Lua from consuming unlimited stack
|
||||
** space (and to reserve some numbers for pseudo-indices).
|
||||
** (It must fit into max(size_t)/32.)
|
||||
** (It must fit into max(size_t)/32 and max(int)/2.)
|
||||
*/
|
||||
#if LUAI_IS32INT
|
||||
#define LUAI_MAXSTACK 1000000
|
||||
|
@ -747,14 +753,15 @@
|
|||
|
||||
/*
|
||||
@@ LUA_IDSIZE gives the maximum size for the description of the source
|
||||
@@ of a function in debug information.
|
||||
** of a function in debug information.
|
||||
** CHANGE it if you want a different size.
|
||||
*/
|
||||
#define LUA_IDSIZE 60
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
|
||||
@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
|
||||
** buffer system.
|
||||
*/
|
||||
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
|
||||
|
||||
|
|
|
@ -120,10 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) {
|
|||
}
|
||||
else { /* long string */
|
||||
ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
|
||||
setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
|
||||
luaD_inctop(L);
|
||||
loadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
L->top--; /* pop string */
|
||||
L->top.p--; /* pop string */
|
||||
}
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return ts;
|
||||
|
@ -248,6 +248,8 @@ static void loadDebug (LoadState *S, Proto *f) {
|
|||
f->locvars[i].endpc = loadInt(S);
|
||||
}
|
||||
n = loadInt(S);
|
||||
if (n != 0) /* does it have debug information? */
|
||||
n = f->sizeupvalues; /* must be this many */
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = loadStringN(S, f);
|
||||
}
|
||||
|
@ -321,7 +323,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
|||
S.Z = Z;
|
||||
checkHeader(&S);
|
||||
cl = luaF_newLclosure(L, loadByte(&S));
|
||||
setclLvalue2s(L, L->top, cl);
|
||||
setclLvalue2s(L, L->top.p, cl);
|
||||
luaD_inctop(L);
|
||||
cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#define MAXUTF 0x7FFFFFFFu
|
||||
|
||||
|
||||
#define MSGInvalid "invalid UTF-8 code"
|
||||
|
||||
/*
|
||||
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
|
||||
*/
|
||||
|
@ -35,7 +38,8 @@ typedef unsigned long utfint;
|
|||
#endif
|
||||
|
||||
|
||||
#define iscont(p) ((*(p) & 0xC0) == 0x80)
|
||||
#define iscont(c) (((c) & 0xC0) == 0x80)
|
||||
#define iscontp(p) iscont(*(p))
|
||||
|
||||
|
||||
/* from strlib */
|
||||
|
@ -65,7 +69,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) {
|
|||
int count = 0; /* to count number of continuation bytes */
|
||||
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
|
||||
unsigned int cc = (unsigned char)s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
if (!iscont(cc)) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
}
|
||||
|
@ -140,7 +144,7 @@ static int codepoint (lua_State *L) {
|
|||
utfint code;
|
||||
s = utf8_decode(s, &code, !lax);
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, code);
|
||||
n++;
|
||||
}
|
||||
|
@ -190,16 +194,16 @@ static int byteoffset (lua_State *L) {
|
|||
"position out of bounds");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
while (posi > 0 && iscontp(s + posi)) posi--;
|
||||
}
|
||||
else {
|
||||
if (iscont(s + posi))
|
||||
if (iscontp(s + posi))
|
||||
return luaL_error(L, "initial position is a continuation byte");
|
||||
if (n < 0) {
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
} while (posi > 0 && iscontp(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +212,7 @@ static int byteoffset (lua_State *L) {
|
|||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
} while (iscontp(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
@ -226,15 +230,15 @@ static int iter_aux (lua_State *L, int strict) {
|
|||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
|
||||
if (n < len) {
|
||||
while (iscont(s + n)) n++; /* skip continuation bytes */
|
||||
while (iscontp(s + n)) n++; /* go to next character */
|
||||
}
|
||||
if (n >= len) /* (also handles original 'n' being negative) */
|
||||
return 0; /* no more codepoints */
|
||||
else {
|
||||
utfint code;
|
||||
const char *next = utf8_decode(s + n, &code, strict);
|
||||
if (next == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
if (next == NULL || iscontp(next))
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, n + 1);
|
||||
lua_pushinteger(L, code);
|
||||
return 2;
|
||||
|
@ -253,7 +257,8 @@ static int iter_auxlax (lua_State *L) {
|
|||
|
||||
static int iter_codes (lua_State *L) {
|
||||
int lax = lua_toboolean(L, 2);
|
||||
luaL_checkstring(L, 1);
|
||||
const char *s = luaL_checkstring(L, 1);
|
||||
luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
|
||||
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 0);
|
||||
|
|
235
src/lua/lvm.c
235
src/lua/lvm.c
|
@ -608,8 +608,8 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
|
|||
if (tm == NULL) /* no TM? */
|
||||
return 0; /* objects are different */
|
||||
else {
|
||||
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
|
||||
return !l_isfalse(s2v(L->top.p));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -633,17 +633,17 @@ static void copy2buff (StkId top, int n, char *buff) {
|
|||
|
||||
/*
|
||||
** Main operation for concatenation: concat 'total' values in the stack,
|
||||
** from 'L->top - total' up to 'L->top - 1'.
|
||||
** from 'L->top.p - total' up to 'L->top.p - 1'.
|
||||
*/
|
||||
void luaV_concat (lua_State *L, int total) {
|
||||
if (total == 1)
|
||||
return; /* "all" values already concatenated */
|
||||
do {
|
||||
StkId top = L->top;
|
||||
StkId top = L->top.p;
|
||||
int n = 2; /* number of elements handled in this pass (at least 2) */
|
||||
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
|
||||
!tostring(L, s2v(top - 1)))
|
||||
luaT_tryconcatTM(L);
|
||||
luaT_tryconcatTM(L); /* may invalidate 'top' */
|
||||
else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
|
||||
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
|
||||
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
|
||||
|
@ -656,8 +656,10 @@ void luaV_concat (lua_State *L, int total) {
|
|||
/* collect total length and number of strings */
|
||||
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
|
||||
size_t l = vslen(s2v(top - n - 1));
|
||||
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
|
||||
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) {
|
||||
L->top.p = top - total; /* pop strings to avoid wasting stack */
|
||||
luaG_runerror(L, "string length overflow");
|
||||
}
|
||||
tl += l;
|
||||
}
|
||||
if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */
|
||||
|
@ -671,8 +673,8 @@ void luaV_concat (lua_State *L, int total) {
|
|||
}
|
||||
setsvalue2s(L, top - n, ts); /* create result */
|
||||
}
|
||||
total -= n-1; /* got 'n' strings to create 1 new */
|
||||
L->top -= n-1; /* popped 'n' strings and pushed one */
|
||||
total -= n - 1; /* got 'n' strings to create one new */
|
||||
L->top.p -= n - 1; /* popped 'n' strings and pushed one */
|
||||
} while (total > 1); /* repeat until only 1 result left */
|
||||
}
|
||||
|
||||
|
@ -763,12 +765,10 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
|
|||
/* number of bits in an integer */
|
||||
#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
|
||||
|
||||
|
||||
/*
|
||||
** Shift left operation. (Shift right just negates 'y'.)
|
||||
*/
|
||||
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
|
||||
|
||||
|
||||
lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
|
||||
if (y < 0) { /* shift right? */
|
||||
if (y <= -NBITS) return 0;
|
||||
|
@ -808,26 +808,26 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
|
|||
*/
|
||||
void luaV_finishOp (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
StkId base = ci->func + 1;
|
||||
StkId base = ci->func.p + 1;
|
||||
Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
|
||||
OpCode op = GET_OPCODE(inst);
|
||||
switch (op) { /* finish its execution */
|
||||
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
|
||||
setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top);
|
||||
setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p);
|
||||
break;
|
||||
}
|
||||
case OP_UNM: case OP_BNOT: case OP_LEN:
|
||||
case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
|
||||
case OP_GETFIELD: case OP_SELF: {
|
||||
setobjs2s(L, base + GETARG_A(inst), --L->top);
|
||||
setobjs2s(L, base + GETARG_A(inst), --L->top.p);
|
||||
break;
|
||||
}
|
||||
case OP_LT: case OP_LE:
|
||||
case OP_LTI: case OP_LEI:
|
||||
case OP_GTI: case OP_GEI:
|
||||
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
|
||||
int res = !l_isfalse(s2v(L->top - 1));
|
||||
L->top--;
|
||||
int res = !l_isfalse(s2v(L->top.p - 1));
|
||||
L->top.p--;
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
|
||||
ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
|
@ -840,11 +840,11 @@ void luaV_finishOp (lua_State *L) {
|
|||
break;
|
||||
}
|
||||
case OP_CONCAT: {
|
||||
StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */
|
||||
StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */
|
||||
int a = GETARG_A(inst); /* first element to concatenate */
|
||||
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
|
||||
setobjs2s(L, top - 2, top); /* put TM result in proper position */
|
||||
L->top = top - 1; /* top is one after last element (at top-2) */
|
||||
L->top.p = top - 1; /* top is one after last element (at top-2) */
|
||||
luaV_concat(L, total); /* concat them (may yield again) */
|
||||
break;
|
||||
}
|
||||
|
@ -856,7 +856,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
StkId ra = base + GETARG_A(inst);
|
||||
/* adjust top to signal correct number of returns, in case the
|
||||
return is "up to top" ('isIT') */
|
||||
L->top = ra + ci->u2.nres;
|
||||
L->top.p = ra + ci->u2.nres;
|
||||
/* repeat instruction to close other vars. and complete the return */
|
||||
ci->u.l.savedpc--;
|
||||
break;
|
||||
|
@ -898,6 +898,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** operation, 'fop' is the float operation.
|
||||
*/
|
||||
#define op_arithI(L,iop,fop) { \
|
||||
StkId ra = RA(i); \
|
||||
TValue *v1 = vRB(i); \
|
||||
int imm = GETARG_sC(i); \
|
||||
if (ttisinteger(v1)) { \
|
||||
|
@ -926,6 +927,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Arithmetic operations over floats and others with register operands.
|
||||
*/
|
||||
#define op_arithf(L,fop) { \
|
||||
StkId ra = RA(i); \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
op_arithf_aux(L, v1, v2, fop); }
|
||||
|
@ -935,6 +937,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Arithmetic operations with K operands for floats.
|
||||
*/
|
||||
#define op_arithfK(L,fop) { \
|
||||
StkId ra = RA(i); \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
|
||||
op_arithf_aux(L, v1, v2, fop); }
|
||||
|
@ -944,6 +947,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Arithmetic operations over integers and floats.
|
||||
*/
|
||||
#define op_arith_aux(L,v1,v2,iop,fop) { \
|
||||
StkId ra = RA(i); \
|
||||
if (ttisinteger(v1) && ttisinteger(v2)) { \
|
||||
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
|
||||
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
|
@ -973,6 +977,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Bitwise operations with constant operand.
|
||||
*/
|
||||
#define op_bitwiseK(L,op) { \
|
||||
StkId ra = RA(i); \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
lua_Integer i1; \
|
||||
|
@ -986,6 +991,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Bitwise operations with register operands.
|
||||
*/
|
||||
#define op_bitwise(L,op) { \
|
||||
StkId ra = RA(i); \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
lua_Integer i1; lua_Integer i2; \
|
||||
|
@ -1000,18 +1006,19 @@ void luaV_finishOp (lua_State *L) {
|
|||
** integers.
|
||||
*/
|
||||
#define op_order(L,opi,opn,other) { \
|
||||
int cond; \
|
||||
TValue *rb = vRB(i); \
|
||||
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
|
||||
lua_Integer ia = ivalue(s2v(ra)); \
|
||||
lua_Integer ib = ivalue(rb); \
|
||||
cond = opi(ia, ib); \
|
||||
} \
|
||||
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
|
||||
cond = opn(s2v(ra), rb); \
|
||||
else \
|
||||
Protect(cond = other(L, s2v(ra), rb)); \
|
||||
docondjump(); }
|
||||
StkId ra = RA(i); \
|
||||
int cond; \
|
||||
TValue *rb = vRB(i); \
|
||||
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
|
||||
lua_Integer ia = ivalue(s2v(ra)); \
|
||||
lua_Integer ib = ivalue(rb); \
|
||||
cond = opi(ia, ib); \
|
||||
} \
|
||||
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
|
||||
cond = opn(s2v(ra), rb); \
|
||||
else \
|
||||
Protect(cond = other(L, s2v(ra), rb)); \
|
||||
docondjump(); }
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1019,20 +1026,21 @@ void luaV_finishOp (lua_State *L) {
|
|||
** always small enough to have an exact representation as a float.)
|
||||
*/
|
||||
#define op_orderI(L,opi,opf,inv,tm) { \
|
||||
int cond; \
|
||||
int im = GETARG_sB(i); \
|
||||
if (ttisinteger(s2v(ra))) \
|
||||
cond = opi(ivalue(s2v(ra)), im); \
|
||||
else if (ttisfloat(s2v(ra))) { \
|
||||
lua_Number fa = fltvalue(s2v(ra)); \
|
||||
lua_Number fim = cast_num(im); \
|
||||
cond = opf(fa, fim); \
|
||||
} \
|
||||
else { \
|
||||
int isf = GETARG_C(i); \
|
||||
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
|
||||
} \
|
||||
docondjump(); }
|
||||
StkId ra = RA(i); \
|
||||
int cond; \
|
||||
int im = GETARG_sB(i); \
|
||||
if (ttisinteger(s2v(ra))) \
|
||||
cond = opi(ivalue(s2v(ra)), im); \
|
||||
else if (ttisfloat(s2v(ra))) { \
|
||||
lua_Number fa = fltvalue(s2v(ra)); \
|
||||
lua_Number fim = cast_num(im); \
|
||||
cond = opf(fa, fim); \
|
||||
} \
|
||||
else { \
|
||||
int isf = GETARG_C(i); \
|
||||
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
|
||||
} \
|
||||
docondjump(); }
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
@ -1061,7 +1069,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
|
||||
#define updatetrap(ci) (trap = ci->u.l.trap)
|
||||
|
||||
#define updatebase(ci) (base = ci->func + 1)
|
||||
#define updatebase(ci) (base = ci->func.p + 1)
|
||||
|
||||
|
||||
#define updatestack(ci) \
|
||||
|
@ -1096,7 +1104,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
** Whenever code can raise errors, the global 'pc' and the global
|
||||
** 'top' must be correct to report occasional errors.
|
||||
*/
|
||||
#define savestate(L,ci) (savepc(L), L->top = ci->top)
|
||||
#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1116,7 +1124,7 @@ void luaV_finishOp (lua_State *L) {
|
|||
|
||||
/* 'c' is the limit of live values in the stack */
|
||||
#define checkGC(L,c) \
|
||||
{ luaC_condGC(L, (savepc(L), L->top = (c)), \
|
||||
{ luaC_condGC(L, (savepc(L), L->top.p = (c)), \
|
||||
updatetrap(ci)); \
|
||||
luai_threadyield(L); }
|
||||
|
||||
|
@ -1128,7 +1136,6 @@ void luaV_finishOp (lua_State *L) {
|
|||
updatebase(ci); /* correct stack */ \
|
||||
} \
|
||||
i = *(pc++); \
|
||||
ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \
|
||||
}
|
||||
|
||||
#define vmdispatch(o) switch(o)
|
||||
|
@ -1148,7 +1155,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
startfunc:
|
||||
trap = L->hookmask;
|
||||
returning: /* trap already set */
|
||||
cl = clLvalue(s2v(ci->func));
|
||||
cl = clLvalue(s2v(ci->func.p));
|
||||
k = cl->p->k;
|
||||
pc = ci->u.l.savedpc;
|
||||
if (l_unlikely(trap)) {
|
||||
|
@ -1160,60 +1167,68 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
}
|
||||
ci->u.l.trap = 1; /* assume trap is on, for now */
|
||||
}
|
||||
base = ci->func + 1;
|
||||
base = ci->func.p + 1;
|
||||
/* main loop of interpreter */
|
||||
for (;;) {
|
||||
Instruction i; /* instruction being executed */
|
||||
StkId ra; /* instruction's A register */
|
||||
vmfetch();
|
||||
#if 0
|
||||
/* low-level line tracing for debugging Lua */
|
||||
printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
|
||||
#endif
|
||||
lua_assert(base == ci->func + 1);
|
||||
lua_assert(base <= L->top && L->top < L->stack_last);
|
||||
lua_assert(base == ci->func.p + 1);
|
||||
lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p);
|
||||
/* invalidate top for instructions not expecting it */
|
||||
lua_assert(isIT(i) || (cast_void(L->top = base), 1));
|
||||
lua_assert(isIT(i) || (cast_void(L->top.p = base), 1));
|
||||
vmdispatch (GET_OPCODE(i)) {
|
||||
vmcase(OP_MOVE) {
|
||||
StkId ra = RA(i);
|
||||
setobjs2s(L, ra, RB(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADI) {
|
||||
StkId ra = RA(i);
|
||||
lua_Integer b = GETARG_sBx(i);
|
||||
setivalue(s2v(ra), b);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADF) {
|
||||
StkId ra = RA(i);
|
||||
int b = GETARG_sBx(i);
|
||||
setfltvalue(s2v(ra), cast_num(b));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADK) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = k + GETARG_Bx(i);
|
||||
setobj2s(L, ra, rb);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADKX) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb;
|
||||
rb = k + GETARG_Ax(*pc); pc++;
|
||||
setobj2s(L, ra, rb);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADFALSE) {
|
||||
StkId ra = RA(i);
|
||||
setbfvalue(s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LFALSESKIP) {
|
||||
StkId ra = RA(i);
|
||||
setbfvalue(s2v(ra));
|
||||
pc++; /* skip next instruction */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADTRUE) {
|
||||
StkId ra = RA(i);
|
||||
setbtvalue(s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADNIL) {
|
||||
StkId ra = RA(i);
|
||||
int b = GETARG_B(i);
|
||||
do {
|
||||
setnilvalue(s2v(ra++));
|
||||
|
@ -1221,19 +1236,22 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETUPVAL) {
|
||||
StkId ra = RA(i);
|
||||
int b = GETARG_B(i);
|
||||
setobj2s(L, ra, cl->upvals[b]->v);
|
||||
setobj2s(L, ra, cl->upvals[b]->v.p);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SETUPVAL) {
|
||||
StkId ra = RA(i);
|
||||
UpVal *uv = cl->upvals[GETARG_B(i)];
|
||||
setobj(L, uv->v, s2v(ra));
|
||||
setobj(L, uv->v.p, s2v(ra));
|
||||
luaC_barrier(L, uv, s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETTABUP) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *upval = cl->upvals[GETARG_B(i)]->v;
|
||||
TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
|
||||
TValue *rc = KC(i);
|
||||
TString *key = tsvalue(rc); /* key must be a string */
|
||||
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
|
||||
|
@ -1244,6 +1262,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETTABLE) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = vRB(i);
|
||||
TValue *rc = vRC(i);
|
||||
|
@ -1258,6 +1277,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETI) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = vRB(i);
|
||||
int c = GETARG_C(i);
|
||||
|
@ -1272,6 +1292,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_GETFIELD) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = vRB(i);
|
||||
TValue *rc = KC(i);
|
||||
|
@ -1285,7 +1306,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
}
|
||||
vmcase(OP_SETTABUP) {
|
||||
const TValue *slot;
|
||||
TValue *upval = cl->upvals[GETARG_A(i)]->v;
|
||||
TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
|
||||
TValue *rb = KB(i);
|
||||
TValue *rc = RKC(i);
|
||||
TString *key = tsvalue(rb); /* key must be a string */
|
||||
|
@ -1297,6 +1318,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SETTABLE) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = vRB(i); /* key (table is in 'ra') */
|
||||
TValue *rc = RKC(i); /* value */
|
||||
|
@ -1311,6 +1333,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SETI) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
int c = GETARG_B(i);
|
||||
TValue *rc = RKC(i);
|
||||
|
@ -1325,6 +1348,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SETFIELD) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = KB(i);
|
||||
TValue *rc = RKC(i);
|
||||
|
@ -1337,6 +1361,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_NEWTABLE) {
|
||||
StkId ra = RA(i);
|
||||
int b = GETARG_B(i); /* log2(hash size) + 1 */
|
||||
int c = GETARG_C(i); /* array size */
|
||||
Table *t;
|
||||
|
@ -1346,7 +1371,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
if (TESTARG_k(i)) /* non-zero extra argument? */
|
||||
c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */
|
||||
pc++; /* skip extra argument */
|
||||
L->top = ra + 1; /* correct top in case of emergency GC */
|
||||
L->top.p = ra + 1; /* correct top in case of emergency GC */
|
||||
t = luaH_new(L); /* memory allocation */
|
||||
sethvalue2s(L, ra, t);
|
||||
if (b != 0 || c != 0)
|
||||
|
@ -1355,6 +1380,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SELF) {
|
||||
StkId ra = RA(i);
|
||||
const TValue *slot;
|
||||
TValue *rb = vRB(i);
|
||||
TValue *rc = RKC(i);
|
||||
|
@ -1384,6 +1410,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MODK) {
|
||||
savestate(L, ci); /* in case of division by 0 */
|
||||
op_arithK(L, luaV_mod, luaV_modf);
|
||||
vmbreak;
|
||||
}
|
||||
|
@ -1396,6 +1423,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIVK) {
|
||||
savestate(L, ci); /* in case of division by 0 */
|
||||
op_arithK(L, luaV_idiv, luai_numidiv);
|
||||
vmbreak;
|
||||
}
|
||||
|
@ -1412,6 +1440,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SHRI) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
int ic = GETARG_sC(i);
|
||||
lua_Integer ib;
|
||||
|
@ -1421,6 +1450,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SHLI) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
int ic = GETARG_sC(i);
|
||||
lua_Integer ib;
|
||||
|
@ -1442,6 +1472,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MOD) {
|
||||
savestate(L, ci); /* in case of division by 0 */
|
||||
op_arith(L, luaV_mod, luaV_modf);
|
||||
vmbreak;
|
||||
}
|
||||
|
@ -1454,6 +1485,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIV) { /* floor division */
|
||||
savestate(L, ci); /* in case of division by 0 */
|
||||
op_arith(L, luaV_idiv, luai_numidiv);
|
||||
vmbreak;
|
||||
}
|
||||
|
@ -1478,6 +1510,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBIN) {
|
||||
StkId ra = RA(i);
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
TValue *rb = vRB(i);
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
|
@ -1487,6 +1520,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBINI) {
|
||||
StkId ra = RA(i);
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
int imm = GETARG_sB(i);
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
|
@ -1496,6 +1530,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBINK) {
|
||||
StkId ra = RA(i);
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
TValue *imm = KB(i);
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
|
@ -1505,6 +1540,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_UNM) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
lua_Number nb;
|
||||
if (ttisinteger(rb)) {
|
||||
|
@ -1519,6 +1555,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BNOT) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
lua_Integer ib;
|
||||
if (tointegerns(rb, &ib)) {
|
||||
|
@ -1529,6 +1566,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_NOT) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
if (l_isfalse(rb))
|
||||
setbtvalue(s2v(ra));
|
||||
|
@ -1537,21 +1575,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LEN) {
|
||||
StkId ra = RA(i);
|
||||
Protect(luaV_objlen(L, ra, vRB(i)));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CONCAT) {
|
||||
StkId ra = RA(i);
|
||||
int n = GETARG_B(i); /* number of elements to concatenate */
|
||||
L->top = ra + n; /* mark the end of concat operands */
|
||||
L->top.p = ra + n; /* mark the end of concat operands */
|
||||
ProtectNT(luaV_concat(L, n));
|
||||
checkGC(L, L->top); /* 'luaV_concat' ensures correct top */
|
||||
checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CLOSE) {
|
||||
StkId ra = RA(i);
|
||||
Protect(luaF_close(L, ra, LUA_OK, 1));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TBC) {
|
||||
StkId ra = RA(i);
|
||||
/* create new to-be-closed upvalue */
|
||||
halfProtect(luaF_newtbcupval(L, ra));
|
||||
vmbreak;
|
||||
|
@ -1561,6 +1603,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_EQ) {
|
||||
StkId ra = RA(i);
|
||||
int cond;
|
||||
TValue *rb = vRB(i);
|
||||
Protect(cond = luaV_equalobj(L, s2v(ra), rb));
|
||||
|
@ -1576,6 +1619,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_EQK) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = KB(i);
|
||||
/* basic types do not use '__eq'; we can use raw equality */
|
||||
int cond = luaV_rawequalobj(s2v(ra), rb);
|
||||
|
@ -1583,6 +1627,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_EQI) {
|
||||
StkId ra = RA(i);
|
||||
int cond;
|
||||
int im = GETARG_sB(i);
|
||||
if (ttisinteger(s2v(ra)))
|
||||
|
@ -1611,11 +1656,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TEST) {
|
||||
StkId ra = RA(i);
|
||||
int cond = !l_isfalse(s2v(ra));
|
||||
docondjump();
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TESTSET) {
|
||||
StkId ra = RA(i);
|
||||
TValue *rb = vRB(i);
|
||||
if (l_isfalse(rb) == GETARG_k(i))
|
||||
pc++;
|
||||
|
@ -1626,11 +1673,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CALL) {
|
||||
StkId ra = RA(i);
|
||||
CallInfo *newci;
|
||||
int b = GETARG_B(i);
|
||||
int nresults = GETARG_C(i) - 1;
|
||||
if (b != 0) /* fixed number of arguments? */
|
||||
L->top = ra + b; /* top signals number of arguments */
|
||||
L->top.p = ra + b; /* top signals number of arguments */
|
||||
/* else previous instruction set top */
|
||||
savepc(L); /* in case of errors */
|
||||
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
|
||||
|
@ -1642,54 +1690,57 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TAILCALL) {
|
||||
StkId ra = RA(i);
|
||||
int b = GETARG_B(i); /* number of arguments + 1 (function) */
|
||||
int n; /* number of results when calling a C function */
|
||||
int nparams1 = GETARG_C(i);
|
||||
/* delta is virtual 'func' - real 'func' (vararg functions) */
|
||||
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
|
||||
if (b != 0)
|
||||
L->top = ra + b;
|
||||
L->top.p = ra + b;
|
||||
else /* previous instruction set top */
|
||||
b = cast_int(L->top - ra);
|
||||
b = cast_int(L->top.p - ra);
|
||||
savepc(ci); /* several calls here can raise errors */
|
||||
if (TESTARG_k(i)) {
|
||||
luaF_closeupval(L, base); /* close upvalues from current call */
|
||||
lua_assert(L->tbclist < base); /* no pending tbc variables */
|
||||
lua_assert(base == ci->func + 1);
|
||||
lua_assert(L->tbclist.p < base); /* no pending tbc variables */
|
||||
lua_assert(base == ci->func.p + 1);
|
||||
}
|
||||
if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */
|
||||
goto startfunc; /* execute the callee */
|
||||
else { /* C function? */
|
||||
ci->func -= delta; /* restore 'func' (if vararg) */
|
||||
ci->func.p -= delta; /* restore 'func' (if vararg) */
|
||||
luaD_poscall(L, ci, n); /* finish caller */
|
||||
updatetrap(ci); /* 'luaD_poscall' can change hooks */
|
||||
goto ret; /* caller returns after the tail call */
|
||||
}
|
||||
}
|
||||
vmcase(OP_RETURN) {
|
||||
StkId ra = RA(i);
|
||||
int n = GETARG_B(i) - 1; /* number of results */
|
||||
int nparams1 = GETARG_C(i);
|
||||
if (n < 0) /* not fixed? */
|
||||
n = cast_int(L->top - ra); /* get what is available */
|
||||
n = cast_int(L->top.p - ra); /* get what is available */
|
||||
savepc(ci);
|
||||
if (TESTARG_k(i)) { /* may there be open upvalues? */
|
||||
ci->u2.nres = n; /* save number of returns */
|
||||
if (L->top < ci->top)
|
||||
L->top = ci->top;
|
||||
if (L->top.p < ci->top.p)
|
||||
L->top.p = ci->top.p;
|
||||
luaF_close(L, base, CLOSEKTOP, 1);
|
||||
updatetrap(ci);
|
||||
updatestack(ci);
|
||||
}
|
||||
if (nparams1) /* vararg function? */
|
||||
ci->func -= ci->u.l.nextraargs + nparams1;
|
||||
L->top = ra + n; /* set call for 'luaD_poscall' */
|
||||
ci->func.p -= ci->u.l.nextraargs + nparams1;
|
||||
L->top.p = ra + n; /* set call for 'luaD_poscall' */
|
||||
luaD_poscall(L, ci, n);
|
||||
updatetrap(ci); /* 'luaD_poscall' can change hooks */
|
||||
goto ret;
|
||||
}
|
||||
vmcase(OP_RETURN0) {
|
||||
if (l_unlikely(L->hookmask)) {
|
||||
L->top = ra;
|
||||
StkId ra = RA(i);
|
||||
L->top.p = ra;
|
||||
savepc(ci);
|
||||
luaD_poscall(L, ci, 0); /* no hurry... */
|
||||
trap = 1;
|
||||
|
@ -1697,15 +1748,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
else { /* do the 'poscall' here */
|
||||
int nres;
|
||||
L->ci = ci->previous; /* back to caller */
|
||||
L->top = base - 1;
|
||||
L->top.p = base - 1;
|
||||
for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
|
||||
setnilvalue(s2v(L->top++)); /* all results are nil */
|
||||
setnilvalue(s2v(L->top.p++)); /* all results are nil */
|
||||
}
|
||||
goto ret;
|
||||
}
|
||||
vmcase(OP_RETURN1) {
|
||||
if (l_unlikely(L->hookmask)) {
|
||||
L->top = ra + 1;
|
||||
StkId ra = RA(i);
|
||||
L->top.p = ra + 1;
|
||||
savepc(ci);
|
||||
luaD_poscall(L, ci, 1); /* no hurry... */
|
||||
trap = 1;
|
||||
|
@ -1714,12 +1766,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
int nres = ci->nresults;
|
||||
L->ci = ci->previous; /* back to caller */
|
||||
if (nres == 0)
|
||||
L->top = base - 1; /* asked for no results */
|
||||
L->top.p = base - 1; /* asked for no results */
|
||||
else {
|
||||
StkId ra = RA(i);
|
||||
setobjs2s(L, base - 1, ra); /* at least this result */
|
||||
L->top = base;
|
||||
L->top.p = base;
|
||||
for (; l_unlikely(nres > 1); nres--)
|
||||
setnilvalue(s2v(L->top++)); /* complete missing results */
|
||||
setnilvalue(s2v(L->top.p++)); /* complete missing results */
|
||||
}
|
||||
}
|
||||
ret: /* return from a Lua function */
|
||||
|
@ -1731,6 +1784,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
}
|
||||
}
|
||||
vmcase(OP_FORLOOP) {
|
||||
StkId ra = RA(i);
|
||||
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
|
||||
lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1)));
|
||||
if (count > 0) { /* still more iterations? */
|
||||
|
@ -1749,12 +1803,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_FORPREP) {
|
||||
StkId ra = RA(i);
|
||||
savestate(L, ci); /* in case of errors */
|
||||
if (forprep(L, ra))
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TFORPREP) {
|
||||
StkId ra = RA(i);
|
||||
/* create to-be-closed upvalue (if needed) */
|
||||
halfProtect(luaF_newtbcupval(L, ra + 3));
|
||||
pc += GETARG_Bx(i);
|
||||
|
@ -1763,7 +1819,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
goto l_tforcall;
|
||||
}
|
||||
vmcase(OP_TFORCALL) {
|
||||
l_tforcall:
|
||||
l_tforcall: {
|
||||
StkId ra = RA(i);
|
||||
/* 'ra' has the iterator function, 'ra + 1' has the state,
|
||||
'ra + 2' has the control variable, and 'ra + 3' has the
|
||||
to-be-closed variable. The call will use the stack after
|
||||
|
@ -1771,29 +1828,31 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
*/
|
||||
/* push function, state, and control variable */
|
||||
memcpy(ra + 4, ra, 3 * sizeof(*ra));
|
||||
L->top = ra + 4 + 3;
|
||||
L->top.p = ra + 4 + 3;
|
||||
ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
|
||||
updatestack(ci); /* stack may have changed */
|
||||
i = *(pc++); /* go to next instruction */
|
||||
lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
|
||||
goto l_tforloop;
|
||||
}
|
||||
}}
|
||||
vmcase(OP_TFORLOOP) {
|
||||
l_tforloop:
|
||||
l_tforloop: {
|
||||
StkId ra = RA(i);
|
||||
if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
|
||||
setobjs2s(L, ra + 2, ra + 4); /* save control variable */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
}
|
||||
vmbreak;
|
||||
}
|
||||
}}
|
||||
vmcase(OP_SETLIST) {
|
||||
StkId ra = RA(i);
|
||||
int n = GETARG_B(i);
|
||||
unsigned int last = GETARG_C(i);
|
||||
Table *h = hvalue(s2v(ra));
|
||||
if (n == 0)
|
||||
n = cast_int(L->top - ra) - 1; /* get up to the top */
|
||||
n = cast_int(L->top.p - ra) - 1; /* get up to the top */
|
||||
else
|
||||
L->top = ci->top; /* correct top in case of emergency GC */
|
||||
L->top.p = ci->top.p; /* correct top in case of emergency GC */
|
||||
last += n;
|
||||
if (TESTARG_k(i)) {
|
||||
last += GETARG_Ax(*pc) * (MAXARG_C + 1);
|
||||
|
@ -1810,12 +1869,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CLOSURE) {
|
||||
StkId ra = RA(i);
|
||||
Proto *p = cl->p->p[GETARG_Bx(i)];
|
||||
halfProtect(pushclosure(L, p, cl->upvals, base, ra));
|
||||
checkGC(L, ra + 1);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_VARARG) {
|
||||
StkId ra = RA(i);
|
||||
int n = GETARG_C(i) - 1; /* required results */
|
||||
Protect(luaT_getvarargs(L, ci, ra, n));
|
||||
vmbreak;
|
||||
|
|
|
@ -110,6 +110,11 @@ typedef enum {
|
|||
luaC_barrierback(L, gcvalue(t), v); }
|
||||
|
||||
|
||||
/*
|
||||
** Shift right is the same as shift left with a negative 'y'
|
||||
*/
|
||||
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
|
||||
|
||||
|
||||
|
||||
LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
|
|
|
@ -20,6 +20,6 @@ char* find_equals(const char* s) __attribute__((pure));
|
|||
void trim_whitespace(char *string);
|
||||
void check_blocking_status(void);
|
||||
|
||||
extern unsigned char blockingstatus;
|
||||
extern enum blocking_status blockingstatus;
|
||||
|
||||
#endif //SETUPVARS_H
|
||||
|
|
|
@ -32,11 +32,14 @@ ssize_t FTLrecvfrom(int sockfd, void *buf, size_t len, int flags, struct sockadd
|
|||
// Backup errno value
|
||||
const int _errno = errno;
|
||||
|
||||
// Final error checking (may have failed for some other reason then an
|
||||
// EINTR = interrupted system call)
|
||||
if(ret < 0)
|
||||
// Final error checking. May have failed for some other reason then an
|
||||
// EINTR = interrupted system call. In that case, log a warning However,
|
||||
// if the error is EAGAIN, this is not an error, but just a non-blocking
|
||||
// socket that has no data available or we ran into an (expected)
|
||||
// timeout. In that case, do not log a warning
|
||||
if(ret < 0 && errno != EAGAIN)
|
||||
logg("WARN: Could not recvfrom() in %s() (%s:%i): %s",
|
||||
func, file, line, strerror(errno));
|
||||
func, file, line, strerror(errno));
|
||||
|
||||
// Restore errno value
|
||||
errno = _errno;
|
||||
|
|
|
@ -33,8 +33,9 @@ ssize_t FTLsendto(int sockfd, void *buf, size_t len, int flags, const struct soc
|
|||
const int _errno = errno;
|
||||
|
||||
// Final error checking (may have failed for some other reason then an
|
||||
// EINTR = interrupted system call)
|
||||
if(ret < 0)
|
||||
// EINTR = interrupted system call), also ignore EPROTONOSUPPORT (ARP scanning)
|
||||
// and EPERM + ENOKEY (DHCP probing)
|
||||
if(ret < 0 && errno != EPROTONOSUPPORT && errno != EPERM && errno != ENOKEY)
|
||||
logg("WARN: Could not sendto() in %s() (%s:%i): %s",
|
||||
func, file, line, strerror(errno));
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# FTL Engine
|
||||
# /src/tools/CMakeList.txt
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
set(tools_sources
|
||||
arp-scan.c
|
||||
arp-scan.h
|
||||
dhcp-discover.c
|
||||
dhcp-discover.h
|
||||
gravity-parseList.c
|
||||
gravity-parseList.h
|
||||
)
|
||||
|
||||
add_library(tools OBJECT ${tools_sources})
|
||||
target_compile_options(tools PRIVATE "${EXTRAWARN}")
|
||||
target_include_directories(tools PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
|
@ -0,0 +1,747 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* ARP scanning routines
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
// Inspired by https://stackoverflow.com/a/39287433 but heavily modified
|
||||
|
||||
#include "FTL.h"
|
||||
#include "arp-scan.h"
|
||||
#include "log.h"
|
||||
// get_hardware_address()
|
||||
#include "dhcp-discover.h"
|
||||
// sleepms()
|
||||
#include "timers.h"
|
||||
// check_capability()
|
||||
#include "capabilities.h"
|
||||
#include <linux/capability.h>
|
||||
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
//htons etc
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// How many threads do we spawn at maximum?
|
||||
// This is also the limit for interfaces
|
||||
// we scan for DHCP activity.
|
||||
#define MAXTHREADS 32
|
||||
|
||||
// How many MAC addresses do we store per IP address?
|
||||
#define MAX_MACS 3
|
||||
|
||||
// How many ARP requests do we send per IP address?
|
||||
#define NUM_SCANS 10
|
||||
|
||||
// How long do we wait for ARP replies in each scan [seconds]?
|
||||
#define ARP_TIMEOUT 1
|
||||
|
||||
// Protocol definitions
|
||||
#define PROTO_ARP 0x0806
|
||||
#define ETH2_HEADER_LEN 14
|
||||
#define HW_TYPE 1
|
||||
#define MAC_LENGTH 6
|
||||
#define IPV4_LENGTH 4
|
||||
#define ARP_REQUEST 0x01
|
||||
#define ARP_REPLY 0x02
|
||||
#define BUF_SIZE 60
|
||||
|
||||
// ARP header struct
|
||||
// See https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure
|
||||
#pragma pack(push, 1)
|
||||
struct arp_header {
|
||||
unsigned short hardware_type;
|
||||
unsigned short protocol_type;
|
||||
unsigned char hardware_len;
|
||||
unsigned char protocol_len;
|
||||
unsigned short opcode;
|
||||
unsigned char sender_mac[MAC_LENGTH];
|
||||
unsigned char sender_ip[IPV4_LENGTH];
|
||||
unsigned char target_mac[MAC_LENGTH];
|
||||
unsigned char target_ip[IPV4_LENGTH];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct arp_result {
|
||||
struct device {
|
||||
unsigned char replied[NUM_SCANS];
|
||||
unsigned char mac[MAC_LENGTH];
|
||||
} device[MAX_MACS];
|
||||
};
|
||||
|
||||
struct arp_result_extreme {
|
||||
struct device_extreme {
|
||||
// In extreme mode, we can scan up to 10x more often
|
||||
unsigned char replied[10*NUM_SCANS];
|
||||
unsigned char mac[MAC_LENGTH];
|
||||
} device[MAX_MACS];
|
||||
};
|
||||
|
||||
enum status {
|
||||
STATUS_INITIALIZING = 0,
|
||||
STATUS_SKIPPED_CIDR_MISMATCH,
|
||||
STATUS_SCANNING,
|
||||
STATUS_ERROR,
|
||||
STATUS_COMPLETE
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct thread_data {
|
||||
bool scan_all :1;
|
||||
bool extreme :1;
|
||||
char iface[IF_NAMESIZE + 1];
|
||||
char ipstr[INET_ADDRSTRLEN];
|
||||
unsigned char mac[MAC_LENGTH];
|
||||
enum status status;
|
||||
int dst_cidr;
|
||||
unsigned int num_scans;
|
||||
unsigned int total_scans;
|
||||
size_t result_size;
|
||||
uint32_t scanned_addresses;
|
||||
const char *error;
|
||||
struct ifaddrs *ifa;
|
||||
union {
|
||||
struct arp_result_extreme *result_extreme;
|
||||
struct arp_result *result;
|
||||
};
|
||||
struct sockaddr_in src_addr;
|
||||
struct sockaddr_in dst_addr;
|
||||
struct sockaddr_in mask;
|
||||
};
|
||||
|
||||
// Sends multiple ARP who-has request on interface ifindex, using source mac src_mac and source ip src_ip.
|
||||
// Iterates over all IP addresses in the range of dst_ip/cidr.
|
||||
static int send_arps(const int fd, const int ifindex, struct thread_data *thread_data)
|
||||
{
|
||||
int err = -1;
|
||||
unsigned char buffer[BUF_SIZE];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
// Construct the Ethernet header
|
||||
struct sockaddr_ll socket_address;
|
||||
socket_address.sll_family = AF_PACKET;
|
||||
socket_address.sll_protocol = htons(ETH_P_ARP);
|
||||
socket_address.sll_ifindex = ifindex;
|
||||
socket_address.sll_hatype = htons(ARPHRD_ETHER);
|
||||
socket_address.sll_pkttype = PACKET_BROADCAST;
|
||||
socket_address.sll_halen = MAC_LENGTH;
|
||||
socket_address.sll_addr[6] = 0;
|
||||
socket_address.sll_addr[7] = 0;
|
||||
|
||||
struct ethhdr *send_req = (struct ethhdr *) buffer;
|
||||
struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
|
||||
ssize_t ret;
|
||||
|
||||
// Destination is the broadcast address
|
||||
memset(send_req->h_dest, 0xff, MAC_LENGTH);
|
||||
|
||||
// Target MAC is zero (we don't know it)
|
||||
memset(arp_req->target_mac, 0x00, MAC_LENGTH);
|
||||
|
||||
// Source MAC to our own MAC address
|
||||
memcpy(send_req->h_source, thread_data->mac, MAC_LENGTH);
|
||||
memcpy(arp_req->sender_mac, thread_data->mac, MAC_LENGTH);
|
||||
memcpy(socket_address.sll_addr, thread_data->mac, MAC_LENGTH);
|
||||
|
||||
// Protocol type is ARP
|
||||
send_req->h_proto = htons(ETH_P_ARP);
|
||||
|
||||
// Create ARP request
|
||||
arp_req->hardware_type = htons(HW_TYPE);
|
||||
arp_req->protocol_type = htons(ETH_P_IP);
|
||||
arp_req->hardware_len = MAC_LENGTH;
|
||||
arp_req->protocol_len = IPV4_LENGTH;
|
||||
arp_req->opcode = htons(ARP_REQUEST);
|
||||
|
||||
// Copy IP address to arp_req
|
||||
memcpy(arp_req->sender_ip, &thread_data->src_addr.sin_addr.s_addr, sizeof(thread_data->src_addr.sin_addr.s_addr));
|
||||
|
||||
// Loop over all possible IP addresses in the range dst_ip/cidr
|
||||
// We start at 1 because the first IP address has already been set above
|
||||
struct in_addr dst_ip = thread_data->dst_addr.sin_addr;
|
||||
for(unsigned int i = 0; i < (1u << (32 - thread_data->dst_cidr)); i++)
|
||||
{
|
||||
// Fill in target IP address
|
||||
memcpy(arp_req->target_ip, &dst_ip.s_addr, sizeof(dst_ip.s_addr));
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Sending ARP request for %s@%s\n", inet_ntoa(*dst_ip), iface);
|
||||
#endif
|
||||
|
||||
// Send ARP request
|
||||
ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address));
|
||||
if (ret == -1)
|
||||
{
|
||||
err = errno;
|
||||
thread_data->error = strerror(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Increment IP address
|
||||
dst_ip.s_addr = htonl(ntohl(dst_ip.s_addr) + 1);
|
||||
|
||||
thread_data->scanned_addresses++;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int create_arp_socket(const int ifindex, const char *iface, const char **error)
|
||||
{
|
||||
// Create socket for ARP communications
|
||||
const int arp_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
|
||||
if(arp_socket < 0)
|
||||
{
|
||||
*error = strerror(errno);
|
||||
#ifdef DEBUG
|
||||
printf("Unable to create socket for ARP communications on interface %s: %s\n", iface, *error);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind socket to interface
|
||||
struct sockaddr_ll sll;
|
||||
memset(&sll, 0, sizeof(struct sockaddr_ll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifindex;
|
||||
if (bind(arp_socket, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0)
|
||||
{
|
||||
*error = strerror(errno);
|
||||
#ifdef DEBUG
|
||||
printf("Unable to bind socket for ARP communications on interface %s: %s\n", iface, *error);
|
||||
#endif
|
||||
close(arp_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set timeout
|
||||
struct timeval tv;
|
||||
tv.tv_sec = ARP_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
if (setsockopt(arp_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
|
||||
{
|
||||
*error = strerror(errno);
|
||||
#ifdef DEBUG
|
||||
printf("Unable to set timeout for ARP communications on interface %s: %s\n", iface, *error);
|
||||
#endif
|
||||
close(arp_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return arp_socket;
|
||||
}
|
||||
|
||||
static void add_result(struct in_addr *rcv_ip, unsigned char *sender_mac,
|
||||
struct thread_data *thread_data, const unsigned int scan_id)
|
||||
{
|
||||
|
||||
// Check if we have already found this IP address
|
||||
uint32_t i = ntohl(rcv_ip->s_addr) - ntohl(thread_data->dst_addr.sin_addr.s_addr);
|
||||
if(i >= thread_data->result_size)
|
||||
{
|
||||
printf("Received IP address %s out of range for interface %s (%u >= %zu)\n",
|
||||
inet_ntoa(*rcv_ip), thread_data->iface, i, thread_data->result_size);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save MAC address
|
||||
unsigned int j = 0;
|
||||
for(; j < MAX_MACS; j++)
|
||||
{
|
||||
unsigned char *mac = thread_data->extreme ?
|
||||
thread_data->result_extreme[i].device[j].mac :
|
||||
thread_data->result[i].device[j].mac;
|
||||
// Check if received MAC is already stored in result[i].device[j].mac
|
||||
if(memcmp(mac, sender_mac, MAC_LENGTH) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Check if mac is all-zero
|
||||
if(memcmp(mac, "\x00\x00\x00\x00\x00\x00", MAC_LENGTH) == 0)
|
||||
{
|
||||
// Copy MAC address to mac
|
||||
memcpy(mac, sender_mac, MAC_LENGTH);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Memorize that we have received a reply for this IP address
|
||||
thread_data->extreme ?
|
||||
thread_data->result_extreme[i].device[j].replied[scan_id]++ :
|
||||
thread_data->result[i].device[j].replied[scan_id]++;
|
||||
}
|
||||
|
||||
// Read all ARP responses
|
||||
static ssize_t read_arp(const int fd, struct thread_data *thread_data)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
unsigned char buffer[BUF_SIZE];
|
||||
|
||||
// Read ARP responses
|
||||
while(ret >= 0)
|
||||
{
|
||||
ret = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL);
|
||||
if (ret == -1)
|
||||
{
|
||||
if(errno == EAGAIN)
|
||||
{
|
||||
// Timeout
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Error
|
||||
thread_data->error = strerror(errno);
|
||||
printf("recvfrom(): %s", thread_data->error);
|
||||
break;
|
||||
}
|
||||
struct ethhdr *rcv_resp = (struct ethhdr *) buffer;
|
||||
struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN);
|
||||
if (ntohs(rcv_resp->h_proto) != PROTO_ARP)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Not an ARP packet");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
if (ntohs(arp_resp->opcode) != ARP_REPLY)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Not an ARP reply");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("received ARP len=%ld", ret);
|
||||
#endif
|
||||
struct in_addr sender_a;
|
||||
memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(sender_a.s_addr));
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%-16s %-20s\t%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
thread_data->iface, inet_ntoa(sender_a),
|
||||
arp_resp->sender_mac[0],
|
||||
arp_resp->sender_mac[1],
|
||||
arp_resp->sender_mac[2],
|
||||
arp_resp->sender_mac[3],
|
||||
arp_resp->sender_mac[4],
|
||||
arp_resp->sender_mac[5]);
|
||||
#endif
|
||||
add_result(&sender_a, arp_resp->sender_mac, thread_data, thread_data->num_scans);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Convert netmask to CIDR
|
||||
static int netmask_to_cidr(struct in_addr *addr)
|
||||
{
|
||||
// Count the number of set bits in an unsigned integer
|
||||
return __builtin_popcount(addr->s_addr);
|
||||
}
|
||||
|
||||
static const char *get_hostname(const struct in_addr *addr)
|
||||
{
|
||||
// Get hostname
|
||||
struct hostent *he = gethostbyaddr(&addr->s_addr, sizeof(addr->s_addr), AF_INET);
|
||||
if(he == NULL)
|
||||
return "N/A";
|
||||
|
||||
// Allow at most 24 characters for the hostname
|
||||
static char hostname[25] = { 0 };
|
||||
strncpy(hostname, he->h_name, 24);
|
||||
|
||||
// Return hostname
|
||||
return hostname;
|
||||
}
|
||||
|
||||
static void *arp_scan_iface(void *args)
|
||||
{
|
||||
// Get thread_data pointer
|
||||
struct thread_data *thread_data = (struct thread_data*)args;
|
||||
|
||||
// Get interface details
|
||||
struct ifaddrs *ifa = thread_data->ifa;
|
||||
|
||||
// Get interface name
|
||||
const char *iface = thread_data->iface;
|
||||
|
||||
// Set interface name as thread name
|
||||
prctl(PR_SET_NAME, iface, 0, 0, 0);
|
||||
|
||||
// Get interface netmask
|
||||
memcpy(&thread_data->mask, ifa->ifa_netmask, sizeof(thread_data->mask));
|
||||
|
||||
// Convert subnet to CIDR
|
||||
thread_data->dst_cidr = netmask_to_cidr(&thread_data->mask.sin_addr);
|
||||
|
||||
// Get interface index
|
||||
const int ifindex = if_nametoindex(iface);
|
||||
|
||||
// Scan only interfaces with CIDR >= 24
|
||||
if(thread_data->dst_cidr < 24 && !thread_data->scan_all)
|
||||
{
|
||||
thread_data->status = STATUS_SKIPPED_CIDR_MISMATCH;
|
||||
#ifdef DEBUG
|
||||
printf("Skipped interface %s (%s/%i)\n", iface, thread_data->ipstr, thread_data->dst_cidr);
|
||||
#endif
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("Scanning interface %s (%s/%i)...\n", iface, thread_data->ipstr, thread_data->dst_cidr);
|
||||
#endif
|
||||
thread_data->status = STATUS_SCANNING;
|
||||
|
||||
// Create socket for ARP communications
|
||||
const int arp_socket = create_arp_socket(ifindex, iface, &thread_data->error);
|
||||
|
||||
// Cannot create socket, likely a permission error
|
||||
if(arp_socket < 0)
|
||||
{
|
||||
thread_data->status = STATUS_ERROR;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
// Get hardware address of client machine
|
||||
get_hardware_address(arp_socket, iface, thread_data->mac);
|
||||
|
||||
// Define destination IP address by masking source IP with netmask
|
||||
thread_data->dst_addr.sin_addr.s_addr = thread_data->src_addr.sin_addr.s_addr & thread_data->mask.sin_addr.s_addr;
|
||||
|
||||
// Allocate memory for ARP response buffer
|
||||
const size_t arp_result_len = 1 << (32 - thread_data->dst_cidr);
|
||||
thread_data->result_size = arp_result_len;
|
||||
if(thread_data->extreme)
|
||||
{
|
||||
// Allocate extreme memory for ARP response buffer
|
||||
struct arp_result_extreme *result = calloc(arp_result_len, sizeof(struct arp_result_extreme));
|
||||
if(result == NULL)
|
||||
{
|
||||
// Memory allocation failed due to insufficient memory being
|
||||
// available
|
||||
thread_data->status = STATUS_ERROR;
|
||||
thread_data->error = strerror(ENOMEM);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
thread_data->result_extreme = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate memory for ARP response buffer
|
||||
struct arp_result *result = calloc(arp_result_len, sizeof(struct arp_result));
|
||||
if(result == NULL)
|
||||
{
|
||||
// Memory allocation failed due to insufficient memory being
|
||||
// available
|
||||
thread_data->status = STATUS_ERROR;
|
||||
thread_data->error = strerror(ENOMEM);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
thread_data->result = result;
|
||||
}
|
||||
|
||||
for(thread_data->num_scans = 0; thread_data->num_scans < thread_data->total_scans; thread_data->num_scans++)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Still scanning interface %s (%s/%i) %i%%...\n", iface, thread_data->ipstr, thread_data->dst_cidr, 100*scan_id/thread_data->total_scans);
|
||||
#endif
|
||||
// Send ARP requests to all IPs in subnet
|
||||
if(send_arps(arp_socket, ifindex, thread_data) != 0)
|
||||
{
|
||||
thread_data->status = STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read ARP responses
|
||||
if(read_arp(arp_socket, thread_data) != 0)
|
||||
{
|
||||
thread_data->status = STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close socket
|
||||
if(close(arp_socket) != 0)
|
||||
thread_data->status = STATUS_ERROR;
|
||||
|
||||
if(thread_data->status != STATUS_ERROR)
|
||||
thread_data->status = STATUS_COMPLETE;
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void print_results(struct thread_data *thread_data)
|
||||
{
|
||||
|
||||
if(thread_data->status == STATUS_SKIPPED_CIDR_MISMATCH)
|
||||
{
|
||||
printf("Skipped interface %s (%s/%i) because of too large network (use -a or -x to force scanning this interface)\n\n",
|
||||
thread_data->iface, thread_data->ipstr, thread_data->dst_cidr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(thread_data->status == STATUS_ERROR)
|
||||
{
|
||||
printf("Error scanning interface %s (%s/%i)%s%s\n\n",
|
||||
thread_data->iface, thread_data->ipstr, thread_data->dst_cidr,
|
||||
thread_data->error ? ": " : "", thread_data->error ? thread_data->error : "");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there are any results
|
||||
bool any_replies = false;
|
||||
for(unsigned int i = 0; i < thread_data->result_size; i++)
|
||||
for(unsigned int j = 0; j < MAX_MACS; j++)
|
||||
for(unsigned int k = 0; k < thread_data->total_scans; k++)
|
||||
if(thread_data->extreme)
|
||||
{
|
||||
if(thread_data->result_extreme[i].device[j].replied[k])
|
||||
{
|
||||
any_replies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thread_data->result[i].device[j].replied[k])
|
||||
{
|
||||
any_replies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit early if there are no results
|
||||
if(!any_replies)
|
||||
{
|
||||
printf("No devices replied on interface %s (%s/%i)\n\n",
|
||||
thread_data->iface, thread_data->ipstr, thread_data->dst_cidr);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is at least one result, print header
|
||||
printf("ARP scan on interface %s (%s/%i) finished\n",
|
||||
thread_data->iface, thread_data->ipstr, thread_data->dst_cidr);
|
||||
printf("%-16s %-16s %-24s %-17s %s\n",
|
||||
"IP address", "Interface", "Hostname", "MAC address", "Reply rate");
|
||||
|
||||
// Add our own IP address to the results so IP conflicts can be detected
|
||||
// (our own IP address is not included in the ARP scan)
|
||||
for(unsigned int i = 0; i < thread_data->total_scans; i++)
|
||||
add_result(&thread_data->src_addr.sin_addr, thread_data->mac, thread_data, i);
|
||||
|
||||
// Print results
|
||||
for(unsigned int i = 0; i < thread_data->result_size; i++)
|
||||
{
|
||||
unsigned int j = 0, replied_devices = 0;
|
||||
|
||||
// Print MAC addresses
|
||||
for(j = 0; j < MAX_MACS; j++)
|
||||
{
|
||||
// Check if result[i].mac[j] is all-zero, if so, skip this entry
|
||||
unsigned char *mac = thread_data->extreme ?
|
||||
thread_data->result_extreme[i].device[j].mac :
|
||||
thread_data->result[i].device[j].mac;
|
||||
if(memcmp(mac, "\x00\x00\x00\x00\x00\x00", 6) == 0)
|
||||
break;
|
||||
|
||||
bool replied = false;
|
||||
unsigned char replies = 0u;
|
||||
unsigned char multiple_replies = 0;
|
||||
const unsigned char *rp = thread_data->extreme ?
|
||||
thread_data->result_extreme[i].device[j].replied :
|
||||
thread_data->result[i].device[j].replied;
|
||||
|
||||
// Check if IP address replied
|
||||
for(unsigned int k = 0; k < thread_data->total_scans; k++)
|
||||
{
|
||||
replied |= rp[k] > 0;
|
||||
replies += rp[k] > 0 ? 1 : 0;
|
||||
multiple_replies += rp[k] > 1;
|
||||
}
|
||||
if(!replied)
|
||||
continue;
|
||||
|
||||
// Check if IP address replied multiple times from different MAC address
|
||||
replied_devices++;
|
||||
|
||||
// Convert IP address to string
|
||||
struct in_addr ip = { 0 };
|
||||
ip.s_addr = htonl(ntohl(thread_data->dst_addr.sin_addr.s_addr) + i);
|
||||
inet_ntop(AF_INET, &ip, thread_data->ipstr, INET_ADDRSTRLEN);
|
||||
|
||||
// Print MAC address
|
||||
printf("%-16s %-16s %-24s %02x:%02x:%02x:%02x:%02x:%02x %3u %%\n",
|
||||
thread_data->ipstr, thread_data->iface,
|
||||
get_hostname(&ip),
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
|
||||
replies * 100 / thread_data->total_scans);
|
||||
|
||||
#ifdef DEBUG
|
||||
for(unsigned int k = 0; k < thread_data->total_scans; k++)
|
||||
printf(" %s", rp[k] > 0 ? "X" : "-");
|
||||
#endif
|
||||
if(multiple_replies > 0)
|
||||
printf("INFO: Received multiple replies from %02x:%02x:%02x:%02x:%02x:%02x for %s in %i scan%s\n",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
|
||||
thread_data->ipstr, multiple_replies, multiple_replies > 1 ? "s" : "");
|
||||
}
|
||||
|
||||
// Print warning if we received multiple replies
|
||||
if(replied_devices > 1)
|
||||
printf("WARNING: Received replies for %s from %i devices\n",
|
||||
thread_data->ipstr, replied_devices);
|
||||
}
|
||||
putc('\n', stdout);
|
||||
}
|
||||
|
||||
int run_arp_scan(const bool scan_all, const bool extreme_mode)
|
||||
{
|
||||
// Check if we are capable of sending ARP packets
|
||||
if(!check_capability(CAP_NET_RAW))
|
||||
{
|
||||
puts("Error: Insufficient permissions or capabilities (needs CAP_NET_RAW). Try running as root (sudo)");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
puts("Discovering IPv4 hosts on the network using the Address Resolution Protocol (ARP)...\n");
|
||||
|
||||
// Get interface names for available interfaces on this machine
|
||||
// and launch a thread for each one
|
||||
pthread_t scanthread[MAXTHREADS];
|
||||
pthread_attr_t attr;
|
||||
// Initialize thread attributes object with default attribute values
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
struct ifaddrs *addrs, *tmp;
|
||||
getifaddrs(&addrs);
|
||||
tmp = addrs;
|
||||
|
||||
// Loop until there are no more interfaces available
|
||||
// or we reached the maximum number of threads
|
||||
unsigned int tid = 0;
|
||||
|
||||
struct thread_data thread_data[MAXTHREADS] = {0};
|
||||
|
||||
while(tmp != NULL && tid < MAXTHREADS)
|
||||
{
|
||||
// Create a thread for interfaces of type AF_INET
|
||||
if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
// Skip interface scan if ...
|
||||
// - interface is not up
|
||||
// - ARP is not supported
|
||||
// - interface is loopback net
|
||||
if(!(tmp->ifa_flags & IFF_UP) ||
|
||||
(tmp->ifa_flags & IFF_NOARP) ||
|
||||
(tmp->ifa_flags & IFF_LOOPBACK))
|
||||
{
|
||||
tmp = tmp->ifa_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
thread_data[tid].ifa = tmp;
|
||||
strncpy(thread_data[tid].iface, tmp->ifa_name, sizeof(thread_data[tid].iface) - 1);
|
||||
|
||||
// Get interface IPv4 address
|
||||
memcpy(&thread_data[tid].src_addr, tmp->ifa_addr, sizeof(thread_data[tid].src_addr));
|
||||
inet_ntop(AF_INET, &thread_data[tid].src_addr.sin_addr, thread_data[tid].ipstr, INET_ADDRSTRLEN);
|
||||
|
||||
thread_data[tid].extreme = extreme_mode;
|
||||
thread_data[tid].scan_all = scan_all || extreme_mode;
|
||||
thread_data[tid].total_scans = extreme_mode ? 10*NUM_SCANS : NUM_SCANS;
|
||||
|
||||
// Always skip the loopback interface
|
||||
if(thread_data[tid].src_addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK))
|
||||
{
|
||||
// Create thread
|
||||
if(pthread_create(&scanthread[tid], &attr, arp_scan_iface, &thread_data[tid] ) != 0)
|
||||
{
|
||||
printf("Unable to launch thread for interface %s, skipping...\n",
|
||||
tmp->ifa_name);
|
||||
}
|
||||
|
||||
// Increase thread ID
|
||||
tid++;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance to the next interface
|
||||
tmp = tmp->ifa_next;
|
||||
}
|
||||
|
||||
// Wait for all threads to finish scanning
|
||||
bool all_done = false;
|
||||
unsigned int progress = 0;
|
||||
while(!all_done)
|
||||
{
|
||||
all_done = true;
|
||||
uint64_t num_scans = 0, total_scans = 0;
|
||||
for(unsigned int i = 0; i < tid; i++)
|
||||
{
|
||||
if(thread_data[i].status == STATUS_INITIALIZING ||
|
||||
thread_data[i].status == STATUS_SCANNING)
|
||||
{
|
||||
// At least one thread is still scanning
|
||||
all_done = false;
|
||||
}
|
||||
if(thread_data[i].status == STATUS_SCANNING ||
|
||||
thread_data[i].status == STATUS_COMPLETE)
|
||||
{
|
||||
// Also add up scans for completed threads
|
||||
num_scans += thread_data[i].scanned_addresses;
|
||||
total_scans += thread_data[i].total_scans * thread_data[i].result_size;
|
||||
}
|
||||
}
|
||||
if(!all_done)
|
||||
{
|
||||
// Calculate progress (total number of scans / total number of addresses)
|
||||
// We add 1 to total_scans to avoid division by zero
|
||||
const unsigned int new_progress = 100 * num_scans / (total_scans + 1);
|
||||
if(new_progress > progress)
|
||||
{
|
||||
// Print progress
|
||||
printf(" %i%% ", new_progress);
|
||||
|
||||
// Update progress
|
||||
progress = new_progress;
|
||||
}
|
||||
|
||||
putc('.', stdout);
|
||||
|
||||
// Flush stdout
|
||||
fflush(stdout);
|
||||
|
||||
// Sleep for 1 second
|
||||
sleepms(1000);
|
||||
}
|
||||
}
|
||||
puts("100%\n");
|
||||
|
||||
// Wait for all threads to join back with us
|
||||
for(unsigned int i = 0; i < tid; i++)
|
||||
pthread_join(scanthread[i], NULL);
|
||||
|
||||
// Destroy the thread attributes object, since we are done with it
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
// Free linked-list of interfaces on this client
|
||||
freeifaddrs(addrs);
|
||||
|
||||
// Loop over thread results and print them
|
||||
for(unsigned int i = 0; i < tid; i++)
|
||||
{
|
||||
// Print results
|
||||
print_results(&thread_data[i]);
|
||||
|
||||
// Free allocated memory
|
||||
if(thread_data[i].result != NULL)
|
||||
free(thread_data[i].result);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* ARP scanning prototypes
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#ifndef ARP_SCAN_H
|
||||
#define ARP_SCAN_H
|
||||
|
||||
int run_arp_scan(const bool scan_all, const bool extreme_mode);
|
||||
|
||||
#endif // ARP_SCAN_H
|
|
@ -13,10 +13,12 @@
|
|||
#undef __USE_XOPEN
|
||||
#include "FTL.h"
|
||||
#include "dhcp-discover.h"
|
||||
// logg(), format_time()
|
||||
// format_time()
|
||||
#include "log.h"
|
||||
// read_FTLconf()
|
||||
#include "config.h"
|
||||
// check_capability()
|
||||
#include "capabilities.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
// SIOCGIFHWADDR
|
||||
|
@ -58,6 +60,15 @@
|
|||
|
||||
// Global lock used by all threads
|
||||
static pthread_mutex_t lock;
|
||||
static void __attribute__((format(gnu_printf, 1, 2))) printf_locked(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
pthread_mutex_lock(&lock);
|
||||
vprintf(format, args);
|
||||
pthread_mutex_unlock(&lock);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
extern const struct opttab_t {
|
||||
char *name;
|
||||
|
@ -82,17 +93,17 @@ static int create_dhcp_socket(const char *iname)
|
|||
const int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(sock < 0)
|
||||
{
|
||||
logg("Error: Could not create socket for interface %s!", iname);
|
||||
printf_locked("Error: Could not create socket for interface %s!\n", iname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
logg("DHCP socket: %d", sock);
|
||||
printf_locked("DHCP socket: %d\n", sock);
|
||||
#endif
|
||||
// set the reuse address flag so we don't get errors when restarting
|
||||
if(setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag))<0)
|
||||
{
|
||||
logg("Error: Could not set reuse address option on DHCP socket (%s)!", iname);
|
||||
printf_locked("Error: Could not set reuse address option on DHCP socket (%s)!\n", iname);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
@ -100,7 +111,7 @@ static int create_dhcp_socket(const char *iname)
|
|||
// set the broadcast option - we need this to listen to DHCP broadcast messages
|
||||
if(setsockopt(sock, SOL_SOCKET,SO_BROADCAST, (char *)&flag, sizeof flag) < 0)
|
||||
{
|
||||
logg("Error: Could not set broadcast option on DHCP socket (%s)!", iname);
|
||||
printf_locked("Error: Could not set broadcast option on DHCP socket (%s)!\n", iname);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
@ -109,8 +120,8 @@ static int create_dhcp_socket(const char *iname)
|
|||
strncpy(interface.ifr_ifrn.ifrn_name, iname, IFNAMSIZ-1);
|
||||
if(setsockopt(sock,SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0)
|
||||
{
|
||||
logg("Error: Could not bind socket to interface %s (%s)\n ---> Check your privileges (run with sudo)!\n",
|
||||
iname, strerror(errno));
|
||||
printf_locked("Error: Could not bind socket to interface %s (%s)\n",
|
||||
iname, strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
@ -118,8 +129,8 @@ static int create_dhcp_socket(const char *iname)
|
|||
// bind the socket
|
||||
if(bind(sock, (struct sockaddr *)&dhcp_socket, sizeof(dhcp_socket)) < 0)
|
||||
{
|
||||
logg("Error: Could not bind to DHCP socket (interface %s, port %d, %s)\n ---> Check your privileges (run with sudo)!\n",
|
||||
iname, DHCP_CLIENT_PORT, strerror(errno));
|
||||
printf_locked("Error: Could not bind to DHCP socket (interface %s, port %d, %s)\n",
|
||||
iname, DHCP_CLIENT_PORT, strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
@ -128,114 +139,118 @@ static int create_dhcp_socket(const char *iname)
|
|||
}
|
||||
|
||||
// determines hardware address on client machine
|
||||
static int get_hardware_address(const int sock, const char *iname, unsigned char *mac)
|
||||
int get_hardware_address(const int sock, const char *iname, unsigned char *mac)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
strncpy((char *)&ifr.ifr_name, iname, sizeof(ifr.ifr_name)-1);
|
||||
|
||||
// try and grab hardware address of requested interface
|
||||
int ret = 0;
|
||||
if((ret = ioctl(sock, SIOCGIFHWADDR, &ifr)) < 0){
|
||||
logg(" Error: Could not get hardware address of interface '%s' (socket %d, error: %s)", iname, sock, strerror(errno));
|
||||
if((ret = ioctl(sock, SIOCGIFHWADDR, &ifr)) < 0)
|
||||
{
|
||||
printf_locked(" Error: Could not get hardware address of interface %s: %s\n", iname, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
memcpy(&mac[0], &ifr.ifr_hwaddr.sa_data, 6);
|
||||
#ifdef DEBUG
|
||||
logg_sameline("Hardware address of this interface: ");
|
||||
printf_locked("Hardware address of this interface: ");
|
||||
for (uint8_t i = 0; i < 6; ++i)
|
||||
logg_sameline("%02x%s", mac[i], i < 5 ? ":" : "");
|
||||
logg(" ");
|
||||
printf_locked("%02x%s", mac[i], i < 5 ? ":" : "");
|
||||
printf_locked("\n");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct dhcp_packet_struct
|
||||
struct dhcp_packet_data
|
||||
{
|
||||
u_int8_t op; // packet type
|
||||
u_int8_t htype; // type of hardware address for this machine (Ethernet, etc)
|
||||
u_int8_t hlen; // length of hardware address (of this machine)
|
||||
u_int8_t hops; // hops
|
||||
u_int32_t xid; // random transaction id number - chosen by this machine
|
||||
u_int16_t secs; // seconds used in timing
|
||||
u_int16_t flags; // flags
|
||||
struct in_addr ciaddr; // IP address of this machine (if we already have one)
|
||||
struct in_addr yiaddr; // IP address of this machine (offered by the DHCP server)
|
||||
struct in_addr siaddr; // IP address of DHCP server
|
||||
struct in_addr giaddr; // IP address of DHCP relay
|
||||
unsigned char chaddr [MAX_DHCP_CHADDR_LENGTH]; // hardware address of this machine
|
||||
char sname [MAX_DHCP_SNAME_LENGTH]; // name of DHCP server
|
||||
char file [MAX_DHCP_FILE_LENGTH]; // boot file name (used for diskless booting?)
|
||||
char options[MAX_DHCP_OPTIONS_LENGTH]; // options
|
||||
} dhcp_packet_data;
|
||||
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
u_int8_t op; // packet type
|
||||
u_int8_t htype; // type of hardware address for this machine (Ethernet, etc)
|
||||
u_int8_t hlen; // length of hardware address (of this machine)
|
||||
u_int8_t hops; // hops
|
||||
u_int32_t xid; // random transaction id number - chosen by this machine
|
||||
u_int16_t secs; // seconds used in timing
|
||||
u_int16_t flags; // flags
|
||||
struct in_addr ciaddr; // IP address of this machine (if we already have one)
|
||||
struct in_addr yiaddr; // IP address of this machine (offered by the DHCP server)
|
||||
struct in_addr siaddr; // IP address of DHCP server
|
||||
struct in_addr giaddr; // IP address of DHCP relay
|
||||
unsigned char chaddr [MAX_DHCP_CHADDR_LENGTH]; // hardware address of this machine
|
||||
char sname [MAX_DHCP_SNAME_LENGTH]; // name of DHCP server
|
||||
char file [MAX_DHCP_FILE_LENGTH]; // boot file name (used for diskless booting?)
|
||||
char options[MAX_DHCP_OPTIONS_LENGTH]; // options
|
||||
};
|
||||
|
||||
// sends a DHCPDISCOVER message to the specified in an attempt to find DHCP servers
|
||||
static bool send_dhcp_discover(const int sock, const uint32_t xid, const char *iface, unsigned char *mac, const in_addr_t addr)
|
||||
static bool send_dhcp_discover(const int sock, const uint32_t xid, const char *iface, unsigned char *mac)
|
||||
{
|
||||
dhcp_packet_data discover_packet;
|
||||
struct dhcp_packet_data discover_packet = { 0 };
|
||||
|
||||
// clear the packet data structure
|
||||
memset(&discover_packet, 0, sizeof(discover_packet));
|
||||
// Boot request flag (backward compatible with BOOTP servers)
|
||||
discover_packet.op = 1; // BOOTREQUEST
|
||||
|
||||
// boot request flag (backward compatible with BOOTP servers)
|
||||
discover_packet.op = BOOTREQUEST;
|
||||
// Hardware address type
|
||||
discover_packet.htype = 1; // ETHERNET_HARDWARE_ADDRESS
|
||||
|
||||
// hardware address type
|
||||
discover_packet.htype = 1; // ETHERNET_HARDWARE_ADDRESS;
|
||||
|
||||
// length of our hardware address
|
||||
discover_packet.hlen = 6; // ETHERNET_HARDWARE_ADDRESS_LENGTH;
|
||||
// Length of our hardware address
|
||||
discover_packet.hlen = 6; // ETHERNET_HARDWARE_ADDRESS_LENGTH
|
||||
discover_packet.hops = 0;
|
||||
|
||||
// transaction id is supposed to be random
|
||||
// Transaction id is supposed to be random
|
||||
discover_packet.xid = htonl(xid);
|
||||
ntohl(discover_packet.xid);
|
||||
discover_packet.secs = 0x00;
|
||||
|
||||
// tell server it should broadcast its response
|
||||
// Tell server it should broadcast its response
|
||||
discover_packet.flags = htons(32768); // DHCP_BROADCAST_FLAG
|
||||
|
||||
// our hardware address
|
||||
// Our hardware address
|
||||
memcpy(discover_packet.chaddr, mac, 6);
|
||||
|
||||
// first four bytes of options field is magic cookie (as per RFC 2132)
|
||||
// First four bytes of options field are the magic cookie (as per RFC 2132)
|
||||
discover_packet.options[0] = '\x63';
|
||||
discover_packet.options[1] = '\x82';
|
||||
discover_packet.options[2] = '\x53';
|
||||
discover_packet.options[3] = '\x63';
|
||||
|
||||
// DHCP message type is embedded in options field
|
||||
discover_packet.options[4] = 53; // DHCP message type option identifier
|
||||
discover_packet.options[5] = '\x01'; // DHCP message option length in bytes
|
||||
discover_packet.options[6] = 1; // DHCP message type code for DHCPDISCOVER
|
||||
discover_packet.options[4] = 53; // DHCP message type option identifier
|
||||
discover_packet.options[5] = 1; // DHCP message option length in bytes
|
||||
discover_packet.options[6] = 1; // DHCP message type code for DHCPDISCOVER
|
||||
|
||||
// Place end option at the end of the options
|
||||
discover_packet.options[7] = 255;
|
||||
|
||||
// send the DHCPDISCOVER packet to the specified address
|
||||
struct sockaddr_in target;
|
||||
// Send the DHCPDISCOVER packet to the specified address
|
||||
struct sockaddr_in target = { 0 };
|
||||
target.sin_family = AF_INET;
|
||||
target.sin_port = htons(DHCP_SERVER_PORT);
|
||||
target.sin_addr.s_addr = addr;
|
||||
memset(&target.sin_zero, 0, sizeof(target.sin_zero));
|
||||
target.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
|
||||
#ifdef DEBUG
|
||||
logg("Sending DHCPDISCOVER on interface %s:%s ... ", iface, inet_ntoa(target.sin_addr));
|
||||
logg("DHCPDISCOVER XID: %lu (0x%X)", (unsigned long) ntohl(discover_packet.xid), ntohl(discover_packet.xid));
|
||||
logg("DHCDISCOVER ciaddr: %s", inet_ntoa(discover_packet.ciaddr));
|
||||
logg("DHCDISCOVER yiaddr: %s", inet_ntoa(discover_packet.yiaddr));
|
||||
logg("DHCDISCOVER siaddr: %s", inet_ntoa(discover_packet.siaddr));
|
||||
logg("DHCDISCOVER giaddr: %s", inet_ntoa(discover_packet.giaddr));
|
||||
printf_locked("Sending DHCPDISCOVER on interface %s@%s ... \n", inet_ntoa(target.sin_addr), iface);
|
||||
printf_locked("DHCPDISCOVER XID: %lu (0x%X)\n", (unsigned long) ntohl(discover_packet.xid), ntohl(discover_packet.xid));
|
||||
printf_locked("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr));
|
||||
printf_locked("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr));
|
||||
printf_locked("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr));
|
||||
printf_locked("DHCDISCOVER giaddr: %s\n", inet_ntoa(discover_packet.giaddr));
|
||||
#endif
|
||||
// send the DHCPDISCOVER packet
|
||||
const int bytes = sendto(sock, (char *)&discover_packet, sizeof(discover_packet), 0, (struct sockaddr *)&target,sizeof(target));
|
||||
#ifdef DEBUG
|
||||
logg("Sent %d bytes", bytes);
|
||||
#endif
|
||||
const int bytes = sendto(sock, (char *)&discover_packet, sizeof(discover_packet), 0, (struct sockaddr *)&target, sizeof(target));
|
||||
if(bytes < 0)
|
||||
{
|
||||
// strerror() returns "Required key not available" for ENOKEY
|
||||
// which is not helpful at all so we substitute a more
|
||||
// meaningful error message for ENOKEY returned by wireguard interfaces
|
||||
// (see https://www.wireguard.com/papers/wireguard.pdf, page 5)
|
||||
const char *error = errno == ENOKEY ? "No route to host (no such peer available)" : strerror(errno);
|
||||
printf_locked("Error: Could not send DHCPDISCOVER to %s@%s: %s\n",
|
||||
inet_ntoa(target.sin_addr), iface, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bytes > 0;
|
||||
#ifdef DEBUG
|
||||
printf_locked("Sent %d bytes\n", bytes);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef TEST_OPT_249
|
||||
|
@ -251,7 +266,7 @@ static void gen_249_test_data(dhcp_packet_data *offer_packet)
|
|||
#endif
|
||||
|
||||
// adds a DHCP OFFER to list in memory
|
||||
static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_packet)
|
||||
static void print_dhcp_offer(struct in_addr source, struct dhcp_packet_data *offer_packet)
|
||||
{
|
||||
if(offer_packet == NULL)
|
||||
return;
|
||||
|
@ -272,7 +287,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
// Sanity check
|
||||
if(x >= MAX_DHCP_OPTIONS_LENGTH-2)
|
||||
{
|
||||
logg(" OVERFLOWING DHCP OPTION (invalid size)");
|
||||
printf(" OVERFLOWING DHCP OPTION (invalid size)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -282,12 +297,12 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
// get option length
|
||||
const uint8_t optlen = offer_packet->options[x++];
|
||||
|
||||
logg_sameline(" ");
|
||||
printf(" ");
|
||||
|
||||
// Sanity check
|
||||
if(x + optlen > MAX_DHCP_OPTIONS_LENGTH)
|
||||
{
|
||||
logg(" OVERFLOWING DHCP OPTION (invalid size)");
|
||||
printf(" OVERFLOWING DHCP OPTION (invalid size)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -307,14 +322,14 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
struct in_addr addr_list = { 0 };
|
||||
memcpy(&addr_list.s_addr, &offer_packet->options[x+n*4], sizeof(addr_list.s_addr));
|
||||
if(n > 0)
|
||||
logg_sameline(" ");
|
||||
printf(" ");
|
||||
|
||||
logg("%s: %s", opttab[i].name, inet_ntoa(addr_list));
|
||||
printf("%s: %s\n", opttab[i].name, inet_ntoa(addr_list));
|
||||
}
|
||||
|
||||
// Special case: optlen == 0
|
||||
if(optlen == 0)
|
||||
logg("--- end of options ---");
|
||||
printf("--- end of options ---\n");
|
||||
}
|
||||
else if(opttab[i].size & OT_NAME)
|
||||
{
|
||||
|
@ -323,7 +338,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
// possible "(empty)"
|
||||
char buffer[4*optlen + 9];
|
||||
binbuf_to_escaped_C_literal(&offer_packet->options[x], optlen, buffer, sizeof(buffer));
|
||||
logg("%s: \"%s\"", opttab[i].name, buffer);
|
||||
printf("%s: \"%s\"\n", opttab[i].name, buffer);
|
||||
}
|
||||
else if(opttab[i].size & OT_TIME)
|
||||
{
|
||||
|
@ -338,12 +353,12 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
optname = "rebinding-time"; // "T2" in dnsmasq-notation
|
||||
|
||||
if(time == 0xFFFFFFFF)
|
||||
logg("%s: Infinite", optname);
|
||||
printf("%s: Infinite\n", optname);
|
||||
else
|
||||
{
|
||||
char buffer[42] = { 0 };
|
||||
format_time(buffer, time, 0.0);
|
||||
logg("%s: %lu (%s)", optname, (unsigned long)time, buffer);
|
||||
printf("%s: %lu (%s)\n", optname, (unsigned long)time, buffer);
|
||||
}
|
||||
}
|
||||
else if(opttab[i].size & OT_DEC)
|
||||
|
@ -353,31 +368,31 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
switch(offer_packet->options[x])
|
||||
{
|
||||
case 1:
|
||||
logg("Message type: DHCPDISCOVER (1)");
|
||||
printf("Message type: DHCPDISCOVER (1)\n");
|
||||
break;
|
||||
case 2:
|
||||
logg("Message type: DHCPOFFER (2)");
|
||||
printf("Message type: DHCPOFFER (2)\n");
|
||||
break;
|
||||
case 3:
|
||||
logg("Message type: DHCPREQUEST (3)");
|
||||
printf("Message type: DHCPREQUEST (3)\n");
|
||||
break;
|
||||
case 4:
|
||||
logg("Message type: DHCPDECLINE (4)");
|
||||
printf("Message type: DHCPDECLINE (4)\n");
|
||||
break;
|
||||
case 5:
|
||||
logg("Message type: DHCPACK (5)");
|
||||
printf("Message type: DHCPACK (5)\n");
|
||||
break;
|
||||
case 6:
|
||||
logg("Message type: DHCPNAK (6)");
|
||||
printf("Message type: DHCPNAK (6)\n");
|
||||
break;
|
||||
case 7:
|
||||
logg("Message type: DHCPRELEASE (7)");
|
||||
printf("Message type: DHCPRELEASE (7)\n");
|
||||
break;
|
||||
case 8:
|
||||
logg("Message type: DHCPINFORM (8)");
|
||||
printf("Message type: DHCPINFORM (8)\n");
|
||||
break;
|
||||
default:
|
||||
logg("Message type: UNKNOWN (%u)", offer_packet->options[x]);
|
||||
printf("Message type: UNKNOWN (%u)\n", offer_packet->options[x]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -392,7 +407,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
number = ntohs(number);
|
||||
else if(optlen == 4)
|
||||
number = ntohl(number);
|
||||
logg("%s: %u", opttab[i].name, number);
|
||||
printf("%s: %u\n", opttab[i].name, number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +424,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
// possible "(empty)"
|
||||
char buffer[4*optlen + 9];
|
||||
binbuf_to_escaped_C_literal(&offer_packet->options[x], optlen, buffer, sizeof(buffer));
|
||||
logg("wpad-server: \"%s\"", buffer);
|
||||
printf("wpad-server: \"%s\"\n", buffer);
|
||||
}
|
||||
else if(opttype == 158) // DHCPv4 PCP Option (RFC 7291)
|
||||
{ // https://tools.ietf.org/html/rfc7291#section-4
|
||||
|
@ -422,9 +437,9 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
break;
|
||||
memcpy(&addr_list.s_addr, &offer_packet->options[x+n*sizeof(addr_list.s_addr)], sizeof(addr_list.s_addr));
|
||||
if(n > 0)
|
||||
logg_sameline(" ");
|
||||
printf(" ");
|
||||
|
||||
logg("Port Control Protocol (PCP) server: %s", inet_ntoa(addr_list));
|
||||
printf("Port Control Protocol (PCP) server: %s\n", inet_ntoa(addr_list));
|
||||
}
|
||||
}
|
||||
else if((opttype == 121 || opttype == 249) && optlen > 4)
|
||||
|
@ -433,7 +448,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
// see
|
||||
// - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dhcpe/f9c19c79-1c7f-4746-b555-0c0fc523f3f9
|
||||
// - https://datatracker.ietf.org/doc/html/rfc3442 (page 3)
|
||||
logg("%s Classless Static Route:", opttype == 121 ? "RFC 3442" : "Microsoft");
|
||||
printf("%s Classless Static Route:", opttype == 121 ? "RFC 3442" : "Microsoft");
|
||||
// Loop over contained routes
|
||||
unsigned int n = 0;
|
||||
for(unsigned int i = 1; n < optlen; i++)
|
||||
|
@ -458,26 +473,26 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
if(cidr == 0)
|
||||
{
|
||||
// default route (0.0.0.0/0)
|
||||
logg(" %d: default via %d.%d.%d.%d", i,
|
||||
router[0], router[1], router[2], router[3]);
|
||||
printf(" %d: default via %d.%d.%d.%d", i,
|
||||
router[0], router[1], router[2], router[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// specific route
|
||||
logg(" %d: %d.%d.%d.%d/%d via %d.%d.%d.%d", i,
|
||||
addr[0], addr[1], addr[2], addr[3], cidr,
|
||||
router[0], router[1], router[2], router[3]);
|
||||
printf(" %d: %d.%d.%d.%d/%d via %d.%d.%d.%d", i,
|
||||
addr[0], addr[1], addr[2], addr[3], cidr,
|
||||
router[0], router[1], router[2], router[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logg_sameline("Unknown option %d:", opttype);
|
||||
printf("Unknown option %d:", opttype);
|
||||
// Print bytes
|
||||
for(unsigned i = 0; i < optlen; i++)
|
||||
logg_sameline(" %02X", (unsigned char)offer_packet->options[x+i]);
|
||||
printf(" %02X", (unsigned char)offer_packet->options[x+i]);
|
||||
// Add newline when done above
|
||||
logg(" (length %d)", optlen);
|
||||
printf(" (length %d)\n", optlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,7 +501,7 @@ static void print_dhcp_offer(struct in_addr source, dhcp_packet_data *offer_pack
|
|||
}
|
||||
|
||||
// Add one empty line for readability
|
||||
logg(" ");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// receives a DHCP packet
|
||||
|
@ -512,13 +527,14 @@ static bool receive_dhcp_packet(void *buffer, int buffer_size, const char *iface
|
|||
address_size = sizeof(struct sockaddr_in);
|
||||
recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)address, &address_size);
|
||||
|
||||
logg("* Received %d bytes from %s:%s", recv_result, iface, inet_ntoa(address->sin_addr));
|
||||
printf_locked("\n* Received %d bytes from %s:%s\n", recv_result, iface, inet_ntoa(address->sin_addr));
|
||||
#ifdef DEBUG
|
||||
logg(" after waiting for %f seconds", difftime(time(NULL), start_time));
|
||||
printf_locked(" after waiting for %f seconds\n", difftime(time(NULL), start_time));
|
||||
#endif
|
||||
// Return on error
|
||||
if(recv_result == -1){
|
||||
logg(" recvfrom() failed on %s, error: %s", iface, strerror(errno));
|
||||
if(recv_result == -1)
|
||||
{
|
||||
printf_locked(" recvfrom() failed on %s, error: %s\n", iface, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -526,9 +542,9 @@ static bool receive_dhcp_packet(void *buffer, int buffer_size, const char *iface
|
|||
}
|
||||
|
||||
// waits for a DHCPOFFER message from one or more DHCP servers
|
||||
static bool get_dhcp_offer(const int sock, const uint32_t xid, const char *iface, unsigned char *mac)
|
||||
static int get_dhcp_offer(const int sock, const uint32_t xid, const char *iface, unsigned char *mac)
|
||||
{
|
||||
dhcp_packet_data offer_packet;
|
||||
struct dhcp_packet_data offer_packet;
|
||||
struct sockaddr_in source;
|
||||
#ifdef DEBUG
|
||||
unsigned int responses = 0;
|
||||
|
@ -553,17 +569,17 @@ static bool get_dhcp_offer(const int sock, const uint32_t xid, const char *iface
|
|||
#endif
|
||||
|
||||
if(pthread_mutex_lock(&lock) != 0)
|
||||
return false;
|
||||
return -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
logg(" DHCPOFFER XID: %lu (0x%X)", (unsigned long) ntohl(offer_packet.xid), ntohl(offer_packet.xid));
|
||||
printf(" DHCPOFFER XID: %lu (0x%X)\n", (unsigned long) ntohl(offer_packet.xid), ntohl(offer_packet.xid));
|
||||
#endif
|
||||
|
||||
// check packet xid to see if its the same as the one we used in the discover packet
|
||||
if(ntohl(offer_packet.xid) != xid)
|
||||
{
|
||||
logg(" DHCPOFFER XID (%lu) does not match our DHCPDISCOVER XID (%lu) - ignoring packet (not for us)\n",
|
||||
(unsigned long) ntohl(offer_packet.xid), (unsigned long) xid);
|
||||
printf(" DHCPOFFER XID (%lu) does not match our DHCPDISCOVER XID (%lu) - ignoring packet (not for us)\n",
|
||||
(unsigned long) ntohl(offer_packet.xid), (unsigned long) xid);
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
continue;
|
||||
|
@ -572,78 +588,78 @@ static bool get_dhcp_offer(const int sock, const uint32_t xid, const char *iface
|
|||
// check hardware address
|
||||
if(memcmp(offer_packet.chaddr, mac, 6) != 0)
|
||||
{
|
||||
logg(" DHCPOFFER hardware address did not match our own - ignoring packet (not for us)");
|
||||
printf(" DHCPOFFER hardware address did not match our own - ignoring packet (not for us)\n");
|
||||
|
||||
logg_sameline(" DHCPREQUEST chaddr: ");
|
||||
printf(" DHCPREQUEST chaddr: ");
|
||||
for(uint8_t x = 0; x < 6; x++)
|
||||
logg_sameline("%02x%s", mac[x], x < 5 ? ":" : "");
|
||||
logg(" (our MAC address)");
|
||||
printf("%02x%s", mac[x], x < 5 ? ":" : "");
|
||||
printf(" (our MAC address)\n");
|
||||
|
||||
logg_sameline(" DHCPOFFER chaddr: ");
|
||||
printf(" DHCPOFFER chaddr: ");
|
||||
for(uint8_t x = 0; x < 6; x++)
|
||||
logg_sameline("%02x%s", offer_packet.chaddr[x], x < 5 ? ":" : "");
|
||||
logg(" (response MAC address)");
|
||||
printf("%02x%s", offer_packet.chaddr[x], x < 5 ? ":" : "");
|
||||
printf(" (response MAC address)\n");
|
||||
|
||||
pthread_mutex_unlock(&lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
logg_sameline(" Offered IP address: ");
|
||||
printf(" Offered IP address: ");
|
||||
if(offer_packet.yiaddr.s_addr != 0)
|
||||
logg("%s", inet_ntoa(offer_packet.yiaddr));
|
||||
printf("%s\n", inet_ntoa(offer_packet.yiaddr));
|
||||
else
|
||||
logg("N/A");
|
||||
printf("N/A\n");
|
||||
|
||||
logg_sameline(" Server IP address: ");
|
||||
printf(" Server IP address: ");
|
||||
if(offer_packet.siaddr.s_addr != 0)
|
||||
logg("%s", inet_ntoa(offer_packet.siaddr));
|
||||
printf("%s\n", inet_ntoa(offer_packet.siaddr));
|
||||
else
|
||||
logg("N/A");
|
||||
printf("N/A\n");
|
||||
|
||||
logg_sameline(" Relay-agent IP address: ");
|
||||
printf(" Relay-agent IP address: ");
|
||||
if(offer_packet.giaddr.s_addr != 0)
|
||||
logg("%s", inet_ntoa(offer_packet.giaddr));
|
||||
printf("%s\n", inet_ntoa(offer_packet.giaddr));
|
||||
else
|
||||
logg("N/A");
|
||||
printf("N/A\n");
|
||||
|
||||
logg_sameline(" BOOTP server: ");
|
||||
printf(" BOOTP server: ");
|
||||
if(offer_packet.sname[0] != 0)
|
||||
{
|
||||
size_t len = strlen(offer_packet.sname);
|
||||
char buffer[4*len + 9];
|
||||
binbuf_to_escaped_C_literal(offer_packet.sname, len, buffer, sizeof(buffer));
|
||||
logg("%s", buffer);
|
||||
printf("%s\n", buffer);
|
||||
}
|
||||
else
|
||||
logg("(empty)");
|
||||
printf("(empty)\n");
|
||||
|
||||
logg_sameline(" BOOTP file: ");
|
||||
printf(" BOOTP file: ");
|
||||
if(offer_packet.file[0] != 0)
|
||||
{
|
||||
size_t len = strlen(offer_packet.file);
|
||||
char buffer[4*len + 9];
|
||||
binbuf_to_escaped_C_literal(offer_packet.file, len, buffer, sizeof(buffer));
|
||||
logg("%s", buffer);
|
||||
printf("%s\n", buffer);
|
||||
}
|
||||
else
|
||||
logg("(empty)");
|
||||
printf("(empty)\n");
|
||||
|
||||
logg(" DHCP options:");
|
||||
printf(" DHCP options:\n");
|
||||
print_dhcp_offer(source.sin_addr, &offer_packet);
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
valid_responses++;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
logg(" Responses seen while scanning: %d", responses);
|
||||
logg(" Responses meant for this machine: %d\n", valid_responses);
|
||||
printf(" Responses seen while scanning: %d\n", responses);
|
||||
printf(" Responses meant for this machine: %d\n\n", valid_responses);
|
||||
#endif
|
||||
logg("DHCP packets received on interface %s: %u", iface, valid_responses);
|
||||
return true;
|
||||
return valid_responses;
|
||||
}
|
||||
|
||||
static void *dhcp_discover_iface(void *args)
|
||||
{
|
||||
int valid_responses = -1;
|
||||
// Get interface details
|
||||
const char *iface = ((struct ifaddrs*)args)->ifa_name;
|
||||
|
||||
|
@ -655,7 +671,7 @@ static void *dhcp_discover_iface(void *args)
|
|||
|
||||
// Cannot create socket, likely a permission error
|
||||
if(dhcp_socket < 0)
|
||||
pthread_exit(NULL);
|
||||
goto end_dhcp_discover_iface;
|
||||
|
||||
// get hardware address of client machine
|
||||
unsigned char mac[MAX_DHCP_CHADDR_LENGTH] = { 0 };
|
||||
|
@ -665,32 +681,34 @@ static void *dhcp_discover_iface(void *args)
|
|||
srand(time(NULL));
|
||||
const uint32_t xid = random();
|
||||
|
||||
if(strcmp(iface, "lo") == 0)
|
||||
{
|
||||
// Probe a local server listening on this interface
|
||||
// Send DHCPDISCOVER packet to interface address
|
||||
struct sockaddr_in ifaddr = { 0 };
|
||||
memcpy(&ifaddr, ((struct ifaddrs*)args)->ifa_addr, sizeof(ifaddr));
|
||||
send_dhcp_discover(dhcp_socket, xid, iface, mac, ifaddr.sin_addr.s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Probe distant servers
|
||||
// Send DHCPDISCOVER packet to broadcast address
|
||||
send_dhcp_discover(dhcp_socket, xid, iface, mac, INADDR_BROADCAST);
|
||||
}
|
||||
// Probe servers on this interface
|
||||
if(!send_dhcp_discover(dhcp_socket, xid, iface, mac))
|
||||
goto end_dhcp_discover_iface;
|
||||
|
||||
// wait for a DHCPOFFER packet
|
||||
get_dhcp_offer(dhcp_socket, xid, iface, mac);
|
||||
valid_responses = get_dhcp_offer(dhcp_socket, xid, iface, mac);
|
||||
|
||||
// close socket we created
|
||||
close(dhcp_socket);
|
||||
end_dhcp_discover_iface:
|
||||
// Close socket if we created one
|
||||
if(dhcp_socket > 0)
|
||||
close(dhcp_socket);
|
||||
|
||||
if(valid_responses >= 0)
|
||||
printf_locked("DHCP packets received on interface %s: %u\n", iface, valid_responses);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int run_dhcp_discover(void)
|
||||
{
|
||||
// Check if we are capable of binding to port 67 (DHCP)
|
||||
// DHCP uses normal UDP datagrams, so we cdon't need CAP_NET_RAW
|
||||
if(!check_capability(CAP_NET_BIND_SERVICE))
|
||||
{
|
||||
puts("Error: Insufficient permissions or capabilities (needs CAP_NET_BIND_SERVICE). Try running as root (sudo)");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Disable terminal output during config config file parsing
|
||||
log_ctrl(false, false);
|
||||
// Process pihole-FTL.conf to get gravity.db
|
||||
|
@ -698,8 +716,8 @@ int run_dhcp_discover(void)
|
|||
// Only print to terminal, disable log file
|
||||
log_ctrl(false, true);
|
||||
|
||||
logg("Scanning all your interfaces for DHCP servers");
|
||||
logg("Timeout: %d seconds\n", DHCPOFFER_TIMEOUT);
|
||||
printf("Scanning all your interfaces for DHCP servers\n");
|
||||
printf("Timeout: %d seconds\n", DHCPOFFER_TIMEOUT);
|
||||
|
||||
// Get interface names for available interfaces on this machine
|
||||
// and launch a thread for each one
|
||||
|
@ -726,13 +744,27 @@ int run_dhcp_discover(void)
|
|||
int tid = 0;
|
||||
while(tmp != NULL && tid < MAXTHREADS)
|
||||
{
|
||||
// Create a thread for interfaces of type AF_PACKET
|
||||
if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET)
|
||||
// Create a thread for interfaces of type AF_INET (IPv4)
|
||||
if(tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
// Skip interface scan if ...
|
||||
// - interface is not up
|
||||
// - broadcast is not supported
|
||||
// - interface is loopback net
|
||||
if(!(tmp->ifa_flags & IFF_UP) ||
|
||||
!(tmp->ifa_flags & IFF_BROADCAST) ||
|
||||
tmp->ifa_flags & IFF_LOOPBACK)
|
||||
{
|
||||
tmp = tmp->ifa_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a probing thread for this interface
|
||||
if(pthread_create(&scanthread[tid], &attr, dhcp_discover_iface, tmp ) != 0)
|
||||
{
|
||||
logg("Unable to launch thread for interface %s, skipping...",
|
||||
tmp->ifa_name);
|
||||
printf_locked("Unable to launch thread for interface %s, skipping...",
|
||||
tmp->ifa_name);
|
||||
tmp = tmp->ifa_next;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -12,5 +12,6 @@
|
|||
#define DHCP_DISCOVER_H
|
||||
|
||||
int run_dhcp_discover(void);
|
||||
int get_hardware_address(const int sock, const char *iname, unsigned char *mac);
|
||||
|
||||
#endif // DHCP_DISCOVER_H
|
|
@ -0,0 +1,370 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Gravity parseList routines
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "tools/gravity-parseList.h"
|
||||
#include "args.h"
|
||||
#include <regex.h>
|
||||
#include "database/sqlite3.h"
|
||||
|
||||
// Define valid domain patterns
|
||||
// No need to include uppercase letters, as we convert to lowercase in gravity_ParseFileIntoDomains() already
|
||||
// Adapted from https://stackoverflow.com/a/30007882
|
||||
// - Added "(?:...)" to form non-capturing groups (slightly faster)
|
||||
#define TLD_PATTERN "[a-z0-9][a-z0-9-]{0,61}[a-z0-9]"
|
||||
#define SUBDOMAIN_PATTERN "([a-z0-9_-]{0,63}\\.)"
|
||||
|
||||
// supported exact style: subdomain.domain.tld
|
||||
// SUBDOMAIN_PATTERN is mandatory for exact style, disallowing TLD blocking
|
||||
#define VALID_DOMAIN_REXEX SUBDOMAIN_PATTERN"+"TLD_PATTERN
|
||||
|
||||
// supported ABP style: ||subdomain.domain.tlp^
|
||||
// SUBDOMAIN_PATTERN is optional for ABP style, allowing TLD blocking: ||tld^
|
||||
// See https://github.com/pi-hole/pi-hole/pull/5240
|
||||
#define ABP_DOMAIN_REXEX "\\|\\|"SUBDOMAIN_PATTERN"*"TLD_PATTERN"\\^"
|
||||
|
||||
// A list of items of common local hostnames not to report as unusable
|
||||
// Some lists (i.e StevenBlack's) contain these as they are supposed to be used as HOST files
|
||||
// but flagging them as unusable causes more confusion than it's worth - so we suppress them from the output
|
||||
#define FALSE_POSITIVES "^(localhost|localhost.localdomain|local|broadcasthost|localhost|ip6-localhost|ip6-loopback|lo0 localhost|ip6-localnet|ip6-mcastprefix|ip6-allnodes|ip6-allrouters|ip6-allhosts)$"
|
||||
|
||||
// Print progress for files larger than 10 MB
|
||||
// This is to avoid printing progress for small files
|
||||
// which would be printed too often as affect performance
|
||||
#define PRINT_PROGRESS_THRESHOLD 10*1000*1000
|
||||
|
||||
// Number of invalid domains to print before skipping the rest
|
||||
#define MAX_INVALID_DOMAINS 5
|
||||
|
||||
int gravity_parseList(const char *infile, const char *outfile, const char *adlistIDstr)
|
||||
{
|
||||
const char *info = cli_info();
|
||||
const char *tick = cli_tick();
|
||||
const char *cross = cli_cross();
|
||||
const char *over = cli_over();
|
||||
|
||||
// Open input file
|
||||
FILE *fpin = fopen(infile, "r");
|
||||
if(fpin == NULL)
|
||||
{
|
||||
printf("%s %s Unable to open %s for reading\n", over, cross, infile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Open output file
|
||||
sqlite3 *db = NULL;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
if(sqlite3_open_v2(outfile, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to open database file %s for writing\n", over, cross, outfile);
|
||||
fclose(fpin);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Get size of input file
|
||||
fseek(fpin, 0L, SEEK_END);
|
||||
const size_t fsize = ftell(fpin);
|
||||
rewind(fpin);
|
||||
|
||||
// Compile regular expression to validate domains
|
||||
regex_t exact_regex, abp_regex, false_positives_regex;
|
||||
if(regcomp(&exact_regex, VALID_DOMAIN_REXEX, REG_EXTENDED) != 0)
|
||||
{
|
||||
printf("%s %s Unable to compile regular expression to validate exact domains\n",
|
||||
over, cross);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(regcomp(&abp_regex, ABP_DOMAIN_REXEX, REG_EXTENDED) != 0)
|
||||
{
|
||||
printf("%s %s Unable to compile regular expression to validate ABP-style domains\n",
|
||||
over, cross);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(regcomp(&false_positives_regex, FALSE_POSITIVES, REG_EXTENDED | REG_NOSUB) != 0)
|
||||
{
|
||||
printf("%s %s Unable to compile regular expression to identify false positives\n",
|
||||
over, cross);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
if(sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to begin transaction to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Prepare SQL statement
|
||||
const char *sql = "INSERT INTO gravity (domain, adlist_id) VALUES (?, ?);";
|
||||
if(sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to prepare SQL statement to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Bind adlistID
|
||||
const int adlistID = atoi(adlistIDstr);
|
||||
if(sqlite3_bind_int(stmt, 2, adlistID) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind adlistID to SQL statement to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Parse list file line by line
|
||||
char *line = NULL;
|
||||
size_t lineno = 0;
|
||||
size_t len = 0;
|
||||
ssize_t read = 0;
|
||||
size_t total_read = 0;
|
||||
int last_progress = 0;
|
||||
char *invalid_domains_list[MAX_INVALID_DOMAINS] = { NULL };
|
||||
unsigned int invalid_domains_list_len = 0;
|
||||
unsigned int exact_domains = 0, abp_domains = 0, invalid_domains = 0;
|
||||
while((read = getline(&line, &len, fpin)) != -1)
|
||||
{
|
||||
// Update total read bytes
|
||||
total_read += read;
|
||||
lineno++;
|
||||
|
||||
// Remove trailing newline
|
||||
if(line[read-1] == '\n')
|
||||
line[--read] = '\0';
|
||||
|
||||
// Remove trailing dot (convert FQDN to domain)
|
||||
if(line[read-1] == '.')
|
||||
line[--read] = '\0';
|
||||
|
||||
|
||||
regmatch_t match = { 0 };
|
||||
// Validate line
|
||||
if(line[0] != '|' && // <- Not an ABP-style match
|
||||
regexec(&exact_regex, line, 1, &match, 0) == 0 && // <- Regex match
|
||||
match.rm_so == 0 && match.rm_eo == read) // <- Match covers entire line
|
||||
{
|
||||
// Exact match found
|
||||
// Append domain to database using prepared statement
|
||||
if(sqlite3_bind_text(stmt, 1, line, -1, SQLITE_STATIC) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind domain to SQL statement to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_step(stmt) != SQLITE_DONE)
|
||||
{
|
||||
printf("%s %s Unable to insert domain into database file %s\n", over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
sqlite3_reset(stmt);
|
||||
// Increment counter
|
||||
exact_domains++;
|
||||
}
|
||||
else if(line[0] == '|' && // <- ABP-style match
|
||||
regexec(&abp_regex, line, 1, &match, 0) == 0 && // <- Regex match
|
||||
match.rm_so == 0 && match.rm_eo == read) // <- Match covers entire line
|
||||
{
|
||||
// ABP-style match (see comments above)
|
||||
|
||||
// Append pattern to database using prepared statement
|
||||
if(sqlite3_bind_text(stmt, 1, line, -1, SQLITE_STATIC) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind domain to SQL statement to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_step(stmt) != SQLITE_DONE)
|
||||
{
|
||||
printf("%s %s Unable to insert domain into database file %s\n", over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
sqlite3_reset(stmt);
|
||||
abp_domains++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match - This is an invalid domain or a false positive
|
||||
|
||||
// Ignore false positives - they don't count as invalid domains
|
||||
if(regexec(&false_positives_regex, line, 0, NULL, 0) != 0)
|
||||
{
|
||||
// Add the domain to invalid_domains_list only
|
||||
// if the list contains < MAX_INVALID_DOMAINS
|
||||
if(invalid_domains_list_len < MAX_INVALID_DOMAINS)
|
||||
{
|
||||
// Check if we have this domain already
|
||||
bool found = false;
|
||||
for(unsigned int i = 0; i < invalid_domains_list_len; i++)
|
||||
{
|
||||
if(strcmp(invalid_domains_list[i], line) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, add it to the list
|
||||
if(!found)
|
||||
invalid_domains_list[invalid_domains_list_len++] = strdup(line);
|
||||
|
||||
}
|
||||
invalid_domains++;
|
||||
}
|
||||
}
|
||||
|
||||
// Print progress if the file is large enough every 100 lines
|
||||
if(fsize > PRINT_PROGRESS_THRESHOLD && lineno % 100 == 1)
|
||||
{
|
||||
// Calculate progress
|
||||
const int progress = (int)(100.0*total_read/fsize);
|
||||
// Print progress if it has changed
|
||||
if(progress > last_progress)
|
||||
{
|
||||
printf("%s %s Processed %i%% of downloaded list", over, info, progress);
|
||||
fflush(stdout);
|
||||
last_progress = progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize SQL statement
|
||||
if(sqlite3_finalize(stmt) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to finalize SQL statement to insert domains into database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Update database properties
|
||||
// Are ABP patterns used?
|
||||
if(abp_domains > 0)
|
||||
{
|
||||
sql = "INSERT OR REPLACE INTO info (property,value) VALUES ('abp_domains',1);";
|
||||
if(sqlite3_exec(db, sql, NULL, NULL, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to update database properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Update number of domains and update timestamp on this list
|
||||
sql = "UPDATE adlist SET number = ?, invalid_domains = ?, date_updated = cast(strftime('%s', 'now') as int) WHERE id = ?;";
|
||||
if(sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to prepare SQL statement to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Update date
|
||||
if(sqlite3_bind_int(stmt, 1, exact_domains) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind number of domains to SQL statement to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_bind_int(stmt, 2, invalid_domains) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind number of invalid domains to SQL statement to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_bind_int(stmt, 3, adlistID) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to bind adlist ID to SQL statement to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_step(stmt) != SQLITE_DONE)
|
||||
{
|
||||
printf("%s %s Unable to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(sqlite3_finalize(stmt) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to finalize SQL statement to update adlist properties in database file %s\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// End transaction
|
||||
if(sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL) != SQLITE_OK)
|
||||
{
|
||||
printf("%s %s Unable to end transaction to insert domains into database file %s (database file may be corrupted)\n",
|
||||
over, cross, outfile);
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Print summary
|
||||
printf("%s %s Parsed %u exact domains and %u ABP-style domains (ignored %u non-domain entries)\n",
|
||||
over, tick, exact_domains, abp_domains, invalid_domains);
|
||||
if(invalid_domains_list_len > 0)
|
||||
{
|
||||
puts(" Sample of non-domain entries:");
|
||||
for(unsigned int i = 0; i < invalid_domains_list_len; i++)
|
||||
printf(" - \"%s\"\n", invalid_domains_list[i]);
|
||||
puts("");
|
||||
}
|
||||
|
||||
// Free memory
|
||||
free(line);
|
||||
regfree(&exact_regex);
|
||||
regfree(&abp_regex);
|
||||
regfree(&false_positives_regex);
|
||||
for(unsigned int i = 0; i < invalid_domains_list_len; i++)
|
||||
if(invalid_domains_list[i] != NULL)
|
||||
free(invalid_domains_list[i]);
|
||||
|
||||
// Close files
|
||||
fclose(fpin);
|
||||
sqlite3_close(db);
|
||||
|
||||
// Return success
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Gravity parseList prototypes
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "FTL.h"
|
||||
|
||||
int gravity_parseList(const char *infile, const char *outfile, const char *adlistID);
|
|
@ -1356,7 +1356,7 @@
|
|||
@test "Embedded SQLite3 shell available and functional" {
|
||||
run bash -c './pihole-FTL sqlite3 -help'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == "Usage: sqlite3 [OPTIONS] FILENAME [SQL]" ]]
|
||||
[[ ${lines[0]} == "Usage: sqlite3 [OPTIONS] [FILENAME [SQL]]" ]]
|
||||
}
|
||||
|
||||
@test "Embedded SQLite3 shell is called for .db file" {
|
||||
|
|
Loading…
Reference in New Issue