Pawn 3.2.3664 code with samp_compatible patches applied

This commit is contained in:
Zeex 2013-05-09 01:32:31 +07:00
commit 00f21e67d5
152 changed files with 59559 additions and 0 deletions

10
EXAMPLES/argument.p Normal file
View File

@ -0,0 +1,10 @@
#include <args>
main()
{
printf "Argument count = %d\n", argcount()
new opt[100]
for (new index = 0; argindex(index, opt); index++)
printf "Argument %d = %s\n", index, opt
}

14
EXAMPLES/c2f.p Normal file
View File

@ -0,0 +1,14 @@
#include <rational>
main()
{
new Rational: Celsius
new Rational: Fahrenheit
print "Celsius\t Fahrenheit\n"
for (Celsius = 5; Celsius <= 25; Celsius++)
{
Fahrenheit = (Celsius * 1.8) + 32
printf "%r \t %r\n", Celsius, Fahrenheit
}
}

37
EXAMPLES/capt.p Normal file
View File

@ -0,0 +1,37 @@
bool: ispacked(string[])
return bool: (string[0] > charmax)
my_strlen(string[])
{
new len = 0
if (ispacked(string))
while (string{len} != EOS) /* get character from pack */
++len
else
while (string[len] != EOS) /* get cell */
++len
return len
}
strupper(string[])
{
assert ispacked(string)
for (new i=0; string{i} != EOS; ++i)
string{i} = toupper(string{i})
}
main()
{
new s[10]
for (new i = 0; i < 5; i++)
s{i}=i+'a'
s{5}=EOS
printf("String is %s\n", ispacked(s) ? "packed" : "unpacked")
printf("String length is %d\n", my_strlen(s))
printf("Original: %s\n", s)
strupper(s)
printf("Upper case: %s\n", s)
}

65
EXAMPLES/cards.p Normal file
View File

@ -0,0 +1,65 @@
enum _: CardSuits
{
Clubs,
Diamonds,
Hearts,
Spades,
}
const CardTypes = 13
const TotalCards = CardSuits * CardTypes
enum CardDescription
{
CardName[10 char],
CardSuit,
CardValue,
}
new CardNames[CardTypes][] = { !"Ace", !"Two", !"Three", !"Four", !"Five",
!"Six", !"Seven", !"Eight", !"Nine", !"Ten",
!"Jack", !"Queen", !"King" }
new CardValues[CardTypes] = { 11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10 }
main()
{
new Cards[ TotalCards ][ CardDescription ]
/* fill in the cards */
for (new suit = 0; suit < CardSuits; suit++)
{
for (new card = 0; card < CardTypes; card++)
{
new index = suit*CardTypes + card
strpack Cards[ index ][CardName], CardNames[ card ]
Cards[ index ][ CardSuit ] = suit
Cards[ index ][ CardValue ] = CardValues[ card ]
}
}
/* shuffle the cards (swap an arbitrary number of randomly selected cards) */
for (new iter = 0; iter < 200; iter++)
{
new first = random(TotalCards)
new second = random(TotalCards)
new TempCard[ CardDescription ]
TempCard = Cards[first]
Cards[ first ] = Cards[ second ]
Cards[ second ] = TempCard
}
/* print the cards with a subroutine */
for (new card = 0; card < TotalCards; card++)
PrintCard Cards[ card]
}
PrintCard( TheCard[ CardDescription ] )
{
new SuitNames[ CardSuits ][] = { !"Clubs", !"Diamonds",
!"Hearts", !"Spades" }
printf !"%s of %s (valued %d)\n",
TheCard[ CardName ],
SuitNames[ TheCard[ CardSuit ] ],
TheCard[ CardValue ]
}

31
EXAMPLES/chat.p Normal file
View File

@ -0,0 +1,31 @@
#include <datagram>
@receivestring(const message[], const source[])
printf "[%s] says: %s\n", source, message
@keypressed(key)
{
static string[100 char]
static index
if (key == '\e')
exit /* quit on 'Esc' key */
echo key
if (key == '\r' || key == '\n' || index char == sizeof string)
{
string{index} = '\0' /* terminate string */
sendstring string
index = 0
string[index] = '\0'
}
else
string{index++} = key
}
echo(key)
{
new string[2 char] = { 0 }
string{0} = key == '\r' ? '\n' : key
printf string
}

45
EXAMPLES/comment.p Normal file
View File

@ -0,0 +1,45 @@
/* parse C comments interactively, using events and a state machine */
main()
state plain
@keypressed(key) <plain>
{
state (key == '/') slash
if (key != '/')
echo key
}
@keypressed(key) <slash>
{
state (key != '/') plain
state (key == '*') comment
echo '/' /* print '/' held back from previous state */
if (key != '/')
echo key
}
@keypressed(key) <comment>
{
echo key
state (key == '*') star
}
@keypressed(key) <star>
{
echo key
state (key != '*') comment
state (key == '/') plain
}
echo(key) <plain, slash>
printchar key, yellow
echo(key) <comment, star>
printchar key, green
printchar(ch, colour)
{
setattr .foreground = colour
printf "%c", ch
}

20
EXAMPLES/faculty.p Normal file
View File

@ -0,0 +1,20 @@
/* Calculation of the faculty of a value */
main()
{
print "Enter a value: "
new v = getvalue()
new f = faculty(v)
printf "The faculty of %d is %d\n", v, f
}
faculty(n)
{
assert n >= 0
new result = 1
while (n > 0)
result *= n--
return result
}

26
EXAMPLES/fib.p Normal file
View File

@ -0,0 +1,26 @@
/* Calculation of Fibonacci numbers by iteration */
main()
{
print "Enter a value: "
new v = getvalue()
if (v > 0)
printf "The value of Fibonacci number %d is %d\n",
v, fibonacci(v)
else
printf "The Fibonacci number %d does not exist\n", v
}
fibonacci(n)
{
assert n > 0
new a = 0, b = 1
for (new i = 2; i < n; i++)
{
new c = a + b
a = b
b = c
}
return a + b
}

17
EXAMPLES/gcd.p Normal file
View File

@ -0,0 +1,17 @@
/*
The greatest common divisor of two values,
using Euclides' algorithm .
*/
main()
{
print "Input two values\n"
new a = getvalue()
new b = getvalue()
while (a != b)
if (a > b)
a = a - b
else
b = b - a
printf "The greatest common divisor is %d\n", a
}

297
EXAMPLES/gtkcalc.p Normal file
View File

@ -0,0 +1,297 @@
/* A demo GTK application in Pawn, using gtk-server and the "process control"
* module for Pawn. This example also illustrates the use of (communicating)
* finite state machines.
*/
#include <process>
#include <string>
enum Btn
{
Btn0, Btn1, Btn2,
Btn3, Btn4, Btn5,
Btn6, Btn7, Btn8,
Btn9, BtnDot, BtnC,
BtnCE, BtnPlus, BtnMin,
BtnMul, BtnDiv, BtnEqual,
BtnNone,
}
static entry /* GTK "entry" widget */
static numberstring[40 char]
static accum
static Btn:pending_op
adddigit(digit, bool:reset = false)
{
new command[80 char], charstr[2 char]
charstr{0} = (0 <= digit <= 9) ? digit + '0' : digit
if (reset)
numberstring[0] = EOS
strcat numberstring, charstr
strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring
GTK(command)
}
displayresult(value)
{
new command[80 char]
valstr numberstring, value, true
strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring
GTK(command)
}
resetentry(digit) <number:negated>
{
adddigit '-', .reset = true
adddigit digit
}
resetentry(digit) <>
{
adddigit digit, .reset = true
}
event_0() <number:zero, number:negated>
{
resetentry 0
}
event_0() <number:int, number:frac>
{
adddigit 0
}
event_1_9(Btn:idx) <number:zero, number:negated>
{
resetentry _:(idx - Btn0)
state number:int
}
event_1_9(Btn:idx) <number:int, number:frac>
{
adddigit _:(idx - Btn0)
}
event_dot() <number:zero, number:negated>
{
resetentry 0
adddigit '.'
state number:frac
}
event_dot() <number:int>
{
adddigit '.'
state number:frac
}
event_dot() <number:frac>
{
/* reject entry */
}
event_minus() <number:zero, number:negated>
{
state number:negated
resetentry 0
}
event_minus() <>
{
event_oper BtnMin /* forward to the "calc" automaton */
}
/* helper function for the calculator automaton */
performoperation()
{
/* get the other operand, perform the operation */
new val = strval(numberstring)
switch (pending_op)
{
case BtnPlus: accum += val
case BtnMin: accum -= val
case BtnMul: accum *= val
case BtnDiv: accum = (val == 0) ? 0 : accum / val
}
displayresult accum
}
event_oper(Btn:idx) <calc:idle>
{
/* save operand and operator */
accum = strval(numberstring)
pending_op = idx
state number:zero
state calc:pending
}
event_oper(Btn:idx) <calc:pending>
{
performoperation /* do the pending operation */
pending_op = idx /* save the operator for the next operation */
state number:zero
}
event_equal() <calc:idle>
{
/* ignore */
}
event_equal() <calc:pending>
{
performoperation /* do the pending operation */
state calc:idle /* reset the calculator */
state number:zero
}
event_C()
{
state calc:idle
state number:zero
resetentry 0
}
event_CE()
{
state number:zero
resetentry 0
}
main()
{
if (!procexec("gtk-server stdin") && !procexec("gtk-server.exe stdin"))
fatal "unable to launch gtk-server"
/* make a window */
GTK("gtk_init NULL NULL")
new win = GTK("gtk_window_new 0")
GTK("gtk_window_set_title %d \"Pawn calculator\"", win)
GTK("gtk_widget_set_usize %d 200 200", win)
/* add a table (align the other controls) */
new table = GTK("gtk_table_new 50 50 1")
GTK("gtk_container_add %d %d", win, table)
/* the number entry */
entry = GTK("gtk_entry_new")
GTK("gtk_table_attach_defaults %d %d 1 49 1 9", table, entry)
/* the key pad */
new buttons[Btn]
buttons[BtnDot] = GTK("gtk_button_new_with_label .")
GTK("gtk_table_attach_defaults %d %d 21 29 41 49", table, buttons[BtnDot])
buttons[Btn0] = GTK("gtk_button_new_with_label 0")
GTK("gtk_table_attach_defaults %d %d 1 19 41 49", table, buttons[Btn0])
buttons[Btn1] = GTK("gtk_button_new_with_label 1")
GTK("gtk_table_attach_defaults %d %d 1 9 31 39", table, buttons[Btn1])
buttons[Btn2] = GTK("gtk_button_new_with_label 2")
GTK("gtk_table_attach_defaults %d %d 11 19 31 39", table, buttons[Btn2])
buttons[Btn3] = GTK("gtk_button_new_with_label 3")
GTK("gtk_table_attach_defaults %d %d 21 29 31 39", table, buttons[Btn3])
buttons[Btn4] = GTK("gtk_button_new_with_label 4")
GTK("gtk_table_attach_defaults %d %d 1 9 21 29", table, buttons[Btn4])
buttons[Btn5] = GTK("gtk_button_new_with_label 5")
GTK("gtk_table_attach_defaults %d %d 11 19 21 29", table, buttons[Btn5])
buttons[Btn6] = GTK("gtk_button_new_with_label 6")
GTK("gtk_table_attach_defaults %d %d 21 29 21 29", table, buttons[Btn6])
buttons[Btn7] = GTK("gtk_button_new_with_label 7")
GTK("gtk_table_attach_defaults %d %d 1 9 11 19", table, buttons[Btn7])
buttons[Btn8] = GTK("gtk_button_new_with_label 8")
GTK("gtk_table_attach_defaults %d %d 11 19 11 19", table, buttons[Btn8])
buttons[Btn9] = GTK("gtk_button_new_with_label 9")
GTK("gtk_table_attach_defaults %d %d 21 29 11 19", table, buttons[Btn9])
buttons[BtnC] = GTK("gtk_button_new_with_label C")
GTK("gtk_table_attach_defaults %d %d 31 39 11 19", table, buttons[BtnC])
buttons[BtnCE] = GTK("gtk_button_new_with_label CE")
GTK("gtk_table_attach_defaults %d %d 41 49 11 19", table, buttons[BtnCE])
buttons[BtnPlus] = GTK("gtk_button_new_with_label +")
GTK("gtk_table_attach_defaults %d %d 31 39 21 29", table, buttons[BtnPlus])
buttons[BtnMin] = GTK("gtk_button_new_with_label -")
GTK("gtk_table_attach_defaults %d %d 41 49 21 29", table, buttons[BtnMin])
buttons[BtnMul] = GTK("gtk_button_new_with_label x")
GTK("gtk_table_attach_defaults %d %d 31 39 31 39", table, buttons[BtnMul])
buttons[BtnDiv] = GTK("gtk_button_new_with_label /")
GTK("gtk_table_attach_defaults %d %d 41 49 31 39", table, buttons[BtnDiv])
buttons[BtnEqual] = GTK("gtk_button_new_with_label =")
GTK("gtk_table_attach_defaults %d %d 31 49 41 49", table, buttons[BtnEqual])
/* initialize automata */
state number:zero
state calc:idle
/* wait for events, and dispatch them */
GTK("gtk_widget_show_all %d", win)
resetentry 0
new event
new Btn:idx
do
{
event = GTK("gtk_server_callback wait")
/* find the button matching the event, generate the event */
for (idx = Btn0; idx < BtnNone && buttons[idx] != event; idx++)
{}
switch (idx)
{
case Btn0:
event_0
case Btn1 .. Btn9:
event_1_9 idx
case BtnDot:
event_dot
case BtnMin:
event_minus
case BtnPlus, BtnMul, BtnDiv:
event_oper idx
case BtnEqual:
event_equal
case BtnC:
event_C
case BtnCE:
event_CE
}
}
while (event != win);
/* direct call, because we must not wait for a reply on this command */
procwrite "gtk_exit 0", true
}
GTK(const format[], ...)
{
new command[256 char]
switch (numargs())
{
case 1:
strpack command, format
case 2:
strformat command, _, true, format, getarg(1)
case 3:
strformat command, _, true, format, getarg(1), getarg(2)
case 4:
strformat command, _, true, format, getarg(1), getarg(2), getarg(3)
case 5:
strformat command, _, true, format, getarg(1), getarg(2), getarg(3), getarg(4)
}
procwrite command, true
new reply[30]
procread reply, .striplf=true
if (strcmp(reply, "ok") == 0)
return true
return strval(reply)
}
fatal(const message[])
{
printf "FATAL: %s\n", message
exit
}

17
EXAMPLES/hanoi.p Normal file
View File

@ -0,0 +1,17 @@
/* The Towers of Hanoi, a game solved through recursion */
main()
{
print "How many disks: "
new disks = getvalue()
move 1, 3, 2, disks
}
move(from, to, spare, numdisks)
{
if (numdisks > 1)
move from, spare, to, numdisks-1
printf "Move disk from pillar %d to pillar %d\n", from, to
if (numdisks > 1)
move spare, to, from, numdisks-1
}

2
EXAMPLES/hello.p Normal file
View File

@ -0,0 +1,2 @@
main()
printf "Hello world\n"

6
EXAMPLES/hello2.p Normal file
View File

@ -0,0 +1,6 @@
#include <console>
main()
{
printf("Hello world\n");
}

61
EXAMPLES/julian.p Normal file
View File

@ -0,0 +1,61 @@
/* calculate Julian Day number from a date, and vice versa */
main()
{
new d, m, y, jdn
print "Give a date (dd-mm-yyyy): "
d = getvalue(_, '-', '/')
m = getvalue(_, '-', '/')
y = getvalue()
jdn = DateToJulian(d, m, y)
printf("Date %d/%d/%d = %d JD\n", d, m, y, jdn)
print "Give a Julian Day Number: "
jdn = getvalue()
JulianToDate jdn, d, m, y
printf "%d JD = %d/%d/%d\n", jdn, d, m, y
}
DateToJulian(day, month, year)
{
/* The first year is 1. Year 0 does not exist: it is 1 BC (or -1) */
assert year != 0
if (year < 0)
year++
/* move January and February to the end of the previous year */
if (month <= 2)
year--, month += 12
new jdn = 365*year + year/4 - year/100 + year/400
+ (153*month - 457) / 5
+ day + 1721119
return jdn
}
JulianToDate(jdn, &day, &month, &year)
{
jdn -= 1721119
/* approximate year, then adjust in a loop */
year = (400 * jdn) / 146097
while (365*year + year/4 - year/100 + year/400 < jdn)
year++
year--
/* determine month */
jdn -= 365*year + year/4 - year/100 + year/400
month = (5*jdn + 457) / 153
/* determine day */
day = jdn - (153*month - 457) / 5
/* move January and February to start of the year */
if (month > 12)
month -= 12, year++
/* adjust negative years (year 0 must become 1 BC, or -1) */
if (year <= 0)
year--
}

62
EXAMPLES/ones.p Normal file
View File

@ -0,0 +1,62 @@
forward ones: operator+(ones: a, ones: b)
forward ones: operator-(ones: a, ones: b)
forward ones: operator-(ones: a)
main()
{
new ones: chksum = ones: 0xffffffff
print "Input values in hexadecimal, zero to exit\n"
new ones: value
do
{
print ">> "
value = ones: getvalue(.base=16)
chksum = chksum + value
printf "Checksum = %x\n", chksum
}
while (value)
}
stock ones: operator+(ones: a, ones: b)
{
const ones: mask = ones: 0xffff /* word mask */
const ones: shift = ones: 16 /* word shift */
/* add low words and high words separately */
new ones: r1 = (a & mask) + (b & mask)
new ones: r2 = (a >>> shift) + (b >>> shift)
new ones: carry
restart: /* code label (goto target) */
/* add carry of the new low word to the high word, then
* strip it from the low word
*/
carry = (r1 >>> shift)
r2 += carry
r1 &= mask
/* add the carry from the new high word back to the low
* word, then strip it from the high word
*/
carry = (r2 >>> shift)
r1 += carry
r2 &= mask
/* a carry from the high word injected back into the low
* word may cause the new low to overflow, so restart in
* that case
*/
if (carry)
goto restart
return (r2 << shift) | r1
}
stock ones: operator-(ones: a)
return (a == ones: 0xffffffff) ? a : ~a
stock ones: operator-(ones: a, ones: b)
return a + -b

78
EXAMPLES/queue.p Normal file
View File

@ -0,0 +1,78 @@
/* Priority queue (for simple text strings) */
enum message
{
text[40 char],
priority
}
main()
{
new msg[message]
/* insert a few items (read from console input) */
printf "Please insert a few messages and their priorities; \
end with an empty string\n"
for ( ;; )
{
printf "Message: "
getstring .string = msg[text], .maxlength = 40, .pack = true
if (strlen(msg[text]) == 0)
break
printf "Priority: "
msg[priority] = getvalue()
if (!insert(msg))
{
printf "Queue is full, cannot insert more items\n"
break
}
}
/* now print the messages extracted from the queue */
printf "\nContents of the queue:\n"
while (extract(msg))
printf "[%d] %s\n", msg[priority], msg[text]
}
const queuesize = 10
new queue[queuesize][message]
new queueitems = 0
insert(const item[message])
{
/* check if the queue can hold one more message */
if (queueitems == queuesize)
return false /* queue is full */
/* find the position to insert it to */
new pos = queueitems /* start at the bottom */
while (pos > 0 && item[priority] > queue[pos-1][priority])
--pos /* higher priority: move up a slot */
/* make place for the item at the insertion spot */
for (new i = queueitems; i > pos; --i)
queue[i] = queue[i-1]
/* add the message to the correct slot */
queue[pos] = item
queueitems++
return true
}
extract(item[message])
{
/* check whether the queue has one more message */
if (queueitems == 0)
return false /* queue is empty */
/* copy the topmost item */
item = queue[0]
--queueitems
/* move the queue one position up */
for (new i = 0; i < queueitems; ++i)
queue[i] = queue[i+1]
return true
}

1
EXAMPLES/quine.p Normal file
View File

@ -0,0 +1 @@
new s[]="new s[]=%c%s%c; main() printf s,34,s,34"; main() printf s,34,s,34

39
EXAMPLES/randlist.p Normal file
View File

@ -0,0 +1,39 @@
main()
{
new HandOfCards[10]
FillRandom(HandOfCards, 52)
print "A draw of 10 numbers from a range of 0 to 51 \
(inclusive) without duplicates:\n"
for (new i = 0; i < sizeof HandOfCards; i++)
printf "%d ", HandOfCards[i]
}
FillRandom(Series[], Range, Number = sizeof Series)
{
assert Range >= Number /* cannot select 50 values
* without duplicates in the
* range 0..40, for example */
new Index = 0
for (new Seq = Range - Number; Seq < Range; Seq++)
{
new Val = random(Seq + 1)
new Pos = InSeries(Series, Val, Index)
if (Pos >= 0)
{
Series[Index] = Series[Pos]
Series[Pos] = Seq
}
else
Series[Index] = Val
Index++
}
}
InSeries(Series[], Value, Top = sizeof Series)
{
for (new i = 0; i < Top; i++)
if (Series[i] == Value)
return i
return -1
}

25
EXAMPLES/readfile.p Normal file
View File

@ -0,0 +1,25 @@
#include <file>
main()
{
/* ask for a filename */
print "Please enter a filename: "
new filename[128 char]
getstring filename, .pack=true
/* try to open the file */
new File: file = fopen(filename, io_read)
if (!file)
{
printf "The file '%s' cannot be opened for reading\n", filename
exit
}
/* dump the file onto the console */
new line[200]
while (fread(file, line))
print line, .highlight=true
/* done */
fclose file
}

21
EXAMPLES/rot13.p Normal file
View File

@ -0,0 +1,21 @@
/* Simple encryption, using ROT13 */
main()
{
printf "Please type the string to mangle: "
new str[100]
getstring str, sizeof str
rot13 str
printf "After mangling, the string is: \"%s\"\n", str
}
rot13(string[])
{
for (new index = 0; string[index]; index++)
if ('a' <= string[index] <= 'z')
string[index] = (string[index] - 'a' + 13) % 26 + 'a'
else if ('A' <= string[index] <= 'Z')
string[index] = (string[index] - 'A' + 13) % 26 + 'A'
}

12
EXAMPLES/rpn.p Normal file
View File

@ -0,0 +1,12 @@
/* a simple RPN calculator */
#include strtok
#include stack
#include rpnparse
main()
{
print "Type an expression in Reverse Polish Notation: "
new string[100]
getstring string, sizeof string
rpncalc string
}

71
EXAMPLES/rpnparse.inc Normal file
View File

@ -0,0 +1,71 @@
/* main rpn parser and lexical analysis, part of the RPN calculator */
#include <rational>
#include <string>
enum token
{
t_type, /* operator or token type */
Rational: t_value, /* value, if t_type is "Number" */
t_word[20], /* raw string */
}
const Number = '0'
const EndOfExpr = '#'
rpncalc(const string[])
{
new index
new field[token]
for ( ;; )
{
field = gettoken(string, index)
switch (field[t_type])
{
case Number:
push field[t_value]
case '+':
push pop() + pop()
case '-':
push - pop() + pop()
case '*':
push pop() * pop()
case '/', ':':
push 1.0 / pop() * pop()
case EndOfExpr:
break /* exit "for" loop */
default:
printf "Unknown operator '%s'\n", field[t_word]
}
}
printf "Result = %r\n", pop()
if (clearstack())
print "Stack not empty\n", red
}
gettoken(const string[], &index)
{
/* first get the next "word" from the string */
new word[20]
word = strtok(string, index)
/* then parse it */
new field[token]
field[t_word] = word
if (strlen(word) == 0)
{
field[t_type] = EndOfExpr /* special "stop" symbol */
field[t_value] = 0
}
else if ('0' <= word[0] <= '9')
{
field[t_type] = Number
field[t_value] = rationalstr(word)
}
else
{
field[t_type] = word[0]
field[t_value] = 0
}
return field
}

46
EXAMPLES/set.p Normal file
View File

@ -0,0 +1,46 @@
/* Set operations, using bit arithmetic */
main()
{
enum (<<= 1) { A = 1, B, C, D, E, F, G }
new nextstep[] =
{ C | E, /* A can reach C and E */
D | E, /* B " " D and E */
G, /* C " " G */
C | F, /* D " " C and F */
0, /* E " " none */
0, /* F " " none */
E | F, /* G " " E and F */
}
#pragma unused A, B
print "The departure point: "
new start = clamp( .value = toupper(getchar()) - 'A',
.min = 0,
.max = sizeof nextstep - 1
)
print "\nThe number of steps: "
new steps = getvalue()
/* make the set */
new result = findtargets(start, steps, nextstep)
printf "The points in range of %c in %d steps: ", start + 'A', steps
for (new i = 0; i < sizeof nextstep; i++)
if (result & 1 << i)
printf "%c ", i + 'A'
}
findtargets(start, steps, nextstep[], numpoints = sizeof nextstep)
{
new result = 0
new addedpoints = nextstep[start]
while (steps-- > 0 && result != addedpoints)
{
result = addedpoints
for (new i = 0; i < numpoints; i++)
if (result & 1 << i)
addedpoints |= nextstep[i]
}
return result
}

16
EXAMPLES/sieve.p Normal file
View File

@ -0,0 +1,16 @@
/* Print all primes below 100, using the "Sieve of Eratosthenes" */
main()
{
const max_primes = 100
new series[max_primes] = { true, ... }
for (new i = 2; i < max_primes; ++i)
if (series[i])
{
printf "%d ", i
/* filter all multiples of this "prime" from the list */
for (new j = 2 * i; j < max_primes; j += i)
series[j] = false
}
}

26
EXAMPLES/stack.inc Normal file
View File

@ -0,0 +1,26 @@
/* stack functions, part of the RPN calculator */
#include <rational>
static Rational: stack[50]
static stackidx = 0
push(Rational: value)
{
assert stackidx < sizeof stack
stack[stackidx++] = value
}
Rational: pop()
{
assert stackidx > 0
return stack[--stackidx]
}
clearstack()
{
assert stackidx >= 0
if (stackidx == 0)
return false
stackidx = 0
return true
}

25
EXAMPLES/strtok.inc Normal file
View File

@ -0,0 +1,25 @@
/* extract words from a string (words are separated by white space) */
#include <string>
strtok(const string[], &index)
{
new length = strlen(string)
/* skip leading white space */
while (index < length && string[index] <= ' ')
index++
/* store the word letter for letter */
new offset = index /* save start position of token */
new result[20] /* string to store the word in */
while (index < length
&& string[index] > ' '
&& index - offset < sizeof result - 1)
{
result[index - offset] = string[index]
index++
}
result[index - offset] = EOS /* zero-terminate the string */
return result
}

35
EXAMPLES/traffic.p Normal file
View File

@ -0,0 +1,35 @@
/* traffic light synchronizer, using states in an event-driven model */
#include <time>
main() state green_wait
@keypressed(key) <green_wait> state yellow_wait
@keypressed(key) <red_walk, red_wait> state red_walk
@keypressed(key) <> {} /* fallback */
@timer() <yellow_wait> state red_walk
@timer() <red_walk> state red_wait
@timer() <red_wait> state green_wait
@timer() <> {} /* fallback */
entry() <green_wait>
print "Green / Don't walk\n"
entry() <yellow_wait>
{
print "Yellow / Don't walk\n"
settimer 2000
}
entry() <red_walk>
{
print "Red / Walk\n"
settimer 5000
}
entry() <red_wait>
{
print "Red / Don't walk\n"
settimer 2000
}

83
EXAMPLES/traffic2.p Normal file
View File

@ -0,0 +1,83 @@
/* a more realistic traffic light synchronizer, including an
* "override" for emergency vehicles
*/
#include <time>
main()
state green_wait_interim
new bool: button_memo <red_wait, green_wait_interim, yellow_wait>
@keypressed(key)
{
switch (key)
{
case ' ': button_press
case '*': mirt_detect
}
}
button_press() <green_wait>
state yellow_wait
button_press() <red_wait, green_wait_interim>
button_memo = true
button_press() <> /* fallback */
{}
mirt_detect()
state mirt_override
@timer() <yellow_wait>
state red_walk
@timer() <red_walk>
state red_wait
@timer() <red_wait>
state green_wait_interim
@timer() <green_wait_interim>
{
state (!button_memo) green_wait
state (button_memo) yellow_wait
}
@timer() <mirt_override>
state green_wait
@timer() <> /* fallback */
{}
entry() <green_wait_interim>
{
print "Green / Don't walk\n"
settimer 5000
}
entry() <yellow_wait>
{
print "Yellow / Don't walk\n"
button_memo = false
settimer 2000
}
entry() <red_walk>
{
print "Red / Walk\n"
settimer 5000
}
entry() <red_wait>
{
print "Red / Don't walk\n"
settimer 2000
}
entry() <mirt_override>
{
print "Green / Don't walk\n"
settimer 5000
}

26
EXAMPLES/turtle.p Normal file
View File

@ -0,0 +1,26 @@
@keypressed(key)
{
/* get current position */
new x, y
wherexy x, y
/* determine how the update the current position */
switch (key)
{
case 'u': y-- /* up */
case 'd': y++ /* down */
case 'l': x-- /* left */
case 'r': x++ /* right */
case '\e': exit /* Escape = exit */
}
/* adjust the cursor position and draw something */
moveturtle x, y
}
moveturtle(x, y)
{
gotoxy x, y
print '*'
gotoxy x, y
}

46
EXAMPLES/wcount.p Normal file
View File

@ -0,0 +1,46 @@
/* word count: count words on a string that the user types */
main()
{
print "Please type a string: "
new string[100]
getstring string, sizeof string
new count = 0
new word[20]
new index
for ( ;; )
{
word = strtok(string, index)
if (strlen(word) == 0)
break
count++
printf "Word %d: '%s'\n", count, word
}
printf "\nNumber of words: %d\n", count
}
strtok(const string[], &index)
{
new length = strlen(string)
/* skip leading white space */
while (index < length && string[index] <= ' ')
index++
/* store the word letter for letter */
new offset = index /* save start position of token */
new result[20] /* string to store the word in */
while (index < length
&& string[index] > ' '
&& index - offset < sizeof result - 1)
{
result[index - offset] = string[index]
index++
}
result[index - offset] = EOS /* zero-terminate the string */
return result
}

164
EXAMPLES/weekday.p Normal file
View File

@ -0,0 +1,164 @@
/**
* This program illustrates Zeller's congruence algorithm to calculate
* the day of the week given a date.
*/
/**
* <summary>
* The main program: asks the user to input a date and prints on
* what day of the week that date falls.
* </summary>
*/
main()
{
new day, month, year
if (readdate(day, month, year))
{
new wkday = weekday(day, month, year)
printf "The date %d-%d-%d falls on a ", day, month, year
switch (wkday)
{
case 0:
print "Saturday"
case 1:
print "Sunday"
case 2:
print "Monday"
case 3:
print "Tuesday"
case 4:
print "Wednesday"
case 5:
print "Thursday"
case 6:
print "Friday"
}
}
else
print "Invalid date"
print "\n"
}
/**
* <summary>
* The core function of Zeller's congruence algorithm. The function
* works for the Gregorian calender.
* </summary>
*
* <param name="day">
* The day in the month, a value between 1 and 31.
* </param>
* <param name="month">
* The month: a value between 1 and 12.
* </param>
* <param name="year">
* The year in four digits.
* </param>
*
* <returns>
* The day of the week, where 0 is Saturday and 6 is Friday.
* </returns>
*
* <remarks>
* This function does not check the validity of the date; when the
* date in the parameters is invalid, the returned "day of the week"
* will hold an incorrect value.
* <p/>
* This equation fails in many programming languages, notably most
* implementations of C, C++ and Pascal, because these languages have
* a loosely defined "remainder" operator. Pawn, on the other hand,
* provides the true modulus operator, as defined in mathematical
* theory and as was intended by Zeller.
* </remarks>
*/
weekday(day, month, year)
{
/**
* <remarks>
* For Zeller's congruence algorithm, the months January and
* February are the 13th and 14th month of the <em>preceding</em>
* year. The idea is that the "difficult month" February (which
* has either 28 or 29 days) is moved to the end of the year.
* </remarks>
*/
if (month <= 2)
month += 12, --year
new j = year % 100
new e = year / 100
return (day + (month+1)*26/10 + j + j/4 + e/4 - 2*e) % 7
}
/**
* <summary>
* Reads a date and stores it in three separate fields. tata
* </summary>
*
* <param name="day">
* Will hold the day number upon return.
* </param>
* <param name="month">
* Will hold the month number upon return.
* </param>
* <param name="year">
* Will hold the year number upon return.
* </param>
*
* <returns>
* <em>true</em> if the date is valid, <em>false</em> otherwise;
* if the function returns <em>false</em>, the values of
* <paramref name="day"/>, <paramref name="month"/> and
* <paramref name="year"/> cannot be relied upon.
* </returns>
*/
bool: readdate(&day, &month, &year)
{
print "Give a date (dd-mm-yyyy): "
day = getvalue(_,'-','/')
month = getvalue(_,'-','/')
year = getvalue()
return 1 <= month <= 12 && 1 <= day <= daysinmonth(month,year)
}
/**
* <summary>
* Returns whether a year is a leap year.
* </summary>
*
* <param name="year">
* The year in 4 digits.
* </param>
*
* <remarks>
* A year is a leap year:
* <ul>
* <li> if it is divisable by 4, </li>
* <li> but <strong>not</strong> if it is divisable by 100, </li>
* <li> but it <strong>is</strong> it is divisable by 400. </li>
* </ul>
* </remarks>
*/
bool: isleapyear(year)
return year % 400 == 0 || year % 100 != 0 && year % 4 == 0
/**
* <summary>
* Returns the number of days in a month (the month is an integer
* in the range 1 .. 12). One needs to pass in the year as well,
* because the function takes leap years into account.
* </summary>
*
* <param name="month">
* The month number, a value between 1 and 12.
* </param>
* <param name="year">
* The year in 4 digits.
* </param>
*/
daysinmonth(month, year)
{
static daylist[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
assert 1 <= month <= 12
return daylist[month-1] + _:(month == 2 && isleapyear(year))
}

78
INCLUDE/amxdll.inc Normal file
View File

@ -0,0 +1,78 @@
/* An assortment of additional functions in the DLL version of the
* Abstract Machine.
*
* (c) Copyright 2000-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _amxdll_included
#endinput
#endif
#define _amxdll_included
enum
{
Ok,
Okcancel,
okCancel,
Yesno,
yesNo,
Yesnocancel,
yesNocancel,
yesnoCancel,
}
enum
{
noicon,
information,
exclamation,
question,
stop
}
native messagebox(const message[], const caption[], buttons=Ok, icons=noicon, timeout=0);
/* Types:
* i = integer (16/32 bit)
* l = 32-bit integer
* h = handle (16/32 bit)
* p = packed string
* s = (unpacked) string
* w = 16-bit unsigned integer
* lower case = by value, upper case = by reference
*
* The types noted as "16/32 bit" are converted to 16-bit in Win16 environments
* prior to the call to the DLL.
*
* calldll() attaches "32" to the dll name if it cannot load the DLL as you
* provide it. So it loads "user32" if you specify "user" and you are in Win32.
* calldll() also appends a "A" to the function name if it cannot find the
* function as specified. So it call "GetWindowTextA" if you ask for
* "GetWindowText". By this means, the interface is more compatible between
* 16-bit and 32-bit platforms.
*/
native calldll(const dllname[], const function[], const typestr[]="", ...);
native loaddll(const dllname[]);
native freedll(const dllname[]);
native iswin32();
native balloon(&Balloon:handle, text[]="", x=0, y=0, timeout=-1);
native balloonfont(&Balloon:handle, const font[]="", height=16, weight=400, italic=0);
stock wGetCursorPos(&x, &y)
/* Get the current position of the mouse cursor relative to the upper
* left corner of the screen
*/
{
new point[2]
calldll(!"user","GetCursorPos","I",point)
if (!iswin32())
{
point[1] = point[0] >> 16
point[0] &= 0xffff
}
x = point[0]
y = point[1]
}

15
INCLUDE/args.inc Normal file
View File

@ -0,0 +1,15 @@
/* Script Arguments support functions
*
* (c) Copyright 2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _args_included
#endinput
#endif
#define _args_included
#pragma library Args
native argcount();
native bool: argindex(index, value[], maxlength = sizeof value, bool: pack = false);
native bool: argstr(index = 0, const option[] = "", value[] = "", maxlength = sizeof value, bool: pack = false);
native bool: argvalue(index = 0, const option[] = "", &value = cellmin);

37
INCLUDE/console.inc Normal file
View File

@ -0,0 +1,37 @@
/* Console input/output functions
*
* (c) Copyright 1998-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _console_included
#endinput
#endif
#define _console_included
#pragma library Console
enum
{
black, /* colours as per the ANSI Escape sequences, ISO 6429 */
red,
green,
yellow,
blue,
magenta,
cyan,
white,
}
native getchar(echo=true);
native getstring(string[], maxlength=sizeof string, bool:pack=false);
native getvalue(base=10, term=0x0d, ...); /* 0x0d == '\r' */
native print(const string[], foreground=-1, background=-1, highlight=-1);
native printf(const format[], {Float,Fixed,_}:...);
native console(columns, lines);
native clrscr(); /* also resets the cursor to (1,1) */
native clreol();
native gotoxy(x=1, y=1);
native wherexy(&x, &y);
native setattr(foreground=-1, background=-1, highlight=-1);
forward @keypressed(key);

34
INCLUDE/core.inc Normal file
View File

@ -0,0 +1,34 @@
/* Core functions
*
* (c) Copyright 1998-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _core_included
#endinput
#endif
#define _core_included
#pragma library Core
native heapspace();
native funcidx(const name[]);
native numargs();
native getarg(arg, index=0);
native setarg(arg, index=0, value);
native tolower(c);
native toupper(c);
native swapchars(c);
native random(max);
native min(value1, value2);
native max(value1, value2);
native clamp(value, min=cellmin, max=cellmax);
native getproperty(id=0, const name[]="", value=cellmin, string[]="");
native setproperty(id=0, const name[]="", value=cellmin, const string[]="");
native deleteproperty(id=0, const name[]="", value=cellmin);
native existproperty(id=0, const name[]="", value=cellmin);

18
INCLUDE/datagram.inc Normal file
View File

@ -0,0 +1,18 @@
/* Datagram sending/receiving
*
* (c) Copyright 2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _datagram_included
#endinput
#endif
#define _datagram_included
#pragma library DGram
native sendstring(const message[], const destination[]="");
native sendpacket(const packet[], size, const destination[]="");
native listenport(port);
forward @receivestring(const message[], const source[]);
forward @receivepacket(const packet[], size, const source[]);

2
INCLUDE/default.inc Normal file
View File

@ -0,0 +1,2 @@
#include <core>
#include <console>

44
INCLUDE/file.inc Normal file
View File

@ -0,0 +1,44 @@
/* File input/output functions
*
* (c) Copyright 2004-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _file_included
#endinput
#endif
#define _file_included
#pragma library File
enum filemode
{
io_read, /* file must exist */
io_write, /* creates a new file */
io_readwrite, /* opens an existing file, or creates a new file */
io_append, /* appends to file (write-only) */
}
enum seek_whence
{
seek_start,
seek_current,
seek_end,
}
const EOF = -1;
native File: fopen(const name[], filemode: mode = io_readwrite);
native bool: fclose(File: handle);
native File: ftemp();
native bool: fremove(const name[]);
native fwrite(File: handle, const string[]);
native fread(File: handle, string[], size = sizeof string, bool: pack = false);
native bool: fputchar(File: handle, value, bool: utf8 = true);
native fgetchar(File: handle, bool: utf8 = true);
native fblockwrite(File: handle, const buffer[], size = sizeof buffer);
native fblockread(File: handle, buffer[], size = sizeof buffer);
native fseek(File: handle, position = 0, seek_whence: whence = seek_start);
native flength(File: handle);
native fexist(const pattern[]);
native bool: fmatch(name[], const pattern[], index = 0, size = sizeof name);

95
INCLUDE/fixed.inc Normal file
View File

@ -0,0 +1,95 @@
/* Fixed point arithmetic
*
* (c) Copyright 1998-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _Fixed_included
#endinput
#endif
#define _Fixed_included
#pragma library Fixed
enum fround_method {
fround_round,
fround_floor,
fround_ceil,
fround_tozero,
fround_unbiased
}
native Fixed:fixed(value);
native Fixed:strfixed(const string[]);
native Fixed:fmul(Fixed:oper1, Fixed:oper2);
native Fixed:fdiv(Fixed:dividend, Fixed:divisor);
native Fixed:ffract(Fixed:value);
native fround(Fixed:value, fround_method:method=fround_round);
native Fixed:fpower(Fixed:value, exponent);
native Fixed:fsqroot(Fixed:value);
native Fixed:fabs(Fixed:value);
#pragma rational Fixed(3)
/* user defined operators */
native Fixed:operator*(Fixed:oper1, Fixed:oper2) = fmul;
native Fixed:operator/(Fixed:oper1, Fixed:oper2) = fdiv;
native Fixed:operator=(oper) = fixed;
stock Fixed:operator++(Fixed:oper)
return oper + fixed(1);
stock Fixed:operator--(Fixed:oper)
return oper - fixed(1);
stock Fixed:operator*(Fixed:oper1, oper2)
return Fixed: (_:oper1 * oper2); /* "*" is commutative */
stock Fixed:operator/(Fixed:oper1, oper2)
return oper1 / fixed(oper2);
stock Fixed:operator/(oper1, Fixed:oper2)
return fdiv(fixed(oper1), oper2);
stock Fixed:operator+(Fixed:oper1, oper2)
return oper1 + fixed(oper2); /* "+" is commutative */
stock Fixed:operator-(Fixed:oper1, oper2)
return oper1 - fixed(oper2);
stock Fixed:operator-(oper1, Fixed:oper2)
return fixed(oper1) - oper2;
stock bool:operator>(Fixed:oper1, oper2)
return oper1 > fixed(oper2);
stock bool:operator>(oper1, Fixed:oper2)
return fixed(oper1) > oper2;
stock bool:operator>=(Fixed:oper1, oper2)
return oper1 >= fixed(oper2);
stock bool:operator>=(oper1, Fixed:oper2)
return fixed(oper1) >= oper2;
stock bool:operator<(Fixed:oper1, oper2)
return oper1 < fixed(oper2);
stock bool:operator<(oper1, Fixed:oper2)
return fixed(oper1) < oper2;
stock bool:operator<=(Fixed:oper1, oper2)
return oper1 <= fixed(oper2);
stock bool:operator<=(oper1, Fixed:oper2)
return fixed(oper1) <= oper2;
stock bool:operator==(Fixed:oper1, oper2) /* "==" is commutative */
return oper1 == fixed(oper2);
stock bool:operator!=(Fixed:oper1, oper2) /* "!=" is commutative */
return oper1 != fixed(oper2);
/* forbidden operations */
forward operator%(Fixed:oper1, Fixed:oper2);
forward operator%(Fixed:oper1, oper2);
forward operator%(oper1, Fixed:oper2);

184
INCLUDE/float.inc Normal file
View File

@ -0,0 +1,184 @@
/* Float arithmetic
*
* (c) Copyright 1999, Artran, Inc.
* Written by Greg Garner (gmg@artran.com)
* Modified in March 2001 to include user defined
* operators for the floating point functions.
*
* This file is provided as is (no warranties).
*/
#if defined _Float_included
#endinput
#endif
#define _Float_included
#pragma library Float
/* Different methods of rounding */
enum floatround_method {
floatround_round,
floatround_floor,
floatround_ceil,
floatround_tozero,
floatround_unbiased
}
enum anglemode {
radian,
degrees,
grades
}
/**************************************************/
/* Convert an integer into a floating point value */
native Float:float(value);
/**************************************************/
/* Convert a string into a floating point value */
native Float:strfloat(const string[]);
/**************************************************/
/* Multiple two floats together */
native Float:floatmul(Float:oper1, Float:oper2);
/**************************************************/
/* Divide the dividend float by the divisor float */
native Float:floatdiv(Float:dividend, Float:divisor);
/**************************************************/
/* Add two floats together */
native Float:floatadd(Float:oper1, Float:oper2);
/**************************************************/
/* Subtract oper2 float from oper1 float */
native Float:floatsub(Float:oper1, Float:oper2);
/**************************************************/
/* Return the fractional part of a float */
native Float:floatfract(Float:value);
/**************************************************/
/* Round a float into a integer value */
native floatround(Float:value, floatround_method:method=floatround_round);
/**************************************************/
/* Compare two integers. If the two elements are equal, return 0.
If the first argument is greater than the second argument, return 1,
If the first argument is less than the second argument, return -1. */
native floatcmp(Float:oper1, Float:oper2);
/**************************************************/
/* Return the square root of the input value, same as floatpower(value, 0.5) */
native Float:floatsqroot(Float:value);
/**************************************************/
/* Return the value raised to the power of the exponent */
native Float:floatpower(Float:value, Float:exponent);
/**************************************************/
/* Return the logarithm */
native Float:floatlog(Float:value, Float:base=10.0);
/**************************************************/
/* Return the sine, cosine or tangent. The input angle may be in radian,
degrees or grades. */
native Float:floatsin(Float:value, anglemode:mode=radian);
native Float:floatcos(Float:value, anglemode:mode=radian);
native Float:floattan(Float:value, anglemode:mode=radian);
/**************************************************/
/* Return the absolute value */
native Float:floatabs(Float:value);
/**************************************************/
#pragma rational Float
/* user defined operators */
native Float:operator*(Float:oper1, Float:oper2) = floatmul;
native Float:operator/(Float:oper1, Float:oper2) = floatdiv;
native Float:operator+(Float:oper1, Float:oper2) = floatadd;
native Float:operator-(Float:oper1, Float:oper2) = floatsub;
native Float:operator=(oper) = float;
stock Float:operator++(Float:oper)
return oper+1.0;
stock Float:operator--(Float:oper)
return oper-1.0;
stock Float:operator-(Float:oper)
return oper^Float:cellmin; /* IEEE values are sign/magnitude */
stock Float:operator*(Float:oper1, oper2)
return floatmul(oper1, float(oper2)); /* "*" is commutative */
stock Float:operator/(Float:oper1, oper2)
return floatdiv(oper1, float(oper2));
stock Float:operator/(oper1, Float:oper2)
return floatdiv(float(oper1), oper2);
stock Float:operator+(Float:oper1, oper2)
return floatadd(oper1, float(oper2)); /* "+" is commutative */
stock Float:operator-(Float:oper1, oper2)
return floatsub(oper1, float(oper2));
stock Float:operator-(oper1, Float:oper2)
return floatsub(float(oper1), oper2);
stock bool:operator==(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) == 0;
stock bool:operator==(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) == 0; /* "==" is commutative */
stock bool:operator!=(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) != 0;
stock bool:operator!=(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) != 0; /* "!=" is commutative */
stock bool:operator>(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) > 0;
stock bool:operator>(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) > 0;
stock bool:operator>(oper1, Float:oper2)
return floatcmp(float(oper1), oper2) > 0;
stock bool:operator>=(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) >= 0;
stock bool:operator>=(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) >= 0;
stock bool:operator>=(oper1, Float:oper2)
return floatcmp(float(oper1), oper2) >= 0;
stock bool:operator<(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) < 0;
stock bool:operator<(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) < 0;
stock bool:operator<(oper1, Float:oper2)
return floatcmp(float(oper1), oper2) < 0;
stock bool:operator<=(Float:oper1, Float:oper2)
return floatcmp(oper1, oper2) <= 0;
stock bool:operator<=(Float:oper1, oper2)
return floatcmp(oper1, float(oper2)) <= 0;
stock bool:operator<=(oper1, Float:oper2)
return floatcmp(float(oper1), oper2) <= 0;
stock bool:operator!(Float:oper)
return (_:oper & cellmax) == 0;
/* forbidden operations */
forward operator%(Float:oper1, Float:oper2);
forward operator%(Float:oper1, oper2);
forward operator%(oper1, Float:oper2);

19
INCLUDE/process.inc Normal file
View File

@ -0,0 +1,19 @@
/* Process control and Foreign Function Interface (calling functions
* in DLLs or shared libraries)
*
* (c) Copyright 2006, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _process_included
#endinput
#endif
#define _process_included
#pragma library Process
native libcall(const libname[], const funcname[], const typestring[], ...);
native bool: libfree(const libname[]="");
native PID: procexec(const progname[]);
native procwait(PID:pid);
native bool: procwrite(const line[], bool:appendlf=false);
native bool: procread(line[], size=sizeof line, bool:striplf=false, bool:packed=false);

53
INCLUDE/rational.inc Normal file
View File

@ -0,0 +1,53 @@
/* Rational number support
* Loads either Float.inc or Fixed.inc, depending on the configuration
*
* (c) Copyright 2004-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _Rational_included
#endinput
#endif
#define _Rational_included
/* first try to include floating point support */
#if !defined NOFLOAT
#tryinclude <float>
#endif
#if defined _Float_included
#define Rational: Float:
#define rationalstr(%1) floatstr(%1)
#define rround(%1, %2) floatround(%1, %2)
#define rsqroot(%1) floatsqroot(%1)
#define rpower(%1, %2) floatpower(%1, %2)
#define rabs(%1) floatabs(%1)
#define rround_round floatround_round
#define rround_floor floatround_floor
#define rround_ceil floatround_ceil
#define rround_tozero floatround_tozero
#define rround_unbiased floatround_unbiased
#endinput
#endif
/* loading floating point support failed, try fixed point support */
#if !defined NOFIXED
#tryinclude <fixed>
#endif
#if defined _Fixed_included
#define Rational: Fixed:
#define rationalstr(%1) fixedstr(%1)
#define rround(%1, %2) fround(%1, %2)
#define rsqroot(%1) fsqroot(%1)
#define rpower(%1, %2) fpower(%1, %2)
#define rabs(%1) fabs(%1)
#define rround_round fround_round
#define rround_floor fround_floor
#define rround_ceil fround_ceil
#define rround_tozero fround_tozero
#define rround_unbiased fround_unbiased
#endinput
#endif
/* if arrived here, neither floating point, nor fixed point support is available */
#error Rational number support is unavailable (disabled or not installed)

35
INCLUDE/string.inc Normal file
View File

@ -0,0 +1,35 @@
/* String functions
*
* (c) Copyright 2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _string_included
#endinput
#endif
#define _string_included
#pragma library String
native strlen(const string[]);
native strpack(dest[], const source[], maxlength=sizeof dest);
native strunpack(dest[], const source[], maxlength=sizeof dest);
native strcat(dest[], const source[], maxlength=sizeof dest);
native strmid(dest[], const source[], start=0, end=cellmax, maxlength=sizeof dest);
native bool: strins(string[], const substr[], index, maxlength=sizeof string);
native bool: strdel(string[], start, end);
native strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax);
native strfind(const string[], const sub[], bool:ignorecase=false, index=0);
native strval(const string[], index=0);
native valstr(dest[], value, bool:pack=false);
native bool: ispacked(const string[]);
native strformat(dest[], size=sizeof dest, bool:pack=false, const format[], {Fixed,Float,_}:...);
native uudecode(dest[], const source[], maxlength=sizeof dest);
native uuencode(dest[], const source[], numbytes, maxlength=sizeof dest);
native memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest);
stock bool: strequal(const string1[], const string2[], bool:ignorecase=false, length=cellmax)
return strcmp(string1, string2, ignorecase, length) == 0

22
INCLUDE/time.inc Normal file
View File

@ -0,0 +1,22 @@
/* Date/time functions
*
* (c) Copyright 2001-2005, ITB CompuPhase
* This file is provided as is (no warranties).
*/
#if defined _time_included
#endinput
#endif
#define _time_included
#pragma library Time
native gettime(&hour=0, &minute=0, &second=0);
native settime(hour=cellmin, minute=cellmin, second=cellmin);
native getdate(&year=0, &month=0, &day=0);
native setdate(year=cellmin, month=cellmin, day=cellmin);
native settimestamp(seconds1970);
native settimer(milliseconds, bool: singleshot=false);
native tickcount(&granularity=0);
native delay(milliseconds);
forward @timer();

206
SOURCE/amx/CMakeLists.txt Normal file
View File

@ -0,0 +1,206 @@
#build file for CMake, see http://www.cmake.org/
PROJECT(pawnamx)
# check for optional include files
INCLUDE(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
CHECK_INCLUDE_FILE("unistd.h" HAVE_UNISTD_H)
IF(HAVE_UNISTD_H)
ADD_DEFINITIONS(-DHAVE_UNISTD_H)
ENDIF(HAVE_UNISTD_H)
CHECK_INCLUDE_FILE("inttypes.h" HAVE_INTTYPES_H)
IF(HAVE_INTTYPES_H)
ADD_DEFINITIONS(-DHAVE_INTTYPES_H)
ENDIF(HAVE_INTTYPES_H)
CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H)
IF(HAVE_STDINT_H)
ADD_DEFINITIONS(-DHAVE_STDINT_H)
ENDIF(HAVE_STDINT_H)
CHECK_INCLUDE_FILE("alloca.h" HAVE_ALLOCA_H)
IF(HAVE_ALLOCA_H)
ADD_DEFINITIONS(-DHAVE_ALLOCA_H)
ENDIF(HAVE_ALLOCA_H)
ADD_DEFINITIONS(-DFLOATPOINT -DFIXEDPOINT)
IF (UNIX)
ADD_DEFINITIONS(-DLINUX)
CHECK_INCLUDE_FILE("ffi.h" HAVE_FFI_H)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../linux)
ENDIF (UNIX)
IF(WIN32)
ADD_DEFINITIONS(-DAMXEXPORT=__stdcall -DAMX_NATIVE_CALL=__stdcall -DSTDECL)
IF(NOT BORLAND)
LINK_LIBRARIES(winmm)
ENDIF(NOT BORLAND)
ENDIF(WIN32)
# --------------------------------------------------------------------------
# Extension modules
# amxArgs
SET(ARGS_SRCS amxargs.c amx.c)
ADD_LIBRARY(amxArgs SHARED ${ARGS_SRCS})
SET_TARGET_PROPERTIES(amxArgs PROPERTIES PREFIX "")
IF(WIN32)
SET(ARGS_SRCS ${ARGS_SRCS} dllmain.c amxargs.rc)
IF(BORLAND)
# Borland linker uses a DEF file if one is in the output directory
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxargs.def ${CMAKE_BINARY_DIR}/amxargs.def COPY_ONLY)
ELSE(BORLAND)
# For Microsoft Visual C/C++ we can set explicit flags for exports
SET_TARGET_PROPERTIES(amxArgs PROPERTIES LINK_FLAGS "/export:amx_ArgsInit /export:amx_ArgsCleanup /export:amx_ArgsSetCmdLine")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
ADD_CUSTOM_COMMAND(TARGET amxArgs POST_BUILD COMMAND strip ARGS -K amx_ArgsInit -K amx_ArgsCleanup -K amx_ArgsSetCmdLine ${CMAKE_BINARY_DIR}/amxArgs.so)
ENDIF(UNIX)
# amxDGram
SET(DGRAM_SRCS amxdgram.c amx.c)
ADD_LIBRARY(amxDGram SHARED ${DGRAM_SRCS})
SET_TARGET_PROPERTIES(amxDGram PROPERTIES PREFIX "")
IF(WIN32)
SET(DGRAM_SRCS ${DGRAM_SRCS} dllmain.c amxargs.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxdgram.def ${CMAKE_BINARY_DIR}/amxdgram.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxDGram PROPERTIES LINK_FLAGS "/export:amx_DGramInit /export:amx_DGramCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
ADD_CUSTOM_COMMAND(TARGET amxDGram POST_BUILD COMMAND strip ARGS -K amx_DGramInit -K amx_DGramCleanup ${CMAKE_BINARY_DIR}/amxDGram.so)
ENDIF(UNIX)
# amxFile
SET(FILE_SRCS amxfile.c amx.c)
ADD_LIBRARY(amxFile SHARED ${FILE_SRCS})
SET_TARGET_PROPERTIES(amxFile PROPERTIES PREFIX "")
IF(WIN32)
SET(FILE_SRCS ${FILE_SRCS} dllmain.c amxfile.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfile.def ${CMAKE_BINARY_DIR}/amxfile.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxFile PROPERTIES LINK_FLAGS "/export:amx_FileInit /export:amx_FileCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
ADD_CUSTOM_COMMAND(TARGET amxFile POST_BUILD COMMAND strip ARGS -K amx_FileInit -K amx_FileCleanup ${CMAKE_BINARY_DIR}/amxFile.so)
ENDIF(UNIX)
# amxFixed
SET(FIXED_SRCS fixed.c amx.c)
ADD_LIBRARY(amxFixed SHARED ${FIXED_SRCS})
SET_TARGET_PROPERTIES(amxFixed PROPERTIES PREFIX "")
IF(WIN32)
SET(FIXED_SRCS ${FIXED_SRCS} dllmain.c amxfixed.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfixed.def ${CMAKE_BINARY_DIR}/amxfixed.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxFixed PROPERTIES LINK_FLAGS "/export:amx_FixedInit /export:amx_FixedCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
TARGET_LINK_LIBRARIES(amxFixed m)
ADD_CUSTOM_COMMAND(TARGET amxFixed POST_BUILD COMMAND strip ARGS -K amx_FixedInit -K amx_FixedCleanup ${CMAKE_BINARY_DIR}/amxFixed.so)
ENDIF(UNIX)
# amxFloat
SET(FLOAT_SRCS float.c amx.c)
ADD_LIBRARY(amxFloat SHARED ${FLOAT_SRCS})
SET_TARGET_PROPERTIES(amxFloat PROPERTIES PREFIX "")
IF(WIN32)
SET(FLOAT_SRCS ${FLOAT_SRCS} dllmain.c amxfloat.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxfloat.def ${CMAKE_BINARY_DIR}/amxfloat.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxFloat PROPERTIES LINK_FLAGS "/export:amx_FloatInit /export:amx_FloatCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
TARGET_LINK_LIBRARIES(amxFloat m)
ADD_CUSTOM_COMMAND(TARGET amxFloat POST_BUILD COMMAND strip ARGS -K amx_FloatInit -K amx_FloatInit ${CMAKE_BINARY_DIR}/amxFloat.so)
ENDIF(UNIX)
# amxProcess
SET(PROCESS_SRCS amxprocess.c amx.c)
IF(WIN32)
ADD_LIBRARY(amxProcess SHARED ${PROCESS_SRCS})
ELSE(WIN32)
IF(HAVE_FFI_H)
ADD_LIBRARY(amxProcess SHARED ${PROCESS_SRCS})
ELSE(HAVE_FFI_H)
MESSAGE(SEND_ERROR "amxProcess requires libffi; see http://sources.redhat.com/libffi/")
MESSAGE(SEND_ERROR "libffi is not available (foreign function interface)")
ENDIF(HAVE_FFI_H)
ENDIF(WIN32)
SET_TARGET_PROPERTIES(amxProcess PROPERTIES PREFIX "")
IF(WIN32)
SET(PROCESS_SRCS ${PROCESS_SRCS} dllmain.c amxprocess.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxprocess.def ${CMAKE_BINARY_DIR}/amxprocess.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxProcess PROPERTIES LINK_FLAGS "/export:amx_ProcessInit /export:amx_ProcessCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
TARGET_LINK_LIBRARIES(amxProcess dl)
ADD_CUSTOM_COMMAND(TARGET amxProcess POST_BUILD COMMAND strip ARGS -K amx_ProcessInit -K amx_ProcessCleanup ${CMAKE_BINARY_DIR}/amxProcess.so)
ENDIF(UNIX)
# amxString
SET(STRING_SRCS amxstring.c amx.c amxcons.c)
ADD_LIBRARY(amxString SHARED ${STRING_SRCS})
SET_TARGET_PROPERTIES(amxString PROPERTIES PREFIX "")
IF(WIN32)
SET(STRING_SRCS ${STRING_SRCS} dllmain.c amxstring.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxstring.def ${CMAKE_BINARY_DIR}/amxstring.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxString PROPERTIES LINK_FLAGS "/export:amx_StringInit /export:amx_StringCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
ADD_CUSTOM_COMMAND(TARGET amxString POST_BUILD COMMAND strip ARGS -K amx_StringInit -K amx_StringCleanup ${CMAKE_BINARY_DIR}/amxString.so)
ENDIF(UNIX)
# amxTime
SET(TIME_SRCS amxtime.c amx.c)
ADD_LIBRARY(amxTime SHARED ${TIME_SRCS})
SET_TARGET_PROPERTIES(amxTime PROPERTIES PREFIX "")
IF(WIN32)
SET(TIME_SRCS ${TIME_SRCS} dllmain.c amxtime.rc)
IF(BORLAND)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/amxtime.def ${CMAKE_BINARY_DIR}/amxtime.def COPY_ONLY)
ELSE(BORLAND)
SET_TARGET_PROPERTIES(amxTime PROPERTIES LINK_FLAGS "/export:amx_TimeInit /export:amx_TimeCleanup")
ENDIF(BORLAND)
ENDIF(WIN32)
IF(UNIX)
ADD_CUSTOM_COMMAND(TARGET amxTime POST_BUILD COMMAND strip ARGS -K amx_TimeInit -K amx_TimeCleanup ${CMAKE_BINARY_DIR}/amxTime.so)
ENDIF(UNIX)
# --------------------------------------------------------------------------
# Simple run-time (example program)
SET(PAWNRUN_SRCS pawnrun.c amx.c amxcore.c amxcons.c amxdbg.c)
IF (UNIX)
SET(PAWNRUN_SRCS ${PAWNRUN_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c)
ENDIF (UNIX)
ADD_EXECUTABLE(pawnrun ${PAWNRUN_SRCS})
SET_TARGET_PROPERTIES(pawnrun PROPERTIES COMPILE_FLAGS -DAMXDBG COMPILE_FLAGS -DENABLE_BINRELOC)
IF (UNIX)
TARGET_LINK_LIBRARIES(pawnrun dl)
ENDIF (UNIX)
# --------------------------------------------------------------------------
# Simple console debugger
SET(PAWNDBG_SRCS pawndbg.c amx.c amxcore.c amxcons.c amxdbg.c)
IF (UNIX)
SET(PAWNDBG_SRCS ${PAWNDBG_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../linux/getch.c ${CMAKE_CURRENT_SOURCE_DIR}/../linux/binreloc.c)
ENDIF (UNIX)
ADD_EXECUTABLE(pawndbg ${PAWNDBG_SRCS})
SET_TARGET_PROPERTIES(pawndbg PROPERTIES COMPILE_FLAGS -DENABLE_BINRELOC)
IF (UNIX)
TARGET_LINK_LIBRARIES(pawndbg dl)
ENDIF (UNIX)

4634
SOURCE/amx/amx.c Normal file

File diff suppressed because it is too large Load Diff

468
SOURCE/amx/amx.h Normal file
View File

@ -0,0 +1,468 @@
/* Pawn Abstract Machine (for the Pawn language)
*
* Copyright (c) ITB CompuPhase, 1997-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amx.h 3648 2006-10-12 11:24:50Z thiadmer $
*/
#ifndef AMX_H_INCLUDED
#define AMX_H_INCLUDED
#include <stdlib.h> /* for size_t */
#include <limits.h>
#if defined FREEBSD && !defined __FreeBSD__
#define __FreeBSD__
#endif
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
#include <sclinux.h>
#endif
#if defined HAVE_STDINT_H
#include <stdint.h>
#else
#if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200)
#if defined HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <stdint.h>
#endif
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
/* The ISO C99 defines the int16_t and int_32t types. If the compiler got
* here, these types are probably undefined.
*/
#if defined __MACH__
#include <ppc/types.h>
typedef unsigned short int uint16_t;
typedef unsigned long int uint32_t;
#elif defined __FreeBSD__
#include <inttypes.h>
#else
typedef short int int16_t;
typedef unsigned short int uint16_t;
#if defined SN_TARGET_PS2
typedef int int32_t;
typedef unsigned int uint32_t;
#else
typedef long int int32_t;
typedef unsigned long int uint32_t;
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define HAVE_I64
#elif defined __GNUC__
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define HAVE_I64
#endif
#endif
#endif
#define HAVE_STDINT_H
#endif
#if defined _LP64 || defined WIN64 || defined _WIN64
#if !defined __64BIT__
#define __64BIT__
#endif
#endif
#if HAVE_ALLOCA_H
#include <alloca.h>
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */
#if !defined alloca
#define alloca(n) _alloca(n)
#endif
#endif
#if !defined arraysize
#define arraysize(array) (sizeof(array) / sizeof((array)[0]))
#endif
#if !defined assert_static
/* see "Compile-Time Assertions" by Ralf Holly,
* C/C++ Users Journal, November 2004
*/
#define assert_static(e) \
do { \
enum { assert_static__ = 1/(e) }; \
} while (0)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined PAWN_DLL
#if !defined AMX_NATIVE_CALL
#define AMX_NATIVE_CALL __stdcall
#endif
#if !defined AMXAPI
#define AMXAPI __stdcall
#endif
#endif
/* calling convention for native functions */
#if !defined AMX_NATIVE_CALL
#define AMX_NATIVE_CALL
#endif
/* calling convention for all interface functions and callback functions */
#if !defined AMXAPI
#if defined STDECL
#define AMXAPI __stdcall
#elif defined CDECL
#define AMXAPI __cdecl
#elif defined GCC_HASCLASSVISIBILITY
#define AMXAPI __attribute__ ((visibility("default")))
#else
#define AMXAPI
#endif
#endif
#if !defined AMXEXPORT
#define AMXEXPORT
#endif
/* File format version (in CUR_FILE_VERSION)
* 0 (original version)
* 1 (opcodes JUMP.pri, SWITCH and CASETBL)
* 2 (compressed files)
* 3 (public variables)
* 4 (opcodes SWAP.pri/alt and PUSHADDR)
* 5 (tagnames table)
* 6 (reformatted header)
* 7 (name table, opcodes SYMTAG & SYSREQ.D)
* 8 (opcode STMT, renewed debug interface)
* 9 (macro opcodes)
* MIN_FILE_VERSION is the lowest file version number that the current AMX
* implementation supports. If the AMX file header gets new fields, this number
* often needs to be incremented. MAX_AMX_VERSION is the lowest AMX version that
* is needed to support the current file version. When there are new opcodes,
* this number needs to be incremented.
* The file version supported by the JIT may run behind MIN_AMX_VERSION. So
* there is an extra constant for it: MAX_FILE_VER_JIT.
*/
#define CUR_FILE_VERSION 9 /* current file version; also the current AMX version */
#define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */
#define MIN_AMX_VERSION 9 /* minimum AMX version needed to support the current file format */
#define MAX_FILE_VER_JIT 8 /* file version supported by the JIT */
#define MIN_AMX_VER_JIT 8 /* AMX version supported by the JIT */
#if !defined PAWN_CELL_SIZE
#define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */
#endif
#if PAWN_CELL_SIZE==16
typedef uint16_t ucell;
typedef int16_t cell;
#elif PAWN_CELL_SIZE==32
typedef uint32_t ucell;
typedef int32_t cell;
#elif PAWN_CELL_SIZE==64
typedef uint64_t ucell;
typedef int64_t cell;
#else
#error Unsupported cell size (PAWN_CELL_SIZE)
#endif
#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1)
#define UNLIMITED (~1u >> 1)
struct tagAMX;
typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, const cell *params);
typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index,
cell *result, const cell *params);
typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx);
typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX *, cell *, int));
#if !defined _FAR
#define _FAR
#endif
#if defined _MSC_VER
#pragma warning(disable:4103) /* disable warning message 4103 that complains
* about pragma pack in a header file */
#pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */
#endif
/* Some compilers do not support the #pragma align, which should be fine. Some
* compilers give a warning on unknown #pragmas, which is not so fine...
*/
#if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN
#define AMX_NO_ALIGN
#endif
#if defined __GNUC__
#define PACKED __attribute__((packed))
#else
#define PACKED
#endif
#if !defined AMX_NO_ALIGN
#if defined LINUX || defined __FreeBSD__
#pragma pack(1) /* structures must be packed (byte-aligned) */
#elif defined MACOS && defined __MWERKS__
#pragma options align=mac68k
#else
#pragma pack(push)
#pragma pack(1) /* structures must be packed (byte-aligned) */
#if defined __TURBOC__
#pragma option -a- /* "pack" pragma for older Borland compilers */
#endif
#endif
#endif
typedef struct tagAMX_NATIVE_INFO {
const char _FAR *name PACKED;
AMX_NATIVE func PACKED;
} AMX_NATIVE_INFO;
#if !defined AMX_USERNUM
#define AMX_USERNUM 4
#endif
#define sEXPMAX 19 /* maximum name length for file version <= 6 */
#define sNAMEMAX 31 /* maximum name length of symbol name */
typedef struct tagAMX_FUNCSTUB {
ucell address PACKED;
char name[sEXPMAX+1];
} AMX_FUNCSTUB;
typedef struct tagFUNCSTUBNT {
ucell address PACKED;
uint32_t nameofs PACKED;
} AMX_FUNCSTUBNT;
/* The AMX structure is the internal structure for many functions. Not all
* fields are valid at all times; many fields are cached in local variables.
*/
typedef struct tagAMX {
unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */
unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */
AMX_CALLBACK callback PACKED;
AMX_DEBUG debug PACKED; /* debug callback */
/* for external functions a few registers must be accessible from the outside */
cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */
cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */
cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */
cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */
cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */
cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */
int flags PACKED; /* current status, see amx_Flags() */
/* user data */
#if AMX_USERNUM > 0
long usertags[AMX_USERNUM] PACKED;
void _FAR *userdata[AMX_USERNUM] PACKED;
#endif
/* native functions can raise an error */
int error PACKED;
/* passing parameters requires a "count" field */
int paramcount;
/* the sleep opcode needs to store the full AMX status */
cell pri PACKED;
cell alt PACKED;
cell reset_stk PACKED;
cell reset_hea PACKED;
/* extra fields for increased performance */
cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */
#if defined JIT
/* support variables for the JIT */
int reloc_size PACKED; /* required temporary buffer for relocations */
long code_size PACKED; /* estimated memory footprint of the native code */
#endif
} AMX;
/* The AMX_HEADER structure is both the memory format as the file format. The
* structure is used internaly.
*/
typedef struct tagAMX_HEADER {
int32_t size PACKED; /* size of the "file" */
uint16_t magic PACKED; /* signature */
char file_version; /* file format version */
char amx_version; /* required version of the AMX */
int16_t flags PACKED;
int16_t defsize PACKED; /* size of a definition record */
int32_t cod PACKED; /* initial value of COD - code block */
int32_t dat PACKED; /* initial value of DAT - data block */
int32_t hea PACKED; /* initial value of HEA - start of the heap */
int32_t stp PACKED; /* initial value of STP - stack top */
int32_t cip PACKED; /* initial value of CIP - the instruction pointer */
int32_t publics PACKED; /* offset to the "public functions" table */
int32_t natives PACKED; /* offset to the "native functions" table */
int32_t libraries PACKED; /* offset to the table of libraries */
int32_t pubvars PACKED; /* the "public variables" table */
int32_t tags PACKED; /* the "public tagnames" table */
int32_t nametable PACKED; /* name table */
} AMX_HEADER;
#if PAWN_CELL_SIZE==16
#define AMX_MAGIC 0xf1e2
#elif PAWN_CELL_SIZE==32
#define AMX_MAGIC 0xf1e0
#elif PAWN_CELL_SIZE==64
#define AMX_MAGIC 0xf1e1
#endif
enum {
AMX_ERR_NONE,
/* reserve the first 15 error codes for exit codes of the abstract machine */
AMX_ERR_EXIT, /* forced exit */
AMX_ERR_ASSERT, /* assertion failed */
AMX_ERR_STACKERR, /* stack/heap collision */
AMX_ERR_BOUNDS, /* index out of bounds */
AMX_ERR_MEMACCESS, /* invalid memory access */
AMX_ERR_INVINSTR, /* invalid instruction */
AMX_ERR_STACKLOW, /* stack underflow */
AMX_ERR_HEAPLOW, /* heap underflow */
AMX_ERR_CALLBACK, /* no callback, or invalid callback */
AMX_ERR_NATIVE, /* native function failed */
AMX_ERR_DIVIDE, /* divide by zero */
AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */
AMX_ERR_INVSTATE, /* invalid state for this access */
AMX_ERR_MEMORY = 16, /* out of memory */
AMX_ERR_FORMAT, /* invalid file format */
AMX_ERR_VERSION, /* file is for a newer version of the AMX */
AMX_ERR_NOTFOUND, /* function not found */
AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */
AMX_ERR_DEBUG, /* debugger cannot run */
AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */
AMX_ERR_USERDATA, /* unable to set user data field (table full) */
AMX_ERR_INIT_JIT, /* cannot initialize the JIT */
AMX_ERR_PARAMS, /* parameter error */
AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */
AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */
};
/* AMX_FLAG_CHAR16 0x01 no longer used */
#define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */
#define AMX_FLAG_COMPACT 0x04 /* compact encoding */
#define AMX_FLAG_SLEEP 0x08 /* script uses the sleep instruction (possible re-entry or power-down mode) */
#define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no BREAK opcodes */
#define AMX_FLAG_SYSREQN 0x800 /* script new (optimized) version of SYSREQ opcode */
#define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */
#define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */
#define AMX_FLAG_BROWSE 0x4000 /* busy browsing */
#define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */
#define AMX_EXEC_MAIN (-1) /* start at program entry point */
#define AMX_EXEC_CONT (-2) /* continue from last address */
#define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24))
#if !defined AMX_COMPACTMARGIN
#define AMX_COMPACTMARGIN 64
#endif
/* for native functions that use floating point parameters, the following
* two macros are convenient for casting a "cell" into a "float" type _without_
* changing the bit pattern
*/
#if PAWN_CELL_SIZE==32
#define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */
#define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */
#elif PAWN_CELL_SIZE==64
#define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */
#define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */
#else
#error Unsupported cell size
#endif
#define amx_StrParam(amx,param,result) \
do { \
cell *amx_cstr_; int amx_length_; \
amx_GetAddr((amx), (param), &amx_cstr_); \
amx_StrLen(amx_cstr_, &amx_length_); \
if (amx_length_ > 0 && \
((result) = (void*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \
amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \
else (result) = NULL; \
} while (0)
uint16_t * AMXAPI amx_Align16(uint16_t *v);
uint32_t * AMXAPI amx_Align32(uint32_t *v);
#if defined _I64_MAX || defined HAVE_I64
uint64_t * AMXAPI amx_Align64(uint64_t *v);
#endif
int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr);
int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, const cell *params);
int AMXAPI amx_Cleanup(AMX *amx);
int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data);
int AMXAPI amx_Exec(AMX *amx, cell *retval, int index);
int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index);
int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index);
int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr);
int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname);
int AMXAPI amx_Flags(AMX *amx,uint16_t *flags);
int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr);
int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname);
int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname);
int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr);
int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size);
int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id);
int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr);
int AMXAPI amx_Init(AMX *amx, void *program);
int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code);
int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap);
int AMXAPI amx_NameLength(AMX *amx, int *length);
AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func);
int AMXAPI amx_NumNatives(AMX *amx, int *number);
int AMXAPI amx_NumPublics(AMX *amx, int *number);
int AMXAPI amx_NumPubVars(AMX *amx, int *number);
int AMXAPI amx_NumTags(AMX *amx, int *number);
int AMXAPI amx_Push(AMX *amx, cell value);
int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells);
int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar);
int AMXAPI amx_RaiseError(AMX *amx, int error);
int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number);
int AMXAPI amx_Release(AMX *amx, cell amx_addr);
int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback);
int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug);
int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size);
int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr);
int AMXAPI amx_StrLen(const cell *cstring, int *length);
int AMXAPI amx_UTF8Check(const char *string, int *length);
int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value);
int AMXAPI amx_UTF8Len(const cell *cstr, int *length);
int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value);
#if PAWN_CELL_SIZE==16
#define amx_AlignCell(v) amx_Align16(v)
#elif PAWN_CELL_SIZE==32
#define amx_AlignCell(v) amx_Align32(v)
#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64)
#define amx_AlignCell(v) amx_Align64(v)
#else
#error Unsupported cell size
#endif
#define amx_RegisterFunc(amx, name, func) \
amx_Register((amx), amx_NativeInfo((name),(func)), 1);
#if !defined AMX_NO_ALIGN
#if defined LINUX || defined __FreeBSD__
#pragma pack() /* reset default packing */
#elif defined MACOS && defined __MWERKS__
#pragma options align=reset
#else
#pragma pack(pop) /* reset previous packing */
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* AMX_H_INCLUDED */

6
SOURCE/amx/amxDGram.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxDGram
DESCRIPTION 'Pawn AMX: network datagram'
EXPORTS
amx_DGramInit
amx_DGramCleanup

54
SOURCE/amx/amxDGram.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 0
#define BUILD 0
#define VERSIONSTR "1.0.0\0"
#define VERSIONNAME "amxDGram.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: network datagram\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxDGram\0"
#define VERSIONCOPYRIGHT "Copyright \251 2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

54
SOURCE/amx/amxFile.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 1
#define BUILD 0
#define VERSIONSTR "1.1.0\0"
#define VERSIONNAME "amxFile.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: File I/O support\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxFile\0"
#define VERSIONCOPYRIGHT "Copyright \251 2004-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

6
SOURCE/amx/amxFixed.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxFixed
DESCRIPTION 'Pawn AMX: fixed-point arithmetic'
EXPORTS
amx_FixedInit
amx_FixedCleanup

54
SOURCE/amx/amxFixed.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 0
#define BUILD 0
#define VERSIONSTR "1.0.0\0"
#define VERSIONNAME "amxFixed.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: Fixed Point support\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxFixed\0"
#define VERSIONCOPYRIGHT "Copyright \251 2003-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

6
SOURCE/amx/amxFloat.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxFloat
DESCRIPTION 'Pawn AMX: floating-point arithmetic'
EXPORTS
amx_FloatInit
amx_FloatCleanup

54
SOURCE/amx/amxFloat.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 0
#define BUILD 0
#define VERSIONSTR "1.0.0\0"
#define VERSIONNAME "amxFloat.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: Floating Point support\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxFloat\0"
#define VERSIONCOPYRIGHT "Copyright \251 2003-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

View File

@ -0,0 +1,6 @@
NAME amxProcess
DESCRIPTION 'Pawn AMX: process control and foreign function interface'
EXPORTS
amx_ProcessInit
amx_ProcessCleanup

54
SOURCE/amx/amxProcess.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 0
#define BUILD 0
#define VERSIONSTR "1.0.0\0"
#define VERSIONNAME "amxProcess.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: process control and foreign function interface\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxProcess\0"
#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

401
SOURCE/amx/amxargs.c Normal file
View File

@ -0,0 +1,401 @@
/* Script Arguments support module for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxargs.c 3649 2006-10-12 13:13:57Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <malloc.h>
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#endif
#include "osdefs.h"
#include "amx.h"
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _istdigit isdigit
# define _tgetenv getenv
# define _tcscat strcat
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
# define _tcsncmp strncmp
# define _tcspbrk strpbrk
# define _tcsrchr strrchr
# define _tcstol strtol
#endif
#if !defined AMXARGS_COLON
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
/* A ':' is also a separator for filenames (the disk drive identifier), and
* therefore it is better not to use it as an name/value seperator for
* command line argiments as well. So, by default, the library uses only
* the '=' as the name/value separator.
*/
#define AMXARGS_COLON 0
#else
#define AMXARGS_COLON 1
#endif
#endif
#if !defined AMXARGS_SKIPARG
/* The first option may be the name of the script (this is common if the
* host application takes the name of the script as the first parameter).
* This can optionally be ignored.
*/
#define AMXARGS_SKIPARG 0
#endif
static const TCHAR *tokenize(const TCHAR *string, int index, int *length);
static const TCHAR *cmdline = NULL;
static const TCHAR *rawcmdline(void)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
#elif defined _Windows || defined __MSDOS__
static char cmdbuffer[128];
#elif defined LINUX
static char cmdbuffer[1024]; /* some arbitrary maximum */
#endif
const TCHAR *ptr;
int skip = 0;
if (cmdline == NULL) {
#if defined __WIN32__ || defined _WIN32 || defined WIN32
cmdline = GetCommandLine();
skip++;
#elif defined _Windows || defined __MSDOS__
#if defined _Windows
unsigned short _psp = GetCurrentPDB();
#endif
char _far *cmd = (char _far *)MK_FP(_psp, 128);
unsigned char length = (unsigned char)*cmd++;
assert(length < 128);
assert(length < sizeof cmdbuffer);
memcpy(cmdbuffer, cmd, length);
cmdbuffer[length] = '\0';
if ((cmd == strchr(cmdbuffer, '\r')) != NULL)
*cmd = '\0'; /* also erase \r after the last option (if any) */
cmdline = cmdbuffer;
#elif defined LINUX
/* Options in /proc/<pid>/cmdline are delimited with '\0' characters
* rather than spaces.
*/
FILE *fp;
size_t fsize;
sprintf(cmdbuffer, "/proc/%d/cmdline", getpid());
if ((fp = fopen(cmdbuffer, "r")) != NULL) {
char *ptr;
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (fsize >= sizeof cmdbuffer)
fsize = sizeof cmdbuffer - 1;
fread(cmdbuffer, 1, fsize, fp);
fclose(fp);
cmdbuffer[fsize] = '\0'; /* terminate with double-zero */
// ??? convert to Unicode
/* convert '\0' characters to spaces, for uniform parsing */
for (ptr = cmdbuffer; *ptr != ' '; ptr = strchr(ptr, '\0') + 1)
*ptr = ' ';
cmdline = cmdbuffer;
skip++;
} /* if */
#else
#error Platform not supported
#endif
/* skip leading white space */
while (*cmdline <= __T(' ') && *cmdline != __T('\0'))
cmdline++;
#if AMXARGS_SKIPARG
skip++;
#endif
/* skip the first option(s), because it is the name of the host program
* and the name of the script
*/
if ((ptr = tokenize(cmdline, skip, NULL)) != NULL)
cmdline = ptr;
else
cmdline = _tcschr(cmdline, __T('\0'));
} /* if */
return cmdline;
}
static const TCHAR *tokenize(const TCHAR *string, int index, int *length)
{
const TCHAR *start = string;
TCHAR endchar;
assert(index >= 0);
assert(start != NULL);
while (*start == __T(' ') || *start == __T('\t'))
start++;
if (*start == __T('\0'))
return NULL;
if (*start == __T('"'))
endchar = *start++;
else
endchar = __T(' ');
while (index > 0 && start != NULL) {
start = _tcschr(start, endchar);
if (start != NULL) {
assert(*start == endchar);
if (endchar != __T(' '))
start++;
while (*start == __T(' ') || *start == __T('\t'))
start++;
if (*start == __T('"'))
endchar = *start++;
else
endchar = __T(' ');
} /* if */
index--;
} /* while */
if (start != NULL && length != NULL) {
const TCHAR *end;
if ((end = _tcschr(start, endchar)) == NULL)
end = _tcschr(start, __T('\0'));
assert(end != NULL);
*length = (int)(end - start);
} /* if */
return start;
}
static const TCHAR *matcharg(const TCHAR *key, int skip, int *length)
{
const TCHAR *cmdline = rawcmdline();
int index, optlen, keylen;
const TCHAR *option, *vptr;
keylen = (key != NULL) ? _tcslen(key) : 0;
index = 0;
while ((option = tokenize(cmdline, index, length)) != NULL) {
/* check for a colon or an equal sign (':' or '=') */
vptr = _tcschr(option, __T('='));
#if AMXARGS_COLON
if (vptr == NULL || (int)(vptr - option) > *length)
vptr = _tcschr(option, __T(':'));
#endif
if (vptr != NULL && (int)(vptr - option) > *length)
vptr = NULL;
optlen = (vptr != NULL) ? (int)(vptr - option) : 0;
if (keylen == 0 && vptr == NULL
|| keylen > 0 && keylen == optlen && _tcsncmp(option, key, optlen) == 0)
{
if (vptr != NULL)
optlen++; /* if ':' or '=' was found, skip it too */
option += optlen; /* point behind option */
*length -= optlen; /* length of the value, not of the option */
assert(length >= 0);
if (skip-- == 0)
break;
} /* if */
index++;
} /* while */
return option;
}
/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false)
* returns true if the option was found and false on error or if the parameter "index" is out of range
*/
static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params)
{
const TCHAR *cmdline = rawcmdline();
const TCHAR *option;
int length, max;
TCHAR *str;
cell *cptr;
max = (int)params[3];
if (max <= 0)
return 0;
amx_GetAddr(amx, params[2], &cptr);
if (cptr == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
if ((option = tokenize(cmdline, params[1], &length)) == NULL) {
/* option not found, return an empty string */
*cptr = 0;
return 0;
} /* if */
if (params[4])
max *= sizeof(cell);
if (max > length + 1)
max = length + 1;
str = (TCHAR *)alloca(max*sizeof(TCHAR));
if (str == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
memcpy(str, option, (max - 1) * sizeof(TCHAR));
str[max - 1] = __T('\0');
amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max);
return 1;
}
/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false)
* returns true if the option was found and false otherwise
*/
static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params)
{
const TCHAR *option, *key;
int length, max;
TCHAR *str;
cell *cptr;
max = (int)params[4];
if (max <= 0)
return 0;
amx_StrParam(amx, params[2], key);
amx_GetAddr(amx, params[3], &cptr);
if (cptr == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
option = matcharg(key, (int)params[1], &length);
if (option == NULL)
return 0; /* option not found */
/* check whether we must write the value of the option at all; in case the
* size is one cell and that cell is already zero, we do not write anything
* back
*/
assert(params[4] > 0);
if (params[4] > 1 || *cptr != 0) {
if (params[5])
max *= sizeof(cell);
if (max > length + 1)
max = length + 1;
str = (TCHAR *)alloca(max*sizeof(TCHAR));
if (str == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
memcpy(str, option, (max - 1) * sizeof(TCHAR));
str[max - 1] = __T('\0');
amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max);
} /* if */
return 1;
}
/* bool: argvalue(index=0, const option[]="", &value=cellmin)
* returns true if the option was found and false otherwise
*/
static cell AMX_NATIVE_CALL n_argvalue(AMX *amx, const cell *params)
{
const TCHAR *option, *key;
int length;
cell *cptr;
amx_StrParam(amx, params[2], key);
amx_GetAddr(amx, params[3], &cptr);
if (cptr == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
option = matcharg(key, (int)params[1], &length);
if (option == NULL)
return 0;
/* check whether we must write the value of the option at all */
if (length > 0 && (_istdigit(*option) || *option == __T('-')))
*cptr = _tcstol(option, NULL, 10);
return 1;
}
/* argcount() */
static cell AMX_NATIVE_CALL n_argcount(AMX *amx, const cell *params)
{
const TCHAR *cmdline = rawcmdline();
cell count = 0;
while (tokenize(cmdline, count, NULL) != NULL)
count++;
(void)amx;
(void)params;
return count;
}
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO args_Natives[] = {
{ "argcount", n_argcount },
{ "argindex", n_argindex },
{ "argstr", n_argstr },
{ "argvalue", n_argvalue },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_ArgsInit(AMX *amx)
{
return amx_Register(amx, args_Natives, -1);
}
int AMXEXPORT amx_ArgsCleanup(AMX *amx)
{
(void)amx;
return AMX_ERR_NONE;
}
/* A host application calls this function to set the command line for the
* script. If the host does not do this, the library will use the global
* options for the application (provided that it can find these). The buffer
* that is passed in to this function is NOT copied, so it may not be freed
* after the call.
*/
int AMXEXPORT amx_ArgsSetCmdLine(const TCHAR *cmd)
{
cmdline = cmd;
return AMX_ERR_NONE;
}

54
SOURCE/amx/amxargs.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 0
#define BUILD 0
#define VERSIONSTR "1.0.0\0"
#define VERSIONNAME "amxArgs.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: Script Arguments support\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxArgs\0"
#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

167
SOURCE/amx/amxaux.c Normal file
View File

@ -0,0 +1,167 @@
/* Support routines for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2003-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxaux.c 3612 2006-07-22 09:59:46Z thiadmer $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "amx.h"
#include "amxaux.h"
size_t AMXAPI aux_ProgramSize(char *filename)
{
FILE *fp;
AMX_HEADER hdr;
if ((fp=fopen(filename,"rb")) == NULL)
return 0;
fread(&hdr, sizeof hdr, 1, fp);
fclose(fp);
amx_Align16(&hdr.magic);
amx_Align32((uint32_t *)&hdr.stp);
return (hdr.magic==AMX_MAGIC) ? (size_t)hdr.stp : 0;
}
int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock)
{
FILE *fp;
AMX_HEADER hdr;
int result, didalloc;
/* open the file, read and check the header */
if ((fp = fopen(filename, "rb")) == NULL)
return AMX_ERR_NOTFOUND;
fread(&hdr, sizeof hdr, 1, fp);
amx_Align16(&hdr.magic);
amx_Align32((uint32_t *)&hdr.size);
amx_Align32((uint32_t *)&hdr.stp);
if (hdr.magic != AMX_MAGIC) {
fclose(fp);
return AMX_ERR_FORMAT;
} /* if */
/* allocate the memblock if it is NULL */
didalloc = 0;
if (memblock == NULL) {
if ((memblock = malloc(hdr.stp)) == NULL) {
fclose(fp);
return AMX_ERR_MEMORY;
} /* if */
didalloc = 1;
/* after amx_Init(), amx->base points to the memory block */
} /* if */
/* read in the file */
rewind(fp);
fread(memblock, 1, (size_t)hdr.size, fp);
fclose(fp);
/* initialize the abstract machine */
memset(amx, 0, sizeof *amx);
result = amx_Init(amx, memblock);
/* free the memory block on error, if it was allocated here */
if (result != AMX_ERR_NONE && didalloc) {
free(memblock);
amx->base = NULL; /* avoid a double free */
} /* if */
return result;
}
int AMXAPI aux_FreeProgram(AMX *amx)
{
if (amx->base!=NULL) {
amx_Cleanup(amx);
free(amx->base);
memset(amx,0,sizeof(AMX));
} /* if */
return AMX_ERR_NONE;
}
char * AMXAPI aux_StrError(int errnum)
{
static char *messages[] = {
/* AMX_ERR_NONE */ "(none)",
/* AMX_ERR_EXIT */ "Forced exit",
/* AMX_ERR_ASSERT */ "Assertion failed",
/* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)",
/* AMX_ERR_BOUNDS */ "Array index out of bounds",
/* AMX_ERR_MEMACCESS */ "Invalid memory access",
/* AMX_ERR_INVINSTR */ "Invalid instruction",
/* AMX_ERR_STACKLOW */ "Stack underflow",
/* AMX_ERR_HEAPLOW */ "Heap underflow",
/* AMX_ERR_CALLBACK */ "No (valid) native function callback",
/* AMX_ERR_NATIVE */ "Native function failed",
/* AMX_ERR_DIVIDE */ "Divide by zero",
/* AMX_ERR_SLEEP */ "(sleep mode)",
/* 13 */ "(reserved)",
/* 14 */ "(reserved)",
/* 15 */ "(reserved)",
/* AMX_ERR_MEMORY */ "Out of memory",
/* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format",
/* AMX_ERR_VERSION */ "File is for a newer version of the AMX",
/* AMX_ERR_NOTFOUND */ "File or function is not found",
/* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)",
/* AMX_ERR_DEBUG */ "Debugger cannot run",
/* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)",
/* AMX_ERR_USERDATA */ "Unable to set user data field (table full)",
/* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT",
/* AMX_ERR_PARAMS */ "Parameter error",
/* AMX_ERR_DOMAIN */ "Domain error, expression result does not fit in range",
/* AMX_ERR_GENERAL */ "General error (unknown or unspecific error)",
};
if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0])
return "(unknown)";
return messages[errnum];
}
int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size)
{
AMX_HEADER *hdr;
if (amx == NULL || start == NULL || size == NULL)
return AMX_ERR_PARAMS;
hdr = (AMX_HEADER*)amx->base;
switch(section) {
case CODE_SECTION:
*start = (cell *)(amx->base + hdr->cod);
*size = hdr->dat - hdr->cod;
break;
case DATA_SECTION:
*start = (cell *)(amx->data);
*size = hdr->hea - hdr->dat;
break;
case HEAP_SECTION:
*start = (cell *)(amx->data + hdr->hea);
*size = amx->hea - hdr->hea;
break;
case STACK_SECTION:
*start = (cell *)(amx->data + amx->stk);
*size = amx->stp - amx->stk;
break;
default:
return AMX_ERR_PARAMS;
} /* switch */
return AMX_ERR_NONE;
}

54
SOURCE/amx/amxaux.h Normal file
View File

@ -0,0 +1,54 @@
/* Support routines for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2003-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxaux.h 3612 2006-07-22 09:59:46Z thiadmer $
*/
#ifndef AMXAUX_H_INCLUDED
#define AMXAUX_H_INCLUDED
#include "amx.h"
#ifdef __cplusplus
extern "C" {
#endif
/* loading and freeing programs */
size_t AMXAPI aux_ProgramSize(char *filename);
int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock);
int AMXAPI aux_FreeProgram(AMX *amx);
/* a readable error message from an error code */
char * AMXAPI aux_StrError(int errnum);
enum {
CODE_SECTION,
DATA_SECTION,
HEAP_SECTION,
STACK_SECTION,
/* ----- */
NUM_SECTIONS
};
int AMXAPI aux_GetSection(AMX *amx, int section, cell **start, size_t *size);
#ifdef __cplusplus
}
#endif
#endif /* AMXAUX_H_INCLUDED */

1266
SOURCE/amx/amxcons.c Normal file

File diff suppressed because it is too large Load Diff

17
SOURCE/amx/amxcons.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef AMXCONS_H_INCLUDED
#define AMXCONS_H_INCLUDED
typedef struct tagFMTINFO {
const cell *params;
int numparams;
int skip; /* number of characters to skip from the beginning */
int length; /* number of characters to print */
/* helper functions */
int (*f_putstr)(void *dest,const TCHAR *);
int (*f_putchar)(void *dest,TCHAR);
void *user; /* user data */
} AMX_FMTINFO;
int amx_printstring(AMX *amx,cell *cstr,AMX_FMTINFO *info);
#endif /* AMXCONS_H_INCLUDED */

514
SOURCE/amx/amxcore.c Normal file
View File

@ -0,0 +1,514 @@
/* Core module for the Pawn AMX
*
* Copyright (c) ITB CompuPhase, 1997-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxcore.c 3657 2006-10-24 20:09:50Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include "amx.h"
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#endif
/* A few compilers do not provide the ANSI C standard "time" functions */
#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__
#include <time.h>
#endif
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
#endif
#define CHARBITS (8*sizeof(char))
typedef unsigned char uchar;
#if !defined AMX_NOPROPLIST
typedef struct _property_list {
struct _property_list *next;
cell id;
char *name;
cell value;
} proplist;
static proplist proproot = { NULL, 0, NULL, 0 };
static proplist *list_additem(proplist *root)
{
proplist *item;
assert(root!=NULL);
if ((item=(proplist *)malloc(sizeof(proplist)))==NULL)
return NULL;
item->name=NULL;
item->id=0;
item->value=0;
item->next=root->next;
root->next=item;
return item;
}
static void list_delete(proplist *pred,proplist *item)
{
assert(pred!=NULL);
assert(item!=NULL);
pred->next=item->next;
assert(item->name!=NULL);
free(item->name);
free(item);
}
static void list_setitem(proplist *item,cell id,char *name,cell value)
{
char *ptr;
assert(item!=NULL);
if ((ptr=(char *)malloc(strlen(name)+1))==NULL)
return;
if (item->name!=NULL)
free(item->name);
strcpy(ptr,name);
item->name=ptr;
item->id=id;
item->value=value;
}
static proplist *list_finditem(proplist *root,cell id,char *name,cell value,
proplist **pred)
{
proplist *item=root->next;
proplist *prev=root;
/* check whether to find by name or by value */
assert(name!=NULL);
if (strlen(name)>0) {
/* find by name */
while (item!=NULL && (item->id!=id || stricmp(item->name,name)!=0)) {
prev=item;
item=item->next;
} /* while */
} else {
/* find by value */
while (item!=NULL && (item->id!=id || item->value!=value)) {
prev=item;
item=item->next;
} /* while */
} /* if */
if (pred!=NULL)
*pred=prev;
return item;
}
#endif
static cell AMX_NATIVE_CALL numargs(AMX *amx,const cell *params)
{
AMX_HEADER *hdr;
uchar *data;
cell bytes;
(void)params;
hdr=(AMX_HEADER *)amx->base;
data=amx->data ? amx->data : amx->base+(int)hdr->dat;
/* the number of bytes is on the stack, at "frm + 2*cell" */
bytes= * (cell *)(data+(int)amx->frm+2*sizeof(cell));
/* the number of arguments is the number of bytes divided
* by the size of a cell */
return bytes/sizeof(cell);
}
static cell AMX_NATIVE_CALL getarg(AMX *amx,const cell *params)
{
AMX_HEADER *hdr;
uchar *data;
cell value;
hdr=(AMX_HEADER *)amx->base;
data=amx->data ? amx->data : amx->base+(int)hdr->dat;
/* get the base value */
value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell));
/* adjust the address in "value" in case of an array access */
value+=params[2]*sizeof(cell);
/* get the value indirectly */
value= * (cell *)(data+(int)value);
return value;
}
static cell AMX_NATIVE_CALL setarg(AMX *amx,const cell *params)
{
AMX_HEADER *hdr;
uchar *data;
cell value;
hdr=(AMX_HEADER *)amx->base;
data=amx->data ? amx->data : amx->base+(int)hdr->dat;
/* get the base value */
value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell));
/* adjust the address in "value" in case of an array access */
value+=params[2]*sizeof(cell);
/* verify the address */
if (value<0 || value>=amx->hea && value<amx->stk)
return 0;
/* set the value indirectly */
* (cell *)(data+(int)value) = params[3];
return 1;
}
static cell AMX_NATIVE_CALL heapspace(AMX *amx,const cell *params)
{
(void)params;
return amx->stk - amx->hea;
}
static cell AMX_NATIVE_CALL funcidx(AMX *amx,const cell *params)
{
char name[64];
cell *cstr;
int index,err,len;
amx_GetAddr(amx,params[1],&cstr);
/* verify string length */
amx_StrLen(cstr,&len);
if (len>=64) {
amx_RaiseError(amx,AMX_ERR_NATIVE);
return 0;
} /* if */
amx_GetString(name,cstr,0,UNLIMITED);
err=amx_FindPublic(amx,name,&index);
if (err!=AMX_ERR_NONE)
index=-1; /* this is not considered a fatal error */
return index;
}
void amx_swapcell(cell *pc)
{
union {
cell c;
#if PAWN_CELL_SIZE==16
uchar b[2];
#elif PAWN_CELL_SIZE==32
uchar b[4];
#elif PAWN_CELL_SIZE==64
uchar b[8];
#else
#error Unsupported cell size
#endif
} value;
uchar t;
assert(pc!=NULL);
value.c = *pc;
#if PAWN_CELL_SIZE==16
t = value.b[0];
value.b[0] = value.b[1];
value.b[1] = t;
#elif PAWN_CELL_SIZE==32
t = value.b[0];
value.b[0] = value.b[3];
value.b[3] = t;
t = value.b[1];
value.b[1] = value.b[2];
value.b[2] = t;
#elif PAWN_CELL_SIZE==64
t = value.b[0];
value.b[0] = value.b[7];
value.b[7] = t;
t = value.b[1];
value.b[1] = value.b[6];
value.b[6] = t;
t = value.b[2];
value.b[2] = value.b[5];
value.b[5] = t;
t = value.b[3];
value.b[3] = value.b[4];
value.b[4] = t;
#else
#error Unsupported cell size
#endif
*pc = value.c;
}
static cell AMX_NATIVE_CALL swapchars(AMX *amx,const cell *params)
{
cell c;
(void)amx;
assert((size_t)params[0]==sizeof(cell));
c=params[1];
amx_swapcell(&c);
return c;
}
static cell AMX_NATIVE_CALL core_tolower(AMX *amx,const cell *params)
{
(void)amx;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
return (cell)CharLower((LPTSTR)params[1]);
#elif defined _Windows
return (cell)AnsiLower((LPSTR)params[1]);
#else
if ((unsigned)(params[1]-'A')<26u)
return params[1]+'a'-'A';
return params[1];
#endif
}
static cell AMX_NATIVE_CALL core_toupper(AMX *amx,const cell *params)
{
(void)amx;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
return (cell)CharUpper((LPTSTR)params[1]);
#elif defined _Windows
return (cell)AnsiUpper((LPSTR)params[1]);
#else
if ((unsigned)(params[1]-'a')<26u)
return params[1]+'A'-'a';
return params[1];
#endif
}
static cell AMX_NATIVE_CALL core_min(AMX *amx,const cell *params)
{
(void)amx;
return params[1] <= params[2] ? params[1] : params[2];
}
static cell AMX_NATIVE_CALL core_max(AMX *amx,const cell *params)
{
(void)amx;
return params[1] >= params[2] ? params[1] : params[2];
}
static cell AMX_NATIVE_CALL core_clamp(AMX *amx,const cell *params)
{
cell value = params[1];
if (params[2] > params[3]) /* minimum value > maximum value ! */
amx_RaiseError(amx,AMX_ERR_NATIVE);
if (value < params[2])
value = params[2];
else if (value > params[3])
value = params[3];
return value;
}
#if !defined AMX_NOPROPLIST
static char *MakePackedString(cell *cptr)
{
int len;
char *dest;
amx_StrLen(cptr,&len);
dest=(char *)malloc(len+sizeof(cell));
amx_GetString(dest,cptr,0,UNLIMITED);
return dest;
}
static int verify_addr(AMX *amx,cell addr)
{
int err;
cell *cdest;
err=amx_GetAddr(amx,addr,&cdest);
if (err!=AMX_ERR_NONE)
amx_RaiseError(amx,err);
return err;
}
static cell AMX_NATIVE_CALL getproperty(AMX *amx,const cell *params)
{
cell *cstr;
char *name;
proplist *item;
amx_GetAddr(amx,params[2],&cstr);
name=MakePackedString(cstr);
item=list_finditem(&proproot,params[1],name,params[3],NULL);
/* if list_finditem() found the value, store the name */
if (item!=NULL && item->value==params[3] && strlen(name)==0) {
int needed=(strlen(item->name)+sizeof(cell)-1)/sizeof(cell); /* # of cells needed */
if (verify_addr(amx,(cell)(params[4]+needed))!=AMX_ERR_NONE) {
free(name);
return 0;
} /* if */
amx_GetAddr(amx,params[4],&cstr);
amx_SetString(cstr,item->name,1,0,UNLIMITED);
} /* if */
free(name);
return (item!=NULL) ? item->value : 0;
}
static cell AMX_NATIVE_CALL setproperty(AMX *amx,const cell *params)
{
cell prev=0;
cell *cstr;
char *name;
proplist *item;
amx_GetAddr(amx,params[2],&cstr);
name=MakePackedString(cstr);
item=list_finditem(&proproot,params[1],name,params[3],NULL);
if (item==NULL)
item=list_additem(&proproot);
if (item==NULL) {
amx_RaiseError(amx,AMX_ERR_MEMORY);
} else {
prev=item->value;
if (strlen(name)==0) {
free(name);
amx_GetAddr(amx,params[4],&cstr);
name=MakePackedString(cstr);
} /* if */
list_setitem(item,params[1],name,params[3]);
} /* if */
free(name);
return prev;
}
static cell AMX_NATIVE_CALL delproperty(AMX *amx,const cell *params)
{
cell prev=0;
cell *cstr;
char *name;
proplist *item,*pred;
amx_GetAddr(amx,params[2],&cstr);
name=MakePackedString(cstr);
item=list_finditem(&proproot,params[1],name,params[3],&pred);
if (item!=NULL) {
prev=item->value;
list_delete(pred,item);
} /* if */
free(name);
return prev;
}
static cell AMX_NATIVE_CALL existproperty(AMX *amx,const cell *params)
{
cell *cstr;
char *name;
proplist *item;
amx_GetAddr(amx,params[2],&cstr);
name=MakePackedString(cstr);
item=list_finditem(&proproot,params[1],name,params[3],NULL);
free(name);
return (item!=NULL);
}
#endif
#if !defined AMX_NORANDOM
/* This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley
* (ISBN 0-201-47960-5). This is a "multiplicative congruential random number
* generator" that has been extended to 31-bits (the standard C version returns
* only 15-bits).
*/
#define INITIAL_SEED 0xcaa938dbL
static unsigned long IL_StandardRandom_seed = INITIAL_SEED; /* always use a non-zero seed */
#define IL_RMULT 1103515245L
#if defined __BORLANDC__ || defined __WATCOMC__
#pragma argsused
#endif
static cell AMX_NATIVE_CALL core_random(AMX *amx,const cell *params)
{
unsigned long lo, hi, ll, lh, hh, hl;
unsigned long result;
/* one-time initialization (or, mostly one-time) */
#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE && !defined __ICC430__
if (IL_StandardRandom_seed == INITIAL_SEED)
IL_StandardRandom_seed=(unsigned long)time(NULL);
#endif
(void)amx;
lo = IL_StandardRandom_seed & 0xffff;
hi = IL_StandardRandom_seed >> 16;
IL_StandardRandom_seed = IL_StandardRandom_seed * IL_RMULT + 12345;
ll = lo * (IL_RMULT & 0xffff);
lh = lo * (IL_RMULT >> 16 );
hl = hi * (IL_RMULT & 0xffff);
hh = hi * (IL_RMULT >> 16 );
result = ((ll + 12345) >> 16) + lh + hl + (hh << 16);
result &= ~LONG_MIN; /* remove sign bit */
if (params[1]!=0)
result %= params[1];
return (cell)result;
}
#endif
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO core_Natives[] = {
{ "numargs", numargs },
{ "getarg", getarg },
{ "setarg", setarg },
{ "heapspace", heapspace },
{ "funcidx", funcidx },
{ "swapchars", swapchars },
{ "tolower", core_tolower },
{ "toupper", core_toupper },
{ "min", core_min },
{ "max", core_max },
{ "clamp", core_clamp },
#if !defined AMX_NORANDOM
{ "random", core_random },
#endif
#if !defined AMX_NOPROPLIST
{ "getproperty", getproperty },
{ "setproperty", setproperty },
{ "deleteproperty",delproperty },
{ "existproperty", existproperty },
#endif
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_CoreInit(AMX *amx)
{
return amx_Register(amx, core_Natives, -1);
}
int AMXEXPORT amx_CoreCleanup(AMX *amx)
{
(void)amx;
#if !defined AMX_NOPROPLIST
while (proproot.next!=NULL)
list_delete(&proproot,proproot.next);
#endif
return AMX_ERR_NONE;
}

474
SOURCE/amx/amxdbg.c Normal file
View File

@ -0,0 +1,474 @@
/* Pawn debugger interface
*
* Support functions for debugger applications
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxdbg.c 3612 2006-07-22 09:59:46Z thiadmer $
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "osdefs.h" /* for _MAX_PATH */
#include "amx.h"
#include "amxdbg.h"
int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg)
{
assert(amxdbg != NULL);
if (amxdbg->hdr != NULL)
free(amxdbg->hdr);
if (amxdbg->filetbl != NULL)
free(amxdbg->filetbl);
if (amxdbg->symboltbl != NULL)
free(amxdbg->symboltbl);
if (amxdbg->tagtbl != NULL)
free(amxdbg->tagtbl);
if (amxdbg->automatontbl != NULL)
free(amxdbg->automatontbl);
if (amxdbg->statetbl != NULL)
free(amxdbg->statetbl);
memset(amxdbg, 0, sizeof(AMX_DBG));
return AMX_ERR_NONE;
}
int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp)
{
AMX_HEADER amxhdr;
AMX_DBG_HDR dbghdr;
unsigned char *ptr;
int index, dim;
AMX_DBG_SYMDIM *symdim;
assert(fp != NULL);
assert(amxdbg != NULL);
memset(&amxhdr, 0, sizeof amxhdr);
fseek(fp, 0L, SEEK_SET);
fread(&amxhdr, sizeof amxhdr, 1, fp);
#if BYTE_ORDER==BIG_ENDIAN
amx_Align32((uint32_t*)&amxhdr.size);
amx_Align16(&amxhdr.magic);
#endif
if (amxhdr.magic != AMX_MAGIC)
return AMX_ERR_FORMAT;
if ((amxhdr.flags & AMX_FLAG_DEBUG) == 0)
return AMX_ERR_DEBUG;
fseek(fp, amxhdr.size, SEEK_SET);
memset(&dbghdr, 0, sizeof(AMX_DBG_HDR));
fread(&dbghdr, sizeof(AMX_DBG_HDR), 1, fp);
#if BYTE_ORDER==BIG_ENDIAN
amx_Align32((uint32_t*)&dbghdr.size);
amx_Align16(&dbghdr.magic);
amx_Align16(&dbghdr.flags);
amx_Align16(&dbghdr.files);
amx_Align16(&dbghdr.lines);
amx_Align16(&dbghdr.symbols);
amx_Align16(&dbghdr.tags);
amx_Align16(&dbghdr.automatons);
amx_Align16(&dbghdr.states);
#endif
if (dbghdr.magic != AMX_DBG_MAGIC)
return AMX_ERR_FORMAT;
/* allocate all memory */
memset(amxdbg, 0, sizeof(AMX_DBG));
amxdbg->hdr = malloc((size_t)dbghdr.size);
if (dbghdr.files > 0)
amxdbg->filetbl = malloc(dbghdr.files * sizeof(AMX_DBG_FILE *));
if (dbghdr.symbols > 0)
amxdbg->symboltbl = malloc(dbghdr.symbols * sizeof(AMX_DBG_SYMBOL *));
if (dbghdr.tags > 0)
amxdbg->tagtbl = malloc(dbghdr.tags * sizeof(AMX_DBG_TAG *));
if (dbghdr.automatons > 0)
amxdbg->automatontbl = malloc(dbghdr.automatons * sizeof(AMX_DBG_MACHINE *));
if (dbghdr.states > 0)
amxdbg->statetbl = malloc(dbghdr.states * sizeof(AMX_DBG_STATE *));
if (amxdbg->hdr == NULL
|| (dbghdr.files > 0 && amxdbg->filetbl == NULL)
|| (dbghdr.symbols > 0 && amxdbg->symboltbl == NULL)
|| (dbghdr.tags > 0 && amxdbg->tagtbl == NULL)
|| (dbghdr.states > 0 && amxdbg->statetbl == NULL)
|| (dbghdr.automatons > 0 && amxdbg->automatontbl == NULL))
{
dbg_FreeInfo(amxdbg);
return AMX_ERR_MEMORY;
} /* if */
/* load the entire symbolic information block into memory */
memcpy(amxdbg->hdr, &dbghdr, sizeof dbghdr);
fread(amxdbg->hdr + 1, 1, (size_t)(dbghdr.size - sizeof dbghdr), fp);
/* run through the file, fix alignment issues and set up table pointers */
ptr = (unsigned char *)(amxdbg->hdr + 1);
/* file table */
for (index = 0; index < dbghdr.files; index++) {
assert(amxdbg->filetbl != NULL);
amxdbg->filetbl[index] = (AMX_DBG_FILE *)ptr;
#if BYTE_ORDER==BIG_ENDIAN
amx_AlignCell(&amxdbg->filetbl[index]->address);
#endif
for (ptr = ptr + sizeof(AMX_DBG_FILE); *ptr != '\0'; ptr++)
/* nothing */;
ptr++; /* skip '\0' too */
} /* for */
/* line table */
amxdbg->linetbl = (AMX_DBG_LINE*)ptr;
#if BYTE_ORDER==BIG_ENDIAN
for (index = 0; index < dbghdr.lines; index++) {
amx_AlignCell(&amxdbg->linetbl[index].address);
amx_Align32((uint32_t*)&amxdbg->linetbl[index].line);
} /* for */
#endif
ptr += dbghdr.lines * sizeof(AMX_DBG_LINE);
/* symbol table (plus index tags) */
for (index = 0; index < dbghdr.symbols; index++) {
assert(amxdbg->symboltbl != NULL);
amxdbg->symboltbl[index] = (AMX_DBG_SYMBOL *)ptr;
#if BYTE_ORDER==BIG_ENDIAN
amx_AlignCell(&amxdbg->symboltbl[index]->address);
amx_Align16((uint16_t*)&amxdbg->symboltbl[index]->tag);
amx_AlignCell(&amxdbg->symboltbl[index]->codestart);
amx_AlignCell(&amxdbg->symboltbl[index]->codeend);
amx_Align16((uint16_t*)&amxdbg->symboltbl[index]->dim);
#endif
for (ptr = ptr + sizeof(AMX_DBG_SYMBOL); *ptr != '\0'; ptr++)
/* nothing */;
ptr++; /* skip '\0' too */
for (dim = 0; dim < amxdbg->symboltbl[index]->dim; dim++) {
symdim = (AMX_DBG_SYMDIM *)ptr;
amx_Align16((uint16_t*)&symdim->tag);
amx_AlignCell(&symdim->size);
ptr += sizeof(AMX_DBG_SYMDIM);
} /* for */
} /* for */
/* tag name table */
for (index = 0; index < dbghdr.tags; index++) {
assert(amxdbg->tagtbl != NULL);
amxdbg->tagtbl[index] = (AMX_DBG_TAG *)ptr;
#if BYTE_ORDER==BIG_ENDIAN
amx_Align16(&amxdbg->tagtbl[index]->tag);
#endif
for (ptr = ptr + sizeof(AMX_DBG_TAG) - 1; *ptr != '\0'; ptr++)
/* nothing */;
ptr++; /* skip '\0' too */
} /* for */
/* automaton name table */
for (index = 0; index < dbghdr.automatons; index++) {
assert(amxdbg->automatontbl != NULL);
amxdbg->automatontbl[index] = (AMX_DBG_MACHINE *)ptr;
#if BYTE_ORDER==BIG_ENDIAN
amx_Align16(&amxdbg->automatontbl[index]->automaton);
amx_AlignCell(&amxdbg->automatontbl[index]->address);
#endif
for (ptr = ptr + sizeof(AMX_DBG_MACHINE) - 1; *ptr != '\0'; ptr++)
/* nothing */;
ptr++; /* skip '\0' too */
} /* for */
/* state name table */
for (index = 0; index < dbghdr.states; index++) {
assert(amxdbg->statetbl != NULL);
amxdbg->statetbl[index] = (AMX_DBG_STATE *)ptr;
#if BYTE_ORDER==BIG_ENDIAN
amx_Align16(&amxdbg->statetbl[index]->state);
amx_Align16(&amxdbg->automatontbl[index]->automaton);
#endif
for (ptr = ptr + sizeof(AMX_DBG_STATE) - 1; *ptr != '\0'; ptr++)
/* nothing */;
ptr++; /* skip '\0' too */
} /* for */
return AMX_ERR_NONE;
}
int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename)
{
int index;
assert(amxdbg != NULL);
assert(filename != NULL);
*filename = NULL;
/* this is a simple linear look-up; a binary search would be possible too */
for (index = 0; index < amxdbg->hdr->files && amxdbg->filetbl[index]->address <= address; index++)
/* nothing */;
/* reset for overrun */
if (--index < 0)
return AMX_ERR_NOTFOUND;
*filename = amxdbg->filetbl[index]->name;
return AMX_ERR_NONE;
}
int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line)
{
int index;
assert(amxdbg != NULL);
assert(line != NULL);
*line = 0;
/* this is a simple linear look-up; a binary search would be possible too */
for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address <= address; index++)
/* nothing */;
/* reset for overrun */
if (--index < 0)
return AMX_ERR_NOTFOUND;
*line = (long)amxdbg->linetbl[index].line;
return AMX_ERR_NONE;
}
int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname)
{
/* dbg_LookupFunction() finds the function a code address is in. It can be
* used for stack walking, and for stepping through a function while stepping
* over sub-functions
*/
int index;
assert(amxdbg != NULL);
assert(funcname != NULL);
*funcname = NULL;
for (index = 0; index < amxdbg->hdr->symbols; index++) {
if (amxdbg->symboltbl[index]->ident == iFUNCTN
&& amxdbg->symboltbl[index]->codestart <= address
&& amxdbg->symboltbl[index]->codeend > address)
break;
} /* for */
if (index >= amxdbg->hdr->symbols)
return AMX_ERR_NOTFOUND;
*funcname = amxdbg->symboltbl[index]->name;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name)
{
int index;
assert(amxdbg != NULL);
assert(name != NULL);
*name = NULL;
for (index = 0; index < amxdbg->hdr->tags && amxdbg->tagtbl[index]->tag != tag; index++)
/* nothing */;
if (index >= amxdbg->hdr->tags)
return AMX_ERR_NOTFOUND;
*name = amxdbg->tagtbl[index]->name;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name)
{
int index;
assert(amxdbg != NULL);
assert(name != NULL);
*name = NULL;
for (index = 0; index < amxdbg->hdr->automatons && amxdbg->automatontbl[index]->automaton != automaton; index++)
/* nothing */;
if (index >= amxdbg->hdr->automatons)
return AMX_ERR_NOTFOUND;
*name = amxdbg->automatontbl[index]->name;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name)
{
int index;
assert(amxdbg != NULL);
assert(name != NULL);
*name = NULL;
for (index = 0; index < amxdbg->hdr->states && amxdbg->statetbl[index]->state != state; index++)
/* nothing */;
if (index >= amxdbg->hdr->states)
return AMX_ERR_NOTFOUND;
*name = amxdbg->statetbl[index]->name;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address)
{
/* Find a suitable "breakpoint address" close to the indicated line (and in
* the specified file). The address is moved up to the next "breakable" line
* if no "breakpoint" is available on the specified line. You can use function
* dbg_LookupLine() to find out at which precise line the breakpoint was set.
*
* The filename comparison is strict (case sensitive and path sensitive); the
* "filename" parameter should point into the "filetbl" of the AMX_DBG
* structure.
*/
int file, index;
ucell bottomaddr,topaddr;
assert(amxdbg != NULL);
assert(filename != NULL);
assert(address != NULL);
*address = 0;
index = 0;
for (file = 0; file < amxdbg->hdr->files; file++) {
/* find the (next) mathing instance of the file */
if (strcmp(amxdbg->filetbl[file]->name, filename) != 0)
continue;
/* get address range for the current file */
bottomaddr = amxdbg->filetbl[file]->address;
topaddr = (file + 1 < amxdbg->hdr->files) ? amxdbg->filetbl[file+1]->address : (ucell)(cell)-1;
/* go to the starting address in the line table */
while (index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < bottomaddr)
index++;
/* browse until the line is found or until the top address is exceeded */
while (index < amxdbg->hdr->lines
&& amxdbg->linetbl[index].line < line
&& amxdbg->linetbl[index].address < topaddr)
index++;
if (index >= amxdbg->hdr->lines)
return AMX_ERR_NOTFOUND;
if (amxdbg->linetbl[index].line >= line)
break;
/* if not found (and the line table is not yet exceeded) try the next
* instance of the same file (a file may appear twice in the file table)
*/
} /* for */
if (strcmp(amxdbg->filetbl[file]->name, filename) != 0)
return AMX_ERR_NOTFOUND;
assert(index < amxdbg->hdr->lines);
*address = amxdbg->linetbl[index].address;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address)
{
/* Find a suitable "breakpoint address" close to the indicated line (and in
* the specified file). The address is moved up to the first "breakable" line
* in the function. You can use function dbg_LookupLine() to find out at which
* precise line the breakpoint was set.
*
* The filename comparison is strict (case sensitive and path sensitive); the
* "filename" parameter should point into the "filetbl" of the AMX_DBG
* structure. The function name comparison is case sensitive too.
*/
int index, err;
const char *tgtfile;
ucell funcaddr;
assert(amxdbg != NULL);
assert(funcname != NULL);
assert(filename != NULL);
assert(address != NULL);
*address = 0;
index = 0;
for ( ;; ) {
/* find (next) matching function */
while (index < amxdbg->hdr->symbols
&& (amxdbg->symboltbl[index]->ident != iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, funcname) != 0))
index++;
if (index >= amxdbg->hdr->symbols)
return AMX_ERR_NOTFOUND;
/* verify that this line falls in the appropriate file */
err = dbg_LookupFile(amxdbg, amxdbg->symboltbl[index]->address, &tgtfile);
if (err == AMX_ERR_NONE || strcmp(filename, tgtfile) == 0)
break;
index++; /* line is the wrong file, search further */
} /* for */
/* now find the first line in the function where we can "break" on */
assert(index < amxdbg->hdr->symbols);
funcaddr = amxdbg->symboltbl[index]->address;
for (index = 0; index < amxdbg->hdr->lines && amxdbg->linetbl[index].address < funcaddr; index++)
/* nothing */;
if (index >= amxdbg->hdr->lines)
return AMX_ERR_NOTFOUND;
*address = amxdbg->linetbl[index].address;
return AMX_ERR_NONE;
}
int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym)
{
ucell codestart,codeend;
int index;
assert(amxdbg != NULL);
assert(symname != NULL);
assert(sym != NULL);
*sym = NULL;
codestart = codeend = 0;
index = 0;
for ( ;; ) {
/* find (next) matching variable */
while (index < amxdbg->hdr->symbols
&& (amxdbg->symboltbl[index]->ident == iFUNCTN || strcmp(amxdbg->symboltbl[index]->name, symname) != 0)
&& (amxdbg->symboltbl[index]->codestart > scopeaddr || amxdbg->symboltbl[index]->codeend < scopeaddr))
index++;
if (index >= amxdbg->hdr->symbols)
break;
/* check the range, keep a pointer to the symbol with the smallest range */
if (strcmp(amxdbg->symboltbl[index]->name, symname) == 0
&& (codestart == 0 && codeend == 0
|| amxdbg->symboltbl[index]->codestart >= codestart && amxdbg->symboltbl[index]->codeend <= codeend))
{
*sym = amxdbg->symboltbl[index];
codestart = amxdbg->symboltbl[index]->codestart;
codeend = amxdbg->symboltbl[index]->codeend;
} /* if */
index++;
} /* for */
return (*sym == NULL) ? AMX_ERR_NOTFOUND : AMX_ERR_NONE;
}
int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim)
{
/* retrieves a pointer to the array dimensions structures of an array symbol */
const char *ptr;
assert(amxdbg != NULL);
assert(sym != NULL);
assert(symdim != NULL);
*symdim = NULL;
if (sym->ident != iARRAY && sym->ident != iREFARRAY)
return AMX_ERR_PARAMS;
assert(sym->dim > 0); /* array must have at least one dimension */
/* find the end of the symbol name */
for (ptr = sym->name; *ptr != '\0'; ptr++)
/* nothing */;
*symdim = (AMX_DBG_SYMDIM *)(ptr + 1);/* skip '\0' too */
return AMX_ERR_NONE;
}

172
SOURCE/amx/amxdbg.h Normal file
View File

@ -0,0 +1,172 @@
/* Abstract Machine for the Pawn compiler, debugger support
*
* This file contains extra definitions that are convenient for debugger
* support.
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxdbg.h 3612 2006-07-22 09:59:46Z thiadmer $
*/
#ifndef AMXDBG_H_INCLUDED
#define AMXDBG_H_INCLUDED
#ifndef AMX_H_INCLUDED
#include "amx.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Some compilers do not support the #pragma align, which should be fine. Some
* compilers give a warning on unknown #pragmas, which is not so fine...
*/
#if defined SN_TARGET_PS2 || defined __GNUC__
#define AMX_NO_ALIGN
#endif
#if defined __GNUC__
#define PACKED __attribute__((packed))
#else
#define PACKED
#endif
#if !defined AMX_NO_ALIGN
#if defined LINUX || defined __FreeBSD__
#pragma pack(1) /* structures must be packed (byte-aligned) */
#elif defined MACOS && defined __MWERKS__
#pragma options align=mac68k
#else
#pragma pack(push)
#pragma pack(1) /* structures must be packed (byte-aligned) */
#if defined __TURBOC__
#pragma option -a- /* "pack" pragma for older Borland compilers */
#endif
#endif
#endif
typedef struct tagAMX_DBG_HDR {
int32_t size PACKED; /* size of the debug information chunk */
uint16_t magic PACKED; /* signature, must be 0xf1ef */
char file_version PACKED; /* file format version */
char amx_version PACKED; /* required version of the AMX */
int16_t flags PACKED; /* currently unused */
int16_t files PACKED; /* number of entries in the "file" table */
int32_t lines PACKED; /* number of entries in the "line" table */
int16_t symbols PACKED; /* number of entries in the "symbol" table */
int16_t tags PACKED; /* number of entries in the "tag" table */
int16_t automatons PACKED; /* number of entries in the "automaton" table */
int16_t states PACKED; /* number of entries in the "state" table */
} PACKED AMX_DBG_HDR;
#define AMX_DBG_MAGIC 0xf2ef
typedef struct tagAMX_DBG_FILE {
ucell address PACKED; /* address in the code segment where generated code (for this file) starts */
const char name[1] PACKED; /* ASCII string, zero-terminated */
} PACKED AMX_DBG_FILE;
typedef struct tagAMX_DBG_LINE {
ucell address PACKED; /* address in the code segment where generated code (for this line) starts */
int32_t line PACKED; /* line number */
} PACKED AMX_DBG_LINE;
typedef struct tagAMX_DBG_SYMBOL {
ucell address PACKED; /* address in the data segment or relative to the frame */
int16_t tag PACKED; /* tag for the symbol */
ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */
ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */
char ident PACKED; /* kind of symbol (function/variable) */
char vclass PACKED; /* class of symbol (global/local) */
int16_t dim PACKED; /* number of dimensions */
const char name[1] PACKED; /* ASCII string, zero-terminated */
} PACKED AMX_DBG_SYMBOL;
typedef struct tagAMX_DBG_SYMDIM {
int16_t tag PACKED; /* tag for the array dimension */
ucell size PACKED; /* size of the array dimension */
} PACKED AMX_DBG_SYMDIM;
typedef struct tagAMX_DBG_TAG {
int16_t tag PACKED; /* tag id */
const char name[1] PACKED; /* ASCII string, zero-terminated */
} PACKED AMX_DBG_TAG;
typedef struct tagAMX_DBG_MACHINE {
int16_t automaton PACKED; /* automaton id */
ucell address PACKED; /* address of state variable */
const char name[1] PACKED; /* ASCII string, zero-terminated */
} PACKED AMX_DBG_MACHINE;
typedef struct tagAMX_DBG_STATE {
int16_t state PACKED; /* state id */
int16_t automaton PACKED; /* automaton id */
const char name[1] PACKED; /* ASCII string, zero-terminated */
} PACKED AMX_DBG_STATE;
typedef struct tagAMX_DBG {
AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */
AMX_DBG_FILE **filetbl PACKED;
AMX_DBG_LINE *linetbl PACKED;
AMX_DBG_SYMBOL **symboltbl PACKED;
AMX_DBG_TAG **tagtbl PACKED;
AMX_DBG_MACHINE **automatontbl PACKED;
AMX_DBG_STATE **statetbl PACKED;
} PACKED AMX_DBG;
#if !defined iVARIABLE
#define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */
#define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */
#define iARRAY 3
#define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */
#define iFUNCTN 9
#endif
int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg);
int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp);
int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename);
int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname);
int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line);
int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address);
int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address);
int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name);
int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name);
int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name);
int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym);
int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim);
#if !defined AMX_NO_ALIGN
#if defined LINUX || defined __FreeBSD__
#pragma pack() /* reset default packing */
#elif defined MACOS && defined __MWERKS__
#pragma options align=reset
#else
#pragma pack(pop) /* reset previous packing */
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* AMXDBG_H_INCLUDED */

86
SOURCE/amx/amxdef.asm Normal file
View File

@ -0,0 +1,86 @@
; Definition of the AMX structure for assembler syntax (MASM/TASM/WASM)
amx_s STRUC
_base DD ?
_dataseg DD ?
_callback DD ?
_debug DD ?
_cip DD ?
_frm DD ?
_hea DD ?
_hlw DD ?
_stk DD ?
_stp DD ?
_flags DD ?
_usertags DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h)
_userdata DD 4 DUP (?) ; 4 = AMX_USERNUM (#define'd in amx.h)
_error DD ?
_paramcount DD ?
_pri DD ?
_alt DD ?
_reset_stk DD ?
_reset_hea DD ?
_syscall_d DD ?
IFDEF JIT
; the two fields below are for the JIT; they do not exist in
; the non-JIT version of the abstract machine
_reloc_size DD ? ; memory block for relocations
_code_size DD ? ; memory size of the native code
ENDIF
amx_s ENDS
amxhead_s STRUC
_size DD ? ; size of the "file"
_magic DW ? ; signature
_file_version DB ? ;file format version
_amx_version DB ? ; required version of the AMX
_h_flags DW ?
_defsize DW ? ; size of one public/native function entry
_cod DD ? ; initial value of COD - code block
_dat DD ? ; initial value of DAT - data block
_h_hea DD ? ; initial value of HEA - start of the heap
_h_stp DD ? ; initial value of STP - stack top
_h_cip DD ? ; initial value of CIP - the instruction pointer
_publics DD ? ; offset to the "public functions" table
_natives DD ? ; offset to the "native functions" table
_libraries DD ? ; offset to the "library" table
_pubvars DD ? ; offset to the "public variables" table
_tags DD ? ; offset to the "public tagnames" table
_nametable DD ? ; offset to the name table, file version 7 only
amxhead_s ENDS
AMX_ERR_NONE EQU 0
AMX_ERR_EXIT EQU 1
AMX_ERR_ASSERT EQU 2
AMX_ERR_STACKERR EQU 3
AMX_ERR_BOUNDS EQU 4
AMX_ERR_MEMACCESS EQU 5
AMX_ERR_INVINSTR EQU 6
AMX_ERR_STACKLOW EQU 7
AMX_ERR_HEAPLOW EQU 8
AMX_ERR_CALLBACK EQU 9
AMX_ERR_NATIVE EQU 10
AMX_ERR_DIVIDE EQU 11 ; for catching divide errors
AMX_ERR_SLEEP EQU 12
AMX_ERR_MEMORY EQU 16
AMX_ERR_FORMAT EQU 17
AMX_ERR_VERSION EQU 18
AMX_ERR_NOTFOUND EQU 19
AMX_ERR_INDEX EQU 20
AMX_ERR_DEBUG EQU 21
AMX_ERR_INIT EQU 22
AMX_ERR_USERDATA EQU 23
AMX_ERR_INIT_JIT EQU 24
AMX_ERR_PARAMS EQU 25
AMX_ERR_DOMAIN EQU 26
AMX_ERR_GENERAL EQU 27
AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available
AMX_FLAG_COMPACT EQU 0004h
AMX_FLAG_BYTEOPC EQU 0008h
AMX_FLAG_NOCHECKS EQU 0010h
AMX_FLAG_BROWSE EQU 4000h
AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated

86
SOURCE/amx/amxdefn.asm Normal file
View File

@ -0,0 +1,86 @@
; Definition of the AMX structure for assembler syntax (NASM)
struc amx_s
_base: resd 1
_dataseg: resd 1
_callback: resd 1
_debug: resd 1
_cip: resd 1
_frm: resd 1
_hea: resd 1
_hlw: resd 1
_stk: resd 1
_stp: resd 1
_flags: resd 1
_usertags: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h)
_userdata: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h)
_error: resd 1
_paramcount: resd 1
_pri: resd 1
_alt: resd 1
_reset_stk: resd 1
_reset_hea: resd 1
_syscall_d: resd 1
%ifdef JIT
; the two fields below are for the JIT; they do not exist in
; the non-JIT version of the abstract machine
_reloc_size: resd 1 ; memory block for relocations
_code_size: resd 1 ; memory size of the native code
%endif
endstruc
struc amxhead_s
_size: resd 1 ; size of the "file"
_magic: resw 1 ; signature
_file_version: resb 1; file format version
_amx_version: resb 1 ; required version of the AMX
_h_flags: resw 1
_defsize: resw 1 ; size of one public/native function entry
_cod: resd 1 ; initial value of COD - code block
_dat: resd 1 ; initial value of DAT - data block
_h_hea: resd 1 ; initial value of HEA - start of the heap
_h_stp: resd 1 ; initial value of STP - stack top
_h_cip: resd 1 ; initial value of CIP - the instruction pointer
_publics: resd 1 ; offset to the "public functions" table
_natives: resd 1 ; offset to the "native functions" table
_libraries: resd 1 ; offset to the "library" table
_pubvars: resd 1 ; offset to the "public variables" table
_tags: resd 1 ; offset to the "public tagnames" table
_nametable: resd 1 ; offset to the name table, file version 7 only
endstruc
AMX_ERR_NONE EQU 0
AMX_ERR_EXIT EQU 1
AMX_ERR_ASSERT EQU 2
AMX_ERR_STACKERR EQU 3
AMX_ERR_BOUNDS EQU 4
AMX_ERR_MEMACCESS EQU 5
AMX_ERR_INVINSTR EQU 6
AMX_ERR_STACKLOW EQU 7
AMX_ERR_HEAPLOW EQU 8
AMX_ERR_CALLBACK EQU 9
AMX_ERR_NATIVE EQU 10
AMX_ERR_DIVIDE EQU 11 ; for catching divide errors
AMX_ERR_SLEEP EQU 12
AMX_ERR_MEMORY EQU 16
AMX_ERR_FORMAT EQU 17
AMX_ERR_VERSION EQU 18
AMX_ERR_NOTFOUND EQU 19
AMX_ERR_INDEX EQU 20
AMX_ERR_DEBUG EQU 21
AMX_ERR_INIT EQU 22
AMX_ERR_USERDATA EQU 23
AMX_ERR_INIT_JIT EQU 24
AMX_ERR_PARAMS EQU 25
AMX_ERR_DOMAIN EQU 26
AMX_ERR_GENERAL EQU 27
AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available
AMX_FLAG_COMPACT EQU 0004h
AMX_FLAG_BYTEOPC EQU 0008h
AMX_FLAG_NOCHECKS EQU 0010h
AMX_FLAG_BROWSE EQU 4000h
AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated

356
SOURCE/amx/amxdgram.c Normal file
View File

@ -0,0 +1,356 @@
/* Datagram sending/receiving module for the Pawn Abstract Machine
*
* This module uses the UDP protocol (from the TCP/IP protocol suite).
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxdgram.c 3664 2006-11-08 12:09:25Z thiadmer $
*/
#include <assert.h>
#include <stdio.h>
#if defined LINUX
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <malloc.h>
#include <winsock.h>
#endif
#include "osdefs.h"
#include "amx.h"
#define SRC_BUFSIZE 22
#define BUFLEN 512
#define AMX_DGRAMPORT 9930 /* default port */
#if !defined SOCKET_ERROR
#define SOCKET_ERROR -1
#endif
static int sLocal;
static unsigned long udp_GetHostAddr(const char *host,int index)
{
unsigned long addr=inet_addr(host);
if (addr==0xffffffffL) {
struct hostent *phost=gethostbyname(host);
if (phost!=NULL) {
/* count the number of addresses in the list */
int count;
for (count=0; phost->h_addr_list[count]!=0; count++)
/* nothing */;
if (index<count)
addr=*(unsigned long *)phost->h_addr_list[index];
} /* if */
} /* if */
return addr;
}
static int udp_Open(void)
{
#if defined __WIN32 || defined _WIN32 || defined WIN32
WORD wVersionRequested = MAKEWORD(1,1);
WSADATA wsaData;
#endif
int optval = 1;
#if defined __WIN32 || defined _WIN32 || defined WIN32
WSAStartup(wVersionRequested, &wsaData);
#endif
if ((sLocal=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return -1;
if (setsockopt(sLocal, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof optval) == -1)
return -1;
return sLocal;
}
static int udp_Close(void)
{
if (sLocal>=0) {
#if defined __WIN32 || defined _WIN32 || defined WIN32
closesocket(sLocal);
#else
close(sLocal);
#endif
} /* if */
#if defined __WIN32 || defined _WIN32 || defined WIN32
WSACleanup();
#endif
return 0;
}
static int udp_Send(const char *host,short port,const char *message,int size)
{
struct sockaddr_in sRemote;
if (sLocal<0)
return -1;
memset((void *)&sRemote,sizeof sRemote,0);
sRemote.sin_family=AF_INET;
sRemote.sin_port=htons(port);
sRemote.sin_addr.s_addr= (host==NULL) ? htonl(INADDR_BROADCAST) : udp_GetHostAddr(host,0);
if (sendto(sLocal,message,size,0,(struct sockaddr *)&sRemote,sizeof sRemote)==-1)
return -1;
return size;
}
/* This call is blocking
* if source is not NULL, it must point to a buffer that can contain at least
* 22 characters.
*/
static int udp_Receive(char *message,size_t maxmsg,char *source)
{
struct sockaddr_in sSource;
int slen=sizeof(sSource);
int size;
size=recvfrom(sLocal, message, maxmsg, 0, (struct sockaddr *)&sSource, &slen);
if (size==-1)
return -1;
if (source!=NULL)
sprintf(source, "%s:%d", inet_ntoa(sSource.sin_addr), ntohs(sSource.sin_port));
return size;
}
static int udp_IsPacket(void)
{
int result;
fd_set rdset;
struct timeval time;
/* the select() function waits until the socket can be read, or until a
* time-out occurs; the time-out is set to 1 microsecond (the shortest
* delay possible).
*/
time.tv_sec=0;
time.tv_usec=1;
FD_ZERO(&rdset);
FD_SET(sLocal,&rdset);
result=select(0,&rdset,NULL,NULL,&time);
if (result==SOCKET_ERROR)
return -1;
return result != 0;
}
static int udp_Listen(short port)
{
struct sockaddr_in sFrom;
memset((void *)&sFrom,sizeof sFrom,0);
sFrom.sin_family=AF_INET;
sFrom.sin_port=htons(port);
sFrom.sin_addr.s_addr=htonl(INADDR_ANY);
if (bind(sLocal,(struct sockaddr *)&sFrom,sizeof sFrom)==-1)
return -1;
return 0;
}
static AMX_IDLE PrevIdle = NULL;
static int idxReceiveString = -1;
static int idxReceivePacket = -1;
static short dgramPort = 0;
static int dgramBound = 0;
/* sendstring(const message[], const destination[]="")
* destination has the format "127.0.0.1:9930"; when set to an empty string,
* a broadcast is sent.
* To mark the text as a "string", the function inserts a "byte order mark" in
* front of it. It does this for Extended ASCII strings too, although this is
* not entirely correct.
* Returns true on success, false on failure.
*/
static cell AMX_NATIVE_CALL n_sendstring(AMX *amx, const cell *params)
{
int r = 0, length;
cell *cstr;
char *host, *message, *ptr;
short port=AMX_DGRAMPORT;
amx_GetAddr(amx, params[1], &cstr);
amx_UTF8Len(cstr, &length);
if ((message = alloca(length + 3 + 1)) != NULL) {
/* insert the byte order mark (BOM) */
message[0]='\xef';
message[1]='\xbb';
message[2]='\xbf';
/* if this is a wide string, convert it to UTF-8 */
if ((ucell)*cstr<=UNPACKEDMAX) {
ptr=message+3;
while (*cstr!=0)
amx_UTF8Put(ptr, &ptr, length - (ptr-message), *cstr++);
*ptr='\0';
} else {
amx_GetString(message+3, cstr, 0, UNLIMITED);
} /* if */
amx_StrParam(amx, params[2], host);
if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) {
*ptr++='\0';
port=(short)atoi(ptr);
} /* if */
r= (udp_Send(host,port,message,strlen(message)+1) > 0);
} /* if */
return r;
}
/* sendpacket(const packet[], size, const destination[]="")
* destination has the format "127.0.0.1:9930"; when set to an empty string,
* a broadcast is sent.
* Returns true on success, false on failure.
*/
static cell AMX_NATIVE_CALL n_sendpacket(AMX *amx, const cell *params)
{
cell *cstr;
char *host, *ptr;
short port=AMX_DGRAMPORT;
amx_GetAddr(amx, params[1], &cstr);
amx_StrParam(amx, params[3], host);
if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) {
*ptr++='\0';
port=(short)atoi(ptr);
} /* if */
return (udp_Send(host,port,(const char *)cstr,params[2] * sizeof(cell)) > 0);
}
/* listenport(port)
* A program must call this function from main() or another start-up function
* because the module will use the default port 9930 otherwise.
*/
static cell AMX_NATIVE_CALL n_listenport(AMX *amx, const cell *params)
{
(void)amx;
dgramPort = (short)params[1];
return 0;
}
static int AMXAPI amx_DGramIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int))
{
char message[BUFLEN], source[SRC_BUFSIZE];
cell amx_addr_msg, amx_addr_src;
int len, chars;
int err=0;
assert(idxReceiveString >= 0 || idxReceivePacket >= 0);
if (PrevIdle != NULL)
PrevIdle(amx, Exec);
/* set up listener (first call only) */
if (!dgramBound) {
if (dgramPort==0)
dgramPort=AMX_DGRAMPORT; /* use default port if none was set */
if (udp_Listen(dgramPort)==-1)
return AMX_ERR_GENERAL;
dgramBound=1;
} /* if */
if (udp_IsPacket()) {
len=udp_Receive(message, sizeof message / sizeof message[0], source);
amx_PushString(amx,&amx_addr_src,NULL,source,1,0);
/* check the presence of a byte order mark: if it is absent, the received
* packet is no string; also check the packet size against string length
*/
if ((message[0]!='\xef' || message[1]!='\xbb' || message[2]!='\xbf'
|| len!=(int)strlen(message)+1 || idxReceiveString<0) && idxReceivePacket>=0)
{
/* receive as "packet" */
amx_Push(amx,len);
amx_PushArray(amx,&amx_addr_msg,NULL,(cell*)message,len);
err=Exec(amx,NULL,idxReceivePacket);
} else {
const char *msg=message;
if (msg[0]=='\xef' && msg[1]=='\xbb' && msg[2]=='\xbf')
msg+=3; /* skip BOM */
/* optionally convert from UTF-8 to a wide string */
if (amx_UTF8Check(msg,&chars)==AMX_ERR_NONE) {
cell *array=alloca((chars+1)*sizeof(cell));
cell *ptr=array;
if (array!=NULL) {
while (err==AMX_ERR_NONE && *msg!='\0')
amx_UTF8Get(msg,&msg,ptr++);
*ptr=0; /* zero-terminate */
amx_PushArray(amx,&amx_addr_msg,NULL,array,chars+1);
} /* if */
} else {
amx_PushString(amx,&amx_addr_msg,NULL,msg,1,0);
} /* if */
err=Exec(amx,NULL,idxReceiveString);
} /* if */
while (err==AMX_ERR_SLEEP)
err=Exec(amx,NULL,AMX_EXEC_CONT);
amx_Release(amx,amx_addr_msg);
amx_Release(amx,amx_addr_src);
} /* if */
return err;
}
#if defined __cplusplus
extern "C"
#endif
AMX_NATIVE_INFO dgram_Natives[] = {
{ "sendstring", n_sendstring },
{ "sendpacket", n_sendpacket },
{ "listenport", n_listenport },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_DGramInit(AMX *amx)
{
dgramBound = 0;
if (udp_Open()==-1)
return AMX_ERR_GENERAL;
/* see whether there is an @receivestring() function */
if (amx_FindPublic(amx,"@receivestring",&idxReceiveString)==AMX_ERR_NONE
|| amx_FindPublic(amx,"@receivepacket",&idxReceivePacket)==AMX_ERR_NONE)
{
if (amx_GetUserData(amx,AMX_USERTAG('I','d','l','e'),(void**)&PrevIdle)!=AMX_ERR_NONE)
PrevIdle=NULL;
amx_SetUserData(amx,AMX_USERTAG('I','d','l','e'),amx_DGramIdle);
} /* if */
return amx_Register(amx,dgram_Natives,-1);
}
int AMXEXPORT amx_DGramCleanup(AMX *amx)
{
(void)amx;
udp_Close();
return AMX_ERR_NONE;
}

2112
SOURCE/amx/amxexec.asm Normal file

File diff suppressed because it is too large Load Diff

2021
SOURCE/amx/amxexecn.asm Normal file

File diff suppressed because it is too large Load Diff

796
SOURCE/amx/amxfile.c Normal file
View File

@ -0,0 +1,796 @@
/* Text file I/O module for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2003-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxfile.c 3660 2006-11-05 13:05:09Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <io.h>
#include <malloc.h>
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#endif
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS
#include <dirent.h>
#endif
#include "osdefs.h"
#include "amx.h"
#include "fpattern.c"
#if !defined AMXFILE_VAR
#define AMXFILE_VAR "AMXFILE"
#elif AMXFILE_VAR==""
#undef AMXFILE_VAR
#endif
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _tfopen fopen
# define _tgetenv getenv
# define _tfputs fputs
# define _tcscat strcat
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
# define _tcsncpy strncpy
# define _tcspbrk strpbrk
# define _tcsrchr strrchr
#endif
#if !defined UNUSED_PARAM
#define UNUSED_PARAM(p) ((void)(p))
#endif
enum filemode {
io_read, /* file must exist */
io_write, /* creates a new file */
io_readwrite, /* file must exist */
io_append, /* file must exist, opened for writing only and seek to the end */
};
enum seek_whence {
seek_start,
seek_current,
seek_end,
};
/* This function only stores unpacked strings. UTF-8 is used for
* Unicode, and packed strings can only store 7-bit and 8-bit
* character sets (ASCII, Latin-1).
*/
static size_t fgets_cell(FILE *fp,cell *string,size_t max,int utf8mode)
{
size_t index;
fpos_t pos;
cell c;
int follow,lastcr;
cell lowmark;
assert(sizeof(cell)>=4);
assert(fp!=NULL);
assert(string!=NULL);
if (max==0)
return 0;
/* get the position, in case we have to back up */
fgetpos(fp, &pos);
index=0;
follow=0;
lowmark=0;
lastcr=0;
for ( ;; ) {
assert(index<max);
if (index==max-1)
break; /* string fully filled */
if ((c=fgetc(fp))==EOF) {
if (!utf8mode || follow==0)
break; /* no more characters */
/* If an EOF happened halfway an UTF-8 code, the string cannot be
* UTF-8 mode, and we must restart.
*/
index=0;
fsetpos(fp, &pos);
continue;
} /* if */
/* 8-bit characters are unsigned */
if (c<0)
c=-c;
if (utf8mode) {
if (follow>0 && (c & 0xc0)==0x80) {
/* leader code is active, combine with earlier code */
string[index]=(string[index] << 6) | ((unsigned char)c & 0x3f);
if (--follow==0) {
/* encoding a character in more bytes than is strictly needed,
* is not really valid UTF-8; we are strict here to increase
* the chance of heuristic dectection of non-UTF-8 text
* (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid)
*/
if (string[index]<lowmark)
utf8mode=0;
/* the code positions 0xd800--0xdfff and 0xfffe & 0xffff do not
* exist in UCS-4 (and hence, they do not exist in Unicode)
*/
if (string[index]>=0xd800 && string[index]<=0xdfff
|| string[index]==0xfffe || string[index]==0xffff)
utf8mode=0;
index++;
} /* if */
} else if (follow==0 && (c & 0x80)==0x80) {
/* UTF-8 leader code */
if ((c & 0xe0)==0xc0) {
/* 110xxxxx 10xxxxxx */
follow=1;
lowmark=0x80;
string[index]=c & 0x1f;
} else if ((c & 0xf0)==0xe0) {
/* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */
follow=2;
lowmark=0x800;
string[index]=c & 0x0f;
} else if ((c & 0xf8)==0xf0) {
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
follow=3;
lowmark=0x10000;
string[index]=c & 0x07;
} else if ((c & 0xfc)==0xf8) {
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
follow=4;
lowmark=0x200000;
string[index]=c & 0x03;
} else if ((c & 0xfe)==0xfc) {
/* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */
follow=5;
lowmark=0x4000000;
string[index]=c & 0x01;
} else {
/* this is invalid UTF-8 */
utf8mode=0;
} /* if */
} else if (follow==0 && (c & 0x80)==0x00) {
/* 0xxxxxxx (US-ASCII) */
string[index++]=c;
if (c==__T('\n'))
break; /* read newline, done */
} else {
/* this is invalid UTF-8 */
utf8mode=0;
} /* if */
if (!utf8mode) {
/* UTF-8 mode was switched just off, which means that non-conforming
* UTF-8 codes were found, which means in turn that the string is
* probably not intended as UTF-8; start over again
*/
index=0;
fsetpos(fp, &pos);
} /* if */
} else {
string[index++]=c;
if (c==__T('\n')) {
break; /* read newline, done */
} else if (lastcr) {
ungetc(c,fp); /* carriage return was read, no newline follows */
break;
} /* if */
lastcr=(c==__T('\r'));
} /* if */
} /* for */
assert(index<max);
string[index]=__T('\0');
return index;
}
static size_t fputs_cell(FILE *fp,cell *string,int utf8mode)
{
size_t count=0;
assert(sizeof(cell)>=4);
assert(fp!=NULL);
assert(string!=NULL);
while (*string!=0) {
if (utf8mode) {
cell c=*string;
if (c<0x80) {
/* 0xxxxxxx */
fputc((unsigned char)c,fp);
} else if (c<0x800) {
/* 110xxxxx 10xxxxxx */
fputc((unsigned char)((c>>6) & 0x1f | 0xc0),fp);
fputc((unsigned char)(c & 0x3f | 0x80),fp);
} else if (c<0x10000) {
/* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */
fputc((unsigned char)((c>>12) & 0x0f | 0xe0),fp);
fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
fputc((unsigned char)(c & 0x3f | 0x80),fp);
} else if (c<0x200000) {
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
fputc((unsigned char)((c>>18) & 0x07 | 0xf0),fp);
fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
fputc((unsigned char)(c & 0x3f | 0x80),fp);
} else if (c<0x4000000) {
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
fputc((unsigned char)((c>>24) & 0x03 | 0xf8),fp);
fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
fputc((unsigned char)(c & 0x3f | 0x80),fp);
} else {
/* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */
fputc((unsigned char)((c>>30) & 0x01 | 0xfc),fp);
fputc((unsigned char)((c>>24) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
fputc((unsigned char)(c & 0x3f | 0x80),fp);
} /* if */
} else {
/* not UTF-8 mode */
fputc((unsigned char)*string,fp);
} /* if */
string++;
count++;
} /* while */
return count;
}
static size_t fgets_char(FILE *fp, char *string, size_t max)
{
size_t index;
int c,lastcr;
index=0;
lastcr=0;
for ( ;; ) {
assert(index<max);
if (index==max-1)
break; /* string fully filled */
if ((c=fgetc(fp))==EOF)
break; /* no more characters */
string[index++]=(char)c;
if (c==__T('\n')) {
break; /* read newline, done */
} else if (lastcr) {
ungetc(c,fp); /* carriage return was read, no newline follows */
break;
} /* if */
lastcr=(c==__T('\r'));
} /* for */
assert(index<max);
string[index]=__T('\0');
return index;
}
#if defined __WIN32__ || defined _WIN32 || defined WIN32
#if defined _UNICODE
wchar_t *_wgetenv(wchar_t *name)
{
static wchar_t buffer[_MAX_PATH];
buffer[0]=L'\0';
GetEnvironmentVariable(name,buffer,sizeof buffer/sizeof(wchar_t));
return buffer[0]!=L'\0' ? buffer : NULL;
}
#else
char *getenv(const char *name)
{
static char buffer[_MAX_PATH];
buffer[0]='\0';
GetEnvironmentVariable(name,buffer,sizeof buffer);
return buffer[0]!='\0' ? buffer : NULL;
}
#endif
#endif
static char *completename(TCHAR *dest, TCHAR *src, size_t size)
{
#if defined AMXFILE_VAR
TCHAR *prefix,*ptr;
size_t len;
/* only files below a specific path are accessible */
prefix=getenv(AMXFILE_VAR);
/* if no specific path for files is present, use the "temporary" path */
if (prefix==NULL)
prefix=getenv(__T("tmp")); /* common under Windows and Unix */
if (prefix==NULL)
prefix=getenv(__T("temp")); /* common under Windows */
if (prefix==NULL)
prefix=getenv(__T("tmpdir")); /* common under Unix */
/* if no path for files is defined, and no temporary directory exists,
* fail the function; this is for security reasons.
*/
if (prefix==NULL)
return NULL;
if (_tcslen(prefix)+1>=size) /* +1 because directory separator is appended */
return NULL;
_tcscpy(dest,prefix);
/* append a directory separator (if not already present) */
len=_tcslen(dest);
if (len==0)
return NULL; /* empty start directory is not allowed */
if (dest[len-1]!=__T(DIRSEP_CHAR) && dest[len-1]!=__T('/') && len+1<size) {
dest[len]=__T(DIRSEP_CHAR);
dest[len+1]=__T('\0');
} /* if */
assert(_tcslen(dest)<size);
/* for DOS/Windows and Unix/Linux, skip everyting up to a comma, because
* this is used to indicate a protocol (e.g. file://C:/myfile.txt)
*/
#if DIRSEP_CHAR!=':'
if ((ptr=_tcsrchr(src,__T(':')))!=NULL) {
src=ptr+1; /* skip protocol/drive and colon */
/* a "drive" specifier is sometimes ended with a vertical bar instead
* of a colon in URL specifications
*/
if ((ptr=_tcschr(src,__T('|')))!=NULL)
src=ptr+1; /* skip drive and vertical bar */
while (src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/'))
src++; /* skip slashes behind the protocol/drive letter */
} /* if */
#endif
/* skip an initial backslash or a drive specifier in the source */
if ((src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/')) && (src[1]==__T(DIRSEP_CHAR) || src[1]==__T('/'))) {
/* UNC path */
char separators[]={__T(DIRSEP_CHAR),__T('/'),__T('\0')};
src+=2;
ptr=_tcspbrk(src,separators);
if (ptr!=NULL)
src=ptr+1;
} else if (src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/')) {
/* simple path starting from the root directory */
src++;
} /* if */
/* disallow any "../" specifications in the source path
* (the check below should be stricter, but directory names with
* trailing periods are rare anyway)
*/
for (ptr=src; *ptr!=__T('\0'); ptr++)
if (ptr[0]==__T('.') && (ptr[1]==__T(DIRSEP_CHAR) || ptr[1]==__T('/')))
return NULL; /* path name is not allowed */
/* concatenate the drive letter to the destination path */
if (_tcslen(dest)+_tcslen(src)>=size)
return NULL;
_tcscat(dest,src);
/* change forward slashes into proper directory separators */
#if DIRSEP_CHAR!='/'
while ((ptr=_tcschr(dest,__T('/')))!=NULL)
*ptr=__T(DIRSEP_CHAR);
#endif
return dest;
#else
if (_tcslen(src)>=size)
return NULL;
_tcscpy(dest,src);
/* change forward slashes into proper directory separators */
#if DIRSEP_CHAR!='/'
while ((ptr=_tcschr(dest,__T('/')))!=NULL)
*ptr=__T(DIRSEP_CHAR);
#endif
return dest;
#endif
}
/* File: fopen(const name[], filemode: mode) */
static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params)
{
TCHAR *attrib,*altattrib;
TCHAR *name,fullname[_MAX_PATH];
FILE *f = NULL;
altattrib=NULL;
switch (params[2] & 0x7fff) {
case io_read:
attrib=__T("rb");
break;
case io_write:
attrib=__T("wb");
break;
case io_readwrite:
attrib=__T("r+b");
altattrib=__T("w+b");
break;
case io_append:
attrib=__T("ab");
break;
default:
return 0;
} /* switch */
/* get the filename */
amx_StrParam(amx,params[1],name);
if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) {
f=_tfopen(fullname,attrib);
if (f==NULL && altattrib!=NULL)
f=_tfopen(fullname,altattrib);
} /* if */
return (cell)f;
}
/* fclose(File: handle) */
static cell AMX_NATIVE_CALL n_fclose(AMX *amx, const cell *params)
{
UNUSED_PARAM(amx);
return fclose((FILE*)params[1]) == 0;
}
/* fwrite(File: handle, const string[]) */
static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params)
{
int r = 0;
cell *cptr;
char *str;
int len;
amx_GetAddr(amx,params[2],&cptr);
amx_StrLen(cptr,&len);
if (len==0)
return 0;
if ((ucell)*cptr>UNPACKEDMAX) {
/* the string is packed, write it as an ASCII/ANSI string */
if ((str=(char*)alloca(len + 1))!=NULL) {
amx_GetString(str,cptr,0,len);
r=fputs(str,(FILE*)params[1]);
} /* if */
} else {
/* the string is unpacked, write it as UTF-8 */
r=fputs_cell((FILE*)params[1],cptr,1);
} /* if */
return r;
}
/* fread(File: handle, string[], size=sizeof string, bool:pack=false) */
static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params)
{
int chars,max;
char *str;
cell *cptr;
max=(int)params[3];
if (max<=0)
return 0;
if (params[4])
max*=sizeof(cell);
amx_GetAddr(amx,params[2],&cptr);
str=(char *)alloca(max);
if (str==NULL || cptr==NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
if (params[4]) {
/* store as packed string, read an ASCII/ANSI string */
chars=fgets_char((FILE*)params[1],str,max);
assert(chars<max);
amx_SetString(cptr,str,(int)params[4],0,max);
} else {
/* store and unpacked string, interpret UTF-8 */
chars=fgets_cell((FILE*)params[1],cptr,max,1);
} /* if */
assert(chars<max);
return chars;
}
/* fputchar(File: handle, value, bool:utf8 = true) */
static cell AMX_NATIVE_CALL n_fputchar(AMX *amx, const cell *params)
{
size_t result;
UNUSED_PARAM(amx);
if (params[3]) {
cell str[2];
str[0]=params[2];
str[1]=0;
result=fputs_cell((FILE*)params[1],str,1);
} else {
fputc((int)params[2],(FILE*)params[1]);
} /* if */
assert(result==0 || result==1);
return result;
}
/* fgetchar(File: handle, bool:utf8 = true) */
static cell AMX_NATIVE_CALL n_fgetchar(AMX *amx, const cell *params)
{
cell str[2];
size_t result;
UNUSED_PARAM(amx);
if (params[2]) {
result=fgets_cell((FILE*)params[1],str,2,1);
} else {
str[0]=fgetc((FILE*)params[1]);
result= (str[0]!=EOF);
} /* if */
assert(result==0 || result==1);
if (result==0)
return EOF;
else
return str[0];
}
#if PAWN_CELL_SIZE==16
#define aligncell amx_Align16
#elif PAWN_CELL_SIZE==32
#define aligncell amx_Align32
#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64)
#define aligncell amx_Align64
#else
#error Unsupported cell size
#endif
/* fblockwrite(File: handle, buffer[], size=sizeof buffer) */
static cell AMX_NATIVE_CALL n_fblockwrite(AMX *amx, const cell *params)
{
cell *cptr;
cell count;
amx_GetAddr(amx,params[2],&cptr);
if (cptr!=NULL) {
cell max=params[3];
ucell v;
for (count=0; count<max; count++) {
v=(ucell)*cptr++;
if (fwrite(aligncell(&v),sizeof(cell),1,(FILE*)params[1])!=1)
break; /* write error */
} /* for */
} /* if */
return count;
}
/* fblockread(File: handle, buffer[], size=sizeof buffer) */
static cell AMX_NATIVE_CALL n_fblockread(AMX *amx, const cell *params)
{
cell *cptr;
cell count;
amx_GetAddr(amx,params[2],&cptr);
if (cptr!=NULL) {
cell max=params[3];
ucell v;
for (count=0; count<max; count++) {
if (fread(&v,sizeof(cell),1,(FILE*)params[1])!=1)
break; /* write error */
*cptr++=(cell)*aligncell(&v);
} /* for */
} /* if */
return count;
}
/* File: ftemp() */
static cell AMX_NATIVE_CALL n_ftemp(AMX *amx, const cell *params)
{
UNUSED_PARAM(amx);
UNUSED_PARAM(params);
return (cell)tmpfile();
}
/* fseek(File: handle, position, seek_whence: whence=seek_start) */
static cell AMX_NATIVE_CALL n_fseek(AMX *amx, const cell *params)
{
int whence;
switch (params[3]) {
case seek_start:
whence=SEEK_SET;
break;
case seek_current:
whence=SEEK_CUR;
break;
case seek_end:
whence=SEEK_END;
//if (params[2]>0)
// params[2]=-params[2];
break;
default:
return 0;
} /* switch */
UNUSED_PARAM(amx);
return lseek(fileno((FILE*)params[1]),params[2],whence);
}
/* bool: fremove(const name[]) */
static cell AMX_NATIVE_CALL n_fremove(AMX *amx, const cell *params)
{
int r=1;
TCHAR *name,fullname[_MAX_PATH];
amx_StrParam(amx,params[1],name);
if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL)
r=remove(fullname);
return r==0;
}
/* flength(File: handle) */
static cell AMX_NATIVE_CALL n_flength(AMX *amx, const cell *params)
{
long l,c;
int fn=fileno((FILE*)params[1]);
c=lseek(fn,0,SEEK_CUR); /* save the current position */
l=lseek(fn,0,SEEK_END); /* return the file position at its end */
lseek(fn,c,SEEK_SET); /* restore the file pointer */
UNUSED_PARAM(amx);
return l;
}
static int match_optcopy(TCHAR *out,int outlen,const TCHAR *in,int skip)
{
if (out==NULL || skip!=0 || outlen<=0)
return 0;
_tcsncpy(out,in,outlen);
out[outlen-1]='\0';
return 1;
}
static int matchfiles(const TCHAR *path,int skip,TCHAR *out,int outlen)
{
int count=0;
const TCHAR *basename;
#if DIRSEP_CHAR!='/'
TCHAR *ptr;
#endif
#if defined __WIN32__
HANDLE hfind;
WIN32_FIND_DATA fd;
#else
/* assume LINUX, FreeBSD, OpenBSD, or some other variant */
DIR *dir;
struct dirent *entry;
TCHAR dirname[_MAX_PATH];
#endif
basename=_tcsrchr(path,DIRSEP_CHAR);
basename=(basename==NULL) ? path : basename+1;
#if DIRSEP_CHAR!='/'
ptr=_tcsrchr(basename,DIRSEP_CHAR);
basename=(ptr==NULL) ? basename : ptr+1;
#endif
#if defined __WIN32__
if ((hfind=FindFirstFile(path,&fd))!=INVALID_HANDLE_VALUE) {
do {
if (fpattern_match(basename,fd.cFileName,-1,FALSE)) {
count++;
if (match_optcopy(out,outlen,fd.cFileName,skip--))
break;
} /* if */
} while (FindNextFile(hfind,&fd));
FindClose(hfind);
} /* if */
#else
/* copy directory part only (zero-terminate) */
if (basename==path) {
strcpy(dirname,".");
} else {
strncpy(dirname,path,(int)(basename-path));
dirname[(int)(basename-path)]=_T('\0');
} /* if */
if ((dir=opendir(dirname))!=NULL) {
while ((entry=readdir(dir))!=NULL) {
if (fpattern_match(basename,entry->d_name,-1,TRUE)) {
count++;
if (match_optcopy(out,outlen,entry->d_name,skip--))
break;
} /* if */
} /* while */
closedir(dir);
} /* if */
#endif
return count;
}
/* fexist(const pattern[]) */
static cell AMX_NATIVE_CALL n_fexist(AMX *amx, const cell *params)
{
int r=0;
TCHAR *name,fullname[_MAX_PATH];
amx_StrParam(amx,params[1],name);
if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL)
r=matchfiles(fullname,0,NULL,0);
return r;
}
/* bool: fmatch(filename[], const pattern[], index=0, maxlength=sizeof filename) */
static cell AMX_NATIVE_CALL n_fmatch(AMX *amx, const cell *params)
{
TCHAR *name,fullname[_MAX_PATH]="";
cell *cptr;
amx_StrParam(amx,params[2],name);
if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) {
if (!matchfiles(fullname,params[3],fullname,sizeof fullname)) {
fullname[0]='\0';
} else {
/* copy the string into the destination */
amx_GetAddr(amx,params[1],&cptr);
amx_SetString(cptr,fullname,1,0,params[4]);
} /* if */
} /* if */
return fullname[0]!='\0';
}
#if defined __cplusplus
extern "C"
#endif
AMX_NATIVE_INFO file_Natives[] = {
{ "fopen", n_fopen },
{ "fclose", n_fclose },
{ "fwrite", n_fwrite },
{ "fread", n_fread },
{ "fputchar", n_fputchar },
{ "fgetchar", n_fgetchar },
{ "fblockwrite", n_fblockwrite },
{ "fblockread", n_fblockread },
{ "ftemp", n_ftemp },
{ "fseek", n_fseek },
{ "flength", n_flength },
{ "fremove", n_fremove },
{ "fexist", n_fexist },
{ "fmatch", n_fmatch },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_FileInit(AMX *amx)
{
return amx_Register(amx, file_Natives, -1);
}
int AMXEXPORT amx_FileCleanup(AMX *amx)
{
UNUSED_PARAM(amx);
return AMX_ERR_NONE;
}

6
SOURCE/amx/amxfile.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxFile
DESCRIPTION 'File I/O support library'
EXPORTS
amx_FileInit
amx_FileCleanup

307
SOURCE/amx/amxgc.c Normal file
View File

@ -0,0 +1,307 @@
/* Simple garbage collector for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2004-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxgc.c 3660 2006-11-05 13:05:09Z thiadmer $
*/
#include <assert.h>
#include <limits.h>
#include <stdlib.h> /* for malloc()/free() */
#include <string.h> /* for memset() */
#include "amx.h"
#include "amxgc.h"
typedef struct tagGCPAIR {
cell value;
int count;
} GCPAIR;
typedef struct tagGCINFO {
GCPAIR *table;
GC_FREE callback;
int exponent;
int flags;
int count;
} GCINFO;
#define SHIFT1 (sizeof(cell)*4)
#define MASK1 (~(((cell)-1) << SHIFT1))
#define FOLD1(p) ( ((p) & MASK1) ^ (((p) >> SHIFT1) & MASK1) )
/* call FOLD1(c) if the table exponent < SHIFT1 */
#define SHIFT2 (sizeof(cell)*2)
#define MASK2 (~(((cell)-1) << SHIFT2))
#define FOLD2(p) ( ((p) & MASK2) ^ (((p) >> SHIFT2) & MASK2) )
/* call FOLD2(c) if the table size < MASK2 */
#define SHIFT3 (sizeof(cell))
#define MASK3 (~(((cell)-1)<<SHIFT3))
#define FOLD3(p) ( ((p) & MASK3) ^ (((p) >> SHIFT3) & MASK3) )
/* call FOLD3(c) if the table size < MASK3 */
#define MASK(exp) (~(((cell)-1) << (exp)))
static unsigned increments[17] = { 1, 1, 1, 3, 5, 7, 17, 31, 67, 127, 257,
509, 1021, 2053, 4099, 8191, 16381 };
static unsigned char inverse[256] = {
255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,
239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,
223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,
207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,
191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,
175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,
159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,
143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,
127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,
111,110,109,108,107,106,105,104,103,102,101,100, 99, 98, 97, 96,
95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64,
63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48,
47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
};
static GCINFO SharedGC;
int gc_setcallback(GC_FREE callback)
{
SharedGC.callback=callback;
return GC_ERR_NONE;
}
int gc_settable(int exponent, int flags)
{
if (exponent==0) {
gc_clean(); /* delete all "live" objects first */
if (SharedGC.table!=NULL) {
free(SharedGC.table);
SharedGC.table=NULL;
} /* if */
SharedGC.exponent=0;
SharedGC.flags=0;
SharedGC.count=0;
} else {
int size,oldsize;
GCPAIR *table,*oldtable;
if (exponent<7 || (1L<<exponent)>INT_MAX)
return GC_ERR_PARAMS;
size=(1<<exponent);
/* the hash table should not hold more elements than the new size */
if (SharedGC.count>size)
return GC_ERR_PARAMS;
/* allocate the new table */
table=malloc(size*sizeof(*table));
if (table==NULL)
return GC_ERR_MEMORY;
/* save the statistics of the old table */
oldtable=SharedGC.table;
oldsize=(1<<SharedGC.exponent);
/* clear and set the new table */
memset(table,0,size*sizeof(*table));
SharedGC.table=table;
SharedGC.exponent=exponent;
SharedGC.count=flags;
SharedGC.count=0; /* old table in initially empty */
/* re-mark all objects in the old table */
if (oldtable!=NULL) {
int index;
for (index=0; index<oldsize; index++)
if (oldtable[index].value!=0)
gc_mark(oldtable[index].value);
free(oldtable);
} /* if */
} /* if */
return GC_ERR_NONE;
}
int gc_tablestat(int *exponent,int *percentage)
{
if (exponent!=NULL)
*exponent=SharedGC.exponent;
if (*percentage!=NULL) {
int size=(1L<<SharedGC.exponent);
/* calculate with floating point to avoid integer overflow */
double p=100.0*SharedGC.count/size;
*percentage=(int)p;
} /* if */
return GC_ERR_NONE;
}
int gc_mark(cell value)
{
int index,incr,incridx,mask;
cell v,t;
unsigned char *minorbyte;
if (SharedGC.table==NULL)
return GC_ERR_INIT;
if (SharedGC.count>=(1<<SharedGC.exponent)) {
int err;
if ((SharedGC.flags & GC_AUTOGROW)==0)
return GC_ERR_TABLEFULL;
err=gc_settable(SharedGC.exponent+1,SharedGC.flags);
if (err!=GC_ERR_NONE)
return err;
} /* if */
assert(SharedGC.count<(1<<SharedGC.exponent));
/* first "fold" the value, to make maximum use of all bits */
v=value;
if (SharedGC.exponent<SHIFT1)
v=FOLD1(v);
if (SharedGC.exponent<SHIFT2)
v=FOLD2(v);
if (SharedGC.exponent<SHIFT3)
v=FOLD3(v);
/* swap the bits of the minor byte */
minorbyte=(unsigned char*)&v;
*minorbyte=inverse[*minorbyte];
/* truncate the value to the required number of bits */
mask=MASK(SharedGC.exponent);
index=(v & mask);
incridx= (SharedGC.exponent<sizeof increments / sizeof increments[0]) ?
SharedGC.exponent :
(sizeof increments / sizeof increments[0]) - 1;
assert(incridx<sizeof increments / sizeof increments[0]);
incr=increments[incridx];
while ((t=SharedGC.table[index].value)!=0 && t!=value) {
assert(incr>0);
index=(index+incr) & mask;
if (incridx>0)
incr=increments[--incridx];
} /* while */
if (t!=0) {
assert(t==value);
assert(SharedGC.table[index].value==value);
return GC_ERR_DUPLICATE;
} /* if */
SharedGC.table[index].value=value;
assert(SharedGC.table[index].count==0);
return GC_ERR_NONE;
}
static void scansection(cell *start,size_t size)
{
int index,incr,incridx,incridx_org,mask;
cell v,t;
unsigned char *minorbyte;
assert(SharedGC.table!=NULL);
assert((size % sizeof(cell))==0);
assert(start!=NULL);
size/=sizeof(cell); /* from number of bytes to number of cells */
incridx_org= (SharedGC.exponent<sizeof increments / sizeof increments[0]) ?
SharedGC.exponent :
(sizeof increments / sizeof increments[0]) - 1;
assert(incridx_org<sizeof increments / sizeof increments[0]);
minorbyte=(unsigned char*)&v;
mask=MASK(SharedGC.exponent);
while (size>0) {
v=*start;
/* first "fold" the value, to make maximum use of all bits */
if (SharedGC.exponent<SHIFT1)
v=FOLD1(v);
if (SharedGC.exponent<SHIFT2)
v=FOLD2(v);
if (SharedGC.exponent<SHIFT3)
v=FOLD3(v);
/* swap the bits of the minor byte */
assert(minorbyte==(unsigned char*)&v);
*minorbyte=inverse[*minorbyte];
/* truncate the value to the required number of bits */
index=(v & mask);
/* find it in the table */
incridx=incridx_org;
incr=increments[incridx];
while ((t=SharedGC.table[index].value)!=*start && t!=0) {
assert(incr>0);
index=(index+incr) & mask;
if (incridx>0)
incr=increments[--incridx];
} /* while */
/* if found, mark it */
if (t!=0) {
assert(t==*start);
assert(SharedGC.table[index].value==*start);
SharedGC.table[index].count+=1;
} /* if */
size--;
start++;
} /* while */
}
int gc_scan(AMX *amx)
{
AMX_HEADER *hdr;
unsigned char *data;
if (amx==NULL)
return GC_ERR_PARAMS;
if (SharedGC.table==NULL)
return GC_ERR_INIT;
hdr=(AMX_HEADER*)amx->base;
/* scan data segment */
data=amx->data ? amx->data : amx->base+(int)hdr->dat;
scansection((cell *)data, hdr->hea - hdr->dat);
/* scan stack */
scansection((cell *)(data + amx->hlw), amx->hea - amx->hlw);
/* scan heap */
scansection((cell *)(data + amx->stk), amx->stp - amx->stk);
return GC_ERR_NONE;
}
int gc_clean(void)
{
int size;
GCPAIR *item;
if (SharedGC.table==NULL)
return GC_ERR_INIT;
if (SharedGC.callback==NULL)
return GC_ERR_CALLBACK;
size=(1<<SharedGC.exponent);
item=SharedGC.table;
while (size>0) {
if (item->value!=0) {
if (item->count==0) {
SharedGC.callback(item->value);
item->value=0;
} /* if */
item->count=0;
} /* if */
size--;
item++;
} /* while */
return GC_ERR_NONE;
}

54
SOURCE/amx/amxgc.h Normal file
View File

@ -0,0 +1,54 @@
/* Simple garbage collector for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2004-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxgc.h 3612 2006-07-22 09:59:46Z thiadmer $
*/
#ifndef AMXGC_H
#define AMXGC_H
typedef void _FAR (* GC_FREE)(cell unreferenced);
enum {
GC_ERR_NONE,
GC_ERR_CALLBACK, /* no callback, or invalid callback */
GC_ERR_INIT, /* garbage collector not initialized (no table size) */
GC_ERR_MEMORY, /* insufficient memory to set/resize the table */
GC_ERR_PARAMS, /* parameter error */
GC_ERR_TABLEFULL, /* domain error, expression result does not fit in range */
GC_ERR_DUPLICATE, /* item is already in the table */
};
/* flags */
#define GC_AUTOGROW 1 /* gc_mark() may grow the hash table when it fills up */
int gc_setcallback(GC_FREE callback);
int gc_settable(int exponent,int flags);
int gc_tablestat(int *exponent,int *percentage);
/* Upon return, "exponent" will hold the values passed to gc_settable();
* "percentage" is the level (in percent) that the hash table is filled
* up. Either parameter may be set to NULL.
*/
int gc_mark(cell value);
int gc_scan(AMX *amx);
int gc_clean(void);
#endif /* AMXGC_H */

2229
SOURCE/amx/amxjitr.asm Normal file

File diff suppressed because it is too large Load Diff

2261
SOURCE/amx/amxjits.asm Normal file

File diff suppressed because it is too large Load Diff

2404
SOURCE/amx/amxjitsn.asm Normal file

File diff suppressed because it is too large Load Diff

932
SOURCE/amx/amxprocess.c Normal file
View File

@ -0,0 +1,932 @@
/* Process control and Foreign Function Interface module for the Pawn AMX
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxprocess.c 3664 2006-11-08 12:09:25Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <malloc.h>
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/wait.h>
/* The package libffi library (required for compiling this extension module
* under Unix/Linux) is not included, because its license is more restrictive
* than that of Pawn (even if ever so slightly). Recent versions of the GCC
* compiler include libffi. A separate download of the libffi package is
* available at http://sources.redhat.com/libffi/ and
* http://sablevm.org/download/snapshot/.
*/
#include <ffi.h>
#endif
#include "osdefs.h"
#include "amx.h"
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _istdigit isdigit
# define _tgetenv getenv
# define _tcscat strcat
# define _tcschr strchr
# define _tcscmp strcmp
# define _tcscpy strcpy
# define _tcsdup strdup
# define _tcslen strlen
# define _tcsncmp strncmp
# define _tcspbrk strpbrk
# define _tcsrchr strrchr
# define _tcstol strtol
#endif
#define MAXPARAMS 32 /* maximum number of parameters to a called function */
typedef struct tagMODlIST {
struct tagMODlIST _FAR *next;
TCHAR _FAR *name;
unsigned long inst;
AMX *amx;
} MODLIST;
typedef struct tagPARAM {
union {
void *ptr;
long val;
} v;
unsigned char type;
unsigned char size;
int range;
} PARAM;
#define BYREF 0x80 /* stored in the "type" field fo the PARAM structure */
static MODLIST ModRoot = { NULL };
/* pipes for I/O redirection */
#if defined __WIN32__ || defined _WIN32 || defined WIN32
static HANDLE newstdin,newstdout,read_stdout,write_stdin;
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
static int pipe_to[2]={-1,-1};
static int pipe_from[2]={-1,-1};
void *inst_ffi=NULL; /* open handle for libffi */
#endif
static const TCHAR *skippath(const TCHAR *name)
{
const TCHAR *ptr;
assert(name != NULL);
if ((ptr = _tcsrchr(name, __T(DIRSEP_CHAR))) == NULL)
ptr = name;
else
ptr++;
assert(ptr != NULL);
return ptr;
}
static MODLIST _FAR *findlib(MODLIST *root, AMX *amx, const TCHAR *name)
{
MODLIST _FAR *item;
const TCHAR *ptr = skippath(name);
for (item = root->next; item != NULL; item = item->next)
if (_tcscmp(item->name, ptr) == 0 && item->amx == amx)
return item;
return NULL;
}
static MODLIST _FAR *addlib(MODLIST *root, AMX *amx, const TCHAR *name)
{
MODLIST _FAR *item;
const TCHAR *ptr = skippath(name);
assert(findlib(root, amx, name) == NULL); /* should not already be there */
if ((item = malloc(sizeof(MODLIST))) == NULL)
goto error;
memset(item, 0, sizeof(MODLIST));
assert(ptr != NULL);
if ((item->name = malloc((_tcslen(ptr) + 1) * sizeof(TCHAR))) == NULL)
goto error;
_tcscpy(item->name, ptr);
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
item->inst = (unsigned long)LoadLibrary(name);
#if !(defined __WIN32__ || defined _WIN32 || defined WIN32)
if (item->inst <= 32)
item->inst = 0;
#endif
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
/* also load the FFI library, if this is the first call */
inst_ffi=dlopen("libffi.so",RTLD_NOW);
if (inst_ffi==NULL)
inst_ffi=dlopen("libffi-2.00-beta.so",RTLD_NOW);
if (inst_ffi==NULL)
goto error; /* failed to load either the old library or the new libbrary */
item->inst = (unsigned long)dlopen(name,RTLD_NOW);
#else
#error Unsupported environment
#endif
if (item->inst == 0)
goto error;
item->amx = amx;
item->next = root->next;
root->next = item;
return item;
error:
if (item != NULL) {
if (item->name != NULL)
free(item->name);
if (item->inst != 0) {
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
FreeLibrary((HINSTANCE)item->inst);
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
dlclose((void*)item->inst);
#else
#error Unsupported environment
#endif
} /* if */
free(item);
} /* if */
return NULL;
}
static int freelib(MODLIST *root, AMX *amx, const TCHAR *name)
{
MODLIST _FAR *item, _FAR *prev;
const TCHAR *ptr;
int count = 0;
ptr = (name != NULL) ? skippath(name) : NULL;
for (prev = root, item = prev->next; item != NULL; prev = item, item = prev->next) {
if ((amx == NULL || amx == item->amx) && (ptr == NULL || _tcscmp(item->name, ptr) == 0)) {
prev->next = item->next; /* unlink first */
assert(item->inst != 0);
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
FreeLibrary((HINSTANCE)item->inst);
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
dlclose((void*)item->inst);
#else
#error Unsupported environment
#endif
assert(item->name != NULL);
free(item->name);
free(item);
count++;
} /* if */
} /* for */
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
if (amx==NULL && name==NULL && inst_ffi!=NULL)
dlclose(inst_ffi);
#endif
return count;
}
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
typedef long (CALLBACK* LIBFUNC)();
/* push()
**
** This function is the kind of programming trick that you don't even dare to
** dream about! With the usual C calling convention, the caller cleans up the
** stack after calling the function. This allows C functions to be flexible
** with parameters, both in number and in type.
** With the Pascal calling convention, used here, the callee (the function)
** cleans up the stack. But here, function push() doesn't know about any
** parameters. We neither declare any, nor indicate that the function has no
** parameters (i.e. the function is not declared having 'void' parameters).
** When we call function push(), the caller thinks the function cleans up the
** stack (because of the Pascal calling convention), while the function does
** not know that it has parameters, so it cannot clean them. As a result,
** nobody cleans up the stack. Ergo, The parameter you pass to function push()
** stays on the stack.
*/
static void PASCAL push() { }
LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname)
{
FARPROC lpfn;
assert(inst!=0);
lpfn=GetProcAddress((HINSTANCE)inst,functionname);
#if defined __WIN32__
if (lpfn==NULL && strlen(functionname)<128-1) {
char str[128];
strcpy(str,functionname);
#if defined UNICODE
strcat(str,"W");
#else
strcat(str,"A");
#endif
lpfn = GetProcAddress((HINSTANCE)inst,str);
} /* if */
#endif
return (LIBFUNC)lpfn;
}
#else
typedef long (* LIBFUNC)();
LIBFUNC SearchProcAddress(unsigned long inst, const char *functionname)
{
assert(inst!=0);
return (LIBFUNC)dlsym((void*)inst, functionname);
}
#endif
static void *fillarray(AMX *amx, PARAM *param, cell *cptr)
{
int i;
void *vptr;
vptr = malloc(param->range * (param->size / 8));
if (vptr == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return NULL;
} /* if */
assert(param->range > 1);
if (param->size == 8) {
unsigned char *ptr = (unsigned char *)vptr;
for (i = 0; i < param->range; i++)
*ptr++ = (unsigned char)*cptr++;
} else if (param->size == 16) {
unsigned short *ptr = (unsigned short *)vptr;
for (i = 0; i < param->range; i++)
*ptr++ = (unsigned short)*cptr++;
} else {
unsigned long *ptr = (unsigned long *)vptr;
for (i = 0; i < param->range; i++)
*ptr++ = (unsigned long)*cptr++;
} /* for */
return vptr;
}
/* libcall(const libname[], const funcname[], const typestring[], ...)
*
* Loads the DLL or shared library if not yet loaded (the name comparison is
* case sensitive).
*
* typestring format:
* Whitespace is permitted between the types, but not inside the type
* specification. The string "ii[4]&u16s" is equivalent to "i i[4] &u16 s",
* but easier on the eye.
*
* types:
* i = signed integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux
* u = unsigned integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux
* f = IEEE floating point, 32-bit
* p = packed string
* s = unpacked string
* The difference between packed and unpacked strings is only relevant when
* the parameter is passed by reference (see below).
*
* pass-by-value and pass-by-reference:
* By default, parameters are passed by value. To pass a parameter by
* reference, prefix the type letter with an "&":
* &i = signed integer passed by reference
* i = signed integer passed by value
* Same for '&u' versus 'u' and '&f' versus 'f'.
*
* Arrays are passed by "copy & copy-back". That is, libcall() allocates a
* block of dynamic memory to copy the array into. On return from the foreign
* function, libcall() copies the array back to the abstract machine. The
* net effect is similar to pass by reference, but the foreign function does
* not work in the AMX stack directly. During the copy and the copy-back
* operations, libcall() may also transform the array elements, for example
* between 16-bit and 32-bit elements. This is done because Pawn only
* supports a single cell size, which may not fit the required integer size
* of the foreign function.
*
* See "element ranges" for the syntax of passing an array.
*
* Strings may either be passed by copy, or by "copy & copy-back". When the
* string is an output parameter (for the foreign function), the size of the
* array that will hold the return string must be indicated between square
* brackets behind the type letter (see "element ranges"). When the string
* is "input only", this is not needed --libcall() will determine the length
* of the input string itself.
*
* The tokens 'p' and 's' are equivalent, but 'p[10]' and 's[10]' are not
* equivalent: the latter syntaxes determine whether the output from the
* foreign function will be stored as a packed or an unpacked string.
*
* element sizes:
* Add an integer behind the type letter; for example, 'i16' refers to a
* 16-bit signed integer. Note that the value behind the type letter must
* be either 8, 16 or 32.
*
* You should only use element size specifiers on the 'i' and 'u' types. That
* is, do not use these specifiers on 'f', 's' and 'p'.
*
* element ranges:
* For passing arrays, the size of the array may be given behind the type
* letter and optional element size. The token 'u[4]' indicates an array of
* four unsigned integers, which are typically 32-bit. The token 'i16[8]'
* is an array of 8 signed 16-bit integers. Arrays are always passed by
* "copy & copy-back"
*
* When compiled as Unicode, this library converts all strings to Unicode
* strings.
*
* The calling convention for the foreign functions is assumed:
* - "__stdcall" for Win32,
* - "far pascal" for Win16
* - and the GCC default for Unix/Linux (_cdecl)
*
* C++ name mangling of the called function is not handled (there is no standard
* convention for name mangling, so there is no portable way to convert C++
* function names to mangled names). Win32 name mangling (used by default by
* Microsoft compilers on functions declared as __stdcall) is also not handled.
*
* Returns the value of the called function.
*/
static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params)
{
const TCHAR *libname, *funcname, *typestring;
MODLIST *item;
int paramidx, typeidx, idx;
PARAM ps[MAXPARAMS];
cell *cptr,result;
LIBFUNC LibFunc;
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
ffi_cif cif;
ffi_type *ptypes[MAXPARAMS];
void *pvalues[MAXPARAMS];
#endif
amx_StrParam(amx, params[1], libname);
item = findlib(&ModRoot, amx, libname);
if (item == NULL)
item = addlib(&ModRoot, amx, libname);
if (item == NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
/* library is loaded, get the function */
amx_StrParam(amx, params[2], funcname);
LibFunc=(LIBFUNC)SearchProcAddress(item->inst, funcname);
if (LibFunc==NULL) {
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
/* decode the parameters */
paramidx=typeidx=0;
amx_StrParam(amx, params[3], typestring);
while (paramidx < MAXPARAMS && typestring[typeidx]!=__T('\0')) {
/* skip white space */
while (typestring[typeidx]!=__T('\0') && typestring[typeidx]<=__T(' '))
typeidx++;
if (typestring[typeidx]==__T('\0'))
break;
/* save "pass-by-reference" token */
ps[paramidx].type=0;
if (typestring[typeidx]==__T('&')) {
ps[paramidx].type=BYREF;
typeidx++;
} /* if */
/* store type character */
ps[paramidx].type |= (unsigned char)typestring[typeidx];
typeidx++;
/* set default size, then check for an explicit size */
#if defined __WIN32__ || defined _WIN32 || defined WIN32
ps[paramidx].size=32;
#elif defined _Windows
ps[paramidx].size=16;
#endif
if (_istdigit(typestring[typeidx])) {
ps[paramidx].size=(unsigned char)_tcstol(&typestring[typeidx],NULL,10);
while (_istdigit(typestring[typeidx]))
typeidx++;
} /* if */
/* set default range, then check for an explicit range */
ps[paramidx].range=1;
if (typestring[typeidx]=='[') {
ps[paramidx].range=_tcstol(&typestring[typeidx+1],NULL,10);
while (typestring[typeidx]!=']' && typestring[typeidx]!='\0')
typeidx++;
ps[paramidx].type |= BYREF; /* arrays are always passed by reference */
typeidx++; /* skip closing ']' too */
} /* if */
/* get pointer to parameter */
amx_GetAddr(amx,params[paramidx+4],&cptr);
switch (ps[paramidx].type) {
case 'i': /* signed integer */
case 'u': /* unsigned integer */
case 'f': /* floating point */
assert(ps[paramidx].range==1);
ps[paramidx].v.val=(int)*cptr;
break;
case 'i' | BYREF:
case 'u' | BYREF:
case 'f' | BYREF:
ps[paramidx].v.ptr=cptr;
if (ps[paramidx].range>1) {
/* convert array and pass by address */
ps[paramidx].v.ptr = fillarray(amx, &ps[paramidx], cptr);
} /* if */
break;
case 'p':
case 's':
case 'p' | BYREF:
case 's' | BYREF:
if (ps[paramidx].type=='s' || ps[paramidx].type=='p') {
int len;
/* get length of input string */
amx_StrLen(cptr,&len);
len++; /* include '\0' */
/* check max. size */
if (len<ps[paramidx].range)
len=ps[paramidx].range;
ps[paramidx].range=len;
} /* if */
ps[paramidx].v.ptr=malloc(ps[paramidx].range*sizeof(TCHAR));
if (ps[paramidx].v.ptr==NULL)
return amx_RaiseError(amx, AMX_ERR_NATIVE);
amx_GetString((char *)ps[paramidx].v.ptr,cptr,sizeof(TCHAR)>1,UNLIMITED);
break;
default:
/* invalid parameter type */
return amx_RaiseError(amx, AMX_ERR_NATIVE);
} /* switch */
paramidx++;
} /* while */
if ((params[0]/sizeof(cell)) - 3 != (size_t)paramidx)
return amx_RaiseError(amx, AMX_ERR_NATIVE); /* format string does not match number of parameters */
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
/* push the parameters to the stack (left-to-right in 16-bit; right-to-left
* in 32-bit)
*/
#if defined __WIN32__ || defined _WIN32 || defined WIN32
for (idx=paramidx-1; idx>=0; idx--) {
#else
for (idx=0; idx<paramidx; idx++) {
#endif
if ((ps[idx].type=='i' || ps[idx].type=='u' || ps[idx].type=='f') && ps[idx].range==1) {
switch (ps[idx].size) {
case 8:
push((unsigned char)(ps[idx].v.val & 0xff));
break;
case 16:
push((unsigned short)(ps[idx].v.val & 0xffff));
break;
default:
push(ps[idx].v.val);
} /* switch */
} else {
push(ps[idx].v.ptr);
} /* if */
} /* for */
/* call the function; all parameters are already pushed to the stack (the
* function should remove the parameters from the stack)
*/
result=LibFunc();
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
/* use libffi (foreign function interface) */
for (idx = 0; idx < paramidx; idx++) {
/* copy parameter types */
switch (ps[idx].type) {
case 'i': /* signed integer */
assert(ps[idx].range==1);
switch (ps[idx].size) {
case 8:
ptypes[idx] = &ffi_type_sint8;
break;
case 16:
ptypes[idx] = &ffi_type_sint16;
break;
default:
ptypes[idx] = &ffi_type_sint32;
} /* switch */
break;
case 'u': /* unsigned integer */
assert(ps[idx].range==1);
switch (ps[idx].size) {
case 8:
ptypes[idx] = &ffi_type_uint8;
break;
case 16:
ptypes[idx] = &ffi_type_uint16;
break;
default:
ptypes[idx] = &ffi_type_uint32;
} /* switch */
break;
case 'f': /* floating point */
assert(ps[idx].range==1);
ptypes[idx] = &ffi_type_float;
break;
default: /* strings, arrays, fields passed by reference */
ptypes[idx] = &ffi_type_pointer;
break;
/* switch */
} /* if */
/* copy pointer to parameter values */
pvalues[idx] = &ps[idx].v;
} /* for */
ffi_prep_cif(&cif, FFI_DEFAULT_ABI, paramidx, &ffi_type_slong, ptypes);
ffi_call(&cif, FFI_FN(LibFunc), (void*)&result, pvalues);
#endif
/* store return values and free allocated memory */
for (idx=0; idx<paramidx; idx++) {
switch (ps[idx].type) {
case 'p':
case 's':
free(ps[idx].v.ptr);
break;
case 'p' | BYREF:
case 's' | BYREF:
amx_GetAddr(amx,params[idx+4],&cptr);
amx_SetString(cptr,(char *)ps[idx].v.ptr,ps[idx].type==('p'|BYREF),sizeof(TCHAR)>1,UNLIMITED);
free(ps[idx].v.ptr);
break;
case 'i':
case 'u':
case 'f':
assert(ps[idx].range==1);
break;
case 'i' | BYREF:
case 'u' | BYREF:
case 'f' | BYREF:
amx_GetAddr(amx,params[idx+4],&cptr);
if (ps[idx].range==1) {
/* modify directly in the AMX (no memory block was allocated */
switch (ps[idx].size) {
case 8:
*cptr= (ps[idx].type==('i' | BYREF)) ? (long)((signed char)*cptr) : (*cptr & 0xff);
break;
case 16:
*cptr= (ps[idx].type==('i' | BYREF)) ? (long)((short)*cptr) : (*cptr & 0xffff);
break;
} /* switch */
} else {
int i;
for (i=0; i<ps[idx].range; i++) {
switch (ps[idx].size) {
case 8:
*cptr= (ps[idx].type==('i' | BYREF)) ? ((signed char*)ps[idx].v.ptr)[i] : ((unsigned char*)ps[idx].v.ptr)[i];
break;
case 16:
*cptr= (ps[idx].type==('i' | BYREF)) ? ((short*)ps[idx].v.ptr)[i] : ((unsigned short*)ps[idx].v.ptr)[i];
break;
default:
*cptr= (ps[idx].type==('i' | BYREF)) ? ((long*)ps[idx].v.ptr)[i] : ((unsigned long*)ps[idx].v.ptr)[i];
} /* switch */
} /* for */
free((char *)ps[idx].v.ptr);
} /* if */
break;
default:
assert(0);
} /* switch */
} /* for */
return result;
}
/* bool: libfree(const libname[]="")
* When the name is an empty string, this function frees all libraries (for this
* abstract machine). The name comparison is case sensitive.
* Returns true if one or more libraries were freed.
*/
static cell AMX_NATIVE_CALL n_libfree(AMX *amx, const cell *params)
{
const TCHAR *libname;
amx_StrParam(amx,params[1],libname);
return freelib(&ModRoot,amx,libname) > 0;
}
static void closepipe(void)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
if (newstdin!=NULL) {
CloseHandle(newstdin);
newstdin=NULL;
} /* if */
if (newstdout!=NULL) {
CloseHandle(newstdout);
newstdout=NULL;
} /* if */
if (read_stdout!=NULL) {
CloseHandle(read_stdout);
read_stdout=NULL;
} /* if */
if (write_stdin!=NULL) {
CloseHandle(write_stdin);
write_stdin=NULL;
} /* if */
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
if (pipe_to[0]>=0) {
close(pipe_to[0]);
pipe_to[0]=-1;
} /* if */
if (pipe_to[1]>=0) {
close(pipe_to[1]);
pipe_to[1]=-1;
} /* if */
if (pipe_from[0]>=0) {
close(pipe_from[0]);
pipe_from[0]=-1;
} /* if */
if (pipe_from[1]>=0) {
close(pipe_from[1]);
pipe_from[1]=-1;
} /* if */
#endif
}
/* PID: procexec(const commandline[])
* Executes a program. Returns an "id" representing the new process (or 0 on
* failure).
*/
static cell AMX_NATIVE_CALL n_procexec(AMX *amx, const cell *params)
{
TCHAR *pgmname;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
BOOL IsWinNT;
OSVERSIONINFO VerInfo;
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
PROCESS_INFORMATION pi;
#elif defined _Windows
HINSTANCE hinst;
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
pid_t pid;
#endif
amx_StrParam(amx,params[1],pgmname);
#if defined __WIN32__ || defined _WIN32 || defined WIN32
/* most of this code comes from a "Borland Network" article, combined
* with some knowledge gained from a CodeProject article
*/
closepipe();
VerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
GetVersionEx(&VerInfo);
IsWinNT = VerInfo.dwPlatformId==VER_PLATFORM_WIN32_NT;
if (IsWinNT) { //initialize security descriptor (Windows NT)
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
sa.lpSecurityDescriptor = &sd;
} else {
sa.lpSecurityDescriptor = NULL;
} /* if */
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; //allow inheritable handles
if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) { //create stdin pipe
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) { //create stdout pipe
closepipe();
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
GetStartupInfo(&si); //set startupinfo for the spawned process
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
si.hStdOutput = newstdout;
si.hStdError = newstdout; //set the new handles for the child process
si.hStdInput = newstdin;
/* spawn the child process */
if (!CreateProcess(NULL,(TCHAR*)pgmname,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
closepipe();
return 0;
} /* if */
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Sleep(100);
return pi.dwProcessId;
#elif defined _Windows
hinst=WinExec(pgmname,SW_SHOW);
if (hinst<=32)
hinst=0;
return (cell)hinst;
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
/* set up communication pipes first */
closepipe();
if (pipe(pipe_to)!=0 || pipe(pipe_from)!=0) {
closepipe();
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
/* attempt to fork */
if ((pid=fork())<0) {
closepipe();
amx_RaiseError(amx, AMX_ERR_NATIVE);
return 0;
} /* if */
if (pid==0) {
/* this is the child process */
#define MAX_ARGS 10
TCHAR *args[MAX_ARGS];
int i;
dup2(pipe_to[0],STDIN_FILENO); /* replace stdin with the in side of the pipe */
dup2(pipe_from[1],STDOUT_FILENO); /* replace stdout with the out side of the pipe */
close(pipe_to[0]); /* the pipes are no longer needed */
close(pipe_to[1]);
close(pipe_from[0]);
close(pipe_from[1]);
pipe_to[0]=-1;
pipe_to[1]=-1;
pipe_from[0]=-1;
pipe_from[1]=-1;
/* split off the option(s) */
assert(MAX_ARGS>=2); /* args[0] is reserved */
memset(args,0,MAX_ARGS*sizeof(TCHAR*));
args[0]=pgmname;
for (i=1; i<MAX_ARGS && args[i-1]!=NULL; i++) {
if ((args[i]=strchr(args[i-1],' '))!=NULL) {
args[i][0]='\0';
args[i]+=1;
} /* if */
} /* for */
/* replace the child fork with a new process */
if(execvp(pgmname,args)<0)
return 0;
} else {
close(pipe_to[0]); /* close unused pipes */
close(pipe_from[1]);
pipe_to[0]=-1;
pipe_from[1]=-1;
} /* if */
return pid;
#else
return (system(pgmname)==0);
#endif
}
/* bool: procwrite(const line[], bool:appendlf=false)
*/
static cell AMX_NATIVE_CALL n_procwrite(AMX *amx, const cell *params)
{
const TCHAR *line;
unsigned long num;
amx_StrParam(amx,params[1],line);
#if defined __WIN32__ || defined _WIN32 || defined WIN32
if (write_stdin==NULL)
return 0;
WriteFile(write_stdin,line,_tcslen(line),&num,NULL); //send it to stdin
if (params[2])
WriteFile(write_stdin,__T("\n"),1,&num,NULL);
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
if (pipe_to[1]<0)
return 0;
write(pipe_to[1],line,_tcslen(line));
if (params[2])
write(pipe_to[1],__T("\n"),1);
#endif
return 1;
}
/* bool: procread(line[], size=sizeof line, bool:striplf=false, bool:packed=false)
*/
static cell AMX_NATIVE_CALL n_procread(AMX *amx, const cell *params)
{
TCHAR line[128];
cell *cptr;
unsigned long num;
int index;
index=0;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
if (read_stdout==NULL)
return 0;
do {
if (!ReadFile(read_stdout,line+index,1,&num,NULL))
break;
index++;
} while (index<sizeof(line)/sizeof(line[0])-1 && line[index-1]!=__T('\n'));
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
if (pipe_from[0]<0)
return 0;
do {
if (read(pipe_from[0],line+index,1)<0)
break;
index++;
} while (index<sizeof(line)/sizeof(line[0])-1 && line[index-1]!=__T('\n'));
#endif
if (params[3])
while (index>0 && (line[index-1]==__T('\r') || line[index-1]==__T('\n')))
index--;
line[index]=__T('\0');
amx_GetAddr(amx,params[1],&cptr);
amx_SetString(cptr,line,params[4],sizeof(TCHAR)>1,params[2]);
return 1;
}
/* procwait(PID:pid)
* Waits until the process has terminated.
*/
static cell AMX_NATIVE_CALL n_procwait(AMX *amx, const cell *params)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
HANDLE hProcess;
DWORD exitcode;
#endif
(void)amx;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)params[1]);
if (hProcess != NULL) {
while (GetExitCodeProcess(hProcess,&exitcode) && exitcode==STILL_ACTIVE)
Sleep(100);
CloseHandle(hProcess);
} /* if */
#elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
waitpid((pid_t)params[1],NULL,WNOHANG);
#endif
return 0;
}
#if defined __cplusplus
extern "C"
#endif
AMX_NATIVE_INFO ffi_Natives[] = {
{ "libcall", n_libcall },
{ "libfree", n_libfree },
{ "procexec", n_procexec },
{ "procread", n_procread },
{ "procwrite", n_procwrite },
{ "procwait", n_procwait },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_ProcessInit(AMX *amx)
{
return amx_Register(amx, ffi_Natives, -1);
}
int AMXEXPORT amx_ProcessCleanup(AMX *amx)
{
freelib(&ModRoot, amx, NULL);
closepipe();
return AMX_ERR_NONE;
}

883
SOURCE/amx/amxstring.c Normal file
View File

@ -0,0 +1,883 @@
/* String functions for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2005-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxstring.c 3656 2006-10-24 07:20:26Z thiadmer $
*/
#include <limits.h>
#include <string.h>
#include <assert.h>
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
#include <malloc.h>
#endif
#include "osdefs.h"
#include "amx.h"
#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
#include <windows.h>
#endif
#define MAX_FORMATSTR 256
#define CHARBITS (8*sizeof(char))
#if defined _UNICODE
# include <tchar.h>
#elif !defined __T
typedef char TCHAR;
# define __T(string) string
# define _tcscat strcat
# define _tcschr strchr
# define _tcscpy strcpy
# define _tcslen strlen
#endif
#include "amxcons.h"
#if !defined isdigit
# define isdigit(c) ((unsigned)((c)-'0')<10u)
#endif
/* dest the destination buffer; the buffer must point to the start of a cell
* source the source buffer, this must be aligned to a cell edge
* len the number of characters (bytes) to copy
* offs the offset in dest, in characters (bytes)
*/
static int amx_StrPack(cell *dest,cell *source,int len,int offs)
{
int i;
if ((ucell)*source>UNPACKEDMAX && offs%sizeof(cell)==0) {
/* source string is already packed and the destination is cell-aligned */
unsigned char* pdest=(unsigned char*)dest+offs;
i=(len+sizeof(cell)-1)/sizeof(cell);
memmove(pdest,source,i*sizeof(cell));
/* zero-terminate */
#if BYTE_ORDER==BIG_ENDIAN
pdest+=len;
for (i=len; i==len || i%sizeof(cell)!=0; i++)
*pdest++='\0';
#else
i=(len/sizeof(cell))*sizeof(cell);
pdest+=i;
len=(len==i) ? sizeof(cell) : sizeof(cell)-(len-i);
assert(len>0 && len<=sizeof(cell));
for (i=0; i<len; i++)
*pdest++='\0';
#endif
} else if ((ucell)*source>UNPACKEDMAX) {
/* source string is packed, destination is not aligned */
cell mask,c;
dest+=offs/sizeof(cell); /* increment whole number of cells */
offs%=sizeof(cell); /* get remainder */
mask=(~(ucell)0) >> (offs*CHARBITS);
c=*dest & ~mask;
for (i=0; i<len+offs+1; i+=sizeof(cell)) {
*dest=c | ((*source >> (offs*CHARBITS)) & mask);
c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask;
dest++;
source++;
} /* for */
/* set the zero byte in the last cell */
mask=(~(ucell)0) >> (((offs+len)%sizeof(cell))*CHARBITS);
*(dest-1) &= ~mask;
} else {
/* source string is unpacked: pack string, from top-down */
cell c=0;
if (offs!=0) {
/* get the last cell in "dest" and mask of the characters that must be changed */
cell mask;
dest+=offs/sizeof(cell); /* increment whole number of cells */
offs%=sizeof(cell); /* get remainder */
mask=(~(ucell)0) >> (offs*CHARBITS);
c=(*dest & ~mask) >> ((sizeof(cell)-offs)*CHARBITS);
} /* if */
/* for proper alignement, add the offset to both the starting and the ending
* criterion (so that the number of iterations stays the same)
*/
assert(offs>=0 && offs<sizeof(cell));
for (i=offs; i<len+offs; i++) {
c=(c<<CHARBITS) | (*source++ & 0xff);
if (i%sizeof(cell)==sizeof(cell)-1) {
*dest++=c;
c=0;
} /* if */
} /* for */
if (i%sizeof(cell) != 0) /* store remaining packed characters */
*dest=c << (sizeof(cell)-i%sizeof(cell))*CHARBITS;
else
*dest=0; /* store full cell of zeros */
} /* if */
return AMX_ERR_NONE;
}
static int amx_StrUnpack(cell *dest,cell *source,int len)
{
/* len excludes the terminating '\0' byte */
if ((ucell)*source>UNPACKEDMAX) {
/* unpack string, from bottom up (so string can be unpacked in place) */
cell c;
int i;
for (i=len-1; i>=0; i--) {
c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS;
dest[i]=c & UCHAR_MAX;
} /* for */
dest[len]=0; /* zero-terminate */
} else {
/* source string is already unpacked */
while (len-->0)
*dest++=*source++;
*dest=0;
} /* if */
return AMX_ERR_NONE;
}
static unsigned char *packedptr(cell *string,int index)
{
unsigned char *ptr=(unsigned char *)(string+index/sizeof(cell));
#if BYTE_ORDER==BIG_ENDIAN
ptr+=index & (sizeof(cell)-1);
#else
ptr+=(sizeof(cell)-1) - (index & (sizeof(cell)-1));
#endif
return ptr;
}
static cell extractchar(cell *string,int index,int mklower)
{
cell c;
if ((ucell)*string>UNPACKEDMAX)
c=*packedptr(string,index);
else
c=string[index];
if (mklower) {
#if defined __WIN32__ || defined _WIN32 || defined WIN32
c=(cell)CharLower((LPTSTR)c);
#elif defined _Windows
c=(cell)AnsiLower((LPSTR)c);
#else
if ((unsigned int)(c-'A')<26u)
c+='a'-'A';
#endif
} /* if */
return c;
}
static int verify_addr(AMX *amx,cell addr)
{
int err;
cell *cdest;
err=amx_GetAddr(amx,addr,&cdest);
if (err!=AMX_ERR_NONE)
amx_RaiseError(amx,err);
return err;
}
/* strlen(const string[])
*/
static cell AMX_NATIVE_CALL n_strlen(AMX *amx,const cell *params)
{
cell *cptr;
int len = 0;
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
amx_StrLen(cptr,&len);
return len;
}
/* strpack(dest[], const source[], maxlength=sizeof dest)
*/
static cell AMX_NATIVE_CALL n_strpack(AMX *amx,const cell *params)
{
cell *cdest,*csrc;
int len,needed,err;
size_t lastaddr;
/* calculate number of cells needed for (packed) destination */
amx_GetAddr(amx,params[2],&csrc);
amx_StrLen(csrc,&len);
if ((unsigned)len>params[3]*sizeof(cell)-1)
len=params[3]*sizeof(cell)-1;
needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */
assert(needed>0);
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
amx_GetAddr(amx,params[1],&cdest);
err=amx_StrPack(cdest,csrc,len,0);
if (err!=AMX_ERR_NONE)
return amx_RaiseError(amx,err);
return len;
}
/* strunpack(dest[], const source[], maxlength=sizeof dest)
*/
static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,const cell *params)
{
cell *cdest,*csrc;
int len,err;
size_t lastaddr;
/* calculate number of cells needed for (unpacked) destination */
amx_GetAddr(amx,params[2],&csrc);
amx_StrLen(csrc,&len);
assert(len>=0);
if (len>=params[3])
len=params[3]-1;
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1);
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
amx_GetAddr(amx,params[1],&cdest);
err=amx_StrUnpack(cdest,csrc,len);
if (err!=AMX_ERR_NONE)
return amx_RaiseError(amx,err);
return len;
}
/* strcat(dest[], const source[], maxlength=sizeof dest)
* packed/unpacked attribute is taken from dest[], or from source[] if dest[]
* is an empty string.
*/
static cell AMX_NATIVE_CALL n_strcat(AMX *amx,const cell *params)
{
cell *cdest,*csrc;
int len,len2,needed;
int packed,err;
size_t lastaddr;
/* calculate number of cells needed for (packed) destination */
amx_GetAddr(amx,params[2],&csrc);
amx_GetAddr(amx,params[1],&cdest);
amx_StrLen(csrc,&len);
amx_StrLen(cdest,&len2);
packed=(*cdest==0) ? ((ucell)*csrc>UNPACKEDMAX) : ((ucell)*cdest>UNPACKEDMAX);
if (packed) {
if ((unsigned)(len+len2)>params[3]*sizeof(cell)-1)
len=params[3]*sizeof(cell)-len2-1;
needed=(len+len2+sizeof(cell))/sizeof(cell); /* # of cells needed */
assert(needed>0);
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
} else {
if (len+len2>params[3]-1)
len=params[3]-len2-1;
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+len2+1)-1);
} /* if */
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
if (packed) {
err=amx_StrPack(cdest,csrc,len,len2);
} else {
/* destination string must either be unpacked, or empty */
assert((ucell)*cdest<=UNPACKEDMAX || len2==0);
err=amx_StrUnpack(cdest+len2,csrc,len);
} /* if */
if (err!=AMX_ERR_NONE)
return amx_RaiseError(amx,err);
return len;
}
static int compare(cell *cstr1,cell *cstr2,int ignorecase,int length,int offs1)
{
int index;
cell c1=0,c2=0;
for (index=0; index<length; index++) {
c1=extractchar(cstr1,index+offs1,ignorecase);
c2=extractchar(cstr2,index,ignorecase);
assert(c1!=0 && c2!=0); /* string lengths are already checked, so zero-bytes should not occur */
if (c1!=c2)
break;
} /* for */
if (c1<c2)
return -1;
if (c1>c2)
return 1;
return 0;
}
/* strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax)
*/
static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,const cell *params)
{
cell *cstr1,*cstr2;
int len1,len2,len;
cell result;
amx_GetAddr(amx,params[1],&cstr1);
amx_GetAddr(amx,params[2],&cstr2);
/* get the maximum length to compare */
amx_StrLen(cstr1,&len1);
amx_StrLen(cstr2,&len2);
len=len1;
if (len>len2)
len=len2;
if (len>params[4])
len=params[4];
if (len==0)
return 0;
result=compare(cstr1,cstr2,params[3],len,0);
if (result==0 && len!=params[4])
result=len1-len2;
return result;
}
/* strfind(const string[], const sub[], bool:ignorecase=false, offset=0)
*/
static cell AMX_NATIVE_CALL n_strfind(AMX *amx,const cell *params)
{
cell *cstr,*csub;
int lenstr,lensub,offs;
cell c,f;
amx_GetAddr(amx,params[1],&cstr);
amx_GetAddr(amx,params[2],&csub);
/* get the maximum length to compare */
amx_StrLen(cstr,&lenstr);
amx_StrLen(csub,&lensub);
if (lensub==0)
return -1;
/* get the start character of the substring, for quicker searching */
f=extractchar(csub,0,params[3]);
assert(f!=0); /* string length is already checked */
for (offs=(int)params[4]; offs+lensub<=lenstr; offs++) {
/* find the initial character */
c=extractchar(csub,0,params[3]);
assert(c!=0); /* string length is already checked */
if (c!=f)
continue;
if (compare(cstr,csub,params[3],lensub,offs)==0)
return offs;
} /* for */
return -1;
}
/* strmid(dest[], const source[], start, end, maxlength=sizeof dest)
* packed/unpacked attribute is taken from source[]
*/
static cell AMX_NATIVE_CALL n_strmid(AMX *amx,const cell *params)
{
cell *cdest,*csrc;
int len,needed,err;
int soffs,doffs;
size_t lastaddr;
unsigned char *ptr;
unsigned char c;
int start=params[3];
int end=params[4];
/* calculate number of cells needed for (packed) destination */
amx_GetAddr(amx,params[2],&csrc);
amx_GetAddr(amx,params[1],&cdest);
amx_StrLen(csrc,&len);
/* clamp the start/end parameters */
if (start<0)
start=0;
else if (start>len)
start=len;
if (end<start)
end=start;
else if (end>len)
end=len;
len=end-start;
if ((ucell)*csrc>UNPACKEDMAX) {
if ((unsigned)len>params[5]*sizeof(cell)-1)
len=params[5]*sizeof(cell)-1;
needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */
assert(needed>0);
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
} else {
if (len>params[5]-1)
len=params[5]-1;
lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1);
} /* if */
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
if ((ucell)*csrc>UNPACKEDMAX) {
/* first align the source to a cell boundary */
for (doffs=0,soffs=start; (soffs & (sizeof(cell)-1))!=0 && len>0; soffs++,doffs++,len--) {
ptr=packedptr(csrc,soffs);
c=*ptr;
ptr=packedptr(cdest,doffs);
*ptr=c;
} /* for */
if (len==0) {
/* nothing left to do, zero-terminate */
ptr=packedptr(cdest,doffs);
*ptr='\0';
err=AMX_ERR_NONE;
} else {
err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs);
} /* if */
} else {
err=amx_StrUnpack(cdest,csrc+start,len);
} /* if */
if (err!=AMX_ERR_NONE)
return amx_RaiseError(amx,err);
return len;
}
/* strdel(string[], start, end)
*/
static cell AMX_NATIVE_CALL n_strdel(AMX *amx,const cell *params)
{
cell *cstr;
int index,offs,length;
unsigned char *ptr;
unsigned char c;
/* calculate number of cells needed for (packed) destination */
amx_GetAddr(amx,params[1],&cstr);
amx_StrLen(cstr,&length);
index=(int)params[2];
offs=(int)params[3]-index;
if (index>=length || offs<=0)
return 0;
if (index+offs>length)
offs=length-index;
index--; /* prepare for increment in the top of the loop */
if (((ucell)*cstr>UNPACKEDMAX)) {
do {
index++;
ptr=packedptr(cstr,index+offs);
c=*ptr;
ptr=packedptr(cstr,index);
*ptr=c;
} while (c!='\0');
if (index==0)
*cstr=0;
} else {
do {
index++;
cstr[index]=cstr[index+offs];
} while (cstr[index]!=0);
} /* if */
return 1;
}
/* strins(string[], const substr[], offset, maxlength=sizeof string)
*/
static cell AMX_NATIVE_CALL n_strins(AMX *amx,const cell *params)
{
cell *cstr,*csub;
int index,lenstr,lensub,count;
int needed;
size_t lastaddr;
unsigned char *ptr;
cell c;
/* calculate number of cells needed for (packed) destination */
amx_GetAddr(amx,params[1],&cstr);
amx_GetAddr(amx,params[2],&csub);
amx_StrLen(cstr,&lenstr);
amx_StrLen(csub,&lensub);
index=(int)params[3];
if (index>lenstr)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
if (((ucell)*cstr>UNPACKEDMAX)) {
needed=(lenstr+lensub+sizeof(cell))/sizeof(cell); /* # of cells needed */
assert(needed>0);
lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1);
} else {
lastaddr=(size_t)(params[1]+sizeof(cell)*(lenstr+lensub+1)-1);
} /* if */
if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE)
return amx_RaiseError(amx,AMX_ERR_NATIVE);
if (*cstr==0) {
/* current string is empty (and the insertion point is zero), just make a copy */
assert(index==0);
if ((ucell)*csub>UNPACKEDMAX)
amx_StrPack(cstr,csub,lensub,0);
else
amx_StrUnpack(cstr,csub,lensub);
return 1;
} /* if */
if (((ucell)*cstr>UNPACKEDMAX)) {
/* make room for the new characters */
for (count=lenstr+lensub; count>index; count--) {
ptr=packedptr(cstr,count-lensub);
c=*ptr;
ptr=packedptr(cstr,count);
*ptr=(unsigned char)c;
} /* for */
/* copy in the new characters */
for (count=0; count<lensub; count++) {
c=extractchar(csub,count,0);
ptr=packedptr(cstr,index+count);
*ptr=(unsigned char)c;
} /* for */
} else {
/* make room for the new characters */
for (count=lenstr+lensub; count>index; count--)
cstr[count]=cstr[count-lensub];
/* copy in the new characters */
for (count=0; count<lensub; count++) {
c=extractchar(csub,count,0);
cstr[index+count]=c;
} /* for */
} /* if */
return 1;
}
/* strval(const string[], index=0)
*/
static cell AMX_NATIVE_CALL n_strval(AMX *amx,const cell *params)
{
TCHAR str[50],*ptr;
cell *cstr,result;
int len,negate=0;
int offset=0;
/* get parameters */
amx_GetAddr(amx,params[1],&cstr);
amx_StrLen(cstr,&len);
if ((unsigned)params[0]>=2*sizeof(cell))
offset=params[2];
if (offset<0)
offset=0;
else if (offset>=len)
offset=len-1;
/* skip a number of cells */
while (offset>=(int)sizeof(cell)) {
cstr++;
offset-=sizeof(cell);
len-=sizeof(cell);
} /* while */
if (len>=(int)sizeof str) {
amx_RaiseError(amx,AMX_ERR_NATIVE);
return 0;
} /* if */
amx_GetString(str,cstr,sizeof(TCHAR)>1,sizeof str);
assert(offset<(int)sizeof(cell) && offset>=0);
ptr=str+offset;
result=0;
while (*ptr!='\0' && *ptr<=' ')
ptr++; /* skip whitespace */
if (*ptr=='-') { /* handle sign */
negate=1;
ptr++;
} else if (*ptr=='+') {
ptr++;
} /* if */
while (isdigit(*ptr)) {
result=result*10 + (*ptr-'0');
ptr++;
} /* while */
if (negate)
result=-result;
return result;
}
/* valstr(dest[], value, bool:pack=false) */
static cell AMX_NATIVE_CALL n_valstr(AMX *amx,const cell *params)
{
TCHAR str[50];
cell value,mult;
cell *cstr;
int len,result,negate=0;
/* find out how many digits are needed */
mult=10;
len=1;
value=params[2];
if (value<0) {
negate=1;
len++;
value=-value;
} /* if */
while (value>=mult) {
len++;
mult*=10;
} /* while */
/* put in the string */
result=len;
str[len--]='\0';
while (len>=negate) {
str[len--]=(char)((value % 10)+'0');
value/=10;
} /* while */
if (negate)
str[0]='-';
amx_GetAddr(amx,params[1],&cstr);
amx_SetString(cstr,str,params[3],sizeof(TCHAR)>1,UNLIMITED);
return result;
}
/* ispacked(const string[]) */
static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,const cell *params)
{
cell *cstr;
amx_GetAddr(amx,params[1],&cstr);
return *cstr>=UNPACKEDMAX;
}
/* single character decode and encode */
#define BITMASK 0x3f
#define DEC(c) (((c) - ' ') & BITMASK)
#define ENC(c) (char)(((c) & BITMASK) == 0 ? 0x60 : ((c) & BITMASK) + ' ')
static int uudecode(unsigned char *target, char *source)
{
int len, retval;
len = DEC(*source++);
retval = len;
while (len > 0) {
if (len-- > 0)
*target++ = (unsigned char)(( DEC(source[0]) << 2 ) | ( DEC(source[1]) >> 4 ));
if (len-- > 0)
*target++ = (unsigned char)(( DEC(source[1]) << 4 ) | ( DEC(source[2]) >> 2 ));
if (len-- > 0)
*target++ = (unsigned char)(( DEC(source[2]) << 6 ) | DEC(source[3]) );
source += 4;
} /* while */
return retval;
}
static int uuencode(char *target, unsigned char *source, int length)
{
int split[4]={0,0,0,0};
if (length > BITMASK)
return 0; /* can encode up to 64 bytes */
*target++ = ENC(length);
while (length > 0) {
split[0] = source[0] >> 2; /* split first byte to char. 0 & 1 */
split[1] = source[0] << 4;
if (length > 1) {
split[1] |= source[1] >> 4; /* split 2nd byte to char. 1 & 2 */
split[2] = source[1] << 2;
if (length > 2) {
split[2] |= source[2] >> 6; /* split 3th byte to char. 2 & 3 */
split[3] = source[2];
} /* if */
} /* if */
*target++ = ENC(split[0]);
*target++ = ENC(split[1]);
if (length > 1)
*target++ = ENC(split[2]);
if (length > 2)
*target++ = ENC(split[3]);
source += 3;
length -= 3;
} /* while */
*target = '\0'; /* end string */
return 1;
}
/* uudecode(dest[], const source[], maxlength=sizeof dest)
* Returns the number of bytes (not cells) decoded; if the dest buffer is
* too small, not all bytes are stored.
* Always creates a (packed) array (not a string; the array is not
* zero-terminated).
* A buffer may be decoded "in-place"; the destination size is always smaller
* than the source size.
* Endian issues (for multi-byte values in the data stream) are not handled.
*/
static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,const cell *params)
{
cell *cstr;
unsigned char dst[BITMASK+2];
char src[BITMASK+BITMASK/3+2];
int len;
size_t size;
/* get the source */
amx_GetAddr(amx,params[2],&cstr);
amx_GetString(src,cstr,0,sizeof src);
/* decode */
len=uudecode(dst,src);
/* store */
amx_GetAddr(amx,params[1],&cstr);
size=len;
if (size>params[3]*sizeof(cell))
size=params[3]*sizeof(cell);
memcpy(cstr,dst,size);
return len;
}
/* uuencode(dest[], const source[], numbytes, maxlength=sizeof dest)
* Returns the number of characters encoded, excluding the zero string
* terminator; if the dest buffer is too small, not all bytes are stored.
* Always creates a packed string. This string has a newline character at the
* end. A buffer may be encoded "in-place" if the destination is large enough.
* Endian issues (for multi-byte values in the data stream) are not handled.
*/
static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,const cell *params)
{
cell *cstr;
unsigned char src[BITMASK+2];
char dst[BITMASK+BITMASK/3+2];
/* get the source */
amx_GetAddr(amx,params[2],&cstr);
/* encode (and check for errors) */
if (uuencode(dst,src,params[3])) {
if (params[4]>0) {
amx_GetAddr(amx,params[1],&cstr);
*cstr=0;
} /* if */
return 0;
} /* if */
/* always add a \n */
assert(strlen(dst)+1<sizeof dst);
strcat(dst,"\n");
/* store */
amx_GetAddr(amx,params[1],&cstr);
amx_SetString(cstr,dst,1,0,params[4]);
return (((params[3]+2)/3) << 2)+2;
}
/* memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest)
* This function can align byte strings in cell arrays, or concatenate two
* byte strings in two arrays. The parameter "index" is a byte offset; "numbytes"
* is the number of bytes to copy. Parameter "maxlength", however, is in cells.
* This function allows copying in-place, for aligning memory buffers.
* Endian issues (for multi-byte values in the data stream) are not handled.
*/
static cell AMX_NATIVE_CALL n_memcpy(AMX *amx,const cell *params)
{
cell *cdest,*csrc;
unsigned char *pdest,*psrc;
if (params[3]<0 || params[4]<0 || (params[3]+params[4])>params[5]*(int)sizeof(cell))
return 0;
amx_GetAddr(amx,params[1],&cdest);
amx_GetAddr(amx,params[2],&csrc);
pdest=(unsigned char*)cdest+params[3];
psrc=(unsigned char*)csrc;
memmove(pdest,psrc,params[4]);
return 1;
}
#if !defined AMX_NOSTRFMT
static int str_putstr(void *dest,const TCHAR *str)
{
if (_tcslen((TCHAR*)dest)+_tcslen(str)<MAX_FORMATSTR)
_tcscat((TCHAR*)dest,str);
return 0;
}
static int str_putchar(void *dest,TCHAR ch)
{
int len=_tcslen((TCHAR*)dest);
if (len<MAX_FORMATSTR-1) {
((TCHAR*)dest)[len]=ch;
((TCHAR*)dest)[len+1]='\0';
} /* if */
return 0;
}
#endif
/* strformat(dest[], size=sizeof dest, bool:pack=false, const format[], {Fixed,_}:...)
*/
static cell AMX_NATIVE_CALL n_strformat(AMX *amx,const cell *params)
{
#if defined AMX_NOSTRFMT
(void)amx;
(void)params;
return 0;
#else
cell *cstr;
AMX_FMTINFO info;
TCHAR output[MAX_FORMATSTR];
memset(&info,0,sizeof info);
info.params=params+5;
info.numparams=(int)(params[0]/sizeof(cell))-4;
info.skip=0;
info.length=MAX_FORMATSTR; /* max. length of the string */
info.f_putstr=str_putstr;
info.f_putchar=str_putchar;
info.user=output;
output[0] = __T('\0');
amx_GetAddr(amx,params[4],&cstr);
amx_printstring(amx,cstr,&info);
/* store the output string */
amx_GetAddr(amx,params[1],&cstr);
amx_SetString(cstr,(char*)output,(int)params[3],sizeof(TCHAR)>1,(int)params[2]);
return 1;
#endif
}
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO string_Natives[] = {
{ "ispacked", n_ispacked },
{ "memcpy", n_memcpy },
{ "strcat", n_strcat },
{ "strcmp", n_strcmp },
{ "strdel", n_strdel },
{ "strfind", n_strfind },
{ "strformat", n_strformat },
{ "strins", n_strins },
{ "strlen", n_strlen },
{ "strmid", n_strmid },
{ "strpack", n_strpack },
{ "strunpack", n_strunpack },
{ "strval", n_strval },
{ "uudecode", n_uudecode },
{ "uuencode", n_uuencode },
{ "valstr", n_valstr },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_StringInit(AMX *amx)
{
return amx_Register(amx, string_Natives, -1);
}
int AMXEXPORT amx_StringCleanup(AMX *amx)
{
(void)amx;
return AMX_ERR_NONE;
}

6
SOURCE/amx/amxstring.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxString
DESCRIPTION 'Pawn AMX: string manipulation routines'
EXPORTS
amx_StringInit
amx_StringCleanup

54
SOURCE/amx/amxstring.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 1
#define BUILD 0
#define VERSIONSTR "1.1.0\0"
#define VERSIONNAME "amxString.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: String manipulation routines\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxString\0"
#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

350
SOURCE/amx/amxtime.c Normal file
View File

@ -0,0 +1,350 @@
/* Date/time module for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 2001-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: amxtime.c 3649 2006-10-12 13:13:57Z thiadmer $
*/
#include <time.h>
#include <assert.h>
#include "amx.h"
#if defined __WIN32__ || defined _WIN32
#include <windows.h>
#include <mmsystem.h>
#endif
#define CELLMIN (-1 << (8*sizeof(cell) - 1))
#if !defined CLOCKS_PER_SEC
#define CLOCKS_PER_SEC CLK_TCK
#endif
#if defined __WIN32__ || defined _WIN32 || defined WIN32
static int timerset = 0;
/* timeGetTime() is more accurate on WindowsNT if timeBeginPeriod(1) is set */
#define INIT_TIMER() \
if (!timerset) { \
timeBeginPeriod(1); \
timerset=1; \
}
#else
#define INIT_TIMER()
#endif
static unsigned long timestamp;
static unsigned long timelimit;
static int timerepeat;
static const unsigned char monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int wrap(int value, int min, int max)
{
if (value<min)
value=max;
else if (value>max)
value=min;
return value;
}
static unsigned long gettimestamp(void)
{
unsigned long value;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
value=timeGetTime(); /* this value is already in milliseconds */
#else
value=(cell)clock();
#if CLOCKS_PER_SEC!=1000
/* convert to milliseconds */
value=(cell)((1000L * (value+CLOCKS_PER_SEC/2)) / CLOCKS_PER_SEC);
#endif
#endif
return value;
}
/* settime(hour, minute, second)
* Always returns 0
*/
static cell AMX_NATIVE_CALL n_settime(AMX *amx, const cell *params)
{
#if defined __WIN32__ || defined _WIN32 || defined WIN32
SYSTEMTIME systim;
GetLocalTime(&systim);
if (params[1]!=CELLMIN)
systim.wHour=(WORD)wrap((int)params[1],0,23);
if (params[2]!=CELLMIN)
systim.wMinute=(WORD)wrap((int)params[2],0,59);
if (params[3]!=CELLMIN)
systim.wSecond=(WORD)wrap((int)params[3],0,59);
SetLocalTime(&systim);
#else
/* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you
* must have "root" permission to call stime()
*/
time_t sec1970;
struct tm gtm;
(void)amx;
time(&sec1970);
gtm=*localtime(&sec1970);
if (params[1]!=CELLMIN)
gtm.tm_hour=wrap((int)params[1],0,23);
if (params[2]!=CELLMIN)
gtm.tm_min=wrap((int)params[2],0,59);
if (params[3]!=CELLMIN)
gtm.tm_sec=wrap((int)params[3],0,59);
sec1970=mktime(&gtm);
stime(&sec1970);
#endif
(void)amx;
return 0;
}
/* gettime(&hour, &minute, &second)
* The return value is the number of seconds since 1 January 1970 (Unix system
* time).
*/
static cell AMX_NATIVE_CALL n_gettime(AMX *amx, const cell *params)
{
time_t sec1970;
struct tm gtm;
cell *cptr;
assert(params[0]==(int)(3*sizeof(cell)));
time(&sec1970);
/* on DOS/Windows, the timezone is usually not set for the C run-time
* library; in that case gmtime() and localtime() return the same value
*/
gtm=*localtime(&sec1970);
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_hour;
if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_min;
if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_sec;
/* the time() function returns the number of seconds since January 1 1970
* in Universal Coordinated Time (the successor to Greenwich Mean Time)
*/
return sec1970;
}
/* setdate(year, month, day)
* Always returns 0
*/
static cell AMX_NATIVE_CALL n_setdate(AMX *amx, const cell *params)
{
int maxday;
#if defined __WIN32__ || defined _WIN32 || defined WIN32
SYSTEMTIME systim;
GetLocalTime(&systim);
if (params[1]!=CELLMIN)
systim.wYear=(WORD)wrap((int)params[1],1970,2099);
if (params[2]!=CELLMIN)
systim.wMonth=(WORD)wrap((int)params[2],1,12);
maxday=monthdays[systim.wMonth - 1];
if (systim.wMonth==2 && ((systim.wYear % 4)==0 && ((systim.wYear % 100)!=0 || (systim.wYear % 400)==0)))
maxday++;
if (params[3]!=CELLMIN)
systim.wDay=(WORD)wrap((int)params[3],1,maxday);
SetLocalTime(&systim);
#else
/* Linux/Unix (and some DOS compilers) have stime(); on Linux/Unix, you
* must have "root" permission to call stime()
*/
time_t sec1970;
struct tm gtm;
(void)amx;
time(&sec1970);
gtm=*localtime(&sec1970);
if (params[1]!=CELLMIN)
gtm.tm_year=params[1]-1900;
if (params[2]!=CELLMIN)
gtm.tm_mon=params[2]-1;
if (params[3]!=CELLMIN)
gtm.tm_mday=params[3];
sec1970=mktime(&gtm);
stime(&sec1970);
#endif
(void)amx;
return 0;
}
/* getdate(&year, &month, &day)
* The return value is the number of days since the start of the year. January
* 1 is day 1 of the year.
*/
static cell AMX_NATIVE_CALL n_getdate(AMX *amx, const cell *params)
{
time_t sec1970;
struct tm gtm;
cell *cptr;
assert(params[0]==(int)(3*sizeof(cell)));
time(&sec1970);
gtm=*localtime(&sec1970);
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_year+1900;
if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_mon+1;
if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE)
*cptr=gtm.tm_mday;
return gtm.tm_yday+1;
}
/* tickcount(&granularity)
* Returns the number of milliseconds since start-up. For a 32-bit cell, this
* count overflows after approximately 24 days of continuous operation.
*/
static cell AMX_NATIVE_CALL n_tickcount(AMX *amx, const cell *params)
{
cell *cptr;
assert(params[0]==(int)sizeof(cell));
INIT_TIMER();
#if defined __WIN32__ || defined _WIN32 || defined WIN32
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=1000; /* granularity = 1 ms */
#else
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=(cell)CLOCKS_PER_SEC; /* in Unix/Linux, this is often 100 */
#endif
return gettimestamp() & 0x7fffffff;
}
/* delay(milliseconds)
* Pauses for (at least) the requested number of milliseconds.
*/
static cell AMX_NATIVE_CALL n_delay(AMX *amx, const cell *params)
{
unsigned long stamp;
(void)amx;
assert(params[0]==(int)sizeof(cell));
INIT_TIMER();
stamp=gettimestamp();
while (gettimestamp()-stamp < (unsigned long)params[1])
/* nothing */;
return 0;
}
/* settimer(milliseconds, bool: singleshot = false)
* Sets the delay until the @timer() callback is called. The timer may either
* be single-shot or repetitive.
*/
static cell AMX_NATIVE_CALL n_settimer(AMX *amx, const cell *params)
{
(void)amx;
assert(params[0]==(int)(2*sizeof(cell)));
timestamp=gettimestamp();
timelimit=params[1];
timerepeat=(int)(params[2]==0);
return 0;
}
/* bool: gettimer(&milliseconds, bool: &singleshot = false)
* Retrieves the timer set with settimer(); returns true if a timer
* was set up, or false otherwise.
*/
static cell AMX_NATIVE_CALL n_gettimer(AMX *amx, const cell *params)
{
cell *cptr;
assert(params[0]==(int)(2*sizeof(cell)));
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=timelimit;
if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE)
*cptr=timerepeat;
return timelimit>0;
}
#if !defined AMXTIME_NOIDLE
static AMX_IDLE PrevIdle = NULL;
static int idxTimer = -1;
static int AMXAPI amx_TimeIdle(AMX *amx, int AMXAPI Exec(AMX *, cell *, int))
{
int err=0;
assert(idxTimer >= 0);
if (PrevIdle != NULL)
PrevIdle(amx, Exec);
if (timelimit>0 && (gettimestamp()-timestamp)>=timelimit) {
if (timerepeat)
timestamp+=timelimit;
else
timelimit=0; /* do not repeat single-shot timer */
err = Exec(amx, NULL, idxTimer);
while (err == AMX_ERR_SLEEP)
err = Exec(amx, NULL, AMX_EXEC_CONT);
} /* if */
return err;
}
#endif
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO time_Natives[] = {
{ "gettime", n_gettime },
{ "settime", n_settime },
{ "getdate", n_getdate },
{ "setdate", n_setdate },
{ "tickcount", n_tickcount },
{ "settimer", n_settimer },
{ "gettimer", n_gettimer },
{ "delay", n_delay },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_TimeInit(AMX *amx)
{
#if !defined AMXTIME_NOIDLE
/* see whether there is a @timer() function */
if (amx_FindPublic(amx,"@timer",&idxTimer) == AMX_ERR_NONE) {
if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE)
PrevIdle = NULL;
amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_TimeIdle);
} /* if */
#endif
return amx_Register(amx, time_Natives, -1);
}
int AMXEXPORT amx_TimeCleanup(AMX *amx)
{
(void)amx;
#if !defined AMXTIME_NOIDLE
PrevIdle = NULL;
#endif
return AMX_ERR_NONE;
}

6
SOURCE/amx/amxtime.def Normal file
View File

@ -0,0 +1,6 @@
NAME amxTime
DESCRIPTION 'Pawn AMX: time routines'
EXPORTS
amx_TimeInit
amx_TimeCleanup

54
SOURCE/amx/amxtime.rc Normal file
View File

@ -0,0 +1,54 @@
#include <windows.h>
#if defined WIN32 || defined _WIN32 || defined __WIN32__
# include <winver.h>
#else
# include <ver.h>
#endif
/* Version information
*
* All strings MUST have an explicit \0. See the Windows SDK documentation
* for details on version information and the VERSIONINFO structure.
*/
#define VERSION 1
#define REVISION 1
#define BUILD 0
#define VERSIONSTR "1.2.0\0"
#define VERSIONNAME "amxTime.dll\0"
#define VERSIONDESCRIPTION "Pawn AMX: time routines\0"
#define VERSIONCOMPANYNAME "ITB CompuPhase\0"
#define VERSIONPRODUCTNAME "amxTime\0"
#define VERSIONCOPYRIGHT "Copyright \251 2005-2006 ITB CompuPhase\0"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION, REVISION, BUILD, 0
PRODUCTVERSION VERSION, REVISION, BUILD, 0
FILEFLAGSMASK 0x0000003FL
FILEFLAGS 0
#if defined(WIN32)
FILEOS VOS__WINDOWS32
#else
FILEOS VOS__WINDOWS16
#endif
FILETYPE VFT_DLL
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VERSIONCOMPANYNAME
VALUE "FileDescription", VERSIONDESCRIPTION
VALUE "FileVersion", VERSIONSTR
VALUE "InternalName", VERSIONNAME
VALUE "LegalCopyright", VERSIONCOPYRIGHT
VALUE "OriginalFilename", VERSIONNAME
VALUE "ProductName", VERSIONPRODUCTNAME
VALUE "ProductVersion", VERSIONSTR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

79
SOURCE/amx/dllmain.c Normal file
View File

@ -0,0 +1,79 @@
/* DLL support functions for dynamically loadable extension libraries.
*
* Copyright (c) ITB CompuPhase, 2004-2005
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: dllmain.c 3363 2005-07-23 09:03:29Z thiadmer $
*/
#if defined _UNICODE || defined __UNICODE__ || defined UNICODE
# if !defined UNICODE /* for Windows */
# define UNICODE
# endif
# if !defined _UNICODE /* for C library */
# define _UNICODE
# endif
#endif
#include <assert.h>
#include <windows.h>
#if !defined UNUSED_PARAM
#define UNUSED_PARAM(p) ((void)(p))
#endif
HINSTANCE hinstDLL;
/* Especially Watcom C/C++ does not like DLLs that do not have a LibMain()
* set. Apparently, the start address is not set well, and some required
* initializations are not done.
*/
#if defined __WIN32__ || defined _WIN32 || defined WIN32
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID lpRes)
{
UNUSED_PARAM(lpRes);
switch (dwReason) {
case DLL_PROCESS_ATTACH:
hinstDLL=hinst;
break;
case DLL_PROCESS_DETACH:
break;
} /* switch */
return TRUE;
}
#else
int FAR PASCAL LibMain(HINSTANCE hinst, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
{
UNUSED_PARAM(wDataSeg);
UNUSED_PARAM(wHeapSize);
UNUSED_PARAM(lpszCmdLine);
hinstDLL=hinst;
return 1;
}
int FAR PASCAL _export WEP(int param)
{
UNUSED_PARAM(param);
return 1;
}
#endif /* __WIN32__ */

View File

@ -0,0 +1,71 @@
#include <stdio.h>
#include <malloc>
#include <map>
#include "amx.h"
class LogFile;
static std::map<AMX*, LogFile*> lookup;
class LogFile {
public:
LogFile()
{
f = tmpfile();
}
~LogFile()
{
fclose(f);
}
private:
write(AMX* amx, cell params[])
{
int r = 0;
char *pstr;
amx_StrParam(amx, params[1], pstr);
if (pstr != NULL)
r = fprintf(f, "%s", pstr);
return r;
}
FILE *f;
public:
static cell n_write(AMX* amx, cell params[])
{
std::map<AMX*, LogFile*>::iterator p = lookup.find(amx);
if (p != lookup.end())
return p->second->write(amx, params);
return 0;
}
};
extern "C"
int amx_LogFileInit(AMX* amx)
{
LogFile* lf = new LogFile;
if (lf) {
lookup.insert(std::make_pair(amx, lf));
static AMX_NATIVE_INFO nativelist[] = {
{ "write", LogFile::n_write },
{ 0, 0 } /* terminator */
};
return amx_Register(amx, nativelist, -1);
} /* if */
return AMX_ERR_MEMORY;
}
extern "C"
int amx_LogFileExit(AMX* amx)
{
std::map<AMX*, LogFile*>::iterator p = lookup.find(amx);
if (p != lookup.end()) {
delete p->second;
lookup.erase(p);
} /* if */
return AMX_ERR_NONE;
}

View File

@ -0,0 +1,46 @@
/* This file implements two the native functions: power(value,exponent)
* and sqroot(value).
*/
#include "amx.h"
static cell n_power(AMX *amx, cell *params)
{
/* power(value, exponent);
* params[1] = value
* params[2] = exponent
*/
cell result = 1;
while (params[2]-- > 0)
result *= params[1];
return result;
}
static cell n_sqroot(AMX *amx, cell *params)
{
/* sqroot(value);
* params[1] = value
* This routine uses a simple successice approximation algorithm.
*/
cell div = params[1];
cell result = 1;
while (div > result) { /* end when div == result, or just below */
div = (div + result) / 2; /* take mean value as new divisor */
result = params[1] / div;
} /* while */
return div;
}
int amx_PowerInit(AMX *amx)
{
static AMX_NATIVE_INFO power_Natives[] = {
{ "power", n_power },
{ "sqroot", n_sqroot },
{ 0, 0 } /* terminator */
};
return amx_Register(amx, power_Natives, -1);
}
int amx_PowerCleanup(AMX *amx)
{
return AMX_ERR_NONE;
}

744
SOURCE/amx/fixed.c Normal file
View File

@ -0,0 +1,744 @@
/* Fixed-point arithmetic for the Pawn Abstract Machine
*
* Fixed point numbers compromise range versus number of decimals. This
* library decimal fixed point numbers with an configurable number of
* decimals. The current setting is 3 decimals.
*
* Copyright (c) ITB CompuPhase, 1998-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: fixed.c 3662 2006-11-07 08:44:33Z thiadmer $
*/
#include <assert.h>
#include <stdio.h> /* for NULL */
#include "amx.h"
/*
#if defined __BORLANDC__
#pragma resource "amxFixed.res"
#endif
*/
#if !defined isdigit
# define isdigit(c) ((unsigned)((c)-'0')<10u)
#endif
#define MULTIPLIER 1000L /* 10^decimals */
static cell AMX_NATIVE_CALL n_fixed(AMX *amx,const cell *params)
{
(void)amx;
return params[1] * MULTIPLIER;
}
static cell AMX_NATIVE_CALL n_strfixed(AMX *amx,const cell *params)
{
char str[50],*ptr;
cell *cstr,intpart,decimals;
long multiplier,divisor;
int len,sign=1;
amx_GetAddr(amx,params[1],&cstr);
amx_StrLen(cstr,&len);
if (len>=50) {
amx_RaiseError(amx,AMX_ERR_NATIVE);
return 0;
} /* if */
amx_GetString(str,cstr,0,UNLIMITED);
ptr=str;
intpart=0;
decimals=0;
multiplier=MULTIPLIER;
divisor=1;
while (*ptr!='\0' && *ptr<=' ')
ptr++; /* skip whitespace */
if (*ptr=='-') { /* handle sign */
sign=-1;
ptr++;
} else if (*ptr=='+') {
ptr++;
} /* if */
while (isdigit(*ptr)) {
intpart=intpart*10 + (*ptr-'0');
ptr++;
} /* while */
if (*ptr=='.') {
ptr++;
len=0;
while (isdigit(*ptr) && len<8) {
decimals=decimals*10 + (*ptr-'0');
if (multiplier>1)
multiplier/=10;
else
divisor*=10;
ptr++;
len++;
} /* while */
} /* if */
return ((intpart*MULTIPLIER) + (decimals*multiplier+(divisor/2))/divisor) * sign;
}
/* Some C/C++ compilers have problems with long lists of definitions, so
* I create another macro to fix this.
*/
#if PAWN_CELL_SIZE!=32
/* the assembler and compiler-supported optimizations are only implemented
* for 32-bit cells
*/
#define USE_ANSI_C 1
#elif defined __WATCOMC__ && defined __386__ /* Watcom C/C++ */
/* ANSI 64-bit division routine not needed for Watcom C/C++ because
* it uses inline assembler.
*/
#define USE_ANSI_C 0
#elif defined _MSC_VER && _MSC_VER>=9 && defined _WIN32 /* Microsoft C/C++ */
/* ANSI 64-bit division routine not needed for Microsoft Visual C/C++
* because it supports 64-bit integers
*/
#define USE_ANSI_C 0
#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && defined __32BIT__ /* Borland C++ v.5 */
/* ANSI 64-bit division routine not needed for Borland C++ because it
* supports 64-bit integers
*/
#define USE_ANSI_C 0
#elif defined __GNUC__ /* GNU GCC */
/* ANSI 64-bit division routine not needed for GNU GCC because it
* supports 64-bit integers
*/
#define USE_ANSI_C 0
#else
#define USE_ANSI_C 1
#endif
#if USE_ANSI_C
#define WORDSHIFT (PAWN_CELL_SIZE/2)
#if PAWN_CELL_SIZE==32
typedef unsigned short word_t;
#define LOWORD(v) (word_t)((v) & 0xffffu)
#define HIWORD(v) (word_t)(((v) >> 16) & 0xffffu)
#elif PAWN_CELL_SIZE==64
typedef uint32_t word_t;
#define LOWORD(v) (word_t)((v) & 0xffffffffu)
#define HIWORD(v) (word_t)(((v) >> 32) & 0xffffffffu)
#else
#error Unsupported cell size
#endif
static ucell div64_32(ucell t[2], ucell divisor)
{
/* This function was adapted from source code that appeared in
* Dr. Dobb's Journal, August 1992, page 117.
*/
ucell u, v;
word_t rHigh, rLow, dHigh, dLow;
assert(divisor!=0);
/* if the divisor is smaller than t[1], the result will not fit in a cell */
assert(divisor>=t[1]);
dHigh=HIWORD(divisor);
dLow=LOWORD(divisor);
/* Underestimate high half of quotient and subtract product
* of estimate and divisor from dividend.
*/
rHigh = (word_t)(t[1] / (dHigh + 1));
u = (ucell)rHigh * (ucell)dLow;
v = (ucell)rHigh * (ucell)dHigh;
if ((t[0] -= (u << WORDSHIFT)) > ((ucell)-1L - (u << WORDSHIFT)))
t[1]--;
t[1] -= HIWORD(u);
t[1] -= v;
/* Correct estimate. */
while ((t[1] > (ucell)dHigh) || ((t[1] == (ucell)dHigh) && (t[0] >= ((ucell)dLow << WORDSHIFT)))) {
if ((t[0] -= ((ucell)dLow << WORDSHIFT)) > (ucell)-1L - ((ucell)dLow << WORDSHIFT))
t[1]--;
t[1] -= dHigh;
rHigh++;
} /* while */
/* Underestimate low half of quotient and subtract product of
* estimate and divisor from what remains of dividend.
*/
rLow = (word_t) ((ucell)((t[1] << WORDSHIFT) + HIWORD(t[0])) / (dHigh + 1));
u = (ucell)rLow * (ucell)dLow;
v = (ucell)rLow * (ucell)dHigh;
if ((t[0] -= u) > ((ucell)-1L - u))
t[1]--;
if ((t[0] -= (v << WORDSHIFT)) > ((ucell)-1L - (v << WORDSHIFT)))
t[1]--;
t[1] -= HIWORD(v);
/* Correct estimate. */
while ((t[1] > 0) || ((t[1] == 0) && t[0] >= divisor)) {
if ((t[0] -= divisor) > ((ucell)-1L - divisor))
t[1]--;
rLow++;
} /* while */
return ((ucell)rHigh << WORDSHIFT) + rLow;
}
#endif
static cell AMX_NATIVE_CALL n_fmul(AMX *amx,const cell *params)
{
#if !USE_ANSI_C
#if defined __WATCOMC__ && defined __386__
cell __fmul(void);
cell a=params[1];
cell b=params[2];
#if MULTIPLIER != 1000
#error Assembler chunks must be modified for a different base
#endif
#pragma aux __fmul = \
"mov eax, [a]" \
"mov ebx, 1000" \
"imul [b]" \
"add eax, 500" \
"adc edx, 0" \
"idiv ebx" \
"mov [a], eax" \
modify [eax ebx edx];
__fmul();
(void)amx;
return a;
#elif _MSC_VER>=9 && defined _WIN32
__int64 a=(__int64)params[1] * (__int64)params[2];
a=(a+MULTIPLIER/2) / MULTIPLIER;
(void)amx;
return (cell)a;
#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__)
__int64 a=(__int64)params[1] * (__int64)params[2];
a=(a+MULTIPLIER/2) / MULTIPLIER;
(void)amx;
return (cell)a;
#elif defined __GNUC__
long long a=(long long)params[1] * (long long)params[2];
a=(a+MULTIPLIER/2) / MULTIPLIER;
(void)amx;
return (cell)a;
#else
#error Unsupported compiler configuration, but USE_ANSI_C is false
#endif
#else // USE_ANSI_C
/* (Xs * Ys) == (X*Y)ss, where "s" stands for scaled.
* The desired result is (X*Y)s, so we must unscale once.
* but we cannot do this before multiplication, because of loss
* of precision, and we cannot do it after the multiplication
* because of the possible overflow.
* The technique used here is to cut the multiplicands into
* components and to multiply these components separately:
*
* Assume Xs == (A << 16) + B and Ys == (C << 16) + D, where A, B,
* C and D are 16 bit numbers.
*
* A B
* C D
* --- *
* D*B + (D*A << 16) + (C*B << 16) + (C*A << (16+16))
*
* Thus we have built a 64-bit number, which can now be scaled back
* to 32-bit by dividing by the scale factor.
*/
#define ADD_WRAP(var,carry,expr) (((var)+=(expr)), ((carry)+=((var)<(expr)) ? 1 : 0))
ucell a,b,c,d;
ucell v[2];
cell sign=1;
(void)amx;
assert(MULTIPLIER<=(1L<<WORDSHIFT));
/* make both operands positive values, but keep the sign of the result */
if (params[1]<0) {
((cell*)params)[1]=-params[1];
sign=-sign; /* negate result */
} /* if */
if (params[2]<0) {
((cell*)params)[2]=-params[2];
sign=-sign; /* negate result */
} /* if */
a = HIWORD(params[1]);
b = LOWORD(params[1]);
c = HIWORD(params[2]);
d = LOWORD(params[2]);
/* store the intermediate into a 64-bit/128-bit number */
v[1]=c*a;
v[0]=d*b;
ADD_WRAP(v[0],v[1],d*a << WORDSHIFT);
ADD_WRAP(v[0],v[1],c*b << WORDSHIFT);
/* add half of the divisor, to round the data */
ADD_WRAP(v[0],v[1],MULTIPLIER/2);
/* if the divisor is smaller than v[1], the result will not fit in a cell */
if (MULTIPLIER<v[1]) {
amx_RaiseError(amx,AMX_ERR_DOMAIN);
return 0;
} /* if */
return (cell)div64_32(v,MULTIPLIER) * sign;
#endif
}
static cell AMX_NATIVE_CALL n_fdiv(AMX *amx,const cell *params)
{
#if !USE_ANSI_C
#if defined __WATCOMC__ && defined __386__
cell __fdiv(void);
cell a=params[1];
cell b=params[2];
#if MULTIPLIER != 1000
#error Assembler chunks must be modified for a different base
#endif
if (b==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
#pragma aux __fdiv = \
"mov eax, [a]" \
"mov ecx, [b]" \
"cdq" \
"mov ebx, 1000" \
"imul ebx" \
"mov ebx, ecx" \
"shr ecx, 1" \
"add eax, ecx" \
"adc edx, 0" \
"idiv ebx" \
"mov [a], eax" \
modify [eax ebx ecx edx];
__fdiv();
return a;
#elif _MSC_VER>=9 && defined _WIN32
__int64 a;
cell divisor=params[2];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor;
return (cell)a;
#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__)
__int64 a;
cell divisor=params[2];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((__int64)params[1] * (__int64)MULTIPLIER + (__int64)(divisor/2)) / (__int64)divisor;
return (cell)a;
#elif defined __GNUC__
long long a;
cell divisor=params[2];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((long long)params[1] * (long long)MULTIPLIER + (long long)(divisor/2)) / (long long)divisor;
return (cell)a;
#else
#error Unsupported compiler configuration, but USE_ANSI_C is false
#endif
#else // USE_ANSI_C
/* The dividend must be scaled prior to division. The dividend
* is a 32-bit number, however, so when shifted, it will become
* a value that no longer fits in a 32-bit variable. This routine
* does the division by using only 16-bit and 32-bit values, but
* with considerable effort.
* If your compiler supports 64-bit integers, modify this routine
* to use them. If your processor can do a simple 64-bit by 32-bit
* division in assembler, write assembler chunks.
* In other words: the straight C routine that follows is correct
* and portable, but use it only as a last resort.
*
* This function was adapted from source code that appeared in
* Dr. Dobb's Journal, August 1992, page 117.
*/
cell dividend=params[1];
cell divisor=params[2];
cell sign=1;
ucell b[2];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_NATIVE);
return 0;
} /* if */
/* make both operands positive values, but keep the sign of the result */
if (dividend<0) {
dividend=-dividend;
sign=-sign; /* negate result */
} /* if */
if (divisor<0) {
divisor=-divisor;
sign=-sign; /* negate result */
} /* if */
/* pre-scale the dividend into a 64-bit/128-bit number */
b[0]=dividend*MULTIPLIER;
b[1]=(HIWORD(dividend)*MULTIPLIER) >> WORDSHIFT;
/* add half of the divisor, to round the data */
b[0]+=(ucell)divisor/2;
if (b[0]<(ucell)divisor/2)
b[1]+=1; /* wrap-around ocurred */
/* if the divisor is smaller than b[1], the result will not fit in a cell */
if ((ucell)divisor<b[1]) {
amx_RaiseError(amx,AMX_ERR_DOMAIN);
return 0;
} /* if */
return (cell)div64_32(b,(ucell)divisor) * sign;
#endif
}
static cell AMX_NATIVE_CALL n_fmuldiv(AMX *amx,const cell *params)
{
#if !USE_ANSI_C
#if defined __WATCOMC__ && defined __386__
cell __fmuldiv(void);
cell a=params[1];
cell b=params[2];
cell c=params[3];
if (c==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
#pragma aux __fmuldiv = \
"mov eax, [a]" \
"mov ecx, [c]" \
"imul [b]" \
"mov ebx, ecx" \
"shr ecx, 1" \
"add eax, ecx" \
"adc edx, 0" \
"idiv ebx" \
"mov [a], eax" \
modify [eax ebx ecx edx];
__fmuldiv();
return a;
#elif _MSC_VER>=9 && defined _WIN32
__int64 a;
cell divisor=params[3];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor;
return (cell)a;
#elif defined __BORLANDC__ && __BORLANDC__ >= 0x500 && (defined __32BIT__ || defined __WIN32__)
__int64 a;
cell divisor=params[3];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((__int64)params[1] * (__int64)params[2] + (__int64)(divisor/2)) / (__int64)divisor;
return (cell)a;
#elif defined __GNUC__
long long a;
cell divisor=params[3];
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
a=((long long)params[1] * (long long)params[2] + (long long)(divisor/2)) / (long long)divisor;
return (cell)a;
#else
#error Unsupported compiler configuration, but USE_ANSI_C is false
#endif
#else // USE_ANSI_C
ucell a,b,c,d;
ucell v[2];
cell sign=1;
cell divisor=params[3];
assert(MULTIPLIER<=(1L<<16));
if (divisor==0) {
amx_RaiseError(amx,AMX_ERR_DIVIDE);
return 0;
} /* if */
/* make all three operands positive values, but keep the sign of the result */
if (params[1]<0) {
((cell*)params)[1]=-params[1];
sign=-sign; /* negate result */
} /* if */
if (params[2]<0) {
((cell*)params)[2]=-params[2];
sign=-sign; /* negate result */
} /* if */
if (divisor<0) {
divisor=-divisor;
sign=-sign; /* negate result */
} /* if */
a = HIWORD(params[1]);
b = LOWORD(params[1]);
c = HIWORD(params[2]);
d = LOWORD(params[2]);
/* store the intermediate into a 64-bit/128-bit number */
v[1]=c*a;
v[0]=d*b;
ADD_WRAP(v[0],v[1],d*a << WORDSHIFT);
ADD_WRAP(v[0],v[1],c*b << WORDSHIFT);
/* add half of the divisor, to round the data */
ADD_WRAP(v[0],v[1],(ucell)divisor/2);
/* if the divisor is smaller than v[1], the result will not fit in a cell */
if ((ucell)divisor<v[1]) {
amx_RaiseError(amx,AMX_ERR_DOMAIN);
return 0;
} /* if */
return (cell)div64_32(v,(ucell)divisor) * sign;
#endif
}
static cell AMX_NATIVE_CALL n_ffract(AMX *amx,const cell *params)
{
(void)amx;
return params[1] % MULTIPLIER;
}
static cell AMX_NATIVE_CALL n_fround(AMX *amx,const cell *params)
{
cell value;
(void)amx;
switch (params[2]) {
case 1: /* round downwards (truncate) */
value=params[1] / MULTIPLIER;
if (params[1]<0 && (params[1] % MULTIPLIER)!=0)
value--;
break;
case 2: /* round upwards */
value=params[1] / MULTIPLIER;
if (params[1]>=0 && (params[1] % MULTIPLIER)!=0)
value++;
break;
case 3: /* truncate: round down when > 0, round up when < 0 */
value=params[1] / MULTIPLIER;
break;
case 4: /* round to even number when fractional part is exactly 0.5 */
value=(params[1] + MULTIPLIER/2) / MULTIPLIER;
if ((params[1] % MULTIPLIER)==MULTIPLIER/2 && (value & 1)==1)
value--;
break;
default: /* standard (fractional part of 0.5 is rounded up */
value=(params[1] + MULTIPLIER/2) / MULTIPLIER;
} /* switch */
return value;
}
/* Fixed:fsqroot(Fixed:value) */
static cell AMX_NATIVE_CALL n_fsqroot(AMX *amx,const cell *params)
{
cell value=params[1];
cell low=0;
cell high=value;
cell mid[3]={8,0,0};
if (value<0) {
amx_RaiseError(amx, AMX_ERR_DOMAIN);
return 0;
} /* if */
while (high-low > 1) {
mid[1]=mid[2]=(low+high)/2;
if (n_fmul(amx,mid) < value)
low=mid[1];
else
high=mid[1];
} /* while */
/* check whether low or high mark comes closest */
if (low!=high) {
cell deltalow, deltahigh;
mid[1]=mid[2]=low;
deltalow=value-n_fmul(amx,mid);
assert(deltalow>=0);
mid[1]=mid[2]=high;
deltahigh=n_fmul(amx,mid)-value;
assert(deltahigh>=0);
if (deltahigh<=deltalow)
low=high; /* return "high" mark (comes closer to the answer) */
} /* if */
return low;
}
/* Fixed: fpower(Fixed:value, exponent)
* note: x^n = exp(n*ln(x)), see http://yacas.sourceforge.net/Algochapter5.html
*/
static cell AMX_NATIVE_CALL n_fpower(AMX *amx,const cell *params)
{
#define LIMIT32 146542L /* top value to calculate with extra digit, when a cell is 32-bit */
cell result[3] = {8,0,0};
int power=(int)params[2];
int iter=1;
int reciprocal=0;
int isscaled=0;
if (power<0) {
reciprocal=1;
power=-power;
} /* if */
if (power==0)
return MULTIPLIER;
/* quick squaring, to nearest power of 2 */
result[1]=params[1];
/* first try to do this with an extra digit of precision */
if (result[1]<LIMIT32) {
assert(sizeof(cell)>=4);
result[1]*=10; /* scale to have an extra digit */
isscaled=1;
while (2*iter<=power && result[1]<LIMIT32*10) {
iter*=2;
result[2]=result[1];
result[1]=(n_fmul(amx,result)+5)/10;
} /* while */
} /* if */
assert(2*iter>power || result[1]>=LIMIT32*10);
if (result[1]>=LIMIT32*10) {
result[1]=(result[1]+5)/10; /* undo scaling */
isscaled=0;
} /* if */
while (2*iter<=power) {
iter*=2;
result[2]=result[1];
result[1]=n_fmul(amx,result);
} /* while */
/* multilply with the remainder */
if (iter<power && (isscaled || result[1]<LIMIT32)) {
if (!isscaled) {
result[1]*=10; /* scale to have an extra digit */
isscaled=1;
} /* if */
while (iter<power && result[1]<LIMIT32*10) {
iter++;
result[2]=params[1];
result[1]=n_fmul(amx,result);
} /* while */
} /* if */
if (isscaled) {
result[1]=(result[1]+5)/10; /* undo scaling */
/* isscaled=0; */
} /* if */
while (iter<power) {
iter++;
result[2]=params[1];
result[1]=n_fmul(amx,result);
} /* while */
if (reciprocal) {
result[2]=result[1];
result[1]=MULTIPLIER;
result[1]=n_fdiv(amx,result); /* result[1] = 1 / result[1] */
} /* if */
return result[1];
}
/* Fixed: fabs(Fixed:value)
*/
static cell AMX_NATIVE_CALL n_fabs(AMX *amx,const cell *params)
{
cell result=params[1];
(void)amx;
return (result>=0) ? result : -result;
}
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO fixed_Natives[] = {
{ "fixed", n_fixed },
{ "strfixed", n_strfixed },
{ "fmul", n_fmul },
{ "fdiv", n_fdiv },
{ "ffract", n_ffract },
{ "fround", n_fround },
{ "fmuldiv", n_fmuldiv },
{ "fsqroot", n_fsqroot },
{ "fpower", n_fpower },
{ "fabs", n_fabs },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_FixedInit(AMX *amx)
{
return amx_Register(amx,fixed_Natives,-1);
}
int AMXEXPORT amx_FixedCleanup(AMX *amx)
{
(void)amx;
return AMX_ERR_NONE;
}

362
SOURCE/amx/float.c Normal file
View File

@ -0,0 +1,362 @@
/* Float arithmetic for the Pawn Abstract Machine
*
* Copyright (c) Artran, Inc. 1999
* Written by Greg Garner (gmg@artran.com)
* This file may be freely used. No warranties of any kind.
*
* CHANGES -
* 2002-08-27: Basic conversion of source from C++ to C by Adam D. Moss
* <adam@gimp.org> <aspirin@icculus.org>
* 2003-08-29: Removal of the dynamic memory allocation and replacing two
* type conversion functions by macros, by Thiadmer Riemersma
* 2003-09-22: Moved the type conversion macros to AMX.H, and simplifications
* of some routines, by Thiadmer Riemersma
* 2003-11-24: A few more native functions (geometry), plus minor modifications,
* mostly to be compatible with dynamically loadable extension
* modules, by Thiadmer Riemersma
* 2004-01-09: Adaptions for 64-bit cells (using "double precision"), by
* Thiadmer Riemersma
*/
#include <stdlib.h> /* for atof() */
#include <stdio.h> /* for NULL */
#include <assert.h>
#include <math.h>
#include "amx.h"
/*
#if defined __BORLANDC__
#pragma resource "amxFloat.res"
#endif
*/
#if PAWN_CELL_SIZE==32
#define REAL float
#elif PAWN_CELL_SIZE==64
#define REAL double
#else
#error Unsupported cell size
#endif
#define PI 3.1415926535897932384626433832795
/******************************************************************/
static cell AMX_NATIVE_CALL n_float(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = long value to convert to a float
*/
REAL fValue;
(void)amx;
/* Convert to a float. Calls the compilers long to float conversion. */
fValue = (REAL) params[1];
/* Return the cell. */
return amx_ftoc(fValue);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_strfloat(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = virtual string address to convert to a float
*/
char szSource[60];
cell *pString;
REAL fNum;
int nLen;
(void)amx;
/* They should have sent us 1 cell. */
assert(params[0]/sizeof(cell)==1);
/* Get the real address of the string. */
amx_GetAddr(amx,params[1],&pString);
/* Find out how long the string is in characters. */
amx_StrLen(pString, &nLen);
if (nLen == 0 || nLen >= sizeof szSource)
return 0;
/* Now convert the Pawn string into a C type null terminated string */
amx_GetString(szSource, pString, 0, sizeof szSource);
/* Now convert this to a float. */
fNum = (REAL)atof(szSource);
return amx_ftoc(fNum);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatmul(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1
* params[2] = float operand 2
*/
REAL fRes = amx_ctof(params[1]) * amx_ctof(params[2]);
(void)amx;
return amx_ftoc(fRes);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatdiv(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float dividend (top)
* params[2] = float divisor (bottom)
*/
REAL fRes = amx_ctof(params[1]) / amx_ctof(params[2]);
(void)amx;
return amx_ftoc(fRes);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatadd(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1
* params[2] = float operand 2
*/
REAL fRes = amx_ctof(params[1]) + amx_ctof(params[2]);
(void)amx;
return amx_ftoc(fRes);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatsub(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1
* params[2] = float operand 2
*/
REAL fRes = amx_ctof(params[1]) - amx_ctof(params[2]);
(void)amx;
return amx_ftoc(fRes);
}
/******************************************************************/
/* Return fractional part of float */
static cell AMX_NATIVE_CALL n_floatfract(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand
*/
REAL fA = amx_ctof(params[1]);
fA = fA - (REAL)(floor((double)fA));
(void)amx;
return amx_ftoc(fA);
}
/******************************************************************/
/* Return integer part of float, rounded */
static cell AMX_NATIVE_CALL n_floatround(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand
* params[2] = Type of rounding (long)
*/
REAL fA = amx_ctof(params[1]);
(void)amx;
switch (params[2])
{
case 1: /* round downwards (truncate) */
fA = (REAL)(floor((double)fA));
break;
case 2: /* round upwards */
fA = (REAL)(ceil((double)fA));
break;
case 3: /* round towards zero */
if ( fA>=0.0 )
fA = (REAL)(floor((double)fA));
else
fA = (REAL)(ceil((double)fA));
break;
default: /* standard, round to nearest */
fA = (REAL)(floor((double)fA+.5));
break;
}
return (long)fA;
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatcmp(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1
* params[2] = float operand 2
*/
REAL fA, fB;
(void)amx;
fA = amx_ctof(params[1]);
fB = amx_ctof(params[2]);
if (fA == fB)
return 0;
else if (fA>fB)
return 1;
else
return -1;
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatsqroot(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand
*/
REAL fA = amx_ctof(params[1]);
fA = (REAL)sqrt(fA);
if (fA < 0)
return amx_RaiseError(amx, AMX_ERR_DOMAIN);
return amx_ftoc(fA);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatpower(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1 (base)
* params[2] = float operand 2 (exponent)
*/
REAL fA = amx_ctof(params[1]);
REAL fB = amx_ctof(params[2]);
fA = (REAL)pow(fA, fB);
(void)amx;
return amx_ftoc(fA);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatlog(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1 (value)
* params[2] = float operand 2 (base)
*/
REAL fValue = amx_ctof(params[1]);
REAL fBase = amx_ctof(params[2]);
(void)amx;
if (fValue <= 0.0 || fBase <= 0)
return amx_RaiseError(amx, AMX_ERR_DOMAIN);
if (fBase == 10.0) // ??? epsilon
fValue = (REAL)log10(fValue);
else
fValue = (REAL)(log(fValue) / log(fBase));
return amx_ftoc(fValue);
}
static REAL ToRadians(REAL angle, int radix)
{
switch (radix)
{
case 1: /* degrees, sexagesimal system (technically: degrees/minutes/seconds) */
return (REAL)(angle * PI / 180.0);
case 2: /* grades, centesimal system */
return (REAL)(angle * PI / 200.0);
default: /* assume already radian */
return angle;
} /* switch */
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatsin(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1 (angle)
* params[2] = float operand 2 (radix)
*/
REAL fA = amx_ctof(params[1]);
fA = ToRadians(fA, params[2]);
fA = sin(fA);
(void)amx;
return amx_ftoc(fA);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatcos(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1 (angle)
* params[2] = float operand 2 (radix)
*/
REAL fA = amx_ctof(params[1]);
fA = ToRadians(fA, params[2]);
fA = cos(fA);
(void)amx;
return amx_ftoc(fA);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floattan(AMX *amx,const cell *params)
{
/*
* params[0] = number of bytes
* params[1] = float operand 1 (angle)
* params[2] = float operand 2 (radix)
*/
REAL fA = amx_ctof(params[1]);
fA = ToRadians(fA, params[2]);
fA = tan(fA);
(void)amx;
return amx_ftoc(fA);
}
/******************************************************************/
static cell AMX_NATIVE_CALL n_floatabs(AMX *amx,const cell *params)
{
REAL fA = amx_ctof(params[1]);
fA = (fA >= 0) ? fA : -fA;
(void)amx;
return amx_ftoc(fA);
}
#if defined __cplusplus
extern "C"
#endif
const AMX_NATIVE_INFO float_Natives[] = {
{ "float", n_float },
{ "strfloat", n_strfloat },
{ "floatmul", n_floatmul },
{ "floatdiv", n_floatdiv },
{ "floatadd", n_floatadd },
{ "floatsub", n_floatsub },
{ "floatfract", n_floatfract },
{ "floatround", n_floatround },
{ "floatcmp", n_floatcmp },
{ "floatsqroot", n_floatsqroot},
{ "floatpower", n_floatpower },
{ "floatlog", n_floatlog },
{ "floatsin", n_floatsin },
{ "floatcos", n_floatcos },
{ "floattan", n_floattan },
{ "floatabs", n_floatabs },
{ NULL, NULL } /* terminator */
};
int AMXEXPORT amx_FloatInit(AMX *amx)
{
return amx_Register(amx,float_Natives,-1);
}
int AMXEXPORT amx_FloatCleanup(AMX *amx)
{
(void)amx;
return AMX_ERR_NONE;
}

906
SOURCE/amx/fpattern.c Normal file
View File

@ -0,0 +1,906 @@
/*
from http://home.flash.net/~dtribble/text/fpattern.htm
2000/12/12
by Perry
License statement is in comments below
*/
/******************************************************************************
* fpattern.c
* Functions for matching filename patterns to filenames.
*
* Usage
* (See "fpattern.h".)
*
* Notes
* These pattern matching capabilities are modeled after those found in
* the UNIX command shells.
*
* `DELIM' must be defined to 1 if pathname separators are to be handled
* explicitly.
*
* History
* 1.00 1997-01-03 David Tribble.
* First cut.
* 1.01 1997-01-03 David Tribble.
* Added SUB pattern character.
* Added fpattern_matchn().
* 1.02 1997-01-12 David Tribble.
* Fixed missing lowercase matching cases.
* 1.03 1997-01-13 David Tribble.
* Pathname separator code is now controlled by DELIM macro.
* 1.04 1997-01-14 David Tribble.
* Added QUOTE macro.
* 1.05 1997-01-15 David Tribble.
* Handles special case of empty pattern and empty filename.
* 1.06 1997-01-26 David Tribble.
* Changed range negation character from '^' to '!', ala Unix.
* 1.07 1997-08-02 David Tribble.
* Uses the 'FPAT_XXX' constants.
* 1.08 1998-06-28 David Tribble.
* Minor fixed for MS-VC++ (5.0).
*
* Limitations
* This code is copyrighted by the author, but permission is hereby
* granted for its unlimited use provided that the original copyright
* and authorship notices are retained intact.
*
* Other queries can be sent to:
* dtribble@technologist.com
* david.tribble@beasys.com
* dtribble@flash.net
*
* Copyright 1997-1998 by David R. Tribble, all rights reserved.
* $Id: fpattern.c 3612 2006-07-22 09:59:46Z thiadmer $
*/
/* Identification */
#if 0
static const char id[] =
"@(#)lib/fpattern.c 1.08";
static const char copyright[] =
"Copyright 1997-1998 David R. Tribble\n";
#endif
/* System includes */
#include <ctype.h>
#include <stddef.h>
#if TEST
#include <locale.h>
#include <stdio.h>
#include <string.h>
#if defined _WIN32 || defined __WIN32__ || defined WIN32
#include <stdlib.h>
#define sleep(q) Sleep(q*1000)
#include <windows.h>
#endif
#endif
#if defined(unix) || defined(_unix) || defined(__unix) || \
defined(__unix__) || ( defined(__MACH__) && defined(__APPLE_CC__) )
#define UNIX 1
#define DOS 0
#elif defined(__MSDOS__) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
#define UNIX 0
#define DOS 1
#else
#error Cannot ascertain O/S from predefined macros
#endif
/* Local includes */
#include "fpattern.h"
/* Local constants */
#ifndef NULL
# define NULL ((void *) 0)
#endif
#ifndef FALSE
# define FALSE 0
#endif
#ifndef TRUE
# define TRUE 1
#endif
#if TEST
# define SUB '~'
#else
# define SUB FPAT_CLOSP
#endif
#ifndef DELIM
# define DELIM 0
#endif
#ifndef SUBCLOS
# define SUBCLOS 0
#endif
#define DEL FPAT_DEL
#if UNIX
#define DEL2 FPAT_DEL
#else /*DOS*/
#define DEL2 FPAT_DEL2
#endif
#if UNIX
#define QUOTE FPAT_QUOTE
#else /*DOS*/
#define QUOTE FPAT_QUOTE2
#endif
/* Local function macros */
/* Warning, while this code was originally filename search code
* which was case insensitive on DOS and case sensitive on UNIX
* it's being used for name searching and should behave the same everywhere
*/
static int lowercase(int c, int keepcase)
{
if (keepcase)
return c;
return tolower(c);
}
/*-----------------------------------------------------------------------------
* fpattern_isvalid()
* Checks that filename pattern 'pat' is a well-formed pattern.
*
* Returns
* 1 (true) if 'pat' is a valid filename pattern, otherwise 0 (false).
*
* Caveats
* If 'pat' is null, 0 (false) is returned.
*
* If 'pat' is empty (""), 1 (true) is returned, and it is considered a
* valid (but degenerate) pattern (the only filename it matches is the
* empty ("") string).
*/
int fpattern_isvalid(const char *pat)
{
int len;
char close;
/* Check args */
if (pat == NULL)
return (FALSE);
/* Verify that the pattern is valid */
for (len = 0; pat[len] != '\0'; len++)
{
switch (pat[len])
{
case FPAT_SET_L:
case FPAT_MSET_L:
/* Char set */
close = (char)((pat[len] == FPAT_SET_L) ? FPAT_SET_R : FPAT_MSET_R);
len++;
if (pat[len] == FPAT_SET_NOT)
len++; /* Set negation */
while (pat[len] != close)
{
if (pat[len] == QUOTE)
len++; /* Quoted char */
if (pat[len] == '\0')
return (FALSE); /* Missing closing bracket */
len++;
if (pat[len] == FPAT_SET_THRU)
{
/* Char range */
len++;
if (pat[len] == QUOTE)
len++; /* Quoted char */
if (pat[len] == '\0')
return (FALSE); /* Missing closing bracket */
len++;
}
if (pat[len] == '\0')
return (FALSE); /* Missing closing bracket */
}
break;
case QUOTE:
/* Quoted char */
len++;
if (pat[len] == '\0')
return (FALSE); /* Missing quoted char */
break;
case FPAT_NOT:
/* Negated pattern */
len++;
if (pat[len] == '\0')
return (FALSE); /* Missing subpattern */
break;
default:
/* Valid character */
break;
}
}
return (TRUE);
}
/*-----------------------------------------------------------------------------
* fpattern_submatch()
* Attempts to match subpattern 'pat' to subfilename 'fname'.
*
* Returns
* 1 (true) if the subfilename matches, otherwise 0 (false).
*
* Caveats
* This does not assume that 'pat' is well-formed.
*
* If 'pat' is empty (""), the only filename it matches is the empty ("")
* string.
*
* Some non-empty patterns (e.g., "") will match an empty filename ("").
*/
static int fpattern_submatch(const char *pat, const char *fname, int flength, int keepcase)
{
int fch;
int pch;
int i;
int yes, match;
int lo, hi;
/* Attempt to match subpattern against subfilename */
while (*pat != '\0')
{
fch = *fname;
pch = *pat;
pat++;
switch (pch)
{
case FPAT_ANY:
/* Match a single char */
#if DELIM
if (fch == DEL || fch == DEL2 || fch == '\0')
return (FALSE);
#else
if (flength == 0)
return (FALSE);
#endif
fname++;
flength--;
break;
case FPAT_CLOS:
/* Match zero or more chars */
#if DELIM
i = 0;
while (fname[i] != '\0' &&
fname[i] != DEL && fname[i] != DEL2)
i++;
#else
i = flength;
#endif
while (i >= 0)
{
if (fpattern_submatch(pat, fname+i, flength-i, keepcase))
return (TRUE);
i--;
}
return (FALSE);
#if SUBCLOS
case SUB:
/* Match zero or more chars */
i = 0;
while (i < flength &&
#if DELIM
fname[i] != DEL && fname[i] != DEL2 &&
#endif
fname[i] != '.')
i++;
while (i >= 0)
{
if (fpattern_submatch(pat, fname+i, flength-i, keepcase))
return (TRUE);
i--;
}
return (FALSE);
#endif
case QUOTE:
/* Match a quoted char */
pch = *pat;
if (lowercase(fch, keepcase) != lowercase(pch, keepcase) || pch == '\0')
return (FALSE);
fname++;
flength--;
pat++;
break;
case FPAT_SET_L:
/* Match char set/range */
yes = TRUE;
if (*pat == FPAT_SET_NOT)
{
pat++;
yes = FALSE; /* Set negation */
}
/* Look for [s], [-], [abc], [a-c] */
match = !yes;
while (*pat != FPAT_SET_R && *pat != '\0')
{
if (*pat == QUOTE)
pat++; /* Quoted char */
if (*pat == '\0')
break;
lo = *pat++;
hi = lo;
if (*pat == FPAT_SET_THRU)
{
/* Range */
pat++;
if (*pat == QUOTE)
pat++; /* Quoted char */
if (*pat == '\0')
break;
hi = *pat++;
}
if (*pat == '\0')
break;
/* Compare character to set range */
if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) &&
lowercase(fch, keepcase) <= lowercase(hi, keepcase))
match = yes;
}
if (!match)
return (FALSE);
if (*pat == '\0')
return (FALSE); /* Missing closing bracket */
fname++;
flength--;
pat++;
break;
case FPAT_MSET_L:
/* Match zero or more characters in a char set/range */
yes = TRUE;
if (*pat == FPAT_SET_NOT)
{
pat++;
yes = FALSE; /* Set negation */
}
do {
const char *org_pat = pat;
/* Look for [s], [-], [abc], [a-c] */
match = !yes;
while (*pat != FPAT_MSET_R && *pat != '\0')
{
if (*pat == QUOTE)
pat++; /* Quoted char */
if (*pat == '\0')
break;
lo = *pat++;
hi = lo;
if (*pat == FPAT_SET_THRU)
{
/* Range */
pat++;
if (*pat == QUOTE)
pat++; /* Quoted char */
if (*pat == '\0')
break;
hi = *pat++;
}
if (*pat == '\0')
break;
/* Compare character to set range */
if (lowercase(fch, keepcase) >= lowercase(lo, keepcase) &&
lowercase(fch, keepcase) <= lowercase(hi, keepcase)) {
match = yes;
while (*pat != FPAT_MSET_R && *pat != '\0')
pat++;
break;
} /* if */
}
if (*pat == '\0')
return (FALSE); /* Missing closing bracket */
if (match) {
fname++;
flength--;
fch = *fname;
if (flength > 0)
pat = org_pat;
}
} while (match && flength > 0);
pat++;
break;
case FPAT_NOT:
/* Match only if rest of pattern does not match */
if (*pat == '\0')
return (FALSE); /* Missing subpattern */
i = fpattern_submatch(pat, fname, flength, keepcase);
return !i;
#if DELIM
case DEL:
#if DEL2 != DEL
case DEL2:
#endif
/* Match path delimiter char */
if (fch != DEL && fch != DEL2)
return (FALSE);
fname++;
flength--;
break;
#endif
default:
/* Match a (non-null) char exactly */
if (lowercase(fch, keepcase) != lowercase(pch, keepcase))
return (FALSE);
fname++;
flength--;
break;
}
}
/* Check for complete match */
if (flength != 0)
return (FALSE);
/* Successful match */
return (TRUE);
}
/*-----------------------------------------------------------------------------
* fpattern_match()
* Attempts to match pattern 'pat' to filename 'fname'. The comparison is case
* sensitive if 'keepcase' is true, and case insensitive otherwise. The 'flength'
* parameter allows to check partial strings, or to check strings with embedded
* zero bytes. When 'flength' is -1, it is set to the string length.
*
* Returns
* 1 (true) if the filename matches, otherwise 0 (false).
*
* Caveats
* If 'fname' is null, zero (false) is returned.
*
* If 'pat' is null, zero (false) is returned.
*
* If 'pat' is empty (""), the only filename it matches is the empty
* string ("").
*
* If 'fname' is empty, the only pattern that will match it is the empty
* string ("").
*
* If 'pat' is not a well-formed pattern, zero (false) is returned.
*
* Upper and lower case letters are treated the same; alphabetic
* characters are converted to lower case before matching occurs.
* Conversion to lower case is dependent upon the current locale setting.
*/
int fpattern_match(const char *pat, const char *fname, int flength, int keepcase)
{
int rc;
/* Check args */
if (fname == NULL)
return (FALSE);
if (pat == NULL)
return (FALSE);
/* Verify that the pattern is valid, and get its length */
if (!fpattern_isvalid(pat))
return (FALSE);
/* Attempt to match pattern against filename */
if (flength < 0)
flength = strlen(fname);
if (flength == 0)
return (pat[0] == '\0'); /* Special case */
rc = fpattern_submatch(pat, fname, flength, keepcase);
return (rc);
}
/*-----------------------------------------------------------------------------
* fpattern_matchn()
* Attempts to match pattern 'pat' to filename 'fname'.
* This operates like fpattern_match() except that it does not verify that
* pattern 'pat' is well-formed, assuming that it has been checked by a
* prior call to fpattern_isvalid().
*
* Returns
* 1 (true) if the filename matches, otherwise 0 (false).
*
* Caveats
* If 'fname' is null, zero (false) is returned.
*
* If 'pat' is null, zero (false) is returned.
*
* If 'pat' is empty (""), the only filename it matches is the empty ("")
* string.
*
* If 'pat' is not a well-formed pattern, unpredictable results may occur.
*
* Upper and lower case letters are treated the same; alphabetic
* characters are converted to lower case before matching occurs.
* Conversion to lower case is dependent upon the current locale setting.
*
* See also
* fpattern_match().
*/
int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase)
{
int rc;
/* Check args */
if (fname == NULL)
return (FALSE);
if (pat == NULL)
return (FALSE);
/* Assume that pattern is well-formed */
/* Attempt to match pattern against filename */
if (flength < 0)
flength = strlen(fname);
rc = fpattern_submatch(pat, fname, flength, keepcase);
return (rc);
}
/* returns the largest packet that matches the pattern */
int fpattern_matchcount(const char *pat, const char *fname, int flength, int keepcase)
{
int len;
if (fname == NULL)
return (FALSE);
if (pat == NULL)
return (FALSE);
/* Assume that pattern is well-formed */
/* Attempt to match pattern against filename */
if (flength < 0)
flength = strlen(fname);
for (len = flength; len >= 0; len--)
if (fpattern_submatch(pat, fname, flength, keepcase))
break;
return len;
}
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
#if TEST
/* Local variables */
static int count = 0;
static int fails = 0;
static int stop_on_fail = FALSE;
/*-----------------------------------------------------------------------------
* test()
*/
static void test(int expect, const char *fname, const char *pat)
{
int failed;
int result;
char fbuf[80+1];
char pbuf[80+1];
count++;
printf("%3d. ", count);
if (fname == NULL)
{
printf("<null>\n");
}
else
{
strcpy(fbuf, fname);
printf("\"%s\"\n", fbuf);
}
if (pat == NULL)
{
printf(" <null>\n");
}
else
{
strcpy(pbuf, pat);
printf(" \"%s\"\n", pbuf);
}
result = fpattern_match(pat == NULL ? NULL : pbuf,
fname == NULL ? NULL : fbuf,
-1, FALSE);
failed = (result != expect);
printf(" -> %c, expected %c: %s\n",
"FT"[!!result], "FT"[!!expect], failed ? "FAIL ***" : "pass");
if (failed)
{
fails++;
if (stop_on_fail)
exit(1);
sleep(1);
}
printf("\n");
}
/*-----------------------------------------------------------------------------
* main()
* Test driver.
*/
int main(int argc, char **argv)
{
(void) argc; /* Shut up lint */
(void) argv; /* Shut up lint */
#if DEBUG
dbg_f = stdout;
#endif
printf("==========================================\n");
setlocale(LC_CTYPE, "");
#if UNIX
printf("[O/S is UNIX]\n");
#elif DOS
printf("[O/S is DOS]\n");
#else
printf("[O/S is unknown]\n");
#endif
#if 1 /* Set to nonzero to stop on first failure */
stop_on_fail = TRUE;
#endif
test(0, NULL, NULL);
test(0, NULL, "");
test(0, NULL, "abc");
test(0, "", NULL);
test(0, "abc", NULL);
test(1, "abc", "abc");
test(0, "ab", "abc");
test(0, "abcd", "abc");
test(0, "Foo.txt", "Foo.x");
test(1, "Foo.txt", "Foo.txt");
test(1, "Foo.txt", "foo.txt");
test(1, "FOO.txt", "foo.TXT");
test(1, "a", "?");
test(1, "foo.txt", "f??.txt");
test(1, "foo.txt", "???????");
test(0, "foo.txt", "??????");
test(0, "foo.txt", "????????");
test(1, "a", "`a");
test(1, "AB", "a`b");
test(0, "aa", "a`b");
test(1, "a`x", "a``x");
test(1, "a`x", "`a```x");
test(1, "a*x", "a`*x");
#if DELIM
test(0, "", "/");
test(0, "", "\\");
test(1, "/", "/");
test(1, "/", "\\");
test(1, "\\", "/");
test(1, "\\", "\\");
test(1, "a/b", "a/b");
test(1, "a/b", "a\\b");
test(1, "/", "*/*");
test(1, "foo/a.c", "f*/*.?");
test(1, "foo/a.c", "*/*");
test(0, "foo/a.c", "/*/*");
test(0, "foo/a.c", "*/*/");
test(1, "/", "~/~");
test(1, "foo/a.c", "f~/~.?");
test(0, "foo/a.c", "~/~");
test(1, "foo/abc", "~/~");
test(0, "foo/a.c", "/~/~");
test(0, "foo/a.c", "~/~/");
#endif
test(0, "", "*");
test(1, "a", "*");
test(1, "ab", "*");
test(1, "abc", "**");
test(1, "ab.c", "*.?");
test(1, "ab.c", "*.*");
test(1, "ab.c", "*?");
test(1, "ab.c", "?*");
test(1, "ab.c", "?*?");
test(1, "ab.c", "?*?*");
test(1, "ac", "a*c");
test(1, "axc", "a*c");
test(1, "ax-yyy.c", "a*c");
test(1, "ax-yyy.c", "a*x-yyy.c");
test(1, "axx/yyy.c", "a*x/*c");
#if SUBCLOS
test(0, "", "~");
test(1, "a", "~");
test(1, "ab", "~");
test(1, "abc", "~~");
test(1, "ab.c", "~.?");
test(1, "ab.c", "~.~");
test(0, "ab.c", "~?");
test(0, "ab.c", "?~");
test(0, "ab.c", "?~?");
test(1, "ab.c", "?~.?");
test(1, "ab.c", "?~?~");
test(1, "ac", "a~c");
test(1, "axc", "a~c");
test(0, "ax-yyy.c", "a~c");
test(1, "ax-yyyvc", "a~c");
test(1, "ax-yyy.c", "a~x-yyy.c");
test(0, "axx/yyy.c", "a~x/~c");
test(1, "axx/yyyvc", "a~x/~c");
#endif
test(0, "a", "!");
test(0, "a", "!a");
test(1, "a", "!b");
test(1, "abc", "!abb");
test(0, "a", "!*");
test(1, "abc", "!*.?");
test(1, "abc", "!*.*");
test(0, "", "!*"); /*!*/
test(0, "", "!*?"); /*!*/
test(0, "a", "!*?");
test(0, "a", "a!*");
test(1, "a", "a!?");
test(1, "a", "a!*?");
test(1, "ab", "*!?");
test(1, "abc", "*!?");
test(0, "ab", "?!?");
test(1, "abc", "?!?");
test(0, "a-b", "!a[-]b");
test(0, "a-b", "!a[x-]b");
test(0, "a=b", "!a[x-]b");
test(0, "a-b", "!a[x`-]b");
test(1, "a=b", "!a[x`-]b");
test(0, "a-b", "!a[x---]b");
test(1, "a=b", "!a[x---]b");
test(1, "abc", "a[b]c");
test(1, "aBc", "a[b]c");
test(1, "abc", "a[bB]c");
test(1, "abc", "a[bcz]c");
test(1, "azc", "a[bcz]c");
test(0, "ab", "a[b]c");
test(0, "ac", "a[b]c");
test(0, "axc", "a[b]c");
test(0, "abc", "a[!b]c");
test(0, "abc", "a[!bcz]c");
test(0, "azc", "a[!bcz]c");
test(0, "ab", "a[!b]c");
test(0, "ac", "a[!b]c");
test(1, "axc", "a[!b]c");
test(1, "axc", "a[!bcz]c");
test(1, "a1z", "a[0-9]z");
test(0, "a1", "a[0-9]z");
test(0, "az", "a[0-9]z");
test(0, "axz", "a[0-9]z");
test(1, "a2z", "a[-0-9]z");
test(1, "a-z", "a[-0-9]z");
test(1, "a-b", "a[-]b");
test(0, "a-b", "a[x-]b");
test(0, "a=b", "a[x-]b");
test(1, "a-b", "a[x`-]b");
test(0, "a=b", "a[x`-]b");
test(1, "a-b", "a[x---]b");
test(0, "a=b", "a[x---]b");
test(0, "a0z", "a[!0-9]z");
test(1, "aoz", "a[!0-9]z");
test(0, "a1", "a[!0-9]z");
test(0, "az", "a[!0-9]z");
test(0, "a9Z", "a[!0-9]z");
test(1, "acz", "a[!-0-9]z");
test(0, "a7z", "a[!-0-9]z");
test(0, "a-z", "a[!-0-9]z");
test(0, "a-b", "a[!-]b");
test(0, "a-b", "a[!x-]b");
test(0, "a=b", "a[!x-]b");
test(0, "a-b", "a[!x`-]b");
test(1, "a=b", "a[!x`-]b");
test(0, "a-b", "a[!x---]b");
test(1, "a=b", "a[!x---]b");
test(1, "a!z", "a[`!0-9]z");
test(1, "a3Z", "a[`!0-9]z");
test(0, "A3Z", "a[`!0`-9]z");
test(1, "a9z", "a[`!0`-9]z");
test(1, "a-z", "a[`!0`-9]z");
test(1, "ac", "a{b}c");
test(1, "abc", "a{b}c");
test(1, "abbc", "a{b}c");
test(1, "aBbBc", "a{b}c");
test(1, "abc", "a{bB}c");
test(1, "abc", "a{bpz}c");
test(1, "azc", "a{bcz}");
test(0, "ab", "a{b}c");
test(0, "axc", "a{b}c");
printf("%d tests, %d failures\n", count, fails);
return (fails == 0 ? 0 : 1);
}
#endif /* TEST */
/* End fpattern.c */

187
SOURCE/amx/fpattern.h Normal file
View File

@ -0,0 +1,187 @@
/******************************************************************************
* fpattern.h
* Functions for matching filename patterns to filenames.
*
* Usage
* Filename patterns are composed of regular (printable) characters which
* may comprise a filename as well as special pattern matching characters:
*
* . Matches a period (.).
* Note that a period in a filename is not treated any
* differently than any other character.
*
* ? Any.
* Matches any single character except '/' or '\'.
*
* * Closure.
* Matches zero or more occurences of any characters other
* than '/' or '\'.
* Leading '*' characters are allowed.
*
* SUB Substitute (^Z); optionally supported.
* Similar to '*', this matches zero or more occurences of
* any characters other than '/', '\', or '.'.
* Leading '^Z' characters are allowed.
*
* [ab] Set.
* Matches the single character 'a' or 'b'.
* If the dash '-' character is to be included, it must
* immediately follow the opening bracket '['.
* If the closing bracket ']' character is to be included,
* it must be preceded by a quote '`'.
*
* [a-z] Range.
* Matches a single character in the range 'a' to 'z'.
* Ranges and sets may be combined within the same set of
* brackets.
*
* [!R] Exclusive range.
* Matches a single character not in the range 'R'.
* If range 'R' includes the dash '-' character, the dash
* must immediately follow the caret '!'.
*
* ! Not.
* Makes the following pattern (up to the next '/') match
* any filename except those what it would normally match.
*
* / Path separator (UNIX and DOS).
* Matches a '/' or '\' pathname (directory) separator.
* Multiple separators are treated like a single
* separator.
* A leading separator indicates an absolute pathname.
*
* \ Path separator (DOS).
* Same as the '/' character.
* Note that this character must be escaped if used within
* string constants ("\\").
*
* \ Quote (UNIX).
* Makes the next character a regular (nonspecial)
* character.
* Note that to match the quote character itself, it must
* be quoted.
* Note that this character must be escaped if used within
* string constants ("\\").
*
* ` Quote (DOS).
* Makes the next character a regular (nonspecial)
* character.
* Note that to match the quote character itself, it must
* be quoted.
*
* Upper and lower case alphabetic characters are considered identical,
* i.e., 'a' and 'A' match each other.
* (What constitutes a lowercase letter depends on the current locale
* settings.)
*
* Spaces and control characters are treated as normal characters.
*
* Examples
* The following patterns in the left column will match the filenames in
* the middle column and will not match filenames in the right column:
*
* Pattern Will Match Will Not Match
* ------- ---------- --------------
* a a (only) (anything else)
* a. a. (only) (anything else)
* a?c abc, acc, arc, a.c a, ac, abbc
* a*c ac, abc, abbc, acc, a.c a, ab, acb, bac
* a* a, ab, abb, a., a.b b, ba
* * a, ab, abb, a., .foo, a.foo (nothing)
* *. a., ab., abb., a.foo. a, ab, a.foo, .foo
* *.* a., a.b, ah.bc.foo a
* ^Z a, ab, abb a., .foo, a.foo
* ^Z. a., ab., abb. a, .foo, a.foo
* ^Z.* a, a., .foo, a.foo ab, abb
* *2.c 2.c, 12.c, foo2.c, foo.12.c 2x.c
* a[b-z]c abc, acc, azc (only) (anything else)
* [ab0-9]x ax, bx, 0x, 9x zx
* a[-.]b a-b, a.b (only) (anything else)
* a[!a-z]b a0b, a.b, a@b aab, azb, aa0b
* a[!-b]x a0x, a+x, acx a-x, abx, axxx
* a[-!b]x a-x, a!x, abx (only) (anything else)
* a[`]]x a]x (only) (anything else)
* a``x a`x (only) (anything else)
* oh`! oh! (only) (anything else)
* is`?it is?it (only) (anything else)
* !a?c a, ac, ab, abb, acb, a.foo abc, a.c, azc
*
* History
* 1.00 1997-01-03 David Tribble.
* First cut.
* 1.01 1997-01-03 David Tribble.
* Added '^Z' pattern character.
* Added fpattern_matchn().
* 1.02 1997-01-26 David Tribble.
* Changed range negation character from '^' to '!', ala Unix.
* 1.03 1997-08-02 David Tribble.
* Added 'FPAT_XXX' macro constants.
*
* Limitations
* This code is copyrighted by the author, but permission is hereby
* granted for its unlimited use provided that the original copyright
* and authorship notices are retained intact.
*
* Other queries can be sent to:
* dtribble@technologist.com
* david.tribble@beasys.com
* dtribble@flash.net
*
* Copyright ©1997 by David R. Tribble, all rights reserved.
* $Id: fpattern.h 3612 2006-07-22 09:59:46Z thiadmer $
*/
#ifndef fpattern_h
#define fpattern_h 1
#ifdef __cplusplus
extern "C"
{
#endif
/* Identification */
#if 0
static const char fpattern_h_id[] = "@(#)lib/fpattern.h 1.03";
#endif
/* Manifest constants */
#define FPAT_QUOTE '\\' /* Quotes a special char */
#define FPAT_QUOTE2 '`' /* Quotes a special char */
#define FPAT_DEL '/' /* Path delimiter (used only when DELIM is true) */
#define FPAT_DEL2 '\\' /* Path delimiter (used only when DELIM is true) */
#define FPAT_DOT '.' /* Dot char */
#define FPAT_NOT '!' /* Exclusion */
#define FPAT_ANY '?' /* Any one char */
#define FPAT_CLOS '*' /* Zero or more chars */
#define FPAT_CLOSP '\x1A' /* Zero or more nondelimiters (used only when SUBCLOS is true) */
#define FPAT_SET_L '[' /* Set/range open bracket */
#define FPAT_SET_R ']' /* Set/range close bracket */
#define FPAT_MSET_L '{' /* Multi-set/range open bracket */
#define FPAT_MSET_R '}' /* Multi-set/range close bracket*/
#define FPAT_SET_NOT '!' /* Set exclusion */
#define FPAT_SET_THRU '-' /* Set range of chars */
/* Model-dependent aliases */
/* omitted */
/* Public functions */
extern int fpattern_isvalid(const char *pat);
extern int fpattern_match(const char *pat, const char *fname, int flength, int keepcase);
extern int fpattern_matchn(const char *pat, const char *fname, int flength, int keepcase);
#ifdef __cplusplus
}
#endif
#endif /* fpattern_h */
/* End fpattern.h */

BIN
SOURCE/amx/obj/amxexecc.obj Normal file

Binary file not shown.

BIN
SOURCE/amx/obj/amxexecs.obj Normal file

Binary file not shown.

BIN
SOURCE/amx/obj/amxjitr.obj Normal file

Binary file not shown.

BIN
SOURCE/amx/obj/amxjits.obj Normal file

Binary file not shown.

98
SOURCE/amx/osdefs.h Normal file
View File

@ -0,0 +1,98 @@
/* __MSDOS__ set when compiling for DOS (not Windows)
* _Windows set when compiling for any version of Microsoft Windows
* __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode)
* __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows)
*
* Copyright 1998-2005, ITB CompuPhase, The Netherlands.
* info@compuphase.com.
*/
#ifndef _OSDEFS_H
#define _OSDEFS_H
/* Every compiler uses different "default" macros to indicate the mode
* it is in. Throughout the source, we use the Borland C++ macros, so
* the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to
* those of Borland C++.
*/
#if defined(__WATCOMC__)
# if defined(__WINDOWS__) || defined(__NT__)
# define _Windows 1
# endif
# ifdef __386__
# define __32BIT__ 1
# endif
# if defined(_Windows) && defined(__32BIT__)
# define __WIN32__ 1
# endif
#elif defined(_MSC_VER)
# if defined(_WINDOWS) || defined(_WIN32)
# define _Windows 1
# endif
# ifdef _WIN32
# define __WIN32__ 1
# define __32BIT__ 1
# endif
#endif
#if defined __FreeBSD__
#include <sys/endian.h>
#elif defined LINUX
#include <endian.h>
#endif
/* Linux NOW has these */
#if !defined BIG_ENDIAN
#define BIG_ENDIAN 4321
#endif
#if !defined LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */
#if !defined BYTE_ORDER
#if defined UCLINUX
#define BYTE_ORDER BIG_ENDIAN
#else
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#endif
#if defined __MSDOS__ || defined __WIN32__ || defined _Windows
#define DIRSEP_CHAR '\\'
#elif defined macintosh
#define DIRSEP_CHAR ':'
#else
#define DIRSEP_CHAR '/' /* directory separator character */
#endif
/* _MAX_PATH is sometimes called differently and it may be in limits.h or
* stdlib.h instead of stdio.h.
*/
#if !defined _MAX_PATH
/* not defined, perhaps stdio.h was not included */
#if !defined PATH_MAX
#include <stdio.h>
#endif
#if !defined _MAX_PATH && !defined PATH_MAX
/* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */
#include <limits.h>
#endif
#if !defined _MAX_PATH && !defined PATH_MAX
/* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */
#include <stdlib.h>
#endif
/* if _MAX_PATH is undefined, try common alternative names */
#if !defined _MAX_PATH
#if defined MAX_PATH
#define _MAX_PATH MAX_PATH
#elif defined _POSIX_PATH_MAX
#define _MAX_PATH _POSIX_PATH_MAX
#else
/* everything failed, actually we have a problem here... */
#define _MAX_PATH 1024
#endif
#endif
#endif
#endif /* _OSDEFS_H */

2865
SOURCE/amx/pawndbg.c Normal file

File diff suppressed because it is too large Load Diff

397
SOURCE/amx/pawnrun.c Normal file
View File

@ -0,0 +1,397 @@
/* Simple "run-time" for the Pawn Abstract Machine
*
* Copyright (c) ITB CompuPhase, 1997-2006
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: pawnrun.c 3615 2006-07-27 07:49:08Z thiadmer $
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h> /* for memset() (on some compilers) */
#include "osdefs.h" /* for _MAX_PATH */
#include "amx.h"
#include <time.h>
#if !defined CLOCKS_PER_SEC /* some (older) compilers do not have it */
#define CLOCKS_PER_SEC CLK_TCK
#endif
#if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__)
#include <binreloc.h> /* from BinReloc, see www.autopackage.org */
#endif
#if defined AMXDBG
#include "amxdbg.h"
static char g_filename[_MAX_PATH];/* for loading the debug information */
#endif
/* These initialization functions are part of the "extension modules"
* (libraries with native functions) that this run-time uses. More
* extension modules may be dynamically linked (depending on whether
* support for dynamic linking is enabled).
*/
extern int AMXEXPORT amx_ConsoleInit(AMX *amx);
extern int AMXEXPORT amx_CoreInit(AMX *amx);
AMX *global_amx;
int AMXAPI srun_Monitor(AMX *amx);
static int abortflagged = 0;
void sigabort(int sig)
{
/* install the debug hook procedure if this was not done already */
amx_SetDebugHook(global_amx, srun_Monitor);
abortflagged=1;
signal(sig,sigabort); /* re-install the signal handler */
}
typedef struct tagSTACKINFO {
long maxstack, maxheap;
} STACKINFO;
/* srun_Monitor()
* A simple debug hook, that allows the user to break out of a program
* and that monitors stack usage. Note that the stack usage can only be
* properly monitored when the debug hook is installed from the start
* of the script to the end.
*/
int AMXAPI srun_Monitor(AMX *amx)
{
int err;
STACKINFO *stackinfo;
/* record the heap and stack usage */
err = amx_GetUserData(amx, AMX_USERTAG('S','t','c','k'), (void**)&stackinfo);
if (err == AMX_ERR_NONE) {
if (amx->stp - amx->stk > stackinfo->maxstack)
stackinfo->maxstack = amx->stp - amx->stk;
if (amx->hea - amx->hlw > stackinfo->maxheap)
stackinfo->maxstack = amx->stp - amx->stk;
} /* if */
/* check whether an "abort" was requested */
return abortflagged ? AMX_ERR_EXIT : AMX_ERR_NONE;
}
/* aux_LoadProgram()
* Load a compiled Pawn script into memory and initialize the abstract machine.
* This function is extracted out of AMXAUX.C.
*/
int AMXAPI aux_LoadProgram(AMX *amx, char *filename, void *memblock)
{
FILE *fp;
AMX_HEADER hdr;
int result, didalloc;
/* open the file, read and check the header */
if ((fp = fopen(filename, "rb")) == NULL)
return AMX_ERR_NOTFOUND;
fread(&hdr, sizeof hdr, 1, fp);
amx_Align16(&hdr.magic);
amx_Align32((uint32_t *)&hdr.size);
amx_Align32((uint32_t *)&hdr.stp);
if (hdr.magic != AMX_MAGIC) {
fclose(fp);
return AMX_ERR_FORMAT;
} /* if */
/* allocate the memblock if it is NULL */
didalloc = 0;
if (memblock == NULL) {
if ((memblock = malloc(hdr.stp)) == NULL) {
fclose(fp);
return AMX_ERR_MEMORY;
} /* if */
didalloc = 1;
/* after amx_Init(), amx->base points to the memory block */
} /* if */
/* read in the file */
rewind(fp);
fread(memblock, 1, (size_t)hdr.size, fp);
fclose(fp);
/* initialize the abstract machine */
memset(amx, 0, sizeof *amx);
result = amx_Init(amx, memblock);
/* free the memory block on error, if it was allocated here */
if (result != AMX_ERR_NONE && didalloc) {
free(memblock);
amx->base = NULL; /* avoid a double free */
} /* if */
/* save the filename, for optionally reading the debug information (we could
* also have read it here immediately
*/
#if defined AMXDBG
strcpy(g_filename, filename);
#endif
return result;
}
/* aux_FreeProgram()
* Clean up a program and free memory.
* This function is extracted out of AMXAUX.C.
*/
int AMXAPI aux_FreeProgram(AMX *amx)
{
if (amx->base!=NULL) {
amx_Cleanup(amx);
free(amx->base);
memset(amx,0,sizeof(AMX));
} /* if */
return AMX_ERR_NONE;
}
/* aux_StrError()
* Convert an error code to a "readable" string.
* This function is extracted out of AMXAUX.C.
*/
char * AMXAPI aux_StrError(int errnum)
{
static char *messages[] = {
/* AMX_ERR_NONE */ "(none)",
/* AMX_ERR_EXIT */ "Forced exit",
/* AMX_ERR_ASSERT */ "Assertion failed",
/* AMX_ERR_STACKERR */ "Stack/heap collision (insufficient stack size)",
/* AMX_ERR_BOUNDS */ "Array index out of bounds",
/* AMX_ERR_MEMACCESS */ "Invalid memory access",
/* AMX_ERR_INVINSTR */ "Invalid instruction",
/* AMX_ERR_STACKLOW */ "Stack underflow",
/* AMX_ERR_HEAPLOW */ "Heap underflow",
/* AMX_ERR_CALLBACK */ "No (valid) native function callback",
/* AMX_ERR_NATIVE */ "Native function failed",
/* AMX_ERR_DIVIDE */ "Divide by zero",
/* AMX_ERR_SLEEP */ "(sleep mode)",
/* 13 */ "(reserved)",
/* 14 */ "(reserved)",
/* 15 */ "(reserved)",
/* AMX_ERR_MEMORY */ "Out of memory",
/* AMX_ERR_FORMAT */ "Invalid/unsupported P-code file format",
/* AMX_ERR_VERSION */ "File is for a newer version of the AMX",
/* AMX_ERR_NOTFOUND */ "File or function is not found",
/* AMX_ERR_INDEX */ "Invalid index parameter (bad entry point)",
/* AMX_ERR_DEBUG */ "Debugger cannot run",
/* AMX_ERR_INIT */ "AMX not initialized (or doubly initialized)",
/* AMX_ERR_USERDATA */ "Unable to set user data field (table full)",
/* AMX_ERR_INIT_JIT */ "Cannot initialize the JIT",
/* AMX_ERR_PARAMS */ "Parameter error",
};
if (errnum < 0 || errnum >= sizeof messages / sizeof messages[0])
return "(unknown)";
return messages[errnum];
}
void ExitOnError(AMX *amx, int error)
{
if (error != AMX_ERR_NONE) {
#if defined AMXDBG
FILE *fp;
AMX_DBG amxdbg;
long line;
const char *filename;
#endif
printf("Run time error %d: \"%s\" on address %ld\n",
error, aux_StrError(error), (long)amx->cip);
/* optionally use the debug library to find the line number (if debug info.
* is available)
*/
#if defined AMXDBG
/* load the debug info. */
if ((fp=fopen(g_filename,"r")) != NULL && dbg_LoadInfo(&amxdbg,fp) == AMX_ERR_NONE) {
dbg_LookupFile(&amxdbg, amx->cip, &filename);
dbg_LookupLine(&amxdbg, amx->cip, &line);
printf("File: %s, line: %ld\n", filename, line);
dbg_FreeInfo(&amxdbg);
fclose(fp);
} /* if */
#endif
exit(1);
} /* if */
}
void PrintUsage(char *program)
{
printf("Usage: %s <filename> [options]\n\n"
"Options:\n"
"\t-stack\tto monitor stack usage\n"
"\t...\tother options are passed to the script\n"
, program);
exit(1);
}
int main(int argc,char *argv[])
{
AMX amx;
cell ret = 0;
int err, i;
clock_t start,end;
STACKINFO stackinfo = { 0 };
AMX_IDLE idlefunc;
if (argc < 2)
PrintUsage(argv[0]); /* function "usage" aborts the program */
#if !defined AMX_NODYNALOAD && (defined LINUX || defined __FreeBSD__ || defined __OpenBSD__)
/* see www.autopackage.org for the BinReloc module */
if (br_init(NULL)) {
char *libroot=br_find_exe_dir("");
setenv("AMXLIB",libroot,0);
free(libroot);
} /* if */
#endif
/* Load the program and initialize the abstract machine. */
err = aux_LoadProgram(&amx, argv[1], NULL);
if (err != AMX_ERR_NONE) {
/* try adding an extension */
char filename[_MAX_PATH];
strcpy(filename, argv[1]);
strcat(filename, ".amx");
err = aux_LoadProgram(&amx, filename, NULL);
if (err != AMX_ERR_NONE)
PrintUsage(argv[0]);
} /* if */
/* To install the debug hook "just-in-time", the signal function needs
* a pointer to the abstract machine(s) to abort. There are various ways
* to implement this; here I have done so with a simple global variable.
*/
global_amx = &amx;
signal(SIGINT, sigabort);
/* Initialize two core extension modules (more extension modules may be
* loaded & initialized automatically as DLLs or shared libraries.
*/
amx_ConsoleInit(&amx);
err = amx_CoreInit(&amx);
ExitOnError(&amx, err);
/* save the idle function, if set by any of the extension modules */
if (amx_GetUserData(&amx, AMX_USERTAG('I','d','l','e'), (void**)&idlefunc) != AMX_ERR_NONE)
idlefunc = NULL;
for (i = 2; i < argc; i++) {
if (strcmp(argv[i],"-stack") == 0) {
uint16_t flags;
amx_Flags(&amx, &flags);
if ((flags & AMX_FLAG_NOCHECKS) != 0)
printf("This script was compiled with debug information removed.\n"
"Stack monitoring is disfunctional\n\n");
/* Set "user data" with which the debug monitor can monitor stack usage
* per abstract machine (in this example, though, there is only one abstract
* machine, so a global variable would have sufficed).
*/
memset(&stackinfo, 0, sizeof stackinfo);
err = amx_SetUserData(&amx, AMX_USERTAG('S','t','c','k'), &stackinfo);
ExitOnError(&amx, err);
/* Install the debug hook, so that we can start monitoring the stack/heap
* usage right from the beginning of the script.
*/
amx_SetDebugHook(&amx, srun_Monitor);
} /* if */
} /* for */
start=clock();
/* Run the compiled script and time it. The "sleep" instruction causes the
* abstract machine to return in a "restartable" state (it restarts from
* the point that it stopped. This allows for a kind of light-weight
* cooperative multi-tasking. As native functions (or the debug hook) can
* also force an abstract machine to "sleep", you can also use it to create
* "latent functions": functions that allow the host application to continue
* processing, and/or other abstract machines to run, while they wait for
* some resource.
*/
err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN);
while (err == AMX_ERR_SLEEP) {
if (idlefunc != NULL) {
/* If the abstract machine was put to sleep, we can handle events during
* that time. To save the "restart point", we must make a copy of the AMX
* (keeping the stack, frame, instruction pointer and other vital
* registers), but without cloning the entire abstract machine.
* There should be some criterion on when the abstract machine must be
* "woken up". In this example run-time, the parameter of the sleep
* instruction is taken to be a delay in milliseconds. In your own host
* application, you can choose to wait on a resource/semaphore or other.
*/
AMX nested_amx = amx;
clock_t stamp = clock();
while (((clock() - stamp)*1000)/CLOCKS_PER_SEC < amx.pri
&& (err = idlefunc(&nested_amx,amx_Exec)) == AMX_ERR_NONE)
/* nothing */;
ExitOnError(&nested_amx, err);
} /* if */
err = amx_Exec(&amx, &ret, AMX_EXEC_CONT);
} /* while */
ExitOnError(&amx, err);
/* For event-driven programs, we also need to loop over the idle/monitor
* function that some extension module installed (this could be the console
* module, for example). We did this if the main program was put to "sleep",
* but we do that here too.
*/
if (idlefunc != NULL) {
while ((err = idlefunc(&amx,amx_Exec)) == AMX_ERR_NONE)
/* nothing */;
ExitOnError(&amx, err);
} /* if */
end=clock();
/* Free the compiled script and resources. This also unloads and DLLs or
* shared libraries that were registered automatically by amx_Init().
*/
aux_FreeProgram(&amx);
/* Print the return code of the compiled script (often not very useful),
* its run time, and its stack usage.
*/
if (ret!=0)
printf("\nReturn value: %ld\n", (long)ret);
printf("\nRun time: %.2f seconds\n",(double)(end-start)/CLOCKS_PER_SEC);
if (stackinfo.maxstack != 0 || stackinfo.maxheap != 0) {
printf("Stack usage: %ld cells (%ld bytes)\n",
stackinfo.maxstack / sizeof(cell), stackinfo.maxstack);
printf("Heap usage: %ld cells (%ld bytes)\n",
stackinfo.maxheap / sizeof(cell), stackinfo.maxheap);
} /* if */
#if defined AMX_TERMINAL
/* This is likely a graphical terminal, which should not be closed
* automatically
*/
{
extern int amx_termctl(int,int);
while (amx_termctl(4,0))
/* nothing */;
}
#endif
return 0;
}

View File

@ -0,0 +1,56 @@
/* Command-line shell for the "Small" Abstract Machine.
*
* Copyright (c) ITB CompuPhase, 2001-2005
*
* This file may be freely used. No warranties of any kind.
*/
#include <stdio.h>
#include <stdlib.h> /* for exit() */
#include <signal.h>
#include <string.h> /* for memset() (on some compilers) */
#include "amx.h"
#include "amxaux.c"
static void ErrorExit(AMX *amx, int errorcode)
{
printf("Run time error %d: \"%s\" on line %ld\n",
errorcode, aux_StrError(errorcode),
(amx != NULL) ? amx->curline : 0);
exit(1);
}
static void PrintUsage(char *program)
{
printf("Usage: %s <filename>\n<filename> is a compiled script.\n", program);
exit(1);
}
int main(int argc,char *argv[])
{
extern AMX_NATIVE_INFO console_Natives[];
extern AMX_NATIVE_INFO core_Natives[];
AMX amx;
cell ret = 0;
int err;
if (argc != 2)
PrintUsage(argv[0]);
err = aux_LoadProgram(&amx, argv[1], NULL, NULL);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
amx_Register(&amx, console_Natives, -1);
err = amx_Register(&amx, core_Natives, -1);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN, 0);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
printf("%s returns %ld\n", argv[1], (long)ret);
aux_FreeProgram(&amx);
return 0;
}

View File

@ -0,0 +1,82 @@
/* Command-line shell for the "Small" Abstract Machine.
*
* Copyright (c) ITB CompuPhase, 2001-2005
*
* This file may be freely used. No warranties of any kind.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h> /* for memset() (on some compilers) */
#include "amx.h"
#include "amxaux.c"
void core_Init(void); /* two functions from AMX_CORE.C */
void core_Exit(void);
static int abortflagged = 0;
void sigabort(int sig)
{
abortflagged=1;
signal(sig,sigabort); /* re-install the signal handler */
}
int AMXAPI srun_Monitor(AMX *amx)
{
switch (amx->dbgcode) {
case DBG_INIT:
return AMX_ERR_NONE;
case DBG_LINE:
/* check whether an "abort" was requested */
return abortflagged ? AMX_ERR_EXIT : AMX_ERR_NONE;
default:
return AMX_ERR_DEBUG;
} /* switch */
}
static void ErrorExit(AMX *amx, int errorcode)
{
printf("Run time error %d: \"%s\" on line %ld\n",
errorcode, aux_StrError(errorcode),
(amx != NULL) ? amx->curline : 0);
exit(1);
}
static void PrintUsage(char *program)
{
printf("Usage: %s <filename>\n<filename> is a compiled script.\n", program);
exit(1);
}
int main(int argc,char *argv[])
{
extern AMX_NATIVE_INFO console_Natives[];
extern AMX_NATIVE_INFO core_Natives[];
AMX amx;
cell ret = 0;
int err;
if (argc != 2)
PrintUsage(argv[0]);
err = aux_LoadProgram(&amx, argv[1], NULL, srun_Monitor);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
amx_Register(&amx, console_Natives, -1);
err = amx_Register(&amx, core_Natives, -1);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
signal(SIGINT, sigabort);
err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN, 0);
if (err != AMX_ERR_NONE)
ErrorExit(&amx, err);
printf("%s returns %ld\n", argv[1], (long)ret);
aux_FreeProgram(&amx);
return 0;
}

Some files were not shown because too many files have changed in this diff Show More