* src/vm/exceptions.c (new_exception_int): Removed.
[cacao.git] / src / native / native.c
1 /* src/native/native.c - table of native functions
2
3    Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    $Id: native.c 7941 2007-05-23 11:59:51Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33 #include <ctype.h>
34
35 #if !defined(WITH_STATIC_CLASSPATH)
36 # include <ltdl.h>
37 #endif
38
39 #include "vm/types.h"
40
41 #include "mm/memory.h"
42
43 #include "native/jni.h"
44 #include "native/native.h"
45
46 #include "native/vm/nativevm.h"
47
48 #include "threads/lock-common.h"
49
50 #include "toolbox/avl.h"
51 #include "toolbox/hashtable.h"
52 #include "toolbox/logging.h"
53
54 #include "vm/builtin.h"
55 #include "vm/exceptions.h"
56 #include "vm/global.h"
57 #include "vm/stringlocal.h"
58 #include "vm/vm.h"
59
60 #include "vm/jit/asmpart.h"
61 #include "vm/jit/jit.h"
62
63 #include "vmcore/loader.h"
64 #include "vmcore/options.h"
65 #include "vm/resolve.h"
66
67 #if defined(ENABLE_JVMTI)
68 #include "native/jvmti/cacaodbg.h"
69 #endif
70
71
72 /* include table of native functions ******************************************/
73
74 #if defined(WITH_STATIC_CLASSPATH)
75 # include "native/nativetable.inc"
76 #endif
77
78
79 /* tables for methods *********************************************************/
80
81 #if defined(WITH_STATIC_CLASSPATH)
82 #define NATIVETABLESIZE  (sizeof(nativetable)/sizeof(struct nativeref))
83
84 /* table for fast string comparison */
85 static nativecompref nativecomptable[NATIVETABLESIZE];
86
87 /* string comparsion table initialized */
88 static bool nativecompdone = false;
89 #endif
90
91
92 /* global variables ***********************************************************/
93
94 static avl_tree_t *tree_native_methods;
95 static hashtable *hashtable_library;
96
97
98 /* prototypes *****************************************************************/
99
100 static s4 native_tree_native_methods_comparator(const void *treenode, const void *node);
101
102
103 /* native_init *****************************************************************
104
105    Initializes the native subsystem.
106
107 *******************************************************************************/
108
109 bool native_init(void)
110 {
111 #if !defined(WITH_STATIC_CLASSPATH)
112         /* initialize libltdl */
113
114         if (lt_dlinit())
115                 vm_abort("native_init: lt_dlinit failed: %s\n", lt_dlerror());
116
117         /* initialize library hashtable, 10 entries should be enough */
118
119         hashtable_library = NEW(hashtable);
120
121         hashtable_create(hashtable_library, 10);
122 #endif
123
124         /* initialize the native methods table */
125
126         tree_native_methods = avl_create(&native_tree_native_methods_comparator);
127
128         /* register the intern native functions */
129
130         nativevm_init();
131
132         /* everything's ok */
133
134         return true;
135 }
136
137
138 /* native_tree_native_methods_comparator ***************************************
139
140    Comparison function for AVL tree of native methods.
141
142    IN:
143        treenode....node in the tree
144            node........node to compare with tree-node
145
146    RETURN VALUE:
147        -1, 0, +1
148
149 *******************************************************************************/
150
151 static s4 native_tree_native_methods_comparator(const void *treenode, const void *node)
152 {
153         const native_methods_node_t *treenmn;
154         const native_methods_node_t *nmn;
155
156         treenmn = treenode;
157         nmn     = node;
158
159         /* these are for walking the tree */
160
161         if (treenmn->classname < nmn->classname)
162                 return -1;
163         else if (treenmn->classname > nmn->classname)
164                 return 1;
165
166         if (treenmn->name < nmn->name)
167                 return -1;
168         else if (treenmn->name > nmn->name)
169                 return 1;
170
171         if (treenmn->descriptor < nmn->descriptor)
172                 return -1;
173         else if (treenmn->descriptor > nmn->descriptor)
174                 return 1;
175
176         /* all pointers are equal, we have found the entry */
177
178         return 0;
179 }
180
181
182 /* native_make_overloaded_function *********************************************
183
184    XXX
185
186 *******************************************************************************/
187
188 #if !defined(WITH_STATIC_CLASSPATH)
189 static utf *native_make_overloaded_function(utf *name, utf *descriptor)
190 {
191         char *newname;
192         s4    namelen;
193         char *utf_ptr;
194         u2    c;
195         s4    i;
196         s4    dumpsize;
197         utf  *u;
198
199         /* mark memory */
200
201         dumpsize = dump_size();
202
203         utf_ptr = descriptor->text;
204         namelen = strlen(name->text) + strlen("__") + strlen("0");
205
206         /* calculate additional length */
207
208         while ((c = utf_nextu2(&utf_ptr)) != ')') {
209                 switch (c) {
210                 case 'Z':
211                 case 'B':
212                 case 'C':
213                 case 'S':
214                 case 'I':
215                 case 'J':
216                 case 'F':
217                 case 'D':
218                         namelen++;
219                         break;
220                 case '[':
221                         namelen += 2;
222                         break;
223                 case 'L':
224                         namelen++;
225                         while (utf_nextu2(&utf_ptr) != ';')
226                                 namelen++;
227                         namelen += 2;
228                         break;
229                 case '(':
230                         break;
231                 default:
232                         assert(0);
233                 }
234         }
235
236         /* reallocate memory */
237
238         i = strlen(name->text);
239
240         newname = DMNEW(char, namelen);
241         MCOPY(newname, name->text, char, i);
242
243         utf_ptr = descriptor->text;
244
245         newname[i++] = '_';
246         newname[i++] = '_';
247
248         while ((c = utf_nextu2(&utf_ptr)) != ')') {
249                 switch (c) {
250                 case 'Z':
251                 case 'B':
252                 case 'C':
253                 case 'S':
254                 case 'J':
255                 case 'I':
256                 case 'F':
257                 case 'D':
258                         newname[i++] = c;
259                         break;
260                 case '[':
261                         newname[i++] = '_';
262                         newname[i++] = '3';
263                         break;
264                 case 'L':
265                         newname[i++] = 'L';
266                         while ((c = utf_nextu2(&utf_ptr)) != ';')
267                                 if (((c >= 'a') && (c <= 'z')) ||
268                                         ((c >= 'A') && (c <= 'Z')) ||
269                                         ((c >= '0') && (c <= '9')))
270                                         newname[i++] = c;
271                                 else
272                                         newname[i++] = '_';
273                         newname[i++] = '_';
274                         newname[i++] = '2';
275                         break;
276                 case '(':
277                         break;
278                 default:
279                         assert(0);
280                 }
281         }
282
283         /* close string */
284
285         newname[i] = '\0';
286
287         /* make a utf-string */
288
289         u = utf_new_char(newname);
290
291         /* release memory */
292
293         dump_release(dumpsize);
294
295         return u;
296 }
297
298
299 /* native_insert_char **********************************************************
300
301    Inserts the passed UTF character into the native method name.  If
302    necessary it is escaped properly.
303
304 *******************************************************************************/
305
306 static s4 native_insert_char(char *name, u4 pos, u2 c)
307 {
308         s4 val;
309         s4 i;
310
311         switch (c) {
312         case '/':
313         case '.':
314                 /* replace '/' or '.' with '_' */
315                 name[pos] = '_';
316                 break;
317
318         case '_':
319                 /* escape sequence for '_' is '_1' */
320                 name[pos]   = '_';
321                 name[++pos] = '1';
322                 break;
323
324         case ';':
325                 /* escape sequence for ';' is '_2' */
326                 name[pos]   = '_';
327                 name[++pos] = '2';
328                 break;
329
330         case '[':
331                 /* escape sequence for '[' is '_1' */
332                 name[pos]   = '_';
333                 name[++pos] = '3';
334                 break;
335
336         default:
337                 if (isalnum(c))
338                         name[pos] = c;
339                 else {
340                         /* unicode character */
341                         name[pos]   = '_';
342                         name[++pos] = '0';
343
344                         for (i = 0; i < 4; ++i) {
345                                 val = c & 0x0f;
346                                 name[pos + 4 - i] = (val > 10) ? ('a' + val - 10) : ('0' + val);
347                                 c >>= 4;
348                         }
349
350                         pos += 4;
351                 }
352                 break;
353         }
354
355         /* return the new buffer index */
356
357         return pos;
358 }
359
360
361 /* native_method_symbol ********************************************************
362
363    Generate a method-symbol string out of the class name and the
364    method name.
365
366 *******************************************************************************/
367
368 static utf *native_method_symbol(utf *classname, utf *methodname)
369 {
370         char *name;
371         s4    namelen;
372         char *utf_ptr;
373         char *utf_endptr;
374         u2    c;
375         u4    pos;
376         s4    dumpsize;
377         utf  *u;
378
379         /* mark memory */
380
381         dumpsize = dump_size();
382
383         /* Calculate length of native function name.  We multiply the
384            class and method name length by 6 as this is the maxium
385            escape-sequence that can be generated (unicode). */
386
387         namelen =
388                 strlen("Java_") +
389                 utf_get_number_of_u2s(classname) * 6 +
390                 strlen("_") +
391                 utf_get_number_of_u2s(methodname) * 6 +
392                 strlen("0");
393
394         /* allocate memory */
395
396         name = DMNEW(char, namelen);
397
398         /* generate name of native functions */
399
400         strcpy(name, "Java_");
401         pos = strlen("Java_");
402
403         utf_ptr    = classname->text;
404         utf_endptr = UTF_END(classname);
405
406         for (; utf_ptr < utf_endptr; utf_ptr++, pos++) {
407                 c   = *utf_ptr;
408                 pos = native_insert_char(name, pos, c);
409         }
410
411         /* seperator between class and method */
412
413         name[pos++] = '_';
414
415         utf_ptr    = methodname->text;
416         utf_endptr = UTF_END(methodname);
417
418         for (; utf_ptr < utf_endptr; utf_ptr++, pos++) {
419                 c   = *utf_ptr;
420                 pos = native_insert_char(name, pos, c);
421         }
422
423         /* close string */
424
425         name[pos] = '\0';
426
427         /* check for an buffer overflow */
428
429         assert(pos <= namelen);
430
431         /* make a utf-string */
432
433         u = utf_new_char(name);
434
435         /* release memory */
436
437         dump_release(dumpsize);
438
439         return u;
440 }
441
442
443 /* native_method_register ******************************************************
444
445    Register a native method in the native method table.
446
447 *******************************************************************************/
448
449 void native_method_register(utf *classname, JNINativeMethod *methods, s4 count)
450 {
451         native_methods_node_t *nmn;
452         utf                   *name;
453         utf                   *descriptor;
454         s4                     i;
455
456         /* insert all methods passed */
457
458         for (i = 0; i < count; i++) {
459                 if (opt_verbosejni) {
460                         printf("[Registering JNI native method ");
461                         utf_display_printable_ascii_classname(classname);
462                         printf(".%s]\n", methods[i].name);
463                 }
464
465                 /* generate the utf8 names */
466
467                 name       = utf_new_char(methods[i].name);
468                 descriptor = utf_new_char(methods[i].signature);
469
470                 /* allocate a new tree node */
471
472                 nmn = NEW(native_methods_node_t);
473
474                 nmn->classname  = classname;
475                 nmn->name       = name;
476                 nmn->descriptor = descriptor;
477                 nmn->function   = (functionptr) (ptrint) methods[i].fnPtr;
478
479                 /* insert the method into the tree */
480
481                 avl_insert(tree_native_methods, nmn);
482         }
483 }
484
485
486 /* native_method_find **********************************************************
487
488    Find a native method in the native method table.
489
490 *******************************************************************************/
491
492 static functionptr native_method_find(methodinfo *m)
493 {
494         native_methods_node_t  tmpnmn;
495         native_methods_node_t *nmn;
496
497         /* fill the temporary structure used for searching the tree */
498
499         tmpnmn.classname  = m->class->name;
500         tmpnmn.name       = m->name;
501         tmpnmn.descriptor = m->descriptor;
502
503         /* find the entry in the AVL-tree */
504
505         nmn = avl_find(tree_native_methods, &tmpnmn);
506
507         if (nmn == NULL)
508                 return NULL;
509
510         return nmn->function;
511 }
512
513
514 /* native_library_open *********************************************************
515
516    Open a native library with the given utf8 name.
517
518 *******************************************************************************/
519
520 #if !defined(WITH_STATIC_CLASSPATH)
521 lt_dlhandle native_library_open(utf *filename)
522 {
523         lt_dlhandle handle;
524
525         /* try to open the library */
526
527         handle = lt_dlopen(filename->text);
528
529         if (handle == NULL) {
530                 if (opt_verbose) {
531                         log_start();
532                         log_print("native_library_load: lt_dlopen failed: ");
533                         log_print(lt_dlerror());
534                         log_finish();
535                 }
536
537                 return NULL;
538         }
539
540         return handle;
541 }
542 #endif
543
544
545 /* native_library_add **********************************************************
546
547    Adds an entry to the native library hashtable.
548
549 *******************************************************************************/
550
551 #if !defined(WITH_STATIC_CLASSPATH)
552 void native_library_add(utf *filename, java_objectheader *loader,
553                                                 lt_dlhandle handle)
554 {
555         hashtable_library_loader_entry *le;
556         hashtable_library_name_entry   *ne; /* library name                       */
557         u4   key;                           /* hashkey                            */
558         u4   slot;                          /* slot in hashtable                  */
559
560         LOCK_MONITOR_ENTER(hashtable_library->header);
561
562         /* normally addresses are aligned to 4, 8 or 16 bytes */
563
564         key  = ((u4) (ptrint) loader) >> 4;        /* align to 16-byte boundaries */
565         slot = key & (hashtable_library->size - 1);
566         le   = hashtable_library->ptr[slot];
567
568         /* search external hash chain for the entry */
569
570         while (le) {
571                 if (le->loader == loader)
572                         break;
573
574                 le = le->hashlink;                  /* next element in external chain */
575         }
576
577         /* no loader found? create a new entry */
578
579         if (le == NULL) {
580                 le = NEW(hashtable_library_loader_entry);
581
582                 le->loader   = loader;
583                 le->namelink = NULL;
584
585                 /* insert entry into hashtable */
586
587                 le->hashlink =
588                         (hashtable_library_loader_entry *) hashtable_library->ptr[slot];
589                 hashtable_library->ptr[slot] = le;
590
591                 /* update number of hashtable-entries */
592
593                 hashtable_library->entries++;
594         }
595
596
597         /* search for library name */
598
599         ne = le->namelink;
600
601         while (ne) {
602                 if (ne->name == filename) {
603                         LOCK_MONITOR_EXIT(hashtable_library->header);
604
605                         return;
606                 }
607
608                 ne = ne->hashlink;                  /* next element in external chain */
609         }
610
611         /* not found? add the library name to the classloader */
612
613         ne = NEW(hashtable_library_name_entry);
614
615         ne->name   = filename;
616         ne->handle = handle;
617
618         /* insert entry into external chain */
619
620         ne->hashlink = le->namelink;
621         le->namelink = ne;
622
623         LOCK_MONITOR_EXIT(hashtable_library->header);
624 }
625 #endif /* !defined(WITH_STATIC_CLASSPATH) */
626
627
628 /* native_library_find *********************************************************
629
630    Find an entry in the native library hashtable.
631
632 *******************************************************************************/
633
634 #if !defined(WITH_STATIC_CLASSPATH)
635 hashtable_library_name_entry *native_library_find(utf *filename,
636                                                                                                   java_objectheader *loader)
637 {
638         hashtable_library_loader_entry *le;
639         hashtable_library_name_entry   *ne; /* library name                       */
640         u4   key;                           /* hashkey                            */
641         u4   slot;                          /* slot in hashtable                  */
642
643         /* normally addresses are aligned to 4, 8 or 16 bytes */
644
645         key  = ((u4) (ptrint) loader) >> 4;        /* align to 16-byte boundaries */
646         slot = key & (hashtable_library->size - 1);
647         le   = hashtable_library->ptr[slot];
648
649         /* search external hash chain for the entry */
650
651         while (le) {
652                 if (le->loader == loader)
653                         break;
654
655                 le = le->hashlink;                  /* next element in external chain */
656         }
657
658         /* no loader found? return NULL */
659
660         if (le == NULL)
661                 return NULL;
662
663         /* search for library name */
664
665         ne = le->namelink;
666
667         while (ne) {
668                 if (ne->name == filename)
669                         return ne;
670
671                 ne = ne->hashlink;                  /* next element in external chain */
672         }
673
674         /* return entry, if no entry was found, ne is NULL */
675
676         return ne;
677 }
678 #endif /* !defined(WITH_STATIC_CLASSPATH) */
679
680
681 /* native_findfunction *********************************************************
682
683    Looks up a method (must have the same class name, method name,
684    descriptor and 'static'ness) and returns a function pointer to it.
685    Returns: function pointer or NULL (if there is no such method)
686
687    Remark: For faster operation, the names/descriptors are converted
688    from C strings to Unicode the first time this function is called.
689
690 *******************************************************************************/
691
692 #if defined(WITH_STATIC_CLASSPATH)
693 functionptr native_findfunction(utf *cname, utf *mname, utf *desc,
694                                                                 bool isstatic)
695 {
696         /* entry of table for fast string comparison */
697         struct nativecompref *n;
698         s4 i;
699
700         isstatic = isstatic ? true : false;
701         
702         if (!nativecompdone) {
703                 for (i = 0; i < NATIVETABLESIZE; i++) {
704                         nativecomptable[i].classname  = 
705                                 utf_new_char(nativetable[i].classname);
706
707                         nativecomptable[i].methodname = 
708                                 utf_new_char(nativetable[i].methodname);
709
710                         nativecomptable[i].descriptor =
711                                 utf_new_char(nativetable[i].descriptor);
712
713                         nativecomptable[i].isstatic   = nativetable[i].isstatic;
714                         nativecomptable[i].func       = nativetable[i].func;
715                 }
716
717                 nativecompdone = true;
718         }
719
720         for (i = 0; i < NATIVETABLESIZE; i++) {
721                 n = &(nativecomptable[i]);
722
723                 if (cname == n->classname && mname == n->methodname &&
724                     desc == n->descriptor && isstatic == n->isstatic)
725                         return n->func;
726         }
727
728         /* no function was found, throw exception */
729
730         *exceptionptr =
731                         new_exception_utfmessage(string_java_lang_UnsatisfiedLinkError,
732                                                                          mname);
733
734         return NULL;
735 }
736 #endif /* defined(WITH_STATIC_CLASSPATH) */
737
738
739 /* native_resolve_function *****************************************************
740
741    Resolves a native function, maybe from a dynamic library.
742
743 *******************************************************************************/
744
745 functionptr native_resolve_function(methodinfo *m)
746 {
747         utf                            *name;
748         utf                            *newname;
749         functionptr                     f;
750         hashtable_library_loader_entry *le;
751         hashtable_library_name_entry   *ne;
752         u4                              key;    /* hashkey                        */
753         u4                              slot;   /* slot in hashtable              */
754
755         /* verbose output */
756
757         if (opt_verbosejni) {
758                 printf("[Dynamic-linking native method ");
759                 utf_display_printable_ascii_classname(m->class->name);
760                 printf(".");
761                 utf_display_printable_ascii(m->name);
762                 printf(" ... ");
763         }
764
765         /* generate method symbol string */
766
767         name = native_method_symbol(m->class->name, m->name);
768
769         /* generate overloaded function (having the types in it's name)           */
770
771         newname = native_make_overloaded_function(name, m->descriptor);
772
773         /* check the library hash entries of the classloader of the
774            methods's class  */
775
776         f = NULL;
777
778         /* normally addresses are aligned to 4, 8 or 16 bytes */
779
780         key  = ((u4) (ptrint) m->class->classloader) >> 4;    /* align to 16-byte */
781         slot = key & (hashtable_library->size - 1);
782         le   = hashtable_library->ptr[slot];
783
784         /* iterate through loaders in this hash slot */
785
786         while ((le != NULL) && (f == NULL)) {
787                 /* iterate through names in this loader */
788
789                 ne = le->namelink;
790                         
791                 while ((ne != NULL) && (f == NULL)) {
792                         f = (functionptr) (ptrint) lt_dlsym(ne->handle, name->text);
793
794                         if (f == NULL)
795                                 f = (functionptr) (ptrint) lt_dlsym(ne->handle, newname->text);
796
797                         ne = ne->hashlink;
798                 }
799
800                 le = le->hashlink;
801         }
802
803         if (f != NULL)
804                 if (opt_verbosejni)
805                         printf("JNI ]\n");
806
807         /* If not found, try to find the native function symbol in the
808            main program. */
809
810         if (f == NULL) {
811                 f = native_method_find(m);
812
813                 if (f != NULL)
814                         if (opt_verbosejni)
815                                 printf("internal ]\n");
816         }
817
818 #if defined(ENABLE_JVMTI)
819         /* fire Native Method Bind event */
820         if (jvmti) jvmti_NativeMethodBind(m, f, &f);
821 #endif
822
823         /* no symbol found? throw exception */
824
825         if (f == NULL) {
826                 if (opt_verbosejni)
827                         printf("failed ]\n");
828
829                 exceptions_throw_unsatisfiedlinkerror(m->name);
830         }
831
832         return f;
833 }
834 #endif /* !defined(WITH_STATIC_CLASSPATH) */
835
836
837 /* native_new_and_init *********************************************************
838
839    Creates a new object on the heap and calls the initializer.
840    Returns the object pointer or NULL if memory is exhausted.
841                         
842 *******************************************************************************/
843
844 java_objectheader *native_new_and_init(classinfo *c)
845 {
846         methodinfo *m;
847         java_objectheader *o;
848
849         if (c == NULL)
850                 vm_abort("native_new_and_init: c == NULL");
851
852         /* create object */
853
854         o = builtin_new(c);
855         
856         if (o == NULL)
857                 return NULL;
858
859         /* try to find the initializer */
860
861         m = class_findmethod(c, utf_init, utf_void__void);
862                                                       
863         /* ATTENTION: returning the object here is ok, since the class may
864        not have an initializer */
865
866         if (m == NULL)
867                 return o;
868
869         /* call initializer */
870
871         (void) vm_call_method(m, o);
872
873         return o;
874 }
875
876
877 java_objectheader *native_new_and_init_string(classinfo *c, java_objectheader *s)
878 {
879         methodinfo        *m;
880         java_objectheader *o;
881
882         if (c == NULL)
883                 vm_abort("native_new_and_init_string: c == NULL");
884
885         /* create object */
886
887         o = builtin_new(c);
888
889         if (o == NULL)
890                 return NULL;
891
892         /* find initializer */
893
894         m = class_resolveclassmethod(c,
895                                                                  utf_init,
896                                                                  utf_java_lang_String__void,
897                                                                  NULL,
898                                                                  true);
899
900         /* initializer not found */
901
902         if (m == NULL)
903                 return NULL;
904
905         /* call initializer */
906
907         (void) vm_call_method(m, o, s);
908
909         return o;
910 }
911
912
913 java_objectheader *native_new_and_init_throwable(classinfo *c, java_objectheader *t)
914 {
915         java_objectheader *o;
916         methodinfo        *m;
917
918         if (c == NULL)
919                 vm_abort("native_new_and_init_throwable: c == NULL");
920
921         /* create object */
922
923         o = builtin_new(c);
924         
925         if (o == NULL)
926                 return NULL;
927
928         /* find initializer */
929
930         m = class_findmethod(c, utf_init, utf_java_lang_Throwable__void);
931                                                       
932         /* initializer not found */
933
934         if (m == NULL)
935                 return NULL;
936
937         /* call initializer */
938
939         (void) vm_call_method(m, o, t);
940
941         return o;
942 }
943
944
945 /*
946  * These are local overrides for various environment variables in Emacs.
947  * Please do not remove this and leave it at the end of the file, where
948  * Emacs will automagically detect them.
949  * ---------------------------------------------------------------------
950  * Local variables:
951  * mode: c
952  * indent-tabs-mode: t
953  * c-basic-offset: 4
954  * tab-width: 4
955  * End:
956  */