xref: /vim-8.2.3635/src/help.c (revision eeec2548)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * help.c: functions for Vim help
12  */
13 
14 #include "vim.h"
15 
16 /*
17  * ":help": open a read-only window on a help file
18  */
19     void
ex_help(exarg_T * eap)20 ex_help(exarg_T *eap)
21 {
22     char_u	*arg;
23     char_u	*tag;
24     FILE	*helpfd;	// file descriptor of help file
25     int		n;
26     int		i;
27     win_T	*wp;
28     int		num_matches;
29     char_u	**matches;
30     char_u	*p;
31     int		empty_fnum = 0;
32     int		alt_fnum = 0;
33     buf_T	*buf;
34 #ifdef FEAT_MULTI_LANG
35     int		len;
36     char_u	*lang;
37 #endif
38 #ifdef FEAT_FOLDING
39     int		old_KeyTyped = KeyTyped;
40 #endif
41 
42     if (ERROR_IF_ANY_POPUP_WINDOW)
43 	return;
44 
45     if (eap != NULL)
46     {
47 	// A ":help" command ends at the first LF, or at a '|' that is
48 	// followed by some text.  Set nextcmd to the following command.
49 	for (arg = eap->arg; *arg; ++arg)
50 	{
51 	    if (*arg == '\n' || *arg == '\r'
52 		    || (*arg == '|' && arg[1] != NUL && arg[1] != '|'))
53 	    {
54 		*arg++ = NUL;
55 		eap->nextcmd = arg;
56 		break;
57 	    }
58 	}
59 	arg = eap->arg;
60 
61 	if (eap->forceit && *arg == NUL && !curbuf->b_help)
62 	{
63 	    emsg(_("E478: Don't panic!"));
64 	    return;
65 	}
66 
67 	if (eap->skip)	    // not executing commands
68 	    return;
69     }
70     else
71 	arg = (char_u *)"";
72 
73     // remove trailing blanks
74     p = arg + STRLEN(arg) - 1;
75     while (p > arg && VIM_ISWHITE(*p) && p[-1] != '\\')
76 	*p-- = NUL;
77 
78 #ifdef FEAT_MULTI_LANG
79     // Check for a specified language
80     lang = check_help_lang(arg);
81 #endif
82 
83     // When no argument given go to the index.
84     if (*arg == NUL)
85 	arg = (char_u *)"help.txt";
86 
87     // Check if there is a match for the argument.
88     n = find_help_tags(arg, &num_matches, &matches,
89 						 eap != NULL && eap->forceit);
90 
91     i = 0;
92 #ifdef FEAT_MULTI_LANG
93     if (n != FAIL && lang != NULL)
94 	// Find first item with the requested language.
95 	for (i = 0; i < num_matches; ++i)
96 	{
97 	    len = (int)STRLEN(matches[i]);
98 	    if (len > 3 && matches[i][len - 3] == '@'
99 				  && STRICMP(matches[i] + len - 2, lang) == 0)
100 		break;
101 	}
102 #endif
103     if (i >= num_matches || n == FAIL)
104     {
105 #ifdef FEAT_MULTI_LANG
106 	if (lang != NULL)
107 	    semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg);
108 	else
109 #endif
110 	    semsg(_("E149: Sorry, no help for %s"), arg);
111 	if (n != FAIL)
112 	    FreeWild(num_matches, matches);
113 	return;
114     }
115 
116     // The first match (in the requested language) is the best match.
117     tag = vim_strsave(matches[i]);
118     FreeWild(num_matches, matches);
119 
120 #ifdef FEAT_GUI
121     need_mouse_correct = TRUE;
122 #endif
123 
124     // Re-use an existing help window or open a new one.
125     // Always open a new one for ":tab help".
126     if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0)
127     {
128 	if (cmdmod.cmod_tab != 0)
129 	    wp = NULL;
130 	else
131 	    FOR_ALL_WINDOWS(wp)
132 		if (bt_help(wp->w_buffer))
133 		    break;
134 	if (wp != NULL && wp->w_buffer->b_nwindows > 0)
135 	    win_enter(wp, TRUE);
136 	else
137 	{
138 	    // There is no help window yet.
139 	    // Try to open the file specified by the "helpfile" option.
140 	    if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL)
141 	    {
142 		smsg(_("Sorry, help file \"%s\" not found"), p_hf);
143 		goto erret;
144 	    }
145 	    fclose(helpfd);
146 
147 	    // Split off help window; put it at far top if no position
148 	    // specified, the current window is vertically split and
149 	    // narrow.
150 	    n = WSP_HELP;
151 	    if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
152 						  && curwin->w_width < 80)
153 		n |= WSP_TOP;
154 	    if (win_split(0, n) == FAIL)
155 		goto erret;
156 
157 	    if (curwin->w_height < p_hh)
158 		win_setheight((int)p_hh);
159 
160 	    // Open help file (do_ecmd() will set b_help flag, readfile() will
161 	    // set b_p_ro flag).
162 	    // Set the alternate file to the previously edited file.
163 	    alt_fnum = curbuf->b_fnum;
164 	    (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
165 			  ECMD_HIDE + ECMD_SET_HELP,
166 			  NULL);  // buffer is still open, don't store info
167 	    if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
168 		curwin->w_alt_fnum = alt_fnum;
169 	    empty_fnum = curbuf->b_fnum;
170 	}
171     }
172 
173     if (!p_im)
174 	restart_edit = 0;	    // don't want insert mode in help file
175 
176 #ifdef FEAT_FOLDING
177     // Restore KeyTyped, setting 'filetype=help' may reset it.
178     // It is needed for do_tag top open folds under the cursor.
179     KeyTyped = old_KeyTyped;
180 #endif
181 
182     if (tag != NULL)
183 	do_tag(tag, DT_HELP, 1, FALSE, TRUE);
184 
185     // Delete the empty buffer if we're not using it.  Careful: autocommands
186     // may have jumped to another window, check that the buffer is not in a
187     // window.
188     if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum)
189     {
190 	buf = buflist_findnr(empty_fnum);
191 	if (buf != NULL && buf->b_nwindows == 0)
192 	    wipe_buffer(buf, TRUE);
193     }
194 
195     // keep the previous alternate file
196     if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
197 				    && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
198 	curwin->w_alt_fnum = alt_fnum;
199 
200 erret:
201     vim_free(tag);
202 }
203 
204 /*
205  * ":helpclose": Close one help window
206  */
207     void
ex_helpclose(exarg_T * eap UNUSED)208 ex_helpclose(exarg_T *eap UNUSED)
209 {
210     win_T *win;
211 
212     FOR_ALL_WINDOWS(win)
213     {
214 	if (bt_help(win->w_buffer))
215 	{
216 	    win_close(win, FALSE);
217 	    return;
218 	}
219     }
220 }
221 
222 #if defined(FEAT_MULTI_LANG) || defined(PROTO)
223 /*
224  * In an argument search for a language specifiers in the form "@xx".
225  * Changes the "@" to NUL if found, and returns a pointer to "xx".
226  * Returns NULL if not found.
227  */
228     char_u *
check_help_lang(char_u * arg)229 check_help_lang(char_u *arg)
230 {
231     int len = (int)STRLEN(arg);
232 
233     if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
234 					       && ASCII_ISALPHA(arg[len - 1]))
235     {
236 	arg[len - 3] = NUL;		// remove the '@'
237 	return arg + len - 2;
238     }
239     return NULL;
240 }
241 #endif
242 
243 /*
244  * Return a heuristic indicating how well the given string matches.  The
245  * smaller the number, the better the match.  This is the order of priorities,
246  * from best match to worst match:
247  *	- Match with least alphanumeric characters is better.
248  *	- Match with least total characters is better.
249  *	- Match towards the start is better.
250  *	- Match starting with "+" is worse (feature instead of command)
251  * Assumption is made that the matched_string passed has already been found to
252  * match some string for which help is requested.  webb.
253  */
254     int
help_heuristic(char_u * matched_string,int offset,int wrong_case)255 help_heuristic(
256     char_u	*matched_string,
257     int		offset,			// offset for match
258     int		wrong_case)		// no matching case
259 {
260     int		num_letters;
261     char_u	*p;
262 
263     num_letters = 0;
264     for (p = matched_string; *p; p++)
265 	if (ASCII_ISALNUM(*p))
266 	    num_letters++;
267 
268     // Multiply the number of letters by 100 to give it a much bigger
269     // weighting than the number of characters.
270     // If there only is a match while ignoring case, add 5000.
271     // If the match starts in the middle of a word, add 10000 to put it
272     // somewhere in the last half.
273     // If the match is more than 2 chars from the start, multiply by 200 to
274     // put it after matches at the start.
275     if (ASCII_ISALNUM(matched_string[offset]) && offset > 0
276 				 && ASCII_ISALNUM(matched_string[offset - 1]))
277 	offset += 10000;
278     else if (offset > 2)
279 	offset *= 200;
280     if (wrong_case)
281 	offset += 5000;
282     // Features are less interesting than the subjects themselves, but "+"
283     // alone is not a feature.
284     if (matched_string[0] == '+' && matched_string[1] != NUL)
285 	offset += 100;
286     return (int)(100 * num_letters + STRLEN(matched_string) + offset);
287 }
288 
289 /*
290  * Compare functions for qsort() below, that checks the help heuristics number
291  * that has been put after the tagname by find_tags().
292  */
293     static int
help_compare(const void * s1,const void * s2)294 help_compare(const void *s1, const void *s2)
295 {
296     char    *p1;
297     char    *p2;
298     int	    cmp;
299 
300     p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
301     p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
302 
303     // Compare by help heuristic number first.
304     cmp = strcmp(p1, p2);
305     if (cmp != 0)
306 	return cmp;
307 
308     // Compare by strings as tie-breaker when same heuristic number.
309     return strcmp(*(char **)s1, *(char **)s2);
310 }
311 
312 /*
313  * Find all help tags matching "arg", sort them and return in matches[], with
314  * the number of matches in num_matches.
315  * The matches will be sorted with a "best" match algorithm.
316  * When "keep_lang" is TRUE try keeping the language of the current buffer.
317  */
318     int
find_help_tags(char_u * arg,int * num_matches,char_u *** matches,int keep_lang)319 find_help_tags(
320     char_u	*arg,
321     int		*num_matches,
322     char_u	***matches,
323     int		keep_lang)
324 {
325     char_u	*s, *d;
326     int		i;
327     // Specific tags that either have a specific replacement or won't go
328     // through the generic rules.
329     static char *(except_tbl[][2]) = {
330 	{"*",		"star"},
331 	{"g*",		"gstar"},
332 	{"[*",		"[star"},
333 	{"]*",		"]star"},
334 	{":*",		":star"},
335 	{"/*",		"/star"},
336 	{"/\\*",	"/\\\\star"},
337 	{"\"*",		"quotestar"},
338 	{"**",		"starstar"},
339 	{"cpo-*",	"cpo-star"},
340 	{"/\\(\\)",	"/\\\\(\\\\)"},
341 	{"/\\%(\\)",	"/\\\\%(\\\\)"},
342 	{"?",		"?"},
343 	{"??",		"??"},
344 	{":?",		":?"},
345 	{"?<CR>",	"?<CR>"},
346 	{"g?",		"g?"},
347 	{"g?g?",	"g?g?"},
348 	{"g??",		"g??"},
349 	{"-?",		"-?"},
350 	{"q?",		"q?"},
351 	{"v_g?",	"v_g?"},
352 	{"/\\?",	"/\\\\?"},
353 	{"/\\z(\\)",	"/\\\\z(\\\\)"},
354 	{"\\=",		"\\\\="},
355 	{":s\\=",	":s\\\\="},
356 	{"[count]",	"\\[count]"},
357 	{"[quotex]",	"\\[quotex]"},
358 	{"[range]",	"\\[range]"},
359 	{":[range]",	":\\[range]"},
360 	{"[pattern]",	"\\[pattern]"},
361 	{"\\|",		"\\\\bar"},
362 	{"\\%$",	"/\\\\%\\$"},
363 	{"s/\\~",	"s/\\\\\\~"},
364 	{"s/\\U",	"s/\\\\U"},
365 	{"s/\\L",	"s/\\\\L"},
366 	{"s/\\1",	"s/\\\\1"},
367 	{"s/\\2",	"s/\\\\2"},
368 	{"s/\\3",	"s/\\\\3"},
369 	{"s/\\9",	"s/\\\\9"},
370 	{NULL, NULL}
371     };
372     static char *(expr_table[]) = {"!=?", "!~?", "<=?", "<?", "==?", "=~?",
373 				   ">=?", ">?", "is?", "isnot?"};
374     int flags;
375 
376     d = IObuff;		    // assume IObuff is long enough!
377     d[0] = NUL;
378 
379     if (STRNICMP(arg, "expr-", 5) == 0)
380     {
381 	// When the string starting with "expr-" and containing '?' and matches
382 	// the table, it is taken literally (but ~ is escaped).  Otherwise '?'
383 	// is recognized as a wildcard.
384 	for (i = (int)ARRAY_LENGTH(expr_table); --i >= 0; )
385 	    if (STRCMP(arg + 5, expr_table[i]) == 0)
386 	    {
387 		int si = 0, di = 0;
388 
389 		for (;;)
390 		{
391 		    if (arg[si] == '~')
392 			d[di++] = '\\';
393 		    d[di++] = arg[si];
394 		    if (arg[si] == NUL)
395 			break;
396 		    ++si;
397 		}
398 		break;
399 	    }
400     }
401     else
402     {
403 	// Recognize a few exceptions to the rule.  Some strings that contain
404 	// '*'are changed to "star", otherwise '*' is recognized as a wildcard.
405 	for (i = 0; except_tbl[i][0] != NULL; ++i)
406 	    if (STRCMP(arg, except_tbl[i][0]) == 0)
407 	    {
408 		STRCPY(d, except_tbl[i][1]);
409 		break;
410 	    }
411     }
412 
413     if (d[0] == NUL)	// no match in table
414     {
415 	// Replace "\S" with "/\\S", etc.  Otherwise every tag is matched.
416 	// Also replace "\%^" and "\%(", they match every tag too.
417 	// Also "\zs", "\z1", etc.
418 	// Also "\@<", "\@=", "\@<=", etc.
419 	// And also "\_$" and "\_^".
420 	if (arg[0] == '\\'
421 		&& ((arg[1] != NUL && arg[2] == NUL)
422 		    || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL
423 							   && arg[2] != NUL)))
424 	{
425 	    STRCPY(d, "/\\\\");
426 	    STRCPY(d + 3, arg + 1);
427 	    // Check for "/\\_$", should be "/\\_\$"
428 	    if (d[3] == '_' && d[4] == '$')
429 		STRCPY(d + 4, "\\$");
430 	}
431 	else
432 	{
433 	  // Replace:
434 	  // "[:...:]" with "\[:...:]"
435 	  // "[++...]" with "\[++...]"
436 	  // "\{" with "\\{"		   -- matching "} \}"
437 	    if ((arg[0] == '[' && (arg[1] == ':'
438 			 || (arg[1] == '+' && arg[2] == '+')))
439 		    || (arg[0] == '\\' && arg[1] == '{'))
440 	      *d++ = '\\';
441 
442 	  // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
443 	  if (*arg == '(' && arg[1] == '\'')
444 	      arg++;
445 	  for (s = arg; *s; ++s)
446 	  {
447 	    // Replace "|" with "bar" and '"' with "quote" to match the name of
448 	    // the tags for these commands.
449 	    // Replace "*" with ".*" and "?" with "." to match command line
450 	    // completion.
451 	    // Insert a backslash before '~', '$' and '.' to avoid their
452 	    // special meaning.
453 	    if (d - IObuff > IOSIZE - 10)	// getting too long!?
454 		break;
455 	    switch (*s)
456 	    {
457 		case '|':   STRCPY(d, "bar");
458 			    d += 3;
459 			    continue;
460 		case '"':   STRCPY(d, "quote");
461 			    d += 5;
462 			    continue;
463 		case '*':   *d++ = '.';
464 			    break;
465 		case '?':   *d++ = '.';
466 			    continue;
467 		case '$':
468 		case '.':
469 		case '~':   *d++ = '\\';
470 			    break;
471 	    }
472 
473 	    // Replace "^x" by "CTRL-X". Don't do this for "^_" to make
474 	    // ":help i_^_CTRL-D" work.
475 	    // Insert '-' before and after "CTRL-X" when applicable.
476 	    if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1])
477 			   || vim_strchr((char_u *)"?@[\\]^", s[1]) != NULL)))
478 	    {
479 		if (d > IObuff && d[-1] != '_' && d[-1] != '\\')
480 		    *d++ = '_';		// prepend a '_' to make x_CTRL-x
481 		STRCPY(d, "CTRL-");
482 		d += 5;
483 		if (*s < ' ')
484 		{
485 #ifdef EBCDIC
486 		    *d++ = CtrlChar(*s);
487 #else
488 		    *d++ = *s + '@';
489 #endif
490 		    if (d[-1] == '\\')
491 			*d++ = '\\';	// double a backslash
492 		}
493 		else
494 		    *d++ = *++s;
495 		if (s[1] != NUL && s[1] != '_')
496 		    *d++ = '_';		// append a '_'
497 		continue;
498 	    }
499 	    else if (*s == '^')		// "^" or "CTRL-^" or "^_"
500 		*d++ = '\\';
501 
502 	    // Insert a backslash before a backslash after a slash, for search
503 	    // pattern tags: "/\|" --> "/\\|".
504 	    else if (s[0] == '\\' && s[1] != '\\'
505 					       && *arg == '/' && s == arg + 1)
506 		*d++ = '\\';
507 
508 	    // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
509 	    // "CTRL-\_CTRL-N"
510 	    if (STRNICMP(s, "CTRL-\\_", 7) == 0)
511 	    {
512 		STRCPY(d, "CTRL-\\\\");
513 		d += 7;
514 		s += 6;
515 	    }
516 
517 	    *d++ = *s;
518 
519 	    // If tag contains "({" or "([", tag terminates at the "(".
520 	    // This is for help on functions, e.g.: abs({expr}).
521 	    if (*s == '(' && (s[1] == '{' || s[1] =='['))
522 		break;
523 
524 	    // If tag starts with ', toss everything after a second '. Fixes
525 	    // CTRL-] on 'option'. (would include the trailing '.').
526 	    if (*s == '\'' && s > arg && *arg == '\'')
527 		break;
528 	    // Also '{' and '}'.
529 	    if (*s == '}' && s > arg && *arg == '{')
530 		break;
531 	  }
532 	  *d = NUL;
533 
534 	  if (*IObuff == '`')
535 	  {
536 	      if (d > IObuff + 2 && d[-1] == '`')
537 	      {
538 		  // remove the backticks from `command`
539 		  mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff));
540 		  d[-2] = NUL;
541 	      }
542 	      else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',')
543 	      {
544 		  // remove the backticks and comma from `command`,
545 		  mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff));
546 		  d[-3] = NUL;
547 	      }
548 	      else if (d > IObuff + 4 && d[-3] == '`'
549 					     && d[-2] == '\\' && d[-1] == '.')
550 	      {
551 		  // remove the backticks and dot from `command`\.
552 		  mch_memmove(IObuff, IObuff + 1, STRLEN(IObuff));
553 		  d[-4] = NUL;
554 	      }
555 	  }
556 	}
557     }
558 
559     *matches = (char_u **)"";
560     *num_matches = 0;
561     flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
562     if (keep_lang)
563 	flags |= TAG_KEEP_LANG;
564     if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK
565 	    && *num_matches > 0)
566     {
567 	// Sort the matches found on the heuristic number that is after the
568 	// tag name.
569 	qsort((void *)*matches, (size_t)*num_matches,
570 					      sizeof(char_u *), help_compare);
571 	// Delete more than TAG_MANY to reduce the size of the listing.
572 	while (*num_matches > TAG_MANY)
573 	    vim_free((*matches)[--*num_matches]);
574     }
575     return OK;
576 }
577 
578 #ifdef FEAT_MULTI_LANG
579 /*
580  * Cleanup matches for help tags:
581  * Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
582  * tag matches it.  Otherwise remove "@en" if "en" is the only language.
583  */
584     void
cleanup_help_tags(int num_file,char_u ** file)585 cleanup_help_tags(int num_file, char_u **file)
586 {
587     int		i, j;
588     int		len;
589     char_u	buf[4];
590     char_u	*p = buf;
591 
592     if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n'))
593     {
594 	*p++ = '@';
595 	*p++ = p_hlg[0];
596 	*p++ = p_hlg[1];
597     }
598     *p = NUL;
599 
600     for (i = 0; i < num_file; ++i)
601     {
602 	len = (int)STRLEN(file[i]) - 3;
603 	if (len <= 0)
604 	    continue;
605 	if (STRCMP(file[i] + len, "@en") == 0)
606 	{
607 	    // Sorting on priority means the same item in another language may
608 	    // be anywhere.  Search all items for a match up to the "@en".
609 	    for (j = 0; j < num_file; ++j)
610 		if (j != i && (int)STRLEN(file[j]) == len + 3
611 			   && STRNCMP(file[i], file[j], len + 1) == 0)
612 		    break;
613 	    if (j == num_file)
614 		// item only exists with @en, remove it
615 		file[i][len] = NUL;
616 	}
617     }
618 
619     if (*buf != NUL)
620 	for (i = 0; i < num_file; ++i)
621 	{
622 	    len = (int)STRLEN(file[i]) - 3;
623 	    if (len <= 0)
624 		continue;
625 	    if (STRCMP(file[i] + len, buf) == 0)
626 	    {
627 		// remove the default language
628 		file[i][len] = NUL;
629 	    }
630 	}
631 }
632 #endif
633 
634 /*
635  * Called when starting to edit a buffer for a help file.
636  */
637     void
prepare_help_buffer(void)638 prepare_help_buffer(void)
639 {
640     char_u	*p;
641 
642     curbuf->b_help = TRUE;
643 #ifdef FEAT_QUICKFIX
644     set_string_option_direct((char_u *)"buftype", -1,
645 				     (char_u *)"help", OPT_FREE|OPT_LOCAL, 0);
646 #endif
647 
648     // Always set these options after jumping to a help tag, because the
649     // user may have an autocommand that gets in the way.
650     // When adding an option here, also update the help file helphelp.txt.
651 
652     // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
653     // latin1 word characters (for translated help files).
654     // Only set it when needed, buf_init_chartab() is some work.
655     p =
656 #ifdef EBCDIC
657 	    (char_u *)"65-255,^*,^|,^\"";
658 #else
659 	    (char_u *)"!-~,^*,^|,^\",192-255";
660 #endif
661     if (STRCMP(curbuf->b_p_isk, p) != 0)
662     {
663 	set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
664 	check_buf_options(curbuf);
665 	(void)buf_init_chartab(curbuf, FALSE);
666     }
667 
668 #ifdef FEAT_FOLDING
669     // Don't use the global foldmethod.
670     set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual",
671 						       OPT_FREE|OPT_LOCAL, 0);
672 #endif
673 
674     curbuf->b_p_ts = 8;		// 'tabstop' is 8
675     curwin->w_p_list = FALSE;	// no list mode
676 
677     curbuf->b_p_ma = FALSE;	// not modifiable
678     curbuf->b_p_bin = FALSE;	// reset 'bin' before reading file
679     curwin->w_p_nu = 0;		// no line numbers
680     curwin->w_p_rnu = 0;	// no relative line numbers
681     RESET_BINDING(curwin);	// no scroll or cursor binding
682 #ifdef FEAT_ARABIC
683     curwin->w_p_arab = FALSE;	// no arabic mode
684 #endif
685 #ifdef FEAT_RIGHTLEFT
686     curwin->w_p_rl  = FALSE;	// help window is left-to-right
687 #endif
688 #ifdef FEAT_FOLDING
689     curwin->w_p_fen = FALSE;	// No folding in the help window
690 #endif
691 #ifdef FEAT_DIFF
692     curwin->w_p_diff = FALSE;	// No 'diff'
693 #endif
694 #ifdef FEAT_SPELL
695     curwin->w_p_spell = FALSE;	// No spell checking
696 #endif
697 
698     set_buflisted(FALSE);
699 }
700 
701 /*
702  * After reading a help file: May cleanup a help buffer when syntax
703  * highlighting is not used.
704  */
705     void
fix_help_buffer(void)706 fix_help_buffer(void)
707 {
708     linenr_T	lnum;
709     char_u	*line;
710     int		in_example = FALSE;
711     int		len;
712     char_u	*fname;
713     char_u	*p;
714     char_u	*rt;
715     int		mustfree;
716 
717     // Set filetype to "help" if still needed.
718     if (STRCMP(curbuf->b_p_ft, "help") != 0)
719     {
720 	++curbuf_lock;
721 	set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL);
722 	--curbuf_lock;
723     }
724 
725 #ifdef FEAT_SYN_HL
726     if (!syntax_present(curwin))
727 #endif
728     {
729 	for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
730 	{
731 	    line = ml_get_buf(curbuf, lnum, FALSE);
732 	    len = (int)STRLEN(line);
733 	    if (in_example && len > 0 && !VIM_ISWHITE(line[0]))
734 	    {
735 		// End of example: non-white or '<' in first column.
736 		if (line[0] == '<')
737 		{
738 		    // blank-out a '<' in the first column
739 		    line = ml_get_buf(curbuf, lnum, TRUE);
740 		    line[0] = ' ';
741 		}
742 		in_example = FALSE;
743 	    }
744 	    if (!in_example && len > 0)
745 	    {
746 		if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' '))
747 		{
748 		    // blank-out a '>' in the last column (start of example)
749 		    line = ml_get_buf(curbuf, lnum, TRUE);
750 		    line[len - 1] = ' ';
751 		    in_example = TRUE;
752 		}
753 		else if (line[len - 1] == '~')
754 		{
755 		    // blank-out a '~' at the end of line (header marker)
756 		    line = ml_get_buf(curbuf, lnum, TRUE);
757 		    line[len - 1] = ' ';
758 		}
759 	    }
760 	}
761     }
762 
763     // In the "help.txt" and "help.abx" file, add the locally added help
764     // files.  This uses the very first line in the help file.
765     fname = gettail(curbuf->b_fname);
766     if (fnamecmp(fname, "help.txt") == 0
767 #ifdef FEAT_MULTI_LANG
768 	|| (fnamencmp(fname, "help.", 5) == 0
769 	    && ASCII_ISALPHA(fname[5])
770 	    && ASCII_ISALPHA(fname[6])
771 	    && TOLOWER_ASC(fname[7]) == 'x'
772 	    && fname[8] == NUL)
773 #endif
774 	)
775     {
776 	for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
777 	{
778 	    line = ml_get_buf(curbuf, lnum, FALSE);
779 	    if (strstr((char *)line, "*local-additions*") == NULL)
780 		continue;
781 
782 	    // Go through all directories in 'runtimepath', skipping
783 	    // $VIMRUNTIME.
784 	    p = p_rtp;
785 	    while (*p != NUL)
786 	    {
787 		copy_option_part(&p, NameBuff, MAXPATHL, ",");
788 		mustfree = FALSE;
789 		rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
790 		if (rt != NULL &&
791 			    fullpathcmp(rt, NameBuff, FALSE, TRUE) != FPC_SAME)
792 		{
793 		    int		fcount;
794 		    char_u	**fnames;
795 		    FILE	*fd;
796 		    char_u	*s;
797 		    int		fi;
798 		    vimconv_T	vc;
799 		    char_u	*cp;
800 
801 		    // Find all "doc/ *.txt" files in this directory.
802 		    add_pathsep(NameBuff);
803 #ifdef FEAT_MULTI_LANG
804 		    STRCAT(NameBuff, "doc/*.??[tx]");
805 #else
806 		    STRCAT(NameBuff, "doc/*.txt");
807 #endif
808 		    if (gen_expand_wildcards(1, &NameBuff, &fcount,
809 					 &fnames, EW_FILE|EW_SILENT) == OK
810 			    && fcount > 0)
811 		    {
812 #ifdef FEAT_MULTI_LANG
813 			int	i1, i2;
814 			char_u	*f1, *f2;
815 			char_u	*t1, *t2;
816 			char_u	*e1, *e2;
817 
818 			// If foo.abx is found use it instead of foo.txt in
819 			// the same directory.
820 			for (i1 = 0; i1 < fcount; ++i1)
821 			{
822 			    for (i2 = 0; i2 < fcount; ++i2)
823 			    {
824 				if (i1 == i2)
825 				    continue;
826 				if (fnames[i1] == NULL || fnames[i2] == NULL)
827 				    continue;
828 				f1 = fnames[i1];
829 				f2 = fnames[i2];
830 				t1 = gettail(f1);
831 				t2 = gettail(f2);
832 				e1 = vim_strrchr(t1, '.');
833 				e2 = vim_strrchr(t2, '.');
834 				if (e1 == NULL || e2 == NULL)
835 				    continue;
836 				if (fnamecmp(e1, ".txt") != 0
837 				    && fnamecmp(e1, fname + 4) != 0)
838 				{
839 				    // Not .txt and not .abx, remove it.
840 				    VIM_CLEAR(fnames[i1]);
841 				    continue;
842 				}
843 				if (e1 - f1 != e2 - f2
844 					    || fnamencmp(f1, f2, e1 - f1) != 0)
845 				    continue;
846 				if (fnamecmp(e1, ".txt") == 0
847 				    && fnamecmp(e2, fname + 4) == 0)
848 				    // use .abx instead of .txt
849 				    VIM_CLEAR(fnames[i1]);
850 			    }
851 			}
852 #endif
853 			for (fi = 0; fi < fcount; ++fi)
854 			{
855 			    if (fnames[fi] == NULL)
856 				continue;
857 			    fd = mch_fopen((char *)fnames[fi], "r");
858 			    if (fd != NULL)
859 			    {
860 				vim_fgets(IObuff, IOSIZE, fd);
861 				if (IObuff[0] == '*'
862 					&& (s = vim_strchr(IObuff + 1, '*'))
863 								  != NULL)
864 				{
865 				    int	this_utf = MAYBE;
866 
867 				    // Change tag definition to a
868 				    // reference and remove <CR>/<NL>.
869 				    IObuff[0] = '|';
870 				    *s = '|';
871 				    while (*s != NUL)
872 				    {
873 					if (*s == '\r' || *s == '\n')
874 					    *s = NUL;
875 					// The text is utf-8 when a byte
876 					// above 127 is found and no
877 					// illegal byte sequence is found.
878 					if (*s >= 0x80 && this_utf != FALSE)
879 					{
880 					    int	l;
881 
882 					    this_utf = TRUE;
883 					    l = utf_ptr2len(s);
884 					    if (l == 1)
885 						this_utf = FALSE;
886 					    s += l - 1;
887 					}
888 					++s;
889 				    }
890 
891 				    // The help file is latin1 or utf-8;
892 				    // conversion to the current
893 				    // 'encoding' may be required.
894 				    vc.vc_type = CONV_NONE;
895 				    convert_setup(&vc, (char_u *)(
896 						this_utf == TRUE ? "utf-8"
897 						      : "latin1"), p_enc);
898 				    if (vc.vc_type == CONV_NONE)
899 					// No conversion needed.
900 					cp = IObuff;
901 				    else
902 				    {
903 					// Do the conversion.  If it fails
904 					// use the unconverted text.
905 					cp = string_convert(&vc, IObuff,
906 								    NULL);
907 					if (cp == NULL)
908 					    cp = IObuff;
909 				    }
910 				    convert_setup(&vc, NULL, NULL);
911 
912 				    ml_append(lnum, cp, (colnr_T)0, FALSE);
913 				    if (cp != IObuff)
914 					vim_free(cp);
915 				    ++lnum;
916 				}
917 				fclose(fd);
918 			    }
919 			}
920 			FreeWild(fcount, fnames);
921 		    }
922 		}
923 		if (mustfree)
924 		    vim_free(rt);
925 	    }
926 	    break;
927 	}
928     }
929 }
930 
931 /*
932  * ":exusage"
933  */
934     void
ex_exusage(exarg_T * eap UNUSED)935 ex_exusage(exarg_T *eap UNUSED)
936 {
937     do_cmdline_cmd((char_u *)"help ex-cmd-index");
938 }
939 
940 /*
941  * ":viusage"
942  */
943     void
ex_viusage(exarg_T * eap UNUSED)944 ex_viusage(exarg_T *eap UNUSED)
945 {
946     do_cmdline_cmd((char_u *)"help normal-index");
947 }
948 
949 /*
950  * Generate tags in one help directory.
951  */
952     static void
helptags_one(char_u * dir,char_u * ext,char_u * tagfname,int add_help_tags,int ignore_writeerr)953 helptags_one(
954     char_u	*dir,		// doc directory
955     char_u	*ext,		// suffix, ".txt", ".itx", ".frx", etc.
956     char_u	*tagfname,	// "tags" for English, "tags-fr" for French.
957     int		add_help_tags,	// add "help-tags" tag
958     int		ignore_writeerr)    // ignore write error
959 {
960     FILE	*fd_tags;
961     FILE	*fd;
962     garray_T	ga;
963     int		filecount;
964     char_u	**files;
965     char_u	*p1, *p2;
966     int		fi;
967     char_u	*s;
968     int		i;
969     char_u	*fname;
970     int		dirlen;
971     int		utf8 = MAYBE;
972     int		this_utf8;
973     int		firstline;
974     int		mix = FALSE;	// detected mixed encodings
975 
976     // Find all *.txt files.
977     dirlen = (int)STRLEN(dir);
978     STRCPY(NameBuff, dir);
979     STRCAT(NameBuff, "/**/*");
980     STRCAT(NameBuff, ext);
981     if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
982 						    EW_FILE|EW_SILENT) == FAIL
983 	    || filecount == 0)
984     {
985 	if (!got_int)
986 	    semsg(_("E151: No match: %s"), NameBuff);
987 	return;
988     }
989 
990     // Open the tags file for writing.
991     // Do this before scanning through all the files.
992     STRCPY(NameBuff, dir);
993     add_pathsep(NameBuff);
994     STRCAT(NameBuff, tagfname);
995     fd_tags = mch_fopen((char *)NameBuff, "w");
996     if (fd_tags == NULL)
997     {
998 	if (!ignore_writeerr)
999 	    semsg(_("E152: Cannot open %s for writing"), NameBuff);
1000 	FreeWild(filecount, files);
1001 	return;
1002     }
1003 
1004     // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
1005     // add the "help-tags" tag.
1006     ga_init2(&ga, (int)sizeof(char_u *), 100);
1007     if (add_help_tags || fullpathcmp((char_u *)"$VIMRUNTIME/doc",
1008 						dir, FALSE, TRUE) == FPC_SAME)
1009     {
1010 	if (ga_grow(&ga, 1) == FAIL)
1011 	    got_int = TRUE;
1012 	else
1013 	{
1014 	    s = alloc(18 + (unsigned)STRLEN(tagfname));
1015 	    if (s == NULL)
1016 		got_int = TRUE;
1017 	    else
1018 	    {
1019 		sprintf((char *)s, "help-tags\t%s\t1\n", tagfname);
1020 		((char_u **)ga.ga_data)[ga.ga_len] = s;
1021 		++ga.ga_len;
1022 	    }
1023 	}
1024     }
1025 
1026     // Go over all the files and extract the tags.
1027     for (fi = 0; fi < filecount && !got_int; ++fi)
1028     {
1029 	fd = mch_fopen((char *)files[fi], "r");
1030 	if (fd == NULL)
1031 	{
1032 	    semsg(_("E153: Unable to open %s for reading"), files[fi]);
1033 	    continue;
1034 	}
1035 	fname = files[fi] + dirlen + 1;
1036 
1037 	firstline = TRUE;
1038 	while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int)
1039 	{
1040 	    if (firstline)
1041 	    {
1042 		// Detect utf-8 file by a non-ASCII char in the first line.
1043 		this_utf8 = MAYBE;
1044 		for (s = IObuff; *s != NUL; ++s)
1045 		    if (*s >= 0x80)
1046 		    {
1047 			int l;
1048 
1049 			this_utf8 = TRUE;
1050 			l = utf_ptr2len(s);
1051 			if (l == 1)
1052 			{
1053 			    // Illegal UTF-8 byte sequence.
1054 			    this_utf8 = FALSE;
1055 			    break;
1056 			}
1057 			s += l - 1;
1058 		    }
1059 		if (this_utf8 == MAYBE)	    // only ASCII characters found
1060 		    this_utf8 = FALSE;
1061 		if (utf8 == MAYBE)	    // first file
1062 		    utf8 = this_utf8;
1063 		else if (utf8 != this_utf8)
1064 		{
1065 		    semsg(_("E670: Mix of help file encodings within a language: %s"), files[fi]);
1066 		    mix = !got_int;
1067 		    got_int = TRUE;
1068 		}
1069 		firstline = FALSE;
1070 	    }
1071 	    p1 = vim_strchr(IObuff, '*');	// find first '*'
1072 	    while (p1 != NULL)
1073 	    {
1074 		// Use vim_strbyte() instead of vim_strchr() so that when
1075 		// 'encoding' is dbcs it still works, don't find '*' in the
1076 		// second byte.
1077 		p2 = vim_strbyte(p1 + 1, '*');	// find second '*'
1078 		if (p2 != NULL && p2 > p1 + 1)	// skip "*" and "**"
1079 		{
1080 		    for (s = p1 + 1; s < p2; ++s)
1081 			if (*s == ' ' || *s == '\t' || *s == '|')
1082 			    break;
1083 
1084 		    // Only accept a *tag* when it consists of valid
1085 		    // characters, there is white space before it and is
1086 		    // followed by a white character or end-of-line.
1087 		    if (s == p2
1088 			    && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
1089 			    && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL
1090 				|| s[1] == '\0'))
1091 		    {
1092 			*p2 = '\0';
1093 			++p1;
1094 			if (ga_grow(&ga, 1) == FAIL)
1095 			{
1096 			    got_int = TRUE;
1097 			    break;
1098 			}
1099 			s = alloc(p2 - p1 + STRLEN(fname) + 2);
1100 			if (s == NULL)
1101 			{
1102 			    got_int = TRUE;
1103 			    break;
1104 			}
1105 			((char_u **)ga.ga_data)[ga.ga_len] = s;
1106 			++ga.ga_len;
1107 			sprintf((char *)s, "%s\t%s", p1, fname);
1108 
1109 			// find next '*'
1110 			p2 = vim_strchr(p2 + 1, '*');
1111 		    }
1112 		}
1113 		p1 = p2;
1114 	    }
1115 	    line_breakcheck();
1116 	}
1117 
1118 	fclose(fd);
1119     }
1120 
1121     FreeWild(filecount, files);
1122 
1123     if (!got_int)
1124     {
1125 	// Sort the tags.
1126 	if (ga.ga_data != NULL)
1127 	    sort_strings((char_u **)ga.ga_data, ga.ga_len);
1128 
1129 	// Check for duplicates.
1130 	for (i = 1; i < ga.ga_len; ++i)
1131 	{
1132 	    p1 = ((char_u **)ga.ga_data)[i - 1];
1133 	    p2 = ((char_u **)ga.ga_data)[i];
1134 	    while (*p1 == *p2)
1135 	    {
1136 		if (*p2 == '\t')
1137 		{
1138 		    *p2 = NUL;
1139 		    vim_snprintf((char *)NameBuff, MAXPATHL,
1140 			    _("E154: Duplicate tag \"%s\" in file %s/%s"),
1141 				     ((char_u **)ga.ga_data)[i], dir, p2 + 1);
1142 		    emsg((char *)NameBuff);
1143 		    *p2 = '\t';
1144 		    break;
1145 		}
1146 		++p1;
1147 		++p2;
1148 	    }
1149 	}
1150 
1151 	if (utf8 == TRUE)
1152 	    fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
1153 
1154 	// Write the tags into the file.
1155 	for (i = 0; i < ga.ga_len; ++i)
1156 	{
1157 	    s = ((char_u **)ga.ga_data)[i];
1158 	    if (STRNCMP(s, "help-tags\t", 10) == 0)
1159 		// help-tags entry was added in formatted form
1160 		fputs((char *)s, fd_tags);
1161 	    else
1162 	    {
1163 		fprintf(fd_tags, "%s\t/*", s);
1164 		for (p1 = s; *p1 != '\t'; ++p1)
1165 		{
1166 		    // insert backslash before '\\' and '/'
1167 		    if (*p1 == '\\' || *p1 == '/')
1168 			putc('\\', fd_tags);
1169 		    putc(*p1, fd_tags);
1170 		}
1171 		fprintf(fd_tags, "*\n");
1172 	    }
1173 	}
1174     }
1175     if (mix)
1176 	got_int = FALSE;    // continue with other languages
1177 
1178     for (i = 0; i < ga.ga_len; ++i)
1179 	vim_free(((char_u **)ga.ga_data)[i]);
1180     ga_clear(&ga);
1181     fclose(fd_tags);	    // there is no check for an error...
1182 }
1183 
1184 /*
1185  * Generate tags in one help directory, taking care of translations.
1186  */
1187     static void
do_helptags(char_u * dirname,int add_help_tags,int ignore_writeerr)1188 do_helptags(char_u *dirname, int add_help_tags, int ignore_writeerr)
1189 {
1190 #ifdef FEAT_MULTI_LANG
1191     int		len;
1192     int		i, j;
1193     garray_T	ga;
1194     char_u	lang[2];
1195     char_u	ext[5];
1196     char_u	fname[8];
1197     int		filecount;
1198     char_u	**files;
1199 
1200     // Get a list of all files in the help directory and in subdirectories.
1201     STRCPY(NameBuff, dirname);
1202     add_pathsep(NameBuff);
1203     STRCAT(NameBuff, "**");
1204     if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
1205 						    EW_FILE|EW_SILENT) == FAIL
1206 	    || filecount == 0)
1207     {
1208 	semsg(_("E151: No match: %s"), NameBuff);
1209 	return;
1210     }
1211 
1212     // Go over all files in the directory to find out what languages are
1213     // present.
1214     ga_init2(&ga, 1, 10);
1215     for (i = 0; i < filecount; ++i)
1216     {
1217 	len = (int)STRLEN(files[i]);
1218 	if (len > 4)
1219 	{
1220 	    if (STRICMP(files[i] + len - 4, ".txt") == 0)
1221 	    {
1222 		// ".txt" -> language "en"
1223 		lang[0] = 'e';
1224 		lang[1] = 'n';
1225 	    }
1226 	    else if (files[i][len - 4] == '.'
1227 		    && ASCII_ISALPHA(files[i][len - 3])
1228 		    && ASCII_ISALPHA(files[i][len - 2])
1229 		    && TOLOWER_ASC(files[i][len - 1]) == 'x')
1230 	    {
1231 		// ".abx" -> language "ab"
1232 		lang[0] = TOLOWER_ASC(files[i][len - 3]);
1233 		lang[1] = TOLOWER_ASC(files[i][len - 2]);
1234 	    }
1235 	    else
1236 		continue;
1237 
1238 	    // Did we find this language already?
1239 	    for (j = 0; j < ga.ga_len; j += 2)
1240 		if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0)
1241 		    break;
1242 	    if (j == ga.ga_len)
1243 	    {
1244 		// New language, add it.
1245 		if (ga_grow(&ga, 2) == FAIL)
1246 		    break;
1247 		((char_u *)ga.ga_data)[ga.ga_len++] = lang[0];
1248 		((char_u *)ga.ga_data)[ga.ga_len++] = lang[1];
1249 	    }
1250 	}
1251     }
1252 
1253     // Loop over the found languages to generate a tags file for each one.
1254     for (j = 0; j < ga.ga_len; j += 2)
1255     {
1256 	STRCPY(fname, "tags-xx");
1257 	fname[5] = ((char_u *)ga.ga_data)[j];
1258 	fname[6] = ((char_u *)ga.ga_data)[j + 1];
1259 	if (fname[5] == 'e' && fname[6] == 'n')
1260 	{
1261 	    // English is an exception: use ".txt" and "tags".
1262 	    fname[4] = NUL;
1263 	    STRCPY(ext, ".txt");
1264 	}
1265 	else
1266 	{
1267 	    // Language "ab" uses ".abx" and "tags-ab".
1268 	    STRCPY(ext, ".xxx");
1269 	    ext[1] = fname[5];
1270 	    ext[2] = fname[6];
1271 	}
1272 	helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr);
1273     }
1274 
1275     ga_clear(&ga);
1276     FreeWild(filecount, files);
1277 
1278 #else
1279     // No language support, just use "*.txt" and "tags".
1280     helptags_one(dirname, (char_u *)".txt", (char_u *)"tags", add_help_tags,
1281 							    ignore_writeerr);
1282 #endif
1283 }
1284 
1285     static void
helptags_cb(char_u * fname,void * cookie)1286 helptags_cb(char_u *fname, void *cookie)
1287 {
1288     do_helptags(fname, *(int *)cookie, TRUE);
1289 }
1290 
1291 /*
1292  * ":helptags"
1293  */
1294     void
ex_helptags(exarg_T * eap)1295 ex_helptags(exarg_T *eap)
1296 {
1297     expand_T	xpc;
1298     char_u	*dirname;
1299     int		add_help_tags = FALSE;
1300 
1301     // Check for ":helptags ++t {dir}".
1302     if (STRNCMP(eap->arg, "++t", 3) == 0 && VIM_ISWHITE(eap->arg[3]))
1303     {
1304 	add_help_tags = TRUE;
1305 	eap->arg = skipwhite(eap->arg + 3);
1306     }
1307 
1308     if (STRCMP(eap->arg, "ALL") == 0)
1309     {
1310 	do_in_path(p_rtp, (char_u *)"doc", DIP_ALL + DIP_DIR,
1311 						 helptags_cb, &add_help_tags);
1312     }
1313     else
1314     {
1315 	ExpandInit(&xpc);
1316 	xpc.xp_context = EXPAND_DIRECTORIES;
1317 	dirname = ExpandOne(&xpc, eap->arg, NULL,
1318 			    WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
1319 	if (dirname == NULL || !mch_isdir(dirname))
1320 	    semsg(_("E150: Not a directory: %s"), eap->arg);
1321 	else
1322 	    do_helptags(dirname, add_help_tags, FALSE);
1323 	vim_free(dirname);
1324     }
1325 }
1326