Use SYMBOL_VALUE/ SET_SYMBOL_VALUE macros instead of accessing
[bpt/emacs.git] / src / abbrev.c
1 /* Primitives for word-abbrev mode.
2 Copyright (C) 1985, 1986, 1993, 1996, 1998, 2001
3 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22
23 #include <config.h>
24 #include <stdio.h>
25 #include "lisp.h"
26 #include "commands.h"
27 #include "buffer.h"
28 #include "window.h"
29 #include "charset.h"
30 #include "syntax.h"
31
32 /* An abbrev table is an obarray.
33 Each defined abbrev is represented by a symbol in that obarray
34 whose print name is the abbreviation.
35 The symbol's value is a string which is the expansion.
36 If its function definition is non-nil, it is called
37 after the expansion is done.
38 The plist slot of the abbrev symbol is its usage count. */
39
40 /* List of all abbrev-table name symbols:
41 symbols whose values are abbrev tables. */
42
43 Lisp_Object Vabbrev_table_name_list;
44
45 /* The table of global abbrevs. These are in effect
46 in any buffer in which abbrev mode is turned on. */
47
48 Lisp_Object Vglobal_abbrev_table;
49
50 /* The local abbrev table used by default (in Fundamental Mode buffers) */
51
52 Lisp_Object Vfundamental_mode_abbrev_table;
53
54 /* Set nonzero when an abbrev definition is changed */
55
56 int abbrevs_changed;
57
58 int abbrev_all_caps;
59
60 /* Non-nil => use this location as the start of abbrev to expand
61 (rather than taking the word before point as the abbrev) */
62
63 Lisp_Object Vabbrev_start_location;
64
65 /* Buffer that Vabbrev_start_location applies to */
66 Lisp_Object Vabbrev_start_location_buffer;
67
68 /* The symbol representing the abbrev most recently expanded */
69
70 Lisp_Object Vlast_abbrev;
71
72 /* A string for the actual text of the abbrev most recently expanded.
73 This has more info than Vlast_abbrev since case is significant. */
74
75 Lisp_Object Vlast_abbrev_text;
76
77 /* Character address of start of last abbrev expanded */
78
79 int last_abbrev_point;
80
81 /* Hook to run before expanding any abbrev. */
82
83 Lisp_Object Vpre_abbrev_expand_hook, Qpre_abbrev_expand_hook;
84 \f
85 DEFUN ("make-abbrev-table", Fmake_abbrev_table, Smake_abbrev_table, 0, 0, 0,
86 "Create a new, empty abbrev table object.")
87 ()
88 {
89 return Fmake_vector (make_number (59), make_number (0));
90 }
91
92 DEFUN ("clear-abbrev-table", Fclear_abbrev_table, Sclear_abbrev_table, 1, 1, 0,
93 "Undefine all abbrevs in abbrev table TABLE, leaving it empty.")
94 (table)
95 Lisp_Object table;
96 {
97 int i, size;
98
99 CHECK_VECTOR (table, 0);
100 size = XVECTOR (table)->size;
101 abbrevs_changed = 1;
102 for (i = 0; i < size; i++)
103 XVECTOR (table)->contents[i] = make_number (0);
104 return Qnil;
105 }
106 \f
107 DEFUN ("define-abbrev", Fdefine_abbrev, Sdefine_abbrev, 3, 5, 0,
108 "Define an abbrev in TABLE named NAME, to expand to EXPANSION and call HOOK.\n\
109 NAME must be a string.\n\
110 EXPANSION should usually be a string.\n\
111 To undefine an abbrev, define it with EXPANSION = nil.\n\
112 If HOOK is non-nil, it should be a function of no arguments;\n\
113 it is called after EXPANSION is inserted.\n\
114 If EXPANSION is not a string, the abbrev is a special one,\n\
115 which does not expand in the usual way but only runs HOOK.\n\
116 COUNT, if specified, initializes the abbrev's usage-count\n\
117 which is incremented each time the abbrev is used.")
118 (table, name, expansion, hook, count)
119 Lisp_Object table, name, expansion, hook, count;
120 {
121 Lisp_Object sym, oexp, ohook, tem;
122 CHECK_VECTOR (table, 0);
123 CHECK_STRING (name, 1);
124
125 if (NILP (count))
126 count = make_number (0);
127 else
128 CHECK_NUMBER (count, 0);
129
130 sym = Fintern (name, table);
131
132 oexp = SYMBOL_VALUE (sym);
133 ohook = XSYMBOL (sym)->function;
134 if (!((EQ (oexp, expansion)
135 || (STRINGP (oexp) && STRINGP (expansion)
136 && (tem = Fstring_equal (oexp, expansion), !NILP (tem))))
137 &&
138 (EQ (ohook, hook)
139 || (tem = Fequal (ohook, hook), !NILP (tem)))))
140 abbrevs_changed = 1;
141
142 Fset (sym, expansion);
143 Ffset (sym, hook);
144 Fsetplist (sym, count);
145
146 return name;
147 }
148
149 DEFUN ("define-global-abbrev", Fdefine_global_abbrev, Sdefine_global_abbrev, 2, 2,
150 "sDefine global abbrev: \nsExpansion for %s: ",
151 "Define ABBREV as a global abbreviation for EXPANSION.")
152 (abbrev, expansion)
153 Lisp_Object abbrev, expansion;
154 {
155 Fdefine_abbrev (Vglobal_abbrev_table, Fdowncase (abbrev),
156 expansion, Qnil, make_number (0));
157 return abbrev;
158 }
159
160 DEFUN ("define-mode-abbrev", Fdefine_mode_abbrev, Sdefine_mode_abbrev, 2, 2,
161 "sDefine mode abbrev: \nsExpansion for %s: ",
162 "Define ABBREV as a mode-specific abbreviation for EXPANSION.")
163 (abbrev, expansion)
164 Lisp_Object abbrev, expansion;
165 {
166 if (NILP (current_buffer->abbrev_table))
167 error ("Major mode has no abbrev table");
168
169 Fdefine_abbrev (current_buffer->abbrev_table, Fdowncase (abbrev),
170 expansion, Qnil, make_number (0));
171 return abbrev;
172 }
173
174 DEFUN ("abbrev-symbol", Fabbrev_symbol, Sabbrev_symbol, 1, 2, 0,
175 "Return the symbol representing abbrev named ABBREV.\n\
176 This symbol's name is ABBREV, but it is not the canonical symbol of that name;\n\
177 it is interned in an abbrev-table rather than the normal obarray.\n\
178 The value is nil if that abbrev is not defined.\n\
179 Optional second arg TABLE is abbrev table to look it up in.\n\
180 The default is to try buffer's mode-specific abbrev table, then global table.")
181 (abbrev, table)
182 Lisp_Object abbrev, table;
183 {
184 Lisp_Object sym;
185 CHECK_STRING (abbrev, 0);
186 if (!NILP (table))
187 sym = Fintern_soft (abbrev, table);
188 else
189 {
190 sym = Qnil;
191 if (!NILP (current_buffer->abbrev_table))
192 sym = Fintern_soft (abbrev, current_buffer->abbrev_table);
193 if (NILP (SYMBOL_VALUE (sym)))
194 sym = Qnil;
195 if (NILP (sym))
196 sym = Fintern_soft (abbrev, Vglobal_abbrev_table);
197 }
198 if (NILP (SYMBOL_VALUE (sym)))
199 return Qnil;
200 return sym;
201 }
202
203 DEFUN ("abbrev-expansion", Fabbrev_expansion, Sabbrev_expansion, 1, 2, 0,
204 "Return the string that ABBREV expands into in the current buffer.\n\
205 Optionally specify an abbrev table as second arg;\n\
206 then ABBREV is looked up in that table only.")
207 (abbrev, table)
208 Lisp_Object abbrev, table;
209 {
210 Lisp_Object sym;
211 sym = Fabbrev_symbol (abbrev, table);
212 if (NILP (sym)) return sym;
213 return Fsymbol_value (sym);
214 }
215 \f
216 /* Expand the word before point, if it is an abbrev.
217 Returns 1 if an expansion is done. */
218
219 DEFUN ("expand-abbrev", Fexpand_abbrev, Sexpand_abbrev, 0, 0, "",
220 "Expand the abbrev before point, if there is an abbrev there.\n\
221 Effective when explicitly called even when `abbrev-mode' is nil.\n\
222 Returns the abbrev symbol, if expansion took place.")
223 ()
224 {
225 register char *buffer, *p;
226 int wordstart, wordend;
227 register int wordstart_byte, wordend_byte, idx;
228 int whitecnt;
229 int uccount = 0, lccount = 0;
230 register Lisp_Object sym;
231 Lisp_Object expansion, hook, tem;
232 Lisp_Object value;
233
234 value = Qnil;
235
236 if (!NILP (Vrun_hooks))
237 call1 (Vrun_hooks, Qpre_abbrev_expand_hook);
238
239 wordstart = 0;
240 if (!(BUFFERP (Vabbrev_start_location_buffer)
241 && XBUFFER (Vabbrev_start_location_buffer) == current_buffer))
242 Vabbrev_start_location = Qnil;
243 if (!NILP (Vabbrev_start_location))
244 {
245 tem = Vabbrev_start_location;
246 CHECK_NUMBER_COERCE_MARKER (tem, 0);
247 wordstart = XINT (tem);
248 Vabbrev_start_location = Qnil;
249 if (wordstart < BEGV || wordstart > ZV)
250 wordstart = 0;
251 if (wordstart && wordstart != ZV)
252 {
253 wordstart_byte = CHAR_TO_BYTE (wordstart);
254 if (FETCH_BYTE (wordstart_byte) == '-')
255 del_range (wordstart, wordstart + 1);
256 }
257 }
258 if (!wordstart)
259 wordstart = scan_words (PT, -1);
260
261 if (!wordstart)
262 return value;
263
264 wordstart_byte = CHAR_TO_BYTE (wordstart);
265 wordend = scan_words (wordstart, 1);
266 if (!wordend)
267 return value;
268
269 if (wordend > PT)
270 wordend = PT;
271
272 wordend_byte = CHAR_TO_BYTE (wordend);
273 whitecnt = PT - wordend;
274 if (wordend <= wordstart)
275 return value;
276
277 p = buffer = (char *) alloca (wordend_byte - wordstart_byte);
278
279 for (idx = wordstart_byte; idx < wordend_byte; idx++)
280 {
281 /* ??? This loop needs to go by characters! */
282 register int c = FETCH_BYTE (idx);
283 if (UPPERCASEP (c))
284 c = DOWNCASE (c), uccount++;
285 else if (! NOCASEP (c))
286 lccount++;
287 *p++ = c;
288 }
289
290 if (VECTORP (current_buffer->abbrev_table))
291 sym = oblookup (current_buffer->abbrev_table, buffer,
292 wordend - wordstart, wordend_byte - wordstart_byte);
293 else
294 XSETFASTINT (sym, 0);
295 if (INTEGERP (sym) || NILP (SYMBOL_VALUE (sym)))
296 sym = oblookup (Vglobal_abbrev_table, buffer,
297 wordend - wordstart, wordend_byte - wordstart_byte);
298 if (INTEGERP (sym) || NILP (SYMBOL_VALUE (sym)))
299 return value;
300
301 if (INTERACTIVE && !EQ (minibuf_window, selected_window))
302 {
303 /* Add an undo boundary, in case we are doing this for
304 a self-inserting command which has avoided making one so far. */
305 SET_PT (wordend);
306 Fundo_boundary ();
307 }
308
309 Vlast_abbrev_text
310 = Fbuffer_substring (make_number (wordstart), make_number (wordend));
311
312 /* Now sym is the abbrev symbol. */
313 Vlast_abbrev = sym;
314 value = sym;
315 last_abbrev_point = wordstart;
316
317 if (INTEGERP (XSYMBOL (sym)->plist))
318 XSETINT (XSYMBOL (sym)->plist,
319 XINT (XSYMBOL (sym)->plist) + 1); /* Increment use count */
320
321 /* If this abbrev has an expansion, delete the abbrev
322 and insert the expansion. */
323 expansion = SYMBOL_VALUE (sym);
324 if (STRINGP (expansion))
325 {
326 SET_PT (wordstart);
327
328 del_range_both (wordstart, wordstart_byte, wordend, wordend_byte, 1);
329
330 insert_from_string (expansion, 0, 0, XSTRING (expansion)->size,
331 STRING_BYTES (XSTRING (expansion)), 1);
332 SET_PT (PT + whitecnt);
333
334 if (uccount && !lccount)
335 {
336 /* Abbrev was all caps */
337 /* If expansion is multiple words, normally capitalize each word */
338 /* This used to be if (!... && ... >= ...) Fcapitalize; else Fupcase
339 but Megatest 68000 compiler can't handle that */
340 if (!abbrev_all_caps)
341 if (scan_words (PT, -1) > scan_words (wordstart, 1))
342 {
343 Fupcase_initials_region (make_number (wordstart),
344 make_number (PT));
345 goto caped;
346 }
347 /* If expansion is one word, or if user says so, upcase it all. */
348 Fupcase_region (make_number (wordstart), make_number (PT));
349 caped: ;
350 }
351 else if (uccount)
352 {
353 /* Abbrev included some caps. Cap first initial of expansion */
354 int pos = wordstart_byte;
355
356 /* Find the initial. */
357 while (pos < PT_BYTE
358 && SYNTAX (*BUF_BYTE_ADDRESS (current_buffer, pos)) != Sword)
359 pos++;
360
361 /* Change just that. */
362 pos = BYTE_TO_CHAR (pos);
363 Fupcase_initials_region (make_number (pos), make_number (pos + 1));
364 }
365 }
366
367 hook = XSYMBOL (sym)->function;
368 if (!NILP (hook))
369 {
370 Lisp_Object expanded, prop;
371
372 /* If the abbrev has a hook function, run it. */
373 expanded = call0 (hook);
374
375 /* In addition, if the hook function is a symbol with a a
376 non-nil `no-self-insert' property, let the value it returned
377 specify whether we consider that an expansion took place. If
378 it returns nil, no expansion has been done. */
379
380 if (SYMBOLP (hook)
381 && NILP (expanded)
382 && (prop = Fget (hook, intern ("no-self-insert")),
383 !NILP (prop)))
384 value = Qnil;
385 }
386
387 return value;
388 }
389
390 DEFUN ("unexpand-abbrev", Funexpand_abbrev, Sunexpand_abbrev, 0, 0, "",
391 "Undo the expansion of the last abbrev that expanded.\n\
392 This differs from ordinary undo in that other editing done since then\n\
393 is not undone.")
394 ()
395 {
396 int opoint = PT;
397 int adjust = 0;
398 if (last_abbrev_point < BEGV
399 || last_abbrev_point > ZV)
400 return Qnil;
401 SET_PT (last_abbrev_point);
402 if (STRINGP (Vlast_abbrev_text))
403 {
404 /* This isn't correct if Vlast_abbrev->function was used
405 to do the expansion */
406 Lisp_Object val;
407 int zv_before;
408
409 val = SYMBOL_VALUE (Vlast_abbrev);
410 if (!STRINGP (val))
411 error ("value of abbrev-symbol must be a string");
412 zv_before = ZV;
413 del_range_byte (PT_BYTE, PT_BYTE + STRING_BYTES (XSTRING (val)), 1);
414 /* Don't inherit properties here; just copy from old contents. */
415 insert_from_string (Vlast_abbrev_text, 0, 0,
416 XSTRING (Vlast_abbrev_text)->size,
417 STRING_BYTES (XSTRING (Vlast_abbrev_text)), 0);
418 Vlast_abbrev_text = Qnil;
419 /* Total number of characters deleted. */
420 adjust = ZV - zv_before;
421 }
422 SET_PT (last_abbrev_point < opoint ? opoint + adjust : opoint);
423 return Qnil;
424 }
425 \f
426 static void
427 write_abbrev (sym, stream)
428 Lisp_Object sym, stream;
429 {
430 Lisp_Object name;
431 if (NILP (SYMBOL_VALUE (sym)))
432 return;
433 insert (" (", 5);
434 XSETSTRING (name, XSYMBOL (sym)->name);
435 Fprin1 (name, stream);
436 insert (" ", 1);
437 Fprin1 (SYMBOL_VALUE (sym), stream);
438 insert (" ", 1);
439 Fprin1 (XSYMBOL (sym)->function, stream);
440 insert (" ", 1);
441 Fprin1 (XSYMBOL (sym)->plist, stream);
442 insert (")\n", 2);
443 }
444
445 static void
446 describe_abbrev (sym, stream)
447 Lisp_Object sym, stream;
448 {
449 Lisp_Object one;
450
451 if (NILP (SYMBOL_VALUE (sym)))
452 return;
453 one = make_number (1);
454 Fprin1 (Fsymbol_name (sym), stream);
455 Findent_to (make_number (15), one);
456 Fprin1 (XSYMBOL (sym)->plist, stream);
457 Findent_to (make_number (20), one);
458 Fprin1 (SYMBOL_VALUE (sym), stream);
459 if (!NILP (XSYMBOL (sym)->function))
460 {
461 Findent_to (make_number (45), one);
462 Fprin1 (XSYMBOL (sym)->function, stream);
463 }
464 Fterpri (stream);
465 }
466
467 DEFUN ("insert-abbrev-table-description", Finsert_abbrev_table_description,
468 Sinsert_abbrev_table_description, 1, 2, 0,
469 "Insert before point a full description of abbrev table named NAME.\n\
470 NAME is a symbol whose value is an abbrev table.\n\
471 If optional 2nd arg READABLE is non-nil, a human-readable description\n\
472 is inserted. Otherwise the description is an expression,\n\
473 a call to `define-abbrev-table', which would\n\
474 define the abbrev table NAME exactly as it is currently defined.")
475 (name, readable)
476 Lisp_Object name, readable;
477 {
478 Lisp_Object table;
479 Lisp_Object stream;
480
481 CHECK_SYMBOL (name, 0);
482 table = Fsymbol_value (name);
483 CHECK_VECTOR (table, 0);
484
485 XSETBUFFER (stream, current_buffer);
486
487 if (!NILP (readable))
488 {
489 insert_string ("(");
490 Fprin1 (name, stream);
491 insert_string (")\n\n");
492 map_obarray (table, describe_abbrev, stream);
493 insert_string ("\n\n");
494 }
495 else
496 {
497 insert_string ("(define-abbrev-table '");
498 Fprin1 (name, stream);
499 insert_string (" '(\n");
500 map_obarray (table, write_abbrev, stream);
501 insert_string (" ))\n\n");
502 }
503
504 return Qnil;
505 }
506 \f
507 DEFUN ("define-abbrev-table", Fdefine_abbrev_table, Sdefine_abbrev_table,
508 2, 2, 0,
509 "Define TABLENAME (a symbol) as an abbrev table name.\n\
510 Define abbrevs in it according to DEFINITIONS, which is a list of elements\n\
511 of the form (ABBREVNAME EXPANSION HOOK USECOUNT).")
512 (tablename, definitions)
513 Lisp_Object tablename, definitions;
514 {
515 Lisp_Object name, exp, hook, count;
516 Lisp_Object table, elt;
517
518 CHECK_SYMBOL (tablename, 0);
519 table = Fboundp (tablename);
520 if (NILP (table) || (table = Fsymbol_value (tablename), NILP (table)))
521 {
522 table = Fmake_abbrev_table ();
523 Fset (tablename, table);
524 Vabbrev_table_name_list = Fcons (tablename, Vabbrev_table_name_list);
525 }
526 CHECK_VECTOR (table, 0);
527
528 for (; !NILP (definitions); definitions = Fcdr (definitions))
529 {
530 elt = Fcar (definitions);
531 name = Fcar (elt); elt = Fcdr (elt);
532 exp = Fcar (elt); elt = Fcdr (elt);
533 hook = Fcar (elt); elt = Fcdr (elt);
534 count = Fcar (elt);
535 Fdefine_abbrev (table, name, exp, hook, count);
536 }
537 return Qnil;
538 }
539 \f
540 void
541 syms_of_abbrev ()
542 {
543 DEFVAR_LISP ("abbrev-table-name-list", &Vabbrev_table_name_list,
544 "List of symbols whose values are abbrev tables.");
545 Vabbrev_table_name_list = Fcons (intern ("fundamental-mode-abbrev-table"),
546 Fcons (intern ("global-abbrev-table"),
547 Qnil));
548
549 DEFVAR_LISP ("global-abbrev-table", &Vglobal_abbrev_table,
550 "The abbrev table whose abbrevs affect all buffers.\n\
551 Each buffer may also have a local abbrev table.\n\
552 If it does, the local table overrides the global one\n\
553 for any particular abbrev defined in both.");
554 Vglobal_abbrev_table = Fmake_abbrev_table ();
555
556 DEFVAR_LISP ("fundamental-mode-abbrev-table", &Vfundamental_mode_abbrev_table,
557 "The abbrev table of mode-specific abbrevs for Fundamental Mode.");
558 Vfundamental_mode_abbrev_table = Fmake_abbrev_table ();
559 current_buffer->abbrev_table = Vfundamental_mode_abbrev_table;
560 buffer_defaults.abbrev_table = Vfundamental_mode_abbrev_table;
561
562 DEFVAR_LISP ("last-abbrev", &Vlast_abbrev,
563 "The abbrev-symbol of the last abbrev expanded. See `abbrev-symbol'.");
564
565 DEFVAR_LISP ("last-abbrev-text", &Vlast_abbrev_text,
566 "The exact text of the last abbrev expanded.\n\
567 nil if the abbrev has already been unexpanded.");
568
569 DEFVAR_INT ("last-abbrev-location", &last_abbrev_point,
570 "The location of the start of the last abbrev expanded.");
571
572 Vlast_abbrev = Qnil;
573 Vlast_abbrev_text = Qnil;
574 last_abbrev_point = 0;
575
576 DEFVAR_LISP ("abbrev-start-location", &Vabbrev_start_location,
577 "Buffer position for `expand-abbrev' to use as the start of the abbrev.\n\
578 nil means use the word before point as the abbrev.\n\
579 Calling `expand-abbrev' sets this to nil.");
580 Vabbrev_start_location = Qnil;
581
582 DEFVAR_LISP ("abbrev-start-location-buffer", &Vabbrev_start_location_buffer,
583 "Buffer that `abbrev-start-location' has been set for.\n\
584 Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.");
585 Vabbrev_start_location_buffer = Qnil;
586
587 DEFVAR_PER_BUFFER ("local-abbrev-table", &current_buffer->abbrev_table, Qnil,
588 "Local (mode-specific) abbrev table of current buffer.");
589
590 DEFVAR_BOOL ("abbrevs-changed", &abbrevs_changed,
591 "Set non-nil by defining or altering any word abbrevs.\n\
592 This causes `save-some-buffers' to offer to save the abbrevs.");
593 abbrevs_changed = 0;
594
595 DEFVAR_BOOL ("abbrev-all-caps", &abbrev_all_caps,
596 "*Set non-nil means expand multi-word abbrevs all caps if abbrev was so.");
597 abbrev_all_caps = 0;
598
599 DEFVAR_LISP ("pre-abbrev-expand-hook", &Vpre_abbrev_expand_hook,
600 "Function or functions to be called before abbrev expansion is done.\n\
601 This is the first thing that `expand-abbrev' does, and so this may change\n\
602 the current abbrev table before abbrev lookup happens.");
603 Vpre_abbrev_expand_hook = Qnil;
604 Qpre_abbrev_expand_hook = intern ("pre-abbrev-expand-hook");
605 staticpro (&Qpre_abbrev_expand_hook);
606
607 defsubr (&Smake_abbrev_table);
608 defsubr (&Sclear_abbrev_table);
609 defsubr (&Sdefine_abbrev);
610 defsubr (&Sdefine_global_abbrev);
611 defsubr (&Sdefine_mode_abbrev);
612 defsubr (&Sabbrev_expansion);
613 defsubr (&Sabbrev_symbol);
614 defsubr (&Sexpand_abbrev);
615 defsubr (&Sunexpand_abbrev);
616 defsubr (&Sinsert_abbrev_table_description);
617 defsubr (&Sdefine_abbrev_table);
618 }