File: | rc/cafe-screensaver-command.c |
Warning: | line 326, column 2 Potential leak of memory pointed to by 'buffer' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- | |||
2 | * | |||
3 | * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu> | |||
4 | * | |||
5 | * This program is free software; you can redistribute it and/or | |||
6 | * modify it under the terms of the GNU General Public License as | |||
7 | * published by the Free Software Foundation; either version 2 of the | |||
8 | * License, or (at your option) any later version. | |||
9 | * | |||
10 | * This program is distributed in the hope that it will be useful, but | |||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
13 | * General Public License for more details. | |||
14 | * | |||
15 | * You should have received a copy of the GNU General Public License | |||
16 | * along with this program; if not, write to the Free Software | |||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
18 | * 02110-1301, USA. | |||
19 | * | |||
20 | * Authors: William Jon McCann <mccann@jhu.edu> | |||
21 | * | |||
22 | */ | |||
23 | ||||
24 | #include "config.h" | |||
25 | ||||
26 | #include <stdlib.h> | |||
27 | #include <locale.h> | |||
28 | #include <glib.h> | |||
29 | #include <glib/gi18n.h> | |||
30 | ||||
31 | #define DBUS_API_SUBJECT_TO_CHANGE | |||
32 | #include <dbus/dbus.h> | |||
33 | #include <dbus/dbus-glib.h> | |||
34 | #include <dbus/dbus-glib-lowlevel.h> | |||
35 | ||||
36 | #define GS_SERVICE"org.cafe.ScreenSaver" "org.cafe.ScreenSaver" | |||
37 | #define GS_PATH"/org/cafe/ScreenSaver" "/org/cafe/ScreenSaver" | |||
38 | #define GS_INTERFACE"org.cafe.ScreenSaver" "org.cafe.ScreenSaver" | |||
39 | ||||
40 | static gboolean do_quit = FALSE(0); | |||
41 | static gboolean do_lock = FALSE(0); | |||
42 | static gboolean do_unlock = FALSE(0); | |||
43 | static gboolean do_cycle = FALSE(0); | |||
44 | static gboolean do_activate = FALSE(0); | |||
45 | static gboolean do_deactivate = FALSE(0); | |||
46 | static gboolean do_version = FALSE(0); | |||
47 | static gboolean do_poke = FALSE(0); | |||
48 | static gboolean do_inhibit = FALSE(0); | |||
49 | ||||
50 | static gboolean do_query = FALSE(0); | |||
51 | static gboolean do_time = FALSE(0); | |||
52 | ||||
53 | static char *inhibit_reason = NULL((void*)0); | |||
54 | static char *inhibit_application = NULL((void*)0); | |||
55 | ||||
56 | static GOptionEntry entries [] = | |||
57 | { | |||
58 | { | |||
59 | "exit", 0, 0, G_OPTION_ARG_NONE, &do_quit, | |||
60 | N_("Causes the screensaver to exit gracefully")("Causes the screensaver to exit gracefully"), NULL((void*)0) | |||
61 | }, | |||
62 | { | |||
63 | "query", 'q', 0, G_OPTION_ARG_NONE, &do_query, | |||
64 | N_("Query the state of the screensaver")("Query the state of the screensaver"), NULL((void*)0) | |||
65 | }, | |||
66 | { | |||
67 | "time", 't', 0, G_OPTION_ARG_NONE, &do_time, | |||
68 | N_("Query the length of time the screensaver has been active")("Query the length of time the screensaver has been active"), NULL((void*)0) | |||
69 | }, | |||
70 | { | |||
71 | "lock", 'l', 0, G_OPTION_ARG_NONE, &do_lock, | |||
72 | N_("Tells the running screensaver process to lock the screen immediately")("Tells the running screensaver process to lock the screen immediately" ), NULL((void*)0) | |||
73 | }, | |||
74 | { | |||
75 | "unlock", 'u', 0, G_OPTION_ARG_NONE, &do_unlock, | |||
76 | N_("Tells the running screensaver process to unlock the screen immediately")("Tells the running screensaver process to unlock the screen immediately" ), NULL((void*)0) | |||
77 | }, | |||
78 | { | |||
79 | "cycle", 'c', 0, G_OPTION_ARG_NONE, &do_cycle, | |||
80 | N_("If the screensaver is active then switch to another graphics demo")("If the screensaver is active then switch to another graphics demo" ), NULL((void*)0) | |||
81 | }, | |||
82 | { | |||
83 | "activate", 'a', 0, G_OPTION_ARG_NONE, &do_activate, | |||
84 | N_("Turn the screensaver on (blank the screen)")("Turn the screensaver on (blank the screen)"), NULL((void*)0) | |||
85 | }, | |||
86 | { | |||
87 | "deactivate", 'd', 0, G_OPTION_ARG_NONE, &do_deactivate, | |||
88 | N_("If the screensaver is active then deactivate it (un-blank the screen)")("If the screensaver is active then deactivate it (un-blank the screen)" ), NULL((void*)0) | |||
89 | }, | |||
90 | { | |||
91 | "poke", 'p', 0, G_OPTION_ARG_NONE, &do_poke, | |||
92 | N_("Poke the running screensaver to simulate user activity")("Poke the running screensaver to simulate user activity"), NULL((void*)0) | |||
93 | }, | |||
94 | { | |||
95 | "inhibit", 'i', 0, G_OPTION_ARG_NONE, &do_inhibit, | |||
96 | N_("Inhibit the screensaver from activating. Command blocks while inhibit is active.")("Inhibit the screensaver from activating. Command blocks while inhibit is active." ), NULL((void*)0) | |||
97 | }, | |||
98 | { | |||
99 | "application-name", 'n', 0, G_OPTION_ARG_STRING, &inhibit_application, | |||
100 | N_("The calling application that is inhibiting the screensaver")("The calling application that is inhibiting the screensaver" ), NULL((void*)0) | |||
101 | }, | |||
102 | { | |||
103 | "reason", 'r', 0, G_OPTION_ARG_STRING, &inhibit_reason, | |||
104 | N_("The reason for inhibiting the screensaver")("The reason for inhibiting the screensaver"), NULL((void*)0) | |||
105 | }, | |||
106 | { | |||
107 | "version", 'V', 0, G_OPTION_ARG_NONE, &do_version, | |||
108 | N_("Version of this application")("Version of this application"), NULL((void*)0) | |||
109 | }, | |||
110 | { NULL((void*)0) } | |||
111 | }; | |||
112 | ||||
113 | static GMainLoop *loop = NULL((void*)0); | |||
114 | ||||
115 | static gboolean | |||
116 | screensaver_is_running (DBusConnection *connection) | |||
117 | { | |||
118 | DBusError error; | |||
119 | gboolean exists; | |||
120 | ||||
121 | g_return_val_if_fail (connection != NULL, FALSE)do{ (void)0; }while (0); | |||
122 | ||||
123 | dbus_error_init (&error); | |||
124 | exists = dbus_bus_name_has_owner (connection, GS_SERVICE"org.cafe.ScreenSaver", &error); | |||
125 | if (dbus_error_is_set (&error)) | |||
126 | dbus_error_free (&error); | |||
127 | ||||
128 | return exists; | |||
129 | } | |||
130 | ||||
131 | static DBusMessage * | |||
132 | screensaver_send_message_inhibit (DBusConnection *connection, | |||
133 | const char *application, | |||
134 | const char *reason) | |||
135 | { | |||
136 | DBusMessage *message; | |||
137 | DBusMessage *reply; | |||
138 | DBusError error; | |||
139 | DBusMessageIter iter; | |||
140 | ||||
141 | g_return_val_if_fail (connection != NULL, NULL)do{ (void)0; }while (0); | |||
142 | ||||
143 | dbus_error_init (&error); | |||
144 | ||||
145 | message = dbus_message_new_method_call (GS_SERVICE"org.cafe.ScreenSaver", GS_PATH"/org/cafe/ScreenSaver", GS_INTERFACE"org.cafe.ScreenSaver", "Inhibit"); | |||
146 | if (message == NULL((void*)0)) | |||
147 | { | |||
148 | g_warning ("Couldn't allocate the dbus message"); | |||
149 | return NULL((void*)0); | |||
150 | } | |||
151 | ||||
152 | dbus_message_iter_init_append (message, &iter); | |||
153 | dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING((int) 's'), &application); | |||
154 | dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING((int) 's'), &reason); | |||
155 | ||||
156 | reply = dbus_connection_send_with_reply_and_block (connection, | |||
157 | message, | |||
158 | -1, &error); | |||
159 | if (dbus_error_is_set (&error)) | |||
160 | { | |||
161 | g_warning ("%s raised:\n %s\n\n", error.name, error.message); | |||
162 | reply = NULL((void*)0); | |||
163 | } | |||
164 | ||||
165 | dbus_connection_flush (connection); | |||
166 | ||||
167 | dbus_message_unref (message); | |||
168 | dbus_error_free (&error); | |||
169 | ||||
170 | return reply; | |||
171 | } | |||
172 | ||||
173 | static DBusMessage * | |||
174 | screensaver_send_message_bool (DBusConnection *connection, | |||
175 | const char *name, | |||
176 | gboolean value) | |||
177 | { | |||
178 | DBusMessage *message; | |||
179 | DBusMessage *reply; | |||
180 | DBusError error; | |||
181 | DBusMessageIter iter; | |||
182 | ||||
183 | g_return_val_if_fail (connection != NULL, NULL)do{ (void)0; }while (0); | |||
184 | g_return_val_if_fail (name != NULL, NULL)do{ (void)0; }while (0); | |||
185 | ||||
186 | dbus_error_init (&error); | |||
187 | ||||
188 | message = dbus_message_new_method_call (GS_SERVICE"org.cafe.ScreenSaver", GS_PATH"/org/cafe/ScreenSaver", GS_INTERFACE"org.cafe.ScreenSaver", name); | |||
189 | if (message == NULL((void*)0)) | |||
190 | { | |||
191 | g_warning ("Couldn't allocate the dbus message"); | |||
192 | return NULL((void*)0); | |||
193 | } | |||
194 | ||||
195 | dbus_message_iter_init_append (message, &iter); | |||
196 | dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN((int) 'b'), &value); | |||
197 | ||||
198 | reply = dbus_connection_send_with_reply_and_block (connection, | |||
199 | message, | |||
200 | -1, &error); | |||
201 | if (dbus_error_is_set (&error)) | |||
202 | { | |||
203 | g_warning ("%s raised:\n %s\n\n", error.name, error.message); | |||
204 | reply = NULL((void*)0); | |||
205 | } | |||
206 | ||||
207 | dbus_connection_flush (connection); | |||
208 | ||||
209 | dbus_message_unref (message); | |||
210 | dbus_error_free (&error); | |||
211 | ||||
212 | return reply; | |||
213 | } | |||
214 | ||||
215 | static DBusMessage * | |||
216 | screensaver_send_message_void (DBusConnection *connection, | |||
217 | const char *name, | |||
218 | gboolean expect_reply) | |||
219 | { | |||
220 | DBusMessage *message; | |||
221 | DBusMessage *reply; | |||
222 | DBusError error; | |||
223 | ||||
224 | g_return_val_if_fail (connection != NULL, NULL)do{ (void)0; }while (0); | |||
225 | g_return_val_if_fail (name != NULL, NULL)do{ (void)0; }while (0); | |||
226 | ||||
227 | dbus_error_init (&error); | |||
228 | ||||
229 | message = dbus_message_new_method_call (GS_SERVICE"org.cafe.ScreenSaver", GS_PATH"/org/cafe/ScreenSaver", GS_INTERFACE"org.cafe.ScreenSaver", name); | |||
230 | if (message == NULL((void*)0)) | |||
231 | { | |||
232 | g_warning ("Couldn't allocate the dbus message"); | |||
233 | return NULL((void*)0); | |||
234 | } | |||
235 | ||||
236 | if (! expect_reply) | |||
237 | { | |||
238 | if (!dbus_connection_send (connection, message, NULL((void*)0))) | |||
239 | g_warning ("could not send message"); | |||
240 | reply = NULL((void*)0); | |||
241 | } | |||
242 | else | |||
243 | { | |||
244 | reply = dbus_connection_send_with_reply_and_block (connection, | |||
245 | message, | |||
246 | -1, &error); | |||
247 | if (dbus_error_is_set (&error)) | |||
248 | { | |||
249 | g_warning ("%s raised:\n %s\n\n", error.name, error.message); | |||
250 | reply = NULL((void*)0); | |||
251 | } | |||
252 | } | |||
253 | dbus_connection_flush (connection); | |||
254 | ||||
255 | dbus_message_unref (message); | |||
256 | dbus_error_free (&error); | |||
257 | ||||
258 | return reply; | |||
259 | } | |||
260 | ||||
261 | static char ** | |||
262 | get_string_from_iter (DBusMessageIter *iter, | |||
263 | int *num_elements) | |||
264 | { | |||
265 | int count; | |||
266 | char **buffer; | |||
267 | ||||
268 | if (num_elements
| |||
269 | { | |||
270 | *num_elements = 0; | |||
271 | } | |||
272 | ||||
273 | count = 0; | |||
274 | buffer = (char **)malloc (sizeof (char *) * 8); | |||
275 | ||||
276 | if (buffer == NULL((void*)0)) | |||
277 | { | |||
278 | goto oom; | |||
279 | } | |||
280 | ||||
281 | buffer[0] = NULL((void*)0); | |||
282 | while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRING((int) 's')) | |||
283 | { | |||
284 | const char *value; | |||
285 | char *str; | |||
286 | ||||
287 | if ((count % 8) == 0 && count != 0) | |||
288 | { | |||
289 | buffer = realloc (buffer, sizeof (char *) * (count + 8)); | |||
290 | if (buffer == NULL((void*)0)) | |||
291 | { | |||
292 | goto oom; | |||
293 | } | |||
294 | } | |||
295 | ||||
296 | dbus_message_iter_get_basic (iter, &value); | |||
297 | str = strdup (value); | |||
298 | if (str == NULL((void*)0)) | |||
299 | { | |||
300 | goto oom; | |||
301 | } | |||
302 | ||||
303 | buffer[count] = str; | |||
304 | ||||
305 | dbus_message_iter_next (iter); | |||
306 | count++; | |||
307 | } | |||
308 | ||||
309 | if ((count % 8) == 0) | |||
310 | { | |||
311 | buffer = realloc (buffer, sizeof (char *) * (count + 1)); | |||
312 | if (buffer == NULL((void*)0)) | |||
313 | { | |||
314 | goto oom; | |||
315 | } | |||
316 | } | |||
317 | ||||
318 | buffer[count] = NULL((void*)0); | |||
319 | if (num_elements != NULL((void*)0)) | |||
320 | { | |||
321 | *num_elements = count; | |||
322 | } | |||
323 | return buffer; | |||
324 | ||||
325 | oom: | |||
326 | g_debug ("%s %d : error allocating memory\n", __FILE__"cafe-screensaver-command.c", __LINE__326); | |||
| ||||
327 | return NULL((void*)0); | |||
328 | ||||
329 | } | |||
330 | ||||
331 | static gboolean | |||
332 | do_command (DBusConnection *connection) | |||
333 | { | |||
334 | DBusMessage *reply; | |||
335 | ||||
336 | if (do_quit) | |||
| ||||
337 | { | |||
338 | reply = screensaver_send_message_void (connection, "Quit", FALSE(0)); | |||
339 | goto done; | |||
340 | } | |||
341 | ||||
342 | if (do_query) | |||
343 | { | |||
344 | DBusMessageIter iter; | |||
345 | DBusMessageIter array; | |||
346 | dbus_bool_t v; | |||
347 | ||||
348 | reply = screensaver_send_message_void (connection, "GetActive", TRUE(!(0))); | |||
349 | if (! reply) | |||
350 | { | |||
351 | g_message ("Did not receive a reply from the screensaver."); | |||
352 | goto done; | |||
353 | } | |||
354 | ||||
355 | dbus_message_iter_init (reply, &iter); | |||
356 | dbus_message_iter_get_basic (&iter, &v); | |||
357 | g_print (_("The screensaver is %s\n")gettext ("The screensaver is %s\n"), v ? _("active")gettext ("active") : _("inactive")gettext ("inactive")); | |||
358 | ||||
359 | dbus_message_unref (reply); | |||
360 | ||||
361 | reply = screensaver_send_message_void (connection, "GetInhibitors", TRUE(!(0))); | |||
362 | if (! reply) | |||
363 | { | |||
364 | g_message ("Did not receive a reply from screensaver."); | |||
365 | goto done; | |||
366 | } | |||
367 | ||||
368 | dbus_message_iter_init (reply, &iter); | |||
369 | dbus_message_iter_recurse (&iter, &array); | |||
370 | ||||
371 | if (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID((int) '\0')) | |||
372 | { | |||
373 | g_print (_("The screensaver is not inhibited\n")gettext ("The screensaver is not inhibited\n")); | |||
374 | } | |||
375 | else | |||
376 | { | |||
377 | char **inhibitors; | |||
378 | int i; | |||
379 | int num; | |||
380 | ||||
381 | g_print (_("The screensaver is being inhibited by:\n")gettext ("The screensaver is being inhibited by:\n")); | |||
382 | inhibitors = get_string_from_iter (&array, &num); | |||
383 | for (i = 0; i < num; i++) | |||
384 | { | |||
385 | g_print ("\t%s\n", inhibitors[i]); | |||
386 | } | |||
387 | g_strfreev (inhibitors); | |||
388 | } | |||
389 | ||||
390 | dbus_message_unref (reply); | |||
391 | } | |||
392 | ||||
393 | if (do_time) | |||
394 | { | |||
395 | DBusMessageIter iter; | |||
396 | dbus_bool_t v; | |||
397 | dbus_int32_t t; | |||
398 | ||||
399 | reply = screensaver_send_message_void (connection, "GetActive", TRUE(!(0))); | |||
400 | if (! reply) | |||
401 | { | |||
402 | g_message ("Did not receive a reply from the screensaver."); | |||
403 | goto done; | |||
404 | } | |||
405 | ||||
406 | dbus_message_iter_init (reply, &iter); | |||
407 | dbus_message_iter_get_basic (&iter, &v); | |||
408 | dbus_message_unref (reply); | |||
409 | ||||
410 | if (v) | |||
411 | { | |||
412 | ||||
413 | reply = screensaver_send_message_void (connection, "GetActiveTime", TRUE(!(0))); | |||
414 | dbus_message_iter_init (reply, &iter); | |||
415 | dbus_message_iter_get_basic (&iter, &t); | |||
416 | g_print (_("The screensaver has been active for %d seconds.\n")gettext ("The screensaver has been active for %d seconds.\n"), t); | |||
417 | ||||
418 | dbus_message_unref (reply); | |||
419 | } | |||
420 | else | |||
421 | { | |||
422 | g_print (_("The screensaver is not currently active.\n")gettext ("The screensaver is not currently active.\n")); | |||
423 | } | |||
424 | } | |||
425 | ||||
426 | if (do_lock) | |||
427 | { | |||
428 | reply = screensaver_send_message_void (connection, "Lock", FALSE(0)); | |||
429 | } | |||
430 | ||||
431 | if (do_unlock) | |||
432 | { | |||
433 | reply = screensaver_send_message_void (connection, "Unlock", FALSE(0)); | |||
434 | } | |||
435 | ||||
436 | if (do_cycle) | |||
437 | { | |||
438 | reply = screensaver_send_message_void (connection, "Cycle", FALSE(0)); | |||
439 | } | |||
440 | ||||
441 | if (do_poke) | |||
442 | { | |||
443 | reply = screensaver_send_message_void (connection, "SimulateUserActivity", FALSE(0)); | |||
444 | } | |||
445 | ||||
446 | if (do_activate) | |||
447 | { | |||
448 | reply = screensaver_send_message_bool (connection, "SetActive", TRUE(!(0))); | |||
449 | if (! reply) | |||
450 | { | |||
451 | g_message ("Did not receive a reply from the screensaver."); | |||
452 | goto done; | |||
453 | } | |||
454 | dbus_message_unref (reply); | |||
455 | } | |||
456 | ||||
457 | if (do_deactivate) | |||
458 | { | |||
459 | reply = screensaver_send_message_bool (connection, "SetActive", FALSE(0)); | |||
460 | if (! reply) | |||
461 | { | |||
462 | g_message ("Did not receive a reply from the screensaver."); | |||
463 | goto done; | |||
464 | } | |||
465 | dbus_message_unref (reply); | |||
466 | } | |||
467 | ||||
468 | if (do_inhibit) | |||
469 | { | |||
470 | reply = screensaver_send_message_inhibit (connection, | |||
471 | inhibit_application ? inhibit_application : "Unknown", | |||
472 | inhibit_reason ? inhibit_reason : "Unknown"); | |||
473 | if (! reply) | |||
474 | { | |||
475 | g_message ("Did not receive a reply from the screensaver."); | |||
476 | goto done; | |||
477 | } | |||
478 | dbus_message_unref (reply); | |||
479 | ||||
480 | return FALSE(0); | |||
481 | } | |||
482 | ||||
483 | done: | |||
484 | g_main_loop_quit (loop); | |||
485 | ||||
486 | return FALSE(0); | |||
487 | } | |||
488 | ||||
489 | int | |||
490 | main (int argc, | |||
491 | char **argv) | |||
492 | { | |||
493 | DBusConnection *connection; | |||
494 | DBusError dbus_error; | |||
495 | GOptionContext *context; | |||
496 | gboolean retval; | |||
497 | GError *error = NULL((void*)0); | |||
498 | ||||
499 | #ifdef ENABLE_NLS1 | |||
500 | bindtextdomain (GETTEXT_PACKAGE"cafe-screensaver", CAFELOCALEDIR"/usr/share/locale"); | |||
501 | # ifdef HAVE_BIND_TEXTDOMAIN_CODESET | |||
502 | bind_textdomain_codeset (GETTEXT_PACKAGE"cafe-screensaver", "UTF-8"); | |||
503 | # endif | |||
504 | textdomain (GETTEXT_PACKAGE"cafe-screensaver"); | |||
505 | #endif | |||
506 | ||||
507 | g_set_prgname (argv[0]); | |||
508 | ||||
509 | if (setlocale (LC_ALL6, "") == NULL((void*)0)) | |||
510 | g_warning ("Locale not understood by C library, internationalization will not work\n"); | |||
511 | ||||
512 | context = g_option_context_new (NULL((void*)0)); | |||
513 | g_option_context_add_main_entries (context, entries, NULL((void*)0)); | |||
514 | retval = g_option_context_parse (context, &argc, &argv, &error); | |||
515 | ||||
516 | g_option_context_free (context); | |||
517 | ||||
518 | if (! retval) | |||
519 | { | |||
520 | g_warning ("%s", error->message); | |||
521 | g_error_free (error); | |||
522 | exit (1); | |||
523 | } | |||
524 | ||||
525 | if (do_version) | |||
526 | { | |||
527 | g_print ("%s %s\n", argv [0], VERSION"1.25.0"); | |||
528 | exit (1); | |||
529 | } | |||
530 | ||||
531 | dbus_error_init (&dbus_error); | |||
532 | connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error); | |||
533 | if (! connection) | |||
534 | { | |||
535 | g_message ("Failed to connect to the D-BUS daemon: %s", dbus_error.message); | |||
536 | dbus_error_free (&dbus_error); | |||
537 | exit (1); | |||
538 | } | |||
539 | ||||
540 | dbus_connection_setup_with_g_main (connection, NULL((void*)0)); | |||
541 | ||||
542 | if (! screensaver_is_running (connection)) | |||
543 | { | |||
544 | g_message ("Screensaver is not running!"); | |||
545 | exit (1); | |||
546 | } | |||
547 | ||||
548 | g_idle_add ((GSourceFunc)do_command, connection); | |||
549 | ||||
550 | loop = g_main_loop_new (NULL((void*)0), FALSE(0)); | |||
551 | g_main_loop_run (loop); | |||
552 | ||||
553 | return 0; | |||
554 | } |