merge branch
[cacao.git] / src / native / vm / java_lang_Class.c
1 /* src/native/vm/java_lang_Class.c - java/lang/Class
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 */
26
27
28 #include "config.h"
29
30 #include <assert.h>
31 #include <stdint.h>
32 #include <string.h>
33
34 #include "vm/types.h"
35
36 #include "mm/memory.h"
37
38 #include "native/jni.h"
39 #include "native/llni.h"
40 #include "native/native.h"
41
42 /* keep this order of the native includes */
43
44 #include "native/include/java_lang_String.h"
45
46 #if defined(ENABLE_JAVASE)
47 # if defined(WITH_CLASSPATH_SUN)
48 #  include "native/include/java_nio_ByteBuffer.h"       /* required by j.l.CL */
49 # endif
50 # include "native/include/java_lang_ClassLoader.h"
51 #endif
52
53 #include "native/include/java_lang_Object.h"
54 #include "native/include/java_lang_Class.h"
55
56 #if defined(ENABLE_JAVASE)
57 # include "native/include/java_lang_reflect_Constructor.h"
58 # include "native/include/java_lang_reflect_Field.h"
59 # include "native/include/java_lang_reflect_Method.h"
60 #endif
61
62 #include "native/vm/java_lang_Class.h"
63
64 #if defined(ENABLE_JAVASE)
65 # include "native/vm/reflect.h"
66 #endif
67
68 #include "toolbox/logging.h"
69
70 #include "vm/builtin.h"
71 #include "vm/exceptions.h"
72 #include "vm/global.h"
73 #include "vm/initialize.h"
74 #include "vm/primitive.h"
75 #include "vm/resolve.h"
76 #include "vm/stringlocal.h"
77
78 #include "vmcore/class.h"
79 #include "vmcore/loader.h"
80
81 #if defined(WITH_CLASSPATH_GNU) && defined(ENABLE_ANNOTATIONS)
82 #include "native/include/sun_reflect_ConstantPool.h"
83
84 #include "vm/vm.h"
85
86 #include "vmcore/annotation.h"
87 #endif
88
89 /*
90  * Class:     java/lang/Class
91  * Method:    getName
92  * Signature: ()Ljava/lang/String;
93  */
94 java_lang_String *_Jv_java_lang_Class_getName(java_lang_Class *klass)
95 {
96         classinfo        *c;
97         java_lang_String *s;
98         java_chararray_t *ca;
99         u4                i;
100
101         c = LLNI_classinfo_unwrap(klass);
102
103         /* create a java string */
104
105         s = (java_lang_String *) javastring_new(c->name);
106
107         if (s == NULL)
108                 return NULL;
109
110         /* return string where '/' is replaced by '.' */
111
112         LLNI_field_get_ref(s, value, ca);
113
114         for (i = 0; i < LLNI_array_size(ca); i++) {
115                 if (LLNI_array_direct(ca, i) == '/')
116                         LLNI_array_direct(ca, i) = '.';
117         }
118
119         return s;
120 }
121
122
123 /*
124  * Class:     java/lang/Class
125  * Method:    forName
126  * Signature: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;
127  */
128 #if defined(ENABLE_JAVASE)
129 java_lang_Class *_Jv_java_lang_Class_forName(java_lang_String *name, s4 initialize, java_lang_ClassLoader *loader)
130 #elif defined(ENABLE_JAVAME_CLDC1_1)
131 java_lang_Class *_Jv_java_lang_Class_forName(java_lang_String *name)
132 #endif
133 {
134 #if defined(ENABLE_JAVASE)
135         classloader *cl;
136 #endif
137         utf         *ufile;
138         utf         *uname;
139         classinfo   *c;
140         u2          *pos;
141         s4           i;
142
143 #if defined(ENABLE_JAVASE)
144         cl = (classloader *) loader;
145 #endif
146
147         /* illegal argument */
148
149         if (name == NULL) {
150                 exceptions_throw_nullpointerexception();
151                 return NULL;
152         }
153
154         /* create utf string in which '.' is replaced by '/' */
155
156         ufile = javastring_toutf((java_handle_t *) name, true);
157         uname = javastring_toutf((java_handle_t *) name, false);
158
159         /* name must not contain '/' (mauve test) */
160
161         for (i = 0, pos = LLNI_field_direct(name, value)->data + LLNI_field_direct(name, offset); i < LLNI_field_direct(name, count); i++, pos++) {
162                 if (*pos == '/') {
163                         exceptions_throw_classnotfoundexception(uname);
164                         return NULL;
165                 }
166         }
167
168         /* try to load, ... */
169
170 #if defined(ENABLE_JAVASE)
171         c = load_class_from_classloader(ufile, cl);
172 #elif defined(ENABLE_JAVAME_CLDC1_1)
173         c = load_class_bootstrap(ufile);
174 #endif
175
176         if (c == NULL)
177             return NULL;
178
179         /* link, ... */
180
181         if (!link_class(c))
182                 return NULL;
183         
184         /* ...and initialize it, if required */
185
186 #if defined(ENABLE_JAVASE)
187         if (initialize)
188 #endif
189                 if (!initialize_class(c))
190                         return NULL;
191
192         return LLNI_classinfo_wrap(c);
193 }
194
195
196 /*
197  * Class:     java/lang/Class
198  * Method:    isInstance
199  * Signature: (Ljava/lang/Object;)Z
200  */
201 s4 _Jv_java_lang_Class_isInstance(java_lang_Class *klass, java_lang_Object *o)
202 {
203         classinfo     *c;
204         java_handle_t *ob;
205
206         c = LLNI_classinfo_unwrap(klass);
207         ob = (java_handle_t *) o;
208
209         if (!(c->state & CLASS_LINKED))
210                 if (!link_class(c))
211                         return 0;
212
213         return builtin_instanceof(ob, c);
214 }
215
216
217 /*
218  * Class:     java/lang/Class
219  * Method:    isAssignableFrom
220  * Signature: (Ljava/lang/Class;)Z
221  */
222 s4 _Jv_java_lang_Class_isAssignableFrom(java_lang_Class *klass, java_lang_Class *c)
223 {
224         classinfo *kc;
225         classinfo *cc;
226
227         kc = LLNI_classinfo_unwrap(klass);
228         cc = LLNI_classinfo_unwrap(c);
229
230         if (cc == NULL) {
231                 exceptions_throw_nullpointerexception();
232                 return 0;
233         }
234
235         if (!(kc->state & CLASS_LINKED))
236                 if (!link_class(kc))
237                         return 0;
238
239         if (!(cc->state & CLASS_LINKED))
240                 if (!link_class(cc))
241                         return 0;
242
243         return class_isanysubclass(cc, kc);
244 }
245
246
247 /*
248  * Class:     java/lang/Class
249  * Method:    isInterface
250  * Signature: ()Z
251  */
252 JNIEXPORT int32_t JNICALL _Jv_java_lang_Class_isInterface(JNIEnv *env, java_lang_Class *this)
253 {
254         classinfo *c;
255
256         c = LLNI_classinfo_unwrap(this);
257
258         return class_is_interface(c);
259 }
260
261
262 #if defined(ENABLE_JAVASE)
263
264 /*
265  * Class:     java/lang/Class
266  * Method:    isPrimitive
267  * Signature: ()Z
268  */
269 s4 _Jv_java_lang_Class_isPrimitive(java_lang_Class *klass)
270 {
271         classinfo *c;
272
273         c = LLNI_classinfo_unwrap(klass);
274
275         return class_is_primitive(c);
276 }
277
278
279 /*
280  * Class:     java/lang/Class
281  * Method:    getSuperclass
282  * Signature: ()Ljava/lang/Class;
283  */
284 java_lang_Class *_Jv_java_lang_Class_getSuperclass(java_lang_Class *klass)
285 {
286         classinfo *c;
287         classinfo *super;
288
289         c = LLNI_classinfo_unwrap(klass);
290
291         super = class_get_superclass(c);
292
293         return LLNI_classinfo_wrap(super);
294 }
295
296
297 /*
298  * Class:     java/lang/Class
299  * Method:    getInterfaces
300  * Signature: ()[Ljava/lang/Class;
301  */
302 java_handle_objectarray_t *_Jv_java_lang_Class_getInterfaces(java_lang_Class *klass)
303 {
304         classinfo                 *c;
305         java_handle_objectarray_t *oa;
306
307         c = LLNI_classinfo_unwrap(klass);
308
309         oa = class_get_interfaces(c);
310
311         return oa;
312 }
313
314
315 /*
316  * Class:     java/lang/Class
317  * Method:    getModifiers
318  * Signature: (Z)I
319  */
320 s4 _Jv_java_lang_Class_getModifiers(java_lang_Class *klass, s4 ignoreInnerClassesAttrib)
321 {
322         classinfo             *c;
323         classref_or_classinfo  inner;
324         classref_or_classinfo  outer;
325         utf                   *innername;
326         s4                     i;
327
328         c = LLNI_classinfo_unwrap(klass);
329
330         if (!ignoreInnerClassesAttrib && (c->innerclasscount != 0)) {
331                 /* search for passed class as inner class */
332
333                 for (i = 0; i < c->innerclasscount; i++) {
334                         inner = c->innerclass[i].inner_class;
335                         outer = c->innerclass[i].outer_class;
336
337                         /* Check if inner is a classref or a real class and get
338                the name of the structure */
339
340                         innername = IS_CLASSREF(inner) ? inner.ref->name : inner.cls->name;
341
342                         /* innerclass is this class */
343
344                         if (innername == c->name) {
345                                 /* has the class actually an outer class? */
346
347                                 if (outer.any)
348                                         /* return flags got from the outer class file */
349                                         return c->innerclass[i].flags & ACC_CLASS_REFLECT_MASK;
350                                 else
351                                         return c->flags & ACC_CLASS_REFLECT_MASK;
352                         }
353                 }
354         }
355
356         /* passed class is no inner class or it was not requested */
357
358         return c->flags & ACC_CLASS_REFLECT_MASK;
359 }
360
361
362 /*
363  * Class:     java/lang/Class
364  * Method:    getDeclaringClass
365  * Signature: ()Ljava/lang/Class;
366  */
367 java_lang_Class *_Jv_java_lang_Class_getDeclaringClass(java_lang_Class *klass)
368 {
369         classinfo *c;
370         classinfo *dc;
371
372         c = LLNI_classinfo_unwrap(klass);
373
374         dc = class_get_declaringclass(c);
375
376         return LLNI_classinfo_wrap(dc);
377 }
378
379
380 /*
381  * Class:     java/lang/Class
382  * Method:    getDeclaredClasses
383  * Signature: (Z)[Ljava/lang/Class;
384  */
385 java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredClasses(java_lang_Class *klass, s4 publicOnly)
386 {
387         classinfo                 *c;
388         java_handle_objectarray_t *oa;
389
390         c = LLNI_classinfo_unwrap(klass);
391
392         oa = class_get_declaredclasses(c, publicOnly);
393
394         return oa;
395 }
396
397
398 /*
399  * Class:     java/lang/Class
400  * Method:    getDeclaredFields
401  * Signature: (Z)[Ljava/lang/reflect/Field;
402  */
403 java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredFields(java_lang_Class *klass, s4 publicOnly)
404 {
405         classinfo                 *c;
406         java_handle_objectarray_t *oa;          /* result: array of field-objects */
407         fieldinfo                 *f;
408         java_lang_reflect_Field   *rf;
409         s4 public_fields;                    /* number of elements in field-array */
410         s4 pos;
411         s4 i;
412
413         c = LLNI_classinfo_unwrap(klass);
414
415         /* determine number of fields */
416
417         for (i = 0, public_fields = 0; i < c->fieldscount; i++)
418                 if ((c->fields[i].flags & ACC_PUBLIC) || (publicOnly == 0))
419                         public_fields++;
420
421         /* create array of fields */
422
423         oa = builtin_anewarray(public_fields, class_java_lang_reflect_Field);
424
425         if (oa == NULL)
426                 return NULL;
427
428         /* get the fields and store in the array */
429
430         for (i = 0, pos = 0; i < c->fieldscount; i++) {
431                 f = &(c->fields[i]);
432
433                 if ((f->flags & ACC_PUBLIC) || (publicOnly == 0)) {
434                         /* create Field object */
435
436                         rf = reflect_field_new(f);
437
438                         /* store object into array */
439
440                         LLNI_objectarray_element_set(oa, pos, rf);
441                         pos++;
442                 }
443         }
444
445         return oa;
446 }
447
448
449 /*
450  * Class:     java/lang/Class
451  * Method:    getDeclaredMethods
452  * Signature: (Z)[Ljava/lang/reflect/Method;
453  */
454 java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredMethods(java_lang_Class *klass, s4 publicOnly)
455 {
456         classinfo                 *c;
457         java_lang_reflect_Method  *rm;
458         java_handle_objectarray_t *oa;         /* result: array of Method-objects */
459         methodinfo                *m;     /* the current method to be represented */
460         s4 public_methods;               /* number of public methods of the class */
461         s4 pos;
462         s4 i;
463
464         c = LLNI_classinfo_unwrap(klass);
465
466         public_methods = 0;
467
468         /* JOWENN: array classes do not declare methods according to mauve
469            test.  It should be considered, if we should return to my old
470            clone method overriding instead of declaring it as a member
471            function. */
472
473         if (class_is_array(c))
474                 return builtin_anewarray(0, class_java_lang_reflect_Method);
475
476         /* determine number of methods */
477
478         for (i = 0; i < c->methodscount; i++) {
479                 m = &c->methods[i];
480
481                 if (((m->flags & ACC_PUBLIC) || (publicOnly == false)) &&
482                         ((m->name != utf_init) && (m->name != utf_clinit)) &&
483                         !(m->flags & ACC_MIRANDA))
484                         public_methods++;
485         }
486
487         oa = builtin_anewarray(public_methods, class_java_lang_reflect_Method);
488
489         if (oa == NULL)
490                 return NULL;
491
492         for (i = 0, pos = 0; i < c->methodscount; i++) {
493                 m = &c->methods[i];
494
495                 if (((m->flags & ACC_PUBLIC) || (publicOnly == false)) && 
496                         ((m->name != utf_init) && (m->name != utf_clinit)) &&
497                         !(m->flags & ACC_MIRANDA)) {
498                         /* create Method object */
499
500                         rm = reflect_method_new(m);
501
502                         /* store object into array */
503
504                         LLNI_objectarray_element_set(oa, pos, rm);
505                         pos++;
506                 }
507         }
508
509         return oa;
510 }
511
512
513 /*
514  * Class:     java/lang/Class
515  * Method:    getDeclaredConstructors
516  * Signature: (Z)[Ljava/lang/reflect/Constructor;
517  */
518 java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredConstructors(java_lang_Class *klass, s4 publicOnly)
519 {
520         classinfo                     *c;
521         methodinfo                    *m; /* the current method to be represented */
522         java_handle_objectarray_t     *oa;     /* result: array of Method-objects */
523         java_lang_reflect_Constructor *rc;
524         s4 public_methods;               /* number of public methods of the class */
525         s4 pos;
526         s4 i;
527
528         c = LLNI_classinfo_unwrap(klass);
529
530         /* determine number of constructors */
531
532         for (i = 0, public_methods = 0; i < c->methodscount; i++) {
533                 m = &c->methods[i];
534
535                 if (((m->flags & ACC_PUBLIC) || (publicOnly == 0)) &&
536                         (m->name == utf_init))
537                         public_methods++;
538         }
539
540         oa = builtin_anewarray(public_methods, class_java_lang_reflect_Constructor);
541
542         if (oa == NULL)
543                 return NULL;
544
545         for (i = 0, pos = 0; i < c->methodscount; i++) {
546                 m = &c->methods[i];
547
548                 if (((m->flags & ACC_PUBLIC) || (publicOnly == 0)) &&
549                         (m->name == utf_init)) {
550                         /* create Constructor object */
551
552                         rc = reflect_constructor_new(m);
553
554                         /* store object into array */
555
556                         LLNI_objectarray_element_set(oa, pos, rc);
557                         pos++;
558                 }
559         }
560
561         return oa;
562 }
563
564
565 /*
566  * Class:     java/lang/Class
567  * Method:    getClassLoader
568  * Signature: ()Ljava/lang/ClassLoader;
569  */
570 java_lang_ClassLoader *_Jv_java_lang_Class_getClassLoader(java_lang_Class *klass)
571 {
572         classinfo *c;
573
574         c = LLNI_classinfo_unwrap(klass);
575
576         return (java_lang_ClassLoader *) c->classloader;
577 }
578
579 #endif /* defined(ENABLE_JAVASE) */
580
581
582 /*
583  * Class:     java/lang/Class
584  * Method:    isArray
585  * Signature: ()Z
586  */
587 JNIEXPORT int32_t JNICALL _Jv_java_lang_Class_isArray(JNIEnv *env, java_lang_Class *this)
588 {
589         classinfo *c;
590
591         c = LLNI_classinfo_unwrap(this);
592
593         return class_is_array(c);
594 }
595
596
597 #if defined(ENABLE_JAVASE)
598
599 /*
600  * Class:     java/lang/Class
601  * Method:    throwException
602  * Signature: (Ljava/lang/Throwable;)V
603  */
604 void _Jv_java_lang_Class_throwException(java_lang_Throwable *t)
605 {
606         java_handle_t *o;
607
608         o = (java_handle_t *) t;
609
610         exceptions_set_exception(o);
611 }
612
613
614 #if defined(WITH_CLASSPATH_GNU) && defined(ENABLE_ANNOTATIONS)
615 /*
616  * Class:     java/lang/Class
617  * Method:    getDeclaredAnnotations
618  * Signature: (Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;
619  */
620 java_handle_objectarray_t *_Jv_java_lang_Class_getDeclaredAnnotations(java_lang_Class* klass)
621 {
622         classinfo                *c               = NULL;
623         static methodinfo        *m_parseAnnotationsIntoArray   = NULL;
624         utf                      *utf_parseAnnotationsIntoArray = NULL;
625         utf                      *utf_desc        = NULL;
626         java_handle_bytearray_t  *annotations     = NULL;
627         sun_reflect_ConstantPool *constantPool    = NULL;
628         java_lang_Object         *constantPoolOop = (java_lang_Object*)klass;
629
630         if (klass == NULL) {
631                 exceptions_throw_nullpointerexception();
632                 return NULL;
633         }
634         
635         c = LLNI_classinfo_unwrap(klass);
636
637         /* get annotations: */
638         annotations = class_get_annotations(c);
639
640         constantPool = 
641                 (sun_reflect_ConstantPool*)native_new_and_init(
642                         class_sun_reflect_ConstantPool);
643         
644         if(constantPool == NULL) {
645                 /* out of memory */
646                 return NULL;
647         }
648
649         LLNI_field_set_ref(constantPool, constantPoolOop, constantPoolOop);
650
651         /* only resolve the method the first time */
652         if (m_parseAnnotationsIntoArray == NULL) {
653                 utf_parseAnnotationsIntoArray = utf_new_char("parseAnnotationsIntoArray");
654                 utf_desc = utf_new_char(
655                         "([BLsun/reflect/ConstantPool;Ljava/lang/Class;)"
656                         "[Ljava/lang/annotation/Annotation;");
657
658                 if (utf_parseAnnotationsIntoArray == NULL || utf_desc == NULL) {
659                         /* out of memory */
660                         return NULL;
661                 }
662
663                 m_parseAnnotationsIntoArray = class_resolveclassmethod(
664                         class_sun_reflect_annotation_AnnotationParser,
665                         utf_parseAnnotationsIntoArray,
666                         utf_desc,
667                         class_java_lang_Class,
668                         true);
669
670                 if (m_parseAnnotationsIntoArray == NULL) {
671                         /* method not found */
672                         return NULL;
673                 }
674         }
675
676         return (java_handle_objectarray_t*)vm_call_method(
677                 m_parseAnnotationsIntoArray, NULL,
678                 annotations, constantPool, klass);
679 }
680 #endif
681
682
683 /* _Jv_java_lang_Class_getEnclosingMethod_intern *******************************
684
685    Helper function for _Jv_java_lang_Class_getEnclosingConstructor and
686    _Jv_java_lang_Class_getEnclosingMethod.
687
688 *******************************************************************************/
689
690 static methodinfo *_Jv_java_lang_Class_getEnclosingMethod_intern(classinfo *c)
691 {
692         constant_nameandtype *cn;
693         classinfo            *ec;
694         methodinfo           *m;
695
696         /* get enclosing class and method */
697
698         ec = class_get_enclosingclass(c);
699         cn = c->enclosingmethod;
700
701         /* check for enclosing class and method */
702
703         if (ec == NULL)
704                 return NULL;
705
706         if (cn == NULL)
707                 return NULL;
708
709         /* find method in enclosing class */
710
711         m = class_findmethod(ec, cn->name, cn->descriptor);
712
713         if (m == NULL) {
714                 exceptions_throw_internalerror("Enclosing method doesn't exist");
715                 return NULL;
716         }
717
718         return m;
719 }
720
721
722 /*
723  * Class:     java/lang/Class
724  * Method:    getEnclosingConstructor
725  * Signature: (Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
726  */
727 java_lang_reflect_Constructor *_Jv_java_lang_Class_getEnclosingConstructor(java_lang_Class *klass)
728 {
729         classinfo                     *c;
730         methodinfo                    *m;
731         java_lang_reflect_Constructor *rc;
732
733         c = LLNI_classinfo_unwrap(klass);
734
735         /* get enclosing method */
736
737         m = _Jv_java_lang_Class_getEnclosingMethod_intern(c);
738
739         if (m == NULL)
740                 return NULL;
741
742         /* check for <init> */
743
744         if (m->name != utf_init)
745                 return NULL;
746
747         /* create Constructor object */
748
749         rc = reflect_constructor_new(m);
750
751         return rc;
752 }
753
754
755 /*
756  * Class:     java/lang/Class
757  * Method:    getEnclosingMethod
758  * Signature: (Ljava/lang/Class;)Ljava/lang/reflect/Method;
759  */
760 java_lang_reflect_Method *_Jv_java_lang_Class_getEnclosingMethod(java_lang_Class *klass)
761 {
762         classinfo                *c;
763         methodinfo               *m;
764         java_lang_reflect_Method *rm;
765
766         c = LLNI_classinfo_unwrap(klass);
767
768         /* get enclosing method */
769
770         m = _Jv_java_lang_Class_getEnclosingMethod_intern(c);
771
772         if (m == NULL)
773                 return NULL;
774
775         /* check for <init> */
776
777         if (m->name == utf_init)
778                 return NULL;
779
780         /* create java.lang.reflect.Method object */
781
782         rm = reflect_method_new(m);
783
784         return rm;
785 }
786
787
788 /*
789  * Class:     java/lang/Class
790  * Method:    getClassSignature
791  * Signature: (Ljava/lang/Class;)Ljava/lang/String;
792  */
793 java_lang_String *_Jv_java_lang_Class_getClassSignature(java_lang_Class* klass)
794 {
795         classinfo     *c;
796         java_handle_t *o;
797
798         c = LLNI_classinfo_unwrap(klass);
799
800         if (c->signature == NULL)
801                 return NULL;
802
803         o = javastring_new(c->signature);
804
805         /* in error case o is NULL */
806
807         return (java_lang_String *) o;
808 }
809
810 #endif /* ENABLE_JAVASE */
811
812
813 /*
814  * These are local overrides for various environment variables in Emacs.
815  * Please do not remove this and leave it at the end of the file, where
816  * Emacs will automagically detect them.
817  * ---------------------------------------------------------------------
818  * Local variables:
819  * mode: c
820  * indent-tabs-mode: t
821  * c-basic-offset: 4
822  * tab-width: 4
823  * End:
824  * vim:noexpandtab:sw=4:ts=4:
825  */