initial commit

This commit is contained in:
tocariimaa 2024-12-26 14:02:40 -03:00
commit fe8f76dd44
29 changed files with 2477 additions and 0 deletions

269
allocators/buddyalloc.c Normal file
View file

@ -0,0 +1,269 @@
#define _BSD_SOURCE
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#define u16set(hi, lo) ((hi << 8) | (lo & 0xff))
#define align_up(n, align) (((n) + (align - 1)) & (-align))
#define header_set_magic(block, mag, poi) ((block)->magic = u16set(poi, mag))
#define Assert(pred) if (!(pred)) {__builtin_trap();}
#define BUDDY_BLOCK_MAGIC 0xb7
#define BUDDY_BLOCK_FREED 0xaa
#define BUDDY_MIN_ALIGNMENT sizeof(BuddyBlockHeader)
#define false ((bool)0)
#define true ((bool)1)
#define nil ((void *)0)
typedef uint8_t u8;
typedef _Bool bool;
typedef ptrdiff_t isize;
/* Header of a buddy block. This gets baked into memory. */
typedef struct {
isize size;
bool used;
uint16_t magic;
} BuddyBlockHeader;
/* Being 16 bytes the minimum alignment, at least on a 64-bit machine... */
_Static_assert(sizeof(BuddyBlockHeader) == 16, "BuddyBlockHeader size is not 16 bytes");
typedef struct {
BuddyBlockHeader *head, *tail;
isize backing_len;
} BuddyAllocator;
static void
abort_msg(const char *s)
{
fputs(s, stderr);
fflush(stderr);
abort();
}
static BuddyBlockHeader *
next_buddy_block(BuddyBlockHeader *cur)
{
/* Calculate the address for the next buddy block header start.
* This has to be padded to a boundary of at least
* `sizeof(BuddyBlockHeader)` bytes. */
return (BuddyBlockHeader *)align_up(
(uintptr_t)((u8 *)cur + cur->size), BUDDY_MIN_ALIGNMENT);
}
/* Split a block until it has a size equal or greater than `size`.
* This function also "builds" the buddy headers. */
static BuddyBlockHeader *
split_block(BuddyBlockHeader *block, isize size)
{
BuddyBlockHeader *cur = block;
while (cur->size > size) {
/* XXX: Splitting by half seems quite wasteful... (see `buddy_alloc_aligned`) */
const isize half = cur->size / 2;
cur->size = half;
cur = next_buddy_block(cur);
cur->size = half;
cur->used = false;
cur->magic = u16set(0, BUDDY_BLOCK_MAGIC);
}
Assert(cur->size >= size);
return cur;
}
static BuddyBlockHeader *
find_block(BuddyAllocator *ba, isize size)
{
BuddyBlockHeader *cur = ba->head;
BuddyBlockHeader *next = next_buddy_block(cur);
/* Case for an initial state, where there aren't any split buddies */
if (!cur->used && next == ba->tail)
return split_block(cur, size);
/* Traverse the block list for a suitable block */
BuddyBlockHeader *block = nil;
while (cur < ba->tail) {
Assert((cur->magic & 0xff) == BUDDY_BLOCK_MAGIC);
Assert(cur->size <= ba->backing_len);
/* Coalesce two adjacent buddies if possible */
if (next < ba->tail
&& !next->used
&& next->size == cur->size
&& next->size + cur->size >= size) {
Assert((next->magic & 0xff) == BUDDY_BLOCK_MAGIC);
fprintf(stderr, "candidate for coalescing (%p U %p)\n", (void *)cur, (void *)next);
cur->size += next->size;
/* wipe header of the buddy just in case */
memset(next, 0, sizeof(*next));
}
if (!cur->used && cur->size >= size) {
fprintf(stderr, "candidate at %p (size: %td)\n", (void *)cur, cur->size);
/* Either if no candidate has been picked yet or the current candidate
* is smaller (but `size` can still fit) than the current buddy. */
if (block == nil || block->size > cur->size)
block = cur;
}
cur = next_buddy_block(cur);
next = cur < ba->tail ? next_buddy_block(cur) : nil;
}
if (block != nil)
return split_block(block, size);
return nil;
}
static void
coalesce_buddies(BuddyAllocator *ba)
{
BuddyBlockHeader *cur = ba->head;
BuddyBlockHeader *next = next_buddy_block(cur);
while (cur < ba->tail) {
Assert((cur->magic & 0xff) == BUDDY_BLOCK_MAGIC);
Assert(cur->size <= ba->backing_len);
if (next < ba->tail
&& !next->used
&& next->size == cur->size) {
Assert((next->magic & 0xff) == BUDDY_BLOCK_MAGIC);
cur->size += next->size;
memset(next, 0, sizeof(*next));
}
cur = next_buddy_block(cur);
next = cur < ba->tail ? next_buddy_block(cur) : nil;
}
}
static void
buddy_alloc_init(BuddyAllocator *balloc, u8 *buf, isize buflen)
{
balloc->head = (BuddyBlockHeader *)buf;
balloc->tail = (BuddyBlockHeader *)(buf + buflen);
balloc->backing_len = buflen;
*balloc->head = (BuddyBlockHeader) {
.size = buflen,
.used = false,
.magic = u16set(0, BUDDY_BLOCK_MAGIC),
};
}
void *
buddy_alloc_aligned(BuddyAllocator *ba, isize size, isize alignment)
{
if (size == 0)
return nil;
Assert(size > 0);
if (alignment < BUDDY_MIN_ALIGNMENT)
alignment = BUDDY_MIN_ALIGNMENT;
isize aligned_size = align_up(size + sizeof(BuddyBlockHeader), alignment);
isize ss = alignment; /* squared up size (because we split the buddies in half) */
while (ss <= aligned_size)
ss *= 2;
Assert(ss <= ba->backing_len);
BuddyBlockHeader *block;
if ((block = find_block(ba, ss)) != nil)
goto happy;
/* First attempt failed, maybe due to fragmentation. Do a run of coalescing to
* solve fragmentation if any and try allocating again. */
coalesce_buddies(ba);
if ((block = find_block(ba, ss)) != nil)
goto happy;
/* Too fragmented or ran out of memory. */
return nil;
happy:
block->used = true;
block->magic &= 0xff; /* clear free'd flag */
return (u8 *)block + sizeof(BuddyBlockHeader);
}
void *
buddy_alloc(BuddyAllocator *ba, isize size)
{
return buddy_alloc_aligned(ba, size, BUDDY_MIN_ALIGNMENT);
}
void
buddy_free(BuddyAllocator *ba, void *ptr)
{
if (ptr == nil)
return;
BuddyBlockHeader *header = (BuddyBlockHeader *)((u8 *)ptr - sizeof(*header));
Assert((header->magic & 0xff) == BUDDY_BLOCK_MAGIC);
/* double free detection */
Assert((header->magic >> 8) != BUDDY_BLOCK_FREED);
header->used = false;
header->magic = u16set(BUDDY_BLOCK_FREED, BUDDY_BLOCK_MAGIC);
}
static void
iter_blocks(BuddyBlockHeader *head, BuddyBlockHeader *tail)
{
int cc = fprintf(stderr, "------ list start (head: %p; tail: %p; size: %td) ------\n",
(void *)head, (void *)tail, tail - head);
BuddyBlockHeader *cur = head;
while (cur < tail) {
Assert((cur->magic & 0xff) == BUDDY_BLOCK_MAGIC);
fprintf(stderr, "block@%p(size: %8td; used: %3s; magic: %04x)\n",
(void *)cur, cur->size, cur->used? "yes":"no", cur->magic);
cur = next_buddy_block(cur);
}
while (cc--)
fputc('-', stderr);
fputc('\n', stderr);
}
#include <time.h>
static void
dirt_block(u8 *buf, isize size)
{
u8 *bufend = buf + size;
while (buf < bufend)
*buf++ = rand() & 0xff;
}
int
main(void)
{
const isize backing_len = align_up(1 << 20, 16);
u8 *backing = mmap(
nil, backing_len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0
);
if (backing == MAP_FAILED) {
perror("mmap");
return 1;
}
*(volatile u8 *)backing = 0xaa; /* page fault it */
BuddyAllocator ba = {0};
buddy_alloc_init(&ba, backing, backing_len);
srand(time(nil));
iter_blocks(ba.head, ba.tail);
u8 *buf1 = buddy_alloc(&ba, 255);
dirt_block(buf1, 255);
u8 *buf2 = buddy_alloc(&ba, 1024);
dirt_block(buf2, 1024);
buddy_free(&ba, buf1);
iter_blocks(ba.head, ba.tail);
u8 *buf3 = buddy_alloc(&ba, 512);
dirt_block(buf3, 512);
iter_blocks(ba.head, ba.tail);
buddy_free(&ba, buf2);
buddy_free(&ba, buf3);
munmap(backing, backing_len);
return 0;
}

View file

@ -0,0 +1,86 @@
// Karplus-Strong string synthesis algorithm implementation, spits out the file to stdout as a WAV file
// if you use mpv, you can redirect it to mpv directly: `./karplus_strong <freq> | mpv -`
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <bsd/stdlib.h> // arc4random (link with -lbsd)
struct wav_header // not the most portable way really
{
char chunk_id[4];
uint32_t chunk_size;
char format[4];
char subchunk_id[4];
uint32_t subchunk1_size;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char subchunk2_id[4];
uint32_t subchunk2_size;
};
void wav_populate_header(struct wav_header* wavh, size_t total_samples, size_t channels, size_t bits_per_sample, size_t sample_rate)
{
memcpy(wavh->chunk_id, "RIFF", 4);
memcpy(wavh->format, "WAVE", 4);
memcpy(wavh->subchunk_id, "fmt ", 4);
wavh->chunk_size = (sizeof(struct wav_header) + total_samples) - 8;
wavh->subchunk1_size = 16;
wavh->audio_format = 1;
wavh->num_channels = 1;
wavh->sample_rate = sample_rate;
wavh->byte_rate = (sample_rate * channels * bits_per_sample) / 8;
wavh->block_align = (channels * bits_per_sample) / 8;
wavh->bits_per_sample = bits_per_sample;
memcpy(wavh->subchunk2_id, "data", 4);
wavh->subchunk2_size = (total_samples * channels * bits_per_sample) / 8;
}
void wav_create_file(struct wav_header* wavh, FILE* wav_fp, int16_t* data, size_t total_samples)
{
fwrite(wavh, sizeof(struct wav_header), 1, wav_fp); // write WAV header to file
fwrite(data, sizeof(int16_t), total_samples, wav_fp); // ...and finally the audio data
}
int main(int argc, char** argv)
{
if (argc < 2) {
fprintf(stderr, "frequency argument expected\n");
return 1;
}
const size_t SAMPLE_RATE = 44100;
const size_t freq = strtoull(argv[1], NULL, 0); // you should check for errors
const size_t seconds = 3;
const size_t total_samples = seconds * SAMPLE_RATE;
const size_t period = SAMPLE_RATE / freq;
int16_t* noise_table = malloc(period * sizeof(int16_t)); // wavetable
int16_t* buffer = malloc(total_samples * sizeof(int16_t)); // PCM buffer
// fill wavetable with noise, it doesn't have to be this function, rand() over all elements would suffice
arc4random_buf(noise_table, period);
// algorithm
size_t prev_val = 0;
for (size_t n = 0; n < total_samples; ++n) {
size_t cs = n % period;
noise_table[cs] = (noise_table[cs] + prev_val) * 0.5;
buffer[n] = noise_table[cs];
prev_val = noise_table[cs];
}
// finally write the WAV file
struct wav_header wavh = {};
wav_populate_header(&wavh, total_samples, 1, 16, SAMPLE_RATE);
wav_create_file(&wavh, stdout, buffer, total_samples);
free(noise_table);
free(buffer);
return 0;
}

View file

@ -0,0 +1,122 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
const double PI = 3.141592653589793;
const double SECONDS = 0.5;
const size_t CHANNELS = 1;
const size_t SAMPLE_RATE = 44100;
const size_t BITS_PER_SAMPLE = 16;
const size_t TOTAL_SAMPLES = (SECONDS * CHANNELS * SAMPLE_RATE);
// http://soundfile.sapp.org/doc/WaveFormat/ (https://archive.is/069fj)
struct wav_header
{
char chunk_id[4];
uint32_t chunk_size;
char format[4];
char subchunk_id[4];
uint32_t subchunk1_size;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char subchunk2_id[4];
uint32_t subchunk2_size;
};
static void *xcalloc(const size_t num, const size_t size)
{
void *ptr = calloc(num, size);
if (ptr == NULL) {
perror("calloc");
exit(1);
}
return ptr;
}
void wav_populate_header(struct wav_header *const wavh, const size_t total_samples, const size_t channels)
{
memcpy(wavh->chunk_id, "RIFF", 4);
memcpy(wavh->format, "WAVE", 4);
memcpy(wavh->subchunk_id, "fmt ", 4);
wavh->chunk_size = (sizeof(struct wav_header) + total_samples) - 8;
wavh->subchunk1_size = 16;
wavh->audio_format = 1;
wavh->num_channels = 1;
wavh->sample_rate = SAMPLE_RATE;
wavh->byte_rate = (SAMPLE_RATE * channels * BITS_PER_SAMPLE) / 8;
wavh->block_align = (channels * BITS_PER_SAMPLE) / 8;
wavh->bits_per_sample = BITS_PER_SAMPLE;
memcpy(wavh->subchunk2_id, "data", 4);
wavh->subchunk2_size = (total_samples * channels * BITS_PER_SAMPLE) / 8;
}
void wav_create_file(const struct wav_header *wavh, FILE *wav_fp, const int16_t *data, const size_t total_samples)
{
fwrite(wavh, sizeof(struct wav_header), 1, wav_fp); // write WAV header to file
fwrite(data, sizeof(int16_t), total_samples, wav_fp); // ...and finally the audio data
}
int16_t sinewave(const size_t time, const size_t sample_rate, const double freq)
{
const double rad = (2 * PI) * (time / (sample_rate / freq)); // get radian frequency
const double t_amplitude = (32767 * 0.9);
return t_amplitude * sin(rad);
}
int16_t squarewave(const size_t time, const size_t sample_rate, const double freq)
{
const double t_amplitude = (32767 * 0.9);
const size_t period = (sample_rate / freq);
if ((time % period) < (period / 2))
return t_amplitude;
return 0;
//const double rad = (2 * PI) * (time / (sample_rate / freq)); // get radian frequency
//return ((sin(rad) >= 0) ? t_amplitude : 0); // -1 * t_amplitude for peak to peak amp.
//32767 * (2 * fabs(2 * (((time / period) - floor((time / period) + (1 / 2)))) ) * -1);
}
int16_t trianglewave(const size_t time, const size_t sample_rate, const double freq)
{
// a triangle wave can be expressed in terms of sine and arcsine
const double t_amplitude = (32767 * 0.9);
const double period = sin((2 * PI) * (time / (sample_rate / freq)));
return (2 * t_amplitude / PI) * asin(period);
}
int main(int argc, char **argv)
{
FILE *fp = stdout;
if (argc > 1) {
if ((fp = fopen(argv[1], "wb")) == NULL) {
perror("fopen");
return 1;
}
}
int16_t buffer[TOTAL_SAMPLES];
const double freq = 220;
for (int i = 0; i < TOTAL_SAMPLES; ++i) {
buffer[i] = sinewave(i, SAMPLE_RATE, freq);
//buffer[i] = squarewave(i, SAMPLE_RATE, freq);
//buffer[i] = trianglewave(i, SAMPLE_RATE, freq);
}
struct wav_header *wavh = xcalloc(1, sizeof(struct wav_header));
wav_populate_header(wavh, TOTAL_SAMPLES, CHANNELS);
wav_create_file(wavh, fp, buffer, TOTAL_SAMPLES);
free(wavh);
fclose(fp);
return 0;
}

14
misc/abacaba.lua Normal file
View file

@ -0,0 +1,14 @@
if #arg < 1 then
print("iteration argument required")
return
end
alpha = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "q", "p", "q", "r", "s", "t", "u", "v", "x", "y", "z", }
prev_pttr = ""
curr_pttr = ""
iterations = arg[1] % #alpha
for i = 1, iterations do
curr_pttr = prev_pttr .. alpha[i] .. prev_pttr
prev_pttr = curr_pttr
print(curr_pttr)
end

15
misc/collatz.lua Normal file
View file

@ -0,0 +1,15 @@
function collatz(n)
io.write(n)
while n > 1 do
if (n % 2) == 0 then
n = n // 2
else
n = 3 * n + 1
end
io.write(' ' .. n)
end
io.write('\n')
end
assert(#arg ~= 0, 'expected initial number')
collatz(tonumber(arg[1]))

25
misc/gregorian.c Normal file
View file

@ -0,0 +1,25 @@
#include <stdio.h>
#include <stdlib.h>
char *dow_n[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// The day of week returned by this algorithm starts from zero, and assumes that Sunday is the first day of the week.
int sakamoto_dow(unsigned int year, unsigned int month, unsigned int day)
{
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
year -= (month < 3);
return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7;
}
int main(int argc, char **argv)
{
if (argc < 4) return 1;
int c_year = strtol(argv[1], &argv[1], 10);
int c_month = strtol(argv[2], &argv[2], 10);
int c_day = strtol(argv[3], &argv[3], 10);
int day = sakamoto_dow(c_year, c_month, c_day);
printf("Day of week for (%i/%i/%i): %i (%s)\n", c_year, c_month, c_day, day, dow_n[day]);
return 0;
}

87
misc/huffman.cpp Normal file
View file

@ -0,0 +1,87 @@
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <unordered_map>
#define HT_TREE_MARK '@'
struct ht {
char chr;
size_t weight;
struct ht *left{};
struct ht *right{};
ht(char _chr, size_t _w, struct ht *l = nullptr, struct ht *r = nullptr) :
chr(_chr), weight(_w), left(l), right(r) {}
};
void ht_build_codes(struct ht *tree, std::unordered_map<char, std::string> &ht_codes, std::string path="")
{
if (tree == nullptr)
return;
if (tree->chr != HT_TREE_MARK) {
std::clog << tree->chr << ": " << path << std::endl;
ht_codes[tree->chr] = path;
}
ht_build_codes(tree->left, ht_codes, path + '0');
ht_build_codes(tree->right, ht_codes, path + '1');
}
bool ht_comp_fn(const struct ht *n1, const struct ht *n2)
{
// enforce stable sorting
if (n1->weight == n2->weight)
return false;
return n1->weight > n2->weight;
};
int main()
{
std::string s = "abracadabraacacacabbaadadeea";
// count occurences (frequency, weight) of each character in the message
std::unordered_map<char, size_t> chr_occu;
for (const auto& c : s)
++chr_occu[c];
std::priority_queue<struct ht *, std::vector<struct ht *>, decltype(&ht_comp_fn)> nodes(ht_comp_fn);
// create initial nodes, from here we start building the tree
for (const auto& v : chr_occu) {
auto *node = new struct ht(v.first, v.second);
nodes.push(node);
}
auto printpq = [](decltype(nodes) n) {
while (!n.empty()) {
auto *cn = n.top();
std::clog << cn->chr << ":" << cn->weight << " ";
n.pop();
}
std::clog << std::endl;
};
while (nodes.size() > 1) { // when the nodes queue element count is 1, we already have our tree
// remove the two first (the one with the lowest weights)
auto *n1 = nodes.top();
nodes.pop();
auto *n2 = nodes.top();
nodes.pop();
// create a new tree with a weight equal to the sum of the two popped nodes
auto *node = new struct ht(HT_TREE_MARK, n1->weight + n2->weight, n1, n2);
nodes.push(node);
}
std::unordered_map<char, std::string> ht_codes;
std::string enc_msg;
enc_msg.reserve(255);
ht_build_codes(nodes.top(), ht_codes);
for (const auto& c : s)
enc_msg += ht_codes[c];
std::clog << s << " -> " << enc_msg << std::endl;
return 0;
}

40
misc/luhn-lua/luhn.lua Normal file
View file

@ -0,0 +1,40 @@
-- implementation of Luhn's checksum algorithm, computing and checking of checksum digit
local luhn = {}
-- main part of the algorithm, double each 2 digits, clamp them and return the sum.
function luhn:_sum_digits(num)
assert(num >= 0, "number must be positive")
local sum = 0
local digits_n = 0
repeat -- iterate over digits from right to left
local tmp = num % 10 -- get current unit
-- each 2 digits, double the current digit
if (digits_n % 2) == 0 then tmp = tmp * 2 end
if tmp > 9 then tmp = tmp - 9 end
digits_n = digits_n + 1
sum = sum + tmp
-- remove unit from number
num = math.floor(num / 10)
until num == 0
return sum
end
-- returns the computed check digit
function luhn:compute_check_digit(num)
return (self:_sum_digits(num) * 9) % 10
end
-- returns true if its valid, false otherwise
function luhn:validate_check_digit(num)
local check_digit = num % 10 -- get check digit
return self:compute_check_digit(math.floor(num / 10)) == check_digit
end
-- returns the original number with the check digit concatenated at the end
function luhn:make_number(num)
return ((num * 10) + self:compute_check_digit(num))
end
return luhn

23
misc/luhn-lua/main.lua Normal file
View file

@ -0,0 +1,23 @@
#!/usr/bin/lua
luhn = require "luhn"
if #arg ~= 0 then
local a = tonumber(arg[1]) -- 7992739871
print("check digit: " .. luhn:compute_check_digit(a))
end
local function bool_str(bool)
return bool and "yes" or "no"
end
print("49927398716: " .. bool_str(luhn:validate_check_digit(49927398716)))
print("49927398717: " .. bool_str(luhn:validate_check_digit(49927398717)))
print("1234567812345678: " .. bool_str(luhn:validate_check_digit(1234567812345678)))
print("1234567812345670: " .. bool_str(luhn:validate_check_digit(1234567812345670)))
print("\ncheck digits:")
print("4992739871: " .. luhn:compute_check_digit(4992739871))
print("4992739871: " .. luhn:compute_check_digit(4992739871))
print("123456781234567: " .. luhn:compute_check_digit(123456781234567))
print("123456781234567: " .. luhn:compute_check_digit(123456781234567))
print(luhn:make_number(4992739871))

55
misc/mmap-file.c Normal file
View file

@ -0,0 +1,55 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/auxv.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
int main(int argc, char **argv)
{
if (argc < 2)
errx(EXIT_FAILURE, "expected file name");
const char *filename = argv[1];
int fd = open(filename, O_RDONLY);
if (fd < 0)
err(EXIT_FAILURE, "open");
struct stat fst = {0};
if (fstat(fd, &fst) < 0)
err(EXIT_FAILURE, "fstat");
const size_t filesize = fst.st_size;
uint8_t *filem = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
if (filem == MAP_FAILED)
err(EXIT_FAILURE, "mmap failed");
/* we can close the descriptor now */
close(fd);
unsigned int seed = 0;
memcpy(&seed, (void *)getauxval(AT_RANDOM), sizeof(seed));
srand(seed);
const size_t off = abs(rand()) % filesize;
uint8_t pb = 0, cb;
size_t cbo = 0;
for (size_t i = off; i < off + 0xff && i < filesize; ++i) {
cb = filem[i];
if (cb == pb)
++cbo;
if (cbo == 16) {
printf("* ");
cbo = 0;
} else
printf("%x ", cb);
pb = cb;
}
putchar('\n');
munmap(filem, filesize);
return 0;
}

38
misc/open_tray_linux.c Normal file
View file

@ -0,0 +1,38 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/cdrom.h>
#include <sys/ioctl.h>
int main(void)
{
// Open cdrom device, it must be opened in non-blocking mode
int cdfd = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
if (cdfd < 0) {
perror("open");
return 1;
}
int cdrom_stat = ioctl(cdfd, CDROM_DRIVE_STATUS, 0);
if ((cdrom_stat > -1 && cdrom_stat != CDS_NO_INFO) &&
cdrom_stat == CDS_TRAY_OPEN) {
printf("tray is already open\n");
return 0;
}
// Issue eject command
if (ioctl(cdfd, CDROMEJECT, 0) < 0) {
if (errno == EBUSY) {
fputs("either another process is using the drive or the door is locked.\n", stderr);
return 1;
}
perror("ioctl");
return 1;
}
close(cdfd);
return 0;
}

47
misc/proc_vm.c Normal file
View file

@ -0,0 +1,47 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
if (argc < 3) {
fprintf(stderr, "%s [pid] [address]\n", argv[0]);
return 0;
}
pid_t proc_pid = strtol(argv[1], NULL, 0);
off_t vm_addr = strtol(argv[2], NULL, 0);
int proc_mem_fd = 0;
char proc_mem_path[255];
snprintf(proc_mem_path, sizeof(proc_mem_path) - 1, "/proc/%i/mem", proc_pid);
if ((proc_mem_fd = open(proc_mem_path, O_RDWR)) < 0) { // open process memory
perror("open");
return 1;
}
if (ptrace(PTRACE_ATTACH, proc_pid, NULL, NULL) < 0) { // attach to process
perror("ptrace");
return 1;
}
waitpid(proc_pid, NULL, 0); // wait for the tracee to stop (SIGSTOP) completly
uint64_t mem_val = 0;
// a read unsigned 64 bit integer from the specified address
if (pread(proc_mem_fd, &mem_val, sizeof(uint64_t), vm_addr) < 0) {
perror("pread");
return 1;
}
printf("value at 0x%lx:%lx\n", vm_addr, mem_val);
if (ptrace(PTRACE_DETACH, proc_pid, NULL, NULL) < 0) { // detach from process
perror("ptrace");
return 1;
}
close(proc_mem_fd);
return 0;
}

203
misc/procread.c Normal file
View file

@ -0,0 +1,203 @@
// see proc(5) manpage for more information
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h> // pid_t
#include <linux/kdev_t.h> // MAJOR() and MINOR() tty macros
#define __STR(n) #n
#define STR(n) __STR(n)
// Filename length of the process executable, truncated to 16 + nil terminator
// However, kernel processes have longer names than that.
#define _TASK_COMM_LEN 255
#define PROC_FOLDER "/proc"
#define INIT_PID 1
#define IS_KERNEL_PROC(p) (((p)->pid != INIT_PID && (p)->session_id == 0))
struct proc_t
{
pid_t pid;
pid_t parent_pid;
gid_t group_id;
id_t session_id;
id_t tty;
char *exe_name;
char state;
unsigned int flags;
};
void *xcalloc(size_t num, size_t size)
{
void *ptr = calloc(num, size);
if (ptr == NULL) {
perror("xcalloc");
//fcloseall();
abort();
}
return ptr;
}
char *build_proc_path(char *buffer, const char *proc_pid, const char *dir)
{
char *path = buffer; //xcalloc(256, sizeof(char));
strcpy(path, PROC_FOLDER"/");
strncat(path, proc_pid, 255 - 5); // always leave space for /stat
char proc_dir[255] = {0};
snprintf(proc_dir, sizeof(proc_dir), "/%s", dir);
strcat(path, proc_dir);
return path;
}
FILE *process_open(char *path)
{
FILE *proc_fp = fopen(path, "rb");
if (proc_fp == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
return proc_fp;
}
int parse_proc(FILE *proc_fp, struct proc_t *p)
{
#define NMATCHES 7 // <- increment each time a format directive (that is bound to a reference) is added
p->exe_name = malloc(_TASK_COMM_LEN * sizeof(char));
int ret = fscanf(
proc_fp,
"%d %*[(]%"STR(_TASK_COMM_LEN)"[^)]%*[)] %c %d %d %d %u",
&p->pid, p->exe_name, &p->state, &p->parent_pid, &p->session_id, &p->tty,
&p->flags
);
if (ret != NMATCHES || (ret == EOF && ferror(proc_fp))) {
fprintf(stderr, "couldn't parse stat info\n");
return 1;
}
return 0;
}
int is_numeric_str(char *str)
{
while (*str)
if (!isdigit(*str++))
return 0;
return 1;
}
void list_procs_init(struct proc_t **proc_list, int proc_list_cnt, int offset)
{
for (int i = offset; i < proc_list_cnt; ++i) {
proc_list[i] = xcalloc(1, sizeof(struct proc_t));
}
}
struct proc_t **list_procs()
{
int proc_list_it = 0;
int proc_list_cnt = 255;
struct proc_t **proc_list = xcalloc(proc_list_cnt + 1, sizeof(**proc_list));
list_procs_init(proc_list, proc_list_cnt, 0);
char path_buf[255] = {0};
DIR *proc_dir = opendir(PROC_FOLDER);
if (proc_dir == NULL) {
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent *dir;
while ((dir = readdir(proc_dir)) != NULL) {
if (dir->d_type == DT_DIR && is_numeric_str(dir->d_name)) {
if ((proc_list_it + 1) > proc_list_cnt) {
proc_list_cnt += proc_list_cnt;
proc_list = realloc(proc_list, (proc_list_cnt + 1) * sizeof(**proc_list));
list_procs_init(proc_list, proc_list_cnt, proc_list_it);
}
FILE *pf = process_open(build_proc_path(path_buf, dir->d_name, "stat"));
parse_proc(pf, proc_list[proc_list_it++]);
fclose(pf);
}
}
closedir(proc_dir);
return proc_list;
}
void print_proc_info(struct proc_t *proc)
{
char *is_kernel_proc = IS_KERNEL_PROC(proc) ? "yes" : "no";
if (IS_KERNEL_PROC(proc)) // not init neither a child of it
printf("process %s (PID: %d) is a kernel process\n", proc->exe_name, proc->pid);
printf("PID: %d (parent: %d, session id: %d) Name: %s Status: %c TTY: %d"
" flags: 0x%08x\n",
proc->pid, proc->parent_pid, proc->session_id, proc->exe_name, proc->state, MAJOR(proc->tty), proc->flags);
}
void backtrace_to_init(struct proc_t *proc)
{
if (proc->parent_pid == 0)
return;
// pid == group pid for parent processes
while (proc->pid != INIT_PID && proc->parent_pid != 0) {
char pid_str[32];
char proc_path[255] = {0};
snprintf(pid_str, sizeof(pid_str), "%d", proc->parent_pid);
FILE *pf = process_open(build_proc_path(proc_path, pid_str, "stat"));
if (parse_proc(pf, proc))
break;
print_proc_info(proc);
fclose(pf);
free(proc->exe_name);
proc->exe_name = NULL;
}
}
int main(int argc, char **argv)
{
int c;
int list_procs_flag = 0, kernel_procs_flag = 0;
while ((c = getopt(argc, argv, "lkp:")) != -1) {
switch (c) {
case 'l': list_procs_flag = 1;
break;
case 'k': kernel_procs_flag = 1;
break;
case '?':
fprintf(stderr, "usage: ./procread [-lk][-p PID]\n");
return EXIT_FAILURE;
}
}
if (list_procs_flag) {
struct proc_t **proc, **proc_o;
proc = proc_o = list_procs();
while (*proc != NULL) {
if ((*proc)->pid && (!IS_KERNEL_PROC(*proc) || kernel_procs_flag)) {
print_proc_info(*proc);
}
free((*proc)->exe_name);
free(*proc++);
}
free(proc_o);
} else {
FILE *proc_fp;
char pid_str[24];
char proc_path[255] = {0};
if (optind >= argc) {
snprintf(pid_str, sizeof(pid_str), "%d", getpid());
proc_fp = process_open(build_proc_path(proc_path, pid_str, "stat"));
} else {
proc_fp = process_open(build_proc_path(proc_path, argv[optind], "stat"));
}
struct proc_t p = {};
if (parse_proc(proc_fp, &p))
return 1;
print_proc_info(&p);
free(p.exe_name);
fclose(proc_fp);
backtrace_to_init(&p);
}
return EXIT_SUCCESS;
}

116
parsers/bencode.nim Normal file
View file

@ -0,0 +1,116 @@
## Bencode decoder
## Resources:
## https://en.m.wikipedia.org/wiki/Bencode
## https://archive.is/yCwj4
import std/[streams, strutils, tables]
from strformat import fmt
type
BencodeSyntaxError = object of ValueError
BencodeInt = int64
BencodeByteStr = string
BencodeDict = Table[string, BencodeObject]
BencodeObjectKind = enum
bokNum
bokBseq
bokList
bokDict
BencodeObject = object
case kind: BencodeObjectKind
of bokNum: num: BencodeInt
of bokBseq: bseq: BencodeByteStr
of bokList: list: seq[BencodeObject]
of bokDict: dict: BencodeDict
proc decodeBencode*(bs: Stream): BencodeObject
var putbackdChar: char
proc expectToken(bs: Stream, expected: char) =
var c: char
if putbackdChar != '\0':
c = putbackdChar
putbackdChar = '\0'
else:
c = bs.readChar()
if c != expected:
let msg =
if c != '\0': fmt"expected `{expected}`, got `{c}`"
else: "unexpected EOB"
raise newException(BencodeSyntaxError, msg)
# converts a base-10 number to its integer representation
proc readNumber(bs: Stream): BencodeInt =
const digits = "0123456789"
var
c = bs.readChar()
sign: int
if c == '-':
c = bs.readChar()
sign = 2
while isDigit(c):
let charVal = digits.find(c)
assert charVal != -1
result = (result * 10) + charVal
c = bs.readChar()
putbackdChar = c
result -= result * sign
# i1234e i-727e
proc parseBencodeInteger(bs: Stream): BencodeInt =
expectToken(bs, 'i')
result = readNumber(bs)
expectToken(bs, 'e')
# 4:spam
proc parseByteSequence(bs: Stream): BencodeByteStr =
var byteLen = readNumber(bs)
expectToken(bs, ':')
while byteLen > 0:
result.add(char(bs.readInt8()))
dec byteLen
proc parseList(bs: Stream): seq[BencodeObject] =
expectToken(bs, 'l')
while bs.peekChar() != 'e':
result.add(decodeBencode(bs))
expectToken(bs, 'e')
proc parseDict(bs: Stream): BencodeDict =
expectToken(bs, 'd')
while bs.peekChar() != 'e':
let (key, val) = (parseByteSequence(bs), decodeBencode(bs))
result[key] = val
expectToken(bs, 'e')
proc decodeBencode*(bs: Stream): BencodeObject =
let c = bs.peekChar()
if isDigit(c):
if c == '0':
raise newException(BencodeSyntaxError, "Byte string with zero length")
let bseq = parseByteSequence(bs)
return BencodeObject(kind: bokBseq, bseq: bseq)
case c
of 'i':
let num = parseBencodeInteger(bs)
result = BencodeObject(kind: bokNum, num: num)
of 'l':
let ben = parseList(bs)
result = BencodeObject(kind: bokList, list: ben)
of 'd':
let dict = parseDict(bs)
result = BencodeObject(kind: bokDict, dict: dict)
of '\0':
raise newException(BencodeSyntaxError, fmt"Unexpected EOB")
else:
raise newException(BencodeSyntaxError, fmt"Unknown token `{c}`")
#var bs = newStringStream("li1234e4:spam6:i1234ed3:foo3:bar4:fuzzli1337e4:abcdeee")
var bs = newStringStream("li1234el4:spamli1337e4:eggseee")
echo decodeBencode(bs).repr()
bs.close()

View file

@ -0,0 +1,19 @@
SRC = $(wildcard *.c)
OBJS = $(SRC:.c=.o)
RELEASE_TARBALL = nsforth.tar.gz
CFLAGS = -Wall -Wextra -ggdb3 -O0
nsforth: $(OBJS)
$(CC) $(OBJS) -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $<
release:
touch $(RELEASE_TARBALL)
tar -cvzf $(RELEASE_TARBALL) ../nsforth
clean:
rm -f *.o *.tar.gz nsforth
.PHONY: all clean release

66
proglangs/nsforth/defs.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef _DEFS_H_
#define _DEFS_H_
#define INT_TYPE intmax_t
#define FLAG_NATIVE 0x1u
#define FLAG_IMMEDIATE 0x2u
#define FLAG_PARAM_WORD 0x4u
#define SIZE_ARR(a) (sizeof(a) / sizeof(*(a)))
#define STRIP_FINAL_NL(s) s[strcspn(s, "\r\n")] = '\0'
#define unbox_exsp(ctype, type, err) (*(ctype *)unbox_ex(pop_stack(), type, err))
#define unbox_exb(box, ctype, type, err) (*(ctype *)unbox_ex(box, type, err))
#define strnil(s) ((s) == NULL || *(s) == '\0')
#define UNREACHABLE() fatal_error("%s:%d: unreachable clause\n", __func__, __LINE__)
#define fatal_error(fmt, ...) \
do { \
error(fmt, ##__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0);
typedef void (*dict_fn)(void);
struct dict_ent
{
uint8_t flags;
char *word_name;
dict_fn nat_fn;
struct comp_word *fn_def;
};
struct dict_table
{
struct dict_table *prev; // previous environment
struct dict_ent *entries; // symbols in this environment
size_t level;
size_t capacity;
size_t used;
};
struct comp_word
{
size_t len;
size_t capacity;
struct value_box *ents;
};
enum comp_ent_type {
CET_WORD, CET_NUMBER, CET_ADDR,
};
struct value_box
{
enum comp_ent_type type;
union {
struct dict_ent *word; // pointer to a word entry in the dictionary
void *addr;
INT_TYPE number;
};
};
void *xcalloc(size_t nmemb, size_t size);
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif

170
proglangs/nsforth/dict.c Normal file
View file

@ -0,0 +1,170 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "dict.h"
#include <stdio.h>
#define FREE_DICT_NAMES(env) \
while ((env)->capacity > 0) { \
char *word_name = (env)->entries[--(env)->capacity].word_name; \
if (word_name != NULL && word_name != DELETED_ENTRY_MARK) \
free(word_name); \
}
const size_t DEFAULT_HASHMAP_SIZE = 255;
const size_t HASHMAP_REHASH_MARK = 70;
struct dict_ent *dict_last;
static size_t dict_table_fnv1a(const char *key, const size_t key_len)
{
const size_t fnv_offset = 0xcbf29ce484222325;
const size_t fnv_prime = 0x100000001b3;
size_t hash = fnv_offset;
for (size_t ki = 0; ki < key_len; ++ki) {
hash ^= (uint8_t)key[ki];
hash *= fnv_prime;
}
return hash;
}
static void dict_destroy_enviroment(struct dict_table *env)
{
FREE_DICT_NAMES(env);
free(env->entries);
free(env);
}
struct dict_table *dict_new_env(struct dict_table *prev_env)
{
struct dict_table *new_env = xcalloc(1, sizeof(*new_env));
new_env->entries = xcalloc(DEFAULT_HASHMAP_SIZE, sizeof(struct dict_ent));
new_env->prev = prev_env;
new_env->capacity = DEFAULT_HASHMAP_SIZE;
if (prev_env != NULL)
new_env->level = prev_env->level + 1;
return new_env;
}
void dict_destroy_env(struct dict_table *env)
{
if (env->prev == NULL) {
dict_destroy_enviroment(env);
return;
}
while (env != NULL) {
struct dict_table *tmp = env;
env = env->prev;
dict_destroy_enviroment(tmp);
}
}
struct dict_ent *_dict_insert(
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
struct comp_word *fn_def
);
// Resizes the bucket size if needed (shrink or expand), also removes deleted entries
void dict_table_rehash(struct dict_table *env)
{
const size_t new_capacity = env->capacity * 2;
struct dict_table new_dict_table = {0};
new_dict_table.entries = xcalloc(new_capacity, sizeof(struct dict_ent));
new_dict_table.capacity = new_capacity;
// copy elements to the new dict_table, removing deleted entries in the process
for (size_t i = 0; i < env->capacity; ++i) {
struct dict_ent *oldent = &env->entries[i];
//if (oldent->word_name != NULL && oldent->word_name != DELETED_ENTRY_MARK) {
if (IS_ENTRY(oldent)) {
dict_insert(&new_dict_table, oldent->word_name, oldent->flags, oldent->nat_fn, oldent->fn_def);
}
}
FREE_DICT_NAMES(env);
free(env->entries); // free old buckets
env->capacity = new_capacity;
env->entries = new_dict_table.entries;
}
struct dict_ent *_dict_insert(
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
struct comp_word *fn_def
)
{
if ((env->used * 100) / env->capacity >= HASHMAP_REHASH_MARK)
dict_table_rehash(env);
size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
while (env->entries[hash_index].word_name != NULL) {
++hash_index;
if (hash_index >= env->capacity)
hash_index = 0;
}
if (env->entries[hash_index].word_name != DELETED_ENTRY_MARK)
++env->used;
struct dict_ent *dslot = &env->entries[hash_index];
dslot->word_name = (char *)word_name;
dslot->flags = flags;
dslot->nat_fn = nat_fn;
dslot->fn_def = fn_def;
dict_last = dslot;
return dslot;
}
struct dict_ent *dict_insert(
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
struct comp_word *fn_def
)
{
return _dict_insert(env, strdup(word_name), flags, nat_fn, fn_def);
}
// like dict_lookup it searches a symbol, but limited to the current environment (scope)
struct dict_ent *dict_lookup(struct dict_table *env, const char *word_name)
{
if (env->entries == NULL)
return NULL;
size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
while (env->entries[hash_index].word_name != NULL) {
struct dict_ent *ent = &env->entries[hash_index];
if (ent->word_name != DELETED_ENTRY_MARK && !strcmp(ent->word_name, word_name))
return ent;
++hash_index;
if (hash_index >= env->capacity)
hash_index = 0;
}
// entry not found
return NULL;
}
// searches for a symbol in the current table and in all linked environments (scopes)
//struct dict_ent *dict_lookup(struct dict_table *env, char *word_name)
//{
// if (env->entries == NULL)
// return NULL;
// size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
//
// for (struct dict_table *s = env; s != NULL; s = s->prev) {
// while (s->entries[hash_index].word_name != NULL) {
// if (s->entries[hash_index].word_name != DELETED_ENTRY_MARK && !strcmp(s->entries[hash_index].word_name, word_name))
// return &s->entries[hash_index];
//
// ++hash_index;
// if (hash_index >= s->capacity)
// hash_index = 0;
// }
// }
//
// // entry not found
// return NULL;
//}

17
proglangs/nsforth/dict.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef _DICT_H_
#define _DICT_H_
#include "defs.h"
#define DELETED_ENTRY_MARK ((void *)-1)
#define IS_ENTRY(ent) ((ent)->word_name != NULL && (ent)->word_name != DELETED_ENTRY_MARK)
#define dict_delete(ent) ((ent)->word_name = DELETED_ENTRY_MARK)
//#define dict_enter_scope(sym_t) ((sym_t = dict_new_enviroment(sym_t)))
//#define dict_leave_scope(sym_t) ((sym_t = sym_t->prev))
struct dict_table *dict_new_env(struct dict_table *prev_env);
void dict_destroy_env(struct dict_table *env);
struct dict_ent *dict_insert(struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn, struct comp_word *fn_def);
struct dict_ent *dict_lookup(struct dict_table *env, const char *word_name);
//struct dict_ent *dict_lookup_scoped(struct dict_table *env, char *word_name);
#endif

View file

580
proglangs/nsforth/nsforth.c Normal file
View file

@ -0,0 +1,580 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include <inttypes.h>
#include <errno.h>
#include "defs.h"
#include "dict.h"
#define STACK_SIZE 4096u
#define DEF_NATIVE(name) void name##_fn(void)
#define DEF_ARITH_FN(op, name, ctype) \
DEF_NATIVE(name) { \
int err = 0; \
struct value_box *n2 = pop_stack(); \
struct value_box *n1 = pop_stack(); \
if (n1 == NULL || n2 == NULL) return; \
ctype v2 = unbox_exb(n2, ctype, CET_NUMBER, &err); \
ctype v1 = unbox_exb(n1, ctype, CET_NUMBER, &err); \
if (err) return; \
push_stack(box_number(v1 op v2)); \
}
#define DEF_BOX(name, val_type, box_member, type_tag) \
struct value_box *box_##name(val_type val) { \
struct value_box *tmp = xcalloc(1, sizeof(*tmp)); \
tmp->type = type_tag; \
tmp->box_member = val; \
return tmp; \
}
struct {
struct value_box *stack[STACK_SIZE];
size_t sp;
} data_stack;
struct dict_table *dict;
extern struct dict_ent *dict_last; // pointer to the last added dictionary entry
size_t pc;
char *next_word;
char **program;
size_t program_len;
int compile_mode;
int error_flag;
void error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
error_flag = 1;
}
intmax_t xstrtoimax(const char *restrict nptr, int base, int *restrict eflag)
{
errno = 0;
char *endptr;
intmax_t val = strtoimax(nptr, &endptr, base);
if (((val == INTMAX_MAX || val == INTMAX_MIN) && errno == ERANGE) || (!val && errno == EINVAL) || *endptr != '\0') {
if (eflag != NULL)
*eflag = 1;
else
fatal_error("invalid number on stream\n");
}
return val;
}
// unboxes a value, only if it has the same type as specified.
// must be cast to the appropiate C type, since it returns a void *
void *unbox_ex(struct value_box *val, enum comp_ent_type type, int *err)
{
#define SETNNIL(v) if ((v) != NULL) {*(v) = 1;}
if (val == NULL) {
SETNNIL(err);
return NULL;
}
if (val->type != type) {
error("invalid type\n");
SETNNIL(err);
return NULL;
}
switch (val->type) {
case CET_NUMBER: return &val->number;
case CET_WORD: return &val->word;
case CET_ADDR: return &val->addr;
default: UNREACHABLE();
}
return NULL;
#undef SETNNIL
}
void push_stack(struct value_box *val)
{
size_t *sp = &data_stack.sp;
if ((*sp + 1) > SIZE_ARR(data_stack.stack)) {
error("stack overflow\n");
return;
}
data_stack.stack[(*sp)++] = val;
}
struct value_box *pop_stack()
{
size_t *sp = &data_stack.sp;
if ((*sp - 1) == SIZE_MAX) {
error("stack underflow\n");
return NULL;
}
return data_stack.stack[--*sp];
}
static inline void print_box(const struct value_box *obj)
{
switch (obj->type) {
case CET_NUMBER:
printf("%jd", obj->number);
break;
case CET_WORD:
printf("<word at %p (%s)>", (void *)obj->word, obj->word->word_name);
break;
case CET_ADDR: puts("address"); break;
default: UNREACHABLE();
}
}
// non-recursive word dump)
void word_dump(const char *word)
{
struct dict_ent *ent;
if ((ent = dict_lookup(dict, word)) != NULL) {
if (ent->flags & FLAG_NATIVE) {
printf("<native word at %p (%s)>\n", (void *)ent, ent->word_name);
return;
}
printf(": %s ", word);
for (size_t i = 0; i < ent->fn_def->len; ++i) {
struct value_box *cent = &(ent->fn_def->ents[i]);
switch (cent->type) {
case CET_ADDR:
printf("addr\n");
break;
case CET_WORD:
printf("%s ", cent->word->word_name);
break;
case CET_NUMBER:
printf("%jd ", cent->number);
break;
default: UNREACHABLE();
}
}
puts(";");
} else {
error("no such word `%s`\n", word);
}
}
DEF_BOX(number, INT_TYPE, number, CET_NUMBER)
DEF_BOX(word, struct dict_ent *, word, CET_WORD)
// --------------- native (primitives) --------------- //
DEF_ARITH_FN(+, add, INT_TYPE)
DEF_ARITH_FN(-, sub, INT_TYPE)
DEF_ARITH_FN(*, mul, INT_TYPE)
DEF_ARITH_FN(/, div, INT_TYPE)
DEF_ARITH_FN(==, equal, INT_TYPE)
DEF_ARITH_FN(<, less, INT_TYPE)
DEF_NATIVE(clearstack) {
while (data_stack.sp > 0)
free(data_stack.stack[--data_stack.sp]);
}
// ' <word> returns the address of a dictionary entry, aborting if it does not exist
DEF_NATIVE(quote) {
++pc;
if (strnil(next_word))
error("' requires a name\n");
struct dict_ent *ent = dict_lookup(dict, next_word);
if (ent == NULL) {
error("no such word `%s'\n", next_word);
return;
}
push_stack(box_word(ent));
}
// allot (n -- ) adds n bytes to the parameter field of the most recently defined word
DEF_NATIVE(allot) {
int err = 0;
const size_t nbytes = unbox_exsp(size_t, CET_NUMBER, &err);
if (err)
return;
const size_t org_len = dict_last->fn_def->len;
const size_t len = org_len + nbytes;
struct value_box **ents = &dict_last->fn_def->ents;
dict_last->fn_def->capacity = len;
*ents = reallocarray(*ents, len, sizeof(struct value_box));
// printf("allot: %d\n", len - dict_last->fn_def->len);
// clear the newly allocated part
//memset(*ents + org_len, 0, len - org_len);
}
DEF_NATIVE(comma) {
struct comp_word *pf = dict_last->fn_def;
if (pf->ents == NULL) {
pf->capacity = 4;
pf->ents = xcalloc(pf->capacity, sizeof(struct value_box));
} else if (pf->len + 1 > pf->capacity) {
pf->capacity <<= 1;
pf->ents = reallocarray(pf->ents, pf->capacity, sizeof(struct value_box));
}
struct value_box *val = pop_stack();
if (val == NULL)
return;
pf->ents[pf->len++] = *val;
}
DEF_NATIVE(create) {
++pc;
if (strnil(next_word))
error("create requires a name\n");
struct comp_word *param = xcalloc(1, sizeof(*param));
dict_insert(dict, next_word, 0, NULL, param);
}
DEF_NATIVE(forget) {
++pc;
if (strnil(next_word))
error("forget requires a name\n");
struct dict_ent *ent;
if ((ent = dict_lookup(dict, next_word)) == NULL) {
error("no such word `%s'\n", next_word);
return;
}
if (!(ent->flags & FLAG_NATIVE))
free(ent->fn_def->ents);
dict_delete(ent);
}
DEF_NATIVE(see) {
++pc;
if (strnil(next_word)) {
error("see requires a name\n");
return;
}
word_dump(next_word);
}
DEF_NATIVE(words) {
for (size_t i = 0; i < dict->capacity; ++i)
if (IS_ENTRY(&dict->entries[i]))
printf("%s ", dict->entries[i].word_name);
putchar('\n');
}
DEF_NATIVE(compile) {
compile_mode = 1;
}
DEF_NATIVE(endcompile) {
compile_mode = 0;
}
DEF_NATIVE(dup) {
push_stack(data_stack.stack[data_stack.sp - 1]);
}
DEF_NATIVE(drop) {
free(pop_stack());
}
DEF_NATIVE(over) {
push_stack(data_stack.stack[data_stack.sp - 2]);
}
DEF_NATIVE(swap) {
struct value_box *e2 = pop_stack();
struct value_box *e1 = pop_stack();
if (e1 == NULL || e2 == NULL)
return;
push_stack(e2);
push_stack(e1);
}
DEF_NATIVE(zero) {
int err = 0;
struct value_box *tmp = box_number(!unbox_exsp(INT_TYPE, CET_NUMBER, &err));
if (err)
return;
push_stack(tmp);
}
DEF_NATIVE(emit) {
int err = 0;
int chr = unbox_exsp(int, CET_NUMBER, &err);
if (!err)
putchar(chr);
}
DEF_NATIVE(print) {
struct value_box *val = pop_stack();
if (val == NULL)
return;
print_box(val);
putchar('\n');
free(val);
}
DEF_NATIVE(prints) {
if (data_stack.sp) {
size_t c = 0;
printf("stack dump: <%zu> ", data_stack.sp);
while (c < data_stack.sp) {
print_box(data_stack.stack[c++]);
putchar(' ');
}
putchar('\n');
}
}
static inline void append_to_code(struct value_box *slot, const char *current_word)
{
int errn = 0;
intmax_t val = xstrtoimax(current_word, 0, &errn);
if (!errn) {
slot->type = CET_NUMBER;
slot->number = val;
} else {
struct dict_ent *ent = dict_lookup(dict, current_word);
if (ent == NULL)
fatal_error("no such word `%s`\n", current_word);
slot->type = CET_WORD;
slot->word = ent;
}
}
void eval_def(struct comp_word *def);
void if_fn()
{
size_t start_pc = ++pc;
size_t end_pc;
while (pc < program_len && strcmp(program[pc], "end") != 0)
++pc;
if (pc >= program_len)
fatal_error("expected `end`, reached EOP\n");
end_pc = pc;
int err = 0;
int cond = unbox_exsp(int, CET_NUMBER, &err);
if (err)
fatal_error("if requires a condition\n");
if (cond) {
char **program_slice = program + start_pc;
size_t len = end_pc - start_pc;
struct value_box *code = xcalloc(len, sizeof(*code));
struct comp_word c = {.len = len, .capacity = len, .ents = code};
for (size_t i = 0; i < len; ++i)
append_to_code(&code[i], program_slice[i]);
eval_def(&c);
free(code);
}
}
// --------------- interpretation --------------- //
void eval_def(struct comp_word *def)
{
for (size_t i = 0; i < def->len; ++i) {
const struct value_box *cent = &(def->ents[i]);
struct dict_ent *ent;
switch (cent->type) {
case CET_WORD:
ent = cent->word;
if (ent->flags & FLAG_NATIVE)
ent->nat_fn();
else
eval_def(ent->fn_def);
break;
case CET_NUMBER:
push_stack(box_number(cent->number));
break;
default: UNREACHABLE();
}
}
}
void interpret(char **prog, size_t prog_len)
{
struct comp_word *c_word = NULL;
char *fn_name = NULL;
for (; pc < prog_len; ++pc) {
if (error_flag) {
pc = prog_len;
break;
}
char *word = prog[pc];
next_word = (pc + 1) < prog_len ? prog[pc + 1] : NULL;
if (compile_mode) {
if (*word == ';') {
dict_insert(dict, fn_name, 0, NULL, c_word);
c_word = NULL;
fn_name = NULL;
compile_mode = 0;
continue;
}
if (c_word == NULL) {
c_word = xcalloc(1, sizeof(*c_word));
c_word->ents = xcalloc(256, sizeof(*c_word->ents));
}
if (fn_name == NULL) {
fn_name = word;
continue;
}
append_to_code(&(c_word->ents[c_word->len++]), word);
} else {
int nef = 0;
INT_TYPE num = xstrtoimax(word, 0, &nef);
if (!nef) {
push_stack(box_number(num));
continue;
}
struct dict_ent *ent;
if ((ent = dict_lookup(dict, word)) != NULL) {
if (ent->flags & FLAG_NATIVE)
ent->nat_fn();
else
eval_def(ent->fn_def);
} else {
fatal_error("interpretation: no such word `%s`\n", word);
}
}
}
}
// --------------- Parsing --------------- //
void tokenize(char *s, char **buf, size_t buflen, size_t *i)
{
const char delim = ' ';
char *p = s, *wp = s;
while (*p != '\0') {
if (*p == '(') { // eat block comment
while (*p != ')' && *p != '\0')
++p;
p += 2; // jump past the ) and space
wp = p;
continue;
} else if (*p == '\\') {
while (*p++ != '\0');
wp = p;
continue;
}
if (*p == delim) {
if (*i + 1 > buflen)
return;
*p = '\0';
buf[(*i)++] = strdup(wp);
wp = ++p;
} else {
++p;
}
}
if (wp < p && *i < buflen) // handle last element
buf[(*i)++] = strdup(wp);
}
size_t parse_program(FILE *fp, char **buf, size_t buflen)
{
size_t i = 0;
char line_buf[1024] = {0};
while (fgets(line_buf, sizeof(line_buf), fp) != NULL) {
STRIP_FINAL_NL(line_buf);
tokenize(line_buf, buf, buflen, &i);
}
return i;
}
void repl(void)
{
char *prog[1024] = {0};
size_t prog_len = 0;
char *line_buf = NULL;
size_t line_buf_len = 0;
ssize_t rb = 0;
int prompt_comp_mode = 0;
errno = 0;
for (;;) {
printf("[%s]%s ", error_flag ? "err" : "ok", !prompt_comp_mode ? ">" : ">>");
fflush(stdout);
error_flag = 0;
if ((rb = getline(&line_buf, &line_buf_len, stdin)) < 0)
break;
STRIP_FINAL_NL(line_buf);
tokenize(line_buf, prog, SIZE_ARR(prog), &prog_len);
if (strchr(line_buf, ':') != NULL)
prompt_comp_mode = 1;
else if (strchr(line_buf, ';') != NULL)
prompt_comp_mode = 0;
if (!prompt_comp_mode)
interpret(prog, prog_len);
}
if (rb < 0 && errno)
fatal_error("getline err\n");
free(line_buf);
}
void init()
{
dict = dict_new_env(NULL);
dict_insert(dict, "+", FLAG_NATIVE, add_fn, NULL);
dict_insert(dict, "-", FLAG_NATIVE, sub_fn, NULL);
dict_insert(dict, "*", FLAG_NATIVE, mul_fn, NULL);
dict_insert(dict, "=", FLAG_NATIVE, equal_fn, NULL);
dict_insert(dict, "<", FLAG_NATIVE, less_fn, NULL);
dict_insert(dict, ".", FLAG_NATIVE, print_fn, NULL);
dict_insert(dict, ".s", FLAG_NATIVE, prints_fn, NULL);
dict_insert(dict, "zero?", FLAG_NATIVE, zero_fn, NULL);
dict_insert(dict, ":", FLAG_NATIVE, compile_fn, NULL);
dict_insert(dict, ";", FLAG_NATIVE | FLAG_IMMEDIATE, endcompile_fn, NULL);
dict_insert(dict, "'", FLAG_NATIVE, quote_fn, NULL);
dict_insert(dict, ",", FLAG_NATIVE, comma_fn, NULL);
dict_insert(dict, "print", FLAG_NATIVE, print_fn, NULL);
dict_insert(dict, "see", FLAG_NATIVE, see_fn, NULL);
dict_insert(dict, "dup", FLAG_NATIVE, dup_fn, NULL);
dict_insert(dict, "drop", FLAG_NATIVE, drop_fn, NULL);
dict_insert(dict, "over", FLAG_NATIVE, over_fn, NULL);
dict_insert(dict, "swap", FLAG_NATIVE, swap_fn, NULL);
dict_insert(dict, "create", FLAG_NATIVE, create_fn, NULL);
dict_insert(dict, "forget", FLAG_NATIVE, forget_fn, NULL);
dict_insert(dict, "allot", FLAG_NATIVE, allot_fn, NULL);
dict_insert(dict, "cst", FLAG_NATIVE, clearstack_fn, NULL);
dict_insert(dict, "words", FLAG_NATIVE, words_fn, NULL);
dict_insert(dict, "emit", FLAG_NATIVE, emit_fn, NULL);
dict_insert(dict, "if", FLAG_NATIVE, if_fn, NULL);
}
int main(int argc, char **argv)
{
init();
if (argc < 2) {
printf("nsforth interactive repl (build: %s, %s)\n", __DATE__, __TIME__);
repl();
} else {
FILE *fp = fopen(*++argv, "r");
if (fp == NULL)
fatal_error("error opening '%s': %s\n", *argv, strerror(errno));
char *prog[1024] = {0};
size_t prog_len = parse_program(fp, prog, SIZE_ARR(prog));
fclose(fp);
program = prog;
program_len = prog_len;
interpret(prog, prog_len);
}
dict_destroy_env(dict);
return EXIT_SUCCESS;
}

38
proglangs/nsforth/test.fs Normal file
View file

@ -0,0 +1,38 @@
\ nsforth a small and slow implementation of a subset of Forth.
: sq ( n -- n ) dup * ;
: foo dup * 10 + ;
5 foo . 42 .s 3 zero? .
see foo \ see foo definition
' foo .
' foo 32 .s swap .s
create array1 4 allot \ 4 elements
create array? 1 , 2 ,
: inc ( n1 -- n1 ) 1 + ;
: dec ( n1 -- n1 ) 1 - ;
4 inc .
4 dec .
\ Forth is a concatenative language, that is:
: fn1 2 + ;
: fn2 3 * ;
: fn3 4 * ;
3 fn1 fn2 fn3 .
forget fn1 forget fn2 forget fn3
\ 4 fn1 <- error
: let create ;
cst
-4 3 - .
\ <cond> if <body> end
2 3 + 10 < if
3 sq .
end
let var1 2 ,
\ <cond> while <body> end
\ import file.fs

20
proglangs/nsforth/util.c Normal file
View file

@ -0,0 +1,20 @@
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
void *xcalloc(size_t nmemb, size_t size)
{
void *ptr = calloc(nmemb, size);
if (ptr == NULL && (nmemb || size))
abort();
return ptr;
}
void *reallocarray(void *ptr, size_t nmemb, size_t size)
{
if (nmemb && size > (SIZE_MAX / nmemb)) {
errno = ENOMEM;
return NULL;
}
return realloc(ptr, nmemb * size);
}

8
proglangs/rpn-go/go.mod Normal file
View file

@ -0,0 +1,8 @@
module rpn
go 1.20
require (
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.8.0 // indirect
)

4
proglangs/rpn-go/go.sum Normal file
View file

@ -0,0 +1,4 @@
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=

3
proglangs/rpn-go/prog1 Normal file
View file

@ -0,0 +1,3 @@
77 50 + .
77 50 + 2 * .
bye

130
proglangs/rpn-go/rpn.go Normal file
View file

@ -0,0 +1,130 @@
package main
import (
"bufio"
"fmt"
"golang.org/x/term"
"os"
"strings"
)
type Word struct {
name string
impl func(stack *Stack) error
}
var Dictionary = map[string]Word{
"+": Word{impl: natAdd},
"*": Word{impl: natMul},
"-": Word{impl: natSub},
"/": Word{impl: natDiv},
"mod": Word{impl: natMod},
".": Word{impl: natPrint},
"s?": Word{impl: natPrintStack},
"dup": Word{impl: natDup},
"drop": Word{impl: natDrop},
"rnd": Word{impl: natRnd},
"bye": Word{impl: natBye},
}
func isNumberAscii(chr byte) bool {
return chr >= '0' && chr <= '9'
}
func isIdent(chr byte) bool {
return (chr >= 'A' && chr <= 'Z') ||
(chr >= 'a' && chr <= 'z') ||
(chr >= '!' && chr <= '/') ||
(chr >= ':' && chr <= '`')
}
func parseNumber(s string, cur *int) StackType {
var num int
const digits = "0123456789abcdf"
for _, c := range s {
bc := byte(c)
if !isNumberAscii(bc) {
*cur--
break
}
idx := strings.IndexByte(digits, bc)
if idx < 0 { // we should't reach this place
panic("unexpected non-digit character")
}
num = num*10 + idx
*cur++
}
return StackType(num)
}
func parseWord(s string, cur *int) string {
for i, c := range s {
if !isIdent(byte(c)) {
return s[:i]
}
*cur++
}
return s
}
func lookupWord(wordName string) (*Word, error) {
word, ok := Dictionary[wordName]
if !ok {
return nil, fmt.Errorf("no such word: `%s`", wordName)
}
return &word, nil
}
func eval(stack *Stack, prog string) {
for i := 0; i < len(prog); i++ {
bc := byte(prog[i])
if bc == ' ' || bc == '\t' || bc == '\n' || bc == '\r' {
continue
}
if isNumberAscii(bc) {
num := parseNumber(prog[i:], &i)
stack.push(num)
} else {
wordImpl, err := lookupWord(parseWord(prog[i:], &i))
if err != nil {
fmt.Printf("Error: %s\n", err)
break
}
if err := wordImpl.impl(stack); err != nil {
fmt.Printf("Error: %s\n", err)
}
}
}
}
func main() {
stack := new(Stack)
stack.data = make([]StackType, 0, StackSize)
Dictionary["words"] = Word{impl: natHelp}
Dictionary["s!"] = Word{
impl: func(stack *Stack) error {
stack.wipe()
return nil
},
}
is_tty := term.IsTerminal(int(os.Stdin.Fd()))
scanner := bufio.NewScanner(os.Stdin)
if is_tty {
fmt.Println("RPN calculator - Type Ctrl-D or \"bye\" to exit")
}
for {
if is_tty {
fmt.Print("> ")
}
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
panic(err)
}
break
}
eval(stack, scanner.Text())
}
}

31
proglangs/rpn-go/stack.go Normal file
View file

@ -0,0 +1,31 @@
package main
import "errors"
const StackSize = 64
type StackType int
type Stack struct {
data []StackType
pos int
}
func (s *Stack) push(val StackType) {
s.data = append(s.data, val)
s.pos++
}
func (s *Stack) pop() (StackType, error) {
if s.pos-1 < 0 {
return 0, errors.New("stack is empty")
}
s.pos--
res := s.data[s.pos]
s.data = s.data[:s.pos]
return res, nil
}
func (s *Stack) wipe() {
s.pos = 0
s.data = s.data[:0]
}

98
proglangs/rpn-go/words.go Normal file
View file

@ -0,0 +1,98 @@
package main
import (
"fmt"
"os"
)
func binOp(stack *Stack, op func(StackType, StackType) StackType) error {
v1, err := stack.pop()
v2, err := stack.pop()
if err != nil {
return err
}
stack.push(op(v1, v2))
return nil
}
func natAdd(stack *Stack) error {
return binOp(stack, func(y StackType, x StackType) StackType {
return x + y
})
}
func natSub(stack *Stack) error {
return binOp(stack, func(y StackType, x StackType) StackType {
return x - y
})
}
func natMul(stack *Stack) error {
return binOp(stack, func(y StackType, x StackType) StackType {
return x * y
})
}
func natDiv(stack *Stack) error {
return binOp(stack, func(y StackType, x StackType) StackType {
return x / y
})
}
func natMod(stack *Stack) error {
return binOp(stack, func(y StackType, x StackType) StackType {
return x % y
})
}
func natPrint(stack *Stack) error {
val, err := stack.pop()
if err != nil {
return err
}
fmt.Printf("%d\n", val)
return nil
}
func natPrintStack(stack *Stack) error {
if stack.pos == 0 {
return nil
}
for _, v := range stack.data {
fmt.Printf("%d ", v)
}
fmt.Println("")
return nil
}
func natDup(stack *Stack) error {
val := stack.data[stack.pos-1]
stack.push(val)
return nil
}
func natDrop(stack *Stack) error {
if _, err := stack.pop(); err != nil {
return err
}
return nil
}
func natRnd(stack *Stack) error {
stack.push(42)
return nil
}
func natBye(stack *Stack) error {
os.Exit(0)
return nil
}
func natHelp(stack *Stack) error {
fmt.Println("Defined words:")
for k := range Dictionary {
fmt.Print(k, " ")
}
fmt.Print("\n")
return nil
}

153
protocols/ntp.c Normal file
View file

@ -0,0 +1,153 @@
// NTP client simple enough to print the current date and time in ctime() and ISO 8601 format
// https://datatracker.ietf.org/doc/html/rfc5905
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define NTP_PORT 123
#define _INT2STR(x) #x
#define MACRO_INT2STR(x) _INT2STR(x)
#define NTP_PORT_STR MACRO_INT2STR(NTP_PORT)
//const uint8_t NTP_PORT = 123;
const uint64_t NTP_UNIX_DELTA = 2208988800ull; // January 1, 1970 in NTP timestamp format
const uint8_t NTP_CURRENT_VER = 4;
struct ntp_packet
{
uint8_t leap_ver_mode;
uint8_t stratum;
uint8_t poll;
uint8_t precision;
uint32_t root_delay;
uint32_t root_dispersion;
uint32_t ref_id;
uint32_t ref_tstamp_sec;
uint32_t ref_tstamp_frac;
uint32_t origin_tstamp_sec;
uint32_t origin_tstamp_frac;
uint32_t rx_tstamp_sec;
uint32_t rx_tstamp_frac;
uint32_t tx_tstamp_sec;
uint32_t tx_tstamp_frac;
};
//#include <sys/time.h>
//int set_system_date(time_t date)
//{
// if (settimeofday(&(struct timeval){.tv_sec = date, .tv_usec = 0}, NULL) < 0) {
// perror("settimeofday");
// return 1;
// }
// return 0;
//}
int main(const int argc, const char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: ./ntp [ntp server hostname]\n");
return 1;
}
int ntp_server_fd;
// create UDP connection
if ((ntp_server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket");
return 1;
}
const struct timeval time_val = {.tv_sec = 4, .tv_usec = 0};
if (setsockopt(ntp_server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &(int){1}, sizeof(int)) < 0
|| setsockopt(ntp_server_fd, SOL_SOCKET, SO_RCVTIMEO, &time_val, sizeof(struct timeval)) < 0
|| setsockopt(ntp_server_fd, SOL_SOCKET, SO_SNDTIMEO, &time_val, sizeof(struct timeval)) < 0
) {
perror("setsockopt");
return 1;
}
struct addrinfo *res;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP,
.ai_flags = 0,
};
int ret_val = 0;
if ((ret_val = getaddrinfo(argv[1], MACRO_INT2STR(NTP_PORT), &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret_val));
return 1;
}
for (; res != NULL; res = res->ai_next) {
//char h[NI_MAXHOST] = {0};
//getnameinfo(res->ai_addr, res->ai_addrlen, h, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
//printf("getaddrinfo ip: %s\n", h);
if (connect(ntp_server_fd, res->ai_addr, res->ai_addrlen) < 0) {
perror("connect");
freeaddrinfo(res);
return 1;
} else { // if zero, we already connected
break;
}
}
freeaddrinfo(res);
struct ntp_packet ntp_pk = {
.leap_ver_mode = 0x23, // leap = 0, vn = 4, mode = 3 (client)
};
// send packet
if (send(ntp_server_fd, &ntp_pk, sizeof(struct ntp_packet), 0) < 0) {
perror("send");
return 1;
}
// receive answer packet from the server
if (recv(ntp_server_fd, &ntp_pk, sizeof(struct ntp_packet), 0) < 0) {
perror("recv");
return 1;
}
// close connection with the server
shutdown(ntp_server_fd, SHUT_RDWR);
close(ntp_server_fd);
ntp_pk.leap_ver_mode = ntohs(ntp_pk.leap_ver_mode);
ntp_pk.stratum = ntohs(ntp_pk.stratum);
//ntp_pk.ref_id = ntohl(ntp_pk.ref_id);
printf("NTP version: %u\n", ntp_pk.leap_ver_mode);
printf("stratum: %u\n", ntp_pk.stratum);
if ((ntp_pk.leap_ver_mode & 0x38) >= NTP_CURRENT_VER) {
fprintf(stderr, "invalid version number, got %u", ntp_pk.leap_ver_mode & 0x38);
return 1;
}
// check if it is a KoD packet
if (ntp_pk.stratum == 0) {
char kod_msg[5] = {0};
memcpy(&kod_msg, &ntp_pk.ref_id, 4);
printf("KoD msg: %s\n", kod_msg);
}
ntp_pk.tx_tstamp_sec = ntohl(ntp_pk.tx_tstamp_sec);
ntp_pk.tx_tstamp_frac = ntohl(ntp_pk.tx_tstamp_frac);
time_t ntp_time = (time_t)(ntp_pk.tx_tstamp_sec - NTP_UNIX_DELTA);
char fmt_time[64] = {0};
strftime(fmt_time, sizeof(fmt_time), "%Y-%m-%d %H:%M:%S%z", localtime(&ntp_time));
printf("Server NTP timestamp: %u (frac): %u\n", ntp_pk.tx_tstamp_sec, ntp_pk.tx_tstamp_frac);
printf("Server NTP time (ctime fmt'd): %s", ctime(&ntp_time));
printf("Server NTP time (ISO 8601): %s\n", fmt_time);
return 0;
}