diff options
-rw-r--r-- | COPYING | 9 | ||||
-rwxr-xr-x | autoshart.sh | 231 |
2 files changed, 240 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..0520203 --- /dev/null +++ b/COPYING | |||
@@ -0,0 +1,9 @@ | |||
1 | Copyright (C) 2022 Case Duckworth <acdw@acdw.net> | ||
2 | |||
3 | Usage of the works is permitted provided that this instrument is | ||
4 | retained with the works, so that any entity that uses the works is | ||
5 | notified of this instrument. | ||
6 | |||
7 | DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. | ||
8 | |||
9 | |||
diff --git a/autoshart.sh b/autoshart.sh new file mode 100755 index 0000000..4868846 --- /dev/null +++ b/autoshart.sh | |||
@@ -0,0 +1,231 @@ | |||
1 | #!/bin/sh | ||
2 | # AUTOSHART: autostart *.desktop files | ||
3 | # Copyright (C) 2022 Case Duckworth <acdw@acdw.net> | ||
4 | # Licensed under the Fair license. See COPYING for details. | ||
5 | # | ||
6 | # Commentary: | ||
7 | # | ||
8 | # Freedesktop autostart specification: | ||
9 | # https://specifications.freedesktop.org/autostart-spec/latest/ | ||
10 | # Freedesktop desktop entry specification: | ||
11 | # https://specifications.freedesktop.org/desktop-entry-spec/latest/ | ||
12 | # | ||
13 | # Prior art: | ||
14 | # - dex -- https://github.com/jceb/dex | ||
15 | # | ||
16 | # Code: | ||
17 | |||
18 | usage() { | ||
19 | cat <<EOF | ||
20 | AUTOSHART: autostart without the shit | ||
21 | USAGE: autoshart -h | ||
22 | autoshart [-k] [-n] | ||
23 | |||
24 | FLAGS: | ||
25 | -h Show this help and exit. | ||
26 | -k Kill all processes started by autoshart and exit. | ||
27 | -n Only print what would happen; don't execute anything. | ||
28 | EOF | ||
29 | } | ||
30 | |||
31 | main() { | ||
32 | PID_FILE="${AUTOSHART_PID_FILE:-/tmp/autoshart.pid}" | ||
33 | FOUND_FILE="${AUTOSHART_FOUND_FILE:-/tmp/autoshart.found}" | ||
34 | RUN=true | ||
35 | QUIET=false | ||
36 | while getopts hknq opt; do | ||
37 | case "$opt" in | ||
38 | h) usage ;; | ||
39 | k) auto_kill ;; | ||
40 | n) RUN=false ;; | ||
41 | q) QUIET=true ;; | ||
42 | esac | ||
43 | done | ||
44 | shift "$((OPTIND - 1))" | ||
45 | auto_search | ||
46 | } | ||
47 | |||
48 | auto_kill() { | ||
49 | cat "$PID_FILE" | xargs kill | ||
50 | } | ||
51 | |||
52 | auto_start() { ## auto_start FILE | ||
53 | eprint "$1" | ||
54 | # When the .desktop file has the Hidden key set to true, the .desktop | ||
55 | # file MUST be ignored. When multiple .desktop files with the same name | ||
56 | # exists in multiple directories then only the Hidden key in the most | ||
57 | # important .desktop file must be considered: If it is set to true all | ||
58 | # .desktop files with the same name in the other directories MUST be | ||
59 | # ignored as well. | ||
60 | hidden="$(get Hidden "$1")" | ||
61 | test "$hidden" = true && return "$(die 1 "Stopped: Hidden: $1")" | ||
62 | # This specification defines 3 types of desktop entries: Application | ||
63 | # (type 1), Link (type 2) and Directory (type 3). To allow the addition | ||
64 | # of new types in the future, implementations should ignore desktop | ||
65 | # entries with an unknown type. (REQUIRED) | ||
66 | type="$(get Type "$1")" | ||
67 | test "$type" = Application || return "$(die 2 "Error: Bad desktop type: ${type:-[none]}")" | ||
68 | # Specific name of the application, for example "Mozilla". (REQUIRED) | ||
69 | name="$(get Name "$1" | head -n1)" | ||
70 | test -n "$name" || return "$(die 3 "Error: No application name")" | ||
71 | # Program to execute, possibly with arguments. See the Exec key for | ||
72 | # details on how this key works. The Exec key is required if | ||
73 | # DBusActivatable is not set to true. Even if DBusActivatable is true, | ||
74 | # Exec should be specified for compatibility with implementations that | ||
75 | # do not understand DBusActivatable. (Makes no sense to be omitted in an | ||
76 | # autostart context --- acdw) | ||
77 | exec="$(get Exec "$1")" | ||
78 | test -n "$exec" || return "$(die 4 "Error: No Exec key: $1")" | ||
79 | # The OnlyShowIn entry may contain a list of strings identifying | ||
80 | # the desktop environments that MUST autostart this application, all | ||
81 | # other desktop environments MUST NOT autostart this application. The | ||
82 | # NotShowIn entry may contain a list of strings identifying the desktop | ||
83 | # environments that MUST NOT autostart this application, all other | ||
84 | # desktop environments MUST autostart this application. Only one of | ||
85 | # these keys, either OnlyShowIn or NotShowIn, may appear in a single | ||
86 | # .desktop file. | ||
87 | # | ||
88 | ### The OnlyShowIn, NotShowIn keys are specified thus: | ||
89 | # | ||
90 | # A list of strings identifying the desktop environments that should | ||
91 | # display/not display a given desktop entry. | ||
92 | # | ||
93 | # By default, a desktop file should be shown, unless an OnlyShowIn key | ||
94 | # is present, in which case, the default is for the file not to be | ||
95 | # shown. | ||
96 | # | ||
97 | # If $XDG_CURRENT_DESKTOP is set then it contains a colon-separated list | ||
98 | # of strings. In order, each string is considered. If a matching entry | ||
99 | # is found in OnlyShowIn then the desktop file is shown. If an entry is | ||
100 | # found in NotShowIn then the desktop file is not shown. If none of the | ||
101 | # strings match then the default action is taken (as above). | ||
102 | # | ||
103 | # $XDG_CURRENT_DESKTOP should have been set by the login manager, | ||
104 | # according to the value of the DesktopNames found in the session | ||
105 | # file. The entry in the session file has multiple values separated in | ||
106 | # the usual way: with a semicolon. | ||
107 | # | ||
108 | # The same desktop name may not appear in both OnlyShowIn and NotShowIn | ||
109 | # of a group. | ||
110 | # | ||
111 | ### I think this "functionality" is dubious at best and pretty | ||
112 | ### ... silly. --- acdw | ||
113 | onlyshowin="$(get OnlyShowIn "$1")" | ||
114 | notshowin="$(get NotShowIn "$1")" | ||
115 | if test -n "$onlyshowin" && test -n "$notshowin"; then | ||
116 | return "$(die 5 "Error: Incompatible keys: OnlyShowIn, NotShowIn: $1")" | ||
117 | elif test -n "$onlyshowin"; then | ||
118 | start=false | ||
119 | else | ||
120 | start=true | ||
121 | fi | ||
122 | |||
123 | # This is so cursed | ||
124 | quit="$( | ||
125 | echo "$XDG_CURRENT_DESKTOP" | tr : \\n | | ||
126 | while read desktop; do | ||
127 | case "$onlyshowin;$notshowin" in | ||
128 | *$desktop*) | ||
129 | if test -n "$onlyshowin"; then | ||
130 | break | ||
131 | elif test -n "$notshowin"; then | ||
132 | echo true | ||
133 | die 6 "Stopped: NotShowIn=$notshowin" | ||
134 | fi | ||
135 | ;; | ||
136 | *) | ||
137 | if ! $start; then | ||
138 | echo true | ||
139 | die 6 "Stopped: OnlyShowIn=$onlyshowin" | ||
140 | fi | ||
141 | ;; | ||
142 | esac | ||
143 | done | ||
144 | echo false | ||
145 | )" | ||
146 | if $quit; then | ||
147 | return 6 | ||
148 | fi | ||
149 | |||
150 | # A .desktop file with a non-empty TryExec field MUST NOT be autostarted | ||
151 | # if the value of the TryExec key does NOT match with an installed | ||
152 | # executable program. The value of the TryExec field may either be an | ||
153 | # absolute path or the name of an executable without any path | ||
154 | # components. If the name of an executable is specified without any path | ||
155 | # components then the $PATH environment is searched to find a matching | ||
156 | # executable program. | ||
157 | tryexec="$(get TryExec "$1")" | ||
158 | if test -n "$tryexec"; then | ||
159 | command -v "$tryexec" | grep -v ^alias | grep -q / || | ||
160 | return "$(die 7 "Error: TryExec command not found: $tryexec")" | ||
161 | fi | ||
162 | # If all these tests pass, we're ready to go | ||
163 | # XXX: probably won't do quoting properly | ||
164 | eprint "> $exec" | ||
165 | if $RUN; then | ||
166 | $exec & | ||
167 | print $! >>"$PID_FILE" | ||
168 | fi | ||
169 | : | ||
170 | } | ||
171 | |||
172 | auto_search() { ## auto_search | ||
173 | # The Autostart Directories are $XDG_CONFIG_DIRS/autostart as defined | ||
174 | # in accordance with the "Referencing this specification" section in | ||
175 | # the "desktop base directory specification". | ||
176 | # | ||
177 | # If the same filename is located under multiple Autostart Directories | ||
178 | # only the file under the most important directory should be used. | ||
179 | # | ||
180 | # Example: If $XDG_CONFIG_HOME is not set the Autostart Directory in the | ||
181 | # user's home directory is ~/.config/autostart/ | ||
182 | # | ||
183 | # Example: If $XDG_CONFIG_DIRS is not set the system wide Autostart | ||
184 | # Directory is /etc/xdg/autostart/ | ||
185 | # | ||
186 | # Example: If $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS are not set and the | ||
187 | # two files /etc/xdg/autostart/foo.desktop and | ||
188 | # ~/.config/autostart/foo.desktop exist then only the file | ||
189 | # ~/.config/autostart/foo.desktop will be used because | ||
190 | # ~/.config/autostart/ is more important than /etc/xdg/autostart/ | ||
191 | echo "${XDG_CONFIG_HOME:-$HOME/.config}:${XDG_CONFIG_DIRS:-/etc/xdg}" | tr : \\n | | ||
192 | sed 's,$,/autostart,' | | ||
193 | while read dir; do | ||
194 | if test -d "$dir"; then | ||
195 | for desktop in "$dir"/*.desktop; do | ||
196 | if ! grep "$desktop" "$FOUND_FILE"; then | ||
197 | auto_start "$desktop" && | ||
198 | print "$desktop" >>"$FOUND_FILE" | ||
199 | else | ||
200 | eprint "Already executed: $desktop" | ||
201 | fi | ||
202 | done | ||
203 | fi | ||
204 | done | ||
205 | } | ||
206 | |||
207 | #################################### | ||
208 | |||
209 | get() { # get KEY FILE | ||
210 | grep "^$1" "${2:-}" 2>/dev/null | cut -d= -f2- | ||
211 | } | ||
212 | |||
213 | print() { | ||
214 | $QUIET || printf '%s\n' "$*" | ||
215 | } | ||
216 | |||
217 | eprint() { | ||
218 | print "$@" >&2 | ||
219 | } | ||
220 | |||
221 | die() { | ||
222 | errcode="$1" | ||
223 | shift | ||
224 | eprint "! $@" | ||
225 | echo $errcode | ||
226 | exit $errcode | ||
227 | } | ||
228 | |||
229 | #################################### | ||
230 | |||
231 | main "$@" | ||