New upstream version 0~git20160517
Sophie Brun
7 years ago
0 | The MIT License (MIT) | |
1 | ||
2 | Copyright (c) 2014 Daurnimator | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | SOFTWARE. |
0 | # [Systemd](http://freedesktop.org/wiki/Software/systemd/) for Lua. | |
1 | ||
2 | This library is for working with systemd from scripts and daemons written in Lua. | |
3 | ||
4 | Where necessary, the low level `libsystemd` functions have been bound in C. | |
5 | Higher level functions with more idiomatic lua semantics are written in Lua on top of these C primitives. | |
6 | ||
7 | Compatible with Lua 5.1, 5.2 and 5.3 (thanks [compat-5.3](https://github.com/keplerproject/lua-compat-5.3)). | |
8 | ||
9 | ||
10 | # Status | |
11 | ||
12 | Waiting for API to stabilise before making an initial release. | |
13 | ||
14 | ||
15 | ## Todo | |
16 | ||
17 | - [x] [Notify](http://www.freedesktop.org/software/systemd/man/sd_notify.html) - Notify service manager about start-up completion and other daemon status changes | |
18 | - [x] [ID128](http://www.freedesktop.org/software/systemd/man/sd-id128.html) - APIs for processing 128-bit IDs | |
19 | - [x] [Journal](http://www.freedesktop.org/software/systemd/man/sd-journal.html) | |
20 | - [x] [Journal Writing](http://www.freedesktop.org/software/systemd/man/sd_journal_sendv.html) | |
21 | - [x] [Journal Reading](http://www.freedesktop.org/software/systemd/man/sd_journal_next.html) | |
22 | - [x] [Journal Change Notification](http://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html) | |
23 | - [x] [Login](http://www.freedesktop.org/software/systemd/man/sd-login.html) | |
24 | - [x] [PID/Peer Information](http://www.freedesktop.org/software/systemd/man/sd_pid_get_session.html) | |
25 | - [x] [User State](http://www.freedesktop.org/software/systemd/man/sd_uid_get_state.html) | |
26 | - [x] [Session Information](http://www.freedesktop.org/software/systemd/man/sd_session_is_active.html) | |
27 | - [x] [Seat Information](http://www.freedesktop.org/software/systemd/man/sd_seat_get_active.html) | |
28 | - [x] [Login Monitoring](http://www.freedesktop.org/software/systemd/man/sd_login_monitor.html) - Monitor login sessions, seats, users and virtual machines/containers | |
29 | - [ ] Unit Control - Requires use of [dbus API](http://www.freedesktop.org/wiki/Software/systemd/dbus/) | |
30 | ||
31 | ||
32 | # Installation | |
33 | ||
34 | lua-systemd is on luarocks: https://luarocks.org/modules/daurnimator/systemd | |
35 | ||
36 | Install via luarocks: `luarocks install --server=http://luarocks.org/manifests/daurnimator systemd` | |
37 | ||
38 | ||
39 | # Usage | |
40 | ||
41 | ## Bound from C | |
42 | ||
43 | All functions return `nil, error_message [, errno]` in case of error. | |
44 | ||
45 | C | Lua | Comments | |
46 | -----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|---------------------------------------- | |
47 | [`SD_LISTEN_FDS_START`](http://www.freedesktop.org/software/systemd/man/SD_LISTEN_FDS_START.html) | `systemd.daemon.LISTEN_FDS_START` | | |
48 | [`sd_notify()`](http://www.freedesktop.org/software/systemd/man/sd_notify.html) | `systemd.daemon.notify()` | | |
49 | [`sd_pid_notify()`](http://www.freedesktop.org/software/systemd/man/sd_pid_notify.html) | `systemd.daemon.pid_notify()` | | |
50 | [`sd_pid_notify_with_fds()`](http://www.freedesktop.org/software/systemd/man/sd_pid_notify_with_fds.html) | `systemd.daemon.pid_notify_with_fds()` | | |
51 | [`sd_booted()`](http://www.freedesktop.org/software/systemd/man/sd_booted.html) | `systemd.daemon.booted()` | | |
52 | [`sd_listen_fds()`](http://www.freedesktop.org/software/systemd/man/sd_listen_fds.html) | `systemd.daemon.listen_fds()` | | |
53 | [`sd_journal_sendv()`](http://www.freedesktop.org/software/systemd/man/sd_journal_sendv.html) | `systemd.journal.sendv()` | | |
54 | [`sd_journal_perror()`](http://www.freedesktop.org/software/systemd/man/sd_journal_perror.html) | `systemd.journal.perror()` | | |
55 | [`sd_journal_stream_fd()`](http://www.freedesktop.org/software/systemd/man/sd_journal_stream_fd.html) | `systemd.journal.stream_fd()` | On success, returns a Lua `file` object instead of raw file descriptor | |
56 | [`SD_JOURNAL_LOCAL_ONLY`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_LOCAL_ONLY.html) | `systemd.journal.OPEN.LOCAL_ONLY` | | |
57 | [`SD_JOURNAL_RUNTIME_ONLY`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_RUNTIME_ONLY.html) | `systemd.journal.OPEN.RUNTIME_ONLY` | | |
58 | [`SD_JOURNAL_SYSTEM`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_SYSTEM.html) | `systemd.journal.OPEN.SYSTEM` | | |
59 | [`SD_JOURNAL_CURRENT_USER`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_CURRENT_USER.html) | `systemd.journal.OPEN.CURRENT_USER` | | |
60 | [`sd_journal_open()`](http://www.freedesktop.org/software/systemd/man/sd_journal_open.html) | `systemd.journal.open()` | | |
61 | [`sd_journal_open_directory()`](http://www.freedesktop.org/software/systemd/man/sd_journal_open_directory.html) | `systemd.journal.open_directory()` | | |
62 | [`sd_journal_open_files()`](http://www.freedesktop.org/software/systemd/man/sd_journal_open_files.html) | `systemd.journal.open_files()` | | |
63 | [`sd_journal_open_container()`](http://www.freedesktop.org/software/systemd/man/sd_journal_open_container.html) | `systemd.journal.open_container()` | | |
64 | [`sd_journal_close()`](http://www.freedesktop.org/software/systemd/man/sd_journal_close.html) | | Bound as `__gc` metamethod on journal objects | |
65 | [`sd_journal_get_cutoff_realtime_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_cutoff_realtime_usec.html) | `my_journal:get_cutoff_realtime_usec()` | | |
66 | [`sd_journal_get_cutoff_monotonic_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_cutoff_monotonic_usec.html) | `my_journal:get_cutoff_monotonic_usec()` | | |
67 | [`sd_journal_get_usage()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_usage.html) | `my_journal:get_usage()` | | |
68 | [`sd_journal_next()`](http://www.freedesktop.org/software/systemd/man/sd_journal_next.html) | `my_journal:next()` | | |
69 | [`sd_journal_next_skip()`](http://www.freedesktop.org/software/systemd/man/sd_journal_next_skip.html) | `my_journal:next_skip()` | | |
70 | [`sd_journal_previous()`](http://www.freedesktop.org/software/systemd/man/sd_journal_previous.html) | `my_journal:previous()` | | |
71 | [`sd_journal_previous_skip()`](http://www.freedesktop.org/software/systemd/man/sd_journal_previous_skip.html) | `my_journal:previous_skip()` | | |
72 | [`sd_journal_seek_head()`](http://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html) | `my_journal:seek_head()` | | |
73 | [`sd_journal_seek_tail()`](http://www.freedesktop.org/software/systemd/man/sd_journal_seek_tail.html) | `my_journal:seek_tail()` | | |
74 | [`sd_journal_seek_monotonic_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_seek_monotonic_usec.html) | `my_journal:seek_monotonic_usec()` | | |
75 | [`sd_journal_seek_realtime_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_seek_realtime_usec.html) | `my_journal:seek_realtime_usec()` | | |
76 | [`sd_journal_seek_cursor()`](http://www.freedesktop.org/software/systemd/man/sd_journal_seek_cursor.html) | `my_journal:seek_cursor()` | | |
77 | [`sd_journal_get_cursor()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_cursor.html) | `my_journal:get_cursor()` | | |
78 | [`sd_journal_test_cursor()`](http://www.freedesktop.org/software/systemd/man/sd_journal_test_cursor.html) | `my_journal:test_cursor()` | | |
79 | [`sd_journal_get_realtime_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_realtime_usec.html) | `my_journal:get_realtime_usec()` | | |
80 | [`sd_journal_get_monotonic_usec()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_monotonic_usec.html) | `my_journal:get_monotonic_usec()` | | |
81 | [`sd_journal_get_data()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html) | `my_journal:get_data()` | | |
82 | [`sd_journal_enumerate_data()`](http://www.freedesktop.org/software/systemd/man/sd_journal_enumerate_data.html) | `my_journal:enumerate_data()` | | |
83 | [`sd_journal_restart_data()`](http://www.freedesktop.org/software/systemd/man/sd_journal_restart_data.html) | `my_journal:restart_data()` | | |
84 | [`sd_journal_query_unique()`](http://www.freedesktop.org/software/systemd/man/sd_journal_query_unique.html) | `my_journal:query_unique()` | | |
85 | [`sd_journal_enumerate_unique()`](http://www.freedesktop.org/software/systemd/man/sd_journal_enumerate_unique.html) | `my_journal:enumerate_unique()` | | |
86 | [`sd_journal_restart_unique()`](http://www.freedesktop.org/software/systemd/man/sd_journal_restart_unique.html) | `my_journal:restart_unique()` | | |
87 | [`sd_journal_set_data_threshold()`](http://www.freedesktop.org/software/systemd/man/sd_journal_set_data_threshold.html) | `my_journal:set_data_threshold()` | | |
88 | [`sd_journal_get_data_threshold()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_data_threshold.html) | `my_journal:get_data_threshold()` | | |
89 | [`sd_journal_add_match()`](http://www.freedesktop.org/software/systemd/man/sd_journal_add_match.html) | `my_journal:add_match()` | | |
90 | [`sd_journal_add_disjunction()`](http://www.freedesktop.org/software/systemd/man/sd_journal_add_disjunction.html) | `my_journal:add_disjunction()` | | |
91 | [`sd_journal_add_conjunction()`](http://www.freedesktop.org/software/systemd/man/sd_journal_add_conjunction.html) | `my_journal:add_conjunction()` | | |
92 | [`sd_journal_flush_matches()`](http://www.freedesktop.org/software/systemd/man/sd_journal_flush_matches.html) | `my_journal:flush_matches()` | | |
93 | [`SD_JOURNAL_NOP`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_NOP.html) | `systemd.journal.WAKEUP.NOP` | | |
94 | [`SD_JOURNAL_APPEND`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_APPEND.html) | `systemd.journal.WAKEUP.APPEND` | | |
95 | [`SD_JOURNAL_INVALIDATE`](http://www.freedesktop.org/software/systemd/man/SD_JOURNAL_INVALIDATE.html) | `systemd.journal.WAKEUP.INVALIDATE` | | |
96 | [`sd_journal_get_fd()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html) | `my_journal:get_fd()` | | |
97 | [`sd_journal_get_events()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_events.html) | `my_journal:get_events()` | | |
98 | [`sd_journal_get_timeout()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_timeout.html) | `my_journal:get_timeout()` | Returns `false` if timeout isn't available, otherwise returns value in seconds | |
99 | [`sd_journal_process()`](http://www.freedesktop.org/software/systemd/man/sd_journal_process.html) | `my_journal:process()` | | |
100 | [`sd_journal_wait()`](http://www.freedesktop.org/software/systemd/man/sd_journal_wait.html) | `my_journal:wait()` | `timeout` is in seconds instead of microseconds | |
101 | [`sd_journal_reliable_fd()`](http://www.freedesktop.org/software/systemd/man/sd_journal_reliable_fd.html) | `my_journal:reliable_fd()` | | |
102 | [`sd_id128_randomize()`](http://www.freedesktop.org/software/systemd/man/sd_id128_randomize.html) | `systemd.id128.randomize()` | Also available as `randomise` for any non-americans out there | |
103 | [`sd_id128_from_string()`](http://www.freedesktop.org/software/systemd/man/sd_id128_from_string.html) | `systemd.id128.from_string()` | | |
104 | [`sd_id128_get_machine()`](http://www.freedesktop.org/software/systemd/man/sd_id128_get_machine.html) | `systemd.id128.get_machine()` | | |
105 | [`sd_id128_get_boot()`](http://www.freedesktop.org/software/systemd/man/sd_id128_get_boot.html) | `systemd.id128.get_boot()` | | |
106 | [`sd_id128_to_string()`](http://www.freedesktop.org/software/systemd/man/sd_id128_to_string.html) | `my_id128:to_string()` | Also available as `__tostring` metamethod: `tostring(my_id128_t)` | |
107 | [`sd_id128_equal()`](http://www.freedesktop.org/software/systemd/man/sd_id128_equal.html) | `id128_a == id128_b` | Bound as `__eq` metamethod | |
108 | [`sd_journal_get_catalog_for_message_id()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_catalog_for_message_id.html) | `my_id128:get_catalog()` | | |
109 | [`sd_get_seats()`](http://www.freedesktop.org/software/systemd/man/sd_get_seats.html) | `systemd.login.get_seats()` | | |
110 | [`sd_get_sessions()`](http://www.freedesktop.org/software/systemd/man/sd_get_sessions.html) | `systemd.login.get_sessions()` | | |
111 | [`sd_get_uids()`](http://www.freedesktop.org/software/systemd/man/sd_get_uids.html) | `systemd.login.get_uids()` | | |
112 | [`sd_get_machine_names()`](http://www.freedesktop.org/software/systemd/man/sd_get_machine_names.html) | `systemd.login.get_machine_names()` | | |
113 | [`sd_pid_get_session()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_session.html) | `systemd.login.pid_get_session()` | | |
114 | [`sd_pid_get_unit()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_unit.html) | `systemd.login.pid_get_unit()` | | |
115 | [`sd_pid_get_user_unit()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_user_unit.html) | `systemd.login.pid_get_user_unit()` | | |
116 | [`sd_pid_get_owner_uid()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_owner_uid.html) | `systemd.login.pid_get_owner_uid()` | | |
117 | [`sd_pid_get_machine_name()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_machine_name.html) | `systemd.login.pid_get_machine_name()` | | |
118 | [`sd_pid_get_slice()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_slice.html) | `systemd.login.pid_get_slice()` | | |
119 | [`sd_pid_get_user_slice()`](http://www.freedesktop.org/software/systemd/man/sd_pid_get_user_slice.html) | `systemd.login.pid_get_user_slice()` | | |
120 | [`sd_peer_get_session()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_session.html) | `systemd.login.peer_get_session()` | | |
121 | [`sd_peer_get_unit()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_unit.html) | `systemd.login.peer_get_unit()` | | |
122 | [`sd_peer_get_user_unit()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_user_unit.html) | `systemd.login.peer_get_user_unit()` | | |
123 | [`sd_peer_get_owner_uid()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_owner_uid.html) | `systemd.login.peer_get_owner_uid()` | | |
124 | [`sd_peer_get_machine_name()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_machine_name.html) | `systemd.login.peer_get_machine_name()` | | |
125 | [`sd_peer_get_slice()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_slice.html) | `systemd.login.peer_get_slice()` | | |
126 | [`sd_peer_get_user_slice()`](http://www.freedesktop.org/software/systemd/man/sd_peer_get_user_slice.html) | `systemd.login.peer_get_user_slice()` | | |
127 | [`sd_uid_get_state()`](http://www.freedesktop.org/software/systemd/man/sd_uid_get_state.html) | `systemd.login.uid_get_state()` | | |
128 | [`sd_uid_is_on_seat()`](http://www.freedesktop.org/software/systemd/man/sd_uid_is_on_seat.html) | `systemd.login.uid_is_on_seat()` | | |
129 | [`sd_uid_get_sessions()`](http://www.freedesktop.org/software/systemd/man/sd_uid_get_sessions.html) | `systemd.login.uid_get_sessions()` | | |
130 | [`sd_uid_get_seats()`](http://www.freedesktop.org/software/systemd/man/sd_uid_get_seats.html) | `systemd.login.uid_get_seats()` | | |
131 | [`sd_uid_get_display()`](http://www.freedesktop.org/software/systemd/man/sd_uid_get_display.html) | `systemd.login.uid_get_display()` | | |
132 | [`sd_session_is_active()`](http://www.freedesktop.org/software/systemd/man/sd_session_is_active.html) | `systemd.login.session_is_active()` | | |
133 | [`sd_session_is_remote()`](http://www.freedesktop.org/software/systemd/man/sd_session_is_remote.html) | `systemd.login.session_is_remote()` | | |
134 | [`sd_session_get_state()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_state.html) | `systemd.login.session_get_state()` | | |
135 | [`sd_session_get_uid()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_uid.html) | `systemd.login.session_get_uid()` | | |
136 | [`sd_session_get_seat()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_seat.html) | `systemd.login.session_get_seat()` | | |
137 | [`sd_session_get_service()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_service.html) | `systemd.login.session_get_service()` | | |
138 | [`sd_session_get_type()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_type.html) | `systemd.login.session_get_type()` | | |
139 | [`sd_session_get_class()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_class.html) | `systemd.login.session_get_class()` | | |
140 | [`sd_session_get_desktop()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_desktop.html) | `systemd.login.session_get_desktop()` | | |
141 | [`sd_session_get_display()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_display.html) | `systemd.login.session_get_display()` | | |
142 | [`sd_session_get_remote_host()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_remote_host.html) | `systemd.login.session_get_remote_host()`| | |
143 | [`sd_session_get_remote_user()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_remote_user.html) | `systemd.login.session_get_remote_user()`| | |
144 | [`sd_session_get_tty()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_tty.html) | `systemd.login.session_get_tty()` | | |
145 | [`sd_session_get_vt()`](http://www.freedesktop.org/software/systemd/man/sd_session_get_vt.html) | `systemd.login.session_get_vt()` | | |
146 | [`sd_seat_get_active()`](http://www.freedesktop.org/software/systemd/man/sd_seat_get_active.html) | `systemd.login.seat_get_active()` | On success, returns `session, uid` | |
147 | [`sd_seat_get_sessions()`](http://www.freedesktop.org/software/systemd/man/sd_seat_get_sessions.html) | `systemd.login.seat_get_sessions()` | On success, returns `sessions, uids` | |
148 | [`sd_seat_can_multi_session()`](http://www.freedesktop.org/software/systemd/man/sd_seat_can_multi_session.html) | `systemd.login.seat_can_multi_session()` | | |
149 | [`sd_seat_can_tty()`](http://www.freedesktop.org/software/systemd/man/sd_seat_can_tty.html) | `systemd.login.seat_can_tty()` | | |
150 | [`sd_seat_can_graphical()`](http://www.freedesktop.org/software/systemd/man/sd_seat_can_graphical.html) | `systemd.login.seat_can_graphical()` | | |
151 | [`sd_machine_get_class()`](http://www.freedesktop.org/software/systemd/man/sd_machine_get_class.html) | `systemd.login.machine_get_class()` | | |
152 | [`sd_machine_get_ifindices()`](http://www.freedesktop.org/software/systemd/man/sd_machine_get_ifindices.html) | `systemd.login.machine_get_ifindices()` | | |
153 | [`sd_login_monitor_new()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_new.html) | `systemd.login.monitor()` | | |
154 | [`sd_login_monitor_unref()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_unref.html) | | Bound as `__gc` metamethod on monitor objects | |
155 | [`sd_login_monitor_flush()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_flush.html) | `my_login_monitor:flush()` | | |
156 | [`sd_login_monitor_get_fd()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_get_fd.html) | `my_login_monitor:get_fd()` | | |
157 | [`sd_login_monitor_get_events()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_get_events.html) | `my_login_monitor:get_events()` | | |
158 | [`sd_login_monitor_get_timeout()`](http://www.freedesktop.org/software/systemd/man/sd_login_monitor_get_timeout.html) | `my_login_monitor:get_timeout()` | Returns `false` if timeout isn't available, otherwise returns value in seconds | |
159 | ||
160 | ||
161 | ## Misc extras | |
162 | ||
163 | ### `systemd.daemon.notifyt(tbl)` and `systemd.daemon.pid_notifyt(tbl)` | |
164 | ||
165 | Like `notify`, but takes a lua table instead of a newline delimited list. | |
166 | Numbers will be coerced to strings. | |
167 | ||
168 | ```lua | |
169 | notifyt { READY = 1, STATUS = "Server now accepting connections", WATCHDOG = 1 } | |
170 | ``` | |
171 | ||
172 | ||
173 | ### `interval = systemd.daemon.watchdog_enabled()` | |
174 | ||
175 | Returns the watchdog interval (in seconds) if there is one set otherwise returns `false`. | |
176 | ||
177 | You should call `kick_dog` or `notify("WATCHDOG=1")` every half of this interval. | |
178 | ||
179 | Similar functionality to [`sd_watchdog_enabled()`](http://www.freedesktop.org/software/systemd/man/sd_watchdog_enabled.html) | |
180 | ||
181 | ||
182 | ### `systemd.daemon.kick_dog()` | |
183 | ||
184 | Tells systemd to update the watchdog timestamp. | |
185 | This should be called on an interval. | |
186 | ||
187 | ||
188 | ### `systemd.journal.LOG` | |
189 | ||
190 | Table containing the `syslog(3)` priority constants: `EMERG`, `ALERT`, `CRIT`, `ERR`, `WARNING`, `NOTICE`, `INFO`, `DEBUG` | |
191 | ||
192 | Useful as the second argument to `systemd.journal.streamfd()` | |
193 | ||
194 | ||
195 | ### `systemd.journal.print(priority, fmt_string, ...)` | |
196 | ||
197 | Same argument signature as C, but written in lua on top of `sendv()` and `string.format()` | |
198 | ||
199 | ||
200 | ### `systemd.journal.sendt(tbl)` | |
201 | ||
202 | Log a message to the journal with the key/value pairs from `tbl` | |
203 | ||
204 | ```lua | |
205 | systemd.journal.sendt { | |
206 | SYSLOG_IDENTIFIER = "identifier" ; | |
207 | SYSLOG_FACILITY = "facility" ; | |
208 | PRIORITY = systemd.journal.LOG.ERR ; | |
209 | MESSAGE = "something happended!" ; | |
210 | MY_CUSTOM_FIELD = "extra detail."; | |
211 | } | |
212 | ``` | |
213 | ||
214 | ||
215 | ### `value = my_journal:get(field)` | |
216 | ||
217 | Returns the given field from the current journal entry (which may be `nil`) | |
218 | ||
219 | Throws a lua error on failure. | |
220 | ||
221 | ||
222 | ### `my_journal:each_data()` | |
223 | ||
224 | A valid lua iterator that enumerates through field, value pairs. | |
225 | ||
226 | ```lua | |
227 | for field, value in my_journal:each_data() do | |
228 | print(field, value) | |
229 | end | |
230 | ``` | |
231 | ||
232 | Throws a lua error on failure. | |
233 | ||
234 | ||
235 | ### `my_journal:each_unique(field_name)` | |
236 | ||
237 | A valid lua iterator that enumerates through unique field values. | |
238 | ||
239 | ```lua | |
240 | -- Print each different `_SYSTEMD_UNIT` | |
241 | for value in my_journal:each_unique("_SYSTEMD_UNIT") do | |
242 | print(value) | |
243 | end | |
244 | ``` | |
245 | ||
246 | Throws a lua error on failure. | |
247 | ||
248 | ||
249 | ### `t = my_journal:to_table()` | |
250 | ||
251 | Converts the current journal entry to a lua table. | |
252 | ||
253 | Includes [Address Fields](http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#Address%20Fields): | |
254 | - `__CURSOR` | |
255 | - `__REALTIME_TIMESTAMP` | |
256 | - `__MONOTONIC_TIMESTAMP` | |
257 | ||
258 | ||
259 | ### `text = my_journal:get_catalog()` | |
260 | ||
261 | Looks up the current journal entry's `MESSAGE_ID` in the [message catalog](http://www.freedesktop.org/wiki/Software/systemd/catalog/). | |
262 | Substitutes the templated fields (between `@` symbols) with values from this journal entry. | |
263 | ||
264 | Returns: | |
265 | - the filled out catalogue entry as a string | |
266 | - `false` if `MESSAGE_ID` is not set, or does not exist in the catalogue | |
267 | - `nil, err_msg, errno` in case of failure. | |
268 | ||
269 | Same functionality as [`sd_journal_get_catalog()`](http://www.freedesktop.org/software/systemd/man/sd_journal_get_catalog.html). | |
270 | ||
271 | ||
272 | ### `systemd.messages` | |
273 | ||
274 | A table of well-known message ids as `id128` objects. | |
275 | ||
276 | Taken from [`sd-messages.h`](http://cgit.freedesktop.org/systemd/systemd/tree/src/systemd/sd-messages.h?id=HEAD) |
0 | #!/usr/bin/env lua | |
1 | --[[ | |
2 | This program acts like journalctl --list-boots | |
3 | ]] | |
4 | local sj = require "systemd.journal" | |
5 | local j = assert(sj.open()) | |
6 | ||
7 | local t = {} | |
8 | local n = 0 | |
9 | for boot_id in j:each_unique("_BOOT_ID") do | |
10 | local boot_info = { | |
11 | id = boot_id; | |
12 | } | |
13 | n = n + 1 | |
14 | t[n] = boot_info | |
15 | ||
16 | -- We need to find the first and last entries for each boot | |
17 | assert(j:add_match("_BOOT_ID="..boot_id)) | |
18 | ||
19 | assert(j:seek_head()) | |
20 | assert(j:next()) | |
21 | boot_info.head = j:get_realtime_usec() | |
22 | ||
23 | assert(j:seek_tail()) | |
24 | assert(j:previous()) | |
25 | boot_info.tail = j:get_realtime_usec() | |
26 | ||
27 | j:flush_matches() | |
28 | end | |
29 | ||
30 | table.sort(t, function(a,b) return a.head < b.head end) | |
31 | ||
32 | local d_width = math.floor(math.log(n, 10))+2 | |
33 | for i=1, n do | |
34 | local boot_info = t[i] | |
35 | io.write(string.format("%"..d_width.."d %s %s—%s\n", | |
36 | i-n, boot_info.id, | |
37 | os.date("%a %Y-%m-%d %H:%M:%S %Z", boot_info.head/1e6), | |
38 | os.date("%a %Y-%m-%d %H:%M:%S %Z", boot_info.tail/1e6) | |
39 | )) | |
40 | end |
0 | local journal = require "systemd.journal" | |
1 | ||
2 | function log(severity, msg, level) | |
3 | local info = debug.getinfo((level or 1)+1, "nlS") | |
4 | if info.currentline == -1 then | |
5 | info.currentline = nil | |
6 | end | |
7 | return assert(journal.sendt{ | |
8 | CODE_FILE = info.short_src; | |
9 | CODE_FUNC = info.name; | |
10 | CODE_LINE = info.currentline; | |
11 | PRIORITY = journal.LOG[severity:upper()] or 5; | |
12 | MESSAGE = msg; | |
13 | }) | |
14 | end |
0 | #!/usr/bin/env lua | |
1 | --[[ | |
2 | This program acts like `journalctl --new-id128` | |
3 | ]] | |
4 | ||
5 | local id128 = require "systemd.id128" | |
6 | ||
7 | local myuuid = tostring(assert(id128.randomise())) | |
8 | ||
9 | io.write(table.concat({ | |
10 | "As string:", | |
11 | myuuid, | |
12 | "", | |
13 | "As UUID:", | |
14 | myuuid:gsub("(........)(....)(....)(....)(........)", "%1-%2-%3-%4-%5"), | |
15 | "", | |
16 | "As macro:", | |
17 | "#define MESSAGE_XYZ SD_ID128_MAKE(" .. myuuid:gsub("(..)", "%1,"):sub(1,-2) .. ")", | |
18 | "", | |
19 | "As Python constant:", | |
20 | ">>> import uuid", | |
21 | ">>> MESSAGE_XYZ = uuid.UUID('" .. myuuid .. "')", | |
22 | "" | |
23 | },"\n")) | |
24 |
0 | #!/usr/bin/env lua | |
1 | --[[ | |
2 | This script will tail your journal. | |
3 | Output will be in json format. | |
4 | ||
5 | Uses the cqueues library (see http://25thandclement.com/~william/projects/cqueues.html) | |
6 | ]] | |
7 | ||
8 | local cqueues = require "cqueues" | |
9 | local bit = require "bit32" | |
10 | local json = require "dkjson" | |
11 | local sj = require "systemd.journal" | |
12 | ||
13 | local j = assert(sj.open()) | |
14 | assert(j:seek_tail()) | |
15 | assert(j:previous()) | |
16 | ||
17 | -- Wrap the journal object with an object that implements the cqueues.poll interface | |
18 | local wrap_journal do | |
19 | local methods = {} | |
20 | function methods:pollfd() | |
21 | return self.j:get_fd() | |
22 | end | |
23 | function methods:events() | |
24 | local mask = self.j:get_events() | |
25 | local events = "" | |
26 | if bit.band(mask, 1) ~= 0 then | |
27 | events = events .. "r" | |
28 | end | |
29 | if bit.band(mask, 4) ~= 0 then | |
30 | events = events .. "w" | |
31 | end | |
32 | return events | |
33 | end | |
34 | function methods:timeout() | |
35 | local m = self.j:get_timeout() | |
36 | if type(m) == "number" then | |
37 | local now = cqueues.monotime() | |
38 | return m - now; | |
39 | end | |
40 | end | |
41 | local mt = { | |
42 | __index = function(t,k) | |
43 | local f = methods[k] | |
44 | if f ~= nil then return f end | |
45 | -- Delegate to original journal methods | |
46 | local f = t.j[k] | |
47 | if type(f) == "function" then | |
48 | -- Need to make sure the correct 'self' is passed through | |
49 | return function(o, ...) | |
50 | if o == t then | |
51 | o = t.j | |
52 | end | |
53 | return f(o, ...) | |
54 | end | |
55 | end | |
56 | end; | |
57 | } | |
58 | function wrap_journal(j) | |
59 | return setmetatable({j = j}, mt) | |
60 | end | |
61 | end | |
62 | local w = wrap_journal(j) | |
63 | ||
64 | local q = cqueues.new() | |
65 | q:wrap(function() | |
66 | while true do | |
67 | local a = cqueues.poll(w) | |
68 | if a and a:process() ~= sj.WAKEUP.NOP then | |
69 | while a:next() do | |
70 | local t = assert(a:to_table()) | |
71 | print(json.encode(t)) | |
72 | end | |
73 | end | |
74 | end | |
75 | end) | |
76 | assert(q:loop()) |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" | |
2 | #include "compat-5.3.h" | |
3 | ||
4 | #include <errno.h> /* ENOTSUP */ | |
5 | #include <sys/types.h> /* pid_t */ | |
6 | ||
7 | #include <systemd/sd-daemon.h> | |
8 | ||
9 | #include "util.c" | |
10 | ||
11 | ||
12 | shim_weak_stub_declare(int, sd_booted, (), -ENOTSUP) | |
13 | shim_weak_stub_declare(int, sd_notify, (int unset_environment, const char *state), -ENOTSUP) | |
14 | shim_weak_stub_declare(int, sd_pid_notify, (pid_t pid, int unset_environment, const char *state), -ENOTSUP) | |
15 | shim_weak_stub_declare(int, sd_pid_notify_with_fds, (pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds), -ENOTSUP) | |
16 | shim_weak_stub_declare(int, sd_listen_fds, (int unset_environment), -ENOTSUP) | |
17 | ||
18 | static int handle_notify_result (lua_State *L, int err) { | |
19 | if (err > 0) { | |
20 | lua_pushboolean(L, 1); | |
21 | return 1; | |
22 | } else if (err == 0) { | |
23 | lua_pushnil(L); | |
24 | lua_pushliteral(L, "NOTIFY_SOCKET not set"); | |
25 | return 2; | |
26 | } else { | |
27 | return handle_error(L, -err); | |
28 | } | |
29 | } | |
30 | ||
31 | static int notify (lua_State *L) { | |
32 | int unset_environment = lua_toboolean(L, 1); | |
33 | const char *state = luaL_checkstring(L, 2); | |
34 | return handle_notify_result(L, shim_weak_stub(sd_notify)(unset_environment, state)); | |
35 | } | |
36 | ||
37 | static int pid_notify (lua_State *L) { | |
38 | pid_t pid = luaL_checkinteger(L, 1); | |
39 | int unset_environment = lua_toboolean(L, 2); | |
40 | const char *state = luaL_checkstring(L, 3); | |
41 | return handle_notify_result(L, shim_weak_stub(sd_pid_notify)(pid, unset_environment, state)); | |
42 | } | |
43 | ||
44 | static int pid_notify_with_fds (lua_State *L) { | |
45 | pid_t pid = luaL_checkinteger(L, 1); | |
46 | int unset_environment = lua_toboolean(L, 2); | |
47 | const char *state = luaL_checkstring(L, 3); | |
48 | int *fds; | |
49 | unsigned n_fds; | |
50 | unsigned i; | |
51 | luaL_checktype(L, 4, LUA_TTABLE); | |
52 | n_fds = lua_rawlen(L, 4); | |
53 | fds = lua_newuserdata(L, n_fds*sizeof(int)); | |
54 | for (i=0; i < n_fds; i++) { | |
55 | lua_rawgeti(L, 4, i+1); | |
56 | luaL_argcheck(L, lua_type(L, -1) == LUA_TNUMBER && lua_isinteger(L, -1), 4, "expected array of file descritors (integers)"); | |
57 | fds[i] = lua_tointeger(L, -1); | |
58 | lua_settop(L, 5); | |
59 | } | |
60 | return handle_notify_result(L, shim_weak_stub(sd_pid_notify_with_fds)(pid, unset_environment, state, fds, n_fds)); | |
61 | } | |
62 | ||
63 | static int booted (lua_State *L) { | |
64 | int booted = shim_weak_stub(sd_booted)(); | |
65 | if (booted >= 0) { | |
66 | lua_pushboolean(L, booted); | |
67 | return 1; | |
68 | } else { | |
69 | return handle_error(L, -booted); | |
70 | } | |
71 | } | |
72 | ||
73 | static int listen_fds (lua_State *L) { | |
74 | int unset_environment = lua_toboolean(L, 1); | |
75 | int n_descriptors = shim_weak_stub(sd_listen_fds)(unset_environment); | |
76 | if (n_descriptors < 0) { | |
77 | return handle_error(L, -n_descriptors); | |
78 | } | |
79 | lua_pushinteger(L, n_descriptors); | |
80 | return 1; | |
81 | } | |
82 | ||
83 | int luaopen_systemd_daemon_core (lua_State *L) { | |
84 | lua_newtable(L); | |
85 | /* 209 */ | |
86 | set_func_if_symbol_exists("sd_notify", L, notify, "notify"); | |
87 | set_func_if_symbol_exists("sd_booted", L, booted, "booted"); | |
88 | set_func_if_symbol_exists("sd_listen_fds", L, listen_fds, "listen_fds"); | |
89 | /* 214 */ | |
90 | set_func_if_symbol_exists("sd_pid_notify", L, pid_notify, "pid_notify"); | |
91 | /* 219 */ | |
92 | set_func_if_symbol_exists("sd_pid_notify_with_fds", L, pid_notify_with_fds, "pid_notify_with_fds"); | |
93 | ||
94 | lua_pushnumber(L, SD_LISTEN_FDS_START); lua_setfield(L, -2, "LISTEN_FDS_START"); | |
95 | ||
96 | return 1; | |
97 | } |
0 | local c = require "systemd.daemon.core" | |
1 | ||
2 | -- Wrap notify functions with versions that take tables | |
3 | -- this lets you write `notifyt { READY=1, STATE="awesome!" }` | |
4 | local function pack_state(t) | |
5 | local state = { } | |
6 | for k, v in pairs(t) do | |
7 | state[#state+1] = k.."="..v | |
8 | end | |
9 | return table.concat(state, "\n") | |
10 | end | |
11 | function c.notifyt(t) | |
12 | return c.notify(false, pack_state(t)) | |
13 | end | |
14 | function c.pid_notifyt(pid, t) | |
15 | return c.pid_notify(pid, false, pack_state(t)) | |
16 | end | |
17 | ||
18 | -- Get our own pid in pure lua. | |
19 | local function get_pid() | |
20 | local fd, err, n = io.open "/proc/self/stat" | |
21 | if fd == nil then return nil, err, n end | |
22 | local pid = fd:read "*n" | |
23 | fd:close() | |
24 | return pid | |
25 | end | |
26 | ||
27 | -- sd_watchdog_enabled in pure lua. | |
28 | -- returns watchdog interval | |
29 | function c.watchdog_enabled(unset_environment) | |
30 | if unset_environment then error("unset not supported", 2) end | |
31 | ||
32 | local pid = os.getenv "WATCHDOG_PID" | |
33 | if not pid then return false end | |
34 | pid = tonumber(pid) | |
35 | if pid ~= get_pid() then return false end | |
36 | ||
37 | local usec = os.getenv "WATCHDOG_USEC" | |
38 | usec = tonumber(usec) | |
39 | if usec == nil then return nil, "invalid interval" end | |
40 | return usec/1e6 | |
41 | end | |
42 | ||
43 | function c.kick_dog() | |
44 | return c.notify(false, "WATCHDOG=1") | |
45 | end | |
46 | ||
47 | return c |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" | |
2 | #include "compat-5.3.h" | |
3 | ||
4 | #include <systemd/sd-id128.h> | |
5 | ||
6 | #include "util.c" | |
7 | #include "id128.h" | |
8 | #include "messages.h" | |
9 | ||
10 | ||
11 | static int randomize (lua_State *L) { | |
12 | sd_id128_t *ret = lua_newuserdata(L, sizeof(sd_id128_t)); | |
13 | int err = sd_id128_randomize(ret); | |
14 | if (err < 0) return handle_error(L, -err); | |
15 | luaL_setmetatable(L, ID128_METATABLE); | |
16 | return 1; | |
17 | } | |
18 | ||
19 | static int from_string (lua_State *L) { | |
20 | size_t l; | |
21 | const char *s = luaL_checklstring(L, 1, &l); | |
22 | if (l != 32 && l != 37) return luaL_argerror(L, 1, "string must be 32 hex characters or a 37 character formatted RFC UUID"); | |
23 | sd_id128_t *ret = lua_newuserdata(L, sizeof(sd_id128_t)); | |
24 | int err = sd_id128_from_string(s, ret); | |
25 | if (err < 0) return handle_error(L, -err); | |
26 | luaL_setmetatable(L, ID128_METATABLE); | |
27 | return 1; | |
28 | } | |
29 | ||
30 | static int get_machine (lua_State *L) { | |
31 | sd_id128_t *ret = lua_newuserdata(L, sizeof(sd_id128_t)); | |
32 | int err = sd_id128_get_machine(ret); | |
33 | if (err < 0) return handle_error(L, -err); | |
34 | luaL_setmetatable(L, ID128_METATABLE); | |
35 | return 1; | |
36 | } | |
37 | ||
38 | static int get_boot (lua_State *L) { | |
39 | sd_id128_t *ret = lua_newuserdata(L, sizeof(sd_id128_t)); | |
40 | int err = sd_id128_get_boot(ret); | |
41 | if (err < 0) return handle_error(L, -err); | |
42 | luaL_setmetatable(L, ID128_METATABLE); | |
43 | return 1; | |
44 | } | |
45 | ||
46 | static int equal (lua_State *L) { | |
47 | sd_id128_t *a = luaL_checkudata(L, 1, ID128_METATABLE); | |
48 | sd_id128_t *b = luaL_checkudata(L, 2, ID128_METATABLE); | |
49 | lua_pushboolean(L, sd_id128_equal(*a, *b)); | |
50 | return 1; | |
51 | } | |
52 | ||
53 | static int to_string (lua_State *L) { | |
54 | sd_id128_t *a = luaL_checkudata(L, 1, ID128_METATABLE); | |
55 | char s[33]; | |
56 | sd_id128_to_string(*a, s); | |
57 | lua_pushlstring(L, s, 32); | |
58 | return 1; | |
59 | } | |
60 | ||
61 | int luaopen_systemd_id128_core (lua_State *L) { | |
62 | static const luaL_Reg lib[] = { | |
63 | {"randomize", randomize}, | |
64 | {"from_string", from_string}, | |
65 | {"get_machine", get_machine}, | |
66 | {"get_boot", get_boot}, | |
67 | {NULL, NULL} | |
68 | }; | |
69 | luaL_newlib(L, lib); | |
70 | ||
71 | static const luaL_Reg meta[] = { | |
72 | {"__eq", equal}, | |
73 | {"__tostring", to_string}, | |
74 | {NULL, NULL} | |
75 | }; | |
76 | ||
77 | static const luaL_Reg methods[] = { | |
78 | {"to_string", to_string}, | |
79 | {"get_catalog", journal_get_catalog_for_message_id}, | |
80 | {NULL, NULL} | |
81 | }; | |
82 | ||
83 | if (luaL_newmetatable(L, ID128_METATABLE)) { | |
84 | luaL_newlib(L, methods); | |
85 | lua_setfield(L, -2, "__index"); | |
86 | luaL_setfuncs(L, meta, 0); | |
87 | } | |
88 | /* Expose id128 methods */ | |
89 | lua_getfield(L, -1, "__index"); | |
90 | lua_setfield(L, -3, "ID128_METHODS"); | |
91 | ||
92 | lua_pop(L, 1); | |
93 | ||
94 | return 1; | |
95 | } |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" | |
2 | ||
3 | #include <systemd/sd-id128.h> | |
4 | ||
5 | ||
6 | #define ID128_METATABLE "ID128" | |
7 | ||
8 | static sd_id128_t check_id128_t (lua_State *L, int arg) { | |
9 | sd_id128_t boot_id; | |
10 | switch(lua_type(L, arg)) { | |
11 | case LUA_TUSERDATA: | |
12 | return *(sd_id128_t*)luaL_checkudata(L, arg, ID128_METATABLE); | |
13 | case LUA_TSTRING: | |
14 | luaL_argcheck(L, sd_id128_from_string(lua_tostring(L, arg), &boot_id) == 0, arg, "string is not a valid id128"); | |
15 | return boot_id; | |
16 | default: | |
17 | luaL_argerror(L, arg, lua_pushfstring(L, "id128 expected, got %s", luaL_typename(L, arg))); | |
18 | /* unreachable */ | |
19 | } | |
20 | } | |
21 | ||
22 | int luaopen_systemd_id128_core (lua_State *L); |
0 | local c = require "systemd.id128.core" | |
1 | local methods = c.ID128_METHODS | |
2 | ||
3 | c.randomise = c.randomize | |
4 | ||
5 | return c |
0 | return { | |
1 | daemon = require "systemd.daemon" ; | |
2 | id128 = require "systemd.id128" ; | |
3 | journal = require "systemd.journal" ; | |
4 | login = require "systemd.login" ; | |
5 | messages = require "systemd.messages" ; | |
6 | } |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" | |
2 | #include "compat-5.3.h" | |
3 | ||
4 | #include <stdlib.h> /* free */ | |
5 | #include <stdio.h> /* FILE, fdopen, fclose */ | |
6 | #include <stdint.h> /* uint64_t */ | |
7 | #include <sys/uio.h> /* struct iovec */ | |
8 | #include <errno.h> | |
9 | ||
10 | #define SD_JOURNAL_SUPPRESS_LOCATION | |
11 | #include <systemd/sd-journal.h> | |
12 | ||
13 | #include "util.c" | |
14 | #include "journal.h" | |
15 | #include "id128.h" | |
16 | ||
17 | ||
18 | static int handle_log_result (lua_State *L, int err) { | |
19 | if (err == 0) { | |
20 | lua_pushboolean(L, 1); | |
21 | return 1; | |
22 | } else { | |
23 | return handle_error(L, -err); | |
24 | } | |
25 | } | |
26 | ||
27 | static int sendv (lua_State *L) { | |
28 | int res; | |
29 | size_t i, n; | |
30 | struct iovec *iov; | |
31 | luaL_checktype(L, 1, LUA_TTABLE); | |
32 | n = lua_rawlen(L, 1); | |
33 | iov = lua_newuserdata(L, n*sizeof(struct iovec)); /* allocate via lua so we get free cleanup */ | |
34 | for (i=0; i<n; i++) { | |
35 | lua_rawgeti(L, 1, i+1); | |
36 | /* Make sure value is a string, we do **not** want automatic coercion */ | |
37 | if (lua_type(L, -1) != LUA_TSTRING) { | |
38 | return luaL_argerror(L, 1, "non-string table entry"); | |
39 | } | |
40 | iov[i].iov_base = (void*)lua_tolstring(L, -1, &iov[i].iov_len); | |
41 | lua_pop(L, 1); | |
42 | } | |
43 | res = sd_journal_sendv(iov, n); | |
44 | return handle_log_result(L, res); | |
45 | } | |
46 | ||
47 | static int _perror (lua_State *L) { | |
48 | const char *message = luaL_checkstring(L, 1); | |
49 | return handle_log_result(L, sd_journal_perror(message)); | |
50 | } | |
51 | ||
52 | /* From http://www.lua.org/source/5.2/liolib.c.html#io_fclose */ | |
53 | static int io_fclose (lua_State *L) { | |
54 | luaL_Stream *p = (luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE); | |
55 | FILE *pf = p->f; | |
56 | int res = fclose(pf); | |
57 | return luaL_fileresult(L, (res == 0), NULL); | |
58 | } | |
59 | ||
60 | static int stream_fd (lua_State *L) { | |
61 | int fd; | |
62 | const char *identifier = luaL_checkstring(L, 1); | |
63 | int priority = luaL_checkinteger(L, 2); | |
64 | int level_prefix = lua_toboolean(L, 3); /* Optional arg, defaults to false */ | |
65 | luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream)); | |
66 | p->closef = NULL; /* create a `closed' file handle before opening file, in case of errors */ | |
67 | luaL_setmetatable(L, LUA_FILEHANDLE); | |
68 | fd = sd_journal_stream_fd(identifier, priority, level_prefix); | |
69 | if (fd < 0) return handle_error(L, -fd); | |
70 | p->f = fdopen(fd, "w"); | |
71 | if (!p->f) return handle_error(L, errno); | |
72 | p->closef = &io_fclose; | |
73 | return 1; | |
74 | } | |
75 | ||
76 | static int journal_open (lua_State *L) { | |
77 | int err; | |
78 | int flags = luaL_optinteger(L, 1, 0); | |
79 | sd_journal **j = lua_newuserdata(L, sizeof(sd_journal*)); | |
80 | err = sd_journal_open(j, flags); | |
81 | if (err != 0) return handle_error(L, -err); | |
82 | luaL_setmetatable(L, JOURNAL_METATABLE); | |
83 | return 1; | |
84 | } | |
85 | ||
86 | static int journal_open_directory (lua_State *L) { | |
87 | int err; | |
88 | const char *path = luaL_checkstring(L, 1); | |
89 | int flags = luaL_optinteger(L, 2, 0); | |
90 | sd_journal **j = lua_newuserdata(L, sizeof(sd_journal*)); | |
91 | err = sd_journal_open_directory(j, path, flags); | |
92 | if (err != 0) return handle_error(L, -err); | |
93 | luaL_setmetatable(L, JOURNAL_METATABLE); | |
94 | return 1; | |
95 | } | |
96 | ||
97 | static int journal_open_files (lua_State *L) { | |
98 | int err; | |
99 | sd_journal **j; | |
100 | const char **paths; | |
101 | size_t len; | |
102 | int flags; | |
103 | luaL_checktype(L, 1, LUA_TTABLE); | |
104 | lua_settop(L, 2); | |
105 | len = lua_rawlen(L, 1); | |
106 | paths = lua_newuserdata(L, sizeof(const char*)*(len+1)); | |
107 | paths[len] = NULL; | |
108 | for (; len>0; len--) { | |
109 | lua_rawgeti(L, 1, len); | |
110 | paths[len-1] = luaL_checkstring(L, -1); | |
111 | lua_pop(L, 1); | |
112 | } | |
113 | flags = luaL_optinteger(L, 2, 0); | |
114 | j = lua_newuserdata(L, sizeof(sd_journal*)); | |
115 | err = sd_journal_open_files(j, paths, flags); | |
116 | if (err != 0) return handle_error(L, -err); | |
117 | luaL_setmetatable(L, JOURNAL_METATABLE); | |
118 | return 1; | |
119 | } | |
120 | ||
121 | static int journal_open_container (lua_State *L) { | |
122 | int err; | |
123 | const char *machine = luaL_checkstring(L, 1); | |
124 | int flags = luaL_optinteger(L, 2, 0); | |
125 | sd_journal **j = lua_newuserdata(L, sizeof(sd_journal*)); | |
126 | err = sd_journal_open_container(j, machine, flags); | |
127 | if (err != 0) return handle_error(L, -err); | |
128 | luaL_setmetatable(L, JOURNAL_METATABLE); | |
129 | return 1; | |
130 | } | |
131 | ||
132 | static int journal_close (lua_State *L) { | |
133 | sd_journal **jp = luaL_checkudata(L, 1, JOURNAL_METATABLE); | |
134 | if (*jp != NULL) { | |
135 | sd_journal_close(*jp); | |
136 | *jp = NULL; | |
137 | } | |
138 | return 0; | |
139 | } | |
140 | ||
141 | static sd_journal* check_journal(lua_State *L, int index) { | |
142 | sd_journal **jp = luaL_checkudata(L, index, JOURNAL_METATABLE); | |
143 | if (*jp == NULL) luaL_error(L, "Invalid journal handle"); | |
144 | return *jp; | |
145 | } | |
146 | ||
147 | static int journal_tostring (lua_State *L) { | |
148 | sd_journal *j = check_journal(L, 1); | |
149 | lua_pushfstring(L, "%s: %p", JOURNAL_METATABLE, j); | |
150 | return 1; | |
151 | } | |
152 | ||
153 | static int journal_get_cutoff_realtime_usec (lua_State *L) { | |
154 | sd_journal *j = check_journal(L, 1); | |
155 | uint64_t from; | |
156 | uint64_t to; | |
157 | int err = sd_journal_get_cutoff_realtime_usec(j, &from, &to); | |
158 | if (err < 0) return handle_error(L, -err); | |
159 | else if (err == 0) { | |
160 | lua_pushboolean(L, 0); | |
161 | return 1; | |
162 | } else { | |
163 | lua_pushuint64(L, from); | |
164 | lua_pushuint64(L, to); | |
165 | return 2; | |
166 | } | |
167 | } | |
168 | ||
169 | static int journal_get_cutoff_monotonic_usec (lua_State *L) { | |
170 | sd_journal *j = check_journal(L, 1); | |
171 | sd_id128_t boot_id = check_id128_t(L, 2); | |
172 | uint64_t from; | |
173 | uint64_t to; | |
174 | int err = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &from, &to); | |
175 | if (err < 0) return handle_error(L, -err); | |
176 | else if (err == 0) { | |
177 | lua_pushboolean(L, 0); | |
178 | return 1; | |
179 | } else { | |
180 | lua_pushuint64(L, from); | |
181 | lua_pushuint64(L, to); | |
182 | return 2; | |
183 | } | |
184 | } | |
185 | ||
186 | static int journal_get_usage (lua_State *L) { | |
187 | sd_journal *j = check_journal(L, 1); | |
188 | uint64_t bytes; | |
189 | int err = sd_journal_get_usage(j, &bytes); | |
190 | if (err != 0) return handle_error(L, -err); | |
191 | lua_pushuint64(L, bytes); | |
192 | return 1; | |
193 | } | |
194 | ||
195 | static int journal_next (lua_State *L) { | |
196 | sd_journal *j = check_journal(L, 1); | |
197 | int err = sd_journal_next(j); | |
198 | if (err < 0) return handle_error(L, -err); | |
199 | lua_pushboolean(L, err); | |
200 | return 1; | |
201 | } | |
202 | ||
203 | static int journal_next_skip (lua_State *L) { | |
204 | sd_journal *j = check_journal(L, 1); | |
205 | uint64_t skip = luaL_checkuint64(L, 2); | |
206 | int err = sd_journal_next_skip(j, skip); | |
207 | if (err < 0) return handle_error(L, -err); | |
208 | lua_pushinteger(L, err); | |
209 | return 1; | |
210 | } | |
211 | ||
212 | static int journal_previous (lua_State *L) { | |
213 | sd_journal *j = check_journal(L, 1); | |
214 | int err = sd_journal_previous(j); | |
215 | if (err < 0) return handle_error(L, -err); | |
216 | lua_pushboolean(L, err); | |
217 | return 1; | |
218 | } | |
219 | ||
220 | static int journal_previous_skip (lua_State *L) { | |
221 | sd_journal *j = check_journal(L, 1); | |
222 | uint64_t skip = luaL_checkuint64(L, 2); | |
223 | int err = sd_journal_previous_skip(j, skip); | |
224 | if (err < 0) return handle_error(L, -err); | |
225 | lua_pushinteger(L, err); | |
226 | return 1; | |
227 | } | |
228 | ||
229 | static int journal_seek_head (lua_State *L) { | |
230 | sd_journal *j = check_journal(L, 1); | |
231 | int err = sd_journal_seek_head(j); | |
232 | if (err != 0) return handle_error(L, -err); | |
233 | lua_pushboolean(L, 1); | |
234 | return 1; | |
235 | } | |
236 | ||
237 | static int journal_seek_tail (lua_State *L) { | |
238 | sd_journal *j = check_journal(L, 1); | |
239 | int err = sd_journal_seek_tail(j); | |
240 | if (err != 0) return handle_error(L, -err); | |
241 | lua_pushboolean(L, 1); | |
242 | return 1; | |
243 | } | |
244 | ||
245 | static int journal_seek_monotonic_usec (lua_State *L) { | |
246 | sd_journal *j = check_journal(L, 1); | |
247 | sd_id128_t boot_id = check_id128_t(L, 2); | |
248 | uint64_t usec = luaL_checkuint64(L, 3); | |
249 | int err = sd_journal_seek_monotonic_usec(j, boot_id, usec); | |
250 | if (err != 0) return handle_error(L, -err); | |
251 | lua_pushboolean(L, 1); | |
252 | return 1; | |
253 | } | |
254 | ||
255 | static int journal_seek_realtime_usec (lua_State *L) { | |
256 | sd_journal *j = check_journal(L, 1); | |
257 | uint64_t usec = luaL_checkuint64(L, 2); | |
258 | int err = sd_journal_seek_realtime_usec(j, usec); | |
259 | if (err != 0) return handle_error(L, -err); | |
260 | lua_pushboolean(L, 1); | |
261 | return 1; | |
262 | } | |
263 | ||
264 | static int journal_seek_cursor (lua_State *L) { | |
265 | sd_journal *j = check_journal(L, 1); | |
266 | const char *cursor = luaL_checkstring(L, 2); | |
267 | int err = sd_journal_seek_cursor(j, cursor); | |
268 | if (err != 0) return handle_error(L, -err); | |
269 | lua_pushboolean(L, 1); | |
270 | return 1; | |
271 | } | |
272 | ||
273 | static int journal_get_cursor (lua_State *L) { | |
274 | sd_journal *j = check_journal(L, 1); | |
275 | char *cursor; | |
276 | int err = sd_journal_get_cursor(j, &cursor); | |
277 | if (err != 0) return handle_error(L, -err); | |
278 | lua_pushstring(L, cursor); | |
279 | free(cursor); | |
280 | return 1; | |
281 | } | |
282 | ||
283 | static int journal_test_cursor (lua_State *L) { | |
284 | sd_journal *j = check_journal(L, 1); | |
285 | const char *cursor = luaL_checkstring(L, 2); | |
286 | int err = sd_journal_test_cursor(j, cursor); | |
287 | if (err < 0) return handle_error(L, -err); | |
288 | lua_pushboolean(L, err); | |
289 | return 1; | |
290 | } | |
291 | ||
292 | static int journal_get_realtime_usec (lua_State *L) { | |
293 | sd_journal *j = check_journal(L, 1); | |
294 | uint64_t usec; | |
295 | int err = sd_journal_get_realtime_usec(j, &usec); | |
296 | if (err != 0) return handle_error(L, -err); | |
297 | lua_pushuint64(L, usec); | |
298 | return 1; | |
299 | } | |
300 | ||
301 | static int journal_get_monotonic_usec (lua_State *L) { | |
302 | sd_journal *j = check_journal(L, 1); | |
303 | uint64_t usec; | |
304 | sd_id128_t *boot_id = lua_newuserdata(L, sizeof(sd_id128_t)); | |
305 | int err = sd_journal_get_monotonic_usec(j, &usec, boot_id); | |
306 | if (err != 0) return handle_error(L, -err); | |
307 | lua_pushuint64(L, usec); | |
308 | lua_insert(L, 2); /* put below boot_id */ | |
309 | luaL_setmetatable(L, ID128_METATABLE); | |
310 | return 2; | |
311 | } | |
312 | ||
313 | static int journal_get_data (lua_State *L) { | |
314 | sd_journal *j = check_journal(L, 1); | |
315 | const char *field = luaL_checkstring(L, 2); | |
316 | const void *data; | |
317 | size_t length; | |
318 | int err = sd_journal_get_data(j, field, &data, &length); | |
319 | if (err == -ENOENT) { | |
320 | lua_pushboolean(L, 0); | |
321 | lua_pushnil(L); | |
322 | return 2; | |
323 | } else if (err != 0) { | |
324 | return handle_error(L, -err); | |
325 | } else { | |
326 | lua_pushboolean(L, 1); | |
327 | lua_pushlstring(L, data, length); | |
328 | return 2; | |
329 | } | |
330 | } | |
331 | ||
332 | static int journal_enumerate_data (lua_State *L) { | |
333 | sd_journal *j = check_journal(L, 1); | |
334 | const void *data; | |
335 | size_t length; | |
336 | int err = sd_journal_enumerate_data(j, &data, &length); | |
337 | if (err < 0) return handle_error(L, -err); | |
338 | else if (err == 0) { | |
339 | lua_pushboolean(L, 0); | |
340 | lua_pushnil(L); | |
341 | } else { | |
342 | lua_pushboolean(L, 1); | |
343 | lua_pushlstring(L, data, length); | |
344 | } | |
345 | return 2; | |
346 | } | |
347 | ||
348 | static int journal_restart_data (lua_State *L) { | |
349 | sd_journal *j = check_journal(L, 1); | |
350 | sd_journal_restart_data(j); | |
351 | return 0; | |
352 | } | |
353 | ||
354 | static int journal_query_unique (lua_State *L) { | |
355 | sd_journal *j = check_journal(L, 1); | |
356 | const char *field = luaL_checkstring(L, 2); | |
357 | int err = sd_journal_query_unique(j, field); | |
358 | if (err != 0) return handle_error(L, -err); | |
359 | lua_pushboolean(L, 1); | |
360 | return 1; | |
361 | } | |
362 | ||
363 | static int journal_enumerate_unique (lua_State *L) { | |
364 | sd_journal *j = check_journal(L, 1); | |
365 | const void *data; | |
366 | size_t length; | |
367 | int err = sd_journal_enumerate_unique(j, &data, &length); | |
368 | if (err < 0) return handle_error(L, -err); | |
369 | else if (err == 0) { | |
370 | lua_pushboolean(L, 0); | |
371 | lua_pushnil(L); | |
372 | } else { | |
373 | lua_pushboolean(L, 1); | |
374 | lua_pushlstring(L, data, length); | |
375 | } | |
376 | return 2; | |
377 | } | |
378 | ||
379 | static int journal_restart_unique (lua_State *L) { | |
380 | sd_journal *j = check_journal(L, 1); | |
381 | sd_journal_restart_unique(j); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | static int journal_set_data_threshold (lua_State *L) { | |
386 | sd_journal *j = check_journal(L, 1); | |
387 | size_t sz = luaL_optinteger(L, 2, 0); | |
388 | int err = sd_journal_set_data_threshold(j, sz); | |
389 | if (err != 0) return handle_error(L, -err); | |
390 | lua_pushboolean(L, 1); | |
391 | return 1; | |
392 | } | |
393 | ||
394 | static int journal_get_data_threshold (lua_State *L) { | |
395 | sd_journal *j = check_journal(L, 1); | |
396 | size_t sz; | |
397 | int err = sd_journal_get_data_threshold(j, &sz); | |
398 | if (err != 0) return handle_error(L, -err); | |
399 | lua_pushinteger(L, sz); | |
400 | return 1; | |
401 | } | |
402 | ||
403 | static int journal_add_match (lua_State *L) { | |
404 | sd_journal *j = check_journal(L, 1); | |
405 | size_t size; | |
406 | const char *data = luaL_checklstring(L, 2, &size); | |
407 | int err = sd_journal_add_match(j, data, size); | |
408 | if (err != 0) return handle_error(L, -err); | |
409 | lua_pushboolean(L, 1); | |
410 | return 1; | |
411 | } | |
412 | ||
413 | static int journal_add_disjunction (lua_State *L) { | |
414 | sd_journal *j = check_journal(L, 1); | |
415 | int err = sd_journal_add_disjunction(j); | |
416 | if (err != 0) return handle_error(L, -err); | |
417 | lua_pushboolean(L, 1); | |
418 | return 1; | |
419 | } | |
420 | ||
421 | static int journal_add_conjunction (lua_State *L) { | |
422 | sd_journal *j = check_journal(L, 1); | |
423 | int err = sd_journal_add_conjunction(j); | |
424 | if (err != 0) return handle_error(L, -err); | |
425 | lua_pushboolean(L, 1); | |
426 | return 1; | |
427 | } | |
428 | ||
429 | static int journal_flush_matches (lua_State *L) { | |
430 | sd_journal *j = check_journal(L, 1); | |
431 | sd_journal_flush_matches(j); | |
432 | lua_pushboolean(L, 1); | |
433 | return 1; | |
434 | } | |
435 | ||
436 | static int journal_get_fd (lua_State *L) { | |
437 | sd_journal *j = check_journal(L, 1); | |
438 | int err = sd_journal_get_fd(j); | |
439 | if (err < 0) return handle_error(L, -err); | |
440 | lua_pushinteger(L, err); | |
441 | return 1; | |
442 | } | |
443 | ||
444 | static int journal_get_events (lua_State *L) { | |
445 | sd_journal *j = check_journal(L, 1); | |
446 | int err = sd_journal_get_events(j); | |
447 | if (err < 0) return handle_error(L, -err); | |
448 | lua_pushinteger(L, err); | |
449 | return 1; | |
450 | } | |
451 | ||
452 | static int journal_get_timeout (lua_State *L) { | |
453 | sd_journal *j = check_journal(L, 1); | |
454 | uint64_t timeout_usec; | |
455 | int err = sd_journal_get_timeout(j, &timeout_usec); | |
456 | if (err < 0) return handle_error(L, -err); | |
457 | if ((err == 0) || (timeout_usec == (uint64_t) -1)) { | |
458 | /* local file; no timeout needed */ | |
459 | lua_pushboolean(L, 0); | |
460 | } else { | |
461 | lua_pushnumber(L, ((double)timeout_usec)/1000000); | |
462 | } | |
463 | return 1; | |
464 | } | |
465 | ||
466 | static int journal_process (lua_State *L) { | |
467 | sd_journal *j = check_journal(L, 1); | |
468 | int err = sd_journal_process(j); | |
469 | if (err < 0) return handle_error(L, -err); | |
470 | lua_pushinteger(L, err); | |
471 | return 1; | |
472 | } | |
473 | ||
474 | static int journal_wait (lua_State *L) { | |
475 | sd_journal *j = check_journal(L, 1); | |
476 | uint64_t timeout_usec; | |
477 | int err; | |
478 | if (lua_isnoneornil(L, 2)) { /* default to infinite wait */ | |
479 | timeout_usec = -1; | |
480 | } else { | |
481 | timeout_usec = luaL_checknumber(L, 2) * 1000000; | |
482 | } | |
483 | err = sd_journal_wait(j, timeout_usec); | |
484 | if (err < 0) return handle_error(L, -err); | |
485 | lua_pushinteger(L, err); | |
486 | return 1; | |
487 | } | |
488 | ||
489 | static int journal_reliable_fd (lua_State *L) { | |
490 | sd_journal *j = check_journal(L, 1); | |
491 | int err = sd_journal_reliable_fd(j); | |
492 | if (err < 0) return handle_error(L, -err); | |
493 | lua_pushboolean(L, err); | |
494 | return 1; | |
495 | } | |
496 | ||
497 | ||
498 | static const luaL_Reg journal_methods[] = { | |
499 | {"get_cutoff_realtime_usec", journal_get_cutoff_realtime_usec}, | |
500 | {"get_cutoff_monotonic_usec", journal_get_cutoff_monotonic_usec}, | |
501 | {"get_usage", journal_get_usage}, | |
502 | {"next", journal_next}, | |
503 | {"next_skip", journal_next_skip}, | |
504 | {"previous", journal_previous}, | |
505 | {"previous_skip", journal_previous_skip}, | |
506 | {"seek_head", journal_seek_head}, | |
507 | {"seek_tail", journal_seek_tail}, | |
508 | {"seek_monotonic_usec", journal_seek_monotonic_usec}, | |
509 | {"seek_realtime_usec", journal_seek_realtime_usec}, | |
510 | {"seek_cursor", journal_seek_cursor}, | |
511 | {"get_cursor", journal_get_cursor}, | |
512 | {"test_cursor", journal_test_cursor}, | |
513 | {"get_realtime_usec", journal_get_realtime_usec}, | |
514 | {"get_monotonic_usec", journal_get_monotonic_usec}, | |
515 | {"get_data", journal_get_data}, | |
516 | {"enumerate_data", journal_enumerate_data}, | |
517 | {"restart_data", journal_restart_data}, | |
518 | {"query_unique", journal_query_unique}, | |
519 | {"enumerate_unique", journal_enumerate_unique}, | |
520 | {"restart_unique", journal_restart_unique}, | |
521 | {"set_data_threshold", journal_set_data_threshold}, | |
522 | {"get_data_threshold", journal_get_data_threshold}, | |
523 | {"add_match", journal_add_match}, | |
524 | {"add_disjunction", journal_add_disjunction}, | |
525 | {"add_conjunction", journal_add_conjunction}, | |
526 | {"flush_matches", journal_flush_matches}, | |
527 | {"get_fd", journal_get_fd}, | |
528 | {"get_events", journal_get_events}, | |
529 | {"get_timeout", journal_get_timeout}, | |
530 | {"process", journal_process}, | |
531 | {"wait", journal_wait}, | |
532 | {"reliable_fd", journal_reliable_fd}, | |
533 | {NULL, NULL} | |
534 | }; | |
535 | ||
536 | int luaopen_systemd_journal_core (lua_State *L) { | |
537 | static const luaL_Reg lib[] = { | |
538 | {"sendv", sendv}, | |
539 | {"perror", _perror}, | |
540 | {"stream_fd", stream_fd}, | |
541 | {"open", journal_open}, | |
542 | {"open_directory", journal_open_directory}, | |
543 | {"open_files", journal_open_files}, | |
544 | {"open_container", journal_open_container}, | |
545 | {NULL, NULL} | |
546 | }; | |
547 | ||
548 | /* ensure ID128_METATABLE is loaded */ | |
549 | luaL_requiref(L, "systemd.id128.core", luaopen_systemd_id128_core, 0); | |
550 | ||
551 | luaL_newlib(L, lib); | |
552 | ||
553 | /* Even with compat-5.2, Lua 5.1 doesn't have an easy way to make your own file objects */ | |
554 | /* Set up function environment for stream_fd for 5.1 so handle gets closed correctly */ | |
555 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501 | |
556 | lua_getfield(L, -1, "stream_fd"); | |
557 | lua_createtable(L, 0, 1); | |
558 | lua_pushcfunction(L, &io_fclose); | |
559 | lua_setfield(L, -2, "__close"); | |
560 | lua_setfenv(L, -2); | |
561 | lua_pop(L, 1); | |
562 | #endif | |
563 | ||
564 | lua_createtable(L, 0, 3); | |
565 | lua_pushnumber(L, SD_JOURNAL_NOP); lua_setfield(L, -2, "NOP"); | |
566 | lua_pushnumber(L, SD_JOURNAL_APPEND); lua_setfield(L, -2, "APPEND"); | |
567 | lua_pushnumber(L, SD_JOURNAL_INVALIDATE); lua_setfield(L, -2, "INVALIDATE"); | |
568 | lua_setfield(L, -2, "WAKEUP"); | |
569 | ||
570 | if (luaL_newmetatable(L, JOURNAL_METATABLE) != 0) { | |
571 | lua_pushcfunction(L, journal_close); | |
572 | lua_setfield(L, -2, "__gc"); | |
573 | lua_pushcfunction(L, journal_tostring); | |
574 | lua_setfield(L, -2, "__tostring"); | |
575 | luaL_newlib(L, journal_methods); | |
576 | lua_setfield(L, -2, "__index"); | |
577 | } | |
578 | /* Expose journal methods */ | |
579 | lua_getfield(L, -1, "__index"); | |
580 | lua_setfield(L, -3, "JOURNAL_METHODS"); | |
581 | ||
582 | lua_pop(L, 1); | |
583 | ||
584 | lua_createtable(L, 0, 4); | |
585 | lua_pushnumber(L, SD_JOURNAL_LOCAL_ONLY); lua_setfield(L, -2, "LOCAL_ONLY"); | |
586 | lua_pushnumber(L, SD_JOURNAL_RUNTIME_ONLY); lua_setfield(L, -2, "RUNTIME_ONLY"); | |
587 | lua_pushnumber(L, SD_JOURNAL_SYSTEM); lua_setfield(L, -2, "SYSTEM"); | |
588 | lua_pushnumber(L, SD_JOURNAL_CURRENT_USER); lua_setfield(L, -2, "CURRENT_USER"); | |
589 | lua_setfield(L, -2, "OPEN"); | |
590 | ||
591 | return 1; | |
592 | } |
0 | #include "lua.h" | |
1 | ||
2 | #define JOURNAL_METATABLE "sd_journal" | |
3 | ||
4 | int luaopen_systemd_journal_core (lua_State *L); |
0 | local id128 = require "systemd.id128" | |
1 | ||
2 | local c = require "systemd.journal.core" | |
3 | local methods = c.JOURNAL_METHODS | |
4 | ||
5 | local function strip_field_name(str) | |
6 | return str:match("=(.*)$") | |
7 | end | |
8 | ||
9 | local function split_field(str) | |
10 | return str:match("^([^=]*)=(.*)$") | |
11 | end | |
12 | ||
13 | c.LOG = { | |
14 | EMERG = 0; | |
15 | ALERT = 1; | |
16 | CRIT = 2; | |
17 | ERR = 3; | |
18 | WARNING = 4; | |
19 | NOTICE = 5; | |
20 | INFO = 6; | |
21 | DEBUG = 7; | |
22 | } | |
23 | ||
24 | function c.print(priority, ...) | |
25 | return c.sendv { | |
26 | "PRIORITY=" .. priority; | |
27 | "MESSAGE=" .. string.format(...); | |
28 | } | |
29 | end | |
30 | ||
31 | function c.sendt(m) | |
32 | local t = { } | |
33 | for k, v in pairs(m) do | |
34 | t[#t+1] = k .. "=" .. v | |
35 | end | |
36 | return c.sendv(t) | |
37 | end | |
38 | ||
39 | function methods:get(field) | |
40 | local ok, res, code = self:get_data(field) | |
41 | if ok then | |
42 | return strip_field_name(res) | |
43 | elseif ok == false then | |
44 | return nil | |
45 | else | |
46 | error(res, 2); | |
47 | end | |
48 | end | |
49 | ||
50 | local function next_field(self) | |
51 | local ok, res = self:enumerate_data() | |
52 | if ok then | |
53 | return split_field(res) | |
54 | elseif ok == false then | |
55 | return nil | |
56 | else | |
57 | error(res, 2) | |
58 | end | |
59 | end | |
60 | function methods:each_data() | |
61 | self:restart_data() | |
62 | return next_field, self | |
63 | end | |
64 | ||
65 | local function next_unique(self) | |
66 | local ok, res = self:enumerate_unique() | |
67 | if ok then | |
68 | return strip_field_name(res) | |
69 | elseif ok == false then | |
70 | return nil | |
71 | else | |
72 | error(res, 2) | |
73 | end | |
74 | end | |
75 | function methods:each_unique(field) | |
76 | self:restart_unique() | |
77 | assert(self:query_unique(field)) | |
78 | return next_unique, self | |
79 | end | |
80 | ||
81 | -- Converts the current journal entry to a lua table | |
82 | -- Includes __ prefixed "Address Fields" | |
83 | function methods:to_table() | |
84 | local t = { | |
85 | __CURSOR = self:get_cursor(); | |
86 | __REALTIME_TIMESTAMP = self:get_realtime_usec(); | |
87 | __MONOTONIC_TIMESTAMP = self:get_monotonic_usec(); | |
88 | } | |
89 | for field, value in self:each_data() do | |
90 | t[field] = value | |
91 | end | |
92 | return t | |
93 | end | |
94 | ||
95 | function methods:get_catalog() | |
96 | local ok, err, errno = self:get_data "MESSAGE_ID" | |
97 | if ok == nil then | |
98 | return nil, err, errno | |
99 | elseif ok == false then | |
100 | return false | |
101 | else | |
102 | local message_id = err:match("=(.*)$") | |
103 | if message_id == nil then return false end | |
104 | local message_id = id128.from_string(message_id) | |
105 | local text = message_id:get_catalog() | |
106 | if not text then return false end | |
107 | local t = self:to_table() | |
108 | text = text:gsub("@([^@]-)@", t) | |
109 | return text | |
110 | end | |
111 | end | |
112 | ||
113 | return c |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" | |
2 | #include "compat-5.3.h" | |
3 | ||
4 | #include <stdlib.h> /* free */ | |
5 | #include <stdint.h> /* uint64_t */ | |
6 | #include <sys/types.h> /* pid_t */ | |
7 | #include <errno.h> /* ENOTSUP */ | |
8 | ||
9 | #include <systemd/sd-login.h> | |
10 | ||
11 | #include "util.c" | |
12 | #include "login.h" | |
13 | ||
14 | ||
15 | shim_weak_stub_declare(int, sd_pid_get_session, (pid_t pid, char **session), -ENOTSUP); | |
16 | shim_weak_stub_declare(int, sd_pid_get_owner_uid, (pid_t pid, uid_t *uid), -ENOTSUP); | |
17 | shim_weak_stub_declare(int, sd_pid_get_unit, (pid_t pid, char **unit), -ENOTSUP); | |
18 | shim_weak_stub_declare(int, sd_pid_get_user_unit, (pid_t pid, char **unit), -ENOTSUP); | |
19 | shim_weak_stub_declare(int, sd_pid_get_machine_name, (pid_t pid, char **machine), -ENOTSUP); | |
20 | shim_weak_stub_declare(int, sd_pid_get_slice, (pid_t pid, char **slice), -ENOTSUP); | |
21 | shim_weak_stub_declare(int, sd_pid_get_user_slice, (pid_t pid, char **slice), -ENOTSUP); | |
22 | shim_weak_stub_declare(int, sd_pid_get_cgroup, (pid_t pid, char **cgroup), -ENOTSUP); | |
23 | shim_weak_stub_declare(int, sd_peer_get_session, (int fd, char **session), -ENOTSUP); | |
24 | shim_weak_stub_declare(int, sd_peer_get_owner_uid, (int fd, uid_t *uid), -ENOTSUP); | |
25 | shim_weak_stub_declare(int, sd_peer_get_unit, (int fd, char **unit), -ENOTSUP); | |
26 | shim_weak_stub_declare(int, sd_peer_get_user_unit, (int fd, char **unit), -ENOTSUP); | |
27 | shim_weak_stub_declare(int, sd_peer_get_machine_name, (int fd, char **machine), -ENOTSUP); | |
28 | shim_weak_stub_declare(int, sd_peer_get_slice, (int fd, char **slice), -ENOTSUP); | |
29 | shim_weak_stub_declare(int, sd_peer_get_user_slice, (int fd, char **slice), -ENOTSUP); | |
30 | shim_weak_stub_declare(int, sd_peer_get_cgroup, (int fd, char **cgroup), -ENOTSUP); | |
31 | shim_weak_stub_declare(int, sd_uid_get_state, (uid_t uid, char **state), -ENOTSUP); | |
32 | shim_weak_stub_declare(int, sd_uid_get_display, (uid_t uid, char **session), -ENOTSUP); | |
33 | shim_weak_stub_declare(int, sd_uid_is_on_seat, (uid_t uid, int require_active, const char *seat), -ENOTSUP); | |
34 | shim_weak_stub_declare(int, sd_uid_get_sessions, (uid_t uid, int require_active, char ***sessions), -ENOTSUP); | |
35 | shim_weak_stub_declare(int, sd_uid_get_seats, (uid_t uid, int require_active, char ***seats), -ENOTSUP); | |
36 | shim_weak_stub_declare(int, sd_session_is_active, (const char *session), -ENOTSUP); | |
37 | shim_weak_stub_declare(int, sd_session_is_remote, (const char *session), -ENOTSUP); | |
38 | shim_weak_stub_declare(int, sd_session_get_state, (const char *session, char **state), -ENOTSUP); | |
39 | shim_weak_stub_declare(int, sd_session_get_uid, (const char *session, uid_t *uid), -ENOTSUP); | |
40 | shim_weak_stub_declare(int, sd_session_get_seat, (const char *session, char **seat), -ENOTSUP); | |
41 | shim_weak_stub_declare(int, sd_session_get_service, (const char *session, char **service), -ENOTSUP); | |
42 | shim_weak_stub_declare(int, sd_session_get_type, (const char *session, char **type), -ENOTSUP); | |
43 | shim_weak_stub_declare(int, sd_session_get_class, (const char *session, char **clazz), -ENOTSUP); | |
44 | shim_weak_stub_declare(int, sd_session_get_desktop, (const char *session, char **desktop), -ENOTSUP); | |
45 | shim_weak_stub_declare(int, sd_session_get_display, (const char *session, char **display), -ENOTSUP); | |
46 | shim_weak_stub_declare(int, sd_session_get_remote_host, (const char *session, char **remote_host), -ENOTSUP); | |
47 | shim_weak_stub_declare(int, sd_session_get_remote_user, (const char *session, char **remote_user), -ENOTSUP); | |
48 | shim_weak_stub_declare(int, sd_session_get_tty, (const char *session, char **display), -ENOTSUP); | |
49 | shim_weak_stub_declare(int, sd_session_get_vt, (const char *session, unsigned *vtnr), -ENOTSUP); | |
50 | shim_weak_stub_declare(int, sd_seat_get_active, (const char *seat, char **session, uid_t *uid), -ENOTSUP); | |
51 | shim_weak_stub_declare(int, sd_seat_get_sessions, (const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids), -ENOTSUP); | |
52 | shim_weak_stub_declare(int, sd_seat_can_multi_session, (const char *seat), -ENOTSUP); | |
53 | shim_weak_stub_declare(int, sd_seat_can_tty, (const char *seat), -ENOTSUP); | |
54 | shim_weak_stub_declare(int, sd_seat_can_graphical, (const char *seat), -ENOTSUP); | |
55 | shim_weak_stub_declare(int, sd_machine_get_class, (const char *machine, char **clazz), -ENOTSUP); | |
56 | shim_weak_stub_declare(int, sd_machine_get_ifindices, (const char *machine, int **ifindices), -ENOTSUP); | |
57 | shim_weak_stub_declare(int, sd_get_seats, (char ***seats), -ENOTSUP); | |
58 | shim_weak_stub_declare(int, sd_get_sessions, (char ***sessions), -ENOTSUP); | |
59 | shim_weak_stub_declare(int, sd_get_uids, (uid_t **users), -ENOTSUP); | |
60 | shim_weak_stub_declare(int, sd_get_machine_names, (char ***machines), -ENOTSUP); | |
61 | shim_weak_stub_declare(int, sd_login_monitor_new, (const char *category, sd_login_monitor** ret), -ENOTSUP); | |
62 | shim_weak_stub_declare(sd_login_monitor*, sd_login_monitor_unref, (sd_login_monitor *m), NULL); | |
63 | shim_weak_stub_declare(int, sd_login_monitor_flush, (sd_login_monitor *m), -ENOTSUP); | |
64 | shim_weak_stub_declare(int, sd_login_monitor_get_fd, (sd_login_monitor *m), -ENOTSUP); | |
65 | shim_weak_stub_declare(int, sd_login_monitor_get_events, (sd_login_monitor *m), -ENOTSUP); | |
66 | shim_weak_stub_declare(int, sd_login_monitor_get_timeout, (sd_login_monitor *m, uint64_t *timeout_usec), -ENOTSUP); | |
67 | ||
68 | static void push_array_of_strings (lua_State *L, char **strings, int n) { | |
69 | int i; | |
70 | lua_createtable(L, n, 0); | |
71 | if (strings != NULL) { | |
72 | for (i=0; i<n; i++) { | |
73 | lua_pushstring(L, strings[i]); | |
74 | free(strings[i]); | |
75 | lua_rawseti(L, -2, i+1); | |
76 | } | |
77 | free(strings); | |
78 | } | |
79 | } | |
80 | ||
81 | static void push_array_of_uids (lua_State *L, uid_t *uids, int n) { | |
82 | int i; | |
83 | lua_createtable(L, n, 0); | |
84 | if (uids != NULL) { | |
85 | for (i=0; i<n; i++) { | |
86 | lua_pushinteger(L, uids[i]); | |
87 | lua_rawseti(L, -2, i+1); | |
88 | } | |
89 | free(uids); | |
90 | } | |
91 | } | |
92 | ||
93 | static int get_seats (lua_State *L) { | |
94 | char **seats; | |
95 | int n = shim_weak_stub(sd_get_seats)(&seats); | |
96 | if (n < 0) return handle_error(L, -n); | |
97 | push_array_of_strings(L, seats, n); | |
98 | return 1; | |
99 | } | |
100 | ||
101 | static int get_sessions (lua_State *L) { | |
102 | char **sessions; | |
103 | int n = shim_weak_stub(sd_get_sessions)(&sessions); | |
104 | if (n < 0) return handle_error(L, -n); | |
105 | push_array_of_strings(L, sessions, n); | |
106 | return 1; | |
107 | } | |
108 | ||
109 | static int get_uids (lua_State *L) { | |
110 | uid_t *users; | |
111 | int n = shim_weak_stub(sd_get_uids)(&users); | |
112 | if (n < 0) return handle_error(L, -n); | |
113 | push_array_of_uids(L, users, n); | |
114 | return 1; | |
115 | } | |
116 | ||
117 | static int get_machine_names (lua_State *L) { | |
118 | char **machines; | |
119 | int n = shim_weak_stub(sd_get_machine_names)(&machines); | |
120 | if (n < 0) return handle_error(L, -n); | |
121 | push_array_of_strings(L, machines, n); | |
122 | return 1; | |
123 | } | |
124 | ||
125 | static int pid_get_session (lua_State *L) { | |
126 | pid_t pid = luaL_checkinteger(L, 1); | |
127 | char *session; | |
128 | int n = shim_weak_stub(sd_pid_get_session)(pid, &session); | |
129 | if (n < 0) return handle_error(L, -n); | |
130 | lua_pushstring(L, session); | |
131 | free(session); | |
132 | return 1; | |
133 | } | |
134 | ||
135 | static int pid_get_unit (lua_State *L) { | |
136 | pid_t pid = luaL_checkinteger(L, 1); | |
137 | char *unit; | |
138 | int n = shim_weak_stub(sd_pid_get_unit)(pid, &unit); | |
139 | if (n < 0) return handle_error(L, -n); | |
140 | lua_pushstring(L, unit); | |
141 | free(unit); | |
142 | return 1; | |
143 | } | |
144 | ||
145 | static int pid_get_user_unit (lua_State *L) { | |
146 | pid_t pid = luaL_checkinteger(L, 1); | |
147 | char *unit; | |
148 | int n = shim_weak_stub(sd_pid_get_user_unit)(pid, &unit); | |
149 | if (n < 0) return handle_error(L, -n); | |
150 | lua_pushstring(L, unit); | |
151 | free(unit); | |
152 | return 1; | |
153 | } | |
154 | ||
155 | static int pid_get_owner_uid (lua_State *L) { | |
156 | pid_t pid = luaL_checkinteger(L, 1); | |
157 | uid_t user; | |
158 | int n = shim_weak_stub(sd_pid_get_owner_uid)(pid, &user); | |
159 | if (n < 0) return handle_error(L, -n); | |
160 | lua_pushinteger(L, user); | |
161 | return 1; | |
162 | } | |
163 | ||
164 | static int pid_get_machine_name (lua_State *L) { | |
165 | pid_t pid = luaL_checkinteger(L, 1); | |
166 | char *machine; | |
167 | int n = shim_weak_stub(sd_pid_get_machine_name)(pid, &machine); | |
168 | if (n < 0) return handle_error(L, -n); | |
169 | lua_pushstring(L, machine); | |
170 | free(machine); | |
171 | return 1; | |
172 | } | |
173 | ||
174 | static int pid_get_slice (lua_State *L) { | |
175 | pid_t pid = luaL_checkinteger(L, 1); | |
176 | char *slice; | |
177 | int n = shim_weak_stub(sd_pid_get_slice)(pid, &slice); | |
178 | if (n < 0) return handle_error(L, -n); | |
179 | lua_pushstring(L, slice); | |
180 | free(slice); | |
181 | return 1; | |
182 | } | |
183 | ||
184 | static int pid_get_user_slice (lua_State *L) { | |
185 | pid_t pid = luaL_checkinteger(L, 1); | |
186 | char *slice; | |
187 | int n = shim_weak_stub(sd_pid_get_user_slice)(pid, &slice); | |
188 | if (n < 0) return handle_error(L, -n); | |
189 | lua_pushstring(L, slice); | |
190 | free(slice); | |
191 | return 1; | |
192 | } | |
193 | ||
194 | static int pid_get_cgroup (lua_State *L) { | |
195 | pid_t pid = luaL_checkinteger(L, 1); | |
196 | char *cgroup; | |
197 | int n = shim_weak_stub(sd_pid_get_cgroup)(pid, &cgroup); | |
198 | if (n < 0) return handle_error(L, -n); | |
199 | lua_pushstring(L, cgroup); | |
200 | free(cgroup); | |
201 | return 1; | |
202 | } | |
203 | ||
204 | static int peer_get_session (lua_State *L) { | |
205 | int fd = luaL_checkinteger(L, 1); | |
206 | char *session; | |
207 | int n = shim_weak_stub(sd_peer_get_session)(fd, &session); | |
208 | if (n < 0) return handle_error(L, -n); | |
209 | lua_pushstring(L, session); | |
210 | free(session); | |
211 | return 1; | |
212 | } | |
213 | ||
214 | static int peer_get_unit (lua_State *L) { | |
215 | int fd = luaL_checkinteger(L, 1); | |
216 | char *unit; | |
217 | int n = shim_weak_stub(sd_peer_get_unit)(fd, &unit); | |
218 | if (n < 0) return handle_error(L, -n); | |
219 | lua_pushstring(L, unit); | |
220 | free(unit); | |
221 | return 1; | |
222 | } | |
223 | ||
224 | static int peer_get_user_unit (lua_State *L) { | |
225 | int fd = luaL_checkinteger(L, 1); | |
226 | char *unit; | |
227 | int n = shim_weak_stub(sd_peer_get_user_unit)(fd, &unit); | |
228 | if (n < 0) return handle_error(L, -n); | |
229 | lua_pushstring(L, unit); | |
230 | free(unit); | |
231 | return 1; | |
232 | } | |
233 | ||
234 | static int peer_get_owner_uid (lua_State *L) { | |
235 | int fd = luaL_checkinteger(L, 1); | |
236 | uid_t user; | |
237 | int n = shim_weak_stub(sd_peer_get_owner_uid)(fd, &user); | |
238 | if (n < 0) return handle_error(L, -n); | |
239 | lua_pushinteger(L, user); | |
240 | return 1; | |
241 | } | |
242 | ||
243 | static int peer_get_machine_name (lua_State *L) { | |
244 | int fd = luaL_checkinteger(L, 1); | |
245 | char *machine; | |
246 | int n = shim_weak_stub(sd_peer_get_machine_name)(fd, &machine); | |
247 | if (n < 0) return handle_error(L, -n); | |
248 | lua_pushstring(L, machine); | |
249 | free(machine); | |
250 | return 1; | |
251 | } | |
252 | ||
253 | static int peer_get_slice (lua_State *L) { | |
254 | int fd = luaL_checkinteger(L, 1); | |
255 | char *slice; | |
256 | int n = shim_weak_stub(sd_peer_get_slice)(fd, &slice); | |
257 | if (n < 0) return handle_error(L, -n); | |
258 | lua_pushstring(L, slice); | |
259 | free(slice); | |
260 | return 1; | |
261 | } | |
262 | ||
263 | static int peer_get_user_slice (lua_State *L) { | |
264 | int fd = luaL_checkinteger(L, 1); | |
265 | char *slice; | |
266 | int n = shim_weak_stub(sd_peer_get_user_slice)(fd, &slice); | |
267 | if (n < 0) return handle_error(L, -n); | |
268 | lua_pushstring(L, slice); | |
269 | free(slice); | |
270 | return 1; | |
271 | } | |
272 | ||
273 | static int peer_get_cgroup (lua_State *L) { | |
274 | int fd = luaL_checkinteger(L, 1); | |
275 | char *cgroup; | |
276 | int n = shim_weak_stub(sd_peer_get_cgroup)(fd, &cgroup); | |
277 | if (n < 0) return handle_error(L, -n); | |
278 | lua_pushstring(L, cgroup); | |
279 | free(cgroup); | |
280 | return 1; | |
281 | } | |
282 | ||
283 | static int uid_get_state (lua_State *L) { | |
284 | uid_t uid = luaL_checkinteger(L, 1); | |
285 | char *state; | |
286 | int err = shim_weak_stub(sd_uid_get_state)(uid, &state); | |
287 | if (err < 0) return handle_error(L, -err); | |
288 | lua_pushstring(L, state); | |
289 | free(state); | |
290 | return 1; | |
291 | } | |
292 | ||
293 | static int uid_get_seats (lua_State *L) { | |
294 | uid_t uid = luaL_checkinteger(L, 1); | |
295 | int require_active = (luaL_checktype(L, 2, LUA_TBOOLEAN), lua_toboolean(L, 2)); | |
296 | char **seats; | |
297 | int err = shim_weak_stub(sd_uid_get_seats)(uid, require_active, &seats); | |
298 | if (err < 0) return handle_error(L, -err); | |
299 | push_array_of_strings(L, seats, err); | |
300 | return 1; | |
301 | } | |
302 | ||
303 | static int uid_is_on_seat (lua_State *L) { | |
304 | uid_t uid = luaL_checkinteger(L, 1); | |
305 | int require_active = (luaL_checktype(L, 2, LUA_TBOOLEAN), lua_toboolean(L, 2)); | |
306 | const char *seat = luaL_checkstring(L, 3); | |
307 | int err = shim_weak_stub(sd_uid_is_on_seat)(uid, require_active, seat); | |
308 | if (err < 0) return handle_error(L, -err); | |
309 | lua_pushboolean(L, err); | |
310 | return 1; | |
311 | } | |
312 | ||
313 | static int uid_get_sessions (lua_State *L) { | |
314 | uid_t uid = luaL_checkinteger(L, 1); | |
315 | int require_active = (luaL_checktype(L, 2, LUA_TBOOLEAN), lua_toboolean(L, 2)); | |
316 | char ** sessions; | |
317 | int err = shim_weak_stub(sd_uid_get_sessions)(uid, require_active, &sessions); | |
318 | if (err < 0) return handle_error(L, -err); | |
319 | push_array_of_strings(L, sessions, err); | |
320 | return 1; | |
321 | } | |
322 | ||
323 | static int uid_get_display (lua_State *L) { | |
324 | uid_t uid = luaL_checkinteger(L, 1); | |
325 | char *session; | |
326 | int err = shim_weak_stub(sd_uid_get_display)(uid, &session); | |
327 | if (err < 0) return handle_error(L, -err); | |
328 | lua_pushstring(L, session); | |
329 | free(session); | |
330 | return 1; | |
331 | } | |
332 | ||
333 | static int session_is_active (lua_State *L) { | |
334 | const char *session = luaL_checkstring(L, 1); | |
335 | int err = shim_weak_stub(sd_session_is_active)(session); | |
336 | if (err < 0) return handle_error(L, -err); | |
337 | lua_pushboolean(L, err); | |
338 | return 1; | |
339 | } | |
340 | ||
341 | static int session_is_remote (lua_State *L) { | |
342 | const char *session = luaL_checkstring(L, 1); | |
343 | int err = shim_weak_stub(sd_session_is_remote)(session); | |
344 | if (err < 0) return handle_error(L, -err); | |
345 | lua_pushboolean(L, err); | |
346 | return 1; | |
347 | } | |
348 | ||
349 | static int session_get_state (lua_State *L) { | |
350 | const char *session = luaL_checkstring(L, 1); | |
351 | char *state; | |
352 | int err = shim_weak_stub(sd_session_get_state)(session, &state); | |
353 | if (err < 0) return handle_error(L, -err); | |
354 | lua_pushstring(L, state); | |
355 | free(state); | |
356 | return 1; | |
357 | } | |
358 | ||
359 | static int session_get_uid (lua_State *L) { | |
360 | const char *session = luaL_checkstring(L, 1); | |
361 | uid_t uid; | |
362 | int err = shim_weak_stub(sd_session_get_uid)(session, &uid); | |
363 | if (err < 0) return handle_error(L, -err); | |
364 | lua_pushinteger(L, uid); | |
365 | return 1; | |
366 | } | |
367 | ||
368 | static int session_get_seat (lua_State *L) { | |
369 | const char *session = luaL_checkstring(L, 1); | |
370 | char *seat; | |
371 | int err = shim_weak_stub(sd_session_get_seat)(session, &seat); | |
372 | if (err < 0) return handle_error(L, -err); | |
373 | lua_pushstring(L, seat); | |
374 | free(seat); | |
375 | return 1; | |
376 | } | |
377 | ||
378 | static int session_get_service (lua_State *L) { | |
379 | const char *session = luaL_checkstring(L, 1); | |
380 | char *service; | |
381 | int err = shim_weak_stub(sd_session_get_service)(session, &service); | |
382 | if (err < 0) return handle_error(L, -err); | |
383 | lua_pushstring(L, service); | |
384 | free(service); | |
385 | return 1; | |
386 | } | |
387 | ||
388 | static int session_get_type (lua_State *L) { | |
389 | const char *session = luaL_checkstring(L, 1); | |
390 | char *type; | |
391 | int err = shim_weak_stub(sd_session_get_type)(session, &type); | |
392 | if (err < 0) return handle_error(L, -err); | |
393 | lua_pushstring(L, type); | |
394 | free(type); | |
395 | return 1; | |
396 | } | |
397 | ||
398 | static int session_get_class (lua_State *L) { | |
399 | const char *session = luaL_checkstring(L, 1); | |
400 | char *clazz; | |
401 | int err = shim_weak_stub(sd_session_get_class)(session, &clazz); | |
402 | if (err < 0) return handle_error(L, -err); | |
403 | lua_pushstring(L, clazz); | |
404 | free(clazz); | |
405 | return 1; | |
406 | } | |
407 | ||
408 | static int session_get_desktop (lua_State *L) { | |
409 | const char *session = luaL_checkstring(L, 1); | |
410 | char *desktop; | |
411 | int err = shim_weak_stub(sd_session_get_desktop)(session, &desktop); | |
412 | if (err < 0) return handle_error(L, -err); | |
413 | lua_pushstring(L, desktop); | |
414 | free(desktop); | |
415 | return 1; | |
416 | } | |
417 | ||
418 | static int session_get_display (lua_State *L) { | |
419 | const char *session = luaL_checkstring(L, 1); | |
420 | char *display; | |
421 | int err = shim_weak_stub(sd_session_get_display)(session, &display); | |
422 | if (err < 0) return handle_error(L, -err); | |
423 | lua_pushstring(L, display); | |
424 | free(display); | |
425 | return 1; | |
426 | } | |
427 | ||
428 | static int session_get_remote_host (lua_State *L) { | |
429 | const char *session = luaL_checkstring(L, 1); | |
430 | char *remote_host; | |
431 | int err = shim_weak_stub(sd_session_get_remote_host)(session, &remote_host); | |
432 | if (err < 0) return handle_error(L, -err); | |
433 | lua_pushstring(L, remote_host); | |
434 | free(remote_host); | |
435 | return 1; | |
436 | } | |
437 | ||
438 | static int session_get_remote_user (lua_State *L) { | |
439 | const char *session = luaL_checkstring(L, 1); | |
440 | char *remote_user; | |
441 | int err = shim_weak_stub(sd_session_get_remote_user)(session, &remote_user); | |
442 | if (err < 0) return handle_error(L, -err); | |
443 | lua_pushstring(L, remote_user); | |
444 | free(remote_user); | |
445 | return 1; | |
446 | } | |
447 | ||
448 | static int session_get_tty (lua_State *L) { | |
449 | const char *session = luaL_checkstring(L, 1); | |
450 | char *display; | |
451 | int err = shim_weak_stub(sd_session_get_tty)(session, &display); | |
452 | if (err < 0) return handle_error(L, -err); | |
453 | lua_pushstring(L, display); | |
454 | free(display); | |
455 | return 1; | |
456 | } | |
457 | ||
458 | static int session_get_vt (lua_State *L) { | |
459 | const char *session = luaL_checkstring(L, 1); | |
460 | unsigned vtnr; | |
461 | int err = shim_weak_stub(sd_session_get_vt)(session, &vtnr); | |
462 | if (err < 0) return handle_error(L, -err); | |
463 | lua_pushinteger(L, vtnr); | |
464 | return 1; | |
465 | } | |
466 | ||
467 | static int seat_get_active (lua_State *L) { | |
468 | const char *seat = luaL_optstring(L, 1, NULL); | |
469 | char *session; | |
470 | uid_t uid; | |
471 | int err = shim_weak_stub(sd_seat_get_active)(seat, &session, &uid); | |
472 | if (err < 0) return handle_error(L, -err); | |
473 | lua_pushstring(L, session); | |
474 | lua_pushinteger(L, uid); | |
475 | return 2; | |
476 | } | |
477 | ||
478 | static int seat_get_sessions (lua_State *L) { | |
479 | const char *seat = luaL_optstring(L, 1, NULL); | |
480 | char **sessions; | |
481 | uid_t *uid; | |
482 | unsigned n_uids; | |
483 | int err = shim_weak_stub(sd_seat_get_sessions)(seat, &sessions, &uid, &n_uids); | |
484 | if (err < 0) return handle_error(L, -err); | |
485 | push_array_of_strings(L, sessions, err); | |
486 | push_array_of_uids(L, uid, n_uids); | |
487 | return 2; | |
488 | } | |
489 | ||
490 | static int seat_can_multi_session (lua_State *L) { | |
491 | const char *seat = luaL_optstring(L, 1, NULL); | |
492 | int err = shim_weak_stub(sd_seat_can_multi_session)(seat); | |
493 | if (err < 0) return handle_error(L, -err); | |
494 | lua_pushboolean(L, err); | |
495 | return 1; | |
496 | } | |
497 | ||
498 | static int seat_can_tty (lua_State *L) { | |
499 | const char *seat = luaL_optstring(L, 1, NULL); | |
500 | int err = shim_weak_stub(sd_seat_can_tty)(seat); | |
501 | if (err < 0) return handle_error(L, -err); | |
502 | lua_pushboolean(L, err); | |
503 | return 1; | |
504 | } | |
505 | ||
506 | static int seat_can_graphical (lua_State *L) { | |
507 | const char *seat = luaL_optstring(L, 1, NULL); | |
508 | int err = shim_weak_stub(sd_seat_can_graphical)(seat); | |
509 | if (err < 0) return handle_error(L, -err); | |
510 | lua_pushboolean(L, err); | |
511 | return 1; | |
512 | } | |
513 | ||
514 | static int machine_get_class (lua_State *L) { | |
515 | const char *machine = luaL_checkstring(L, 1); | |
516 | char *clazz; | |
517 | int err = shim_weak_stub(sd_machine_get_class)(machine, &clazz); | |
518 | if (err < 0) return handle_error(L, -err); | |
519 | lua_pushstring(L, clazz); | |
520 | free(clazz); | |
521 | return 1; | |
522 | } | |
523 | ||
524 | static int machine_get_ifindices (lua_State *L) { | |
525 | const char *machine = luaL_checkstring(L, 1); | |
526 | int *ifindices; | |
527 | int i; | |
528 | int err = shim_weak_stub(sd_machine_get_ifindices)(machine, &ifindices); | |
529 | if (err < 0) return handle_error(L, -err); | |
530 | lua_createtable(L, err, 0); | |
531 | if (ifindices != NULL) { | |
532 | for (i=0; i<err; i++) { | |
533 | lua_pushinteger(L, ifindices[i]); | |
534 | lua_rawseti(L, -2, i+1); | |
535 | } | |
536 | free(ifindices); | |
537 | } | |
538 | return 1; | |
539 | } | |
540 | ||
541 | /* sd_login_monitor */ | |
542 | ||
543 | static sd_login_monitor* check_monitor(lua_State *L, int index) { | |
544 | sd_login_monitor **mp = luaL_checkudata(L, index, MONITOR_METATABLE); | |
545 | if (*mp == NULL) luaL_error(L, "Invalid monitor handle"); | |
546 | return *mp; | |
547 | } | |
548 | ||
549 | static int monitor_unref (lua_State *L) { | |
550 | sd_login_monitor **mp = luaL_checkudata(L, 1, MONITOR_METATABLE); | |
551 | if (*mp != NULL) { | |
552 | shim_weak_stub(sd_login_monitor_unref)(*mp); | |
553 | *mp = NULL; | |
554 | } | |
555 | return 0; | |
556 | } | |
557 | ||
558 | static int monitor_new (lua_State *L) { | |
559 | const char* category = luaL_optstring(L, 1, NULL); | |
560 | sd_login_monitor** ret = lua_newuserdata(L, sizeof(sd_login_monitor*)); | |
561 | int err = shim_weak_stub(sd_login_monitor_new)(category, ret); | |
562 | if (err < 0) return handle_error(L, -err); | |
563 | luaL_setmetatable(L, MONITOR_METATABLE); | |
564 | return 1; | |
565 | } | |
566 | ||
567 | static int monitor_tostring (lua_State *L) { | |
568 | sd_login_monitor *m = check_monitor(L, 1); | |
569 | lua_pushfstring(L, "%s: %p", MONITOR_METATABLE, m); | |
570 | return 1; | |
571 | } | |
572 | ||
573 | static int monitor_flush (lua_State *L) { | |
574 | sd_login_monitor *m = check_monitor(L, 1); | |
575 | int err = shim_weak_stub(sd_login_monitor_flush)(m); | |
576 | if (err < 0) return handle_error(L, -err); | |
577 | lua_pushboolean(L, 1); | |
578 | return 1; | |
579 | } | |
580 | ||
581 | static int monitor_get_fd (lua_State *L) { | |
582 | sd_login_monitor *m = check_monitor(L, 1); | |
583 | int err = shim_weak_stub(sd_login_monitor_get_fd)(m); | |
584 | if (err < 0) return handle_error(L, -err); | |
585 | lua_pushinteger(L, err); | |
586 | return 1; | |
587 | } | |
588 | ||
589 | static int monitor_get_events (lua_State *L) { | |
590 | sd_login_monitor *m = check_monitor(L, 1); | |
591 | int err = shim_weak_stub(sd_login_monitor_get_events)(m); | |
592 | if (err < 0) return handle_error(L, -err); | |
593 | lua_pushinteger(L, err); | |
594 | return 1; | |
595 | } | |
596 | ||
597 | static int monitor_get_timeout (lua_State *L) { | |
598 | sd_login_monitor *m = check_monitor(L, 1); | |
599 | uint64_t timeout_usec; | |
600 | int err = shim_weak_stub(sd_login_monitor_get_timeout)(m, &timeout_usec); | |
601 | if (err < 0) return handle_error(L, -err); | |
602 | if ((err == 0) || (timeout_usec == (uint64_t) -1)) { | |
603 | lua_pushboolean(L, 0); | |
604 | } else { | |
605 | lua_pushnumber(L, ((double)timeout_usec)/1000000); | |
606 | } | |
607 | return 1; | |
608 | } | |
609 | ||
610 | int luaopen_systemd_login_core (lua_State *L) { | |
611 | lua_newtable(L); | |
612 | /* 209 */ | |
613 | if (symbol_exists("sd_monitor_new") && symbol_exists("sd_login_monitor_unref")) { | |
614 | if (luaL_newmetatable(L, MONITOR_METATABLE) != 0) { | |
615 | lua_newtable(L); | |
616 | set_func_if_symbol_exists("sd_monitor_flush", L, monitor_flush, "flush"); | |
617 | set_func_if_symbol_exists("sd_monitor_get_fd", L, monitor_get_fd, "get_fd"); | |
618 | set_func_if_symbol_exists("sd_monitor_get_events", L, monitor_get_events,"get_events"); | |
619 | set_func_if_symbol_exists("sd_monitor_get_timeout", L, monitor_get_timeout, "get_timeout"); | |
620 | lua_setfield(L, -2, "__index"); | |
621 | lua_pushcfunction(L, monitor_unref); | |
622 | lua_setfield(L, -2, "__gc"); | |
623 | lua_pushcfunction(L, monitor_tostring); | |
624 | lua_setfield(L, -2, "__tostring"); | |
625 | } | |
626 | /* Expose monitor methods */ | |
627 | lua_getfield(L, -1, "__index"); | |
628 | lua_setfield(L, -3, "MONITOR_METHODS"); | |
629 | lua_pop(L, 1); | |
630 | ||
631 | set_func(L, monitor_new, "monitor"); | |
632 | } | |
633 | ||
634 | set_func_if_symbol_exists("sd_pid_get_session", L, pid_get_session, "pid_get_session"); | |
635 | set_func_if_symbol_exists("sd_pid_get_unit", L, pid_get_unit, "pid_get_unit"); | |
636 | set_func_if_symbol_exists("sd_pid_get_user_unit", L, pid_get_user_unit, "pid_get_user_unit"); | |
637 | set_func_if_symbol_exists("sd_pid_get_owner_uid", L, pid_get_owner_uid, "pid_get_owner_uid"); | |
638 | set_func_if_symbol_exists("sd_pid_get_machine_name", L, pid_get_machine_name, "pid_get_machine_name"); | |
639 | set_func_if_symbol_exists("sd_pid_get_slice", L, pid_get_slice, "pid_get_slice"); | |
640 | ||
641 | set_func_if_symbol_exists("sd_uid_get_state", L, uid_get_state, "uid_get_state"); | |
642 | set_func_if_symbol_exists("sd_uid_is_on_seat", L, uid_is_on_seat, "uid_is_on_seat"); | |
643 | set_func_if_symbol_exists("sd_uid_get_sessions", L, uid_get_sessions, "uid_get_sessions"); | |
644 | set_func_if_symbol_exists("sd_uid_get_seats", L, uid_get_seats, "uid_get_seats"); | |
645 | ||
646 | set_func_if_symbol_exists("sd_session_is_active", L, session_is_active, "session_is_active"); | |
647 | set_func_if_symbol_exists("sd_session_is_remote", L, session_is_remote, "session_is_remote"); | |
648 | set_func_if_symbol_exists("sd_session_get_state", L, session_get_state, "session_get_state"); | |
649 | set_func_if_symbol_exists("sd_session_get_uid", L, session_get_uid, "session_get_uid"); | |
650 | set_func_if_symbol_exists("sd_session_get_seat", L, session_get_seat, "session_get_seat"); | |
651 | set_func_if_symbol_exists("sd_session_get_service", L, session_get_service, "session_get_service"); | |
652 | set_func_if_symbol_exists("sd_session_get_type", L, session_get_type, "session_get_type"); | |
653 | set_func_if_symbol_exists("sd_session_get_class", L, session_get_class, "session_get_class"); | |
654 | set_func_if_symbol_exists("sd_session_get_display", L, session_get_display, "session_get_display"); | |
655 | set_func_if_symbol_exists("sd_session_get_remote_host", L, session_get_remote_host, "session_get_remote_host"); | |
656 | set_func_if_symbol_exists("sd_session_get_remote_user", L, session_get_remote_user, "session_get_remote_user"); | |
657 | set_func_if_symbol_exists("sd_session_get_tty", L, session_get_tty, "session_get_tty"); | |
658 | set_func_if_symbol_exists("sd_session_get_vt", L, session_get_vt, "session_get_vt"); | |
659 | ||
660 | set_func_if_symbol_exists("sd_seat_get_active", L, seat_get_active, "seat_get_active"); | |
661 | set_func_if_symbol_exists("sd_seat_get_sessions", L, seat_get_sessions, "seat_get_sessions"); | |
662 | set_func_if_symbol_exists("sd_seat_can_multi_session", L, seat_can_multi_session, "seat_can_multi_session"); | |
663 | set_func_if_symbol_exists("sd_seat_can_tty", L, seat_can_tty, "seat_can_tty"); | |
664 | set_func_if_symbol_exists("sd_seat_can_graphical", L, seat_can_graphical, "seat_can_graphical"); | |
665 | ||
666 | set_func_if_symbol_exists("sd_get_seats", L, get_seats, "get_seats"); | |
667 | set_func_if_symbol_exists("sd_get_sessions", L, get_sessions, "get_sessions"); | |
668 | set_func_if_symbol_exists("sd_get_uids", L, get_uids, "get_uids"); | |
669 | set_func_if_symbol_exists("sd_get_machine_names", L, get_machine_names, "get_machine_names"); | |
670 | /* 211 */ | |
671 | set_func_if_symbol_exists("sd_machine_get_class", L, machine_get_class, "machine_get_class"); | |
672 | set_func_if_symbol_exists("sd_peer_get_session", L, peer_get_session, "peer_get_session"); | |
673 | set_func_if_symbol_exists("sd_peer_get_owner_uid", L, peer_get_owner_uid, "peer_get_owner_uid"); | |
674 | set_func_if_symbol_exists("sd_peer_get_unit", L, peer_get_unit, "peer_get_unit"); | |
675 | set_func_if_symbol_exists("sd_peer_get_user_unit", L, peer_get_user_unit, "peer_get_user_unit"); | |
676 | set_func_if_symbol_exists("sd_peer_get_machine_name", L, peer_get_machine_name, "peer_get_machine_name"); | |
677 | set_func_if_symbol_exists("sd_peer_get_slice", L, peer_get_slice, "peer_get_slice"); | |
678 | /* 213 */ | |
679 | set_func_if_symbol_exists("sd_sd_uid_get_display", L, uid_get_display, "sd_uid_get_display"); | |
680 | /* 216 */ | |
681 | set_func_if_symbol_exists("sd_machine_get_ifindices", L, machine_get_ifindices, "machine_get_ifindices"); | |
682 | /* 217 */ | |
683 | set_func_if_symbol_exists("sd_session_get_desktop", L, session_get_desktop, "session_get_desktop"); | |
684 | /* 220 */ | |
685 | set_func_if_symbol_exists("sd_pid_get_user_slice", L, pid_get_user_slice, "pid_get_user_slice"); | |
686 | set_func_if_symbol_exists("sd_peer_get_user_slice", L, peer_get_user_slice, "peer_get_user_slice"); | |
687 | /* 226 */ | |
688 | set_func_if_symbol_exists("sd_pid_get_cgroup", L, pid_get_cgroup, "pid_get_cgroup"); | |
689 | set_func_if_symbol_exists("sd_peer_get_cgroup", L, peer_get_cgroup, "peer_get_cgroup"); | |
690 | ||
691 | return 1; | |
692 | } |
0 | #include "lua.h" | |
1 | ||
2 | #define MONITOR_METATABLE "sd_login_monitor" | |
3 | ||
4 | int luaopen_systemd_login_core (lua_State *L); |
0 | #include "lua.h" | |
1 | ||
2 | #include <stdlib.h> /* free */ | |
3 | #include <errno.h> /* ENOENT */ | |
4 | ||
5 | #include <systemd/sd-journal.h> | |
6 | ||
7 | #include "util.c" | |
8 | #include "messages.h" | |
9 | #include "id128.h" | |
10 | ||
11 | ||
12 | int journal_get_catalog_for_message_id (lua_State *L) { | |
13 | sd_id128_t id = check_id128_t(L, 1); | |
14 | char *ret; | |
15 | int err = sd_journal_get_catalog_for_message_id(id, &ret); | |
16 | if (err == -ENOENT) { | |
17 | lua_pushboolean(L, 0); | |
18 | return 1; | |
19 | } else if (err != 0) { | |
20 | return handle_error(L, -err); | |
21 | } else { | |
22 | lua_pushstring(L, ret); | |
23 | free(ret); | |
24 | return 1; | |
25 | } | |
26 | } |
0 | #include "lua.h" | |
1 | ||
2 | __attribute__((visibility("hidden"))) int journal_get_catalog_for_message_id (lua_State *L); |
0 | --[[ | |
1 | This module gives aliases for some common well known message ids. | |
2 | The aliases are taken from `sd_messages.h`. | |
3 | ||
4 | Information about these message ids can be found in the system catalog in /usr/lib/systemd/catalog/ | |
5 | ]] | |
6 | ||
7 | local id128 = require "systemd.id128" | |
8 | ||
9 | local messages = {} | |
10 | ||
11 | messages.JOURNAL_START = id128.from_string "f77379a8490b408bbe5f6940505a777b" | |
12 | messages.JOURNAL_STOP = id128.from_string "d93fb3c9c24d451a97cea615ce59c00b" | |
13 | messages.JOURNAL_DROPPED = id128.from_string "a596d6fe7bfa4994828e72309e95d61e" | |
14 | messages.JOURNAL_MISSED = id128.from_string "e9bf28e6e834481bb6f48f548ad13606" | |
15 | messages.JOURNAL_USAGE = id128.from_string "ec387f577b844b8fa948f33cad9a75e6" | |
16 | ||
17 | messages.COREDUMP = id128.from_string "fc2e22bc6ee647b6b90729ab34a250b1" | |
18 | ||
19 | messages.SESSION_START = id128.from_string "8d45620c1a4348dbb17410da57c60c66" | |
20 | messages.SESSION_STOP = id128.from_string "3354939424b4456d9802ca8333ed424a" | |
21 | messages.SEAT_START = id128.from_string "fcbefc5da23d428093f97c82a9290f7b" | |
22 | messages.SEAT_STOP = id128.from_string "e7852bfe46784ed0accde04bc864c2d5" | |
23 | messages.MACHINE_START = id128.from_string "24d8d4452573402496068381a6312df2" | |
24 | messages.MACHINE_STOP = id128.from_string "58432bd3bace477cb514b56381b8a758" | |
25 | ||
26 | messages.TIME_CHANGE = id128.from_string "c7a787079b354eaaa9e77b371893cd27" | |
27 | messages.TIMEZONE_CHANGE = id128.from_string "45f82f4aef7a4bbf942ce861d1f20990" | |
28 | ||
29 | messages.STARTUP_FINISHED = id128.from_string "b07a249cd024414a82dd00cd181378ff" | |
30 | ||
31 | messages.SLEEP_START = id128.from_string "6bbd95ee977941e497c48be27c254128" | |
32 | messages.SLEEP_STOP = id128.from_string "8811e6df2a8e40f58a94cea26f8ebf14" | |
33 | ||
34 | messages.SHUTDOWN = id128.from_string "98268866d1d54a499c4e98921d93bc40" | |
35 | ||
36 | messages.UNIT_STARTING = id128.from_string "7d4958e842da4a758f6c1cdc7b36dcc5" | |
37 | messages.UNIT_STARTED = id128.from_string "39f53479d3a045ac8e11786248231fbf" | |
38 | messages.UNIT_STOPPING = id128.from_string "de5b426a63be47a7b6ac3eaac82e2f6f" | |
39 | messages.UNIT_STOPPED = id128.from_string "9d1aaa27d60140bd96365438aad20286" | |
40 | messages.UNIT_FAILED = id128.from_string "be02cf6855d2428ba40df7e9d022f03d" | |
41 | messages.UNIT_RELOADING = id128.from_string "d34d037fff1847e6ae669a370e694725" | |
42 | messages.UNIT_RELOADED = id128.from_string "7b05ebc668384222baa8881179cfda54" | |
43 | ||
44 | messages.SPAWN_FAILED = id128.from_string "641257651c1b4ec9a8624d7a40a9e1e7" | |
45 | ||
46 | messages.FORWARD_SYSLOG_MISSED = id128.from_string "0027229ca0644181a76c4e92458afa2e" | |
47 | ||
48 | messages.OVERMOUNTING = id128.from_string "1dee0369c7fc4736b7099b38ecb46ee7" | |
49 | ||
50 | messages.LID_OPENED = id128.from_string "b72ea4a2881545a0b50e200e55b9b06f" | |
51 | messages.LID_CLOSED = id128.from_string "b72ea4a2881545a0b50e200e55b9b070" | |
52 | messages.SYSTEM_DOCKED = id128.from_string "f5f416b862074b28927a48c3ba7d51ff" | |
53 | messages.SYSTEM_UNDOCKED = id128.from_string "51e171bd585248568110144c517cca53" | |
54 | messages.POWER_KEY = id128.from_string "b72ea4a2881545a0b50e200e55b9b071" | |
55 | messages.SUSPEND_KEY = id128.from_string "b72ea4a2881545a0b50e200e55b9b072" | |
56 | messages.HIBERNATE_KEY = id128.from_string "b72ea4a2881545a0b50e200e55b9b073" | |
57 | ||
58 | messages.CONFIG_ERROR = id128.from_string "c772d24e9a884cbeb9ea12625c306c01" | |
59 | ||
60 | messages.BOOTCHART = id128.from_string "9f26aa562cf440c2b16c773d0479b518" | |
61 | ||
62 | return messages |
0 | #include "lua.h" | |
1 | #include "lauxlib.h" /* luaL_checknumber */ | |
2 | ||
3 | #include <string.h> /* strerror */ | |
4 | #include <dlfcn.h> /* dlsym, dlerror */ | |
5 | ||
6 | ||
7 | static int handle_error(lua_State *L, int err) { | |
8 | lua_pushnil(L); | |
9 | lua_pushstring(L, strerror(err)); | |
10 | lua_pushinteger(L, err); | |
11 | return 3; | |
12 | } | |
13 | ||
14 | #ifndef lua_pushuint64 | |
15 | #define lua_pushuint64 lua_pushnumber | |
16 | #endif | |
17 | ||
18 | #ifndef luaL_checkuint64 | |
19 | #define luaL_checkuint64 luaL_checknumber | |
20 | #endif | |
21 | ||
22 | /* This hack is required as lua always passes RTLD_NOW to dlopen | |
23 | * Without this loading lua-systemd would fail with an error about undefined symbols | |
24 | * e.g. sd_machine_get_ifindices missing on a system running systemd 213 | |
25 | * | |
26 | * To ensure graceful fallback we have to have some indirection via a function pointer. | |
27 | */ | |
28 | #define shim_weak_stub_name(symbol) symbol ## _stub | |
29 | #define shim_weak_stub(symbol) symbol ## _pointer | |
30 | #define shim_weak_stub_declare(ret_type, symbol, args, val) \ | |
31 | static ret_type (*shim_weak_stub(symbol)) args; \ | |
32 | static ret_type shim_weak_stub_name(symbol) args { return val; } \ | |
33 | __attribute__((constructor)) static void initialize_ ## symbol() { \ | |
34 | if ((shim_weak_stub(symbol) = dlsym(RTLD_DEFAULT, #symbol)) == NULL) { \ | |
35 | shim_weak_stub(symbol) = shim_weak_stub_name(symbol); \ | |
36 | } \ | |
37 | } | |
38 | ||
39 | #define symbol_exists(name) (dlsym(RTLD_DEFAULT, name) != NULL || dlerror() == NULL) | |
40 | #define set_func(L, func, name) (lua_pushcclosure(L, func, 0), lua_setfield(L, -2, name)) | |
41 | #define set_func_if_symbol_exists(symbol, L, func, name) if (symbol_exists(symbol)) set_func(L, func, name) |
0 | package = "systemd" | |
1 | version = "scm-0" | |
2 | source = { | |
3 | url = "git://github.com/daurnimator/lua-systemd" | |
4 | } | |
5 | description = { | |
6 | summary = "Lua bindings to systemd", | |
7 | homepage = "https://github.com/daurnimator/lua-systemd", | |
8 | license = "MIT/X11" | |
9 | } | |
10 | dependencies = { | |
11 | "lua >= 5.0, < 5.4" | |
12 | } | |
13 | build = { | |
14 | type = "builtin", | |
15 | modules = { | |
16 | ["systemd.init"] = "src/init.lua", | |
17 | ["systemd.daemon"] = "src/daemon.lua", | |
18 | ["systemd"] = { | |
19 | sources = { | |
20 | "src/daemon.c", | |
21 | "src/id128.c", | |
22 | "src/messages.c", | |
23 | "src/journal.c", | |
24 | "src/login.c", | |
25 | "vendor/compat-5.3/c-api/compat-5.3.c", | |
26 | }, | |
27 | defines = { "_GNU_SOURCE" }, -- for RTLD_DEFAULT | |
28 | libraries = { "systemd" }, | |
29 | incdirs = { "vendor/compat-5.3/c-api/" } | |
30 | }, | |
31 | ["systemd.id128"] = "src/id128.lua", | |
32 | ["systemd.journal"] = "src/journal.lua", | |
33 | ["systemd.login"] = "src/login.lua", | |
34 | ["systemd.messages"] = "src/messages.lua" | |
35 | } | |
36 | } |
0 | The MIT License (MIT) | |
1 | ||
2 | Copyright (c) 2015 Kepler Project. | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of | |
5 | this software and associated documentation files (the "Software"), to deal in | |
6 | the Software without restriction, including without limitation the rights to | |
7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
8 | the Software, and to permit persons to whom the Software is furnished to do so, | |
9 | subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
0 | # lua-compat-5.3 | |
1 | ||
2 | Lua-5.3-style APIs for Lua 5.2 and 5.1. | |
3 | ||
4 | ## What is it | |
5 | ||
6 | This is a small module that aims to make it easier to write code | |
7 | in a Lua-5.3-style that is compatible with Lua 5.1, Lua 5.2, and Lua | |
8 | 5.3. This does *not* make Lua 5.2 (or even Lua 5.1) entirely | |
9 | compatible with Lua 5.3, but it brings the API closer to that of Lua | |
10 | 5.3. | |
11 | ||
12 | It includes: | |
13 | ||
14 | * _For writing Lua_: The Lua module `compat53`, which can be require'd | |
15 | from Lua scripts and run in Lua 5.1, 5.2, and 5.3, including a | |
16 | backport of the `utf8` module, the 5.3 `table` module, and the | |
17 | string packing functions straight from the Lua 5.3 sources. | |
18 | * _For writing C_: A C header and file which can be linked to your | |
19 | Lua module written in C, providing some functions from the C API | |
20 | of Lua 5.3 that do not exist in Lua 5.2 or 5.1, making it easier to | |
21 | write C code that compiles with all three versions of liblua. | |
22 | ||
23 | ## How to use it | |
24 | ||
25 | ### Lua module | |
26 | ||
27 | ```lua | |
28 | require("compat53") | |
29 | ``` | |
30 | ||
31 | `compat53` makes changes to your global environment and does not return | |
32 | a meaningful return value, so the usual idiom of storing the return of | |
33 | `require` in a local variable makes no sense. | |
34 | ||
35 | When run under Lua 5.3, this module does nothing. | |
36 | ||
37 | When run under Lua 5.2 or 5.1, it replaces some of your standard | |
38 | functions and adds new ones to bring your environment closer to that | |
39 | of Lua 5.3. It also tries to load the backported `utf8`, `table`, and | |
40 | string packing modules automatically. If unsuccessful, pure Lua | |
41 | versions of the new `table` functions are used as a fallback, and | |
42 | [Roberto's struct library][1] is tried for string packing. | |
43 | ||
44 | ### C code | |
45 | ||
46 | There are two ways of adding the C API compatibility functions/macros to | |
47 | your project: | |
48 | * If `COMPAT53_PREFIX` is *not* `#define`d, `compat-5.3.h` `#include`s | |
49 | `compat-5.3.c`, and all functions are made `static`. You don't have to | |
50 | compile/link/add `compat-5.3.c` yourself. This is useful for one-file | |
51 | projects. | |
52 | * If `COMPAT53_PREFIX` is `#define`d, all exported functions are renamed | |
53 | behind the scenes using this prefix to avoid linker conflicts with other | |
54 | code using this package. This doesn't change the way you call the | |
55 | compatibility functions in your code. You have to compile and link | |
56 | `compat-5.3.c` to your project yourself. You can change the way the | |
57 | functions are exported using the `COMPAT53_API` macro (e.g. if you need | |
58 | some `__declspec` magic). | |
59 | ||
60 | ## What's implemented | |
61 | ||
62 | ### Lua | |
63 | ||
64 | * the `utf8` module backported from the Lua 5.3 sources | |
65 | * `string.pack`, `string.packsize`, and `string.unpack` from the Lua | |
66 | 5.3 sources or from the `struct` module. (`struct` is not 100% | |
67 | compatible to Lua 5.3's string packing!) | |
68 | * `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`, | |
69 | and `math.ult` | |
70 | * `ipairs` respects `__index` metamethod | |
71 | * `table.move` | |
72 | * `table` library respects metamethods | |
73 | ||
74 | For Lua 5.1 additionally: | |
75 | * `load` and `loadfile` accept `mode` and `env` parameters | |
76 | * `table.pack` and `table.unpack` | |
77 | * string patterns may contain embedded zeros | |
78 | * `string.rep` accepts `sep` argument | |
79 | * `string.format` calls `tostring` on arguments for `%s` | |
80 | * `math.log` accepts base argument | |
81 | * `xpcall` takes additional arguments | |
82 | * `pcall` and `xpcall` can execute functions that yield | |
83 | * `pairs` respects `__pairs` metamethod | |
84 | * `rawlen` (but `#` still doesn't respect `__len` for tables) | |
85 | * `package.searchers` as alias for `package.loaders` | |
86 | * `package.searchpath` | |
87 | * `coroutine` functions dealing with the main coroutine | |
88 | * `coroutine.create` accepts functions written in C | |
89 | * return code of `os.execute` | |
90 | * `io.write` and `file:write` return file handle | |
91 | * `io.lines` and `file:lines` accept format arguments (like `io.read`) | |
92 | * `debug.setmetatable` returns object | |
93 | * `debug.getuservalue` and `debug.setuservalue` | |
94 | ||
95 | ### C | |
96 | ||
97 | * `lua_KContext` | |
98 | * `lua_KFunction` | |
99 | * `lua_dump` (extra `strip` parameter, ignored) | |
100 | * `lua_getfield` (return value) | |
101 | * `lua_geti` and `lua_seti` | |
102 | * `lua_getglobal` (return value) | |
103 | * `lua_getmetafield` (return value) | |
104 | * `lua_gettable` (return value) | |
105 | * `lua_getuservalue` and `lua_setuservalue` (limited compatibility) | |
106 | * `lua_isinteger` | |
107 | * `lua_numbertointeger` | |
108 | * `lua_callk` and `lua_pcallk` (limited compatibility) | |
109 | * `lua_rawget` and `lua_rawgeti` (return values) | |
110 | * `lua_rawgetp` and `lua_rawsetp` | |
111 | * `luaL_requiref` (now checks `package.loaded` first) | |
112 | * `lua_rotate` | |
113 | * `lua_stringtonumber` | |
114 | ||
115 | For Lua 5.1 additionally: | |
116 | * `LUA_OK` | |
117 | * `LUA_OP*` macros for `lua_arith` and `lua_compare` | |
118 | * `lua_Unsigned` | |
119 | * `luaL_Stream` | |
120 | * `LUA_FILEHANDLE` | |
121 | * `lua_absindex` | |
122 | * `lua_arith` | |
123 | * `lua_compare` | |
124 | * `lua_len`, `lua_rawlen`, and `luaL_len` | |
125 | * `lua_copy` | |
126 | * `lua_pushglobaltable` | |
127 | * `luaL_testudata` | |
128 | * `luaL_setfuncs`, `luaL_newlibtable`, and `luaL_newlib` | |
129 | * `luaL_setmetatable` | |
130 | * `luaL_getsubtable` | |
131 | * `luaL_traceback` | |
132 | * `luaL_fileresult` | |
133 | * `luaL_checkversion` (with empty body, only to avoid compile errors) | |
134 | * `luaL_tolstring` | |
135 | * `luaL_buffinitsize`, `luaL_prepbuffsize`, and `luaL_pushresultsize` | |
136 | * `lua_pushunsigned`, `lua_tounsignedx`, `lua_tounsigned`, | |
137 | `luaL_checkunsigned`, `luaL_optunsigned`, if | |
138 | `LUA_COMPAT_APIINTCASTS` is defined. | |
139 | ||
140 | ## What's not implemented | |
141 | ||
142 | * bit operators | |
143 | * integer division operator | |
144 | * utf8 escape sequences | |
145 | * 64 bit integers | |
146 | * `coroutine.isyieldable` | |
147 | * Lua 5.1: `_ENV`, `goto`, labels, ephemeron tables, etc. See | |
148 | [`lua-compat-5.2`][2] for a detailed list. | |
149 | * the following C API functions/macros: | |
150 | * `lua_isyieldable` | |
151 | * `lua_getextraspace` | |
152 | * `lua_arith` (new operators missing) | |
153 | * `lua_push(v)fstring` (new formats missing) | |
154 | * `lua_upvalueid` (5.1) | |
155 | * `lua_upvaluejoin` (5.1) | |
156 | * `lua_version` (5.1) | |
157 | * `lua_yieldk` (5.1) | |
158 | * `luaL_execresult` (5.1) | |
159 | * `luaL_loadbufferx` (5.1) | |
160 | * `luaL_loadfilex` (5.1) | |
161 | ||
162 | ### Yieldable C functions | |
163 | ||
164 | The emulation of `lua_(p)callk` for previous Lua versions is not 100% | |
165 | perfect, because the continuation functions in Lua 5.2 have different | |
166 | signatures than the ones in Lua 5.3 (and Lua 5.1 doesn't have | |
167 | continuation functions at all). But with the help of a small macro the | |
168 | same code can be used for all three Lua versions (the 5.1 version | |
169 | won't support yielding though). | |
170 | ||
171 | Original Lua 5.3 code (example adapted from the Lua 5.3 manual): | |
172 | ||
173 | ```C | |
174 | static int k (lua_State *L, int status, lua_KContext ctx) { | |
175 | ... /* code 2 */ | |
176 | } | |
177 | ||
178 | int original_function (lua_State *L) { | |
179 | ... /* code 1 */ | |
180 | return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1); | |
181 | } | |
182 | ``` | |
183 | ||
184 | Portable version: | |
185 | ||
186 | ```C | |
187 | LUA_KFUNCTION( k ) { | |
188 | ... /* code 2; parameters L, status, and ctx available here */ | |
189 | } | |
190 | ||
191 | int original_function (lua_State *L) { | |
192 | ... /* code 1 */ | |
193 | return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1); | |
194 | } | |
195 | ``` | |
196 | ||
197 | ## See also | |
198 | ||
199 | * For Lua-5.2-style APIs under Lua 5.1, see [lua-compat-5.2][2], | |
200 | which also is the basis for most of the code in this project. | |
201 | * For Lua-5.1-style APIs under Lua 5.0, see [Compat-5.1][3] | |
202 | ||
203 | ## Credits | |
204 | ||
205 | This package contains code written by: | |
206 | ||
207 | * [The Lua Team](http://www.lua.org) | |
208 | * Philipp Janda ([@siffiejoe](http://github.com/siffiejoe)) | |
209 | * Tomás Guisasola Gorham ([@tomasguisasola](http://github.com/tomasguisasola)) | |
210 | * Hisham Muhammad ([@hishamhm](http://github.com/hishamhm)) | |
211 | * Renato Maia ([@renatomaia](http://github.com/renatomaia)) | |
212 | ||
213 | ||
214 | [1]: http://www.inf.puc-rio.br/~roberto/struct/ | |
215 | [2]: http://github.com/keplerproject/lua-compat-5.2/ | |
216 | [3]: http://keplerproject.org/compat/ | |
217 |
0 | #include <stddef.h> | |
1 | #include <stdlib.h> | |
2 | #include <string.h> | |
3 | #include <ctype.h> | |
4 | #include <errno.h> | |
5 | #include <lua.h> | |
6 | #include <lauxlib.h> | |
7 | #include "compat-5.3.h" | |
8 | ||
9 | /* don't compile it again if it already is included via compat53.h */ | |
10 | #ifndef COMPAT53_C_ | |
11 | #define COMPAT53_C_ | |
12 | ||
13 | ||
14 | ||
15 | /* definitions for Lua 5.1 only */ | |
16 | #if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM == 501 | |
17 | ||
18 | ||
19 | COMPAT53_API int lua_absindex (lua_State *L, int i) { | |
20 | if (i < 0 && i > LUA_REGISTRYINDEX) | |
21 | i += lua_gettop(L) + 1; | |
22 | return i; | |
23 | } | |
24 | ||
25 | ||
26 | static void compat53_call_lua (lua_State *L, char const code[], size_t len, | |
27 | int nargs, int nret) { | |
28 | lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); | |
29 | if (lua_type(L, -1) != LUA_TFUNCTION) { | |
30 | lua_pop(L, 1); | |
31 | if (luaL_loadbuffer(L, code, len, "=none")) | |
32 | lua_error(L); | |
33 | lua_pushvalue(L, -1); | |
34 | lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); | |
35 | } | |
36 | lua_insert(L, -nargs-1); | |
37 | lua_call(L, nargs, nret); | |
38 | } | |
39 | ||
40 | ||
41 | static const char compat53_arith_code[] = | |
42 | "local op,a,b=...\n" | |
43 | "if op==0 then return a+b\n" | |
44 | "elseif op==1 then return a-b\n" | |
45 | "elseif op==2 then return a*b\n" | |
46 | "elseif op==3 then return a/b\n" | |
47 | "elseif op==4 then return a%b\n" | |
48 | "elseif op==5 then return a^b\n" | |
49 | "elseif op==6 then return -a\n" | |
50 | "end\n"; | |
51 | ||
52 | COMPAT53_API void lua_arith (lua_State *L, int op) { | |
53 | if (op < LUA_OPADD || op > LUA_OPUNM) | |
54 | luaL_error(L, "invalid 'op' argument for lua_arith"); | |
55 | luaL_checkstack(L, 5, "not enough stack slots"); | |
56 | if (op == LUA_OPUNM) | |
57 | lua_pushvalue(L, -1); | |
58 | lua_pushnumber(L, op); | |
59 | lua_insert(L, -3); | |
60 | compat53_call_lua(L, compat53_arith_code, | |
61 | sizeof(compat53_arith_code)-1, 3, 1); | |
62 | } | |
63 | ||
64 | ||
65 | static const char compat53_compare_code[] = | |
66 | "local a,b=...\n" | |
67 | "return a<=b\n"; | |
68 | ||
69 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { | |
70 | int result = 0; | |
71 | switch (op) { | |
72 | case LUA_OPEQ: | |
73 | return lua_equal(L, idx1, idx2); | |
74 | case LUA_OPLT: | |
75 | return lua_lessthan(L, idx1, idx2); | |
76 | case LUA_OPLE: | |
77 | luaL_checkstack(L, 5, "not enough stack slots"); | |
78 | idx1 = lua_absindex(L, idx1); | |
79 | idx2 = lua_absindex(L, idx2); | |
80 | lua_pushvalue(L, idx1); | |
81 | lua_pushvalue(L, idx2); | |
82 | compat53_call_lua(L, compat53_compare_code, | |
83 | sizeof(compat53_compare_code)-1, 2, 1); | |
84 | result = lua_toboolean(L, -1); | |
85 | lua_pop(L, 1); | |
86 | return result; | |
87 | default: | |
88 | luaL_error(L, "invalid 'op' argument for lua_compare"); | |
89 | } | |
90 | return 0; | |
91 | } | |
92 | ||
93 | ||
94 | COMPAT53_API void lua_copy (lua_State *L, int from, int to) { | |
95 | int abs_to = lua_absindex(L, to); | |
96 | luaL_checkstack(L, 1, "not enough stack slots"); | |
97 | lua_pushvalue(L, from); | |
98 | lua_replace(L, abs_to); | |
99 | } | |
100 | ||
101 | ||
102 | COMPAT53_API void lua_len (lua_State *L, int i) { | |
103 | switch (lua_type(L, i)) { | |
104 | case LUA_TSTRING: /* fall through */ | |
105 | case LUA_TTABLE: | |
106 | if (!luaL_callmeta(L, i, "__len")) | |
107 | lua_pushnumber(L, (int)lua_objlen(L, i)); | |
108 | break; | |
109 | case LUA_TUSERDATA: | |
110 | if (luaL_callmeta(L, i, "__len")) | |
111 | break; | |
112 | /* maybe fall through */ | |
113 | default: | |
114 | luaL_error(L, "attempt to get length of a %s value", | |
115 | lua_typename(L, lua_type(L, i))); | |
116 | } | |
117 | } | |
118 | ||
119 | ||
120 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { | |
121 | int abs_i = lua_absindex(L, i); | |
122 | lua_pushlightuserdata(L, (void*)p); | |
123 | lua_rawget(L, abs_i); | |
124 | return lua_type(L, -1); | |
125 | } | |
126 | ||
127 | COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { | |
128 | int abs_i = lua_absindex(L, i); | |
129 | luaL_checkstack(L, 1, "not enough stack slots"); | |
130 | lua_pushlightuserdata(L, (void*)p); | |
131 | lua_insert(L, -2); | |
132 | lua_rawset(L, abs_i); | |
133 | } | |
134 | ||
135 | ||
136 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { | |
137 | lua_Integer n = lua_tointeger(L, i); | |
138 | if (isnum != NULL) { | |
139 | *isnum = (n != 0 || lua_isnumber(L, i)); | |
140 | } | |
141 | return n; | |
142 | } | |
143 | ||
144 | ||
145 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { | |
146 | lua_Number n = lua_tonumber(L, i); | |
147 | if (isnum != NULL) { | |
148 | *isnum = (n != 0 || lua_isnumber(L, i)); | |
149 | } | |
150 | return n; | |
151 | } | |
152 | ||
153 | ||
154 | COMPAT53_API void luaL_checkversion (lua_State *L) { | |
155 | (void)L; | |
156 | } | |
157 | ||
158 | ||
159 | COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { | |
160 | int abs_i = lua_absindex(L, i); | |
161 | luaL_checkstack(L, 3, "not enough stack slots"); | |
162 | lua_pushstring(L, name); | |
163 | lua_gettable(L, abs_i); | |
164 | if (lua_istable(L, -1)) | |
165 | return 1; | |
166 | lua_pop(L, 1); | |
167 | lua_newtable(L); | |
168 | lua_pushstring(L, name); | |
169 | lua_pushvalue(L, -2); | |
170 | lua_settable(L, abs_i); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | ||
175 | COMPAT53_API int luaL_len (lua_State *L, int i) { | |
176 | int res = 0, isnum = 0; | |
177 | luaL_checkstack(L, 1, "not enough stack slots"); | |
178 | lua_len(L, i); | |
179 | res = (int)lua_tointegerx(L, -1, &isnum); | |
180 | lua_pop(L, 1); | |
181 | if (!isnum) | |
182 | luaL_error(L, "object length is not a number"); | |
183 | return res; | |
184 | } | |
185 | ||
186 | ||
187 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { | |
188 | luaL_checkstack(L, nup+1, "too many upvalues"); | |
189 | for (; l->name != NULL; l++) { /* fill the table with given functions */ | |
190 | int i; | |
191 | lua_pushstring(L, l->name); | |
192 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ | |
193 | lua_pushvalue(L, -(nup + 1)); | |
194 | lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ | |
195 | lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ | |
196 | } | |
197 | lua_pop(L, nup); /* remove upvalues */ | |
198 | } | |
199 | ||
200 | ||
201 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { | |
202 | luaL_checkstack(L, 1, "not enough stack slots"); | |
203 | luaL_getmetatable(L, tname); | |
204 | lua_setmetatable(L, -2); | |
205 | } | |
206 | ||
207 | ||
208 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { | |
209 | void *p = lua_touserdata(L, i); | |
210 | luaL_checkstack(L, 2, "not enough stack slots"); | |
211 | if (p == NULL || !lua_getmetatable(L, i)) | |
212 | return NULL; | |
213 | else { | |
214 | int res = 0; | |
215 | luaL_getmetatable(L, tname); | |
216 | res = lua_rawequal(L, -1, -2); | |
217 | lua_pop(L, 2); | |
218 | if (!res) | |
219 | p = NULL; | |
220 | } | |
221 | return p; | |
222 | } | |
223 | ||
224 | ||
225 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { | |
226 | if (!luaL_callmeta(L, idx, "__tostring")) { | |
227 | int t = lua_type(L, idx); | |
228 | switch (t) { | |
229 | case LUA_TNIL: | |
230 | lua_pushliteral(L, "nil"); | |
231 | break; | |
232 | case LUA_TSTRING: | |
233 | case LUA_TNUMBER: | |
234 | lua_pushvalue(L, idx); | |
235 | break; | |
236 | case LUA_TBOOLEAN: | |
237 | if (lua_toboolean(L, idx)) | |
238 | lua_pushliteral(L, "true"); | |
239 | else | |
240 | lua_pushliteral(L, "false"); | |
241 | break; | |
242 | default: | |
243 | lua_pushfstring(L, "%s: %p", lua_typename(L, t), | |
244 | lua_topointer(L, idx)); | |
245 | break; | |
246 | } | |
247 | } | |
248 | return lua_tolstring(L, -1, len); | |
249 | } | |
250 | ||
251 | ||
252 | #if !defined(COMPAT53_IS_LUAJIT) | |
253 | static int compat53_countlevels (lua_State *L) { | |
254 | lua_Debug ar; | |
255 | int li = 1, le = 1; | |
256 | /* find an upper bound */ | |
257 | while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } | |
258 | /* do a binary search */ | |
259 | while (li < le) { | |
260 | int m = (li + le)/2; | |
261 | if (lua_getstack(L, m, &ar)) li = m + 1; | |
262 | else le = m; | |
263 | } | |
264 | return le - 1; | |
265 | } | |
266 | ||
267 | static int compat53_findfield (lua_State *L, int objidx, int level) { | |
268 | if (level == 0 || !lua_istable(L, -1)) | |
269 | return 0; /* not found */ | |
270 | lua_pushnil(L); /* start 'next' loop */ | |
271 | while (lua_next(L, -2)) { /* for each pair in table */ | |
272 | if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ | |
273 | if (lua_rawequal(L, objidx, -1)) { /* found object? */ | |
274 | lua_pop(L, 1); /* remove value (but keep name) */ | |
275 | return 1; | |
276 | } | |
277 | else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ | |
278 | lua_remove(L, -2); /* remove table (but keep name) */ | |
279 | lua_pushliteral(L, "."); | |
280 | lua_insert(L, -2); /* place '.' between the two names */ | |
281 | lua_concat(L, 3); | |
282 | return 1; | |
283 | } | |
284 | } | |
285 | lua_pop(L, 1); /* remove value */ | |
286 | } | |
287 | return 0; /* not found */ | |
288 | } | |
289 | ||
290 | static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { | |
291 | int top = lua_gettop(L); | |
292 | lua_getinfo(L, "f", ar); /* push function */ | |
293 | lua_pushvalue(L, LUA_GLOBALSINDEX); | |
294 | if (compat53_findfield(L, top + 1, 2)) { | |
295 | lua_copy(L, -1, top + 1); /* move name to proper place */ | |
296 | lua_pop(L, 2); /* remove pushed values */ | |
297 | return 1; | |
298 | } | |
299 | else { | |
300 | lua_settop(L, top); /* remove function and global table */ | |
301 | return 0; | |
302 | } | |
303 | } | |
304 | ||
305 | static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { | |
306 | if (*ar->namewhat != '\0') /* is there a name? */ | |
307 | lua_pushfstring(L, "function " LUA_QS, ar->name); | |
308 | else if (*ar->what == 'm') /* main? */ | |
309 | lua_pushliteral(L, "main chunk"); | |
310 | else if (*ar->what == 'C') { | |
311 | if (compat53_pushglobalfuncname(L, ar)) { | |
312 | lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); | |
313 | lua_remove(L, -2); /* remove name */ | |
314 | } | |
315 | else | |
316 | lua_pushliteral(L, "?"); | |
317 | } | |
318 | else | |
319 | lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); | |
320 | } | |
321 | ||
322 | #define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ | |
323 | #define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ | |
324 | ||
325 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, | |
326 | const char *msg, int level) { | |
327 | lua_Debug ar; | |
328 | int top = lua_gettop(L); | |
329 | int numlevels = compat53_countlevels(L1); | |
330 | int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; | |
331 | if (msg) lua_pushfstring(L, "%s\n", msg); | |
332 | lua_pushliteral(L, "stack traceback:"); | |
333 | while (lua_getstack(L1, level++, &ar)) { | |
334 | if (level == mark) { /* too many levels? */ | |
335 | lua_pushliteral(L, "\n\t..."); /* add a '...' */ | |
336 | level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */ | |
337 | } | |
338 | else { | |
339 | lua_getinfo(L1, "Slnt", &ar); | |
340 | lua_pushfstring(L, "\n\t%s:", ar.short_src); | |
341 | if (ar.currentline > 0) | |
342 | lua_pushfstring(L, "%d:", ar.currentline); | |
343 | lua_pushliteral(L, " in "); | |
344 | compat53_pushfuncname(L, &ar); | |
345 | lua_concat(L, lua_gettop(L) - top); | |
346 | } | |
347 | } | |
348 | lua_concat(L, lua_gettop(L) - top); | |
349 | } | |
350 | ||
351 | ||
352 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { | |
353 | int en = errno; /* calls to Lua API may change this value */ | |
354 | if (stat) { | |
355 | lua_pushboolean(L, 1); | |
356 | return 1; | |
357 | } | |
358 | else { | |
359 | lua_pushnil(L); | |
360 | if (fname) | |
361 | lua_pushfstring(L, "%s: %s", fname, strerror(en)); | |
362 | else | |
363 | lua_pushstring(L, strerror(en)); | |
364 | lua_pushnumber(L, (lua_Number)en); | |
365 | return 3; | |
366 | } | |
367 | } | |
368 | #endif /* not COMPAT53_IS_LUAJIT */ | |
369 | ||
370 | ||
371 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { | |
372 | /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ | |
373 | B->b.p = NULL; | |
374 | B->b.L = NULL; | |
375 | B->b.lvl = 0; | |
376 | /* reuse the buffer from the 5.1-style luaL_Buffer though! */ | |
377 | B->ptr = B->b.buffer; | |
378 | B->capacity = LUAL_BUFFERSIZE; | |
379 | B->nelems = 0; | |
380 | B->L2 = L; | |
381 | } | |
382 | ||
383 | ||
384 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { | |
385 | if (B->capacity - B->nelems < s) { /* needs to grow */ | |
386 | char* newptr = NULL; | |
387 | size_t newcap = B->capacity * 2; | |
388 | if (newcap - B->nelems < s) | |
389 | newcap = B->nelems + s; | |
390 | if (newcap < B->capacity) /* overflow */ | |
391 | luaL_error(B->L2, "buffer too large"); | |
392 | newptr = lua_newuserdata(B->L2, newcap); | |
393 | memcpy(newptr, B->ptr, B->nelems); | |
394 | if (B->ptr != B->b.buffer) | |
395 | lua_replace(B->L2, -2); /* remove old buffer */ | |
396 | B->ptr = newptr; | |
397 | B->capacity = newcap; | |
398 | } | |
399 | return B->ptr+B->nelems; | |
400 | } | |
401 | ||
402 | ||
403 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { | |
404 | memcpy(luaL_prepbuffsize(B, l), s, l); | |
405 | luaL_addsize(B, l); | |
406 | } | |
407 | ||
408 | ||
409 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { | |
410 | size_t len = 0; | |
411 | const char *s = lua_tolstring(B->L2, -1, &len); | |
412 | if (!s) | |
413 | luaL_error(B->L2, "cannot convert value to string"); | |
414 | if (B->ptr != B->b.buffer) | |
415 | lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ | |
416 | luaL_addlstring(B, s, len); | |
417 | lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); | |
418 | } | |
419 | ||
420 | ||
421 | void luaL_pushresult (luaL_Buffer_53 *B) { | |
422 | lua_pushlstring(B->L2, B->ptr, B->nelems); | |
423 | if (B->ptr != B->b.buffer) | |
424 | lua_replace(B->L2, -2); /* remove userdata buffer */ | |
425 | } | |
426 | ||
427 | ||
428 | #endif /* Lua 5.1 */ | |
429 | ||
430 | ||
431 | ||
432 | /* definitions for Lua 5.1 and Lua 5.2 */ | |
433 | #if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 | |
434 | ||
435 | ||
436 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { | |
437 | index = lua_absindex(L, index); | |
438 | lua_pushinteger(L, i); | |
439 | lua_gettable(L, index); | |
440 | return lua_type(L, -1); | |
441 | } | |
442 | ||
443 | ||
444 | COMPAT53_API int lua_isinteger (lua_State *L, int index) { | |
445 | if (lua_type(L, index) == LUA_TNUMBER) { | |
446 | lua_Number n = lua_tonumber(L, index); | |
447 | lua_Integer i = lua_tointeger(L, index); | |
448 | if (i == n) | |
449 | return 1; | |
450 | } | |
451 | return 0; | |
452 | } | |
453 | ||
454 | ||
455 | static void compat53_reverse (lua_State *L, int a, int b) { | |
456 | for (; a < b; ++a, --b) { | |
457 | lua_pushvalue(L, a); | |
458 | lua_pushvalue(L, b); | |
459 | lua_replace(L, a); | |
460 | lua_replace(L, b); | |
461 | } | |
462 | } | |
463 | ||
464 | ||
465 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { | |
466 | int n_elems = 0; | |
467 | idx = lua_absindex(L, idx); | |
468 | n_elems = lua_gettop(L)-idx+1; | |
469 | if (n < 0) | |
470 | n += n_elems; | |
471 | if ( n > 0 && n < n_elems) { | |
472 | luaL_checkstack(L, 2, "not enough stack slots available"); | |
473 | n = n_elems - n; | |
474 | compat53_reverse(L, idx, idx+n-1); | |
475 | compat53_reverse(L, idx+n, idx+n_elems-1); | |
476 | compat53_reverse(L, idx, idx+n_elems-1); | |
477 | } | |
478 | } | |
479 | ||
480 | ||
481 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { | |
482 | luaL_checkstack(L, 1, "not enough stack slots available"); | |
483 | index = lua_absindex(L, index); | |
484 | lua_pushinteger(L, i); | |
485 | lua_insert(L, -2); | |
486 | lua_settable(L, index); | |
487 | } | |
488 | ||
489 | ||
490 | #if !defined(lua_str2number) | |
491 | # define lua_str2number(s, p) strtod(s, p) | |
492 | #endif | |
493 | ||
494 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { | |
495 | char* endptr; | |
496 | lua_Number n = lua_str2number(s, &endptr); | |
497 | if (endptr != s) { | |
498 | while (*endptr != '\0' && isspace((unsigned char)*endptr)) | |
499 | ++endptr; | |
500 | if (*endptr == '\0') { | |
501 | lua_pushnumber(L, n); | |
502 | return endptr - s + 1; | |
503 | } | |
504 | } | |
505 | return 0; | |
506 | } | |
507 | ||
508 | ||
509 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, | |
510 | lua_CFunction openf, int glb) { | |
511 | luaL_checkstack(L, 3, "not enough stack slots available"); | |
512 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); | |
513 | if (lua_getfield(L, -1, modname) == LUA_TNIL) { | |
514 | lua_pop(L, 1); | |
515 | lua_pushcfunction(L, openf); | |
516 | lua_pushstring(L, modname); | |
517 | lua_call(L, 1, 1); | |
518 | lua_pushvalue(L, -1); | |
519 | lua_setfield(L, -3, modname); | |
520 | } | |
521 | if (glb) { | |
522 | lua_pushvalue(L, -1); | |
523 | lua_setglobal(L, modname); | |
524 | } | |
525 | lua_replace(L, -2); | |
526 | } | |
527 | ||
528 | ||
529 | #endif /* Lua 5.1 and 5.2 */ | |
530 | ||
531 | ||
532 | #endif /* COMPAT53_C_ */ | |
533 | ||
534 | ||
535 | /********************************************************************* | |
536 | * This file contains parts of Lua 5.2's and Lua 5.3's source code: | |
537 | * | |
538 | * Copyright (C) 1994-2014 Lua.org, PUC-Rio. | |
539 | * | |
540 | * Permission is hereby granted, free of charge, to any person obtaining | |
541 | * a copy of this software and associated documentation files (the | |
542 | * "Software"), to deal in the Software without restriction, including | |
543 | * without limitation the rights to use, copy, modify, merge, publish, | |
544 | * distribute, sublicense, and/or sell copies of the Software, and to | |
545 | * permit persons to whom the Software is furnished to do so, subject to | |
546 | * the following conditions: | |
547 | * | |
548 | * The above copyright notice and this permission notice shall be | |
549 | * included in all copies or substantial portions of the Software. | |
550 | * | |
551 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
552 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
553 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
554 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
555 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
556 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
557 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
558 | *********************************************************************/ | |
559 |
0 | #ifndef COMPAT53_H_ | |
1 | #define COMPAT53_H_ | |
2 | ||
3 | #include <stddef.h> | |
4 | #include <limits.h> | |
5 | #include <string.h> | |
6 | #include <lua.h> | |
7 | #include <lauxlib.h> | |
8 | #include <lualib.h> | |
9 | ||
10 | ||
11 | #if defined(COMPAT53_PREFIX) | |
12 | /* - change the symbol names of functions to avoid linker conflicts | |
13 | * - compat-5.3.c needs to be compiled (and linked) separately | |
14 | */ | |
15 | # if !defined(COMPAT53_API) | |
16 | # define COMPAT53_API extern | |
17 | # endif | |
18 | # undef COMPAT53_INCLUDE_SOURCE | |
19 | #else /* COMPAT53_PREFIX */ | |
20 | /* - make all functions static and include the source. | |
21 | * - don't mess with the symbol names of functions | |
22 | * - compat-5.3.c doesn't need to be compiled (and linked) separately | |
23 | */ | |
24 | # define COMPAT53_PREFIX lua | |
25 | # undef COMPAT53_API | |
26 | # if defined(__GNUC__) || defined(__clang__) | |
27 | # define COMPAT53_API __attribute__((__unused__)) static | |
28 | # else | |
29 | # define COMPAT53_API static | |
30 | # endif | |
31 | # define COMPAT53_INCLUDE_SOURCE | |
32 | #endif /* COMPAT53_PREFIX */ | |
33 | ||
34 | #define COMPAT53_CONCAT_HELPER(a, b) a##b | |
35 | #define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) | |
36 | ||
37 | ||
38 | ||
39 | /* declarations for Lua 5.1 */ | |
40 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 | |
41 | ||
42 | /* XXX not implemented: | |
43 | * lua_arith (new operators) | |
44 | * lua_upvalueid | |
45 | * lua_upvaluejoin | |
46 | * lua_version | |
47 | * lua_yieldk | |
48 | * luaL_execresult | |
49 | * luaL_loadbufferx | |
50 | * luaL_loadfilex | |
51 | */ | |
52 | ||
53 | /* PUC-Rio Lua uses lconfig_h as include guard for luaconf.h, | |
54 | * LuaJIT uses luaconf_h. If you use PUC-Rio's include files | |
55 | * but LuaJIT's library, you will need to define the macro | |
56 | * COMPAT53_IS_LUAJIT yourself! */ | |
57 | #if !defined(COMPAT53_IS_LUAJIT) && defined(luaconf_h) | |
58 | # define COMPAT53_IS_LUAJIT | |
59 | #endif | |
60 | ||
61 | #define LUA_OK 0 | |
62 | #define LUA_OPADD 0 | |
63 | #define LUA_OPSUB 1 | |
64 | #define LUA_OPMUL 2 | |
65 | #define LUA_OPDIV 3 | |
66 | #define LUA_OPMOD 4 | |
67 | #define LUA_OPPOW 5 | |
68 | #define LUA_OPUNM 6 | |
69 | #define LUA_OPEQ 0 | |
70 | #define LUA_OPLT 1 | |
71 | #define LUA_OPLE 2 | |
72 | ||
73 | typedef struct luaL_Stream { | |
74 | FILE *f; | |
75 | /* The following field is for LuaJIT which adds a uint32_t field | |
76 | * to file handles. */ | |
77 | #if INT_MAX > 2147483640L | |
78 | unsigned int type; | |
79 | #else | |
80 | unsigned long type; | |
81 | #endif | |
82 | lua_CFunction closef; | |
83 | } luaL_Stream; | |
84 | ||
85 | typedef size_t lua_Unsigned; | |
86 | ||
87 | typedef struct luaL_Buffer_53 { | |
88 | luaL_Buffer b; /* make incorrect code crash! */ | |
89 | char *ptr; | |
90 | size_t nelems; | |
91 | size_t capacity; | |
92 | lua_State *L2; | |
93 | } luaL_Buffer_53; | |
94 | #define luaL_Buffer luaL_Buffer_53 | |
95 | ||
96 | #define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) | |
97 | COMPAT53_API int lua_absindex (lua_State *L, int i); | |
98 | ||
99 | #define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) | |
100 | COMPAT53_API void lua_arith (lua_State *L, int op); | |
101 | ||
102 | #define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) | |
103 | COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); | |
104 | ||
105 | #define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) | |
106 | COMPAT53_API void lua_copy (lua_State *L, int from, int to); | |
107 | ||
108 | #define lua_getuservalue(L, i) \ | |
109 | (lua_getfenv(L, i), lua_type(L, -1)) | |
110 | #define lua_setuservalue(L, i) \ | |
111 | (luaL_checktype(L, -1, LUA_TTABLE), lua_setfenv(L, i)) | |
112 | ||
113 | #define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) | |
114 | COMPAT53_API void lua_len (lua_State *L, int i); | |
115 | ||
116 | #define luaL_newlibtable(L, l) \ | |
117 | (lua_createtable(L, 0, sizeof(l)/sizeof(*(l))-1)) | |
118 | #define luaL_newlib(L, l) \ | |
119 | (luaL_newlibtable(L, l), luaL_register(L, NULL, l)) | |
120 | ||
121 | #define lua_pushglobaltable(L) \ | |
122 | lua_pushvalue(L, LUA_GLOBALSINDEX) | |
123 | ||
124 | #define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) | |
125 | COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); | |
126 | ||
127 | #define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) | |
128 | COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); | |
129 | ||
130 | #define lua_rawlen(L, i) lua_objlen(L, i) | |
131 | ||
132 | #define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx) | |
133 | COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); | |
134 | ||
135 | #define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) | |
136 | COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); | |
137 | ||
138 | #define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) | |
139 | COMPAT53_API void luaL_checkversion (lua_State *L); | |
140 | ||
141 | #define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) | |
142 | COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); | |
143 | ||
144 | #define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) | |
145 | COMPAT53_API int luaL_len (lua_State *L, int i); | |
146 | ||
147 | #define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) | |
148 | COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); | |
149 | ||
150 | #define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) | |
151 | COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); | |
152 | ||
153 | #define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) | |
154 | COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); | |
155 | ||
156 | #define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) | |
157 | COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); | |
158 | ||
159 | #if !defined(COMPAT53_IS_LUAJIT) | |
160 | #define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) | |
161 | COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); | |
162 | ||
163 | #define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) | |
164 | COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); | |
165 | #endif /* COMPAT53_IS_LUAJIT */ | |
166 | ||
167 | #define lua_callk(L, na, nr, ctx, cont) \ | |
168 | ((void)(ctx), (void)(cont), lua_call(L, na, nr)) | |
169 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ | |
170 | ((void)(ctx), (void)(cont), lua_pcall(L, na, nr, err)) | |
171 | ||
172 | #define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) | |
173 | COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); | |
174 | ||
175 | #define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) | |
176 | COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); | |
177 | ||
178 | #define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) | |
179 | COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); | |
180 | ||
181 | #define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) | |
182 | COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); | |
183 | ||
184 | #define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) | |
185 | COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); | |
186 | ||
187 | #undef luaL_buffinitsize | |
188 | #define luaL_buffinitsize(L, B, s) \ | |
189 | (luaL_buffinit(L, B), luaL_prepbuffsize(B, s)) | |
190 | ||
191 | #undef luaL_prepbuffer | |
192 | #define luaL_prepbuffer(B) \ | |
193 | luaL_prepbuffsize(B, LUAL_BUFFERSIZE) | |
194 | ||
195 | #undef luaL_addchar | |
196 | #define luaL_addchar(B, c) \ | |
197 | ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize(B, 1)), \ | |
198 | ((B)->ptr[(B)->nelems++] = (c))) | |
199 | ||
200 | #undef luaL_addsize | |
201 | #define luaL_addsize(B, s) \ | |
202 | ((B)->nelems += (s)) | |
203 | ||
204 | #undef luaL_addstring | |
205 | #define luaL_addstring(B, s) \ | |
206 | luaL_addlstring(B, s, strlen(s)) | |
207 | ||
208 | #undef luaL_pushresultsize | |
209 | #define luaL_pushresultsize(B, s) \ | |
210 | (luaL_addsize(B, s), luaL_pushresult(B)) | |
211 | ||
212 | #if defined(LUA_COMPAT_APIINTCASTS) | |
213 | #define lua_pushunsigned(L, n) \ | |
214 | lua_pushinteger(L, (lua_Integer)(n)) | |
215 | #define lua_tounsignedx(L, i, is) \ | |
216 | ((lua_Unsigned)lua_tointegerx(L, i, is)) | |
217 | #define lua_tounsigned(L, i) \ | |
218 | lua_tounsignedx(L, i, NULL) | |
219 | #define luaL_checkunsigned(L, a) \ | |
220 | ((lua_Unsigned)luaL_checkinteger(L, a)) | |
221 | #define luaL_optunsigned(L, a, d) \ | |
222 | ((lua_Unsigned)luaL_optinteger(L, a, (lua_Integer)(d))) | |
223 | #endif | |
224 | ||
225 | #endif /* Lua 5.1 only */ | |
226 | ||
227 | ||
228 | ||
229 | /* declarations for Lua 5.1 and 5.2 */ | |
230 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 | |
231 | ||
232 | typedef int lua_KContext; | |
233 | ||
234 | typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); | |
235 | ||
236 | #define lua_dump(L, w, d, s) \ | |
237 | ((void)(s), lua_dump(L, w, d)) | |
238 | ||
239 | #define lua_getfield(L, i, k) \ | |
240 | (lua_getfield(L, i, k), lua_type(L, -1)) | |
241 | ||
242 | #define lua_gettable(L, i) \ | |
243 | (lua_gettable(L, i), lua_type(L, -1)) | |
244 | ||
245 | #define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) | |
246 | COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); | |
247 | ||
248 | #define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) | |
249 | COMPAT53_API int lua_isinteger (lua_State *L, int index); | |
250 | ||
251 | #define lua_numbertointeger(n, p) \ | |
252 | ((*(p) = (lua_Integer)(n)), 1) | |
253 | ||
254 | #define lua_rawget(L, i) \ | |
255 | (lua_rawget(L, i), lua_type(L, -1)) | |
256 | ||
257 | #define lua_rawgeti(L, i, n) \ | |
258 | (lua_rawgeti(L, i, n), lua_type(L, -1)) | |
259 | ||
260 | #define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) | |
261 | COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); | |
262 | ||
263 | #define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) | |
264 | COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); | |
265 | ||
266 | #define lua_strtonum COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) | |
267 | COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); | |
268 | ||
269 | #define luaL_getmetafield(L, o, e) \ | |
270 | (luaL_getmetafield(L, o, e) ? lua_type(L, -1) : LUA_TNIL) | |
271 | ||
272 | #define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) | |
273 | COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, | |
274 | lua_CFunction openf, int glb ); | |
275 | ||
276 | #endif /* Lua 5.1 and Lua 5.2 */ | |
277 | ||
278 | ||
279 | ||
280 | /* declarations for Lua 5.2 */ | |
281 | #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 | |
282 | ||
283 | /* XXX not implemented: | |
284 | * lua_isyieldable | |
285 | * lua_getextraspace | |
286 | * lua_arith (new operators) | |
287 | * lua_pushfstring (new formats) | |
288 | */ | |
289 | ||
290 | #define lua_getglobal(L, n) \ | |
291 | (lua_getglobal(L, n), lua_type(L, -1)) | |
292 | ||
293 | #define lua_getuservalue(L, i) \ | |
294 | (lua_getuservalue(L, i), lua_type(L, -1)) | |
295 | ||
296 | #define lua_rawgetp(L, i, p) \ | |
297 | (lua_rawgetp(L, i, p), lua_type(L, -1)) | |
298 | ||
299 | #define LUA_KFUNCTION(_name) \ | |
300 | static int (_name)(lua_State *L, int status, lua_KContext ctx); \ | |
301 | static int (_name ## _52)(lua_State *L) { \ | |
302 | lua_KContext ctx; \ | |
303 | int status = lua_getctx(L, &ctx); \ | |
304 | return (_name)(L, status, ctx); \ | |
305 | } \ | |
306 | static int (_name)(lua_State *L, int status, lua_KContext ctx) | |
307 | ||
308 | #define lua_pcallk(L, na, nr, err, ctx, cont) \ | |
309 | lua_pcallk(L, na, nr, err, ctx, cont ## _52) | |
310 | ||
311 | #define lua_callk(L, na, nr, ctx, cont) \ | |
312 | lua_callk(L, na, nr, ctx, cont ## _52) | |
313 | ||
314 | #define lua_yieldk(L, nr, ctx, cont) \ | |
315 | lua_yieldk(L, nr, ctx, cont ## _52) | |
316 | ||
317 | #ifdef lua_call | |
318 | # undef lua_call | |
319 | # define lua_call(L, na, nr) \ | |
320 | (lua_callk)(L, na, nr, 0, NULL) | |
321 | #endif | |
322 | ||
323 | #ifdef lua_pcall | |
324 | # undef lua_pcall | |
325 | # define lua_pcall(L, na, nr, err) \ | |
326 | (lua_pcallk)(L, na, nr, err, 0, NULL) | |
327 | #endif | |
328 | ||
329 | #ifdef lua_yield | |
330 | # undef lua_yield | |
331 | # define lua_yield(L, nr) \ | |
332 | (lua_yieldk)(L, nr, 0, NULL) | |
333 | #endif | |
334 | ||
335 | #endif /* Lua 5.2 only */ | |
336 | ||
337 | ||
338 | ||
339 | /* other Lua versions */ | |
340 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 503 | |
341 | ||
342 | # error "unsupported Lua version (i.e. not Lua 5.1, 5.2, or 5.3)" | |
343 | ||
344 | #endif /* other Lua versions except 5.1, 5.2, and 5.3 */ | |
345 | ||
346 | ||
347 | ||
348 | /* helper macro for defining continuation functions (for every version | |
349 | * *except* Lua 5.2) */ | |
350 | #ifndef LUA_KFUNCTION | |
351 | #define LUA_KFUNCTION(_name) \ | |
352 | static int (_name)(lua_State *L, int status, lua_KContext ctx) | |
353 | #endif | |
354 | ||
355 | ||
356 | #if defined(COMPAT53_INCLUDE_SOURCE) | |
357 | # include "compat-5.3.c" | |
358 | #endif | |
359 | ||
360 | ||
361 | #endif /* COMPAT53_H_ */ | |
362 |
0 | local lua_version = _VERSION:sub(-3) | |
1 | ||
2 | if lua_version < "5.3" then | |
3 | -- local aliases for commonly used functions | |
4 | local type, select, error = type, select, error | |
5 | ||
6 | -- select the most powerful getmetatable function available | |
7 | local gmt = type(debug) == "table" and debug.getmetatable or | |
8 | getmetatable or function() return false end | |
9 | ||
10 | -- type checking functions | |
11 | local checkinteger -- forward declararation | |
12 | ||
13 | local function argcheck(cond, i, f, extra) | |
14 | if not cond then | |
15 | error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0) | |
16 | end | |
17 | end | |
18 | ||
19 | local function checktype(x, t, i, f) | |
20 | local xt = type(x) | |
21 | if xt ~= t then | |
22 | error("bad argument #"..i.." to '"..f.."' ("..t.. | |
23 | " expected, got "..xt..")", 0) | |
24 | end | |
25 | end | |
26 | ||
27 | ||
28 | -- load utf8 library | |
29 | local utf8_ok, utf8lib = pcall(require, "compat53.utf8") | |
30 | if utf8_ok then | |
31 | utf8 = utf8lib | |
32 | package.loaded["utf8"] = utf8lib | |
33 | if lua_version == "5.1" then | |
34 | utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*" | |
35 | end | |
36 | end | |
37 | ||
38 | ||
39 | -- load table library | |
40 | local table_ok, tablib = pcall(require, "compat53.table") | |
41 | if table_ok then | |
42 | for k,v in pairs(tablib) do | |
43 | table[k] = v | |
44 | end | |
45 | end | |
46 | ||
47 | ||
48 | -- load string packing functions | |
49 | local str_ok, strlib = pcall(require, "compat53.string") | |
50 | if str_ok then | |
51 | for k,v in pairs(strlib) do | |
52 | string[k] = v | |
53 | end | |
54 | end | |
55 | ||
56 | ||
57 | -- try Roberto's struct module for string packing/unpacking if | |
58 | -- compat53.string is unavailable | |
59 | if not str_ok then | |
60 | local struct_ok, struct = pcall(require, "struct") | |
61 | if struct_ok then | |
62 | string.pack = struct.pack | |
63 | string.packsize = struct.size | |
64 | string.unpack = struct.unpack | |
65 | end | |
66 | end | |
67 | ||
68 | ||
69 | -- update math library | |
70 | do | |
71 | local maxint, minint = 1, 0 | |
72 | ||
73 | while maxint+1 > maxint and 2*maxint > maxint do | |
74 | maxint = maxint * 2 | |
75 | end | |
76 | if 2*maxint <= maxint then | |
77 | maxint = 2*maxint-1 | |
78 | minint = -maxint-1 | |
79 | else | |
80 | maxint = maxint | |
81 | minint = -maxint | |
82 | end | |
83 | math.maxinteger = maxint | |
84 | math.mininteger = minint | |
85 | ||
86 | function math.tointeger(n) | |
87 | if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then | |
88 | return n | |
89 | end | |
90 | return nil | |
91 | end | |
92 | ||
93 | function math.type(n) | |
94 | if type(n) == "number" then | |
95 | if n <= maxint and n >= minint and n % 1 == 0 then | |
96 | return "integer" | |
97 | else | |
98 | return "float" | |
99 | end | |
100 | else | |
101 | return nil | |
102 | end | |
103 | end | |
104 | ||
105 | function checkinteger(x, i, f) | |
106 | local t = type(x) | |
107 | if t ~= "number" then | |
108 | error("bad argument #"..i.." to '"..f.. | |
109 | "' (number expected, got "..t..")", 0) | |
110 | elseif x > maxint or x < minint or x % 1 ~= 0 then | |
111 | error("bad argument #"..i.." to '"..f.. | |
112 | "' (number has no integer representation)", 0) | |
113 | else | |
114 | return x | |
115 | end | |
116 | end | |
117 | ||
118 | function math.ult(m, n) | |
119 | m = checkinteger(m, "1", "math.ult") | |
120 | n = checkinteger(n, "2", "math.ult") | |
121 | if m >= 0 and n < 0 then | |
122 | return true | |
123 | elseif m < 0 and n >= 0 then | |
124 | return false | |
125 | else | |
126 | return m < n | |
127 | end | |
128 | end | |
129 | end | |
130 | ||
131 | ||
132 | -- ipairs should respect __index metamethod | |
133 | do | |
134 | local _ipairs = ipairs | |
135 | local function ipairs_iterator(st, var) | |
136 | var = var + 1 | |
137 | local val = st[var] | |
138 | if val ~= nil then | |
139 | return var, st[var] | |
140 | end | |
141 | end | |
142 | function ipairs(t) | |
143 | if gmt(t) ~= nil then -- t has metatable | |
144 | return ipairs_iterator, t, 0 | |
145 | else | |
146 | return _ipairs(t) | |
147 | end | |
148 | end | |
149 | end | |
150 | ||
151 | ||
152 | -- update table library (if C module not available) | |
153 | if not table_ok then | |
154 | local table_concat = table.concat | |
155 | function table.concat(list, sep, i, j) | |
156 | local mt = gmt(list) | |
157 | if type(mt) == "table" and type(mt.__len) == "function" then | |
158 | local src = list | |
159 | list, i, j = {}, i or 1, j or mt.__len(src) | |
160 | for k = i, j do | |
161 | list[k] = src[k] | |
162 | end | |
163 | end | |
164 | return table_concat(list, sep, i, j) | |
165 | end | |
166 | ||
167 | local table_insert = table.insert | |
168 | function table.insert(list, ...) | |
169 | local mt = gmt(list) | |
170 | local has_mt = type(mt) == "table" | |
171 | local has_len = has_mt and type(mt.__len) == "function" | |
172 | if has_mt and (has_len or mt.__index or mt.__newindex) then | |
173 | local e = (has_len and mt.__len(list) or #list)+1 | |
174 | local nargs, pos, value = select('#', ...), ... | |
175 | if nargs == 1 then | |
176 | pos, value = e, pos | |
177 | elseif nargs == 2 then | |
178 | pos = checkinteger(pos, "2", "table.insert") | |
179 | argcheck(1 <= pos and pos <= e, "2", "table.insert", | |
180 | "position out of bounds" ) | |
181 | else | |
182 | error("wrong number of arguments to 'insert'", 0) | |
183 | end | |
184 | for i = e-1, pos, -1 do | |
185 | list[i+1] = list[i] | |
186 | end | |
187 | list[pos] = value | |
188 | else | |
189 | return table_insert(list, ...) | |
190 | end | |
191 | end | |
192 | ||
193 | function table.move(a1, f, e, t, a2) | |
194 | a2 = a2 or a1 | |
195 | f = checkinteger(f, "2", "table.move") | |
196 | argcheck(f > 0, "2", "table.move", | |
197 | "initial position must be positive") | |
198 | e = checkinteger(e, "3", "table.move") | |
199 | t = checkinteger(t, "4", "table.move") | |
200 | if e >= f then | |
201 | local m, n, d = 0, e-f, 1 | |
202 | if t > f then m, n, d = n, m, -1 end | |
203 | for i = m, n, d do | |
204 | a2[t+i] = a1[f+i] | |
205 | end | |
206 | end | |
207 | return a2 | |
208 | end | |
209 | ||
210 | local table_remove = table.remove | |
211 | function table.remove(list, pos) | |
212 | local mt = gmt(list) | |
213 | local has_mt = type(mt) == "table" | |
214 | local has_len = has_mt and type(mt.__len) == "function" | |
215 | if has_mt and (has_len or mt.__index or mt.__newindex) then | |
216 | local e = (has_len and mt.__len(list) or #list) | |
217 | pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e | |
218 | if pos ~= e then | |
219 | argcheck(1 <= pos and pos <= e+1, "2", "table.remove", | |
220 | "position out of bounds" ) | |
221 | end | |
222 | local result = list[pos] | |
223 | while pos < e do | |
224 | list[pos] = list[pos+1] | |
225 | pos = pos + 1 | |
226 | end | |
227 | list[pos] = nil | |
228 | return result | |
229 | else | |
230 | return table_remove(list, pos) | |
231 | end | |
232 | end | |
233 | ||
234 | do | |
235 | local function pivot(list, cmp, a, b) | |
236 | local m = b - a | |
237 | if m > 2 then | |
238 | local c = a + (m-m%2)/2 | |
239 | local x, y, z = list[a], list[b], list[c] | |
240 | if not cmp(x, y) then | |
241 | x, y, a, b = y, x, b, a | |
242 | end | |
243 | if not cmp(y, z) then | |
244 | y, z, b, c = z, y, c, b | |
245 | end | |
246 | if not cmp(x, y) then | |
247 | x, y, a, b = y, x, b, a | |
248 | end | |
249 | return b, y | |
250 | else | |
251 | return b, list[b] | |
252 | end | |
253 | end | |
254 | ||
255 | local function lt_cmp(a, b) | |
256 | return a < b | |
257 | end | |
258 | ||
259 | local function qsort(list, cmp, b, e) | |
260 | if b < e then | |
261 | local i, j, k, val = b, e, pivot(list, cmp, b, e) | |
262 | while i < j do | |
263 | while i < j and cmp(list[i], val) do | |
264 | i = i + 1 | |
265 | end | |
266 | while i < j and not cmp(list[j], val) do | |
267 | j = j - 1 | |
268 | end | |
269 | if i < j then | |
270 | list[i], list[j] = list[j], list[i] | |
271 | if i == k then k = j end -- update pivot position | |
272 | i, j = i+1, j-1 | |
273 | end | |
274 | end | |
275 | if i ~= k and not cmp(list[i], val) then | |
276 | list[i], list[k] = val, list[i] | |
277 | k = i -- update pivot position | |
278 | end | |
279 | qsort(list, cmp, b, i == k and i-1 or i) | |
280 | return qsort(list, cmp, i+1, e) | |
281 | end | |
282 | end | |
283 | ||
284 | local table_sort = table.sort | |
285 | function table.sort(list, cmp) | |
286 | local mt = gmt(list) | |
287 | local has_mt = type(mt) == "table" | |
288 | local has_len = has_mt and type(mt.__len) == "function" | |
289 | if has_len then | |
290 | cmp = cmp or lt_cmp | |
291 | local len = mt.__len(list) | |
292 | return qsort(list, cmp, 1, len) | |
293 | else | |
294 | return table_sort(list, cmp) | |
295 | end | |
296 | end | |
297 | end | |
298 | ||
299 | local table_unpack = lua_version == "5.1" and unpack or table.unpack | |
300 | local function unpack_helper(list, i, j, ...) | |
301 | if j < i then | |
302 | return ... | |
303 | else | |
304 | return unpack_helper(list, i, j-1, list[j], ...) | |
305 | end | |
306 | end | |
307 | function table.unpack(list, i, j) | |
308 | local mt = gmt(list) | |
309 | local has_mt = type(mt) == "table" | |
310 | local has_len = has_mt and type(mt.__len) == "function" | |
311 | if has_mt and (has_len or mt.__index) then | |
312 | i, j = i or 1, j or (has_len and mt.__len(list)) or #list | |
313 | return unpack_helper(list, i, j) | |
314 | else | |
315 | return table_unpack(list, i, j) | |
316 | end | |
317 | end | |
318 | end -- update table library | |
319 | ||
320 | ||
321 | ||
322 | -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2 | |
323 | if lua_version == "5.1" then | |
324 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | |
325 | local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" | |
326 | local is_luajit52 = is_luajit and | |
327 | #setmetatable({}, { __len = function() return 1 end }) == 1 | |
328 | ||
329 | ||
330 | -- table that maps each running coroutine to the coroutine that resumed it | |
331 | -- this is used to build complete tracebacks when "coroutine-friendly" pcall | |
332 | -- is used. | |
333 | local pcall_previous = {} | |
334 | local pcall_callOf = {} | |
335 | local xpcall_running = {} | |
336 | local coroutine_running = coroutine.running | |
337 | ||
338 | -- handle debug functions | |
339 | if type(debug) == "table" then | |
340 | ||
341 | if not is_luajit52 then | |
342 | local _G, package = _G, package | |
343 | local debug_setfenv = debug.setfenv | |
344 | function debug.setuservalue(obj, value) | |
345 | if type(obj) ~= "userdata" then | |
346 | error("bad argument #1 to 'setuservalue' (userdata expected, got ".. | |
347 | type(obj)..")", 2) | |
348 | end | |
349 | if value == nil then value = _G end | |
350 | if type(value) ~= "table" then | |
351 | error("bad argument #2 to 'setuservalue' (table expected, got ".. | |
352 | type(value)..")", 2) | |
353 | end | |
354 | return debug_setfenv(obj, value) | |
355 | end | |
356 | ||
357 | local debug_getfenv = debug.getfenv | |
358 | function debug.getuservalue(obj) | |
359 | if type(obj) ~= "userdata" then | |
360 | return nil | |
361 | else | |
362 | local v = debug_getfenv(obj) | |
363 | if v == _G or v == package then | |
364 | return nil | |
365 | end | |
366 | return v | |
367 | end | |
368 | end | |
369 | ||
370 | local debug_setmetatable = debug.setmetatable | |
371 | function debug.setmetatable(value, tab) | |
372 | debug_setmetatable(value, tab) | |
373 | return value | |
374 | end | |
375 | end -- not luajit with compat52 enabled | |
376 | ||
377 | if not is_luajit then | |
378 | local debug_getinfo = debug.getinfo | |
379 | local function calculate_trace_level(co, level) | |
380 | if level ~= nil then | |
381 | for out = 1, 1/0 do | |
382 | local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "") | |
383 | if info == nil then | |
384 | local max = out-1 | |
385 | if level <= max then | |
386 | return level | |
387 | end | |
388 | return nil, level-max | |
389 | end | |
390 | end | |
391 | end | |
392 | return 1 | |
393 | end | |
394 | ||
395 | local stack_pattern = "\nstack traceback:" | |
396 | local stack_replace = "" | |
397 | local debug_traceback = debug.traceback | |
398 | function debug.traceback(co, msg, level) | |
399 | local lvl | |
400 | local nilmsg | |
401 | if type(co) ~= "thread" then | |
402 | co, msg, level = coroutine_running(), co, msg | |
403 | end | |
404 | if msg == nil then | |
405 | msg = "" | |
406 | nilmsg = true | |
407 | elseif type(msg) ~= "string" then | |
408 | return msg | |
409 | end | |
410 | if co == nil then | |
411 | msg = debug_traceback(msg, level or 1) | |
412 | else | |
413 | local xpco = xpcall_running[co] | |
414 | if xpco ~= nil then | |
415 | lvl, level = calculate_trace_level(xpco, level) | |
416 | if lvl then | |
417 | msg = debug_traceback(xpco, msg, lvl) | |
418 | else | |
419 | msg = msg..stack_pattern | |
420 | end | |
421 | lvl, level = calculate_trace_level(co, level) | |
422 | if lvl then | |
423 | local trace = debug_traceback(co, "", lvl) | |
424 | msg = msg..trace:gsub(stack_pattern, stack_replace) | |
425 | end | |
426 | else | |
427 | co = pcall_callOf[co] or co | |
428 | lvl, level = calculate_trace_level(co, level) | |
429 | if lvl then | |
430 | msg = debug_traceback(co, msg, lvl) | |
431 | else | |
432 | msg = msg..stack_pattern | |
433 | end | |
434 | end | |
435 | co = pcall_previous[co] | |
436 | while co ~= nil do | |
437 | lvl, level = calculate_trace_level(co, level) | |
438 | if lvl then | |
439 | local trace = debug_traceback(co, "", lvl) | |
440 | msg = msg..trace:gsub(stack_pattern, stack_replace) | |
441 | end | |
442 | co = pcall_previous[co] | |
443 | end | |
444 | end | |
445 | if nilmsg then | |
446 | msg = msg:gsub("^\n", "") | |
447 | end | |
448 | msg = msg:gsub("\n\t%(tail call%): %?", "\000") | |
449 | msg = msg:gsub("\n\t%.%.%.\n", "\001\n") | |
450 | msg = msg:gsub("\n\t%.%.%.$", "\001") | |
451 | msg = msg:gsub("(%z+)\001(%z+)", function(some, other) | |
452 | return "\n\t(..."..#some+#other.."+ tail call(s)...)" | |
453 | end) | |
454 | msg = msg:gsub("\001(%z+)", function(zeros) | |
455 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | |
456 | end) | |
457 | msg = msg:gsub("(%z+)\001", function(zeros) | |
458 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | |
459 | end) | |
460 | msg = msg:gsub("%z+", function(zeros) | |
461 | return "\n\t(..."..#zeros.." tail call(s)...)" | |
462 | end) | |
463 | msg = msg:gsub("\001", function(zeros) | |
464 | return "\n\t..." | |
465 | end) | |
466 | return msg | |
467 | end | |
468 | end -- is not luajit | |
469 | end -- debug table available | |
470 | ||
471 | ||
472 | if not is_luajit52 then | |
473 | local _pairs = pairs | |
474 | function pairs(t) | |
475 | local mt = gmt(t) | |
476 | if type(mt) == "table" and type(mt.__pairs) == "function" then | |
477 | return mt.__pairs(t) | |
478 | else | |
479 | return _pairs(t) | |
480 | end | |
481 | end | |
482 | end | |
483 | ||
484 | ||
485 | if not is_luajit then | |
486 | local function check_mode(mode, prefix) | |
487 | local has = { text = false, binary = false } | |
488 | for i = 1,#mode do | |
489 | local c = mode:sub(i, i) | |
490 | if c == "t" then has.text = true end | |
491 | if c == "b" then has.binary = true end | |
492 | end | |
493 | local t = prefix:sub(1, 1) == "\27" and "binary" or "text" | |
494 | if not has[t] then | |
495 | return "attempt to load a "..t.." chunk (mode is '"..mode.."')" | |
496 | end | |
497 | end | |
498 | ||
499 | local setfenv = setfenv | |
500 | local _load, _loadstring = load, loadstring | |
501 | function load(ld, source, mode, env) | |
502 | mode = mode or "bt" | |
503 | local chunk, msg | |
504 | if type( ld ) == "string" then | |
505 | if mode ~= "bt" then | |
506 | local merr = check_mode(mode, ld) | |
507 | if merr then return nil, merr end | |
508 | end | |
509 | chunk, msg = _loadstring(ld, source) | |
510 | else | |
511 | local ld_type = type(ld) | |
512 | if ld_type ~= "function" then | |
513 | error("bad argument #1 to 'load' (function expected, got ".. | |
514 | ld_type..")", 2) | |
515 | end | |
516 | if mode ~= "bt" then | |
517 | local checked, merr = false, nil | |
518 | local function checked_ld() | |
519 | if checked then | |
520 | return ld() | |
521 | else | |
522 | checked = true | |
523 | local v = ld() | |
524 | merr = check_mode(mode, v or "") | |
525 | if merr then return nil end | |
526 | return v | |
527 | end | |
528 | end | |
529 | chunk, msg = _load(checked_ld, source) | |
530 | if merr then return nil, merr end | |
531 | else | |
532 | chunk, msg = _load(ld, source) | |
533 | end | |
534 | end | |
535 | if not chunk then | |
536 | return chunk, msg | |
537 | end | |
538 | if env ~= nil then | |
539 | setfenv(chunk, env) | |
540 | end | |
541 | return chunk | |
542 | end | |
543 | ||
544 | loadstring = load | |
545 | ||
546 | local _loadfile = loadfile | |
547 | local io_open = io.open | |
548 | function loadfile(file, mode, env) | |
549 | mode = mode or "bt" | |
550 | if mode ~= "bt" then | |
551 | local f = io_open(file, "rb") | |
552 | if f then | |
553 | local prefix = f:read(1) | |
554 | f:close() | |
555 | if prefix then | |
556 | local merr = check_mode(mode, prefix) | |
557 | if merr then return nil, merr end | |
558 | end | |
559 | end | |
560 | end | |
561 | local chunk, msg = _loadfile(file) | |
562 | if not chunk then | |
563 | return chunk, msg | |
564 | end | |
565 | if env ~= nil then | |
566 | setfenv(chunk, env) | |
567 | end | |
568 | return chunk | |
569 | end | |
570 | end -- not luajit | |
571 | ||
572 | ||
573 | if not is_luajit52 then | |
574 | function rawlen(v) | |
575 | local t = type(v) | |
576 | if t ~= "string" and t ~= "table" then | |
577 | error("bad argument #1 to 'rawlen' (table or string expected)", 2) | |
578 | end | |
579 | return #v | |
580 | end | |
581 | end | |
582 | ||
583 | ||
584 | if not is_luajit52 then | |
585 | local os_execute = os.execute | |
586 | function os.execute(cmd) | |
587 | local code = os_execute(cmd) | |
588 | -- Lua 5.1 does not report exit by signal. | |
589 | if code == 0 then | |
590 | return true, "exit", code | |
591 | else | |
592 | return nil, "exit", code/256 -- only correct on POSIX! | |
593 | end | |
594 | end | |
595 | end | |
596 | ||
597 | ||
598 | if not table_ok and not is_luajit52 then | |
599 | table.pack = function(...) | |
600 | return { n = select('#', ...), ... } | |
601 | end | |
602 | end | |
603 | ||
604 | ||
605 | local main_coroutine = coroutine.create(function() end) | |
606 | ||
607 | local _pcall = pcall | |
608 | local coroutine_create = coroutine.create | |
609 | function coroutine.create(func) | |
610 | local success, result = _pcall(coroutine_create, func) | |
611 | if not success then | |
612 | if type(func) ~= "function" then | |
613 | error("bad argument #1 (function expected)", 0) | |
614 | end | |
615 | result = coroutine_create(function(...) return func(...) end) | |
616 | end | |
617 | return result | |
618 | end | |
619 | ||
620 | local pcall_mainOf = {} | |
621 | ||
622 | if not is_luajit52 then | |
623 | function coroutine.running() | |
624 | local co = coroutine_running() | |
625 | if co then | |
626 | return pcall_mainOf[co] or co, false | |
627 | else | |
628 | return main_coroutine, true | |
629 | end | |
630 | end | |
631 | end | |
632 | ||
633 | local coroutine_yield = coroutine.yield | |
634 | function coroutine.yield(...) | |
635 | local co, flag = coroutine_running() | |
636 | if co and not flag then | |
637 | return coroutine_yield(...) | |
638 | else | |
639 | error("attempt to yield from outside a coroutine", 0) | |
640 | end | |
641 | end | |
642 | ||
643 | if not is_luajit then | |
644 | local coroutine_resume = coroutine.resume | |
645 | function coroutine.resume(co, ...) | |
646 | if co == main_coroutine then | |
647 | return false, "cannot resume non-suspended coroutine" | |
648 | else | |
649 | return coroutine_resume(co, ...) | |
650 | end | |
651 | end | |
652 | ||
653 | local coroutine_status = coroutine.status | |
654 | function coroutine.status(co) | |
655 | local notmain = coroutine_running() | |
656 | if co == main_coroutine then | |
657 | return notmain and "normal" or "running" | |
658 | else | |
659 | return coroutine_status(co) | |
660 | end | |
661 | end | |
662 | ||
663 | local function pcall_results(current, call, success, ...) | |
664 | if coroutine_status(call) == "suspended" then | |
665 | return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...))) | |
666 | end | |
667 | if pcall_previous then | |
668 | pcall_previous[call] = nil | |
669 | local main = pcall_mainOf[call] | |
670 | if main == current then current = nil end | |
671 | pcall_callOf[main] = current | |
672 | end | |
673 | pcall_mainOf[call] = nil | |
674 | return success, ... | |
675 | end | |
676 | local function pcall_exec(current, call, ...) | |
677 | local main = pcall_mainOf[current] or current | |
678 | pcall_mainOf[call] = main | |
679 | if pcall_previous then | |
680 | pcall_previous[call] = current | |
681 | pcall_callOf[main] = call | |
682 | end | |
683 | return pcall_results(current, call, coroutine_resume(call, ...)) | |
684 | end | |
685 | local coroutine_create52 = coroutine.create | |
686 | local function pcall_coroutine(func) | |
687 | if type(func) ~= "function" then | |
688 | local callable = func | |
689 | func = function (...) return callable(...) end | |
690 | end | |
691 | return coroutine_create52(func) | |
692 | end | |
693 | function pcall(func, ...) | |
694 | local current = coroutine_running() | |
695 | if not current then return _pcall(func, ...) end | |
696 | return pcall_exec(current, pcall_coroutine(func), ...) | |
697 | end | |
698 | ||
699 | local _tostring = tostring | |
700 | local function xpcall_catch(current, call, msgh, success, ...) | |
701 | if not success then | |
702 | xpcall_running[current] = call | |
703 | local ok, result = _pcall(msgh, ...) | |
704 | xpcall_running[current] = nil | |
705 | if not ok then | |
706 | return false, "error in error handling (".._tostring(result)..")" | |
707 | end | |
708 | return false, result | |
709 | end | |
710 | return true, ... | |
711 | end | |
712 | local _xpcall = xpcall | |
713 | local _unpack = unpack | |
714 | function xpcall(f, msgh, ...) | |
715 | local current = coroutine_running() | |
716 | if not current then | |
717 | local args, n = { ... }, select('#', ...) | |
718 | return _xpcall(function() return f(_unpack(args, 1, n)) end, msgh) | |
719 | end | |
720 | local call = pcall_coroutine(f) | |
721 | return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...)) | |
722 | end | |
723 | end -- not luajit | |
724 | ||
725 | ||
726 | if not is_luajit then | |
727 | local math_log = math.log | |
728 | math.log = function(x, base) | |
729 | if base ~= nil then | |
730 | return math_log(x)/math_log(base) | |
731 | else | |
732 | return math_log(x) | |
733 | end | |
734 | end | |
735 | end | |
736 | ||
737 | ||
738 | local package = package | |
739 | if not is_luajit then | |
740 | local io_open = io.open | |
741 | local table_concat = table.concat | |
742 | function package.searchpath(name, path, sep, rep) | |
743 | sep = (sep or "."):gsub("(%p)", "%%%1") | |
744 | rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1") | |
745 | local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1") | |
746 | local msg = {} | |
747 | for subpath in path:gmatch("[^;]+") do | |
748 | local fpath = subpath:gsub("%?", pname) | |
749 | local f = io_open(fpath, "r") | |
750 | if f then | |
751 | f:close() | |
752 | return fpath | |
753 | end | |
754 | msg[#msg+1] = "\n\tno file '" .. fpath .. "'" | |
755 | end | |
756 | return nil, table_concat(msg) | |
757 | end | |
758 | end | |
759 | ||
760 | local p_index = { searchers = package.loaders } | |
761 | local rawset = rawset | |
762 | setmetatable(package, { | |
763 | __index = p_index, | |
764 | __newindex = function(p, k, v) | |
765 | if k == "searchers" then | |
766 | rawset(p, "loaders", v) | |
767 | p_index.searchers = v | |
768 | else | |
769 | rawset(p, k, v) | |
770 | end | |
771 | end | |
772 | }) | |
773 | ||
774 | ||
775 | local string_gsub = string.gsub | |
776 | local function fix_pattern(pattern) | |
777 | return (string_gsub(pattern, "%z", "%%z")) | |
778 | end | |
779 | ||
780 | local string_find = string.find | |
781 | function string.find(s, pattern, ...) | |
782 | return string_find(s, fix_pattern(pattern), ...) | |
783 | end | |
784 | ||
785 | local string_gmatch = string.gmatch | |
786 | function string.gmatch(s, pattern) | |
787 | return string_gmatch(s, fix_pattern(pattern)) | |
788 | end | |
789 | ||
790 | function string.gsub(s, pattern, ...) | |
791 | return string_gsub(s, fix_pattern(pattern), ...) | |
792 | end | |
793 | ||
794 | local string_match = string.match | |
795 | function string.match(s, pattern, ...) | |
796 | return string_match(s, fix_pattern(pattern), ...) | |
797 | end | |
798 | ||
799 | if not is_luajit then | |
800 | local string_rep = string.rep | |
801 | function string.rep(s, n, sep) | |
802 | if sep ~= nil and sep ~= "" and n >= 2 then | |
803 | return s .. string_rep(sep..s, n-1) | |
804 | else | |
805 | return string_rep(s, n) | |
806 | end | |
807 | end | |
808 | end | |
809 | ||
810 | if not is_luajit then | |
811 | local string_format = string.format | |
812 | do | |
813 | local addqt = { | |
814 | ["\n"] = "\\\n", | |
815 | ["\\"] = "\\\\", | |
816 | ["\""] = "\\\"" | |
817 | } | |
818 | ||
819 | local function addquoted(c) | |
820 | return addqt[c] or string_format("\\%03d", c:byte()) | |
821 | end | |
822 | ||
823 | local _unpack = unpack | |
824 | function string.format(fmt, ...) | |
825 | local args, n = { ... }, select('#', ...) | |
826 | local i = 0 | |
827 | local function adjust_fmt(lead, mods, kind) | |
828 | if #lead % 2 == 0 then | |
829 | i = i + 1 | |
830 | if kind == "s" then | |
831 | args[i] = tostring(args[i]) | |
832 | elseif kind == "q" then | |
833 | args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"' | |
834 | return lead.."%"..mods.."s" | |
835 | end | |
836 | end | |
837 | end | |
838 | fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt) | |
839 | return string_format(fmt, _unpack(args, 1, n)) | |
840 | end | |
841 | end | |
842 | end | |
843 | ||
844 | ||
845 | local io_open = io.open | |
846 | local io_write = io.write | |
847 | local io_output = io.output | |
848 | function io.write(...) | |
849 | local res, msg, errno = io_write(...) | |
850 | if res then | |
851 | return io_output() | |
852 | else | |
853 | return nil, msg, errno | |
854 | end | |
855 | end | |
856 | ||
857 | if not is_luajit then | |
858 | local lines_iterator | |
859 | do | |
860 | local function helper( st, var_1, ... ) | |
861 | if var_1 == nil then | |
862 | if st.doclose then st.f:close() end | |
863 | if (...) ~= nil then | |
864 | error((...), 2) | |
865 | end | |
866 | end | |
867 | return var_1, ... | |
868 | end | |
869 | ||
870 | local _unpack = unpack | |
871 | function lines_iterator(st) | |
872 | return helper(st, st.f:read(_unpack(st, 1, st.n))) | |
873 | end | |
874 | end | |
875 | ||
876 | local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true } | |
877 | ||
878 | local io_input = io.input | |
879 | function io.lines(fname, ...) | |
880 | local doclose, file, msg | |
881 | if fname ~= nil then | |
882 | doclose, file, msg = true, io_open(fname, "r") | |
883 | if not file then error(msg, 2) end | |
884 | else | |
885 | doclose, file = false, io_input() | |
886 | end | |
887 | local st = { f=file, doclose=doclose, n=select('#', ...), ... } | |
888 | for i = 1, st.n do | |
889 | if type(st[i]) ~= "number" and not valid_format[st[i]] then | |
890 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | |
891 | end | |
892 | end | |
893 | return lines_iterator, st | |
894 | end | |
895 | ||
896 | do | |
897 | local io_stdout = io.stdout | |
898 | local io_type = io.type | |
899 | local file_meta = gmt(io_stdout) | |
900 | if type(file_meta) == "table" and type(file_meta.__index) == "table" then | |
901 | local file_write = file_meta.__index.write | |
902 | file_meta.__index.write = function(self, ...) | |
903 | local res, msg, errno = file_write(self, ...) | |
904 | if res then | |
905 | return self | |
906 | else | |
907 | return nil, msg, errno | |
908 | end | |
909 | end | |
910 | ||
911 | file_meta.__index.lines = function(self, ...) | |
912 | if io_type(self) == "closed file" then | |
913 | error("attempt to use a closed file", 2) | |
914 | end | |
915 | local st = { f=self, doclose=false, n=select('#', ...), ... } | |
916 | for i = 1, st.n do | |
917 | if type(st[i]) ~= "number" and not valid_format[st[i]] then | |
918 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | |
919 | end | |
920 | end | |
921 | return lines_iterator, st | |
922 | end | |
923 | end | |
924 | end | |
925 | end -- not luajit | |
926 | ||
927 | ||
928 | end -- lua 5.1 | |
929 | ||
930 | end -- lua < 5.3 | |
931 | ||
932 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : |
0 | /* | |
1 | ** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $ | |
2 | ** Definitions for Lua code that must come before any other header file | |
3 | ** See Copyright Notice in lua.h | |
4 | */ | |
5 | ||
6 | #ifndef lprefix_h | |
7 | #define lprefix_h | |
8 | ||
9 | ||
10 | /* | |
11 | ** Allows POSIX/XSI stuff | |
12 | */ | |
13 | #if !defined(LUA_USE_C89) /* { */ | |
14 | ||
15 | #if !defined(_XOPEN_SOURCE) | |
16 | #define _XOPEN_SOURCE 600 | |
17 | #elif _XOPEN_SOURCE == 0 | |
18 | #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ | |
19 | #endif | |
20 | ||
21 | /* | |
22 | ** Allows manipulation of large files in gcc and some other compilers | |
23 | */ | |
24 | #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) | |
25 | #define _LARGEFILE_SOURCE 1 | |
26 | #define _FILE_OFFSET_BITS 64 | |
27 | #endif | |
28 | ||
29 | #endif /* } */ | |
30 | ||
31 | ||
32 | /* | |
33 | ** Windows stuff | |
34 | */ | |
35 | #if defined(_WIN32) /* { */ | |
36 | ||
37 | #if !defined(_CRT_SECURE_NO_WARNINGS) | |
38 | #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ | |
39 | #endif | |
40 | ||
41 | #endif /* } */ | |
42 | ||
43 | ||
44 | /* COMPAT53 adaptation */ | |
45 | #include "c-api/compat-5.3.h" | |
46 | ||
47 | #undef LUAMOD_API | |
48 | #define LUAMOD_API extern | |
49 | ||
50 | #ifdef lutf8lib_c | |
51 | # define luaopen_utf8 luaopen_compat53_utf8 | |
52 | # include <stdarg.h> | |
53 | /* we don't support the %U format string of lua_pushfstring! | |
54 | * code below adapted from the Lua 5.3 sources: | |
55 | */ | |
56 | static const char *compat53_utf8_escape (lua_State* L, ...) { | |
57 | long x = 0; | |
58 | va_list argp; | |
59 | va_start(argp, L); | |
60 | x = (long)va_arg(argp, long); | |
61 | va_end(argp); | |
62 | if (x < 0x80) { /* ASCII */ | |
63 | char c = (char)x; | |
64 | lua_pushlstring(L, &c, 1); | |
65 | } else { | |
66 | char buff[8] = { 0 }; | |
67 | unsigned int mfb = 0x3f; | |
68 | int n = 1; | |
69 | do { | |
70 | buff[8 - (n++)] = (char)(0x80|(x & 0x3f)); | |
71 | x >>= 6; | |
72 | mfb >>= 1; | |
73 | } while (x > mfb); | |
74 | buff[8-n] = (char)((~mfb << 1) | x); | |
75 | lua_pushlstring(L, buff+8-n, n); | |
76 | } | |
77 | return lua_tostring(L, -1); | |
78 | } | |
79 | # define lua_pushfstring(L, fmt, l) \ | |
80 | compat53_utf8_escape(L, l) | |
81 | #endif | |
82 | ||
83 | #ifdef ltablib_c | |
84 | # define luaopen_table luaopen_compat53_table | |
85 | /* lua_rawgeti in compat53.h is implemented as a macro, so the | |
86 | * function signature doesn't match when you use a function pointer | |
87 | */ | |
88 | static int compat53_rawgeti (lua_State *L, int i, lua_Integer n) { | |
89 | return lua_rawgeti(L, i, n); | |
90 | } | |
91 | # undef lua_rawgeti | |
92 | # define lua_rawgeti compat53_rawgeti | |
93 | static void compat53_rawseti (lua_State *L, int i, lua_Integer n) { | |
94 | lua_rawseti(L, i, (int)n); | |
95 | } | |
96 | # undef lua_rawseti | |
97 | # define lua_rawseti compat53_rawseti | |
98 | #endif /* ltablib_c */ | |
99 | ||
100 | #ifdef lstrlib_c | |
101 | /* move the string library open function out of the way (we only take | |
102 | * the string packing functions)! | |
103 | */ | |
104 | # define luaopen_string luaopen_string_XXX | |
105 | /* used in string.format implementation, which we don't use: */ | |
106 | # ifndef LUA_INTEGER_FRMLEN | |
107 | # define LUA_INTEGER_FRMLEN "" | |
108 | # define LUA_NUMBER_FRMLEN "" | |
109 | # endif | |
110 | # if LUA_VERSION_NUM < 503 | |
111 | /* lstrlib assumes that lua_Integer and lua_Unsigned have the same | |
112 | * size, so we use the unsigned equivalent of ptrdiff_t! */ | |
113 | # define lua_Unsigned size_t | |
114 | # endif | |
115 | static int str_pack (lua_State *L); | |
116 | static int str_packsize (lua_State *L); | |
117 | static int str_unpack (lua_State *L); | |
118 | LUAMOD_API int luaopen_compat53_string (lua_State *L) { | |
119 | luaL_Reg const funcs[] = { | |
120 | { "pack", str_pack }, | |
121 | { "packsize", str_packsize }, | |
122 | { "unpack", str_unpack }, | |
123 | { NULL, NULL } | |
124 | }; | |
125 | luaL_newlib(L, funcs); | |
126 | return 1; | |
127 | } | |
128 | /* make luaopen_string(_XXX) static, so it (and all other referenced | |
129 | * string functions) won't be included in the resulting dll | |
130 | * (hopefully). | |
131 | */ | |
132 | # undef LUAMOD_API | |
133 | # define LUAMOD_API static | |
134 | #endif /* lstrlib.c */ | |
135 | ||
136 | #endif | |
137 |
0 | /* | |
1 | ** $Id: lstrlib.c,v 1.221 2014/12/11 14:03:07 roberto Exp $ | |
2 | ** Standard library for string operations and pattern-matching | |
3 | ** See Copyright Notice in lua.h | |
4 | */ | |
5 | ||
6 | #define lstrlib_c | |
7 | #define LUA_LIB | |
8 | ||
9 | #include "lprefix.h" | |
10 | ||
11 | ||
12 | #include <ctype.h> | |
13 | #include <limits.h> | |
14 | #include <stddef.h> | |
15 | #include <stdio.h> | |
16 | #include <stdlib.h> | |
17 | #include <string.h> | |
18 | ||
19 | #include "lua.h" | |
20 | ||
21 | #include "lauxlib.h" | |
22 | #include "lualib.h" | |
23 | ||
24 | ||
25 | /* | |
26 | ** maximum number of captures that a pattern can do during | |
27 | ** pattern-matching. This limit is arbitrary. | |
28 | */ | |
29 | #if !defined(LUA_MAXCAPTURES) | |
30 | #define LUA_MAXCAPTURES 32 | |
31 | #endif | |
32 | ||
33 | ||
34 | /* macro to 'unsign' a character */ | |
35 | #define uchar(c) ((unsigned char)(c)) | |
36 | ||
37 | ||
38 | /* | |
39 | ** Some sizes are better limited to fit in 'int', but must also fit in | |
40 | ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) | |
41 | */ | |
42 | #define MAXSIZE \ | |
43 | (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) | |
44 | ||
45 | ||
46 | ||
47 | ||
48 | static int str_len (lua_State *L) { | |
49 | size_t l; | |
50 | luaL_checklstring(L, 1, &l); | |
51 | lua_pushinteger(L, (lua_Integer)l); | |
52 | return 1; | |
53 | } | |
54 | ||
55 | ||
56 | /* translate a relative string position: negative means back from end */ | |
57 | static lua_Integer posrelat (lua_Integer pos, size_t len) { | |
58 | if (pos >= 0) return pos; | |
59 | else if (0u - (size_t)pos > len) return 0; | |
60 | else return (lua_Integer)len + pos + 1; | |
61 | } | |
62 | ||
63 | ||
64 | static int str_sub (lua_State *L) { | |
65 | size_t l; | |
66 | const char *s = luaL_checklstring(L, 1, &l); | |
67 | lua_Integer start = posrelat(luaL_checkinteger(L, 2), l); | |
68 | lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l); | |
69 | if (start < 1) start = 1; | |
70 | if (end > (lua_Integer)l) end = l; | |
71 | if (start <= end) | |
72 | lua_pushlstring(L, s + start - 1, (size_t)(end - start + 1)); | |
73 | else lua_pushliteral(L, ""); | |
74 | return 1; | |
75 | } | |
76 | ||
77 | ||
78 | static int str_reverse (lua_State *L) { | |
79 | size_t l, i; | |
80 | luaL_Buffer b; | |
81 | const char *s = luaL_checklstring(L, 1, &l); | |
82 | char *p = luaL_buffinitsize(L, &b, l); | |
83 | for (i = 0; i < l; i++) | |
84 | p[i] = s[l - i - 1]; | |
85 | luaL_pushresultsize(&b, l); | |
86 | return 1; | |
87 | } | |
88 | ||
89 | ||
90 | static int str_lower (lua_State *L) { | |
91 | size_t l; | |
92 | size_t i; | |
93 | luaL_Buffer b; | |
94 | const char *s = luaL_checklstring(L, 1, &l); | |
95 | char *p = luaL_buffinitsize(L, &b, l); | |
96 | for (i=0; i<l; i++) | |
97 | p[i] = tolower(uchar(s[i])); | |
98 | luaL_pushresultsize(&b, l); | |
99 | return 1; | |
100 | } | |
101 | ||
102 | ||
103 | static int str_upper (lua_State *L) { | |
104 | size_t l; | |
105 | size_t i; | |
106 | luaL_Buffer b; | |
107 | const char *s = luaL_checklstring(L, 1, &l); | |
108 | char *p = luaL_buffinitsize(L, &b, l); | |
109 | for (i=0; i<l; i++) | |
110 | p[i] = toupper(uchar(s[i])); | |
111 | luaL_pushresultsize(&b, l); | |
112 | return 1; | |
113 | } | |
114 | ||
115 | ||
116 | static int str_rep (lua_State *L) { | |
117 | size_t l, lsep; | |
118 | const char *s = luaL_checklstring(L, 1, &l); | |
119 | lua_Integer n = luaL_checkinteger(L, 2); | |
120 | const char *sep = luaL_optlstring(L, 3, "", &lsep); | |
121 | if (n <= 0) lua_pushliteral(L, ""); | |
122 | else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */ | |
123 | return luaL_error(L, "resulting string too large"); | |
124 | else { | |
125 | size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; | |
126 | luaL_Buffer b; | |
127 | char *p = luaL_buffinitsize(L, &b, totallen); | |
128 | while (n-- > 1) { /* first n-1 copies (followed by separator) */ | |
129 | memcpy(p, s, l * sizeof(char)); p += l; | |
130 | if (lsep > 0) { /* empty 'memcpy' is not that cheap */ | |
131 | memcpy(p, sep, lsep * sizeof(char)); | |
132 | p += lsep; | |
133 | } | |
134 | } | |
135 | memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ | |
136 | luaL_pushresultsize(&b, totallen); | |
137 | } | |
138 | return 1; | |
139 | } | |
140 | ||
141 | ||
142 | static int str_byte (lua_State *L) { | |
143 | size_t l; | |
144 | const char *s = luaL_checklstring(L, 1, &l); | |
145 | lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l); | |
146 | lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l); | |
147 | int n, i; | |
148 | if (posi < 1) posi = 1; | |
149 | if (pose > (lua_Integer)l) pose = l; | |
150 | if (posi > pose) return 0; /* empty interval; return no values */ | |
151 | n = (int)(pose - posi + 1); | |
152 | if (posi + n <= pose) /* arithmetic overflow? */ | |
153 | return luaL_error(L, "string slice too long"); | |
154 | luaL_checkstack(L, n, "string slice too long"); | |
155 | for (i=0; i<n; i++) | |
156 | lua_pushinteger(L, uchar(s[posi+i-1])); | |
157 | return n; | |
158 | } | |
159 | ||
160 | ||
161 | static int str_char (lua_State *L) { | |
162 | int n = lua_gettop(L); /* number of arguments */ | |
163 | int i; | |
164 | luaL_Buffer b; | |
165 | char *p = luaL_buffinitsize(L, &b, n); | |
166 | for (i=1; i<=n; i++) { | |
167 | lua_Integer c = luaL_checkinteger(L, i); | |
168 | luaL_argcheck(L, uchar(c) == c, i, "value out of range"); | |
169 | p[i - 1] = uchar(c); | |
170 | } | |
171 | luaL_pushresultsize(&b, n); | |
172 | return 1; | |
173 | } | |
174 | ||
175 | ||
176 | static int writer (lua_State *L, const void *b, size_t size, void *B) { | |
177 | (void)L; | |
178 | luaL_addlstring((luaL_Buffer *) B, (const char *)b, size); | |
179 | return 0; | |
180 | } | |
181 | ||
182 | ||
183 | static int str_dump (lua_State *L) { | |
184 | luaL_Buffer b; | |
185 | int strip = lua_toboolean(L, 2); | |
186 | luaL_checktype(L, 1, LUA_TFUNCTION); | |
187 | lua_settop(L, 1); | |
188 | luaL_buffinit(L,&b); | |
189 | if (lua_dump(L, writer, &b, strip) != 0) | |
190 | return luaL_error(L, "unable to dump given function"); | |
191 | luaL_pushresult(&b); | |
192 | return 1; | |
193 | } | |
194 | ||
195 | ||
196 | ||
197 | /* | |
198 | ** {====================================================== | |
199 | ** PATTERN MATCHING | |
200 | ** ======================================================= | |
201 | */ | |
202 | ||
203 | ||
204 | #define CAP_UNFINISHED (-1) | |
205 | #define CAP_POSITION (-2) | |
206 | ||
207 | ||
208 | typedef struct MatchState { | |
209 | int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ | |
210 | const char *src_init; /* init of source string */ | |
211 | const char *src_end; /* end ('\0') of source string */ | |
212 | const char *p_end; /* end ('\0') of pattern */ | |
213 | lua_State *L; | |
214 | int level; /* total number of captures (finished or unfinished) */ | |
215 | struct { | |
216 | const char *init; | |
217 | ptrdiff_t len; | |
218 | } capture[LUA_MAXCAPTURES]; | |
219 | } MatchState; | |
220 | ||
221 | ||
222 | /* recursive function */ | |
223 | static const char *match (MatchState *ms, const char *s, const char *p); | |
224 | ||
225 | ||
226 | /* maximum recursion depth for 'match' */ | |
227 | #if !defined(MAXCCALLS) | |
228 | #define MAXCCALLS 200 | |
229 | #endif | |
230 | ||
231 | ||
232 | #define L_ESC '%' | |
233 | #define SPECIALS "^$*+?.([%-" | |
234 | ||
235 | ||
236 | static int check_capture (MatchState *ms, int l) { | |
237 | l -= '1'; | |
238 | if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) | |
239 | return luaL_error(ms->L, "invalid capture index %%%d", l + 1); | |
240 | return l; | |
241 | } | |
242 | ||
243 | ||
244 | static int capture_to_close (MatchState *ms) { | |
245 | int level = ms->level; | |
246 | for (level--; level>=0; level--) | |
247 | if (ms->capture[level].len == CAP_UNFINISHED) return level; | |
248 | return luaL_error(ms->L, "invalid pattern capture"); | |
249 | } | |
250 | ||
251 | ||
252 | static const char *classend (MatchState *ms, const char *p) { | |
253 | switch (*p++) { | |
254 | case L_ESC: { | |
255 | if (p == ms->p_end) | |
256 | luaL_error(ms->L, "malformed pattern (ends with '%%')"); | |
257 | return p+1; | |
258 | } | |
259 | case '[': { | |
260 | if (*p == '^') p++; | |
261 | do { /* look for a ']' */ | |
262 | if (p == ms->p_end) | |
263 | luaL_error(ms->L, "malformed pattern (missing ']')"); | |
264 | if (*(p++) == L_ESC && p < ms->p_end) | |
265 | p++; /* skip escapes (e.g. '%]') */ | |
266 | } while (*p != ']'); | |
267 | return p+1; | |
268 | } | |
269 | default: { | |
270 | return p; | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | ||
276 | static int match_class (int c, int cl) { | |
277 | int res; | |
278 | switch (tolower(cl)) { | |
279 | case 'a' : res = isalpha(c); break; | |
280 | case 'c' : res = iscntrl(c); break; | |
281 | case 'd' : res = isdigit(c); break; | |
282 | case 'g' : res = isgraph(c); break; | |
283 | case 'l' : res = islower(c); break; | |
284 | case 'p' : res = ispunct(c); break; | |
285 | case 's' : res = isspace(c); break; | |
286 | case 'u' : res = isupper(c); break; | |
287 | case 'w' : res = isalnum(c); break; | |
288 | case 'x' : res = isxdigit(c); break; | |
289 | case 'z' : res = (c == 0); break; /* deprecated option */ | |
290 | default: return (cl == c); | |
291 | } | |
292 | return (islower(cl) ? res : !res); | |
293 | } | |
294 | ||
295 | ||
296 | static int matchbracketclass (int c, const char *p, const char *ec) { | |
297 | int sig = 1; | |
298 | if (*(p+1) == '^') { | |
299 | sig = 0; | |
300 | p++; /* skip the '^' */ | |
301 | } | |
302 | while (++p < ec) { | |
303 | if (*p == L_ESC) { | |
304 | p++; | |
305 | if (match_class(c, uchar(*p))) | |
306 | return sig; | |
307 | } | |
308 | else if ((*(p+1) == '-') && (p+2 < ec)) { | |
309 | p+=2; | |
310 | if (uchar(*(p-2)) <= c && c <= uchar(*p)) | |
311 | return sig; | |
312 | } | |
313 | else if (uchar(*p) == c) return sig; | |
314 | } | |
315 | return !sig; | |
316 | } | |
317 | ||
318 | ||
319 | static int singlematch (MatchState *ms, const char *s, const char *p, | |
320 | const char *ep) { | |
321 | if (s >= ms->src_end) | |
322 | return 0; | |
323 | else { | |
324 | int c = uchar(*s); | |
325 | switch (*p) { | |
326 | case '.': return 1; /* matches any char */ | |
327 | case L_ESC: return match_class(c, uchar(*(p+1))); | |
328 | case '[': return matchbracketclass(c, p, ep-1); | |
329 | default: return (uchar(*p) == c); | |
330 | } | |
331 | } | |
332 | } | |
333 | ||
334 | ||
335 | static const char *matchbalance (MatchState *ms, const char *s, | |
336 | const char *p) { | |
337 | if (p >= ms->p_end - 1) | |
338 | luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); | |
339 | if (*s != *p) return NULL; | |
340 | else { | |
341 | int b = *p; | |
342 | int e = *(p+1); | |
343 | int cont = 1; | |
344 | while (++s < ms->src_end) { | |
345 | if (*s == e) { | |
346 | if (--cont == 0) return s+1; | |
347 | } | |
348 | else if (*s == b) cont++; | |
349 | } | |
350 | } | |
351 | return NULL; /* string ends out of balance */ | |
352 | } | |
353 | ||
354 | ||
355 | static const char *max_expand (MatchState *ms, const char *s, | |
356 | const char *p, const char *ep) { | |
357 | ptrdiff_t i = 0; /* counts maximum expand for item */ | |
358 | while (singlematch(ms, s + i, p, ep)) | |
359 | i++; | |
360 | /* keeps trying to match with the maximum repetitions */ | |
361 | while (i>=0) { | |
362 | const char *res = match(ms, (s+i), ep+1); | |
363 | if (res) return res; | |
364 | i--; /* else didn't match; reduce 1 repetition to try again */ | |
365 | } | |
366 | return NULL; | |
367 | } | |
368 | ||
369 | ||
370 | static const char *min_expand (MatchState *ms, const char *s, | |
371 | const char *p, const char *ep) { | |
372 | for (;;) { | |
373 | const char *res = match(ms, s, ep+1); | |
374 | if (res != NULL) | |
375 | return res; | |
376 | else if (singlematch(ms, s, p, ep)) | |
377 | s++; /* try with one more repetition */ | |
378 | else return NULL; | |
379 | } | |
380 | } | |
381 | ||
382 | ||
383 | static const char *start_capture (MatchState *ms, const char *s, | |
384 | const char *p, int what) { | |
385 | const char *res; | |
386 | int level = ms->level; | |
387 | if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); | |
388 | ms->capture[level].init = s; | |
389 | ms->capture[level].len = what; | |
390 | ms->level = level+1; | |
391 | if ((res=match(ms, s, p)) == NULL) /* match failed? */ | |
392 | ms->level--; /* undo capture */ | |
393 | return res; | |
394 | } | |
395 | ||
396 | ||
397 | static const char *end_capture (MatchState *ms, const char *s, | |
398 | const char *p) { | |
399 | int l = capture_to_close(ms); | |
400 | const char *res; | |
401 | ms->capture[l].len = s - ms->capture[l].init; /* close capture */ | |
402 | if ((res = match(ms, s, p)) == NULL) /* match failed? */ | |
403 | ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ | |
404 | return res; | |
405 | } | |
406 | ||
407 | ||
408 | static const char *match_capture (MatchState *ms, const char *s, int l) { | |
409 | size_t len; | |
410 | l = check_capture(ms, l); | |
411 | len = ms->capture[l].len; | |
412 | if ((size_t)(ms->src_end-s) >= len && | |
413 | memcmp(ms->capture[l].init, s, len) == 0) | |
414 | return s+len; | |
415 | else return NULL; | |
416 | } | |
417 | ||
418 | ||
419 | static const char *match (MatchState *ms, const char *s, const char *p) { | |
420 | if (ms->matchdepth-- == 0) | |
421 | luaL_error(ms->L, "pattern too complex"); | |
422 | init: /* using goto's to optimize tail recursion */ | |
423 | if (p != ms->p_end) { /* end of pattern? */ | |
424 | switch (*p) { | |
425 | case '(': { /* start capture */ | |
426 | if (*(p + 1) == ')') /* position capture? */ | |
427 | s = start_capture(ms, s, p + 2, CAP_POSITION); | |
428 | else | |
429 | s = start_capture(ms, s, p + 1, CAP_UNFINISHED); | |
430 | break; | |
431 | } | |
432 | case ')': { /* end capture */ | |
433 | s = end_capture(ms, s, p + 1); | |
434 | break; | |
435 | } | |
436 | case '$': { | |
437 | if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ | |
438 | goto dflt; /* no; go to default */ | |
439 | s = (s == ms->src_end) ? s : NULL; /* check end of string */ | |
440 | break; | |
441 | } | |
442 | case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ | |
443 | switch (*(p + 1)) { | |
444 | case 'b': { /* balanced string? */ | |
445 | s = matchbalance(ms, s, p + 2); | |
446 | if (s != NULL) { | |
447 | p += 4; goto init; /* return match(ms, s, p + 4); */ | |
448 | } /* else fail (s == NULL) */ | |
449 | break; | |
450 | } | |
451 | case 'f': { /* frontier? */ | |
452 | const char *ep; char previous; | |
453 | p += 2; | |
454 | if (*p != '[') | |
455 | luaL_error(ms->L, "missing '[' after '%%f' in pattern"); | |
456 | ep = classend(ms, p); /* points to what is next */ | |
457 | previous = (s == ms->src_init) ? '\0' : *(s - 1); | |
458 | if (!matchbracketclass(uchar(previous), p, ep - 1) && | |
459 | matchbracketclass(uchar(*s), p, ep - 1)) { | |
460 | p = ep; goto init; /* return match(ms, s, ep); */ | |
461 | } | |
462 | s = NULL; /* match failed */ | |
463 | break; | |
464 | } | |
465 | case '0': case '1': case '2': case '3': | |
466 | case '4': case '5': case '6': case '7': | |
467 | case '8': case '9': { /* capture results (%0-%9)? */ | |
468 | s = match_capture(ms, s, uchar(*(p + 1))); | |
469 | if (s != NULL) { | |
470 | p += 2; goto init; /* return match(ms, s, p + 2) */ | |
471 | } | |
472 | break; | |
473 | } | |
474 | default: goto dflt; | |
475 | } | |
476 | break; | |
477 | } | |
478 | default: dflt: { /* pattern class plus optional suffix */ | |
479 | const char *ep = classend(ms, p); /* points to optional suffix */ | |
480 | /* does not match at least once? */ | |
481 | if (!singlematch(ms, s, p, ep)) { | |
482 | if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ | |
483 | p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ | |
484 | } | |
485 | else /* '+' or no suffix */ | |
486 | s = NULL; /* fail */ | |
487 | } | |
488 | else { /* matched once */ | |
489 | switch (*ep) { /* handle optional suffix */ | |
490 | case '?': { /* optional */ | |
491 | const char *res; | |
492 | if ((res = match(ms, s + 1, ep + 1)) != NULL) | |
493 | s = res; | |
494 | else { | |
495 | p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ | |
496 | } | |
497 | break; | |
498 | } | |
499 | case '+': /* 1 or more repetitions */ | |
500 | s++; /* 1 match already done */ | |
501 | /* go through */ | |
502 | case '*': /* 0 or more repetitions */ | |
503 | s = max_expand(ms, s, p, ep); | |
504 | break; | |
505 | case '-': /* 0 or more repetitions (minimum) */ | |
506 | s = min_expand(ms, s, p, ep); | |
507 | break; | |
508 | default: /* no suffix */ | |
509 | s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ | |
510 | } | |
511 | } | |
512 | break; | |
513 | } | |
514 | } | |
515 | } | |
516 | ms->matchdepth++; | |
517 | return s; | |
518 | } | |
519 | ||
520 | ||
521 | ||
522 | static const char *lmemfind (const char *s1, size_t l1, | |
523 | const char *s2, size_t l2) { | |
524 | if (l2 == 0) return s1; /* empty strings are everywhere */ | |
525 | else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ | |
526 | else { | |
527 | const char *init; /* to search for a '*s2' inside 's1' */ | |
528 | l2--; /* 1st char will be checked by 'memchr' */ | |
529 | l1 = l1-l2; /* 's2' cannot be found after that */ | |
530 | while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { | |
531 | init++; /* 1st char is already checked */ | |
532 | if (memcmp(init, s2+1, l2) == 0) | |
533 | return init-1; | |
534 | else { /* correct 'l1' and 's1' to try again */ | |
535 | l1 -= init-s1; | |
536 | s1 = init; | |
537 | } | |
538 | } | |
539 | return NULL; /* not found */ | |
540 | } | |
541 | } | |
542 | ||
543 | ||
544 | static void push_onecapture (MatchState *ms, int i, const char *s, | |
545 | const char *e) { | |
546 | if (i >= ms->level) { | |
547 | if (i == 0) /* ms->level == 0, too */ | |
548 | lua_pushlstring(ms->L, s, e - s); /* add whole match */ | |
549 | else | |
550 | luaL_error(ms->L, "invalid capture index %%%d", i + 1); | |
551 | } | |
552 | else { | |
553 | ptrdiff_t l = ms->capture[i].len; | |
554 | if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); | |
555 | if (l == CAP_POSITION) | |
556 | lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); | |
557 | else | |
558 | lua_pushlstring(ms->L, ms->capture[i].init, l); | |
559 | } | |
560 | } | |
561 | ||
562 | ||
563 | static int push_captures (MatchState *ms, const char *s, const char *e) { | |
564 | int i; | |
565 | int nlevels = (ms->level == 0 && s) ? 1 : ms->level; | |
566 | luaL_checkstack(ms->L, nlevels, "too many captures"); | |
567 | for (i = 0; i < nlevels; i++) | |
568 | push_onecapture(ms, i, s, e); | |
569 | return nlevels; /* number of strings pushed */ | |
570 | } | |
571 | ||
572 | ||
573 | /* check whether pattern has no special characters */ | |
574 | static int nospecials (const char *p, size_t l) { | |
575 | size_t upto = 0; | |
576 | do { | |
577 | if (strpbrk(p + upto, SPECIALS)) | |
578 | return 0; /* pattern has a special character */ | |
579 | upto += strlen(p + upto) + 1; /* may have more after \0 */ | |
580 | } while (upto <= l); | |
581 | return 1; /* no special chars found */ | |
582 | } | |
583 | ||
584 | ||
585 | static int str_find_aux (lua_State *L, int find) { | |
586 | size_t ls, lp; | |
587 | const char *s = luaL_checklstring(L, 1, &ls); | |
588 | const char *p = luaL_checklstring(L, 2, &lp); | |
589 | lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls); | |
590 | if (init < 1) init = 1; | |
591 | else if (init > (lua_Integer)ls + 1) { /* start after string's end? */ | |
592 | lua_pushnil(L); /* cannot find anything */ | |
593 | return 1; | |
594 | } | |
595 | /* explicit request or no special characters? */ | |
596 | if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { | |
597 | /* do a plain search */ | |
598 | const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp); | |
599 | if (s2) { | |
600 | lua_pushinteger(L, s2 - s + 1); | |
601 | lua_pushinteger(L, s2 - s + lp); | |
602 | return 2; | |
603 | } | |
604 | } | |
605 | else { | |
606 | MatchState ms; | |
607 | const char *s1 = s + init - 1; | |
608 | int anchor = (*p == '^'); | |
609 | if (anchor) { | |
610 | p++; lp--; /* skip anchor character */ | |
611 | } | |
612 | ms.L = L; | |
613 | ms.matchdepth = MAXCCALLS; | |
614 | ms.src_init = s; | |
615 | ms.src_end = s + ls; | |
616 | ms.p_end = p + lp; | |
617 | do { | |
618 | const char *res; | |
619 | ms.level = 0; | |
620 | lua_assert(ms.matchdepth == MAXCCALLS); | |
621 | if ((res=match(&ms, s1, p)) != NULL) { | |
622 | if (find) { | |
623 | lua_pushinteger(L, s1 - s + 1); /* start */ | |
624 | lua_pushinteger(L, res - s); /* end */ | |
625 | return push_captures(&ms, NULL, 0) + 2; | |
626 | } | |
627 | else | |
628 | return push_captures(&ms, s1, res); | |
629 | } | |
630 | } while (s1++ < ms.src_end && !anchor); | |
631 | } | |
632 | lua_pushnil(L); /* not found */ | |
633 | return 1; | |
634 | } | |
635 | ||
636 | ||
637 | static int str_find (lua_State *L) { | |
638 | return str_find_aux(L, 1); | |
639 | } | |
640 | ||
641 | ||
642 | static int str_match (lua_State *L) { | |
643 | return str_find_aux(L, 0); | |
644 | } | |
645 | ||
646 | ||
647 | static int gmatch_aux (lua_State *L) { | |
648 | MatchState ms; | |
649 | size_t ls, lp; | |
650 | const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); | |
651 | const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); | |
652 | const char *src; | |
653 | ms.L = L; | |
654 | ms.matchdepth = MAXCCALLS; | |
655 | ms.src_init = s; | |
656 | ms.src_end = s+ls; | |
657 | ms.p_end = p + lp; | |
658 | for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); | |
659 | src <= ms.src_end; | |
660 | src++) { | |
661 | const char *e; | |
662 | ms.level = 0; | |
663 | lua_assert(ms.matchdepth == MAXCCALLS); | |
664 | if ((e = match(&ms, src, p)) != NULL) { | |
665 | lua_Integer newstart = e-s; | |
666 | if (e == src) newstart++; /* empty match? go at least one position */ | |
667 | lua_pushinteger(L, newstart); | |
668 | lua_replace(L, lua_upvalueindex(3)); | |
669 | return push_captures(&ms, src, e); | |
670 | } | |
671 | } | |
672 | return 0; /* not found */ | |
673 | } | |
674 | ||
675 | ||
676 | static int gmatch (lua_State *L) { | |
677 | luaL_checkstring(L, 1); | |
678 | luaL_checkstring(L, 2); | |
679 | lua_settop(L, 2); | |
680 | lua_pushinteger(L, 0); | |
681 | lua_pushcclosure(L, gmatch_aux, 3); | |
682 | return 1; | |
683 | } | |
684 | ||
685 | ||
686 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | |
687 | const char *e) { | |
688 | size_t l, i; | |
689 | lua_State *L = ms->L; | |
690 | const char *news = lua_tolstring(L, 3, &l); | |
691 | for (i = 0; i < l; i++) { | |
692 | if (news[i] != L_ESC) | |
693 | luaL_addchar(b, news[i]); | |
694 | else { | |
695 | i++; /* skip ESC */ | |
696 | if (!isdigit(uchar(news[i]))) { | |
697 | if (news[i] != L_ESC) | |
698 | luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); | |
699 | luaL_addchar(b, news[i]); | |
700 | } | |
701 | else if (news[i] == '0') | |
702 | luaL_addlstring(b, s, e - s); | |
703 | else { | |
704 | push_onecapture(ms, news[i] - '1', s, e); | |
705 | luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ | |
706 | lua_remove(L, -2); /* remove original value */ | |
707 | luaL_addvalue(b); /* add capture to accumulated result */ | |
708 | } | |
709 | } | |
710 | } | |
711 | } | |
712 | ||
713 | ||
714 | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | |
715 | const char *e, int tr) { | |
716 | lua_State *L = ms->L; | |
717 | switch (tr) { | |
718 | case LUA_TFUNCTION: { | |
719 | int n; | |
720 | lua_pushvalue(L, 3); | |
721 | n = push_captures(ms, s, e); | |
722 | lua_call(L, n, 1); | |
723 | break; | |
724 | } | |
725 | case LUA_TTABLE: { | |
726 | push_onecapture(ms, 0, s, e); | |
727 | lua_gettable(L, 3); | |
728 | break; | |
729 | } | |
730 | default: { /* LUA_TNUMBER or LUA_TSTRING */ | |
731 | add_s(ms, b, s, e); | |
732 | return; | |
733 | } | |
734 | } | |
735 | if (!lua_toboolean(L, -1)) { /* nil or false? */ | |
736 | lua_pop(L, 1); | |
737 | lua_pushlstring(L, s, e - s); /* keep original text */ | |
738 | } | |
739 | else if (!lua_isstring(L, -1)) | |
740 | luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); | |
741 | luaL_addvalue(b); /* add result to accumulator */ | |
742 | } | |
743 | ||
744 | ||
745 | static int str_gsub (lua_State *L) { | |
746 | size_t srcl, lp; | |
747 | const char *src = luaL_checklstring(L, 1, &srcl); | |
748 | const char *p = luaL_checklstring(L, 2, &lp); | |
749 | int tr = lua_type(L, 3); | |
750 | lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); | |
751 | int anchor = (*p == '^'); | |
752 | lua_Integer n = 0; | |
753 | MatchState ms; | |
754 | luaL_Buffer b; | |
755 | luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || | |
756 | tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, | |
757 | "string/function/table expected"); | |
758 | luaL_buffinit(L, &b); | |
759 | if (anchor) { | |
760 | p++; lp--; /* skip anchor character */ | |
761 | } | |
762 | ms.L = L; | |
763 | ms.matchdepth = MAXCCALLS; | |
764 | ms.src_init = src; | |
765 | ms.src_end = src+srcl; | |
766 | ms.p_end = p + lp; | |
767 | while (n < max_s) { | |
768 | const char *e; | |
769 | ms.level = 0; | |
770 | lua_assert(ms.matchdepth == MAXCCALLS); | |
771 | e = match(&ms, src, p); | |
772 | if (e) { | |
773 | n++; | |
774 | add_value(&ms, &b, src, e, tr); | |
775 | } | |
776 | if (e && e>src) /* non empty match? */ | |
777 | src = e; /* skip it */ | |
778 | else if (src < ms.src_end) | |
779 | luaL_addchar(&b, *src++); | |
780 | else break; | |
781 | if (anchor) break; | |
782 | } | |
783 | luaL_addlstring(&b, src, ms.src_end-src); | |
784 | luaL_pushresult(&b); | |
785 | lua_pushinteger(L, n); /* number of substitutions */ | |
786 | return 2; | |
787 | } | |
788 | ||
789 | /* }====================================================== */ | |
790 | ||
791 | ||
792 | ||
793 | /* | |
794 | ** {====================================================== | |
795 | ** STRING FORMAT | |
796 | ** ======================================================= | |
797 | */ | |
798 | ||
799 | /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ | |
800 | #define MAX_ITEM 512 | |
801 | ||
802 | /* valid flags in a format specification */ | |
803 | #define FLAGS "-+ #0" | |
804 | ||
805 | /* | |
806 | ** maximum size of each format specification (such as "%-099.99d") | |
807 | ** (+2 for length modifiers; +10 accounts for %99.99x plus margin of error) | |
808 | */ | |
809 | #define MAX_FORMAT (sizeof(FLAGS) + 2 + 10) | |
810 | ||
811 | ||
812 | static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { | |
813 | size_t l; | |
814 | const char *s = luaL_checklstring(L, arg, &l); | |
815 | luaL_addchar(b, '"'); | |
816 | while (l--) { | |
817 | if (*s == '"' || *s == '\\' || *s == '\n') { | |
818 | luaL_addchar(b, '\\'); | |
819 | luaL_addchar(b, *s); | |
820 | } | |
821 | else if (*s == '\0' || iscntrl(uchar(*s))) { | |
822 | char buff[10]; | |
823 | if (!isdigit(uchar(*(s+1)))) | |
824 | sprintf(buff, "\\%d", (int)uchar(*s)); | |
825 | else | |
826 | sprintf(buff, "\\%03d", (int)uchar(*s)); | |
827 | luaL_addstring(b, buff); | |
828 | } | |
829 | else | |
830 | luaL_addchar(b, *s); | |
831 | s++; | |
832 | } | |
833 | luaL_addchar(b, '"'); | |
834 | } | |
835 | ||
836 | static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { | |
837 | const char *p = strfrmt; | |
838 | while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ | |
839 | if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) | |
840 | luaL_error(L, "invalid format (repeated flags)"); | |
841 | if (isdigit(uchar(*p))) p++; /* skip width */ | |
842 | if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ | |
843 | if (*p == '.') { | |
844 | p++; | |
845 | if (isdigit(uchar(*p))) p++; /* skip precision */ | |
846 | if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ | |
847 | } | |
848 | if (isdigit(uchar(*p))) | |
849 | luaL_error(L, "invalid format (width or precision too long)"); | |
850 | *(form++) = '%'; | |
851 | memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); | |
852 | form += p - strfrmt + 1; | |
853 | *form = '\0'; | |
854 | return p; | |
855 | } | |
856 | ||
857 | ||
858 | /* | |
859 | ** add length modifier into formats | |
860 | */ | |
861 | static void addlenmod (char *form, const char *lenmod) { | |
862 | size_t l = strlen(form); | |
863 | size_t lm = strlen(lenmod); | |
864 | char spec = form[l - 1]; | |
865 | strcpy(form + l - 1, lenmod); | |
866 | form[l + lm - 1] = spec; | |
867 | form[l + lm] = '\0'; | |
868 | } | |
869 | ||
870 | ||
871 | static int str_format (lua_State *L) { | |
872 | int top = lua_gettop(L); | |
873 | int arg = 1; | |
874 | size_t sfl; | |
875 | const char *strfrmt = luaL_checklstring(L, arg, &sfl); | |
876 | const char *strfrmt_end = strfrmt+sfl; | |
877 | luaL_Buffer b; | |
878 | luaL_buffinit(L, &b); | |
879 | while (strfrmt < strfrmt_end) { | |
880 | if (*strfrmt != L_ESC) | |
881 | luaL_addchar(&b, *strfrmt++); | |
882 | else if (*++strfrmt == L_ESC) | |
883 | luaL_addchar(&b, *strfrmt++); /* %% */ | |
884 | else { /* format item */ | |
885 | char form[MAX_FORMAT]; /* to store the format ('%...') */ | |
886 | char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ | |
887 | int nb = 0; /* number of bytes in added item */ | |
888 | if (++arg > top) | |
889 | luaL_argerror(L, arg, "no value"); | |
890 | strfrmt = scanformat(L, strfrmt, form); | |
891 | switch (*strfrmt++) { | |
892 | case 'c': { | |
893 | nb = sprintf(buff, form, (int)luaL_checkinteger(L, arg)); | |
894 | break; | |
895 | } | |
896 | case 'd': case 'i': | |
897 | case 'o': case 'u': case 'x': case 'X': { | |
898 | lua_Integer n = luaL_checkinteger(L, arg); | |
899 | addlenmod(form, LUA_INTEGER_FRMLEN); | |
900 | nb = sprintf(buff, form, n); | |
901 | break; | |
902 | } | |
903 | #if defined(LUA_USE_AFORMAT) | |
904 | case 'a': case 'A': | |
905 | #endif | |
906 | case 'e': case 'E': case 'f': | |
907 | case 'g': case 'G': { | |
908 | addlenmod(form, LUA_NUMBER_FRMLEN); | |
909 | nb = sprintf(buff, form, luaL_checknumber(L, arg)); | |
910 | break; | |
911 | } | |
912 | case 'q': { | |
913 | addquoted(L, &b, arg); | |
914 | break; | |
915 | } | |
916 | case 's': { | |
917 | size_t l; | |
918 | const char *s = luaL_tolstring(L, arg, &l); | |
919 | if (!strchr(form, '.') && l >= 100) { | |
920 | /* no precision and string is too long to be formatted; | |
921 | keep original string */ | |
922 | luaL_addvalue(&b); | |
923 | break; | |
924 | } | |
925 | else { | |
926 | nb = sprintf(buff, form, s); | |
927 | lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ | |
928 | break; | |
929 | } | |
930 | } | |
931 | default: { /* also treat cases 'pnLlh' */ | |
932 | return luaL_error(L, "invalid option '%%%c' to 'format'", | |
933 | *(strfrmt - 1)); | |
934 | } | |
935 | } | |
936 | luaL_addsize(&b, nb); | |
937 | } | |
938 | } | |
939 | luaL_pushresult(&b); | |
940 | return 1; | |
941 | } | |
942 | ||
943 | /* }====================================================== */ | |
944 | ||
945 | ||
946 | /* | |
947 | ** {====================================================== | |
948 | ** PACK/UNPACK | |
949 | ** ======================================================= | |
950 | */ | |
951 | ||
952 | ||
953 | /* value used for padding */ | |
954 | #if !defined(LUA_PACKPADBYTE) | |
955 | #define LUA_PACKPADBYTE 0x00 | |
956 | #endif | |
957 | ||
958 | /* maximum size for the binary representation of an integer */ | |
959 | #define MAXINTSIZE 16 | |
960 | ||
961 | /* number of bits in a character */ | |
962 | #define NB CHAR_BIT | |
963 | ||
964 | /* mask for one character (NB 1's) */ | |
965 | #define MC ((1 << NB) - 1) | |
966 | ||
967 | /* size of a lua_Integer */ | |
968 | #define SZINT ((int)sizeof(lua_Integer)) | |
969 | ||
970 | ||
971 | /* dummy union to get native endianness */ | |
972 | static const union { | |
973 | int dummy; | |
974 | char little; /* true iff machine is little endian */ | |
975 | } nativeendian = {1}; | |
976 | ||
977 | ||
978 | /* dummy structure to get native alignment requirements */ | |
979 | struct cD { | |
980 | char c; | |
981 | union { double d; void *p; lua_Integer i; lua_Number n; } u; | |
982 | }; | |
983 | ||
984 | #define MAXALIGN (offsetof(struct cD, u)) | |
985 | ||
986 | ||
987 | /* | |
988 | ** Union for serializing floats | |
989 | */ | |
990 | typedef union Ftypes { | |
991 | float f; | |
992 | double d; | |
993 | lua_Number n; | |
994 | char buff[5 * sizeof(lua_Number)]; /* enough for any float type */ | |
995 | } Ftypes; | |
996 | ||
997 | ||
998 | /* | |
999 | ** information to pack/unpack stuff | |
1000 | */ | |
1001 | typedef struct Header { | |
1002 | lua_State *L; | |
1003 | int islittle; | |
1004 | int maxalign; | |
1005 | } Header; | |
1006 | ||
1007 | ||
1008 | /* | |
1009 | ** options for pack/unpack | |
1010 | */ | |
1011 | typedef enum KOption { | |
1012 | Kint, /* signed integers */ | |
1013 | Kuint, /* unsigned integers */ | |
1014 | Kfloat, /* floating-point numbers */ | |
1015 | Kchar, /* fixed-length strings */ | |
1016 | Kstring, /* strings with prefixed length */ | |
1017 | Kzstr, /* zero-terminated strings */ | |
1018 | Kpadding, /* padding */ | |
1019 | Kpaddalign, /* padding for alignment */ | |
1020 | Knop /* no-op (configuration or spaces) */ | |
1021 | } KOption; | |
1022 | ||
1023 | ||
1024 | /* | |
1025 | ** Read an integer numeral from string 'fmt' or return 'df' if | |
1026 | ** there is no numeral | |
1027 | */ | |
1028 | static int digit (int c) { return '0' <= c && c <= '9'; } | |
1029 | ||
1030 | static int getnum (const char **fmt, int df) { | |
1031 | if (!digit(**fmt)) /* no number? */ | |
1032 | return df; /* return default value */ | |
1033 | else { | |
1034 | int a = 0; | |
1035 | do { | |
1036 | a = a*10 + (*((*fmt)++) - '0'); | |
1037 | } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); | |
1038 | return a; | |
1039 | } | |
1040 | } | |
1041 | ||
1042 | ||
1043 | /* | |
1044 | ** Read an integer numeral and raises an error if it is larger | |
1045 | ** than the maximum size for integers. | |
1046 | */ | |
1047 | static int getnumlimit (Header *h, const char **fmt, int df) { | |
1048 | int sz = getnum(fmt, df); | |
1049 | if (sz > MAXINTSIZE || sz <= 0) | |
1050 | luaL_error(h->L, "integral size (%d) out of limits [1,%d]", | |
1051 | sz, MAXINTSIZE); | |
1052 | return sz; | |
1053 | } | |
1054 | ||
1055 | ||
1056 | /* | |
1057 | ** Initialize Header | |
1058 | */ | |
1059 | static void initheader (lua_State *L, Header *h) { | |
1060 | h->L = L; | |
1061 | h->islittle = nativeendian.little; | |
1062 | h->maxalign = 1; | |
1063 | } | |
1064 | ||
1065 | ||
1066 | /* | |
1067 | ** Read and classify next option. 'size' is filled with option's size. | |
1068 | */ | |
1069 | static KOption getoption (Header *h, const char **fmt, int *size) { | |
1070 | int opt = *((*fmt)++); | |
1071 | *size = 0; /* default */ | |
1072 | switch (opt) { | |
1073 | case 'b': *size = sizeof(char); return Kint; | |
1074 | case 'B': *size = sizeof(char); return Kuint; | |
1075 | case 'h': *size = sizeof(short); return Kint; | |
1076 | case 'H': *size = sizeof(short); return Kuint; | |
1077 | case 'l': *size = sizeof(long); return Kint; | |
1078 | case 'L': *size = sizeof(long); return Kuint; | |
1079 | case 'j': *size = sizeof(lua_Integer); return Kint; | |
1080 | case 'J': *size = sizeof(lua_Integer); return Kuint; | |
1081 | case 'T': *size = sizeof(size_t); return Kuint; | |
1082 | case 'f': *size = sizeof(float); return Kfloat; | |
1083 | case 'd': *size = sizeof(double); return Kfloat; | |
1084 | case 'n': *size = sizeof(lua_Number); return Kfloat; | |
1085 | case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; | |
1086 | case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; | |
1087 | case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; | |
1088 | case 'c': | |
1089 | *size = getnum(fmt, -1); | |
1090 | if (*size == -1) | |
1091 | luaL_error(h->L, "missing size for format option 'c'"); | |
1092 | return Kchar; | |
1093 | case 'z': return Kzstr; | |
1094 | case 'x': *size = 1; return Kpadding; | |
1095 | case 'X': return Kpaddalign; | |
1096 | case ' ': break; | |
1097 | case '<': h->islittle = 1; break; | |
1098 | case '>': h->islittle = 0; break; | |
1099 | case '=': h->islittle = nativeendian.little; break; | |
1100 | case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; | |
1101 | default: luaL_error(h->L, "invalid format option '%c'", opt); | |
1102 | } | |
1103 | return Knop; | |
1104 | } | |
1105 | ||
1106 | ||
1107 | /* | |
1108 | ** Read, classify, and fill other details about the next option. | |
1109 | ** 'psize' is filled with option's size, 'notoalign' with its | |
1110 | ** alignment requirements. | |
1111 | ** Local variable 'size' gets the size to be aligned. (Kpadal option | |
1112 | ** always gets its full alignment, other options are limited by | |
1113 | ** the maximum alignment ('maxalign'). Kchar option needs no alignment | |
1114 | ** despite its size. | |
1115 | */ | |
1116 | static KOption getdetails (Header *h, size_t totalsize, | |
1117 | const char **fmt, int *psize, int *ntoalign) { | |
1118 | KOption opt = getoption(h, fmt, psize); | |
1119 | int align = *psize; /* usually, alignment follows size */ | |
1120 | if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ | |
1121 | if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) | |
1122 | luaL_argerror(h->L, 1, "invalid next option for option 'X'"); | |
1123 | } | |
1124 | if (align <= 1 || opt == Kchar) /* need no alignment? */ | |
1125 | *ntoalign = 0; | |
1126 | else { | |
1127 | if (align > h->maxalign) /* enforce maximum alignment */ | |
1128 | align = h->maxalign; | |
1129 | if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ | |
1130 | luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); | |
1131 | *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); | |
1132 | } | |
1133 | return opt; | |
1134 | } | |
1135 | ||
1136 | ||
1137 | /* | |
1138 | ** Pack integer 'n' with 'size' bytes and 'islittle' endianness. | |
1139 | ** The final 'if' handles the case when 'size' is larger than | |
1140 | ** the size of a Lua integer, correcting the extra sign-extension | |
1141 | ** bytes if necessary (by default they would be zeros). | |
1142 | */ | |
1143 | static void packint (luaL_Buffer *b, lua_Unsigned n, | |
1144 | int islittle, int size, int neg) { | |
1145 | char *buff = luaL_prepbuffsize(b, size); | |
1146 | int i; | |
1147 | buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ | |
1148 | for (i = 1; i < size; i++) { | |
1149 | n >>= NB; | |
1150 | buff[islittle ? i : size - 1 - i] = (char)(n & MC); | |
1151 | } | |
1152 | if (neg && size > SZINT) { /* negative number need sign extension? */ | |
1153 | for (i = SZINT; i < size; i++) /* correct extra bytes */ | |
1154 | buff[islittle ? i : size - 1 - i] = (char)MC; | |
1155 | } | |
1156 | luaL_addsize(b, size); /* add result to buffer */ | |
1157 | } | |
1158 | ||
1159 | ||
1160 | /* | |
1161 | ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if | |
1162 | ** given 'islittle' is different from native endianness. | |
1163 | */ | |
1164 | static void copywithendian (volatile char *dest, volatile const char *src, | |
1165 | int size, int islittle) { | |
1166 | if (islittle == nativeendian.little) { | |
1167 | while (size-- != 0) | |
1168 | *(dest++) = *(src++); | |
1169 | } | |
1170 | else { | |
1171 | dest += size - 1; | |
1172 | while (size-- != 0) | |
1173 | *(dest--) = *(src++); | |
1174 | } | |
1175 | } | |
1176 | ||
1177 | ||
1178 | static int str_pack (lua_State *L) { | |
1179 | luaL_Buffer b; | |
1180 | Header h; | |
1181 | const char *fmt = luaL_checkstring(L, 1); /* format string */ | |
1182 | int arg = 1; /* current argument to pack */ | |
1183 | size_t totalsize = 0; /* accumulate total size of result */ | |
1184 | initheader(L, &h); | |
1185 | lua_pushnil(L); /* mark to separate arguments from string buffer */ | |
1186 | luaL_buffinit(L, &b); | |
1187 | while (*fmt != '\0') { | |
1188 | int size, ntoalign; | |
1189 | KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); | |
1190 | totalsize += ntoalign + size; | |
1191 | while (ntoalign-- > 0) | |
1192 | luaL_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ | |
1193 | arg++; | |
1194 | switch (opt) { | |
1195 | case Kint: { /* signed integers */ | |
1196 | lua_Integer n = luaL_checkinteger(L, arg); | |
1197 | if (size < SZINT) { /* need overflow check? */ | |
1198 | lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); | |
1199 | luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); | |
1200 | } | |
1201 | packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); | |
1202 | break; | |
1203 | } | |
1204 | case Kuint: { /* unsigned integers */ | |
1205 | lua_Integer n = luaL_checkinteger(L, arg); | |
1206 | if (size < SZINT) /* need overflow check? */ | |
1207 | luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), | |
1208 | arg, "unsigned overflow"); | |
1209 | packint(&b, (lua_Unsigned)n, h.islittle, size, 0); | |
1210 | break; | |
1211 | } | |
1212 | case Kfloat: { /* floating-point options */ | |
1213 | volatile Ftypes u; | |
1214 | char *buff = luaL_prepbuffsize(&b, size); | |
1215 | lua_Number n = luaL_checknumber(L, arg); /* get argument */ | |
1216 | if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ | |
1217 | else if (size == sizeof(u.d)) u.d = (double)n; | |
1218 | else u.n = n; | |
1219 | /* move 'u' to final result, correcting endianness if needed */ | |
1220 | copywithendian(buff, u.buff, size, h.islittle); | |
1221 | luaL_addsize(&b, size); | |
1222 | break; | |
1223 | } | |
1224 | case Kchar: { /* fixed-size string */ | |
1225 | size_t len; | |
1226 | const char *s = luaL_checklstring(L, arg, &len); | |
1227 | luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); | |
1228 | luaL_addlstring(&b, s, size); | |
1229 | break; | |
1230 | } | |
1231 | case Kstring: { /* strings with length count */ | |
1232 | size_t len; | |
1233 | const char *s = luaL_checklstring(L, arg, &len); | |
1234 | luaL_argcheck(L, size >= (int)sizeof(size_t) || | |
1235 | len < ((size_t)1 << (size * NB)), | |
1236 | arg, "string length does not fit in given size"); | |
1237 | packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ | |
1238 | luaL_addlstring(&b, s, len); | |
1239 | totalsize += len; | |
1240 | break; | |
1241 | } | |
1242 | case Kzstr: { /* zero-terminated string */ | |
1243 | size_t len; | |
1244 | const char *s = luaL_checklstring(L, arg, &len); | |
1245 | luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); | |
1246 | luaL_addlstring(&b, s, len); | |
1247 | luaL_addchar(&b, '\0'); /* add zero at the end */ | |
1248 | totalsize += len + 1; | |
1249 | break; | |
1250 | } | |
1251 | case Kpadding: luaL_addchar(&b, LUA_PACKPADBYTE); /* go through */ | |
1252 | case Kpaddalign: case Knop: | |
1253 | arg--; /* undo increment */ | |
1254 | break; | |
1255 | } | |
1256 | } | |
1257 | luaL_pushresult(&b); | |
1258 | return 1; | |
1259 | } | |
1260 | ||
1261 | ||
1262 | static int str_packsize (lua_State *L) { | |
1263 | Header h; | |
1264 | const char *fmt = luaL_checkstring(L, 1); /* format string */ | |
1265 | size_t totalsize = 0; /* accumulate total size of result */ | |
1266 | initheader(L, &h); | |
1267 | while (*fmt != '\0') { | |
1268 | int size, ntoalign; | |
1269 | KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); | |
1270 | size += ntoalign; /* total space used by option */ | |
1271 | luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, | |
1272 | "format result too large"); | |
1273 | totalsize += size; | |
1274 | switch (opt) { | |
1275 | case Kstring: /* strings with length count */ | |
1276 | case Kzstr: /* zero-terminated string */ | |
1277 | luaL_argerror(L, 1, "variable-length format"); | |
1278 | break; | |
1279 | default: break; | |
1280 | } | |
1281 | } | |
1282 | lua_pushinteger(L, (lua_Integer)totalsize); | |
1283 | return 1; | |
1284 | } | |
1285 | ||
1286 | ||
1287 | /* | |
1288 | ** Unpack an integer with 'size' bytes and 'islittle' endianness. | |
1289 | ** If size is smaller than the size of a Lua integer and integer | |
1290 | ** is signed, must do sign extension (propagating the sign to the | |
1291 | ** higher bits); if size is larger than the size of a Lua integer, | |
1292 | ** it must check the unread bytes to see whether they do not cause an | |
1293 | ** overflow. | |
1294 | */ | |
1295 | static lua_Integer unpackint (lua_State *L, const char *str, | |
1296 | int islittle, int size, int issigned) { | |
1297 | lua_Unsigned res = 0; | |
1298 | int i; | |
1299 | int limit = (size <= SZINT) ? size : SZINT; | |
1300 | for (i = limit - 1; i >= 0; i--) { | |
1301 | res <<= NB; | |
1302 | res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; | |
1303 | } | |
1304 | if (size < SZINT) { /* real size smaller than lua_Integer? */ | |
1305 | if (issigned) { /* needs sign extension? */ | |
1306 | lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); | |
1307 | res = ((res ^ mask) - mask); /* do sign extension */ | |
1308 | } | |
1309 | } | |
1310 | else if (size > SZINT) { /* must check unread bytes */ | |
1311 | int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; | |
1312 | for (i = limit; i < size; i++) { | |
1313 | if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) | |
1314 | luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); | |
1315 | } | |
1316 | } | |
1317 | return (lua_Integer)res; | |
1318 | } | |
1319 | ||
1320 | ||
1321 | static int str_unpack (lua_State *L) { | |
1322 | Header h; | |
1323 | const char *fmt = luaL_checkstring(L, 1); | |
1324 | size_t ld; | |
1325 | const char *data = luaL_checklstring(L, 2, &ld); | |
1326 | size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1; | |
1327 | int n = 0; /* number of results */ | |
1328 | luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); | |
1329 | initheader(L, &h); | |
1330 | while (*fmt != '\0') { | |
1331 | int size, ntoalign; | |
1332 | KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); | |
1333 | if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld) | |
1334 | luaL_argerror(L, 2, "data string too short"); | |
1335 | pos += ntoalign; /* skip alignment */ | |
1336 | /* stack space for item + next position */ | |
1337 | luaL_checkstack(L, 2, "too many results"); | |
1338 | n++; | |
1339 | switch (opt) { | |
1340 | case Kint: | |
1341 | case Kuint: { | |
1342 | lua_Integer res = unpackint(L, data + pos, h.islittle, size, | |
1343 | (opt == Kint)); | |
1344 | lua_pushinteger(L, res); | |
1345 | break; | |
1346 | } | |
1347 | case Kfloat: { | |
1348 | volatile Ftypes u; | |
1349 | lua_Number num; | |
1350 | copywithendian(u.buff, data + pos, size, h.islittle); | |
1351 | if (size == sizeof(u.f)) num = (lua_Number)u.f; | |
1352 | else if (size == sizeof(u.d)) num = (lua_Number)u.d; | |
1353 | else num = u.n; | |
1354 | lua_pushnumber(L, num); | |
1355 | break; | |
1356 | } | |
1357 | case Kchar: { | |
1358 | lua_pushlstring(L, data + pos, size); | |
1359 | break; | |
1360 | } | |
1361 | case Kstring: { | |
1362 | size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); | |
1363 | luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short"); | |
1364 | lua_pushlstring(L, data + pos + size, len); | |
1365 | pos += len; /* skip string */ | |
1366 | break; | |
1367 | } | |
1368 | case Kzstr: { | |
1369 | size_t len = (int)strlen(data + pos); | |
1370 | lua_pushlstring(L, data + pos, len); | |
1371 | pos += len + 1; /* skip string plus final '\0' */ | |
1372 | break; | |
1373 | } | |
1374 | case Kpaddalign: case Kpadding: case Knop: | |
1375 | n--; /* undo increment */ | |
1376 | break; | |
1377 | } | |
1378 | pos += size; | |
1379 | } | |
1380 | lua_pushinteger(L, pos + 1); /* next position */ | |
1381 | return n + 1; | |
1382 | } | |
1383 | ||
1384 | /* }====================================================== */ | |
1385 | ||
1386 | ||
1387 | static const luaL_Reg strlib[] = { | |
1388 | {"byte", str_byte}, | |
1389 | {"char", str_char}, | |
1390 | {"dump", str_dump}, | |
1391 | {"find", str_find}, | |
1392 | {"format", str_format}, | |
1393 | {"gmatch", gmatch}, | |
1394 | {"gsub", str_gsub}, | |
1395 | {"len", str_len}, | |
1396 | {"lower", str_lower}, | |
1397 | {"match", str_match}, | |
1398 | {"rep", str_rep}, | |
1399 | {"reverse", str_reverse}, | |
1400 | {"sub", str_sub}, | |
1401 | {"upper", str_upper}, | |
1402 | {"pack", str_pack}, | |
1403 | {"packsize", str_packsize}, | |
1404 | {"unpack", str_unpack}, | |
1405 | {NULL, NULL} | |
1406 | }; | |
1407 | ||
1408 | ||
1409 | static void createmetatable (lua_State *L) { | |
1410 | lua_createtable(L, 0, 1); /* table to be metatable for strings */ | |
1411 | lua_pushliteral(L, ""); /* dummy string */ | |
1412 | lua_pushvalue(L, -2); /* copy table */ | |
1413 | lua_setmetatable(L, -2); /* set table as metatable for strings */ | |
1414 | lua_pop(L, 1); /* pop dummy string */ | |
1415 | lua_pushvalue(L, -2); /* get string library */ | |
1416 | lua_setfield(L, -2, "__index"); /* metatable.__index = string */ | |
1417 | lua_pop(L, 1); /* pop metatable */ | |
1418 | } | |
1419 | ||
1420 | ||
1421 | /* | |
1422 | ** Open string library | |
1423 | */ | |
1424 | LUAMOD_API int luaopen_string (lua_State *L) { | |
1425 | luaL_newlib(L, strlib); | |
1426 | createmetatable(L); | |
1427 | return 1; | |
1428 | } | |
1429 |
0 | /* | |
1 | ** $Id: ltablib.c,v 1.79 2014/11/02 19:19:04 roberto Exp $ | |
2 | ** Library for Table Manipulation | |
3 | ** See Copyright Notice in lua.h | |
4 | */ | |
5 | ||
6 | #define ltablib_c | |
7 | #define LUA_LIB | |
8 | ||
9 | #include "lprefix.h" | |
10 | ||
11 | ||
12 | #include <limits.h> | |
13 | #include <stddef.h> | |
14 | ||
15 | #include "lua.h" | |
16 | ||
17 | #include "lauxlib.h" | |
18 | #include "lualib.h" | |
19 | ||
20 | ||
21 | ||
22 | /* | |
23 | ** Structure with table-access functions | |
24 | */ | |
25 | typedef struct { | |
26 | int (*geti) (lua_State *L, int idx, lua_Integer n); | |
27 | void (*seti) (lua_State *L, int idx, lua_Integer n); | |
28 | } TabA; | |
29 | ||
30 | ||
31 | /* | |
32 | ** Check that 'arg' has a table and set access functions in 'ta' to raw | |
33 | ** or non-raw according to the presence of corresponding metamethods. | |
34 | */ | |
35 | static void checktab (lua_State *L, int arg, TabA *ta) { | |
36 | ta->geti = NULL; ta->seti = NULL; | |
37 | if (lua_getmetatable(L, arg)) { | |
38 | lua_pushliteral(L, "__index"); /* 'index' metamethod */ | |
39 | if (lua_rawget(L, -2) != LUA_TNIL) | |
40 | ta->geti = lua_geti; | |
41 | lua_pushliteral(L, "__newindex"); /* 'newindex' metamethod */ | |
42 | if (lua_rawget(L, -3) != LUA_TNIL) | |
43 | ta->seti = lua_seti; | |
44 | lua_pop(L, 3); /* pop metatable plus both metamethods */ | |
45 | } | |
46 | if (ta->geti == NULL || ta->seti == NULL) { | |
47 | luaL_checktype(L, arg, LUA_TTABLE); /* must be table for raw methods */ | |
48 | if (ta->geti == NULL) ta->geti = lua_rawgeti; | |
49 | if (ta->seti == NULL) ta->seti = lua_rawseti; | |
50 | } | |
51 | } | |
52 | ||
53 | ||
54 | #define aux_getn(L,n,ta) (checktab(L, n, ta), luaL_len(L, n)) | |
55 | ||
56 | ||
57 | #if defined(LUA_COMPAT_MAXN) | |
58 | static int maxn (lua_State *L) { | |
59 | lua_Number max = 0; | |
60 | luaL_checktype(L, 1, LUA_TTABLE); | |
61 | lua_pushnil(L); /* first key */ | |
62 | while (lua_next(L, 1)) { | |
63 | lua_pop(L, 1); /* remove value */ | |
64 | if (lua_type(L, -1) == LUA_TNUMBER) { | |
65 | lua_Number v = lua_tonumber(L, -1); | |
66 | if (v > max) max = v; | |
67 | } | |
68 | } | |
69 | lua_pushnumber(L, max); | |
70 | return 1; | |
71 | } | |
72 | #endif | |
73 | ||
74 | ||
75 | static int tinsert (lua_State *L) { | |
76 | TabA ta; | |
77 | lua_Integer e = aux_getn(L, 1, &ta) + 1; /* first empty element */ | |
78 | lua_Integer pos; /* where to insert new element */ | |
79 | switch (lua_gettop(L)) { | |
80 | case 2: { /* called with only 2 arguments */ | |
81 | pos = e; /* insert new element at the end */ | |
82 | break; | |
83 | } | |
84 | case 3: { | |
85 | lua_Integer i; | |
86 | pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ | |
87 | luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); | |
88 | for (i = e; i > pos; i--) { /* move up elements */ | |
89 | (*ta.geti)(L, 1, i - 1); | |
90 | (*ta.seti)(L, 1, i); /* t[i] = t[i - 1] */ | |
91 | } | |
92 | break; | |
93 | } | |
94 | default: { | |
95 | return luaL_error(L, "wrong number of arguments to 'insert'"); | |
96 | } | |
97 | } | |
98 | (*ta.seti)(L, 1, pos); /* t[pos] = v */ | |
99 | return 0; | |
100 | } | |
101 | ||
102 | ||
103 | static int tremove (lua_State *L) { | |
104 | TabA ta; | |
105 | lua_Integer size = aux_getn(L, 1, &ta); | |
106 | lua_Integer pos = luaL_optinteger(L, 2, size); | |
107 | if (pos != size) /* validate 'pos' if given */ | |
108 | luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); | |
109 | (*ta.geti)(L, 1, pos); /* result = t[pos] */ | |
110 | for ( ; pos < size; pos++) { | |
111 | (*ta.geti)(L, 1, pos + 1); | |
112 | (*ta.seti)(L, 1, pos); /* t[pos] = t[pos + 1] */ | |
113 | } | |
114 | lua_pushnil(L); | |
115 | (*ta.seti)(L, 1, pos); /* t[pos] = nil */ | |
116 | return 1; | |
117 | } | |
118 | ||
119 | ||
120 | static int tmove (lua_State *L) { | |
121 | TabA ta; | |
122 | lua_Integer f = luaL_checkinteger(L, 2); | |
123 | lua_Integer e = luaL_checkinteger(L, 3); | |
124 | lua_Integer t = luaL_checkinteger(L, 4); | |
125 | int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ | |
126 | /* the following restriction avoids several problems with overflows */ | |
127 | luaL_argcheck(L, f > 0, 2, "initial position must be positive"); | |
128 | if (e >= f) { /* otherwise, nothing to move */ | |
129 | lua_Integer n, i; | |
130 | ta.geti = (luaL_getmetafield(L, 1, "__index") == LUA_TNIL) | |
131 | ? (luaL_checktype(L, 1, LUA_TTABLE), lua_rawgeti) | |
132 | : lua_geti; | |
133 | ta.seti = (luaL_getmetafield(L, tt, "__newindex") == LUA_TNIL) | |
134 | ? (luaL_checktype(L, tt, LUA_TTABLE), lua_rawseti) | |
135 | : lua_seti; | |
136 | n = e - f + 1; /* number of elements to move */ | |
137 | if (t > f) { | |
138 | for (i = n - 1; i >= 0; i--) { | |
139 | (*ta.geti)(L, 1, f + i); | |
140 | (*ta.seti)(L, tt, t + i); | |
141 | } | |
142 | } | |
143 | else { | |
144 | for (i = 0; i < n; i++) { | |
145 | (*ta.geti)(L, 1, f + i); | |
146 | (*ta.seti)(L, tt, t + i); | |
147 | } | |
148 | } | |
149 | } | |
150 | lua_pushvalue(L, tt); /* return "to table" */ | |
151 | return 1; | |
152 | } | |
153 | ||
154 | ||
155 | static void addfield (lua_State *L, luaL_Buffer *b, TabA *ta, lua_Integer i) { | |
156 | (*ta->geti)(L, 1, i); | |
157 | if (!lua_isstring(L, -1)) | |
158 | luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", | |
159 | luaL_typename(L, -1), i); | |
160 | luaL_addvalue(b); | |
161 | } | |
162 | ||
163 | ||
164 | static int tconcat (lua_State *L) { | |
165 | TabA ta; | |
166 | luaL_Buffer b; | |
167 | size_t lsep; | |
168 | lua_Integer i, last; | |
169 | const char *sep = luaL_optlstring(L, 2, "", &lsep); | |
170 | checktab(L, 1, &ta); | |
171 | i = luaL_optinteger(L, 3, 1); | |
172 | last = luaL_opt(L, luaL_checkinteger, 4, luaL_len(L, 1)); | |
173 | luaL_buffinit(L, &b); | |
174 | for (; i < last; i++) { | |
175 | addfield(L, &b, &ta, i); | |
176 | luaL_addlstring(&b, sep, lsep); | |
177 | } | |
178 | if (i == last) /* add last value (if interval was not empty) */ | |
179 | addfield(L, &b, &ta, i); | |
180 | luaL_pushresult(&b); | |
181 | return 1; | |
182 | } | |
183 | ||
184 | ||
185 | /* | |
186 | ** {====================================================== | |
187 | ** Pack/unpack | |
188 | ** ======================================================= | |
189 | */ | |
190 | ||
191 | static int pack (lua_State *L) { | |
192 | int i; | |
193 | int n = lua_gettop(L); /* number of elements to pack */ | |
194 | lua_createtable(L, n, 1); /* create result table */ | |
195 | lua_insert(L, 1); /* put it at index 1 */ | |
196 | for (i = n; i >= 1; i--) /* assign elements */ | |
197 | lua_rawseti(L, 1, i); | |
198 | lua_pushinteger(L, n); | |
199 | lua_setfield(L, 1, "n"); /* t.n = number of elements */ | |
200 | return 1; /* return table */ | |
201 | } | |
202 | ||
203 | ||
204 | static int unpack (lua_State *L) { | |
205 | TabA ta; | |
206 | lua_Integer i, e; | |
207 | lua_Unsigned n; | |
208 | checktab(L, 1, &ta); | |
209 | i = luaL_optinteger(L, 2, 1); | |
210 | e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); | |
211 | if (i > e) return 0; /* empty range */ | |
212 | n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ | |
213 | if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) | |
214 | return luaL_error(L, "too many results to unpack"); | |
215 | do { /* must have at least one element */ | |
216 | (*ta.geti)(L, 1, i); /* push arg[i..e] */ | |
217 | } while (i++ < e); | |
218 | ||
219 | return (int)n; | |
220 | } | |
221 | ||
222 | /* }====================================================== */ | |
223 | ||
224 | ||
225 | ||
226 | /* | |
227 | ** {====================================================== | |
228 | ** Quicksort | |
229 | ** (based on 'Algorithms in MODULA-3', Robert Sedgewick; | |
230 | ** Addison-Wesley, 1993.) | |
231 | ** ======================================================= | |
232 | */ | |
233 | ||
234 | ||
235 | static void set2 (lua_State *L, TabA *ta, int i, int j) { | |
236 | (*ta->seti)(L, 1, i); | |
237 | (*ta->seti)(L, 1, j); | |
238 | } | |
239 | ||
240 | static int sort_comp (lua_State *L, int a, int b) { | |
241 | if (!lua_isnil(L, 2)) { /* function? */ | |
242 | int res; | |
243 | lua_pushvalue(L, 2); | |
244 | lua_pushvalue(L, a-1); /* -1 to compensate function */ | |
245 | lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ | |
246 | lua_call(L, 2, 1); | |
247 | res = lua_toboolean(L, -1); | |
248 | lua_pop(L, 1); | |
249 | return res; | |
250 | } | |
251 | else /* a < b? */ | |
252 | return lua_compare(L, a, b, LUA_OPLT); | |
253 | } | |
254 | ||
255 | static void auxsort (lua_State *L, TabA *ta, int l, int u) { | |
256 | while (l < u) { /* for tail recursion */ | |
257 | int i, j; | |
258 | /* sort elements a[l], a[(l+u)/2] and a[u] */ | |
259 | (*ta->geti)(L, 1, l); | |
260 | (*ta->geti)(L, 1, u); | |
261 | if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ | |
262 | set2(L, ta, l, u); /* swap a[l] - a[u] */ | |
263 | else | |
264 | lua_pop(L, 2); | |
265 | if (u-l == 1) break; /* only 2 elements */ | |
266 | i = (l+u)/2; | |
267 | (*ta->geti)(L, 1, i); | |
268 | (*ta->geti)(L, 1, l); | |
269 | if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */ | |
270 | set2(L, ta, i, l); | |
271 | else { | |
272 | lua_pop(L, 1); /* remove a[l] */ | |
273 | (*ta->geti)(L, 1, u); | |
274 | if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */ | |
275 | set2(L, ta, i, u); | |
276 | else | |
277 | lua_pop(L, 2); | |
278 | } | |
279 | if (u-l == 2) break; /* only 3 elements */ | |
280 | (*ta->geti)(L, 1, i); /* Pivot */ | |
281 | lua_pushvalue(L, -1); | |
282 | (*ta->geti)(L, 1, u-1); | |
283 | set2(L, ta, i, u-1); | |
284 | /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ | |
285 | i = l; j = u-1; | |
286 | for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ | |
287 | /* repeat ++i until a[i] >= P */ | |
288 | while ((*ta->geti)(L, 1, ++i), sort_comp(L, -1, -2)) { | |
289 | if (i>=u) luaL_error(L, "invalid order function for sorting"); | |
290 | lua_pop(L, 1); /* remove a[i] */ | |
291 | } | |
292 | /* repeat --j until a[j] <= P */ | |
293 | while ((*ta->geti)(L, 1, --j), sort_comp(L, -3, -1)) { | |
294 | if (j<=l) luaL_error(L, "invalid order function for sorting"); | |
295 | lua_pop(L, 1); /* remove a[j] */ | |
296 | } | |
297 | if (j<i) { | |
298 | lua_pop(L, 3); /* pop pivot, a[i], a[j] */ | |
299 | break; | |
300 | } | |
301 | set2(L, ta, i, j); | |
302 | } | |
303 | (*ta->geti)(L, 1, u-1); | |
304 | (*ta->geti)(L, 1, i); | |
305 | set2(L, ta, u-1, i); /* swap pivot (a[u-1]) with a[i] */ | |
306 | /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ | |
307 | /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ | |
308 | if (i-l < u-i) { | |
309 | j=l; i=i-1; l=i+2; | |
310 | } | |
311 | else { | |
312 | j=i+1; i=u; u=j-2; | |
313 | } | |
314 | auxsort(L, ta, j, i); /* call recursively the smaller one */ | |
315 | } /* repeat the routine for the larger one */ | |
316 | } | |
317 | ||
318 | static int sort (lua_State *L) { | |
319 | TabA ta; | |
320 | int n = (int)aux_getn(L, 1, &ta); | |
321 | luaL_checkstack(L, 50, ""); /* assume array is smaller than 2^50 */ | |
322 | if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ | |
323 | luaL_checktype(L, 2, LUA_TFUNCTION); | |
324 | lua_settop(L, 2); /* make sure there are two arguments */ | |
325 | auxsort(L, &ta, 1, n); | |
326 | return 0; | |
327 | } | |
328 | ||
329 | /* }====================================================== */ | |
330 | ||
331 | ||
332 | static const luaL_Reg tab_funcs[] = { | |
333 | {"concat", tconcat}, | |
334 | #if defined(LUA_COMPAT_MAXN) | |
335 | {"maxn", maxn}, | |
336 | #endif | |
337 | {"insert", tinsert}, | |
338 | {"pack", pack}, | |
339 | {"unpack", unpack}, | |
340 | {"remove", tremove}, | |
341 | {"move", tmove}, | |
342 | {"sort", sort}, | |
343 | {NULL, NULL} | |
344 | }; | |
345 | ||
346 | ||
347 | LUAMOD_API int luaopen_table (lua_State *L) { | |
348 | luaL_newlib(L, tab_funcs); | |
349 | #if defined(LUA_COMPAT_UNPACK) | |
350 | /* _G.unpack = table.unpack */ | |
351 | lua_getfield(L, -1, "unpack"); | |
352 | lua_setglobal(L, "unpack"); | |
353 | #endif | |
354 | return 1; | |
355 | } | |
356 |
0 | /* | |
1 | ** $Id: lutf8lib.c,v 1.13 2014/11/02 19:19:04 roberto Exp $ | |
2 | ** Standard library for UTF-8 manipulation | |
3 | ** See Copyright Notice in lua.h | |
4 | */ | |
5 | ||
6 | #define lutf8lib_c | |
7 | #define LUA_LIB | |
8 | ||
9 | #include "lprefix.h" | |
10 | ||
11 | ||
12 | #include <assert.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | ||
16 | #include "lua.h" | |
17 | ||
18 | #include "lauxlib.h" | |
19 | #include "lualib.h" | |
20 | ||
21 | #define MAXUNICODE 0x10FFFF | |
22 | ||
23 | #define iscont(p) ((*(p) & 0xC0) == 0x80) | |
24 | ||
25 | ||
26 | /* from strlib */ | |
27 | /* translate a relative string position: negative means back from end */ | |
28 | static lua_Integer u_posrelat (lua_Integer pos, size_t len) { | |
29 | if (pos >= 0) return pos; | |
30 | else if (0u - (size_t)pos > len) return 0; | |
31 | else return (lua_Integer)len + pos + 1; | |
32 | } | |
33 | ||
34 | ||
35 | /* | |
36 | ** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid. | |
37 | */ | |
38 | static const char *utf8_decode (const char *o, int *val) { | |
39 | static unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF}; | |
40 | const unsigned char *s = (const unsigned char *)o; | |
41 | unsigned int c = s[0]; | |
42 | unsigned int res = 0; /* final result */ | |
43 | if (c < 0x80) /* ascii? */ | |
44 | res = c; | |
45 | else { | |
46 | int count = 0; /* to count number of continuation bytes */ | |
47 | while (c & 0x40) { /* still have continuation bytes? */ | |
48 | int cc = s[++count]; /* read next byte */ | |
49 | if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ | |
50 | return NULL; /* invalid byte sequence */ | |
51 | res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ | |
52 | c <<= 1; /* to test next bit */ | |
53 | } | |
54 | res |= ((c & 0x7F) << (count * 5)); /* add first byte */ | |
55 | if (count > 3 || res > MAXUNICODE || res <= limits[count]) | |
56 | return NULL; /* invalid byte sequence */ | |
57 | s += count; /* skip continuation bytes read */ | |
58 | } | |
59 | if (val) *val = res; | |
60 | return (const char *)s + 1; /* +1 to include first byte */ | |
61 | } | |
62 | ||
63 | ||
64 | /* | |
65 | ** utf8len(s [, i [, j]]) --> number of characters that start in the | |
66 | ** range [i,j], or nil + current position if 's' is not well formed in | |
67 | ** that interval | |
68 | */ | |
69 | static int utflen (lua_State *L) { | |
70 | int n = 0; | |
71 | size_t len; | |
72 | const char *s = luaL_checklstring(L, 1, &len); | |
73 | lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); | |
74 | lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); | |
75 | luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, | |
76 | "initial position out of string"); | |
77 | luaL_argcheck(L, --posj < (lua_Integer)len, 3, | |
78 | "final position out of string"); | |
79 | while (posi <= posj) { | |
80 | const char *s1 = utf8_decode(s + posi, NULL); | |
81 | if (s1 == NULL) { /* conversion error? */ | |
82 | lua_pushnil(L); /* return nil ... */ | |
83 | lua_pushinteger(L, posi + 1); /* ... and current position */ | |
84 | return 2; | |
85 | } | |
86 | posi = s1 - s; | |
87 | n++; | |
88 | } | |
89 | lua_pushinteger(L, n); | |
90 | return 1; | |
91 | } | |
92 | ||
93 | ||
94 | /* | |
95 | ** codepoint(s, [i, [j]]) -> returns codepoints for all characters | |
96 | ** that start in the range [i,j] | |
97 | */ | |
98 | static int codepoint (lua_State *L) { | |
99 | size_t len; | |
100 | const char *s = luaL_checklstring(L, 1, &len); | |
101 | lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); | |
102 | lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); | |
103 | int n; | |
104 | const char *se; | |
105 | luaL_argcheck(L, posi >= 1, 2, "out of range"); | |
106 | luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range"); | |
107 | if (posi > pose) return 0; /* empty interval; return no values */ | |
108 | n = (int)(pose - posi + 1); | |
109 | if (posi + n <= pose) /* (lua_Integer -> int) overflow? */ | |
110 | return luaL_error(L, "string slice too long"); | |
111 | luaL_checkstack(L, n, "string slice too long"); | |
112 | n = 0; | |
113 | se = s + pose; | |
114 | for (s += posi - 1; s < se;) { | |
115 | int code; | |
116 | s = utf8_decode(s, &code); | |
117 | if (s == NULL) | |
118 | return luaL_error(L, "invalid UTF-8 code"); | |
119 | lua_pushinteger(L, code); | |
120 | n++; | |
121 | } | |
122 | return n; | |
123 | } | |
124 | ||
125 | ||
126 | static void pushutfchar (lua_State *L, int arg) { | |
127 | lua_Integer code = luaL_checkinteger(L, arg); | |
128 | luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range"); | |
129 | lua_pushfstring(L, "%U", (long)code); | |
130 | } | |
131 | ||
132 | ||
133 | /* | |
134 | ** utfchar(n1, n2, ...) -> char(n1)..char(n2)... | |
135 | */ | |
136 | static int utfchar (lua_State *L) { | |
137 | int n = lua_gettop(L); /* number of arguments */ | |
138 | if (n == 1) /* optimize common case of single char */ | |
139 | pushutfchar(L, 1); | |
140 | else { | |
141 | int i; | |
142 | luaL_Buffer b; | |
143 | luaL_buffinit(L, &b); | |
144 | for (i = 1; i <= n; i++) { | |
145 | pushutfchar(L, i); | |
146 | luaL_addvalue(&b); | |
147 | } | |
148 | luaL_pushresult(&b); | |
149 | } | |
150 | return 1; | |
151 | } | |
152 | ||
153 | ||
154 | /* | |
155 | ** offset(s, n, [i]) -> index where n-th character counting from | |
156 | ** position 'i' starts; 0 means character at 'i'. | |
157 | */ | |
158 | static int byteoffset (lua_State *L) { | |
159 | size_t len; | |
160 | const char *s = luaL_checklstring(L, 1, &len); | |
161 | lua_Integer n = luaL_checkinteger(L, 2); | |
162 | lua_Integer posi = (n >= 0) ? 1 : len + 1; | |
163 | posi = u_posrelat(luaL_optinteger(L, 3, posi), len); | |
164 | luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, | |
165 | "position out of range"); | |
166 | if (n == 0) { | |
167 | /* find beginning of current byte sequence */ | |
168 | while (posi > 0 && iscont(s + posi)) posi--; | |
169 | } | |
170 | else { | |
171 | if (iscont(s + posi)) | |
172 | luaL_error(L, "initial position is a continuation byte"); | |
173 | if (n < 0) { | |
174 | while (n < 0 && posi > 0) { /* move back */ | |
175 | do { /* find beginning of previous character */ | |
176 | posi--; | |
177 | } while (posi > 0 && iscont(s + posi)); | |
178 | n++; | |
179 | } | |
180 | } | |
181 | else { | |
182 | n--; /* do not move for 1st character */ | |
183 | while (n > 0 && posi < (lua_Integer)len) { | |
184 | do { /* find beginning of next character */ | |
185 | posi++; | |
186 | } while (iscont(s + posi)); /* (cannot pass final '\0') */ | |
187 | n--; | |
188 | } | |
189 | } | |
190 | } | |
191 | if (n == 0) /* did it find given character? */ | |
192 | lua_pushinteger(L, posi + 1); | |
193 | else /* no such character */ | |
194 | lua_pushnil(L); | |
195 | return 1; | |
196 | } | |
197 | ||
198 | ||
199 | static int iter_aux (lua_State *L) { | |
200 | size_t len; | |
201 | const char *s = luaL_checklstring(L, 1, &len); | |
202 | lua_Integer n = lua_tointeger(L, 2) - 1; | |
203 | if (n < 0) /* first iteration? */ | |
204 | n = 0; /* start from here */ | |
205 | else if (n < (lua_Integer)len) { | |
206 | n++; /* skip current byte */ | |
207 | while (iscont(s + n)) n++; /* and its continuations */ | |
208 | } | |
209 | if (n >= (lua_Integer)len) | |
210 | return 0; /* no more codepoints */ | |
211 | else { | |
212 | int code; | |
213 | const char *next = utf8_decode(s + n, &code); | |
214 | if (next == NULL || iscont(next)) | |
215 | return luaL_error(L, "invalid UTF-8 code"); | |
216 | lua_pushinteger(L, n + 1); | |
217 | lua_pushinteger(L, code); | |
218 | return 2; | |
219 | } | |
220 | } | |
221 | ||
222 | ||
223 | static int iter_codes (lua_State *L) { | |
224 | luaL_checkstring(L, 1); | |
225 | lua_pushcfunction(L, iter_aux); | |
226 | lua_pushvalue(L, 1); | |
227 | lua_pushinteger(L, 0); | |
228 | return 3; | |
229 | } | |
230 | ||
231 | ||
232 | /* pattern to match a single UTF-8 character */ | |
233 | #define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" | |
234 | ||
235 | ||
236 | static struct luaL_Reg funcs[] = { | |
237 | {"offset", byteoffset}, | |
238 | {"codepoint", codepoint}, | |
239 | {"char", utfchar}, | |
240 | {"len", utflen}, | |
241 | {"codes", iter_codes}, | |
242 | /* placeholders */ | |
243 | {"charpattern", NULL}, | |
244 | {NULL, NULL} | |
245 | }; | |
246 | ||
247 | ||
248 | LUAMOD_API int luaopen_utf8 (lua_State *L) { | |
249 | luaL_newlib(L, funcs); | |
250 | lua_pushliteral(L, UTF8PATT); | |
251 | lua_setfield(L, -2, "charpattern"); | |
252 | return 1; | |
253 | } | |
254 |
0 | package = "compat53" | |
1 | version = "0.1-1" | |
2 | source = { | |
3 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.1.zip", | |
4 | dir = "lua-compat-5.3-0.1", | |
5 | } | |
6 | description = { | |
7 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", | |
8 | detailed = [[ | |
9 | This is a small module that aims to make it easier to write Lua | |
10 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. | |
11 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible | |
12 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. | |
13 | ]], | |
14 | homepage = "https://github.com/keplerproject/lua-compat-5.3", | |
15 | license = "MIT" | |
16 | } | |
17 | dependencies = { | |
18 | "lua >= 5.1, < 5.4", | |
19 | --"struct" -- make Roberto's struct module optional | |
20 | } | |
21 | build = { | |
22 | type = "builtin", | |
23 | modules = { | |
24 | ["compat53"] = "compat53.lua", | |
25 | ["compat53.utf8"] = "lutf8lib.c", | |
26 | ["compat53.table"] = "ltablib.c", | |
27 | ["compat53.string"] = "lstrlib.c", | |
28 | } | |
29 | } | |
30 |
0 | package = "compat53" | |
1 | version = "scm-0" | |
2 | source = { | |
3 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/master.zip", | |
4 | dir = "lua-compat-5.3-master", | |
5 | } | |
6 | description = { | |
7 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", | |
8 | detailed = [[ | |
9 | This is a small module that aims to make it easier to write Lua | |
10 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. | |
11 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible | |
12 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. | |
13 | ]], | |
14 | homepage = "https://github.com/keplerproject/lua-compat-5.3", | |
15 | license = "MIT" | |
16 | } | |
17 | dependencies = { | |
18 | "lua >= 5.1, < 5.4", | |
19 | --"struct" -- make Roberto's struct module optional | |
20 | } | |
21 | build = { | |
22 | type = "builtin", | |
23 | modules = { | |
24 | ["compat53"] = "compat53.lua", | |
25 | ["compat53.utf8"] = "lutf8lib.c", | |
26 | ["compat53.table"] = "ltablib.c", | |
27 | ["compat53.string"] = "lstrlib.c", | |
28 | } | |
29 | } | |
30 |
0 | #!/usr/bin/env lua | |
1 | ||
2 | local F, tproxy, writefile, noprint, ___ | |
3 | do | |
4 | local type, unpack = type, table.unpack or unpack | |
5 | local assert, io = assert, io | |
6 | function F(...) | |
7 | local args, n = { ... }, select('#', ...) | |
8 | for i = 1, n do | |
9 | local t = type(args[i]) | |
10 | if t ~= "string" and t ~= "number" and t ~= "boolean" then | |
11 | args[i] = t | |
12 | end | |
13 | end | |
14 | return unpack(args, 1, n) | |
15 | end | |
16 | function tproxy(t) | |
17 | return setmetatable({}, { | |
18 | __index = t, | |
19 | __newindex = t, | |
20 | __len = function() return #t end, | |
21 | }), t | |
22 | end | |
23 | function writefile(name, contents, bin) | |
24 | local f = assert(io.open(name, bin and "wb" or "w")) | |
25 | f:write(contents) | |
26 | f:close() | |
27 | end | |
28 | function noprint() end | |
29 | local sep = ("="):rep(70) | |
30 | function ___() | |
31 | print(sep) | |
32 | end | |
33 | end | |
34 | ||
35 | local V = _VERSION:gsub("^.*(%d+)%.(%d+)$", "%1%2") | |
36 | if jit then V = "jit" end | |
37 | ||
38 | print( "testing Lua API ..." ) | |
39 | package.path = "../?.lua;"..package.path | |
40 | package.cpath = "./?-"..V..".so;./?-"..V..".dll;./?.so;./?.dll" | |
41 | require("compat53") | |
42 | ||
43 | ___'' | |
44 | do | |
45 | local t = setmetatable( {}, { __index = { 1, false, "three" } } ) | |
46 | for i,v in ipairs(t) do | |
47 | print("ipairs", i, v) | |
48 | end | |
49 | end | |
50 | ||
51 | ||
52 | ___'' | |
53 | do | |
54 | local p, t = tproxy{ "a", "b", "c" } | |
55 | print("table.concat", table.concat(p)) | |
56 | print("table.concat", table.concat(p, ",", 2)) | |
57 | print("table.concat", table.concat(p, ".", 1, 2)) | |
58 | print("table.concat", table.concat(t)) | |
59 | print("table.concat", table.concat(t, ",", 2)) | |
60 | print("table.concat", table.concat(t, ".", 1, 2)) | |
61 | end | |
62 | ||
63 | ||
64 | ___'' | |
65 | do | |
66 | local p, t = tproxy{ "a", "b", "c" } | |
67 | table.insert(p, "d") | |
68 | print("table.insert", next(p), t[4]) | |
69 | table.insert(p, 1, "z") | |
70 | print("table.insert", next(p), t[1], t[2]) | |
71 | table.insert(p, 2, "y") | |
72 | print("table.insert", next(p), t[1], t[2], p[3]) | |
73 | t = { "a", "b", "c" } | |
74 | table.insert(t, "d") | |
75 | print("table.insert", t[1], t[2], t[3], t[4]) | |
76 | table.insert(t, 1, "z") | |
77 | print("table.insert", t[1], t[2], t[3], t[4], t[5]) | |
78 | table.insert(t, 2, "y") | |
79 | print("table.insert", t[1], t[2], t[3], t[4], t[5]) | |
80 | end | |
81 | ||
82 | ||
83 | ___'' | |
84 | do | |
85 | local ps, s = tproxy{ "a", "b", "c", "d" } | |
86 | local pd, d = tproxy{ "A", "B", "C", "D" } | |
87 | table.move(ps, 1, 4, 1, pd) | |
88 | print("table.move", next(pd), d[1], d[2], d[3], d[4]) | |
89 | pd, d = tproxy{ "A", "B", "C", "D" } | |
90 | table.move(ps, 2, 4, 1, pd) | |
91 | print("table.move", next(pd), d[1], d[2], d[3], d[4]) | |
92 | pd, d = tproxy{ "A", "B", "C", "D" } | |
93 | table.move(ps, 2, 3, 4, pd) | |
94 | print("table.move", next(pd), d[1], d[2], d[3], d[4], d[5]) | |
95 | table.move(ps, 2, 4, 1) | |
96 | print("table.move", next(ps), s[1], s[2], s[3], s[4]) | |
97 | ps, s = tproxy{ "a", "b", "c", "d" } | |
98 | table.move(ps, 2, 3, 4) | |
99 | print("table.move", next(ps), s[1], s[2], s[3], s[4], s[5]) | |
100 | s = { "a", "b", "c", "d" } | |
101 | d = { "A", "B", "C", "D" } | |
102 | table.move(s, 1, 4, 1, d) | |
103 | print("table.move", d[1], d[2], d[3], d[4]) | |
104 | d = { "A", "B", "C", "D" } | |
105 | table.move(s, 2, 4, 1, d) | |
106 | print("table.move", d[1], d[2], d[3], d[4]) | |
107 | d = { "A", "B", "C", "D" } | |
108 | table.move(s, 2, 3, 4, d) | |
109 | print("table.move", d[1], d[2], d[3], d[4], d[5]) | |
110 | table.move(s, 2, 4, 1) | |
111 | print("table.move", s[1], s[2], s[3], s[4]) | |
112 | s = { "a", "b", "c", "d" } | |
113 | table.move(s, 2, 3, 4) | |
114 | print("table.move", s[1], s[2], s[3], s[4], s[5]) | |
115 | end | |
116 | ||
117 | ||
118 | ___'' | |
119 | do | |
120 | local p, t = tproxy{ "a", "b", "c", "d", "e" } | |
121 | print("table.remove", table.remove(p)) | |
122 | print("table.remove", next(p), t[1], t[2], t[3], t[4], t[5]) | |
123 | print("table.remove", table.remove(p, 1)) | |
124 | print("table.remove", next(p), t[1], t[2], t[3], t[4]) | |
125 | print("table.remove", table.remove(p, 2)) | |
126 | print("table.remove", next(p), t[1], t[2], t[3]) | |
127 | print("table.remove", table.remove(p, 3)) | |
128 | print("table.remove", next(p), t[1], t[2], t[3]) | |
129 | p, t = tproxy{} | |
130 | print("table.remove", table.remove(p)) | |
131 | print("table.remove", next(p), next(t)) | |
132 | t = { "a", "b", "c", "d", "e" } | |
133 | print("table.remove", table.remove(t)) | |
134 | print("table.remove", t[1], t[2], t[3], t[4], t[5]) | |
135 | print("table.remove", table.remove(t, 1)) | |
136 | print("table.remove", t[1], t[2], t[3], t[4]) | |
137 | print("table.remove", table.remove(t, 2)) | |
138 | print("table.remove", t[1], t[2], t[3]) | |
139 | print("table.remove", table.remove(t, 3)) | |
140 | print("table.remove", t[1], t[2], t[3]) | |
141 | t = {} | |
142 | print("table.remove", table.remove(t)) | |
143 | print("table.remove", next(t)) | |
144 | end | |
145 | ||
146 | ___'' | |
147 | do | |
148 | local p, t = tproxy{ 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 } | |
149 | table.sort(p) | |
150 | print("table.sort", next(p)) | |
151 | for i,v in ipairs(t) do | |
152 | print("table.sort", i, v) | |
153 | end | |
154 | table.sort(p) | |
155 | print("table.sort", next(p)) | |
156 | for i,v in ipairs(t) do | |
157 | print("table.sort", i, v) | |
158 | end | |
159 | p, t = tproxy{ 9, 8, 7, 6, 5, 4, 3, 2, 1 } | |
160 | table.sort(p) | |
161 | print("table.sort", next(p)) | |
162 | for i,v in ipairs(t) do | |
163 | print("table.sort", i, v) | |
164 | end | |
165 | table.sort(p, function(a, b) return a > b end) | |
166 | print("table.sort", next(p)) | |
167 | for i,v in ipairs(t) do | |
168 | print("table.sort", i, v) | |
169 | end | |
170 | p, t = tproxy{ 1, 1, 1, 1, 1 } | |
171 | print("table.sort", next(p)) | |
172 | for i,v in ipairs(t) do | |
173 | print("table.sort", i, v) | |
174 | end | |
175 | t = { 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 } | |
176 | table.sort(t) | |
177 | for i,v in ipairs(t) do | |
178 | print("table.sort", i, v) | |
179 | end | |
180 | table.sort(t, function(a, b) return a > b end) | |
181 | for i,v in ipairs(t) do | |
182 | print("table.sort", i, v) | |
183 | end | |
184 | end | |
185 | ||
186 | ||
187 | ___'' | |
188 | do | |
189 | local p, t = tproxy{ "a", "b", "c" } | |
190 | print("table.unpack", table.unpack(p)) | |
191 | print("table.unpack", table.unpack(p, 2)) | |
192 | print("table.unpack", table.unpack(p, 1, 2)) | |
193 | print("table.unpack", table.unpack(t)) | |
194 | print("table.unpack", table.unpack(t, 2)) | |
195 | print("table.unpack", table.unpack(t, 1, 2)) | |
196 | end | |
197 | ||
198 | ||
199 | ___'' | |
200 | print("math.maxinteger", math.maxinteger+1 > math.maxinteger) | |
201 | print("math.mininteger", math.mininteger-1 < math.mininteger) | |
202 | ||
203 | ||
204 | ___'' | |
205 | print("math.tointeger", math.tointeger(0)) | |
206 | print("math.tointeger", math.tointeger(math.pi)) | |
207 | print("math.tointeger", math.tointeger("hello")) | |
208 | print("math.tointeger", math.tointeger(math.maxinteger+2.0)) | |
209 | print("math.tointeger", math.tointeger(math.mininteger*2.0)) | |
210 | ||
211 | ||
212 | ___'' | |
213 | print("math.type", math.type(0)) | |
214 | print("math.type", math.type(math.pi)) | |
215 | print("math.type", math.type("hello")) | |
216 | ||
217 | ||
218 | ___'' | |
219 | print("math.ult", math.ult(1, 2), math.ult(2, 1)) | |
220 | print("math.ult", math.ult(-1, 2), math.ult(2, -1)) | |
221 | print("math.ult", math.ult(-1, -2), math.ult(-2, -1)) | |
222 | print("math.ult", pcall(math.ult, "x", 2)) | |
223 | print("math.ult", pcall(math.ult, 1, 2.1)) | |
224 | ___'' | |
225 | ||
226 | ||
227 | print("testing Lua API for Lua 5.1 ...") | |
228 | ||
229 | ___'' | |
230 | print("debug.getuservalue()", F(debug.getuservalue(false))) | |
231 | print("debug.setuservalue()", pcall(function() | |
232 | debug.setuservalue(false, {}) | |
233 | end)) | |
234 | print("debug.setmetatable()", F(debug.setmetatable({}, {}))) | |
235 | ||
236 | ||
237 | ___'' | |
238 | do | |
239 | local t = setmetatable({}, { | |
240 | __pairs = function() return pairs({ a = "a" }) end, | |
241 | }) | |
242 | for k,v in pairs(t) do | |
243 | print("pairs()", k, v) | |
244 | end | |
245 | end | |
246 | ||
247 | ||
248 | ___'' | |
249 | do | |
250 | local code = "print('hello world')\n" | |
251 | local badcode = "print('blub\n" | |
252 | print("load()", pcall(function() load(true) end)) | |
253 | print("load()", F(load(badcode))) | |
254 | print("load()", F(load(code))) | |
255 | print("load()", F(load(code, "[L]"))) | |
256 | print("load()", F(load(code, "[L]", "b"))) | |
257 | print("load()", F(load(code, "[L]", "t"))) | |
258 | print("load()", F(load(code, "[L]", "bt"))) | |
259 | local f = load(code, "[L]", "bt", {}) | |
260 | print("load()", pcall(f)) | |
261 | f = load(code, "[L]", "bt", { print = noprint }) | |
262 | print("load()", pcall(f)) | |
263 | local bytecode = string.dump(f) | |
264 | print("load()", F(load(bytecode))) | |
265 | print("load()", F(load(bytecode, "[L]"))) | |
266 | print("load()", F(load(bytecode, "[L]", "b"))) | |
267 | print("load()", F(load(bytecode, "[L]", "t"))) | |
268 | print("load()", F(load(bytecode, "[L]", "bt"))) | |
269 | f = load(bytecode, "[L]", "bt", {}) | |
270 | print("load()", pcall(f)) | |
271 | f = load(bytecode, "[L]", "bt", { print = noprint }) | |
272 | print("load()", pcall(f)) | |
273 | local function make_loader(code) | |
274 | local mid = math.floor( #code/2 ) | |
275 | local array = { code:sub(1, mid), code:sub(mid+1) } | |
276 | local i = 0 | |
277 | return function() | |
278 | i = i + 1 | |
279 | return array[i] | |
280 | end | |
281 | end | |
282 | print("load()", F(load(make_loader(badcode)))) | |
283 | print("load()", F(load(make_loader(code)))) | |
284 | print("load()", F(load(make_loader(code), "[L]"))) | |
285 | print("load()", F(load(make_loader(code), "[L]", "b"))) | |
286 | print("load()", F(load(make_loader(code), "[L]", "t"))) | |
287 | print("load()", F(load(make_loader(code), "[L]", "bt"))) | |
288 | f = load(make_loader(code), "[L]", "bt", {}) | |
289 | print("load()", pcall(f)) | |
290 | f = load(make_loader(code), "[L]", "bt", { print = noprint }) | |
291 | print("load()", pcall(f)) | |
292 | print("load()", F(load(make_loader(bytecode)))) | |
293 | print("load()", F(load(make_loader(bytecode), "[L]"))) | |
294 | print("load()", F(load(make_loader(bytecode), "[L]", "b"))) | |
295 | print("load()", F(load(make_loader(bytecode), "[L]", "t"))) | |
296 | print("load()", F(load(make_loader(bytecode), "[L]", "bt"))) | |
297 | f = load(make_loader(bytecode), "[L]", "bt", {}) | |
298 | print("load()", pcall(f)) | |
299 | f = load(make_loader(bytecode), "[L]", "bt", { print = noprint }) | |
300 | print("load()", pcall(f)) | |
301 | writefile("good.lua", code) | |
302 | writefile("bad.lua", badcode) | |
303 | writefile("good.luac", bytecode, true) | |
304 | print("loadfile()", F(loadfile("bad.lua"))) | |
305 | print("loadfile()", F(loadfile("good.lua"))) | |
306 | print("loadfile()", F(loadfile("good.lua", "b"))) | |
307 | print("loadfile()", F(loadfile("good.lua", "t"))) | |
308 | print("loadfile()", F(loadfile("good.lua", "bt"))) | |
309 | f = loadfile("good.lua", "bt", {}) | |
310 | print("loadfile()", pcall(f)) | |
311 | f = loadfile("good.lua", "bt", { print = noprint }) | |
312 | print("loadfile()", pcall(f)) | |
313 | print("loadfile()", F(loadfile("good.luac"))) | |
314 | print("loadfile()", F(loadfile("good.luac", "b"))) | |
315 | print("loadfile()", F(loadfile("good.luac", "t"))) | |
316 | print("loadfile()", F(loadfile("good.luac", "bt"))) | |
317 | f = loadfile("good.luac", "bt", {}) | |
318 | print("loadfile()", pcall(f)) | |
319 | f = loadfile("good.luac", "bt", { print = noprint }) | |
320 | print("loadfile()", pcall(f)) | |
321 | os.remove("good.lua") | |
322 | os.remove("bad.lua") | |
323 | os.remove("good.luac") | |
324 | end | |
325 | ||
326 | ||
327 | ___'' | |
328 | do | |
329 | local function func(throw) | |
330 | if throw then | |
331 | error("argh") | |
332 | else | |
333 | return 1, 2, 3 | |
334 | end | |
335 | end | |
336 | local function tb(err) return "|"..err.."|" end | |
337 | print("xpcall()", xpcall(func, debug.traceback, false)) | |
338 | print("xpcall()", xpcall(func, debug.traceback, true)) | |
339 | print("xpcall()", xpcall(func, tb, true)) | |
340 | local function func2(cb) | |
341 | print("xpcall()", xpcall(cb, debug.traceback, "str")) | |
342 | end | |
343 | local function func3(cb) | |
344 | print("pcall()", pcall(cb, "str")) | |
345 | end | |
346 | local function cb(arg) | |
347 | coroutine.yield(2) | |
348 | return arg | |
349 | end | |
350 | local c = coroutine.wrap(func2) | |
351 | print("xpcall()", c(cb)) | |
352 | print("xpcall()", c()) | |
353 | local c = coroutine.wrap(func3) | |
354 | print("pcall()", c(cb)) | |
355 | print("pcall()", c()) | |
356 | end | |
357 | ||
358 | ||
359 | ___'' | |
360 | do | |
361 | local t = setmetatable({ 1 }, { __len = function() return 5 end }) | |
362 | print("rawlen()", rawlen(t), rawlen("123")) | |
363 | end | |
364 | ||
365 | ||
366 | ___'' | |
367 | print("os.execute()", os.execute("exit 1")) | |
368 | io.flush() | |
369 | print("os.execute()", os.execute("echo 'hello world!'")) | |
370 | io.flush() | |
371 | print("os.execute()", os.execute("no_such_file")) | |
372 | ||
373 | ||
374 | ___'' | |
375 | do | |
376 | local t = table.pack("a", nil, "b", nil) | |
377 | print("table.(un)pack()", t.n, table.unpack(t, 1, t.n)) | |
378 | end | |
379 | ||
380 | ||
381 | ___'' | |
382 | do | |
383 | print("coroutine.running()", F(coroutine.wrap(function() | |
384 | return coroutine.running() | |
385 | end)())) | |
386 | print("coroutine.running()", F(coroutine.running())) | |
387 | local main_co, co1, co2 = coroutine.running() | |
388 | -- coroutine.yield | |
389 | print("coroutine.yield()", pcall(function() | |
390 | coroutine.yield(1, 2, 3) | |
391 | end)) | |
392 | print("coroutine.yield()", coroutine.wrap(function() | |
393 | coroutine.yield(1, 2, 3) | |
394 | end)()) | |
395 | print("coroutine.resume()", coroutine.resume(main_co, 1, 2, 3)) | |
396 | co1 = coroutine.create(function(a, b, c) | |
397 | print("coroutine.resume()", a, b, c) | |
398 | return a, b, c | |
399 | end) | |
400 | print("coroutine.resume()", coroutine.resume(co1, 1, 2, 3)) | |
401 | co1 = coroutine.create(function() | |
402 | print("coroutine.status()", "[co1] main is", coroutine.status(main_co)) | |
403 | print("coroutine.status()", "[co1] co2 is", coroutine.status(co2)) | |
404 | end) | |
405 | co2 = coroutine.create(function() | |
406 | print("coroutine.status()", "[co2] main is", coroutine.status(main_co)) | |
407 | print("coroutine.status()", "[co2] co2 is", coroutine.status(co2)) | |
408 | coroutine.yield() | |
409 | coroutine.resume(co1) | |
410 | end) | |
411 | print("coroutine.status()", coroutine.status(main_co)) | |
412 | print("coroutine.status()", coroutine.status(co2)) | |
413 | coroutine.resume(co2) | |
414 | print("coroutine.status()", F(coroutine.status(co2))) | |
415 | coroutine.resume(co2) | |
416 | print("coroutine.status()", F(coroutine.status(co2))) | |
417 | end | |
418 | ||
419 | ||
420 | ___'' | |
421 | print("math.log()", math.log(1000)) | |
422 | print("math.log()", math.log(1000, 10)) | |
423 | ||
424 | ||
425 | ___'' | |
426 | do | |
427 | local path, prefix = "./?.lua;?/init.lua;../?.lua", "package.searchpath()" | |
428 | print(prefix, package.searchpath("no.such.module", path)) | |
429 | print(prefix, package.searchpath("no.such.module", "")) | |
430 | print(prefix, package.searchpath("compat52", path)) | |
431 | print(prefix, package.searchpath("no:such:module", path, ":", "|")) | |
432 | end | |
433 | ||
434 | ||
435 | ___'' | |
436 | do | |
437 | local function mod_func() return {} end | |
438 | local function my_searcher(name) | |
439 | if name == "my.module" then | |
440 | print("package.searchers", "my.module found") | |
441 | return mod_func | |
442 | end | |
443 | end | |
444 | local function my_searcher2(name) | |
445 | if name == "my.module" then | |
446 | print("package.searchers", "my.module found 2") | |
447 | return mod_func | |
448 | end | |
449 | end | |
450 | table.insert(package.searchers, my_searcher) | |
451 | require("my.module") | |
452 | package.loaded["my.module"] = nil | |
453 | local new_s = { my_searcher2 } | |
454 | for i,f in ipairs(package.searchers) do | |
455 | new_s[i+1] = f | |
456 | end | |
457 | package.searchers = new_s | |
458 | require("my.module") | |
459 | end | |
460 | ||
461 | ||
462 | ___'' | |
463 | do | |
464 | print("string.find()", ("abc\0abc\0abc"):find("[^a\0]+")) | |
465 | print("string.find()", ("abc\0abc\0abc"):find("%w+\0", 5)) | |
466 | for x in ("abc\0def\0ghi"):gmatch("[^\0]+") do | |
467 | print("string.gmatch()", x) | |
468 | end | |
469 | for x in ("abc\0def\0ghi"):gmatch("%w*\0") do | |
470 | print("string.gmatch()", #x) | |
471 | end | |
472 | print("string.gsub()", ("abc\0def\0ghi"):gsub("[\0]", "X")) | |
473 | print("string.gsub()", ("abc\0def\0ghi"):gsub("%w*\0", "X")) | |
474 | print("string.gsub()", ("abc\0def\0ghi"):gsub("%A", "X")) | |
475 | print("string.match()", ("abc\0abc\0abc"):match("([^\0a]+)")) | |
476 | print("string.match()", #("abc\0abc\0abc"):match(".*\0")) | |
477 | print("string.rep()", string.rep("a", 0)) | |
478 | print("string.rep()", string.rep("b", 1)) | |
479 | print("string.rep()", string.rep("c", 4)) | |
480 | print("string.rep()", string.rep("a", 0, "|")) | |
481 | print("string.rep()", string.rep("b", 1, "|")) | |
482 | print("string.rep()", string.rep("c", 4, "|")) | |
483 | local _tostring = tostring | |
484 | function tostring(v) | |
485 | if type(v) == "number" then | |
486 | return "(".._tostring(v)..")" | |
487 | else | |
488 | return _tostring(v) | |
489 | end | |
490 | end | |
491 | print("string.format()", string.format("%q", "\"\\\0000\0010\r0\n0\t0\"")) | |
492 | print("string.format()", string.format("%12.3fx%%sxx%.6s", 3.1, {})) | |
493 | print("string.format()", string.format("%-3f %%%s %%s", 3.1, true)) | |
494 | print("string.format()", string.format("% 3.2g %%d %%%s", 3.1, nil)) | |
495 | print("string.format()", string.format("%+3d %%d %%%%%10.6s", 3, io.stdout)) | |
496 | print("string.format()", pcall(function() | |
497 | print("string.format()", string.format("%d %%s", {})) | |
498 | end)) | |
499 | tostring = _tostring | |
500 | end | |
501 | ||
502 | ||
503 | ___'' | |
504 | do | |
505 | print("io.write()", io.type(io.write("hello world\n"))) | |
506 | local f = assert(io.tmpfile()) | |
507 | print("file:write()", io.type(f:write("hello world\n"))) | |
508 | f:close() | |
509 | end | |
510 | ||
511 | ||
512 | ___'' | |
513 | do | |
514 | writefile("data.txt", "123 18.8 hello world\ni'm here\n") | |
515 | for a,b in io.lines("test.lua", 2, "*l") do | |
516 | print("io.lines()", a, b) | |
517 | break | |
518 | end | |
519 | for l in io.lines("test.lua") do | |
520 | print("io.lines()", l) | |
521 | break | |
522 | end | |
523 | for n1,n2,rest in io.lines("data.txt", "*n", "*n", "*a") do | |
524 | print("io.lines()", n1, n2, rest) | |
525 | end | |
526 | for l in io.lines("data.txt") do | |
527 | print("io.lines()", l) | |
528 | end | |
529 | print("io.lines()", pcall(function() | |
530 | for l in io.lines("data.txt", "*x") do print(l) end | |
531 | end)) | |
532 | print("io.lines()", pcall(function() | |
533 | for l in io.lines("no_such_file.txt") do print(l) end | |
534 | end)) | |
535 | local f = assert(io.open("test.lua", "r")) | |
536 | for a,b in f:lines(2, "*l") do | |
537 | print("file:lines()", a, b) | |
538 | break | |
539 | end | |
540 | f:close() | |
541 | f = assert(io.open("data.txt", "r")) | |
542 | for n1,n2,rest in f:lines("*n", "*n", "*a") do | |
543 | print("file:lines()", n1, n2, rest) | |
544 | end | |
545 | f:close() | |
546 | f = assert(io.open("data.txt", "r")) | |
547 | for l in f:lines() do | |
548 | print("file:lines()", l) | |
549 | end | |
550 | f:close() | |
551 | print("file:lines()", pcall(function() | |
552 | for l in f:lines() do print(l) end | |
553 | end)) | |
554 | print("file:lines()", pcall(function() | |
555 | local f = assert(io.open("data.txt", "r")) | |
556 | for l in f:lines("*l", "*x") do print(l) end | |
557 | f:close() | |
558 | end)) | |
559 | os.remove("data.txt") | |
560 | end | |
561 | ___'' | |
562 | ||
563 | ||
564 | print("testing C API ...") | |
565 | local mod = require("testmod") | |
566 | ___'' | |
567 | print(mod.isinteger(1)) | |
568 | print(mod.isinteger(0)) | |
569 | print(mod.isinteger(1234567)) | |
570 | print(mod.isinteger(12.3)) | |
571 | print(mod.isinteger(math.huge)) | |
572 | print(mod.isinteger(math.sqrt(-1))) | |
573 | ||
574 | ||
575 | ___'' | |
576 | print(mod.rotate(1, 1, 2, 3, 4, 5, 6)) | |
577 | print(mod.rotate(-1, 1, 2, 3, 4, 5, 6)) | |
578 | print(mod.rotate(4, 1, 2, 3, 4, 5, 6)) | |
579 | print(mod.rotate(-4, 1, 2, 3, 4, 5, 6)) | |
580 | ||
581 | ||
582 | ___'' | |
583 | print(mod.strtonum("+123")) | |
584 | print(mod.strtonum(" 123 ")) | |
585 | print(mod.strtonum("-1.23")) | |
586 | print(mod.strtonum(" 123 abc")) | |
587 | print(mod.strtonum("jkl")) | |
588 | ||
589 | ||
590 | ___'' | |
591 | local a, b, c = mod.requiref() | |
592 | print( type(a), type(b), type(c), | |
593 | a.boolean, b.boolean, c.boolean, | |
594 | type(requiref1), type(requiref2), type(requiref3)) | |
595 | ||
596 | ___'' | |
597 | local proxy, backend = {}, {} | |
598 | setmetatable(proxy, { __index = backend, __newindex = backend }) | |
599 | print(rawget(proxy, 1), rawget(backend, 1)) | |
600 | print(mod.getseti(proxy, 1)) | |
601 | print(rawget(proxy, 1), rawget(backend, 1)) | |
602 | print(mod.getseti(proxy, 1)) | |
603 | print(rawget(proxy, 1), rawget(backend, 1)) | |
604 | ||
605 | -- tests for Lua 5.1 | |
606 | ___'' | |
607 | print(mod.tonumber(12)) | |
608 | print(mod.tonumber("12")) | |
609 | print(mod.tonumber("0")) | |
610 | print(mod.tonumber(false)) | |
611 | print(mod.tonumber("error")) | |
612 | ||
613 | ___'' | |
614 | print(mod.tointeger(12)) | |
615 | print(mod.tointeger("12")) | |
616 | print(mod.tointeger("0")) | |
617 | print( "aaa" ) | |
618 | print(mod.tointeger(math.pi)) | |
619 | print( "bbb" ) | |
620 | print(mod.tointeger(false)) | |
621 | print(mod.tointeger("error")) | |
622 | ||
623 | ___'' | |
624 | print(mod.len("123")) | |
625 | print(mod.len({ 1, 2, 3})) | |
626 | print(pcall(mod.len, true)) | |
627 | local ud, meta = mod.newproxy() | |
628 | meta.__len = function() return 5 end | |
629 | print(mod.len(ud)) | |
630 | meta.__len = function() return true end | |
631 | print(pcall(mod.len, ud)) | |
632 | ||
633 | ___'' | |
634 | print(mod.copy(true, "string", {}, 1)) | |
635 | ||
636 | ___'' | |
637 | print(mod.rawxetp()) | |
638 | print(mod.rawxetp("I'm back")) | |
639 | ||
640 | ___'' | |
641 | print(F(mod.globals()), mod.globals() == _G) | |
642 | ||
643 | ___'' | |
644 | local t = {} | |
645 | print(F(mod.subtable(t))) | |
646 | local x, msg = mod.subtable(t) | |
647 | print(F(x, msg, x == t.xxx)) | |
648 | ||
649 | ___'' | |
650 | print(F(mod.udata())) | |
651 | print(mod.udata("nosuchtype")) | |
652 | ||
653 | ___'' | |
654 | print(F(mod.uservalue())) | |
655 | ||
656 | ___'' | |
657 | print(mod.getupvalues()) | |
658 | ||
659 | ___'' | |
660 | print(mod.absindex("hi", true)) | |
661 | ||
662 | ___'' | |
663 | print(mod.arith(2, 1)) | |
664 | print(mod.arith(3, 5)) | |
665 | ||
666 | ___'' | |
667 | print(mod.compare(1, 1)) | |
668 | print(mod.compare(2, 1)) | |
669 | print(mod.compare(1, 2)) | |
670 | ||
671 | ___'' | |
672 | print(mod.tolstring("string")) | |
673 | local t = setmetatable({}, { | |
674 | __tostring = function(v) return "mytable" end | |
675 | }) | |
676 | print(mod.tolstring(t)) | |
677 | local t = setmetatable({}, { | |
678 | __tostring = function(v) return nil end | |
679 | }) | |
680 | print(pcall(mod.tolstring, t)) | |
681 | ||
682 | ___'' | |
683 | print(mod.buffer()) | |
684 | ___'' | |
685 |
0 | #include <stdio.h> | |
1 | #include <lua.h> | |
2 | #include <lauxlib.h> | |
3 | #include "compat-5.3.h" | |
4 | ||
5 | ||
6 | static int test_isinteger (lua_State *L) { | |
7 | lua_pushboolean(L, lua_isinteger(L, 1)); | |
8 | return 1; | |
9 | } | |
10 | ||
11 | ||
12 | static int test_rotate (lua_State *L) { | |
13 | int r = luaL_checkint(L, 1); | |
14 | int n = lua_gettop(L)-1; | |
15 | luaL_argcheck(L, (r < 0 ? -r : r) <= n, 1, "not enough arguments"); | |
16 | lua_rotate(L, 2, r); | |
17 | return n; | |
18 | } | |
19 | ||
20 | ||
21 | static int test_str2num (lua_State *L) { | |
22 | const char *s = luaL_checkstring(L, 1); | |
23 | size_t len = lua_stringtonumber(L, s); | |
24 | if (len == 0) | |
25 | lua_pushnumber(L, 0); | |
26 | lua_pushinteger(L, (lua_Integer)len); | |
27 | return 2; | |
28 | } | |
29 | ||
30 | ||
31 | static int my_mod (lua_State *L ) { | |
32 | lua_newtable(L); | |
33 | lua_pushboolean(L, 1); | |
34 | lua_setfield(L, -2, "boolean"); | |
35 | return 1; | |
36 | } | |
37 | ||
38 | static int test_requiref (lua_State *L) { | |
39 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); | |
40 | lua_newtable(L); | |
41 | lua_pushboolean(L, 0); | |
42 | lua_setfield(L, -2, "boolean"); | |
43 | lua_setfield(L, -2, "requiref3"); | |
44 | lua_pop(L, 1); | |
45 | luaL_requiref(L, "requiref1", my_mod, 0); | |
46 | luaL_requiref(L, "requiref2", my_mod, 1); | |
47 | luaL_requiref(L, "requiref3", my_mod, 1); | |
48 | return 3; | |
49 | } | |
50 | ||
51 | static int test_getseti (lua_State *L) { | |
52 | lua_Integer k = luaL_checkinteger(L, 2); | |
53 | lua_Integer n = 0; | |
54 | if (lua_geti(L, 1, k) == LUA_TNUMBER) { | |
55 | n = lua_tointeger(L, -1); | |
56 | } else { | |
57 | lua_pop(L, 1); | |
58 | lua_pushinteger(L, n); | |
59 | } | |
60 | lua_pushinteger(L, n+1); | |
61 | lua_seti(L, 1, k); | |
62 | return 1; | |
63 | } | |
64 | ||
65 | ||
66 | /* additional tests for Lua5.1 */ | |
67 | #define NUP 3 | |
68 | ||
69 | static int test_newproxy (lua_State *L) { | |
70 | lua_settop(L, 0); | |
71 | lua_newuserdata(L, 0); | |
72 | lua_newtable(L); | |
73 | lua_pushvalue(L, -1); | |
74 | lua_pushboolean(L, 1); | |
75 | lua_setfield(L, -2, "__gc"); | |
76 | lua_setmetatable(L, -3); | |
77 | return 2; | |
78 | } | |
79 | ||
80 | static int test_absindex (lua_State *L) { | |
81 | int i = 1; | |
82 | for (i = 1; i <= NUP; ++i) | |
83 | lua_pushvalue(L, lua_absindex(L, lua_upvalueindex(i))); | |
84 | lua_pushvalue(L, lua_absindex(L, LUA_REGISTRYINDEX)); | |
85 | lua_pushstring(L, lua_typename(L, lua_type(L, lua_absindex(L, -1)))); | |
86 | lua_replace(L, lua_absindex(L, -2)); | |
87 | lua_pushvalue(L, lua_absindex(L, -2)); | |
88 | lua_pushvalue(L, lua_absindex(L, -4)); | |
89 | lua_pushvalue(L, lua_absindex(L, -6)); | |
90 | i += 3; | |
91 | lua_pushvalue(L, lua_absindex(L, 1)); | |
92 | lua_pushvalue(L, lua_absindex(L, 2)); | |
93 | lua_pushvalue(L, lua_absindex(L, 3)); | |
94 | i += 3; | |
95 | return i; | |
96 | } | |
97 | ||
98 | static int test_arith (lua_State *L) { | |
99 | lua_settop(L, 2); | |
100 | lua_pushvalue(L, 1); | |
101 | lua_pushvalue(L, 2); | |
102 | lua_arith(L, LUA_OPADD); | |
103 | lua_pushvalue(L, 1); | |
104 | lua_pushvalue(L, 2); | |
105 | lua_arith(L, LUA_OPSUB); | |
106 | lua_pushvalue(L, 1); | |
107 | lua_pushvalue(L, 2); | |
108 | lua_arith(L, LUA_OPMUL); | |
109 | lua_pushvalue(L, 1); | |
110 | lua_pushvalue(L, 2); | |
111 | lua_arith(L, LUA_OPDIV); | |
112 | lua_pushvalue(L, 1); | |
113 | lua_pushvalue(L, 2); | |
114 | lua_arith(L, LUA_OPMOD); | |
115 | lua_pushvalue(L, 1); | |
116 | lua_pushvalue(L, 2); | |
117 | lua_arith(L, LUA_OPPOW); | |
118 | lua_pushvalue(L, 1); | |
119 | lua_arith(L, LUA_OPUNM); | |
120 | return lua_gettop(L)-2; | |
121 | } | |
122 | ||
123 | static int test_compare (lua_State *L) { | |
124 | luaL_checknumber(L, 1); | |
125 | luaL_checknumber(L, 2); | |
126 | lua_settop(L, 2); | |
127 | lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPEQ)); | |
128 | lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPLT)); | |
129 | lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPLE)); | |
130 | return 3; | |
131 | } | |
132 | ||
133 | static int test_globals (lua_State *L) { | |
134 | lua_pushglobaltable(L); | |
135 | return 1; | |
136 | } | |
137 | ||
138 | static int test_tonumber (lua_State *L) { | |
139 | int isnum = 0; | |
140 | lua_Number n = lua_tonumberx(L, 1, &isnum); | |
141 | if (!isnum) | |
142 | lua_pushnil(L); | |
143 | else | |
144 | lua_pushnumber(L, n); | |
145 | return 1; | |
146 | } | |
147 | ||
148 | static int test_tointeger (lua_State *L) { | |
149 | int isnum = 0; | |
150 | lua_Integer n = lua_tointegerx(L, 1, &isnum); | |
151 | if (!isnum) | |
152 | lua_pushnil(L); | |
153 | else | |
154 | lua_pushinteger(L, n); | |
155 | return 1; | |
156 | } | |
157 | ||
158 | static int test_len (lua_State *L) { | |
159 | luaL_checkany(L, 1); | |
160 | lua_len(L, 1); | |
161 | lua_pushinteger(L, luaL_len(L, 1)); | |
162 | return 2; | |
163 | } | |
164 | ||
165 | static int test_copy (lua_State *L) { | |
166 | int args = lua_gettop(L); | |
167 | if (args >= 2) { | |
168 | int i = 0; | |
169 | for (i = args-1; i > 0; --i) | |
170 | lua_copy(L, args, i); | |
171 | } | |
172 | return args; | |
173 | } | |
174 | ||
175 | /* need an address */ | |
176 | static char const dummy = 0; | |
177 | ||
178 | static int test_rawxetp (lua_State *L) { | |
179 | if (lua_gettop(L) > 0) | |
180 | lua_pushvalue(L, 1); | |
181 | else | |
182 | lua_pushliteral(L, "hello again"); | |
183 | lua_rawsetp(L, LUA_REGISTRYINDEX, &dummy); | |
184 | lua_settop(L, 0); | |
185 | lua_rawgetp(L, LUA_REGISTRYINDEX, &dummy); | |
186 | return 1; | |
187 | } | |
188 | ||
189 | static int test_udata (lua_State *L) { | |
190 | const char *tname = luaL_optstring(L, 1, "utype1"); | |
191 | void *u1 = lua_newuserdata(L, 1); | |
192 | int u1pos = lua_gettop(L); | |
193 | void *u2 = lua_newuserdata(L, 1); | |
194 | int u2pos = lua_gettop(L); | |
195 | luaL_newmetatable(L, "utype1"); | |
196 | luaL_newmetatable(L, "utype2"); | |
197 | lua_pop(L, 2); | |
198 | luaL_setmetatable(L, "utype2"); | |
199 | lua_pushvalue(L, u1pos); | |
200 | luaL_setmetatable(L, "utype1"); | |
201 | lua_pop(L, 1); | |
202 | (void)u1; | |
203 | (void)u2; | |
204 | lua_pushlightuserdata(L, luaL_testudata(L, u1pos, tname)); | |
205 | lua_pushlightuserdata(L, luaL_testudata(L, u2pos, tname)); | |
206 | return 2; | |
207 | } | |
208 | ||
209 | static int test_subtable (lua_State *L) { | |
210 | luaL_checktype(L, 1, LUA_TTABLE); | |
211 | lua_settop(L, 1); | |
212 | if (luaL_getsubtable(L, 1, "xxx")) { | |
213 | lua_pushliteral(L, "oldtable"); | |
214 | } else { | |
215 | lua_pushliteral(L, "newtable"); | |
216 | } | |
217 | return 2; | |
218 | } | |
219 | ||
220 | static int test_uservalue (lua_State *L) { | |
221 | void *udata = lua_newuserdata(L, 1); | |
222 | int ui = lua_gettop(L); | |
223 | lua_newtable(L); | |
224 | lua_setuservalue(L, ui); | |
225 | lua_getuservalue(L, ui); | |
226 | (void)udata; | |
227 | return 1; | |
228 | } | |
229 | ||
230 | static int test_upvalues (lua_State *L) { | |
231 | int i = 1; | |
232 | for (i = 1; i <= NUP; ++i) | |
233 | lua_pushvalue(L, lua_upvalueindex(i)); | |
234 | return NUP; | |
235 | } | |
236 | ||
237 | static int test_tolstring (lua_State *L) { | |
238 | size_t len = 0; | |
239 | luaL_tolstring(L, 1, &len); | |
240 | lua_pushinteger(L, (int)len); | |
241 | return 2; | |
242 | } | |
243 | ||
244 | static int test_buffer (lua_State *L) { | |
245 | luaL_Buffer b; | |
246 | char *p = luaL_buffinitsize(L, &b, LUAL_BUFFERSIZE+1); | |
247 | p[0] = 'a'; | |
248 | p[1] = 'b'; | |
249 | luaL_addsize(&b, 2); | |
250 | luaL_addstring(&b, "c"); | |
251 | lua_pushliteral(L, "d"); | |
252 | luaL_addvalue(&b); | |
253 | luaL_addchar(&b, 'e'); | |
254 | luaL_pushresult(&b); | |
255 | return 1; | |
256 | } | |
257 | ||
258 | ||
259 | static const luaL_Reg funcs[] = { | |
260 | { "isinteger", test_isinteger }, | |
261 | { "rotate", test_rotate }, | |
262 | { "strtonum", test_str2num }, | |
263 | { "requiref", test_requiref }, | |
264 | { "getseti", test_getseti }, | |
265 | { "newproxy", test_newproxy }, | |
266 | { "arith", test_arith }, | |
267 | { "compare", test_compare }, | |
268 | { "tonumber", test_tonumber }, | |
269 | { "tointeger", test_tointeger }, | |
270 | { "len", test_len }, | |
271 | { "copy", test_copy }, | |
272 | { "rawxetp", test_rawxetp }, | |
273 | { "subtable", test_subtable }, | |
274 | { "udata", test_udata }, | |
275 | { "uservalue", test_uservalue }, | |
276 | { "globals", test_globals }, | |
277 | { "tolstring", test_tolstring }, | |
278 | { "buffer", test_buffer }, | |
279 | { NULL, NULL } | |
280 | }; | |
281 | ||
282 | static const luaL_Reg more_funcs[] = { | |
283 | { "getupvalues", test_upvalues }, | |
284 | { "absindex", test_absindex }, | |
285 | { NULL, NULL } | |
286 | }; | |
287 | ||
288 | ||
289 | int luaopen_testmod (lua_State *L) { | |
290 | int i = 1; | |
291 | luaL_newlib(L, funcs); | |
292 | for (i = 1; i <= NUP; ++i) | |
293 | lua_pushnumber(L, i); | |
294 | luaL_setfuncs(L, more_funcs, NUP); | |
295 | return 1; | |
296 | } | |
297 |