view interps/cfunge/cfunge-src/src/instructions/sysinfo.c @ 12518:2d8fe55c6e65 draft default tip

<int-e> learn The password of the month is release incident pilot.
author HackEso <hackeso@esolangs.org>
date Sun, 03 Nov 2024 00:31:02 +0000
parents 859f9b4339e6
children
line wrap: on
line source

/* -*- mode: C; coding: utf-8; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * cfunge - A standard-conforming Befunge93/98/109 interpreter in C.
 * Copyright (C) 2008-2009 Arvid Norlander <anmaster AT tele2 DOT se>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at the proxy's option) any later version. Arvid Norlander is a
 * proxy who can decide which future versions of the GNU General Public
 * License can be used.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "../global.h"
#include "sysinfo.h"
#include "safe_env.h"

#include "../funge-space/funge-space.h" /* fungespace_get_bounds_rect */
#include "../ip.h"
#include "../main.h"                    /* fungeargc, fungeargv */
#include "../rect.h"
#include "../settings.h"
#include "../stack.h"
#include "../vector.h"

#include <unistd.h> /* environ (partly) */
#include <time.h>   /* gmtime, time, time_t */
#include <string.h> /* strlen */
#include <stdlib.h>
#include <limits.h>

// Temp variable used for pushing of stack size.
static size_t TOSSSize = 0;

#ifdef __WIN32__
// Now, win32 is crap and insane, so we just fake it, much simpler.
static const char * environ[] = {
	"SYSTEM=windows crap",
	"SUPPORTS=not environ at least, get a sane system if you want this to work.",
	"REALLY=we mean it, cfunge on windows is NOT SUPPORTED."
};
#else
#  ifndef _GNU_SOURCE
extern char **environ;
#  endif
#endif

#define FUNGE_FLAGS_CONCURRENT 0x01
#define FUNGE_FLAGS_INPUT      0x02
#define FUNGE_FLAGS_OUTPUT     0x04
#define FUNGE_FLAGS_EXECUTE    0x08
#define FUNGE_FLAGS_STD109     0x20

#define FUNGE_FLAGS_NOTSANDBOX FUNGE_FLAGS_INPUT | FUNGE_FLAGS_OUTPUT | FUNGE_FLAGS_EXECUTE

#ifdef CONCURRENT_FUNGE
#  define FUNGE_FLAGS_BASIC FUNGE_FLAGS_CONCURRENT
#else
#  define FUNGE_FLAGS_BASIC 0x0
#endif

/// We cache the number of environment variables here
static size_t environ_count = 0;

/// Flags
#define PUSH_REQ_1(m_pushstack) \
	do { \
		funge_cell tmp = FUNGE_FLAGS_BASIC; \
		if (!setting_enable_sandbox) { \
			tmp |= FUNGE_FLAGS_NOTSANDBOX; \
		} \
		if (FUNGE_UNLIKELY(setting_current_standard == stdver109)) \
			tmp |= FUNGE_FLAGS_STD109; \
		stack_push((m_pushstack), tmp); \
	} while(0)
/// Cell size
#define PUSH_REQ_2(m_pushstack) \
	stack_push((m_pushstack), sizeof(funge_cell))
/// Funge-98 Handprint
#define PUSH_REQ_3(m_pushstack) \
	stack_push((m_pushstack), FUNGE_OLD_HANDPRINT)
/// Interpreter version
#define PUSH_REQ_4(m_pushstack) \
	stack_push((m_pushstack), CFUNGE_VERSION_Y)
/// Operating paradigm
#define PUSH_REQ_5(m_pushstack) \
	do { \
		if (setting_enable_sandbox) \
			stack_push((m_pushstack), 0); \
		else \
			stack_push((m_pushstack), 1); \
	} while(0)
/// Path separator
#ifdef __WIN32__
#  define PUSH_REQ_6(m_pushstack) \
	stack_push((m_pushstack), (funge_cell)'\\')
#else
#  define PUSH_REQ_6(m_pushstack) \
	stack_push((m_pushstack), (funge_cell)'/')
#endif
/// Scalars / vector
#define PUSH_REQ_7(m_pushstack) \
	stack_push((m_pushstack), 2)
/// IP ID
#define PUSH_REQ_8(m_pushstack, m_ip) \
	stack_push((m_pushstack), (m_ip)->ID)
/// Team ID
#define PUSH_REQ_9(m_pushstack) \
	stack_push((m_pushstack), 0)
/// Position
#define PUSH_REQ_10(m_pushstack, m_ip) \
	stack_push_vector((m_pushstack), &(m_ip)->position)
/// Delta
#define PUSH_REQ_11(m_pushstack, m_ip) \
	stack_push_vector((m_pushstack), &(m_ip)->delta)
/// Storage offset
#define PUSH_REQ_12(m_pushstack, m_ip) \
	stack_push_vector((m_pushstack), &(m_ip)->storageOffset)
/// Least point
#define PUSH_REQ_13(m_pushstack, m_bounds_rect) \
	stack_push_vector((m_pushstack), vector_create_ref((m_bounds_rect).x, (m_bounds_rect).y))
/// Greatest point
#define PUSH_REQ_14(m_pushstack, m_bounds_rect) \
	stack_push_vector((m_pushstack), vector_create_ref((m_bounds_rect).w, (m_bounds_rect).h))
/// Date
#define PUSH_REQ_15(m_pushstack, m_tm) \
	stack_push((m_pushstack), (funge_cell)((m_tm)->tm_year * 256 * 256 + ((m_tm)->tm_mon + 1) * 256 + (m_tm)->tm_mday))
/// Time
#define PUSH_REQ_16(m_pushstack, m_tm) \
	stack_push((m_pushstack), (funge_cell)((m_tm)->tm_hour * 256 * 256 + (m_tm)->tm_min * 256 + (m_tm)->tm_sec))
/// Stack stack count
#define PUSH_REQ_17(m_pushstack, m_ip) \
	stack_push((m_pushstack), (funge_cell)(m_ip)->stackstack->size)
/// Number of elements on stacks
#define PUSH_REQ_18(m_pushstack, m_stackstack) \
	do { \
		const size_t stack_stack_count = (m_stackstack)->current; \
		for (size_t i = 0; i < stack_stack_count; i++) \
			stack_push((m_pushstack), (funge_cell)(m_stackstack)->stacks[i]->top); \
		stack_push((m_pushstack), (funge_cell)TOSSSize); \
		break; \
	} while(0)
/// Argc (109)
#define PUSH_REQ_19a(m_pushstack) \
	stack_push((m_pushstack), fungeargc)
/// Argv
#define PUSH_REQ_19b(m_pushstack) \
	do { \
		stack_push((m_pushstack), (funge_cell)'\0');\
		stack_push((m_pushstack), (funge_cell)'\0'); \
		for (int i = fungeargc - 1; i >= 0; i--) { \
			stack_push_string((m_pushstack), (const unsigned char*)fungeargv[i], strlen(fungeargv[i])); \
		} \
	} while(0)
/// Environment variable count (109)
#define PUSH_REQ_20a(m_pushstack) \
	do { \
		if (environ_count == 0) { \
			size_t i = 0; \
			while (environ[i]) { \
				if (FUNGE_UNLIKELY(setting_enable_sandbox)) { \
					if (FUNGE_UNLIKELY(!check_env_is_safe(environ[i]))) { \
						i++; \
						continue; \
					} \
				} \
				environ_count++; \
				i++; \
			} \
		} \
		stack_push((m_pushstack), (funge_cell)environ_count); \
	} while(0)
/// Environment variables
#define PUSH_REQ_20b(m_pushstack) \
	do { \
		size_t i = 0; \
		stack_push((m_pushstack), (funge_cell)'\0'); \
		while (environ[i]) { \
			if (FUNGE_UNLIKELY(setting_enable_sandbox)) { \
				if (FUNGE_LIKELY(!check_env_is_safe(environ[i]))) { \
					i++; \
					continue; \
				} \
			} \
			stack_push_string((m_pushstack), (const unsigned char*)environ[i], strlen(environ[i])); \
			i++; \
		} \
	} while(0)
/// 109 handprint
#define PUSH_REQ_21(m_pushstack) \
	stack_push_string((m_pushstack), (const unsigned char*)FUNGE_NEW_HANDPRINT, strlen(FUNGE_NEW_HANDPRINT))

/**
 * Push all of the y values (as would be done for 0y or such).
 * @param ip IP that is calling y.
 * @param pushStack Stack to push on.
 */
FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
static void push_all(instructionPointer * restrict ip, funge_stack * restrict pushStack) {
	fungeRect rect;
	time_t now;
	struct tm *curTime;
	if (FUNGE_UNLIKELY(setting_current_standard == stdver109))
		PUSH_REQ_21(pushStack);
	PUSH_REQ_20b(pushStack);
	if (FUNGE_UNLIKELY(setting_current_standard == stdver109))
		PUSH_REQ_20a(pushStack);
	PUSH_REQ_19b(pushStack);
	if (FUNGE_UNLIKELY(setting_current_standard == stdver109))
		PUSH_REQ_19a(pushStack);
	PUSH_REQ_18(pushStack, ip->stackstack);
	PUSH_REQ_17(pushStack, ip);
	now = time(NULL);
	curTime = gmtime(&now);
	PUSH_REQ_16(pushStack, curTime);
	PUSH_REQ_15(pushStack, curTime);
	fungespace_get_bounds_rect(&rect);
	PUSH_REQ_14(pushStack, rect);
	PUSH_REQ_13(pushStack, rect);
	PUSH_REQ_12(pushStack, ip);
	PUSH_REQ_11(pushStack, ip);
	PUSH_REQ_10(pushStack, ip);
	PUSH_REQ_9(pushStack);
	PUSH_REQ_8(pushStack, ip);
	PUSH_REQ_7(pushStack);
	PUSH_REQ_6(pushStack);
	PUSH_REQ_5(pushStack);
	PUSH_REQ_4(pushStack);
	PUSH_REQ_3(pushStack);
	PUSH_REQ_2(pushStack);
	PUSH_REQ_1(pushStack);
}


/**
 * Push a single y value.
 * @param request Value to push (indexed as cell from top).
 * @param ip IP that is calling y.
 * @param pushStack Stack to push on.
 */
FUNGE_ATTR_FAST FUNGE_ATTR_NONNULL
static void push_yval(funge_cell request, instructionPointer * restrict ip, funge_stack * restrict pushStack)
{	switch (request) {
		case 1: // Flags
			PUSH_REQ_1(pushStack);
			break;
		case 2: // Cell size
			PUSH_REQ_2(pushStack);
			break;
		case 3: // Handprint
			PUSH_REQ_3(pushStack);
			break;
		case 4: // Version
			PUSH_REQ_4(pushStack);
			break;
		case 5: // Operating paradigm
			PUSH_REQ_5(pushStack);
			break;
		case 6: // Path separator
			PUSH_REQ_6(pushStack);
			break;
		case 7: // Scalars / vector
			PUSH_REQ_7(pushStack);
			break;
		case 8: // IP ID
			PUSH_REQ_8(pushStack, ip);
			break;
		case 9: // TEAM ID
			PUSH_REQ_9(pushStack);
			break;
		// Vector of current IP position (y component)
		case 10:
			stack_push(pushStack, ip->position.y);
			break;
		// Vector of current IP position (x component)
		case 11:
			stack_push(pushStack, ip->position.x);
			break;
		// Delta of current IP (y component)
		case 12:
			stack_push(pushStack, ip->delta.y);
			break;
		// Delta of current IP (x component)
		case 13:
			stack_push(pushStack, ip->delta.x);
			break;
		// Storage offset of current IP (y component)
		case 14:
			stack_push(pushStack, ip->storageOffset.y);
			break;
		// Storage offset of current IP (x component)
		case 15:
			stack_push(pushStack, ip->storageOffset.x);
			break;
		// Least point (y component)
		case 16: {
			fungeRect rect;
			fungespace_get_bounds_rect(&rect);
			stack_push(pushStack, rect.y);
			break;
		}
		// Least point (x component)
		case 17: {
			fungeRect rect;
			fungespace_get_bounds_rect(&rect);
			stack_push(pushStack, rect.x);
			break;
		}
		// Greatest point (y component)
		case 18: {
			fungeRect rect;
			fungespace_get_bounds_rect(&rect);
			stack_push(pushStack, rect.h);
			break;
		}
		// Greatest point (x component)
		case 19: {
			fungeRect rect;
			fungespace_get_bounds_rect(&rect);
			stack_push(pushStack, rect.w);
			break;
		}
		case 20: { // Date ((year - 1900) * 256 * 256) + (month * 256) + (day of month)
			time_t now;
			struct tm *curTime;
			now = time(NULL);
			curTime = gmtime(&now);
			PUSH_REQ_15(pushStack, curTime);
			break;
		}
		case 21: { // Time (hour * 256 * 256) + (minute * 256) + (second)
			time_t now;
			struct tm *curTime;
			now = time(NULL);
			curTime = gmtime(&now);
			PUSH_REQ_16(pushStack, curTime);
			break;
		}
		case 22: // Number of stacks on stack stack
			PUSH_REQ_17(pushStack, ip);
			break;
		case 23: // Size of TOSS
			stack_push(pushStack, (funge_cell)TOSSSize);
			break;
	}
}


/// Temp stack for pushing on when needed. Faster.
static funge_stack* restrict sysinfo_tmp_stack = NULL;

FUNGE_ATTR_FAST
void run_sys_info(instructionPointer *ip)
{
	funge_cell request = stack_pop(ip->stack);
	TOSSSize = ip->stack->top;
	// Negative or 0: push all
	if (request <= 0) {
		push_all(ip, ip->stack);
	// Simple to get single cell in this range
	} else if (request < 24) {
		push_yval(request, ip, ip->stack);
	// Large positive, hard to calculate in advance, or may even be pick.
	} else {
		if (FUNGE_UNLIKELY(!sysinfo_tmp_stack))
			sysinfo_tmp_stack = stack_create();
		push_all(ip, sysinfo_tmp_stack);
		// Find out if we should act as pick or not...
		if (sysinfo_tmp_stack->top > (size_t)request) {
			stack_push(ip->stack, sysinfo_tmp_stack->entries[sysinfo_tmp_stack->top - (size_t)request]);
		} else {
			// Act as pick
			stack_push(ip->stack, stack_get_index(ip->stack, request - sysinfo_tmp_stack->top));
		}
		stack_clear(sysinfo_tmp_stack);
	}
}

#ifndef NDEBUG
/// Free some memory if debug build.
FUNGE_ATTR_FAST
void sysinfo_cleanup(void) {
	if (sysinfo_tmp_stack)
		stack_free(sysinfo_tmp_stack);
}
#endif