ctags.rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/Gemfile +2 -0
  2. data/Rakefile +23 -0
  3. data/ctags.rb.gemspec +23 -0
  4. data/ext/.gitignore +3 -0
  5. data/ext/extconf.rb +15 -0
  6. data/ext/vendor/exuberant-ctags/.gitignore +6 -0
  7. data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
  8. data/ext/vendor/exuberant-ctags/COPYING +340 -0
  9. data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
  10. data/ext/vendor/exuberant-ctags/FAQ +371 -0
  11. data/ext/vendor/exuberant-ctags/INSTALL +215 -0
  12. data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
  13. data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
  14. data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
  15. data/ext/vendor/exuberant-ctags/NEWS +871 -0
  16. data/ext/vendor/exuberant-ctags/README +73 -0
  17. data/ext/vendor/exuberant-ctags/ant.c +42 -0
  18. data/ext/vendor/exuberant-ctags/argproc.c +505 -0
  19. data/ext/vendor/exuberant-ctags/args.c +274 -0
  20. data/ext/vendor/exuberant-ctags/args.h +63 -0
  21. data/ext/vendor/exuberant-ctags/asm.c +387 -0
  22. data/ext/vendor/exuberant-ctags/asp.c +328 -0
  23. data/ext/vendor/exuberant-ctags/awk.c +81 -0
  24. data/ext/vendor/exuberant-ctags/basic.c +203 -0
  25. data/ext/vendor/exuberant-ctags/beta.c +321 -0
  26. data/ext/vendor/exuberant-ctags/c.c +2932 -0
  27. data/ext/vendor/exuberant-ctags/cobol.c +50 -0
  28. data/ext/vendor/exuberant-ctags/config.h.in +277 -0
  29. data/ext/vendor/exuberant-ctags/configure +7704 -0
  30. data/ext/vendor/exuberant-ctags/configure.ac +532 -0
  31. data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
  32. data/ext/vendor/exuberant-ctags/ctags.h +28 -0
  33. data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
  34. data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
  35. data/ext/vendor/exuberant-ctags/debug.c +113 -0
  36. data/ext/vendor/exuberant-ctags/debug.h +70 -0
  37. data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
  38. data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
  39. data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
  40. data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
  41. data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
  42. data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
  43. data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
  44. data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
  45. data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
  46. data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
  47. data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
  48. data/ext/vendor/exuberant-ctags/entry.c +847 -0
  49. data/ext/vendor/exuberant-ctags/entry.h +103 -0
  50. data/ext/vendor/exuberant-ctags/erlang.c +189 -0
  51. data/ext/vendor/exuberant-ctags/flex.c +2243 -0
  52. data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
  53. data/ext/vendor/exuberant-ctags/general.h +127 -0
  54. data/ext/vendor/exuberant-ctags/get.c +669 -0
  55. data/ext/vendor/exuberant-ctags/get.h +50 -0
  56. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
  57. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
  58. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
  59. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
  60. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
  61. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
  62. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
  63. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
  64. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
  65. data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
  66. data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
  67. data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
  68. data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
  69. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
  70. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
  71. data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
  72. data/ext/vendor/exuberant-ctags/html.c +49 -0
  73. data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
  74. data/ext/vendor/exuberant-ctags/keyword.c +258 -0
  75. data/ext/vendor/exuberant-ctags/keyword.h +34 -0
  76. data/ext/vendor/exuberant-ctags/lisp.c +139 -0
  77. data/ext/vendor/exuberant-ctags/lregex.c +704 -0
  78. data/ext/vendor/exuberant-ctags/lua.c +133 -0
  79. data/ext/vendor/exuberant-ctags/mac.c +273 -0
  80. data/ext/vendor/exuberant-ctags/magic.diff +21 -0
  81. data/ext/vendor/exuberant-ctags/main.c +584 -0
  82. data/ext/vendor/exuberant-ctags/main.h +32 -0
  83. data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
  84. data/ext/vendor/exuberant-ctags/make.c +217 -0
  85. data/ext/vendor/exuberant-ctags/matlab.c +44 -0
  86. data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
  87. data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
  88. data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
  89. data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
  90. data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
  91. data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
  92. data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
  93. data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
  94. data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
  95. data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
  96. data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
  97. data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
  98. data/ext/vendor/exuberant-ctags/options.c +1842 -0
  99. data/ext/vendor/exuberant-ctags/options.h +155 -0
  100. data/ext/vendor/exuberant-ctags/parse.c +677 -0
  101. data/ext/vendor/exuberant-ctags/parse.h +129 -0
  102. data/ext/vendor/exuberant-ctags/parsers.h +63 -0
  103. data/ext/vendor/exuberant-ctags/pascal.c +267 -0
  104. data/ext/vendor/exuberant-ctags/perl.c +382 -0
  105. data/ext/vendor/exuberant-ctags/php.c +237 -0
  106. data/ext/vendor/exuberant-ctags/python.c +771 -0
  107. data/ext/vendor/exuberant-ctags/qdos.c +106 -0
  108. data/ext/vendor/exuberant-ctags/read.c +569 -0
  109. data/ext/vendor/exuberant-ctags/read.h +116 -0
  110. data/ext/vendor/exuberant-ctags/readtags.c +959 -0
  111. data/ext/vendor/exuberant-ctags/readtags.h +252 -0
  112. data/ext/vendor/exuberant-ctags/rexx.c +39 -0
  113. data/ext/vendor/exuberant-ctags/routines.c +891 -0
  114. data/ext/vendor/exuberant-ctags/routines.h +134 -0
  115. data/ext/vendor/exuberant-ctags/ruby.c +408 -0
  116. data/ext/vendor/exuberant-ctags/scheme.c +111 -0
  117. data/ext/vendor/exuberant-ctags/sh.c +115 -0
  118. data/ext/vendor/exuberant-ctags/slang.c +41 -0
  119. data/ext/vendor/exuberant-ctags/sml.c +212 -0
  120. data/ext/vendor/exuberant-ctags/sort.c +230 -0
  121. data/ext/vendor/exuberant-ctags/sort.h +32 -0
  122. data/ext/vendor/exuberant-ctags/source.mak +122 -0
  123. data/ext/vendor/exuberant-ctags/sql.c +2112 -0
  124. data/ext/vendor/exuberant-ctags/strlist.c +281 -0
  125. data/ext/vendor/exuberant-ctags/strlist.h +54 -0
  126. data/ext/vendor/exuberant-ctags/tcl.c +116 -0
  127. data/ext/vendor/exuberant-ctags/tex.c +524 -0
  128. data/ext/vendor/exuberant-ctags/verilog.c +340 -0
  129. data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
  130. data/ext/vendor/exuberant-ctags/vim.c +636 -0
  131. data/ext/vendor/exuberant-ctags/vstring.c +232 -0
  132. data/ext/vendor/exuberant-ctags/vstring.h +85 -0
  133. data/ext/vendor/exuberant-ctags/yacc.c +40 -0
  134. data/lib/ctags/exuberant.rb +45 -0
  135. data/lib/ctags/version.rb +3 -0
  136. data/lib/ctags.rb +6 -0
  137. data/test/test_ctags.rb +24 -0
  138. metadata +233 -0
@@ -0,0 +1,258 @@
1
+ /*
2
+ * $Id: keyword.c 715 2009-07-06 03:31:00Z dhiebert $
3
+ *
4
+ * Copyright (c) 1998-2002, Darren Hiebert
5
+ *
6
+ * This source code is released for free distribution under the terms of the
7
+ * GNU General Public License.
8
+ *
9
+ * Manages a keyword hash.
10
+ */
11
+
12
+ /*
13
+ * INCLUDE FILES
14
+ */
15
+ #include "general.h" /* must always come first */
16
+
17
+ #include <string.h>
18
+
19
+ #include "debug.h"
20
+ #include "keyword.h"
21
+ #include "options.h"
22
+ #include "routines.h"
23
+
24
+ /*
25
+ * MACROS
26
+ */
27
+ #define HASH_EXPONENT 7 /* must be less than 17 */
28
+
29
+ /*
30
+ * DATA DECLARATIONS
31
+ */
32
+ typedef struct sHashEntry {
33
+ struct sHashEntry *next;
34
+ const char *string;
35
+ langType language;
36
+ int value;
37
+ } hashEntry;
38
+
39
+ /*
40
+ * DATA DEFINITIONS
41
+ */
42
+ static const unsigned int TableSize = 1 << HASH_EXPONENT;
43
+ static hashEntry **HashTable = NULL;
44
+
45
+ /*
46
+ * FUNCTION DEFINITIONS
47
+ */
48
+
49
+ static hashEntry **getHashTable (void)
50
+ {
51
+ static boolean allocated = FALSE;
52
+
53
+ if (! allocated)
54
+ {
55
+ unsigned int i;
56
+
57
+ HashTable = xMalloc (TableSize, hashEntry*);
58
+
59
+ for (i = 0 ; i < TableSize ; ++i)
60
+ HashTable [i] = NULL;
61
+
62
+ allocated = TRUE;
63
+ }
64
+ return HashTable;
65
+ }
66
+
67
+ static hashEntry *getHashTableEntry (unsigned long hashedValue)
68
+ {
69
+ hashEntry **const table = getHashTable ();
70
+ hashEntry *entry;
71
+
72
+ Assert (hashedValue < TableSize);
73
+ entry = table [hashedValue];
74
+
75
+ return entry;
76
+ }
77
+
78
+ static unsigned long hashValue (const char *const string)
79
+ {
80
+ unsigned long value = 0;
81
+ const unsigned char *p;
82
+
83
+ Assert (string != NULL);
84
+
85
+ /* We combine the various words of the multiword key using the method
86
+ * described on page 512 of Vol. 3 of "The Art of Computer Programming".
87
+ */
88
+ for (p = (const unsigned char *) string ; *p != '\0' ; ++p)
89
+ {
90
+ value <<= 1;
91
+ if (value & 0x00000100L)
92
+ value = (value & 0x000000ffL) + 1L;
93
+ value ^= *p;
94
+ }
95
+ /* Algorithm from page 509 of Vol. 3 of "The Art of Computer Programming"
96
+ * Treats "value" as a 16-bit integer plus 16-bit fraction.
97
+ */
98
+ value *= 40503L; /* = 2^16 * 0.6180339887 ("golden ratio") */
99
+ value &= 0x0000ffffL; /* keep fractional part */
100
+ value >>= 16 - HASH_EXPONENT; /* scale up by hash size and move down */
101
+
102
+ return value;
103
+ }
104
+
105
+ static hashEntry *newEntry (
106
+ const char *const string, langType language, int value)
107
+ {
108
+ hashEntry *const entry = xMalloc (1, hashEntry);
109
+
110
+ entry->next = NULL;
111
+ entry->string = string;
112
+ entry->language = language;
113
+ entry->value = value;
114
+
115
+ return entry;
116
+ }
117
+
118
+ /* Note that it is assumed that a "value" of zero means an undefined keyword
119
+ * and clients of this function should observe this. Also, all keywords added
120
+ * should be added in lower case. If we encounter a case-sensitive language
121
+ * whose keywords are in upper case, we will need to redesign this.
122
+ */
123
+ extern void addKeyword (const char *const string, langType language, int value)
124
+ {
125
+ const unsigned long hashedValue = hashValue (string);
126
+ hashEntry *entry = getHashTableEntry (hashedValue);
127
+
128
+ if (entry == NULL)
129
+ {
130
+ hashEntry **const table = getHashTable ();
131
+ table [hashedValue] = newEntry (string, language, value);
132
+ }
133
+ else
134
+ {
135
+ hashEntry *prev = NULL;
136
+
137
+ while (entry != NULL)
138
+ {
139
+ if (language == entry->language &&
140
+ strcmp (string, entry->string) == 0)
141
+ {
142
+ Assert (("Already in table" == NULL));
143
+ }
144
+ prev = entry;
145
+ entry = entry->next;
146
+ }
147
+ if (entry == NULL)
148
+ {
149
+ Assert (prev != NULL);
150
+ prev->next = newEntry (string, language, value);
151
+ }
152
+ }
153
+ }
154
+
155
+ extern int lookupKeyword (const char *const string, langType language)
156
+ {
157
+ const unsigned long hashedValue = hashValue (string);
158
+ hashEntry *entry = getHashTableEntry (hashedValue);
159
+ int result = -1;
160
+
161
+ while (entry != NULL)
162
+ {
163
+ if (language == entry->language && strcmp (string, entry->string) == 0)
164
+ {
165
+ result = entry->value;
166
+ break;
167
+ }
168
+ entry = entry->next;
169
+ }
170
+ return result;
171
+ }
172
+
173
+ extern void freeKeywordTable (void)
174
+ {
175
+ if (HashTable != NULL)
176
+ {
177
+ unsigned int i;
178
+
179
+ for (i = 0 ; i < TableSize ; ++i)
180
+ {
181
+ hashEntry *entry = HashTable [i];
182
+
183
+ while (entry != NULL)
184
+ {
185
+ hashEntry *next = entry->next;
186
+ eFree (entry);
187
+ entry = next;
188
+ }
189
+ }
190
+ eFree (HashTable);
191
+ }
192
+ }
193
+
194
+ extern int analyzeToken (vString *const name, langType language)
195
+ {
196
+ vString *keyword = vStringNew ();
197
+ int result;
198
+ vStringCopyToLower (keyword, name);
199
+ result = lookupKeyword (vStringValue (keyword), language);
200
+ vStringDelete (keyword);
201
+ return result;
202
+ }
203
+
204
+ #ifdef DEBUG
205
+
206
+ static void printEntry (const hashEntry *const entry)
207
+ {
208
+ printf (" %-15s %-7s\n", entry->string, getLanguageName (entry->language));
209
+ }
210
+
211
+ static unsigned int printBucket (const unsigned int i)
212
+ {
213
+ hashEntry **const table = getHashTable ();
214
+ hashEntry *entry = table [i];
215
+ unsigned int measure = 1;
216
+ boolean first = TRUE;
217
+
218
+ printf ("%2d:", i);
219
+ if (entry == NULL)
220
+ printf ("\n");
221
+ else while (entry != NULL)
222
+ {
223
+ if (! first)
224
+ printf (" ");
225
+ else
226
+ {
227
+ printf (" ");
228
+ first = FALSE;
229
+ }
230
+ printEntry (entry);
231
+ entry = entry->next;
232
+ measure = 2 * measure;
233
+ }
234
+ return measure - 1;
235
+ }
236
+
237
+ extern void printKeywordTable (void)
238
+ {
239
+ unsigned long emptyBucketCount = 0;
240
+ unsigned long measure = 0;
241
+ unsigned int i;
242
+
243
+ for (i = 0 ; i < TableSize ; ++i)
244
+ {
245
+ const unsigned int pass = printBucket (i);
246
+
247
+ measure += pass;
248
+ if (pass == 0)
249
+ ++emptyBucketCount;
250
+ }
251
+
252
+ printf ("spread measure = %ld\n", measure);
253
+ printf ("%ld empty buckets\n", emptyBucketCount);
254
+ }
255
+
256
+ #endif
257
+
258
+ /* vi:set tabstop=4 shiftwidth=4: */
@@ -0,0 +1,34 @@
1
+ /*
2
+ * $Id: keyword.h 658 2008-04-20 23:21:35Z elliotth $
3
+ *
4
+ * Copyright (c) 1998-2002, Darren Hiebert
5
+ *
6
+ * This source code is released for free distribution under the terms of the
7
+ * GNU General Public License.
8
+ *
9
+ * External interface to keyword.c
10
+ */
11
+ #ifndef _KEYWORD_H
12
+ #define _KEYWORD_H
13
+
14
+ /*
15
+ * INCLUDE FILES
16
+ */
17
+ #include "general.h" /* must always come first */
18
+
19
+ #include "parse.h"
20
+
21
+ /*
22
+ * FUNCTION PROTOTYPES
23
+ */
24
+ extern void addKeyword (const char *const string, langType language, int value);
25
+ extern int lookupKeyword (const char *const string, langType language);
26
+ extern void freeKeywordTable (void);
27
+ #ifdef DEBUG
28
+ extern void printKeywordTable (void);
29
+ #endif
30
+ extern int analyzeToken (vString *const name, langType language);
31
+
32
+ #endif /* _KEYWORD_H */
33
+
34
+ /* vi:set tabstop=4 shiftwidth=4: */
@@ -0,0 +1,139 @@
1
+ /*
2
+ * $Id: lisp.c 717 2009-07-07 03:40:50Z dhiebert $
3
+ *
4
+ * Copyright (c) 2000-2002, Darren Hiebert
5
+ *
6
+ * This source code is released for free distribution under the terms of the
7
+ * GNU General Public License.
8
+ *
9
+ * This module contains functions for generating tags for LISP files.
10
+ */
11
+
12
+ /*
13
+ * INCLUDE FILES
14
+ */
15
+ #include "general.h" /* must always come first */
16
+
17
+ #include "parse.h"
18
+ #include "read.h"
19
+ #include "vstring.h"
20
+
21
+ /*
22
+ * DATA DEFINITIONS
23
+ */
24
+ typedef enum {
25
+ K_FUNCTION
26
+ } lispKind;
27
+
28
+ static kindOption LispKinds [] = {
29
+ { TRUE, 'f', "function", "functions" }
30
+ };
31
+
32
+ /*
33
+ * FUNCTION DEFINITIONS
34
+ */
35
+
36
+ /*
37
+ * lisp tag functions
38
+ * look for (def or (DEF, quote or QUOTE
39
+ */
40
+ static int L_isdef (const unsigned char *strp)
41
+ {
42
+ return ( (strp [1] == 'd' || strp [1] == 'D')
43
+ && (strp [2] == 'e' || strp [2] == 'E')
44
+ && (strp [3] == 'f' || strp [3] == 'F'));
45
+ }
46
+
47
+ static int L_isquote (const unsigned char *strp)
48
+ {
49
+ return ( (*(++strp) == 'q' || *strp == 'Q')
50
+ && (*(++strp) == 'u' || *strp == 'U')
51
+ && (*(++strp) == 'o' || *strp == 'O')
52
+ && (*(++strp) == 't' || *strp == 'T')
53
+ && (*(++strp) == 'e' || *strp == 'E')
54
+ && isspace (*(++strp)));
55
+ }
56
+
57
+ static void L_getit (vString *const name, const unsigned char *dbp)
58
+ {
59
+ const unsigned char *p;
60
+
61
+ if (*dbp == '\'') /* Skip prefix quote */
62
+ dbp++;
63
+ else if (*dbp == '(' && L_isquote (dbp)) /* Skip "(quote " */
64
+ {
65
+ dbp += 7;
66
+ while (isspace (*dbp))
67
+ dbp++;
68
+ }
69
+ for (p=dbp ; *p!='\0' && *p!='(' && !isspace ((int) *p) && *p!=')' ; p++)
70
+ vStringPut (name, *p);
71
+ vStringTerminate (name);
72
+
73
+ if (vStringLength (name) > 0)
74
+ makeSimpleTag (name, LispKinds, K_FUNCTION);
75
+ vStringClear (name);
76
+ }
77
+
78
+ /* Algorithm adapted from from GNU etags.
79
+ */
80
+ static void findLispTags (void)
81
+ {
82
+ vString *name = vStringNew ();
83
+ const unsigned char* p;
84
+
85
+
86
+ while ((p = fileReadLine ()) != NULL)
87
+ {
88
+ if (*p == '(')
89
+ {
90
+ if (L_isdef (p))
91
+ {
92
+ while (*p != '\0' && !isspace ((int) *p))
93
+ p++;
94
+ while (isspace ((int) *p))
95
+ p++;
96
+ L_getit (name, p);
97
+ }
98
+ else
99
+ {
100
+ /* Check for (foo::defmumble name-defined ... */
101
+ do
102
+ p++;
103
+ while (*p != '\0' && !isspace ((int) *p)
104
+ && *p != ':' && *p != '(' && *p != ')');
105
+ if (*p == ':')
106
+ {
107
+ do
108
+ p++;
109
+ while (*p == ':');
110
+
111
+ if (L_isdef (p - 1))
112
+ {
113
+ while (*p != '\0' && !isspace ((int) *p))
114
+ p++;
115
+ while (isspace (*p))
116
+ p++;
117
+ L_getit (name, p);
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ vStringDelete (name);
124
+ }
125
+
126
+ extern parserDefinition* LispParser (void)
127
+ {
128
+ static const char *const extensions [] = {
129
+ "cl", "clisp", "el", "l", "lisp", "lsp", NULL
130
+ };
131
+ parserDefinition* def = parserNew ("Lisp");
132
+ def->kinds = LispKinds;
133
+ def->kindCount = KIND_COUNT (LispKinds);
134
+ def->extensions = extensions;
135
+ def->parser = findLispTags;
136
+ return def;
137
+ }
138
+
139
+ /* vi:set tabstop=4 shiftwidth=4: */