-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathposix-utils.sh
More file actions
143 lines (132 loc) · 4.69 KB
/
posix-utils.sh
File metadata and controls
143 lines (132 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
################################################################################
#
# Bindings for POSIX shell utilities
#
# ############################# Calling convention #############################
#
# The calling convention is the following:
#
# 1. C functions are prefixed with an underscore when compiled to shell.
#
# 2. The first argument is the return variable, it indicates where the return
# value should be stored. The other arguments are the function arguments.
#
# 3. Variables are globally scoped, and the callee is responsible for restoring
# the initial values of local variables before returning. Variables starting
# with `_` are global variables, variables starting with `__` are reserved
# for the runtime library, and `___` is free to be used by the user.
#
# 4. The arguments and return value can either be immediates (int) or pointers.
# Shell strings must always be converted to the C string format before being
# returned using the `unpack_string` function.
#
# ############################# String conversions #############################
#
# To help with the calling convention, the `pack_string` and `unpack_string`
# functions can be used to convert to/from shell strings and C strings. For
# functions that return multiple strings, the `unpack_lines` function can be
# used to unpack them into an array of strings.
#
# By default, the `unpack_string` function from the runtime library is included.
# This is because the implementation of `unpack_string` depends on the
# compilation options of pnut and may be optimized for different use cases.
#
# Similarly, the `pack_string` function calls the `_put_pstr` function from the
# runtime library so it doesn't have to be included twice (one for the runtime
# library and one for this script).
#
# ############################# Memory allocation ##############################
#
# Memory can be dynamically allocated using the `_malloc` function, and freed
# using the `_free` function. For long running scripts, it is recommended to
# free memory as it tends to slow down the execution of scripts. Because POSIX
# shell requires 32-bit signed integers, the maximum amount of memory that can
# be allocated is 2^31 - 1 words. In practice, most shells will become unusably
# slow or run out of memory before reaching this limit.
#
# Because memory is allocated using a simple bump allocator, address space is
# never reclaimed. Programs that allocate over 2^31 - 1 words may need to manage
# blocks of memory manually to avoid running out of memory.
#
# Because the memory is word-addressable, pointers, integers and characters all
# occupy the same amount of memory. Keep that in mind when allocating
# structures.
#
################################################################################
# Convert a C string to a shell string. The result is stored in $__res.
pack_string() { # $1 = string address
__res=$(_put_pstr __ $1)
}
# Like `unpack_string` but for multiple strings separated by newlines.
# Returns a pointer to the first string in the array, and null terminates the
# array.
unpack_lines() {
___i=1 # Account for null delimiter
IFS="
"
for ___line in $2; do
: $((___i += 1))
done
_malloc $1 $___i
___i=0
for ___line in $2; do
unpack_string _$(($1 + ___i)) "$___line"
: $((___i += 1))
done
: $((_$(($1 + ___i)) = 0)) # Null delimiter
}
_cat() { # $2 = file (char*)
pack_string $2
cat $__res
}
# Return the current date with the format "YYYY-MM-DDTHH:MM:SS"
_date() {
___date=$(date -Iseconds)
unpack_string $1 "$___date"
}
_pwd() {
___pwd=$(pwd)
unpack_string $1 "$___pwd"
}
# Example of a variadic function.
# This function can take an optional string argument for the directory to list.
_ls() { # $2 = dir (char*)
__res=
if [ $# -eq 2 ]; then
pack_string $2
fi
___lines=$(ls $__res)
unpack_lines $1 "$___lines"
}
_touch() { # $2 = file (char*)
pack_string $2
touch $__res
}
# Create a directory and return the exit code.
_mkdir() { # $2 = dir (char*)
pack_string $2
set +e # Ignore errors
mkdir -p $__res
: $(($1 = $?)) # Return the exit code
set -e # Restore set -e
}
_file_permission() { # $2 = file (char*)
pack_string $2
___perms=$(ls -l $__res | cut -c1-10) # Produce -rwxr-xr-x
unpack_string $1 "$___perms"
}
_chmod() { # $2 = mode (int), $3 = file (char*)
pack_string $3
set +e # Ignore errors
chmod "$2" $__res
: $(($1 = $?)) # Return the exit code
set -e # Restore set -e
}
_wc() { # $2 = file (char*), $3 = lines addr (int*), $4 = words addr (int*), $5 = chars addr (int*)
pack_string $2
__res=$(wc $__res | read -r ___lines ___words ___chars _)
# Write result to the addresses
: $((_$3 = ___lines))
: $((_$4 = ___words))
: $((_$5 = ___chars))
}