xref: /vim-8.2.3635/src/ex_cmds2.c (revision f4b8e57f)
1 /* vi:set ts=8 sts=4 sw=4:
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  * ex_cmds2.c: some more functions for command line commands
12  */
13 
14 #if defined(WIN32) && defined(FEAT_CSCOPE)
15 # include <io.h>
16 #endif
17 
18 #include "vim.h"
19 
20 #if defined(WIN32) && defined(FEAT_CSCOPE)
21 # include <fcntl.h>
22 #endif
23 
24 #include "version.h"
25 
26 static void	cmd_source __ARGS((char_u *fname, exarg_T *eap));
27 
28 #if defined(FEAT_EVAL) || defined(PROTO)
29 static int debug_greedy = FALSE;	/* batch mode debugging: don't save
30 					   and restore typeahead. */
31 
32 /*
33  * do_debug(): Debug mode.
34  * Repeatedly get Ex commands, until told to continue normal execution.
35  */
36     void
37 do_debug(cmd)
38     char_u	*cmd;
39 {
40     int		save_msg_scroll = msg_scroll;
41     int		save_State = State;
42     int		save_did_emsg = did_emsg;
43     int		save_cmd_silent = cmd_silent;
44     int		save_msg_silent = msg_silent;
45     int		save_emsg_silent = emsg_silent;
46     int		save_redir_off = redir_off;
47     tasave_T	typeaheadbuf;
48 # ifdef FEAT_EX_EXTRA
49     int		save_ex_normal_busy;
50 # endif
51     int		n;
52     char_u	*cmdline = NULL;
53     char_u	*p;
54     char	*tail = NULL;
55     static int	last_cmd = 0;
56 #define CMD_CONT	1
57 #define CMD_NEXT	2
58 #define CMD_STEP	3
59 #define CMD_FINISH	4
60 #define CMD_QUIT	5
61 #define CMD_INTERRUPT	6
62 
63 #ifdef ALWAYS_USE_GUI
64     /* Can't do this when there is no terminal for input/output. */
65     if (!gui.in_use)
66     {
67 	/* Break as soon as possible. */
68 	debug_break_level = 9999;
69 	return;
70     }
71 #endif
72 
73     /* Make sure we are in raw mode and start termcap mode.  Might have side
74      * effects... */
75     settmode(TMODE_RAW);
76     starttermcap();
77 
78     ++RedrawingDisabled;	/* don't redisplay the window */
79     ++no_wait_return;		/* don't wait for return */
80     did_emsg = FALSE;		/* don't use error from debugged stuff */
81     cmd_silent = FALSE;		/* display commands */
82     msg_silent = FALSE;		/* display messages */
83     emsg_silent = FALSE;	/* display error messages */
84     redir_off = TRUE;		/* don't redirect debug commands */
85 
86     State = NORMAL;
87 #ifdef FEAT_SNIFF
88     want_sniff_request = 0;    /* No K_SNIFF wanted */
89 #endif
90 
91     if (!debug_did_msg)
92 	MSG(_("Entering Debug mode.  Type \"cont\" to continue."));
93     if (sourcing_name != NULL)
94 	msg(sourcing_name);
95     if (sourcing_lnum != 0)
96 	smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
97     else
98 	msg_str((char_u *)_("cmd: %s"), cmd);
99 
100     /*
101      * Repeat getting a command and executing it.
102      */
103     for (;;)
104     {
105 	msg_scroll = TRUE;
106 	need_wait_return = FALSE;
107 #ifdef FEAT_SNIFF
108 	ProcessSniffRequests();
109 #endif
110 	/* Save the current typeahead buffer and replace it with an empty one.
111 	 * This makes sure we get input from the user here and don't interfere
112 	 * with the commands being executed.  Reset "ex_normal_busy" to avoid
113 	 * the side effects of using ":normal". Save the stuff buffer and make
114 	 * it empty. */
115 # ifdef FEAT_EX_EXTRA
116 	save_ex_normal_busy = ex_normal_busy;
117 	ex_normal_busy = 0;
118 # endif
119 	if (!debug_greedy)
120 	    save_typeahead(&typeaheadbuf);
121 
122 	cmdline = getcmdline_prompt('>', NULL, 0);
123 
124 	if (!debug_greedy)
125 	    restore_typeahead(&typeaheadbuf);
126 # ifdef FEAT_EX_EXTRA
127 	ex_normal_busy = save_ex_normal_busy;
128 # endif
129 
130 	cmdline_row = msg_row;
131 	if (cmdline != NULL)
132 	{
133 	    /* If this is a debug command, set "last_cmd".
134 	     * If not, reset "last_cmd".
135 	     * For a blank line use previous command. */
136 	    p = skipwhite(cmdline);
137 	    if (*p != NUL)
138 	    {
139 		switch (*p)
140 		{
141 		    case 'c': last_cmd = CMD_CONT;
142 			      tail = "ont";
143 			      break;
144 		    case 'n': last_cmd = CMD_NEXT;
145 			      tail = "ext";
146 			      break;
147 		    case 's': last_cmd = CMD_STEP;
148 			      tail = "tep";
149 			      break;
150 		    case 'f': last_cmd = CMD_FINISH;
151 			      tail = "inish";
152 			      break;
153 		    case 'q': last_cmd = CMD_QUIT;
154 			      tail = "uit";
155 			      break;
156 		    case 'i': last_cmd = CMD_INTERRUPT;
157 			      tail = "nterrupt";
158 			      break;
159 		    default: last_cmd = 0;
160 		}
161 		if (last_cmd != 0)
162 		{
163 		    /* Check that the tail matches. */
164 		    ++p;
165 		    while (*p != NUL && *p == *tail)
166 		    {
167 			++p;
168 			++tail;
169 		    }
170 		    if (ASCII_ISALPHA(*p))
171 			last_cmd = 0;
172 		}
173 	    }
174 
175 	    if (last_cmd != 0)
176 	    {
177 		/* Execute debug command: decided where to break next and
178 		 * return. */
179 		switch (last_cmd)
180 		{
181 		    case CMD_CONT:
182 			debug_break_level = -1;
183 			break;
184 		    case CMD_NEXT:
185 			debug_break_level = ex_nesting_level;
186 			break;
187 		    case CMD_STEP:
188 			debug_break_level = 9999;
189 			break;
190 		    case CMD_FINISH:
191 			debug_break_level = ex_nesting_level - 1;
192 			break;
193 		    case CMD_QUIT:
194 			got_int = TRUE;
195 			debug_break_level = -1;
196 			break;
197 		    case CMD_INTERRUPT:
198 			got_int = TRUE;
199 			debug_break_level = 9999;
200 			/* Do not repeat ">interrupt" cmd, continue stepping. */
201 			last_cmd = CMD_STEP;
202 			break;
203 		}
204 		break;
205 	    }
206 
207 	    /* don't debug this command */
208 	    n = debug_break_level;
209 	    debug_break_level = -1;
210 	    (void)do_cmdline(cmdline, getexline, NULL,
211 						DOCMD_VERBOSE|DOCMD_EXCRESET);
212 	    debug_break_level = n;
213 
214 	    vim_free(cmdline);
215 	}
216 	lines_left = Rows - 1;
217     }
218     vim_free(cmdline);
219 
220     --RedrawingDisabled;
221     --no_wait_return;
222     redraw_all_later(NOT_VALID);
223     need_wait_return = FALSE;
224     msg_scroll = save_msg_scroll;
225     lines_left = Rows - 1;
226     State = save_State;
227     did_emsg = save_did_emsg;
228     cmd_silent = save_cmd_silent;
229     msg_silent = save_msg_silent;
230     emsg_silent = save_emsg_silent;
231     redir_off = save_redir_off;
232 
233     /* Only print the message again when typing a command before coming back
234      * here. */
235     debug_did_msg = TRUE;
236 }
237 
238 /*
239  * ":debug".
240  */
241     void
242 ex_debug(eap)
243     exarg_T	*eap;
244 {
245     int		debug_break_level_save = debug_break_level;
246 
247     debug_break_level = 9999;
248     do_cmdline_cmd(eap->arg);
249     debug_break_level = debug_break_level_save;
250 }
251 
252 static char_u	*debug_breakpoint_name = NULL;
253 static linenr_T	debug_breakpoint_lnum;
254 
255 /*
256  * When debugging or a breakpoint is set on a skipped command, no debug prompt
257  * is shown by do_one_cmd().  This situation is indicated by debug_skipped, and
258  * debug_skipped_name is then set to the source name in the breakpoint case.  If
259  * a skipped command decides itself that a debug prompt should be displayed, it
260  * can do so by calling dbg_check_skipped().
261  */
262 static int	debug_skipped;
263 static char_u	*debug_skipped_name;
264 
265 /*
266  * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
267  * at or below the break level.  But only when the line is actually
268  * executed.  Return TRUE and set breakpoint_name for skipped commands that
269  * decide to execute something themselves.
270  * Called from do_one_cmd() before executing a command.
271  */
272     void
273 dbg_check_breakpoint(eap)
274     exarg_T	*eap;
275 {
276     char_u	*p;
277 
278     debug_skipped = FALSE;
279     if (debug_breakpoint_name != NULL)
280     {
281 	if (!eap->skip)
282 	{
283 	    /* replace K_SNR with "<SNR>" */
284 	    if (debug_breakpoint_name[0] == K_SPECIAL
285 		    && debug_breakpoint_name[1] == KS_EXTRA
286 		    && debug_breakpoint_name[2] == (int)KE_SNR)
287 		p = (char_u *)"<SNR>";
288 	    else
289 		p = (char_u *)"";
290 	    smsg((char_u *)_("Breakpoint in \"%s%s\" line %ld"), p,
291 		    debug_breakpoint_name + (*p == NUL ? 0 : 3),
292 		    (long)debug_breakpoint_lnum);
293 	    debug_breakpoint_name = NULL;
294 	    do_debug(eap->cmd);
295 	}
296 	else
297 	{
298 	    debug_skipped = TRUE;
299 	    debug_skipped_name = debug_breakpoint_name;
300 	    debug_breakpoint_name = NULL;
301 	}
302     }
303     else if (ex_nesting_level <= debug_break_level)
304     {
305 	if (!eap->skip)
306 	    do_debug(eap->cmd);
307 	else
308 	{
309 	    debug_skipped = TRUE;
310 	    debug_skipped_name = NULL;
311 	}
312     }
313 }
314 
315 /*
316  * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
317  * set.  Return TRUE when the debug mode is entered this time.
318  */
319     int
320 dbg_check_skipped(eap)
321     exarg_T	*eap;
322 {
323     int		prev_got_int;
324 
325     if (debug_skipped)
326     {
327 	/*
328 	 * Save the value of got_int and reset it.  We don't want a previous
329 	 * interruption cause flushing the input buffer.
330 	 */
331 	prev_got_int = got_int;
332 	got_int = FALSE;
333 	debug_breakpoint_name = debug_skipped_name;
334 	/* eap->skip is TRUE */
335 	eap->skip = FALSE;
336 	(void)dbg_check_breakpoint(eap);
337 	eap->skip = TRUE;
338 	got_int |= prev_got_int;
339 	return TRUE;
340     }
341     return FALSE;
342 }
343 
344 /*
345  * The list of breakpoints: dbg_breakp.
346  * This is a grow-array of structs.
347  */
348 struct debuggy
349 {
350     int		dbg_nr;		/* breakpoint number */
351     int		dbg_type;	/* DBG_FUNC or DBG_FILE */
352     char_u	*dbg_name;	/* function or file name */
353     regprog_T	*dbg_prog;	/* regexp program */
354     linenr_T	dbg_lnum;	/* line number in function or file */
355 };
356 
357 static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
358 #define BREAKP(idx)	(((struct debuggy *)dbg_breakp.ga_data)[idx])
359 static int last_breakp = 0;	/* nr of last defined breakpoint */
360 
361 #define DBG_FUNC	1
362 #define DBG_FILE	2
363 
364 static int dbg_parsearg __ARGS((char_u *arg));
365 
366 /*
367  * Parse the arguments of ":breakadd" or ":breakdel" and put them in the entry
368  * just after the last one in dbg_breakp.  Note that "dbg_name" is allocated.
369  * Returns FAIL for failure.
370  */
371     static int
372 dbg_parsearg(arg)
373     char_u	*arg;
374 {
375     char_u	*p = arg;
376     char_u	*q;
377     struct debuggy *bp;
378     int		here = FALSE;
379 
380     if (ga_grow(&dbg_breakp, 1) == FAIL)
381 	return FAIL;
382     bp = &BREAKP(dbg_breakp.ga_len);
383 
384     /* Find "func" or "file". */
385     if (STRNCMP(p, "func", 4) == 0)
386 	bp->dbg_type = DBG_FUNC;
387     else if (STRNCMP(p, "file", 4) == 0)
388 	bp->dbg_type = DBG_FILE;
389     else if (STRNCMP(p, "here", 4) == 0)
390     {
391 	if (curbuf->b_ffname == NULL)
392 	{
393 	    EMSG(_(e_noname));
394 	    return FAIL;
395 	}
396 	bp->dbg_type = DBG_FILE;
397 	here = TRUE;
398     }
399     else
400     {
401 	EMSG2(_(e_invarg2), p);
402 	return FAIL;
403     }
404     p = skipwhite(p + 4);
405 
406     /* Find optional line number. */
407     if (here)
408 	bp->dbg_lnum = curwin->w_cursor.lnum;
409     else if (VIM_ISDIGIT(*p))
410     {
411 	bp->dbg_lnum = getdigits(&p);
412 	p = skipwhite(p);
413     }
414     else
415 	bp->dbg_lnum = 0;
416 
417     /* Find the function or file name.  Don't accept a function name with (). */
418     if ((!here && *p == NUL)
419 	    || (here && *p != NUL)
420 	    || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL))
421     {
422 	EMSG2(_(e_invarg2), arg);
423 	return FAIL;
424     }
425 
426     if (bp->dbg_type == DBG_FUNC)
427 	bp->dbg_name = vim_strsave(p);
428     else if (here)
429 	bp->dbg_name = vim_strsave(curbuf->b_ffname);
430     else
431     {
432 	/* Expand the file name in the same way as do_source().  This means
433 	 * doing it twice, so that $DIR/file gets expanded when $DIR is
434 	 * "~/dir". */
435 #ifdef RISCOS
436 	q = mch_munge_fname(p);
437 #else
438 	q = expand_env_save(p);
439 #endif
440 	if (q == NULL)
441 	    return FAIL;
442 #ifdef RISCOS
443 	p = mch_munge_fname(q);
444 #else
445 	p = expand_env_save(q);
446 #endif
447 	vim_free(q);
448 	if (p == NULL)
449 	    return FAIL;
450 	bp->dbg_name = p;
451 #ifdef MACOS_CLASSIC
452 	if (bp->dbg_name != NULL)
453 	    slash_n_colon_adjust(bp->dbg_name);
454 #endif
455     }
456 
457     if (bp->dbg_name == NULL)
458 	return FAIL;
459     return OK;
460 }
461 
462 /*
463  * ":breakadd".
464  */
465     void
466 ex_breakadd(eap)
467     exarg_T	*eap;
468 {
469     struct debuggy *bp;
470     char_u	*pat;
471 
472     if (dbg_parsearg(eap->arg) == OK)
473     {
474 	bp = &BREAKP(dbg_breakp.ga_len);
475 	pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
476 	if (pat != NULL)
477 	{
478 	    bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
479 	    vim_free(pat);
480 	}
481 	if (pat == NULL || bp->dbg_prog == NULL)
482 	    vim_free(bp->dbg_name);
483 	else
484 	{
485 	    if (bp->dbg_lnum == 0)	/* default line number is 1 */
486 		bp->dbg_lnum = 1;
487 	    BREAKP(dbg_breakp.ga_len++).dbg_nr = ++last_breakp;
488 	    --dbg_breakp.ga_room;
489 	    ++debug_tick;
490 	}
491     }
492 }
493 
494 /*
495  * ":debuggreedy".
496  */
497     void
498 ex_debuggreedy(eap)
499     exarg_T	*eap;
500 {
501     if (eap->addr_count == 0 || eap->line2 != 0)
502 	debug_greedy = TRUE;
503     else
504 	debug_greedy = FALSE;
505 }
506 
507 /*
508  * ":breakdel".
509  */
510     void
511 ex_breakdel(eap)
512     exarg_T	*eap;
513 {
514     struct debuggy *bp, *bpi;
515     int		nr;
516     int		todel = -1;
517     int		i;
518     linenr_T	best_lnum = 0;
519 
520     if (vim_isdigit(*eap->arg))
521     {
522 	/* ":breakdel {nr}" */
523 	nr = atol((char *)eap->arg);
524 	for (i = 0; i < dbg_breakp.ga_len; ++i)
525 	    if (BREAKP(i).dbg_nr == nr)
526 	    {
527 		todel = i;
528 		break;
529 	    }
530     }
531     else
532     {
533 	/* ":breakdel {func|file} [lnum] {name}" */
534 	if (dbg_parsearg(eap->arg) == FAIL)
535 	    return;
536 	bp = &BREAKP(dbg_breakp.ga_len);
537 	for (i = 0; i < dbg_breakp.ga_len; ++i)
538 	{
539 	    bpi = &BREAKP(i);
540 	    if (bp->dbg_type == bpi->dbg_type
541 		    && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
542 		    && (bp->dbg_lnum == bpi->dbg_lnum
543 			|| (bp->dbg_lnum == 0
544 			    && (best_lnum == 0
545 				|| bpi->dbg_lnum < best_lnum))))
546 	    {
547 		todel = i;
548 		best_lnum = bpi->dbg_lnum;
549 	    }
550 	}
551 	vim_free(bp->dbg_name);
552     }
553 
554     if (todel < 0)
555 	EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
556     else
557     {
558 	vim_free(BREAKP(todel).dbg_name);
559 	vim_free(BREAKP(todel).dbg_prog);
560 	--dbg_breakp.ga_len;
561 	++dbg_breakp.ga_room;
562 	if (todel < dbg_breakp.ga_len)
563 	    mch_memmove(&BREAKP(todel), &BREAKP(todel + 1),
564 		    (dbg_breakp.ga_len - todel) * sizeof(struct debuggy));
565 	++debug_tick;
566     }
567 }
568 
569 /*
570  * ":breaklist".
571  */
572 /*ARGSUSED*/
573     void
574 ex_breaklist(eap)
575     exarg_T	*eap;
576 {
577     struct debuggy *bp;
578     int		i;
579 
580     if (dbg_breakp.ga_len == 0)
581 	MSG(_("No breakpoints defined"));
582     else
583 	for (i = 0; i < dbg_breakp.ga_len; ++i)
584 	{
585 	    bp = &BREAKP(i);
586 	    smsg((char_u *)_("%3d  %s %s  line %ld"),
587 		    bp->dbg_nr,
588 		    bp->dbg_type == DBG_FUNC ? "func" : "file",
589 		    bp->dbg_name,
590 		    (long)bp->dbg_lnum);
591 	}
592 }
593 
594 /*
595  * Find a breakpoint for a function or sourced file.
596  * Returns line number at which to break; zero when no matching breakpoint.
597  */
598     linenr_T
599 dbg_find_breakpoint(file, fname, after)
600     int		file;	    /* TRUE for a file, FALSE for a function */
601     char_u	*fname;	    /* file or function name */
602     linenr_T	after;	    /* after this line number */
603 {
604     struct debuggy *bp;
605     int		i;
606     linenr_T	lnum = 0;
607     regmatch_T	regmatch;
608     char_u	*name = fname;
609     int		prev_got_int;
610 
611     /* Replace K_SNR in function name with "<SNR>". */
612     if (!file && fname[0] == K_SPECIAL)
613     {
614 	name = alloc((unsigned)STRLEN(fname) + 3);
615 	if (name == NULL)
616 	    name = fname;
617 	else
618 	{
619 	    STRCPY(name, "<SNR>");
620 	    STRCPY(name + 5, fname + 3);
621 	}
622     }
623 
624     for (i = 0; i < dbg_breakp.ga_len; ++i)
625     {
626 	/* skip entries that are not useful or are for a line that is beyond
627 	 * an already found breakpoint */
628 	bp = &BREAKP(i);
629 	if ((bp->dbg_type == DBG_FILE) == file
630 		&& bp->dbg_lnum > after
631 		&& (lnum == 0 || bp->dbg_lnum < lnum))
632 	{
633 	    regmatch.regprog = bp->dbg_prog;
634 	    regmatch.rm_ic = FALSE;
635 	    /*
636 	     * Save the value of got_int and reset it.  We don't want a previous
637 	     * interruption cancel matching, only hitting CTRL-C while matching
638 	     * should abort it.
639 	     */
640 	    prev_got_int = got_int;
641 	    got_int = FALSE;
642 	    if (vim_regexec(&regmatch, name, (colnr_T)0))
643 		lnum = bp->dbg_lnum;
644 	    got_int |= prev_got_int;
645 	}
646     }
647     if (name != fname)
648 	vim_free(name);
649 
650     return lnum;
651 }
652 
653 /*
654  * Called when a breakpoint was encountered.
655  */
656     void
657 dbg_breakpoint(name, lnum)
658     char_u	*name;
659     linenr_T	lnum;
660 {
661     /* We need to check if this line is actually executed in do_one_cmd() */
662     debug_breakpoint_name = name;
663     debug_breakpoint_lnum = lnum;
664 }
665 #endif
666 
667 /*
668  * If 'autowrite' option set, try to write the file.
669  * Careful: autocommands may make "buf" invalid!
670  *
671  * return FAIL for failure, OK otherwise
672  */
673     int
674 autowrite(buf, forceit)
675     buf_T	*buf;
676     int		forceit;
677 {
678     if (!(p_aw || p_awa) || !p_write
679 #ifdef FEAT_QUICKFIX
680 	/* never autowrite a "nofile" or "nowrite" buffer */
681 	|| bt_dontwrite(buf)
682 #endif
683 	|| (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
684 	return FAIL;
685     return buf_write_all(buf, forceit);
686 }
687 
688 /*
689  * flush all buffers, except the ones that are readonly
690  */
691     void
692 autowrite_all()
693 {
694     buf_T	*buf;
695 
696     if (!(p_aw || p_awa) || !p_write)
697 	return;
698     for (buf = firstbuf; buf; buf = buf->b_next)
699 	if (bufIsChanged(buf) && !buf->b_p_ro)
700 	{
701 	    (void)buf_write_all(buf, FALSE);
702 #ifdef FEAT_AUTOCMD
703 	    /* an autocommand may have deleted the buffer */
704 	    if (!buf_valid(buf))
705 		buf = firstbuf;
706 #endif
707 	}
708 }
709 
710 /*
711  * return TRUE if buffer was changed and cannot be abandoned.
712  */
713 /*ARGSUSED*/
714     int
715 check_changed(buf, checkaw, mult_win, forceit, allbuf)
716     buf_T	*buf;
717     int		checkaw;	/* do autowrite if buffer was changed */
718     int		mult_win;	/* check also when several wins for the buf */
719     int		forceit;
720     int		allbuf;		/* may write all buffers */
721 {
722     if (       !forceit
723 	    && bufIsChanged(buf)
724 	    && (mult_win || buf->b_nwindows <= 1)
725 	    && (!checkaw || autowrite(buf, forceit) == FAIL))
726     {
727 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
728 	if ((p_confirm || cmdmod.confirm) && p_write)
729 	{
730 	    buf_T	*buf2;
731 	    int		count = 0;
732 
733 	    if (allbuf)
734 		for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
735 		    if (bufIsChanged(buf2)
736 				     && (buf2->b_ffname != NULL
737 # ifdef FEAT_BROWSE
738 					 || cmdmod.browse
739 # endif
740 					))
741 			++count;
742 # ifdef FEAT_AUTOCMD
743 	    if (!buf_valid(buf))
744 		/* Autocommand deleted buffer, oops!  It's not changed now. */
745 		return FALSE;
746 # endif
747 	    dialog_changed(buf, count > 1);
748 # ifdef FEAT_AUTOCMD
749 	    if (!buf_valid(buf))
750 		/* Autocommand deleted buffer, oops!  It's not changed now. */
751 		return FALSE;
752 # endif
753 	    return bufIsChanged(buf);
754 	}
755 #endif
756 	EMSG(_(e_nowrtmsg));
757 	return TRUE;
758     }
759     return FALSE;
760 }
761 
762 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
763 
764 #if defined(FEAT_BROWSE) || defined(PROTO)
765 /*
766  * When wanting to write a file without a file name, ask the user for a name.
767  */
768     void
769 browse_save_fname(buf)
770     buf_T	*buf;
771 {
772     if (buf->b_fname == NULL)
773     {
774 	char_u *fname;
775 
776 	fname = do_browse(TRUE, (char_u *)_("Save As"), NULL, NULL, NULL,
777 								   NULL, buf);
778 	if (fname != NULL)
779 	{
780 	    if (setfname(buf, fname, NULL, TRUE) == OK)
781 		buf->b_flags |= BF_NOTEDITED;
782 	    vim_free(fname);
783 	}
784     }
785 }
786 #endif
787 
788 /*
789  * Ask the user what to do when abondoning a changed buffer.
790  * Must check 'write' option first!
791  */
792     void
793 dialog_changed(buf, checkall)
794     buf_T	*buf;
795     int		checkall;	/* may abandon all changed buffers */
796 {
797     char_u	buff[IOSIZE];
798     int		ret;
799     buf_T	*buf2;
800 
801     dialog_msg(buff, _("Save changes to \"%.*s\"?"),
802 			(buf->b_fname != NULL) ?
803 			buf->b_fname : (char_u *)_("Untitled"));
804     if (checkall)
805 	ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
806     else
807 	ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
808 
809     if (ret == VIM_YES)
810     {
811 #ifdef FEAT_BROWSE
812 	/* May get file name, when there is none */
813 	browse_save_fname(buf);
814 #endif
815 	if (buf->b_fname != NULL)   /* didn't hit Cancel */
816 	    (void)buf_write_all(buf, FALSE);
817     }
818     else if (ret == VIM_NO)
819     {
820 	unchanged(buf, TRUE);
821     }
822     else if (ret == VIM_ALL)
823     {
824 	/*
825 	 * Write all modified files that can be written.
826 	 * Skip readonly buffers, these need to be confirmed
827 	 * individually.
828 	 */
829 	for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
830 	{
831 	    if (bufIsChanged(buf2)
832 		    && (buf2->b_ffname != NULL
833 #ifdef FEAT_BROWSE
834 			|| cmdmod.browse
835 #endif
836 			)
837 		    && !buf2->b_p_ro)
838 	    {
839 #ifdef FEAT_BROWSE
840 		/* May get file name, when there is none */
841 		browse_save_fname(buf2);
842 #endif
843 		if (buf2->b_fname != NULL)   /* didn't hit Cancel */
844 		    (void)buf_write_all(buf2, FALSE);
845 #ifdef FEAT_AUTOCMD
846 		/* an autocommand may have deleted the buffer */
847 		if (!buf_valid(buf2))
848 		    buf2 = firstbuf;
849 #endif
850 	    }
851 	}
852     }
853     else if (ret == VIM_DISCARDALL)
854     {
855 	/*
856 	 * mark all buffers as unchanged
857 	 */
858 	for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
859 	    unchanged(buf2, TRUE);
860     }
861 }
862 #endif
863 
864 /*
865  * Return TRUE if the buffer "buf" can be abandoned, either by making it
866  * hidden, autowriting it or unloading it.
867  */
868     int
869 can_abandon(buf, forceit)
870     buf_T	*buf;
871     int		forceit;
872 {
873     return (	   P_HID(buf)
874 		|| !bufIsChanged(buf)
875 		|| buf->b_nwindows > 1
876 		|| autowrite(buf, forceit) == OK
877 		|| forceit);
878 }
879 
880 /*
881  * Return TRUE if any buffer was changed and cannot be abandoned.
882  * That changed buffer becomes the current buffer.
883  */
884     int
885 check_changed_any(hidden)
886     int		hidden;		/* Only check hidden buffers */
887 {
888     buf_T	*buf;
889     int		save;
890 #ifdef FEAT_WINDOWS
891     win_T	*wp;
892 #endif
893 
894     for (;;)
895     {
896 	/* check curbuf first: if it was changed we can't abandon it */
897 	if (!hidden && curbufIsChanged())
898 	    buf = curbuf;
899 	else
900 	{
901 	    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
902 		if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
903 		    break;
904 	}
905 	if (buf == NULL)    /* No buffers changed */
906 	    return FALSE;
907 
908 	if (check_changed(buf, p_awa, TRUE, FALSE, TRUE) && buf_valid(buf))
909 	    break;	    /* didn't save - still changes */
910     }
911 
912     exiting = FALSE;
913 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
914     /*
915      * When ":confirm" used, don't give an error message.
916      */
917     if (!(p_confirm || cmdmod.confirm))
918 #endif
919     {
920 	/* There must be a wait_return for this message, do_buffer()
921 	 * may cause a redraw.  But wait_return() is a no-op when vgetc()
922 	 * is busy (Quit used from window menu), then make sure we don't
923 	 * cause a scroll up. */
924 	if (vgetc_busy)
925 	{
926 	    msg_row = cmdline_row;
927 	    msg_col = 0;
928 	    msg_didout = FALSE;
929 	}
930 	if (EMSG2(_("E162: No write since last change for buffer \"%s\""),
931 		    buf_spname(buf) != NULL ? (char_u *)buf_spname(buf) :
932 		    buf->b_fname))
933 	{
934 	    save = no_wait_return;
935 	    no_wait_return = FALSE;
936 	    wait_return(FALSE);
937 	    no_wait_return = save;
938 	}
939     }
940 
941 #ifdef FEAT_WINDOWS
942     /* Try to find a window that contains the buffer. */
943     if (buf != curbuf)
944 	for (wp = firstwin; wp != NULL; wp = wp->w_next)
945 	    if (wp->w_buffer == buf)
946 	    {
947 		win_goto(wp);
948 # ifdef FEAT_AUTOCMD
949 		/* Paranoia: did autocms wipe out the buffer with changes? */
950 		if (!buf_valid(buf))
951 		    return TRUE;
952 # endif
953 		break;
954 	    }
955 #endif
956 
957     /* Open the changed buffer in the current window. */
958     if (buf != curbuf)
959 	set_curbuf(buf, DOBUF_GOTO);
960 
961     return TRUE;
962 }
963 
964 /*
965  * return FAIL if there is no file name, OK if there is one
966  * give error message for FAIL
967  */
968     int
969 check_fname()
970 {
971     if (curbuf->b_ffname == NULL)
972     {
973 	EMSG(_(e_noname));
974 	return FAIL;
975     }
976     return OK;
977 }
978 
979 /*
980  * flush the contents of a buffer, unless it has no file name
981  *
982  * return FAIL for failure, OK otherwise
983  */
984     int
985 buf_write_all(buf, forceit)
986     buf_T	*buf;
987     int		forceit;
988 {
989     int	    retval;
990 #ifdef FEAT_AUTOCMD
991     buf_T	*old_curbuf = curbuf;
992 #endif
993 
994     retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
995 				   (linenr_T)1, buf->b_ml.ml_line_count, NULL,
996 						  FALSE, forceit, TRUE, FALSE));
997 #ifdef FEAT_AUTOCMD
998     if (curbuf != old_curbuf)
999 	MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
1000 #endif
1001     return retval;
1002 }
1003 
1004 /*
1005  * Code to handle the argument list.
1006  */
1007 
1008 /*
1009  * Isolate one argument, taking quotes and backticks.
1010  * Changes the argument in-place, puts a NUL after it.
1011  * Quotes are removed, backticks remain.
1012  * Return a pointer to the start of the next argument.
1013  */
1014     char_u *
1015 do_one_arg(str)
1016     char_u *str;
1017 {
1018     char_u	*p;
1019     int		inquote;
1020     int		inbacktick;
1021 
1022     inquote = FALSE;
1023     inbacktick = FALSE;
1024     for (p = str; *str; ++str)
1025     {
1026 	/*
1027 	 * for MSDOS et.al. a backslash is part of a file name.
1028 	 * Only skip ", space and tab.
1029 	 */
1030 	if (rem_backslash(str))
1031 	{
1032 	    *p++ = *str++;
1033 	    *p++ = *str;
1034 	}
1035 	else
1036 	{
1037 	    /* An item ends at a space not in quotes or backticks */
1038 	    if (!inquote && !inbacktick && vim_isspace(*str))
1039 		break;
1040 	    if (!inquote && *str == '`')
1041 		inbacktick ^= TRUE;
1042 	    if (!inbacktick && *str == '"')
1043 		inquote ^= TRUE;
1044 	    else
1045 		*p++ = *str;
1046 	}
1047     }
1048     str = skipwhite(str);
1049     *p = NUL;
1050 
1051     return str;
1052 }
1053 
1054 static int do_arglist __ARGS((char_u *str, int what, int after));
1055 static void alist_check_arg_idx __ARGS((void));
1056 #ifdef FEAT_LISTCMDS
1057 static int alist_add_list __ARGS((int count, char_u **files, int after));
1058 #endif
1059 #define AL_SET	1
1060 #define AL_ADD	2
1061 #define AL_DEL	3
1062 
1063 #if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
1064 /*
1065  * Redefine the argument list.
1066  */
1067     void
1068 set_arglist(str)
1069     char_u	*str;
1070 {
1071     do_arglist(str, AL_SET, 0);
1072 }
1073 #endif
1074 
1075 /*
1076  * "what" == AL_SET: Redefine the argument list to 'str'.
1077  * "what" == AL_ADD: add files in 'str' to the argument list after "after".
1078  * "what" == AL_DEL: remove files in 'str' from the argument list.
1079  *
1080  * Return FAIL for failure, OK otherwise.
1081  */
1082 /*ARGSUSED*/
1083     static int
1084 do_arglist(str, what, after)
1085     char_u	*str;
1086     int		what;
1087     int		after;		/* 0 means before first one */
1088 {
1089     garray_T	new_ga;
1090     int		exp_count;
1091     char_u	**exp_files;
1092     int		i;
1093 #ifdef FEAT_LISTCMDS
1094     char_u	*p;
1095     int		match;
1096 #endif
1097 
1098     /*
1099      * Collect all file name arguments in "new_ga".
1100      */
1101     ga_init2(&new_ga, (int)sizeof(char_u *), 20);
1102     while (*str)
1103     {
1104 	if (ga_grow(&new_ga, 1) == FAIL)
1105 	{
1106 	    ga_clear(&new_ga);
1107 	    return FAIL;
1108 	}
1109 	((char_u **)new_ga.ga_data)[new_ga.ga_len++] = str;
1110 	--new_ga.ga_room;
1111 
1112 	/* Isolate one argument, change it in-place, put a NUL after it. */
1113 	str = do_one_arg(str);
1114     }
1115 
1116 #ifdef FEAT_LISTCMDS
1117     if (what == AL_DEL)
1118     {
1119 	regmatch_T	regmatch;
1120 	int		didone;
1121 
1122 	/*
1123 	 * Delete the items: use each item as a regexp and find a match in the
1124 	 * argument list.
1125 	 */
1126 #ifdef CASE_INSENSITIVE_FILENAME
1127 	regmatch.rm_ic = TRUE;		/* Always ignore case */
1128 #else
1129 	regmatch.rm_ic = FALSE;		/* Never ignore case */
1130 #endif
1131 	for (i = 0; i < new_ga.ga_len && !got_int; ++i)
1132 	{
1133 	    p = ((char_u **)new_ga.ga_data)[i];
1134 	    p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
1135 	    if (p == NULL)
1136 		break;
1137 	    regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1138 	    if (regmatch.regprog == NULL)
1139 	    {
1140 		vim_free(p);
1141 		break;
1142 	    }
1143 
1144 	    didone = FALSE;
1145 	    for (match = 0; match < ARGCOUNT; ++match)
1146 		if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1147 								  (colnr_T)0))
1148 		{
1149 		    didone = TRUE;
1150 		    vim_free(ARGLIST[match].ae_fname);
1151 		    mch_memmove(ARGLIST + match, ARGLIST + match + 1,
1152 			    (ARGCOUNT - match - 1) * sizeof(aentry_T));
1153 		    --ALIST(curwin)->al_ga.ga_len;
1154 		    ++ALIST(curwin)->al_ga.ga_room;
1155 		    if (curwin->w_arg_idx > match)
1156 			--curwin->w_arg_idx;
1157 		    --match;
1158 		}
1159 
1160 	    vim_free(regmatch.regprog);
1161 	    vim_free(p);
1162 	    if (!didone)
1163 		EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
1164 	}
1165 	ga_clear(&new_ga);
1166     }
1167     else
1168 #endif
1169     {
1170 	i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1171 		&exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1172 	ga_clear(&new_ga);
1173 	if (i == FAIL)
1174 	    return FAIL;
1175 	if (exp_count == 0)
1176 	{
1177 	    EMSG(_(e_nomatch));
1178 	    return FAIL;
1179 	}
1180 
1181 #ifdef FEAT_LISTCMDS
1182 	if (what == AL_ADD)
1183 	{
1184 	    (void)alist_add_list(exp_count, exp_files, after);
1185 	    vim_free(exp_files);
1186 	}
1187 	else /* what == AL_SET */
1188 #endif
1189 	    alist_set(ALIST(curwin), exp_count, exp_files, FALSE);
1190     }
1191 
1192     alist_check_arg_idx();
1193 
1194     return OK;
1195 }
1196 
1197 /*
1198  * Check the validity of the arg_idx for each other window.
1199  */
1200     static void
1201 alist_check_arg_idx()
1202 {
1203 #ifdef FEAT_WINDOWS
1204     win_T	*win;
1205 
1206     for (win = firstwin; win != NULL; win = win->w_next)
1207 	if (win->w_alist == curwin->w_alist)
1208 	    check_arg_idx(win);
1209 #else
1210     check_arg_idx(curwin);
1211 #endif
1212 }
1213 
1214 /*
1215  * Check if window "win" is editing the w_arg_idx file in its argument list.
1216  */
1217     void
1218 check_arg_idx(win)
1219     win_T	*win;
1220 {
1221     if (WARGCOUNT(win) > 1
1222 	    && (win->w_arg_idx >= WARGCOUNT(win)
1223 		|| (win->w_buffer->b_fnum
1224 				      != WARGLIST(win)[win->w_arg_idx].ae_fnum
1225 		    && (win->w_buffer->b_ffname == NULL
1226 			 || !(fullpathcmp(
1227 				 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1228 				win->w_buffer->b_ffname, TRUE) & FPC_SAME)))))
1229     {
1230 	/* We are not editing the current entry in the argument list.
1231 	 * Set "arg_had_last" if we are editing the last one. */
1232 	win->w_arg_idx_invalid = TRUE;
1233 	if (win->w_arg_idx != WARGCOUNT(win) - 1
1234 		&& arg_had_last == FALSE
1235 #ifdef FEAT_WINDOWS
1236 		&& ALIST(win) == &global_alist
1237 #endif
1238 		&& GARGCOUNT > 0
1239 		&& win->w_arg_idx < GARGCOUNT
1240 		&& (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1241 		    || (win->w_buffer->b_ffname != NULL
1242 			&& (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
1243 				win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
1244 	    arg_had_last = TRUE;
1245     }
1246     else
1247     {
1248 	/* We are editing the current entry in the argument list.
1249 	 * Set "arg_had_last" if it's also the last one */
1250 	win->w_arg_idx_invalid = FALSE;
1251 	if (win->w_arg_idx == WARGCOUNT(win) - 1
1252 #ifdef FEAT_WINDOWS
1253 		&& win->w_alist == &global_alist
1254 #endif
1255 		)
1256 	    arg_had_last = TRUE;
1257     }
1258 }
1259 
1260 /*
1261  * ":args", ":argslocal" and ":argsglobal".
1262  */
1263     void
1264 ex_args(eap)
1265     exarg_T	*eap;
1266 {
1267     int		i;
1268 
1269     if (eap->cmdidx != CMD_args)
1270     {
1271 #if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1272 	alist_unlink(ALIST(curwin));
1273 	if (eap->cmdidx == CMD_argglobal)
1274 	    ALIST(curwin) = &global_alist;
1275 	else /* eap->cmdidx == CMD_arglocal */
1276 	    alist_new();
1277 #else
1278 	ex_ni(eap);
1279 	return;
1280 #endif
1281     }
1282 
1283     if (!ends_excmd(*eap->arg))
1284     {
1285 	/*
1286 	 * ":args file ..": define new argument list, handle like ":next"
1287 	 * Also for ":argslocal file .." and ":argsglobal file ..".
1288 	 */
1289 	ex_next(eap);
1290     }
1291     else
1292 #if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1293 	if (eap->cmdidx == CMD_args)
1294 #endif
1295     {
1296 	/*
1297 	 * ":args": list arguments.
1298 	 */
1299 	if (ARGCOUNT > 0)
1300 	{
1301 	    /* Overwrite the command, for a short list there is no scrolling
1302 	     * required and no wait_return(). */
1303 	    gotocmdline(TRUE);
1304 	    for (i = 0; i < ARGCOUNT; ++i)
1305 	    {
1306 		if (i == curwin->w_arg_idx)
1307 		    msg_putchar('[');
1308 		msg_outtrans(alist_name(&ARGLIST[i]));
1309 		if (i == curwin->w_arg_idx)
1310 		    msg_putchar(']');
1311 		msg_putchar(' ');
1312 	    }
1313 	}
1314     }
1315 #if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1316     else if (eap->cmdidx == CMD_arglocal)
1317     {
1318 	garray_T	*gap = &curwin->w_alist->al_ga;
1319 
1320 	/*
1321 	 * ":argslocal": make a local copy of the global argument list.
1322 	 */
1323 	if (ga_grow(gap, GARGCOUNT) == OK)
1324 	    for (i = 0; i < GARGCOUNT; ++i)
1325 		if (GARGLIST[i].ae_fname != NULL)
1326 		{
1327 		    AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1328 					    vim_strsave(GARGLIST[i].ae_fname);
1329 		    AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1330 							  GARGLIST[i].ae_fnum;
1331 		    ++gap->ga_len;
1332 		    --gap->ga_room;
1333 		}
1334     }
1335 #endif
1336 }
1337 
1338 /*
1339  * ":previous", ":sprevious", ":Next" and ":sNext".
1340  */
1341     void
1342 ex_previous(eap)
1343     exarg_T	*eap;
1344 {
1345     /* If past the last one already, go to the last one. */
1346     if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
1347 	do_argfile(eap, ARGCOUNT - 1);
1348     else
1349 	do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1350 }
1351 
1352 /*
1353  * ":rewind", ":first", ":sfirst" and ":srewind".
1354  */
1355     void
1356 ex_rewind(eap)
1357     exarg_T	*eap;
1358 {
1359     do_argfile(eap, 0);
1360 }
1361 
1362 /*
1363  * ":last" and ":slast".
1364  */
1365     void
1366 ex_last(eap)
1367     exarg_T	*eap;
1368 {
1369     do_argfile(eap, ARGCOUNT - 1);
1370 }
1371 
1372 /*
1373  * ":argument" and ":sargument".
1374  */
1375     void
1376 ex_argument(eap)
1377     exarg_T	*eap;
1378 {
1379     int		i;
1380 
1381     if (eap->addr_count > 0)
1382 	i = eap->line2 - 1;
1383     else
1384 	i = curwin->w_arg_idx;
1385     do_argfile(eap, i);
1386 }
1387 
1388 /*
1389  * Edit file "argn" of the argument lists.
1390  */
1391     void
1392 do_argfile(eap, argn)
1393     exarg_T	*eap;
1394     int		argn;
1395 {
1396     int		other;
1397     char_u	*p;
1398 
1399     if (argn < 0 || argn >= ARGCOUNT)
1400     {
1401 	if (ARGCOUNT <= 1)
1402 	    EMSG(_("E163: There is only one file to edit"));
1403 	else if (argn < 0)
1404 	    EMSG(_("E164: Cannot go before first file"));
1405 	else
1406 	    EMSG(_("E165: Cannot go beyond last file"));
1407     }
1408     else
1409     {
1410 	setpcmark();
1411 #ifdef FEAT_GUI
1412 	need_mouse_correct = TRUE;
1413 #endif
1414 
1415 #ifdef FEAT_WINDOWS
1416 	if (*eap->cmd == 's')	    /* split window first */
1417 	{
1418 	    if (win_split(0, 0) == FAIL)
1419 		return;
1420 # ifdef FEAT_SCROLLBIND
1421 	    curwin->w_p_scb = FALSE;
1422 # endif
1423 	}
1424 	else
1425 #endif
1426 	{
1427 	    /*
1428 	     * if 'hidden' set, only check for changed file when re-editing
1429 	     * the same buffer
1430 	     */
1431 	    other = TRUE;
1432 	    if (P_HID(curbuf))
1433 	    {
1434 		p = fix_fname(alist_name(&ARGLIST[argn]));
1435 		other = otherfile(p);
1436 		vim_free(p);
1437 	    }
1438 	    if ((!P_HID(curbuf) || !other)
1439 		  && check_changed(curbuf, TRUE, !other, eap->forceit, FALSE))
1440 		return;
1441 	}
1442 
1443 	curwin->w_arg_idx = argn;
1444 	if (argn == ARGCOUNT - 1
1445 #ifdef FEAT_WINDOWS
1446 		&& curwin->w_alist == &global_alist
1447 #endif
1448 	   )
1449 	    arg_had_last = TRUE;
1450 
1451 	/* Edit the file; always use the last known line number. */
1452 	(void)do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
1453 		      eap, ECMD_LAST,
1454 		      (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) +
1455 					   (eap->forceit ? ECMD_FORCEIT : 0));
1456 
1457 	/* like Vi: set the mark where the cursor is in the file. */
1458 	if (eap->cmdidx != CMD_argdo)
1459 	    setmark('\'');
1460     }
1461 }
1462 
1463 /*
1464  * ":next", and commands that behave like it.
1465  */
1466     void
1467 ex_next(eap)
1468     exarg_T	*eap;
1469 {
1470     int		i;
1471 
1472     /*
1473      * check for changed buffer now, if this fails the argument list is not
1474      * redefined.
1475      */
1476     if (       P_HID(curbuf)
1477 	    || eap->cmdidx == CMD_snext
1478 	    || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
1479     {
1480 	if (*eap->arg != NUL)		    /* redefine file list */
1481 	{
1482 	    if (do_arglist(eap->arg, AL_SET, 0) == FAIL)
1483 		return;
1484 	    i = 0;
1485 	}
1486 	else
1487 	    i = curwin->w_arg_idx + (int)eap->line2;
1488 	do_argfile(eap, i);
1489     }
1490 }
1491 
1492 #ifdef FEAT_LISTCMDS
1493 /*
1494  * ":argedit"
1495  */
1496     void
1497 ex_argedit(eap)
1498     exarg_T	*eap;
1499 {
1500     int		fnum;
1501     int		i;
1502     char_u	*s;
1503 
1504     /* Add the argument to the buffer list and get the buffer number. */
1505     fnum = buflist_add(eap->arg, BLN_LISTED);
1506 
1507     /* Check if this argument is already in the argument list. */
1508     for (i = 0; i < ARGCOUNT; ++i)
1509 	if (ARGLIST[i].ae_fnum == fnum)
1510 	    break;
1511     if (i == ARGCOUNT)
1512     {
1513 	/* Can't find it, add it to the argument list. */
1514 	s = vim_strsave(eap->arg);
1515 	if (s == NULL)
1516 	    return;
1517 	i = alist_add_list(1, &s,
1518 	       eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1519 	if (i < 0)
1520 	    return;
1521 	curwin->w_arg_idx = i;
1522     }
1523 
1524     alist_check_arg_idx();
1525 
1526     /* Edit the argument. */
1527     do_argfile(eap, i);
1528 }
1529 
1530 /*
1531  * ":argadd"
1532  */
1533     void
1534 ex_argadd(eap)
1535     exarg_T	*eap;
1536 {
1537     do_arglist(eap->arg, AL_ADD,
1538 	       eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1539 #ifdef FEAT_TITLE
1540     maketitle();
1541 #endif
1542 }
1543 
1544 /*
1545  * ":argdelete"
1546  */
1547     void
1548 ex_argdelete(eap)
1549     exarg_T	*eap;
1550 {
1551     int		i;
1552     int		n;
1553 
1554     if (eap->addr_count > 0)
1555     {
1556 	/* ":1,4argdel": Delete all arguments in the range. */
1557 	if (eap->line2 > ARGCOUNT)
1558 	    eap->line2 = ARGCOUNT;
1559 	n = eap->line2 - eap->line1 + 1;
1560 	if (*eap->arg != NUL || n <= 0)
1561 	    EMSG(_(e_invarg));
1562 	else
1563 	{
1564 	    for (i = eap->line1; i <= eap->line2; ++i)
1565 		vim_free(ARGLIST[i - 1].ae_fname);
1566 	    mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
1567 			(size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
1568 	    ALIST(curwin)->al_ga.ga_len -= n;
1569 	    ALIST(curwin)->al_ga.ga_room += n;
1570 	    if (curwin->w_arg_idx >= eap->line2)
1571 		curwin->w_arg_idx -= n;
1572 	    else if (curwin->w_arg_idx > eap->line1)
1573 		curwin->w_arg_idx = eap->line1;
1574 	}
1575     }
1576     else if (*eap->arg == NUL)
1577 	EMSG(_(e_argreq));
1578     else
1579 	do_arglist(eap->arg, AL_DEL, 0);
1580 #ifdef FEAT_TITLE
1581     maketitle();
1582 #endif
1583 }
1584 
1585 /*
1586  * ":argdo", ":windo", ":bufdo"
1587  */
1588     void
1589 ex_listdo(eap)
1590     exarg_T	*eap;
1591 {
1592     int		i;
1593 #ifdef FEAT_WINDOWS
1594     win_T	*win;
1595 #endif
1596     buf_T	*buf;
1597     int		next_fnum = 0;
1598 #if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
1599     char_u	*save_ei = NULL;
1600     char_u	*new_ei;
1601 #endif
1602 
1603 #ifndef FEAT_WINDOWS
1604     if (eap->cmdidx == CMD_windo)
1605     {
1606 	ex_ni(eap);
1607 	return;
1608     }
1609 #endif
1610 
1611 #if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
1612     if (eap->cmdidx != CMD_windo)
1613     {
1614 	/* Add "Syntax" to 'eventignore' to skip loading syntax highlighting
1615 	 * for every buffer loaded into the window.  A considerable speed
1616 	 * improvement. */
1617 	save_ei = vim_strsave(p_ei);
1618 	if (save_ei != NULL)
1619 	{
1620 	    new_ei = vim_strnsave(p_ei, (int)STRLEN(p_ei) + 8);
1621 	    if (new_ei != NULL)
1622 	    {
1623 		STRCAT(new_ei, ",Syntax");
1624 		set_string_option_direct((char_u *)"ei", -1, new_ei, OPT_FREE);
1625 		vim_free(new_ei);
1626 	    }
1627 	}
1628     }
1629 #endif
1630 
1631     if (eap->cmdidx == CMD_windo
1632 	    || P_HID(curbuf)
1633 	    || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
1634     {
1635 	/* start at the first argument/window/buffer */
1636 	i = 0;
1637 #ifdef FEAT_WINDOWS
1638 	win = firstwin;
1639 #endif
1640 	/* set pcmark now */
1641 	if (eap->cmdidx == CMD_bufdo)
1642 	    goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
1643 	else
1644 	    setpcmark();
1645 	listcmd_busy = TRUE;	    /* avoids setting pcmark below */
1646 
1647 	while (!got_int)
1648 	{
1649 	    if (eap->cmdidx == CMD_argdo)
1650 	    {
1651 		/* go to argument "i" */
1652 		if (i == ARGCOUNT)
1653 		    break;
1654 		/* Don't call do_argfile() when already there, it will try
1655 		 * reloading the file. */
1656 		if (curwin->w_arg_idx != i)
1657 		    do_argfile(eap, i);
1658 		if (curwin->w_arg_idx != i)
1659 		    break;
1660 		++i;
1661 	    }
1662 #ifdef FEAT_WINDOWS
1663 	    else if (eap->cmdidx == CMD_windo)
1664 	    {
1665 		/* go to window "win" */
1666 		if (!win_valid(win))
1667 		    break;
1668 		win_goto(win);
1669 		win = win->w_next;
1670 	    }
1671 #endif
1672 	    else if (eap->cmdidx == CMD_bufdo)
1673 	    {
1674 		/* Remember the number of the next listed buffer, in case
1675 		 * ":bwipe" is used or autocommands do something strange. */
1676 		next_fnum = -1;
1677 		for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
1678 		    if (buf->b_p_bl)
1679 		    {
1680 			next_fnum = buf->b_fnum;
1681 			break;
1682 		    }
1683 	    }
1684 
1685 	    /* execute the command */
1686 	    do_cmdline(eap->arg, eap->getline, eap->cookie,
1687 						DOCMD_VERBOSE + DOCMD_NOWAIT);
1688 
1689 	    if (eap->cmdidx == CMD_bufdo)
1690 	    {
1691 		/* Done? */
1692 		if (next_fnum < 0)
1693 		    break;
1694 		/* Check if the buffer still exists. */
1695 		for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1696 		    if (buf->b_fnum == next_fnum)
1697 			break;
1698 		if (buf == NULL)
1699 		    break;
1700 		goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
1701 		/* If autocommands took us elsewhere, quit here */
1702 		if (curbuf->b_fnum != next_fnum)
1703 		    break;
1704 	    }
1705 
1706 	    if (eap->cmdidx == CMD_windo)
1707 	    {
1708 		validate_cursor();	/* cursor may have moved */
1709 #ifdef FEAT_SCROLLBIND
1710 		/* required when 'scrollbind' has been set */
1711 		if (curwin->w_p_scb)
1712 		    do_check_scrollbind(TRUE);
1713 #endif
1714 	    }
1715 	}
1716 	listcmd_busy = FALSE;
1717     }
1718 
1719 #if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
1720     if (save_ei != NULL)
1721     {
1722 	set_string_option_direct((char_u *)"ei", -1, save_ei, OPT_FREE);
1723 	apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
1724 					       curbuf->b_fname, TRUE, curbuf);
1725 	vim_free(save_ei);
1726     }
1727 #endif
1728 }
1729 
1730 /*
1731  * Add files[count] to the arglist of the current window after arg "after".
1732  * The file names in files[count] must have been allocated and are taken over.
1733  * Files[] itself is not taken over.
1734  * Returns index of first added argument.  Returns -1 when failed (out of mem).
1735  */
1736     static int
1737 alist_add_list(count, files, after)
1738     int		count;
1739     char_u	**files;
1740     int		after;	    /* where to add: 0 = before first one */
1741 {
1742     int		i;
1743 
1744     if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
1745     {
1746 	if (after < 0)
1747 	    after = 0;
1748 	if (after > ARGCOUNT)
1749 	    after = ARGCOUNT;
1750 	if (after < ARGCOUNT)
1751 	    mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
1752 				       (ARGCOUNT - after) * sizeof(aentry_T));
1753 	for (i = 0; i < count; ++i)
1754 	{
1755 	    ARGLIST[after + i].ae_fname = files[i];
1756 	    ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
1757 	}
1758 	ALIST(curwin)->al_ga.ga_len += count;
1759 	ALIST(curwin)->al_ga.ga_room -= count;
1760 	if (curwin->w_arg_idx >= after)
1761 	    ++curwin->w_arg_idx;
1762 	return after;
1763     }
1764 
1765     for (i = 0; i < count; ++i)
1766 	vim_free(files[i]);
1767     return -1;
1768 }
1769 
1770 #endif /* FEAT_LISTCMDS */
1771 
1772 #ifdef FEAT_EVAL
1773 /*
1774  * ":compiler[!] {name}"
1775  */
1776     void
1777 ex_compiler(eap)
1778     exarg_T	*eap;
1779 {
1780     char_u	*buf;
1781     char_u	*old_cur_comp = NULL;
1782     char_u	*p;
1783 
1784     if (*eap->arg == NUL)
1785     {
1786 	/* List all compiler scripts. */
1787 	do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
1788 					/* ) keep the indenter happy... */
1789     }
1790     else
1791     {
1792 	buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
1793 	if (buf != NULL)
1794 	{
1795 	    if (eap->forceit)
1796 	    {
1797 		/* ":compiler! {name}" sets global options */
1798 		do_cmdline_cmd((char_u *)
1799 				   "command -nargs=* CompilerSet set <args>");
1800 	    }
1801 	    else
1802 	    {
1803 		/* ":compiler! {name}" sets local options.
1804 		 * To remain backwards compatible "current_compiler" is always
1805 		 * used.  A user's compiler plugin may set it, the distributed
1806 		 * plugin will then skip the settings.  Afterwards set
1807 		 * "b:current_compiler" and restore "current_compiler". */
1808 		old_cur_comp = get_var_value((char_u *)"current_compiler");
1809 		if (old_cur_comp != NULL)
1810 		    old_cur_comp = vim_strsave(old_cur_comp);
1811 		do_cmdline_cmd((char_u *)
1812 			      "command -nargs=* CompilerSet setlocal <args>");
1813 	    }
1814 	    do_unlet((char_u *)"current_compiler");
1815 	    do_unlet((char_u *)"b:current_compiler");
1816 
1817 	    sprintf((char *)buf, "compiler/%s.vim", eap->arg);
1818 	    if (cmd_runtime(buf, TRUE) == FAIL)
1819 		EMSG2(_("E666: compiler not supported: %s"), eap->arg);
1820 	    vim_free(buf);
1821 
1822 	    do_cmdline_cmd((char_u *)":delcommand CompilerSet");
1823 
1824 	    /* Set "b:current_compiler" from "current_compiler". */
1825 	    p = get_var_value((char_u *)"current_compiler");
1826 	    if (p != NULL)
1827 		set_internal_string_var((char_u *)"b:current_compiler", p);
1828 
1829 	    /* Restore "current_compiler" for ":compiler {name}". */
1830 	    if (!eap->forceit)
1831 	    {
1832 		if (old_cur_comp != NULL)
1833 		{
1834 		    set_internal_string_var((char_u *)"current_compiler",
1835 								old_cur_comp);
1836 		    vim_free(old_cur_comp);
1837 		}
1838 		else
1839 		    do_unlet((char_u *)"current_compiler");
1840 	    }
1841 	}
1842     }
1843 }
1844 #endif
1845 
1846 /*
1847  * ":runtime {name}"
1848  */
1849     void
1850 ex_runtime(eap)
1851     exarg_T	*eap;
1852 {
1853     cmd_runtime(eap->arg, eap->forceit);
1854 }
1855 
1856 static void source_callback __ARGS((char_u *fname));
1857 
1858     static void
1859 source_callback(fname)
1860     char_u	*fname;
1861 {
1862     (void)do_source(fname, FALSE, FALSE);
1863 }
1864 
1865 /*
1866  * Source the file "name" from all directories in 'runtimepath'.
1867  * "name" can contain wildcards.
1868  * When "all" is TRUE, source all files, otherwise only the first one.
1869  * return FAIL when no file could be sourced, OK otherwise.
1870  */
1871     int
1872 cmd_runtime(name, all)
1873     char_u	*name;
1874     int		all;
1875 {
1876     return do_in_runtimepath(name, all, source_callback);
1877 }
1878 
1879 /*
1880  * Find "name" in 'runtimepath'.  When found, call the "callback" function for
1881  * it.
1882  * When "all" is TRUE repeat for all matches, otherwise only the first one is
1883  * used.
1884  * Returns OK when at least one match found, FAIL otherwise.
1885  */
1886     int
1887 do_in_runtimepath(name, all, callback)
1888     char_u	*name;
1889     int		all;
1890     void	(*callback)__ARGS((char_u *fname));
1891 {
1892     char_u	*rtp;
1893     char_u	*np;
1894     char_u	*buf;
1895     char_u	*rtp_copy;
1896     char_u	*tail;
1897     int		num_files;
1898     char_u	**files;
1899     int		i;
1900     int		did_one = FALSE;
1901 #ifdef AMIGA
1902     struct Process	*proc = (struct Process *)FindTask(0L);
1903     APTR		save_winptr = proc->pr_WindowPtr;
1904 
1905     /* Avoid a requester here for a volume that doesn't exist. */
1906     proc->pr_WindowPtr = (APTR)-1L;
1907 #endif
1908 
1909     /* Make a copy of 'runtimepath'.  Invoking the callback may change the
1910      * value. */
1911     rtp_copy = vim_strsave(p_rtp);
1912     buf = alloc(MAXPATHL);
1913     if (buf != NULL && rtp_copy != NULL)
1914     {
1915 	if (p_verbose > 1)
1916 	    smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
1917 						 (char *)name, (char *)p_rtp);
1918 	/* Loop over all entries in 'runtimepath'. */
1919 	rtp = rtp_copy;
1920 	while (*rtp != NUL && (all || !did_one))
1921 	{
1922 	    /* Copy the path from 'runtimepath' to buf[]. */
1923 	    copy_option_part(&rtp, buf, MAXPATHL, ",");
1924 	    if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL)
1925 	    {
1926 		add_pathsep(buf);
1927 		tail = buf + STRLEN(buf);
1928 
1929 		/* Loop over all patterns in "name" */
1930 		np = name;
1931 		while (*np != NUL && (all || !did_one))
1932 		{
1933 		    /* Append the pattern from "name" to buf[]. */
1934 		    copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
1935 								       "\t ");
1936 
1937 		    if (p_verbose > 2)
1938 			msg_str((char_u *)_("Searching for \"%s\""), buf);
1939 
1940 		    /* Expand wildcards, invoke the callback for each match. */
1941 		    if (gen_expand_wildcards(1, &buf, &num_files, &files,
1942 							       EW_FILE) == OK)
1943 		    {
1944 			for (i = 0; i < num_files; ++i)
1945 			{
1946 			    (*callback)(files[i]);
1947 			    did_one = TRUE;
1948 			    if (!all)
1949 				break;
1950 			}
1951 			FreeWild(num_files, files);
1952 		    }
1953 		}
1954 	    }
1955 	}
1956     }
1957     vim_free(buf);
1958     vim_free(rtp_copy);
1959     if (p_verbose > 0 && !did_one)
1960 	msg_str((char_u *)_("not found in 'runtimepath': \"%s\""), name);
1961 
1962 #ifdef AMIGA
1963     proc->pr_WindowPtr = save_winptr;
1964 #endif
1965 
1966     return did_one ? OK : FAIL;
1967 }
1968 
1969 #if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
1970 /*
1971  * ":options"
1972  */
1973 /*ARGSUSED*/
1974     void
1975 ex_options(eap)
1976     exarg_T	*eap;
1977 {
1978     cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
1979 }
1980 #endif
1981 
1982 /*
1983  * ":source {fname}"
1984  */
1985     void
1986 ex_source(eap)
1987     exarg_T	*eap;
1988 {
1989 #ifdef FEAT_BROWSE
1990     if (cmdmod.browse)
1991     {
1992 	char_u *fname = NULL;
1993 
1994 	fname = do_browse(FALSE, (char_u *)_("Source Vim script"), eap->arg,
1995 				      NULL, NULL, BROWSE_FILTER_MACROS, NULL);
1996 	if (fname != NULL)
1997 	{
1998 	    cmd_source(fname, eap);
1999 	    vim_free(fname);
2000 	}
2001     }
2002     else
2003 #endif
2004 	cmd_source(eap->arg, eap);
2005 }
2006 
2007     static void
2008 cmd_source(fname, eap)
2009     char_u	*fname;
2010     exarg_T	*eap;
2011 {
2012     if (*fname == NUL)
2013 	EMSG(_(e_argreq));
2014 
2015     /* ":source!" read vi commands */
2016     else if (eap != NULL && eap->forceit)
2017 	/* Need to execute the commands directly when:
2018 	 * - ":g" command busy
2019 	 * - after ":argdo", ":windo" or ":bufdo"
2020 	 * - another command follows
2021 	 * - inside a loop
2022 	 */
2023 	openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2024 #ifdef FEAT_EVAL
2025 						 || eap->cstack->cs_idx >= 0
2026 #endif
2027 						 );
2028 
2029     /* ":source" read ex commands */
2030     else if (do_source(fname, FALSE, FALSE) == FAIL)
2031 	EMSG2(_(e_notopen), fname);
2032 }
2033 
2034 /*
2035  * ":source" and associated commands.
2036  */
2037 /*
2038  * Structure used to store info for each sourced file.
2039  * It is shared between do_source() and getsourceline().
2040  * This is required, because it needs to be handed to do_cmdline() and
2041  * sourcing can be done recursively.
2042  */
2043 struct source_cookie
2044 {
2045     FILE	*fp;		/* opened file for sourcing */
2046     char_u      *nextline;      /* if not NULL: line that was read ahead */
2047     int		finished;	/* ":finish" used */
2048 #if defined (USE_CRNL) || defined (USE_CR)
2049     int		fileformat;	/* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
2050     int		error;		/* TRUE if LF found after CR-LF */
2051 #endif
2052 #ifdef FEAT_EVAL
2053     linenr_T	breakpoint;	/* next line with breakpoint or zero */
2054     char_u	*fname;		/* name of sourced file */
2055     int		dbg_tick;	/* debug_tick when breakpoint was set */
2056     int		level;		/* top nesting level of sourced file */
2057 #endif
2058 #ifdef FEAT_MBYTE
2059     vimconv_T	conv;		/* type of conversion */
2060 #endif
2061 };
2062 
2063 #ifdef FEAT_EVAL
2064 /*
2065  * Return the address holding the next breakpoint line for a source cookie.
2066  */
2067     linenr_T *
2068 source_breakpoint(cookie)
2069     void *cookie;
2070 {
2071     return &((struct source_cookie *)cookie)->breakpoint;
2072 }
2073 
2074 /*
2075  * Return the address holding the debug tick for a source cookie.
2076  */
2077     int *
2078 source_dbg_tick(cookie)
2079     void *cookie;
2080 {
2081     return &((struct source_cookie *)cookie)->dbg_tick;
2082 }
2083 
2084 /*
2085  * Return the nesting level for a source cookie.
2086  */
2087     int
2088 source_level(cookie)
2089     void *cookie;
2090 {
2091     return ((struct source_cookie *)cookie)->level;
2092 }
2093 #endif
2094 
2095 static char_u *get_one_sourceline __ARGS((struct source_cookie *sp));
2096 
2097 #ifdef FEAT_EVAL
2098 /* Growarray to store the names of sourced scripts.
2099  * For Unix also store the dev/ino, so that we don't have to stat() each
2100  * script when going through the list. */
2101 struct scriptstuff
2102 {
2103     char_u	*name;
2104 # ifdef UNIX
2105     int		dev;
2106     ino_t	ino;
2107 # endif
2108 };
2109 static garray_T script_names = {0, 0, sizeof(struct scriptstuff), 4, NULL};
2110 #define SCRIPT_NAME(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].name)
2111 #define SCRIPT_DEV(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].dev)
2112 #define SCRIPT_INO(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].ino)
2113 #endif
2114 
2115 #if defined(WIN32) && defined(FEAT_CSCOPE)
2116 static FILE *fopen_noinh_readbin __ARGS((char *filename));
2117 
2118 /*
2119  * Special function to open a file without handle inheritance.
2120  */
2121     static FILE *
2122 fopen_noinh_readbin(filename)
2123     char    *filename;
2124 {
2125     int	fd_tmp = open(filename, O_RDONLY | O_BINARY | O_NOINHERIT);
2126 
2127     if (fd_tmp == -1)
2128 	return NULL;
2129     return fdopen(fd_tmp, READBIN);
2130 }
2131 #endif
2132 
2133 
2134 /*
2135  * do_source: Read the file "fname" and execute its lines as EX commands.
2136  *
2137  * This function may be called recursively!
2138  *
2139  * return FAIL if file could not be opened, OK otherwise
2140  */
2141     int
2142 do_source(fname, check_other, is_vimrc)
2143     char_u	*fname;
2144     int		check_other;	    /* check for .vimrc and _vimrc */
2145     int		is_vimrc;	    /* call vimrc_found() when file exists */
2146 {
2147     struct source_cookie    cookie;
2148     char_u		    *save_sourcing_name;
2149     linenr_T		    save_sourcing_lnum;
2150     char_u		    *p;
2151     char_u		    *fname_exp;
2152     int			    retval = FAIL;
2153 #ifdef FEAT_EVAL
2154     scid_T		    save_current_SID;
2155     static scid_T	    last_current_SID = 0;
2156     void		    *save_funccalp;
2157     int			    save_debug_break_level = debug_break_level;
2158 # ifdef UNIX
2159     struct stat		    st;
2160     int			    stat_ok;
2161 # endif
2162 #endif
2163 #ifdef STARTUPTIME
2164     struct timeval	    tv_rel;
2165     struct timeval	    tv_start;
2166 #endif
2167 
2168 #ifdef RISCOS
2169     p = mch_munge_fname(fname);
2170 #else
2171     p = expand_env_save(fname);
2172 #endif
2173     if (p == NULL)
2174 	return retval;
2175     fname_exp = fix_fname(p);
2176     vim_free(p);
2177     if (fname_exp == NULL)
2178 	return retval;
2179 #ifdef MACOS_CLASSIC
2180     slash_n_colon_adjust(fname_exp);
2181 #endif
2182     if (mch_isdir(fname_exp))
2183     {
2184 	msg_str((char_u *)_("Cannot source a directory: \"%s\""), fname);
2185 	goto theend;
2186     }
2187 
2188 #if defined(WIN32) && defined(FEAT_CSCOPE)
2189     cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2190 #else
2191     cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2192 #endif
2193     if (cookie.fp == NULL && check_other)
2194     {
2195 	/*
2196 	 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
2197 	 * and ".exrc" by "_exrc" or vice versa.
2198 	 */
2199 	p = gettail(fname_exp);
2200 	if ((*p == '.' || *p == '_')
2201 		&& (STRICMP(p + 1, "vimrc") == 0
2202 		    || STRICMP(p + 1, "gvimrc") == 0
2203 		    || STRICMP(p + 1, "exrc") == 0))
2204 	{
2205 	    if (*p == '_')
2206 		*p = '.';
2207 	    else
2208 		*p = '_';
2209 #if defined(WIN32) && defined(FEAT_CSCOPE)
2210 	    cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2211 #else
2212 	    cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2213 #endif
2214 	}
2215     }
2216 
2217     if (cookie.fp == NULL)
2218     {
2219 	if (p_verbose > 0)
2220 	{
2221 	    if (sourcing_name == NULL)
2222 		msg_str((char_u *)_("could not source \"%s\""), fname);
2223 	    else
2224 		smsg((char_u *)_("line %ld: could not source \"%s\""),
2225 			sourcing_lnum, fname);
2226 	}
2227 	goto theend;
2228     }
2229 
2230     /*
2231      * The file exists.
2232      * - In verbose mode, give a message.
2233      * - For a vimrc file, may want to set 'compatible', call vimrc_found().
2234      */
2235     if (p_verbose > 1)
2236     {
2237 	if (sourcing_name == NULL)
2238 	    msg_str((char_u *)_("sourcing \"%s\""), fname);
2239 	else
2240 	    smsg((char_u *)_("line %ld: sourcing \"%s\""),
2241 		    sourcing_lnum, fname);
2242     }
2243     if (is_vimrc)
2244 	vimrc_found();
2245 
2246 #ifdef USE_CRNL
2247     /* If no automatic file format: Set default to CR-NL. */
2248     if (*p_ffs == NUL)
2249 	cookie.fileformat = EOL_DOS;
2250     else
2251 	cookie.fileformat = EOL_UNKNOWN;
2252     cookie.error = FALSE;
2253 #endif
2254 
2255 #ifdef USE_CR
2256     /* If no automatic file format: Set default to CR. */
2257     if (*p_ffs == NUL)
2258 	cookie.fileformat = EOL_MAC;
2259     else
2260 	cookie.fileformat = EOL_UNKNOWN;
2261     cookie.error = FALSE;
2262 #endif
2263 
2264     cookie.nextline = NULL;
2265     cookie.finished = FALSE;
2266 
2267 #ifdef FEAT_EVAL
2268     /*
2269      * Check if this script has a breakpoint.
2270      */
2271     cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
2272     cookie.fname = fname_exp;
2273     cookie.dbg_tick = debug_tick;
2274 
2275     cookie.level = ex_nesting_level;
2276 #endif
2277 #ifdef FEAT_MBYTE
2278     cookie.conv.vc_type = CONV_NONE;		/* no conversion */
2279 
2280     /* Try reading the first few bytes to check for a UTF-8 BOM. */
2281     {
2282 	char_u	    buf[3];
2283 
2284 	if (fread((char *)buf, sizeof(char_u), (size_t)3, cookie.fp)
2285 								  == (size_t)3
2286 		&& buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
2287 	    /* Found BOM, setup conversion and skip over it. */
2288 	    convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
2289 	else
2290 	    /* No BOM found, rewind. */
2291 	    fseek(cookie.fp, 0L, SEEK_SET);
2292     }
2293 #endif
2294 
2295     /*
2296      * Keep the sourcing name/lnum, for recursive calls.
2297      */
2298     save_sourcing_name = sourcing_name;
2299     sourcing_name = fname_exp;
2300     save_sourcing_lnum = sourcing_lnum;
2301     sourcing_lnum = 0;
2302 
2303 #ifdef STARTUPTIME
2304     time_push(&tv_rel, &tv_start);
2305 #endif
2306 
2307 #ifdef FEAT_EVAL
2308     /*
2309      * Check if this script was sourced before to finds its SID.
2310      * If it's new, generate a new SID.
2311      */
2312     save_current_SID = current_SID;
2313 # ifdef UNIX
2314     stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
2315 # endif
2316     for (current_SID = script_names.ga_len; current_SID > 0; --current_SID)
2317 	if (SCRIPT_NAME(current_SID) != NULL
2318 		&& (
2319 # ifdef UNIX
2320 		    /* compare dev/ino when possible, it catches symbolic
2321 		     * links */
2322 		    (stat_ok && SCRIPT_DEV(current_SID) != -1)
2323 			? (SCRIPT_DEV(current_SID) == st.st_dev
2324 			    && SCRIPT_INO(current_SID) == st.st_ino) :
2325 # endif
2326 		fnamecmp(SCRIPT_NAME(current_SID), fname_exp) == 0))
2327 	    break;
2328     if (current_SID == 0)
2329     {
2330 	current_SID = ++last_current_SID;
2331 	if (ga_grow(&script_names, (int)(current_SID - script_names.ga_len))
2332 									== OK)
2333 	{
2334 	    while (script_names.ga_len < current_SID)
2335 	    {
2336 		SCRIPT_NAME(script_names.ga_len + 1) = NULL;
2337 		++script_names.ga_len;
2338 		--script_names.ga_room;
2339 	    }
2340 	    SCRIPT_NAME(current_SID) = fname_exp;
2341 # ifdef UNIX
2342 	    if (stat_ok)
2343 	    {
2344 		SCRIPT_DEV(current_SID) = st.st_dev;
2345 		SCRIPT_INO(current_SID) = st.st_ino;
2346 	    }
2347 	    else
2348 		SCRIPT_DEV(current_SID) = -1;
2349 # endif
2350 	    fname_exp = NULL;
2351 	}
2352 	/* Allocate the local script variables to use for this script. */
2353 	new_script_vars(current_SID);
2354     }
2355 
2356     /* Don't use local function variables, if called from a function */
2357     save_funccalp = save_funccal();
2358 #endif
2359 
2360     /*
2361      * Call do_cmdline, which will call getsourceline() to get the lines.
2362      */
2363     do_cmdline(NULL, getsourceline, (void *)&cookie,
2364 				     DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2365 
2366     retval = OK;
2367     fclose(cookie.fp);
2368     vim_free(cookie.nextline);
2369 #ifdef FEAT_MBYTE
2370     convert_setup(&cookie.conv, NULL, NULL);
2371 #endif
2372 
2373     if (got_int)
2374 	EMSG(_(e_interr));
2375     sourcing_name = save_sourcing_name;
2376     sourcing_lnum = save_sourcing_lnum;
2377 #ifdef FEAT_EVAL
2378     current_SID = save_current_SID;
2379     restore_funccal(save_funccalp);
2380 #endif
2381     if (p_verbose > 1)
2382     {
2383 	msg_str((char_u *)_("finished sourcing %s"), fname);
2384 	if (sourcing_name != NULL)
2385 	    msg_str((char_u *)_("continuing in %s"), sourcing_name);
2386     }
2387 #ifdef STARTUPTIME
2388 # ifdef HAVE_SNPRINTF
2389     snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2390 # else
2391     sprintf(IObuff, "sourcing %s", fname);
2392 # endif
2393     time_msg(IObuff, &tv_start);
2394     time_pop(&tv_rel);
2395 #endif
2396 
2397 #ifdef FEAT_EVAL
2398     /*
2399      * After a "finish" in debug mode, need to break at first command of next
2400      * sourced file.
2401      */
2402     if (save_debug_break_level > ex_nesting_level
2403 	    && debug_break_level == ex_nesting_level)
2404 	++debug_break_level;
2405 #endif
2406 
2407 theend:
2408     vim_free(fname_exp);
2409     return retval;
2410 }
2411 
2412 #if defined(FEAT_EVAL) || defined(PROTO)
2413 /*
2414  * ":scriptnames"
2415  */
2416 /*ARGSUSED*/
2417     void
2418 ex_scriptnames(eap)
2419     exarg_T	*eap;
2420 {
2421     int i;
2422 
2423     for (i = 1; i <= script_names.ga_len && !got_int; ++i)
2424 	if (SCRIPT_NAME(i) != NULL)
2425 	    smsg((char_u *)"%3d: %s", i, SCRIPT_NAME(i));
2426 }
2427 
2428 # if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2429 /*
2430  * Fix slashes in the list of script names for 'shellslash'.
2431  */
2432     void
2433 scriptnames_slash_adjust()
2434 {
2435     int i;
2436 
2437     for (i = 1; i <= script_names.ga_len; ++i)
2438 	if (SCRIPT_NAME(i) != NULL)
2439 	    slash_adjust(SCRIPT_NAME(i));
2440 }
2441 # endif
2442 
2443 /*
2444  * Get a pointer to a script name.  Used for ":verbose set".
2445  */
2446     char_u *
2447 get_scriptname(id)
2448     scid_T	id;
2449 {
2450     if (id == SID_MODELINE)
2451 	return (char_u *)"modeline";
2452     if (id == SID_CMDARG)
2453 	return (char_u *)"--cmd argument";
2454     if (id == SID_CARG)
2455 	return (char_u *)"-c argument";
2456     if (id == SID_ENV)
2457 	return (char_u *)"environment variable";
2458     return SCRIPT_NAME(id);
2459 }
2460 #endif
2461 
2462 #if defined(USE_CR) || defined(PROTO)
2463 
2464 # if defined(__MSL__) && (__MSL__ >= 22)
2465 /*
2466  * Newer version of the Metrowerks library handle DOS and UNIX files
2467  * without help.
2468  * Test with earlier versions, MSL 2.2 is the library supplied with
2469  * Codewarrior Pro 2.
2470  */
2471     char *
2472 fgets_cr(s, n, stream)
2473     char	*s;
2474     int		n;
2475     FILE	*stream;
2476 {
2477     return fgets(s, n, stream);
2478 }
2479 # else
2480 /*
2481  * Version of fgets() which also works for lines ending in a <CR> only
2482  * (Macintosh format).
2483  * For older versions of the Metrowerks library.
2484  * At least CodeWarrior 9 needed this code.
2485  */
2486     char *
2487 fgets_cr(s, n, stream)
2488     char	*s;
2489     int		n;
2490     FILE	*stream;
2491 {
2492     int	c = 0;
2493     int char_read = 0;
2494 
2495     while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
2496     {
2497 	c = fgetc(stream);
2498 	s[char_read++] = c;
2499 	/* If the file is in DOS format, we need to skip a NL after a CR.  I
2500 	 * thought it was the other way around, but this appears to work... */
2501 	if (c == '\n')
2502 	{
2503 	    c = fgetc(stream);
2504 	    if (c != '\r')
2505 		ungetc(c, stream);
2506 	}
2507     }
2508 
2509     s[char_read] = 0;
2510     if (char_read == 0)
2511 	return NULL;
2512 
2513     if (feof(stream) && char_read == 1)
2514 	return NULL;
2515 
2516     return s;
2517 }
2518 # endif
2519 #endif
2520 
2521 /*
2522  * Get one full line from a sourced file.
2523  * Called by do_cmdline() when it's called from do_source().
2524  *
2525  * Return a pointer to the line in allocated memory.
2526  * Return NULL for end-of-file or some error.
2527  */
2528 /* ARGSUSED */
2529     char_u *
2530 getsourceline(c, cookie, indent)
2531     int		c;		/* not used */
2532     void	*cookie;
2533     int		indent;		/* not used */
2534 {
2535     struct source_cookie *sp = (struct source_cookie *)cookie;
2536     char_u		*line;
2537     char_u		*p, *s;
2538 
2539 #ifdef FEAT_EVAL
2540     /* If breakpoints have been added/deleted need to check for it. */
2541     if (sp->dbg_tick < debug_tick)
2542     {
2543 	sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2544 	sp->dbg_tick = debug_tick;
2545     }
2546 #endif
2547     /*
2548      * Get current line.  If there is a read-ahead line, use it, otherwise get
2549      * one now.
2550      */
2551     if (sp->finished)
2552 	line = NULL;
2553     else if (sp->nextline == NULL)
2554 	line = get_one_sourceline(sp);
2555     else
2556     {
2557 	line = sp->nextline;
2558 	sp->nextline = NULL;
2559 	++sourcing_lnum;
2560     }
2561 
2562     /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
2563      * contain the 'C' flag. */
2564     if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
2565     {
2566 	/* compensate for the one line read-ahead */
2567 	--sourcing_lnum;
2568 	for (;;)
2569 	{
2570 	    sp->nextline = get_one_sourceline(sp);
2571 	    if (sp->nextline == NULL)
2572 		break;
2573 	    p = skipwhite(sp->nextline);
2574 	    if (*p != '\\')
2575 		break;
2576 	    s = alloc((int)(STRLEN(line) + STRLEN(p)));
2577 	    if (s == NULL)	/* out of memory */
2578 		break;
2579 	    STRCPY(s, line);
2580 	    STRCAT(s, p + 1);
2581 	    vim_free(line);
2582 	    line = s;
2583 	    vim_free(sp->nextline);
2584 	}
2585     }
2586 
2587 #ifdef FEAT_MBYTE
2588     if (line != NULL && sp->conv.vc_type != CONV_NONE)
2589     {
2590 	/* Convert the encoding of the script line. */
2591 	s = string_convert(&sp->conv, line, NULL);
2592 	if (s != NULL)
2593 	{
2594 	    vim_free(line);
2595 	    line = s;
2596 	}
2597     }
2598 #endif
2599 
2600 #ifdef FEAT_EVAL
2601     /* Did we encounter a breakpoint? */
2602     if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
2603     {
2604 	dbg_breakpoint(sp->fname, sourcing_lnum);
2605 	/* Find next breakpoint. */
2606 	sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2607 	sp->dbg_tick = debug_tick;
2608     }
2609 #endif
2610 
2611     return line;
2612 }
2613 
2614     static char_u *
2615 get_one_sourceline(sp)
2616     struct source_cookie    *sp;
2617 {
2618     garray_T		ga;
2619     int			len;
2620     int			c;
2621     char_u		*buf;
2622 #ifdef USE_CRNL
2623     int			has_cr;		/* CR-LF found */
2624 #endif
2625 #ifdef USE_CR
2626     char_u		*scan;
2627 #endif
2628     int			have_read = FALSE;
2629 
2630     /* use a growarray to store the sourced line */
2631     ga_init2(&ga, 1, 200);
2632 
2633     /*
2634      * Loop until there is a finished line (or end-of-file).
2635      */
2636     sourcing_lnum++;
2637     for (;;)
2638     {
2639 	/* make room to read at least 80 (more) characters */
2640 	if (ga_grow(&ga, 80) == FAIL)
2641 	    break;
2642 	buf = (char_u *)ga.ga_data;
2643 
2644 #ifdef USE_CR
2645 	if (sp->fileformat == EOL_MAC)
2646 	{
2647 	    if (fgets_cr((char *)buf + ga.ga_len, ga.ga_room, sp->fp) == NULL)
2648 		break;
2649 	}
2650 	else
2651 #endif
2652 	    if (fgets((char *)buf + ga.ga_len, ga.ga_room, sp->fp) == NULL)
2653 		break;
2654 	len = (int)STRLEN(buf);
2655 #ifdef USE_CRNL
2656 	/* Ignore a trailing CTRL-Z, when in Dos mode.	Only recognize the
2657 	 * CTRL-Z by its own, or after a NL. */
2658 	if (	   (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
2659 		&& sp->fileformat == EOL_DOS
2660 		&& buf[len - 1] == Ctrl_Z)
2661 	{
2662 	    buf[len - 1] = NUL;
2663 	    break;
2664 	}
2665 #endif
2666 
2667 #ifdef USE_CR
2668 	/* If the read doesn't stop on a new line, and there's
2669 	 * some CR then we assume a Mac format */
2670 	if (sp->fileformat == EOL_UNKNOWN)
2671 	{
2672 	    if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
2673 		sp->fileformat = EOL_MAC;
2674 	    else
2675 		sp->fileformat = EOL_UNIX;
2676 	}
2677 
2678 	if (sp->fileformat == EOL_MAC)
2679 	{
2680 	    scan = vim_strchr(buf, '\r');
2681 
2682 	    if (scan != NULL)
2683 	    {
2684 		*scan = '\n';
2685 		if (*(scan + 1) != 0)
2686 		{
2687 		    *(scan + 1) = 0;
2688 		    fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
2689 		}
2690 	    }
2691 	    len = STRLEN(buf);
2692 	}
2693 #endif
2694 
2695 	have_read = TRUE;
2696 	ga.ga_room -= len - ga.ga_len;
2697 	ga.ga_len = len;
2698 
2699 	/* If the line was longer than the buffer, read more. */
2700 	if (ga.ga_room == 1 && buf[len - 1] != '\n')
2701 	    continue;
2702 
2703 	if (len >= 1 && buf[len - 1] == '\n')	/* remove trailing NL */
2704 	{
2705 #ifdef USE_CRNL
2706 	    has_cr = (len >= 2 && buf[len - 2] == '\r');
2707 	    if (sp->fileformat == EOL_UNKNOWN)
2708 	    {
2709 		if (has_cr)
2710 		    sp->fileformat = EOL_DOS;
2711 		else
2712 		    sp->fileformat = EOL_UNIX;
2713 	    }
2714 
2715 	    if (sp->fileformat == EOL_DOS)
2716 	    {
2717 		if (has_cr)	    /* replace trailing CR */
2718 		{
2719 		    buf[len - 2] = '\n';
2720 		    --len;
2721 		    --ga.ga_len;
2722 		    ++ga.ga_room;
2723 		}
2724 		else	    /* lines like ":map xx yy^M" will have failed */
2725 		{
2726 		    if (!sp->error)
2727 			EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
2728 		    sp->error = TRUE;
2729 		    sp->fileformat = EOL_UNIX;
2730 		}
2731 	    }
2732 #endif
2733 	    /* The '\n' is escaped if there is an odd number of ^V's just
2734 	     * before it, first set "c" just before the 'V's and then check
2735 	     * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
2736 	    for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
2737 		;
2738 	    if ((len & 1) != (c & 1))	/* escaped NL, read more */
2739 	    {
2740 		sourcing_lnum++;
2741 		continue;
2742 	    }
2743 
2744 	    buf[len - 1] = NUL;		/* remove the NL */
2745 	}
2746 
2747 	/*
2748 	 * Check for ^C here now and then, so recursive :so can be broken.
2749 	 */
2750 	line_breakcheck();
2751 	break;
2752     }
2753 
2754     if (have_read)
2755 	return (char_u *)ga.ga_data;
2756 
2757     vim_free(ga.ga_data);
2758     return NULL;
2759 }
2760 
2761 /*
2762  * ":scriptencoding": Set encoding conversion for a sourced script.
2763  * Without the multi-byte feature it's simply ignored.
2764  */
2765 /*ARGSUSED*/
2766     void
2767 ex_scriptencoding(eap)
2768     exarg_T	*eap;
2769 {
2770 #ifdef FEAT_MBYTE
2771     struct source_cookie	*sp;
2772     char_u			*name;
2773 
2774     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
2775     {
2776 	EMSG(_("E167: :scriptencoding used outside of a sourced file"));
2777 	return;
2778     }
2779 
2780     if (*eap->arg != NUL)
2781     {
2782 	name = enc_canonize(eap->arg);
2783 	if (name == NULL)	/* out of memory */
2784 	    return;
2785     }
2786     else
2787 	name = eap->arg;
2788 
2789     /* Setup for conversion from the specified encoding to 'encoding'. */
2790     sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
2791     convert_setup(&sp->conv, name, p_enc);
2792 
2793     if (name != eap->arg)
2794 	vim_free(name);
2795 #endif
2796 }
2797 
2798 #if defined(FEAT_EVAL) || defined(PROTO)
2799 /*
2800  * ":finish": Mark a sourced file as finished.
2801  */
2802     void
2803 ex_finish(eap)
2804     exarg_T	*eap;
2805 {
2806     if (getline_equal(eap->getline, eap->cookie, getsourceline))
2807 	do_finish(eap, FALSE);
2808     else
2809 	EMSG(_("E168: :finish used outside of a sourced file"));
2810 }
2811 
2812 /*
2813  * Mark a sourced file as finished.  Possibly makes the ":finish" pending.
2814  * Also called for a pending finish at the ":endtry" or after returning from
2815  * an extra do_cmdline().  "reanimate" is used in the latter case.
2816  */
2817     void
2818 do_finish(eap, reanimate)
2819     exarg_T	*eap;
2820     int		reanimate;
2821 {
2822     int		idx;
2823 
2824     if (reanimate)
2825 	((struct source_cookie *)getline_cookie(eap->getline,
2826 					      eap->cookie))->finished = FALSE;
2827 
2828     /*
2829      * Cleanup (and inactivate) conditionals, but stop when a try conditional
2830      * not in its finally clause (which then is to be executed next) is found.
2831      * In this case, make the ":finish" pending for execution at the ":endtry".
2832      * Otherwise, finish normally.
2833      */
2834     idx = cleanup_conditionals(eap->cstack, 0, TRUE);
2835     if (idx >= 0)
2836     {
2837 	eap->cstack->cs_pending[idx] = CSTP_FINISH;
2838 	report_make_pending(CSTP_FINISH, NULL);
2839     }
2840     else
2841 	((struct source_cookie *)getline_cookie(eap->getline,
2842 					       eap->cookie))->finished = TRUE;
2843 }
2844 
2845 
2846 /*
2847  * Return TRUE when a sourced file had the ":finish" command: Don't give error
2848  * message for missing ":endif".
2849  * Return FALSE when not sourcing a file.
2850  */
2851     int
2852 source_finished(getline, cookie)
2853     char_u	*(*getline) __ARGS((int, void *, int));
2854     void	*cookie;
2855 {
2856     return (getline_equal(getline, cookie, getsourceline)
2857 	    && ((struct source_cookie *)getline_cookie(
2858 						 getline, cookie))->finished);
2859 }
2860 #endif
2861 
2862 #if defined(FEAT_LISTCMDS) || defined(PROTO)
2863 /*
2864  * ":checktime [buffer]"
2865  */
2866     void
2867 ex_checktime(eap)
2868     exarg_T	*eap;
2869 {
2870     buf_T	*buf;
2871     int		save_no_check_timestamps = no_check_timestamps;
2872 
2873     no_check_timestamps = 0;
2874     if (eap->addr_count == 0)	/* default is all buffers */
2875 	check_timestamps(FALSE);
2876     else
2877     {
2878 	buf = buflist_findnr((int)eap->line2);
2879 	if (buf != NULL)	/* cannot happen? */
2880 	    (void)buf_check_timestamp(buf, FALSE);
2881     }
2882     no_check_timestamps = save_no_check_timestamps;
2883 }
2884 #endif
2885 
2886 #if defined(FEAT_PRINTER) || defined(PROTO)
2887 /*
2888  * Printing code (Machine-independent.)
2889  * To implement printing on a platform, the following functions must be
2890  * defined:
2891  *
2892  * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
2893  * Called once.  Code should display printer dialogue (if appropriate) and
2894  * determine printer font and margin settings.  Reset has_color if the printer
2895  * doesn't support colors at all.
2896  * Returns FAIL to abort.
2897  *
2898  * int mch_print_begin(prt_settings_T *settings)
2899  * Called to start the print job.
2900  * Return FALSE to abort.
2901  *
2902  * int mch_print_begin_page(char_u *msg)
2903  * Called at the start of each page.
2904  * "msg" indicates the progress of the print job, can be NULL.
2905  * Return FALSE to abort.
2906  *
2907  * int mch_print_end_page()
2908  * Called at the end of each page.
2909  * Return FALSE to abort.
2910  *
2911  * int mch_print_blank_page()
2912  * Called to generate a blank page for collated, duplex, multiple copy
2913  * document.  Return FALSE to abort.
2914  *
2915  * void mch_print_end(prt_settings_T *psettings)
2916  * Called at normal end of print job.
2917  *
2918  * void mch_print_cleanup()
2919  * Called if print job ends normally or is abandoned. Free any memory, close
2920  * devices and handles.  Also called when mch_print_begin() fails, but not
2921  * when mch_print_init() fails.
2922  *
2923  * void mch_print_set_font(int Bold, int Italic, int Underline);
2924  * Called whenever the font style changes.
2925  *
2926  * void mch_print_set_bg(long bgcol);
2927  * Called to set the background color for the following text. Parameter is an
2928  * RGB value.
2929  *
2930  * void mch_print_set_fg(long fgcol);
2931  * Called to set the foreground color for the following text. Parameter is an
2932  * RGB value.
2933  *
2934  * mch_print_start_line(int margin, int page_line)
2935  * Sets the current position at the start of line "page_line".
2936  * If margin is TRUE start in the left margin (for header and line number).
2937  *
2938  * int mch_print_text_out(char_u *p, int len);
2939  * Output one character of text p[len] at the current position.
2940  * Return TRUE if there is no room for another character in the same line.
2941  *
2942  * Note that the generic code has no idea of margins. The machine code should
2943  * simply make the page look smaller!  The header and the line numbers are
2944  * printed in the margin.
2945  */
2946 
2947 #ifdef FEAT_SYN_HL
2948 static const long_u  cterm_color_8[8] =
2949 {
2950     (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
2951     (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
2952 };
2953 
2954 static const long_u  cterm_color_16[16] =
2955 {
2956     (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
2957     (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
2958     (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
2959     (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
2960 };
2961 
2962 static int		current_syn_id;
2963 #endif
2964 
2965 #define PRCOLOR_BLACK	(long_u)0
2966 #define PRCOLOR_WHITE	(long_u)0xFFFFFFL
2967 
2968 static int	curr_italic;
2969 static int	curr_bold;
2970 static int	curr_underline;
2971 static long_u	curr_bg;
2972 static long_u	curr_fg;
2973 static int	page_count;
2974 
2975 /*
2976  * These values determine the print position on a page.
2977  */
2978 typedef struct
2979 {
2980     int		lead_spaces;	    /* remaining spaces for a TAB */
2981     int		print_pos;	    /* virtual column for computing TABs */
2982     colnr_T	column;		    /* byte column */
2983     linenr_T	file_line;	    /* line nr in the buffer */
2984     long_u	bytes_printed;	    /* bytes printed so far */
2985     int		ff;		    /* seen form feed character */
2986 } prt_pos_T;
2987 
2988 #ifdef FEAT_SYN_HL
2989 static long_u darken_rgb __ARGS((long_u rgb));
2990 static long_u prt_get_term_color __ARGS((int colorindex));
2991 #endif
2992 static void prt_set_fg __ARGS((long_u fg));
2993 static void prt_set_bg __ARGS((long_u bg));
2994 static void prt_set_font __ARGS((int bold, int italic, int underline));
2995 static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
2996 static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
2997 static void prt_message __ARGS((char_u *s));
2998 static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
2999 static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3000 
3001 #ifdef FEAT_SYN_HL
3002 /*
3003  * If using a dark background, the colors will probably be too bright to show
3004  * up well on white paper, so reduce their brightness.
3005  */
3006     static long_u
3007 darken_rgb(rgb)
3008     long_u	rgb;
3009 {
3010     return	((rgb >> 17) << 16)
3011 	    +	(((rgb & 0xff00) >> 9) << 8)
3012 	    +	((rgb & 0xff) >> 1);
3013 }
3014 
3015     static long_u
3016 prt_get_term_color(colorindex)
3017     int	    colorindex;
3018 {
3019     /* TODO: Should check for xterm with 88 or 256 colors. */
3020     if (t_colors > 8)
3021 	return cterm_color_16[colorindex % 16];
3022     return cterm_color_8[colorindex % 8];
3023 }
3024 
3025     static void
3026 prt_get_attr(hl_id, pattr, modec)
3027     int			hl_id;
3028     prt_text_attr_T*	pattr;
3029     int			modec;
3030 {
3031     int     colorindex;
3032     long_u  fg_color;
3033     long_u  bg_color;
3034     char    *color;
3035 
3036     pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3037     pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3038     pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
3039 
3040 # ifdef FEAT_GUI
3041     if (gui.in_use)
3042     {
3043 	bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3044 	if (bg_color == PRCOLOR_BLACK)
3045 	    bg_color = PRCOLOR_WHITE;
3046 
3047 	fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3048     }
3049     else
3050 # endif
3051     {
3052 	bg_color = PRCOLOR_WHITE;
3053 
3054 	color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3055 	if (color == NULL)
3056 	    colorindex = 0;
3057 	else
3058 	    colorindex = atoi(color);
3059 
3060 	if (colorindex >= 0 && colorindex < t_colors)
3061 	    fg_color = prt_get_term_color(colorindex);
3062 	else
3063 	    fg_color = PRCOLOR_BLACK;
3064     }
3065 
3066     if (fg_color == PRCOLOR_WHITE)
3067 	fg_color = PRCOLOR_BLACK;
3068     else if (*p_bg == 'd')
3069 	fg_color = darken_rgb(fg_color);
3070 
3071     pattr->fg_color = fg_color;
3072     pattr->bg_color = bg_color;
3073 }
3074 #endif /* FEAT_SYN_HL */
3075 
3076     static void
3077 prt_set_fg(fg)
3078     long_u fg;
3079 {
3080     if (fg != curr_fg)
3081     {
3082 	curr_fg = fg;
3083 	mch_print_set_fg(fg);
3084     }
3085 }
3086 
3087     static void
3088 prt_set_bg(bg)
3089     long_u bg;
3090 {
3091     if (bg != curr_bg)
3092     {
3093 	curr_bg = bg;
3094 	mch_print_set_bg(bg);
3095     }
3096 }
3097 
3098     static void
3099 prt_set_font(bold, italic, underline)
3100     int		bold;
3101     int		italic;
3102     int		underline;
3103 {
3104     if (curr_bold != bold
3105 	    || curr_italic != italic
3106 	    || curr_underline != underline)
3107     {
3108 	curr_underline = underline;
3109 	curr_italic = italic;
3110 	curr_bold = bold;
3111 	mch_print_set_font(bold, italic, underline);
3112     }
3113 }
3114 
3115 /*
3116  * Print the line number in the left margin.
3117  */
3118     static void
3119 prt_line_number(psettings, page_line, lnum)
3120     prt_settings_T *psettings;
3121     int		page_line;
3122     linenr_T	lnum;
3123 {
3124     int		i;
3125     char_u	tbuf[20];
3126 
3127     prt_set_fg(psettings->number.fg_color);
3128     prt_set_bg(psettings->number.bg_color);
3129     prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3130     mch_print_start_line(TRUE, page_line);
3131 
3132     /* Leave two spaces between the number and the text; depends on
3133      * PRINT_NUMBER_WIDTH. */
3134     sprintf((char *)tbuf, "%6ld", (long)lnum);
3135     for (i = 0; i < 6; i++)
3136 	(void)mch_print_text_out(&tbuf[i], 1);
3137 
3138 #ifdef FEAT_SYN_HL
3139     if (psettings->do_syntax)
3140 	/* Set colors for next character. */
3141 	current_syn_id = -1;
3142     else
3143 #endif
3144     {
3145 	/* Set colors and font back to normal. */
3146 	prt_set_fg(PRCOLOR_BLACK);
3147 	prt_set_bg(PRCOLOR_WHITE);
3148 	prt_set_font(FALSE, FALSE, FALSE);
3149     }
3150 }
3151 
3152 static linenr_T printer_page_num;
3153 
3154     int
3155 get_printer_page_num()
3156 {
3157     return printer_page_num;
3158 }
3159 
3160 /*
3161  * Get the currently effective header height.
3162  */
3163     int
3164 prt_header_height()
3165 {
3166     if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3167 	return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3168     return 2;
3169 }
3170 
3171 /*
3172  * Return TRUE if using a line number for printing.
3173  */
3174     int
3175 prt_use_number()
3176 {
3177     return (printer_opts[OPT_PRINT_NUMBER].present
3178 	    && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3179 }
3180 
3181 /*
3182  * Return the unit used in a margin item in 'printoptions'.
3183  * Returns PRT_UNIT_NONE if not recognized.
3184  */
3185     int
3186 prt_get_unit(idx)
3187     int		idx;
3188 {
3189     int		u = PRT_UNIT_NONE;
3190     int		i;
3191     static char *(units[4]) = PRT_UNIT_NAMES;
3192 
3193     if (printer_opts[idx].present)
3194 	for (i = 0; i < 4; ++i)
3195 	    if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3196 	    {
3197 		u = i;
3198 		break;
3199 	    }
3200     return u;
3201 }
3202 
3203 /*
3204  * Print the page header.
3205  */
3206 /*ARGSUSED*/
3207     static void
3208 prt_header(psettings, pagenum, lnum)
3209     prt_settings_T  *psettings;
3210     int		pagenum;
3211     linenr_T	lnum;
3212 {
3213     int		width = psettings->chars_per_line;
3214     int		page_line;
3215     char_u	*tbuf;
3216     char_u	*p;
3217 #ifdef FEAT_MBYTE
3218     int		l;
3219 #endif
3220 
3221     /* Also use the space for the line number. */
3222     if (prt_use_number())
3223 	width += PRINT_NUMBER_WIDTH;
3224 
3225     tbuf = alloc(width + IOSIZE);
3226     if (tbuf == NULL)
3227 	return;
3228 
3229 #ifdef FEAT_STL_OPT
3230     if (*p_header != NUL)
3231     {
3232 	linenr_T	tmp_lnum, tmp_topline, tmp_botline;
3233 
3234 	/*
3235 	 * Need to (temporarily) set current line number and first/last line
3236 	 * number on the 'window'.  Since we don't know how long the page is,
3237 	 * set the first and current line number to the top line, and guess
3238 	 * that the page length is 64.
3239 	 */
3240 	tmp_lnum = curwin->w_cursor.lnum;
3241 	tmp_topline = curwin->w_topline;
3242 	tmp_botline = curwin->w_botline;
3243 	curwin->w_cursor.lnum = lnum;
3244 	curwin->w_topline = lnum;
3245 	curwin->w_botline = lnum + 63;
3246 	printer_page_num = pagenum;
3247 
3248 	build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3249 						  p_header, ' ', width, NULL);
3250 
3251 	/* Reset line numbers */
3252 	curwin->w_cursor.lnum = tmp_lnum;
3253 	curwin->w_topline = tmp_topline;
3254 	curwin->w_botline = tmp_botline;
3255     }
3256     else
3257 #endif
3258 	sprintf((char *)tbuf, _("Page %d"), pagenum);
3259 
3260     prt_set_fg(PRCOLOR_BLACK);
3261     prt_set_bg(PRCOLOR_WHITE);
3262     prt_set_font(TRUE, FALSE, FALSE);
3263 
3264     /* Use a negative line number to indicate printing in the top margin. */
3265     page_line = 0 - prt_header_height();
3266     mch_print_start_line(TRUE, page_line);
3267     for (p = tbuf; *p != NUL; )
3268     {
3269 	if (mch_print_text_out(p,
3270 #ifdef FEAT_MBYTE
3271 		(l = (*mb_ptr2len_check)(p))
3272 #else
3273 		1
3274 #endif
3275 		    ))
3276 	{
3277 	    ++page_line;
3278 	    if (page_line >= 0) /* out of room in header */
3279 		break;
3280 	    mch_print_start_line(TRUE, page_line);
3281 	}
3282 #ifdef FEAT_MBYTE
3283 	p += l;
3284 #else
3285 	p++;
3286 #endif
3287     }
3288 
3289     vim_free(tbuf);
3290 
3291 #ifdef FEAT_SYN_HL
3292     if (psettings->do_syntax)
3293 	/* Set colors for next character. */
3294 	current_syn_id = -1;
3295     else
3296 #endif
3297     {
3298 	/* Set colors and font back to normal. */
3299 	prt_set_fg(PRCOLOR_BLACK);
3300 	prt_set_bg(PRCOLOR_WHITE);
3301 	prt_set_font(FALSE, FALSE, FALSE);
3302     }
3303 }
3304 
3305 /*
3306  * Display a print status message.
3307  */
3308     static void
3309 prt_message(s)
3310     char_u	*s;
3311 {
3312     screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3313     screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3314     out_flush();
3315 }
3316 
3317     void
3318 ex_hardcopy(eap)
3319     exarg_T	*eap;
3320 {
3321     linenr_T		lnum;
3322     int			collated_copies, uncollated_copies;
3323     prt_settings_T	settings;
3324     long_u		bytes_to_print = 0;
3325     int			page_line;
3326     int			jobsplit;
3327     int			id;
3328 
3329     memset(&settings, 0, sizeof(prt_settings_T));
3330     settings.has_color = TRUE;
3331 
3332 # ifdef FEAT_POSTSCRIPT
3333     if (*eap->arg == '>')
3334     {
3335 	char_u	*errormsg = NULL;
3336 
3337 	/* Expand things like "%.ps". */
3338 	if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3339 	{
3340 	    if (errormsg != NULL)
3341 		EMSG(errormsg);
3342 	    return;
3343 	}
3344 	settings.outfile = skipwhite(eap->arg + 1);
3345     }
3346     else if (*eap->arg != NUL)
3347 	settings.arguments = eap->arg;
3348 # endif
3349 
3350     /*
3351      * Initialise for printing.  Ask the user for settings, unless forceit is
3352      * set.
3353      * The mch_print_init() code should set up margins if applicable. (It may
3354      * not be a real printer - for example the engine might generate HTML or
3355      * PS.)
3356      */
3357     if (mch_print_init(&settings,
3358 			curbuf->b_fname == NULL
3359 			    ? (char_u *)buf_spname(curbuf)
3360 			    : curbuf->b_sfname == NULL
3361 				? curbuf->b_fname
3362 				: curbuf->b_sfname,
3363 			eap->forceit) == FAIL)
3364 	return;
3365 
3366 #ifdef FEAT_SYN_HL
3367 # ifdef  FEAT_GUI
3368     if (gui.in_use)
3369 	settings.modec = 'g';
3370     else
3371 # endif
3372 	if (t_colors > 1)
3373 	    settings.modec = 'c';
3374 	else
3375 	    settings.modec = 't';
3376 
3377     if (!syntax_present(curbuf))
3378 	settings.do_syntax = FALSE;
3379     else if (printer_opts[OPT_PRINT_SYNTAX].present
3380 	    && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
3381 	settings.do_syntax =
3382 	       (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
3383     else
3384 	settings.do_syntax = settings.has_color;
3385 #endif
3386 
3387     /* Set up printing attributes for line numbers */
3388     settings.number.fg_color = PRCOLOR_BLACK;
3389     settings.number.bg_color = PRCOLOR_WHITE;
3390     settings.number.bold = FALSE;
3391     settings.number.italic = TRUE;
3392     settings.number.underline = FALSE;
3393 #ifdef FEAT_SYN_HL
3394     /*
3395      * Syntax highlighting of line numbers.
3396      */
3397     if (prt_use_number() && settings.do_syntax)
3398     {
3399 	id = syn_name2id((char_u *)"LineNr");
3400 	if (id > 0)
3401 	    id = syn_get_final_id(id);
3402 
3403 	prt_get_attr(id, &settings.number, settings.modec);
3404     }
3405 #endif /* FEAT_SYN_HL */
3406 
3407     /*
3408      * Estimate the total lines to be printed
3409      */
3410     for (lnum = eap->line1; lnum <= eap->line2; lnum++)
3411 	bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
3412     if (bytes_to_print == 0)
3413     {
3414 	MSG(_("No text to be printed"));
3415 	goto print_fail_no_begin;
3416     }
3417 
3418     /* Set colors and font to normal. */
3419     curr_bg = (long_u)0xffffffffL;
3420     curr_fg = (long_u)0xffffffffL;
3421     curr_italic = MAYBE;
3422     curr_bold = MAYBE;
3423     curr_underline = MAYBE;
3424 
3425     prt_set_fg(PRCOLOR_BLACK);
3426     prt_set_bg(PRCOLOR_WHITE);
3427     prt_set_font(FALSE, FALSE, FALSE);
3428 #ifdef FEAT_SYN_HL
3429     current_syn_id = -1;
3430 #endif
3431 
3432     jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
3433 	   && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
3434 
3435     if (!mch_print_begin(&settings))
3436 	goto print_fail_no_begin;
3437 
3438     /*
3439      * Loop over collated copies: 1 2 3, 1 2 3, ...
3440      */
3441     page_count = 0;
3442     for (collated_copies = 0;
3443 	    collated_copies < settings.n_collated_copies;
3444 	    collated_copies++)
3445     {
3446 	prt_pos_T	prtpos;		/* current print position */
3447 	prt_pos_T	page_prtpos;	/* print position at page start */
3448 	int		side;
3449 
3450 	memset(&page_prtpos, 0, sizeof(prt_pos_T));
3451 	page_prtpos.file_line = eap->line1;
3452 	prtpos = page_prtpos;
3453 
3454 	if (jobsplit && collated_copies > 0)
3455 	{
3456 	    /* Splitting jobs: Stop a previous job and start a new one. */
3457 	    mch_print_end(&settings);
3458 	    if (!mch_print_begin(&settings))
3459 		goto print_fail_no_begin;
3460 	}
3461 
3462 	/*
3463 	 * Loop over all pages in the print job: 1 2 3 ...
3464 	 */
3465 	for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
3466 	{
3467 	    /*
3468 	     * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
3469 	     * For duplex: 12 12 12 34 34 34, ...
3470 	     */
3471 	    for (uncollated_copies = 0;
3472 		    uncollated_copies < settings.n_uncollated_copies;
3473 		    uncollated_copies++)
3474 	    {
3475 		/* Set the print position to the start of this page. */
3476 		prtpos = page_prtpos;
3477 
3478 		/*
3479 		 * Do front and rear side of a page.
3480 		 */
3481 		for (side = 0; side <= settings.duplex; ++side)
3482 		{
3483 		    /*
3484 		     * Print one page.
3485 		     */
3486 
3487 		    /* Check for interrupt character every page. */
3488 		    ui_breakcheck();
3489 		    if (got_int || settings.user_abort)
3490 			goto print_fail;
3491 
3492 		    sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
3493 			    page_count + 1 + side,
3494 			    prtpos.bytes_printed > 1000000
3495 				? (int)(prtpos.bytes_printed /
3496 						       (bytes_to_print / 100))
3497 				: (int)((prtpos.bytes_printed * 100)
3498 							   / bytes_to_print));
3499 		    if (!mch_print_begin_page(IObuff))
3500 			goto print_fail;
3501 
3502 		    if (settings.n_collated_copies > 1)
3503 			sprintf((char *)IObuff + STRLEN(IObuff),
3504 				_(" Copy %d of %d"),
3505 				collated_copies + 1,
3506 				settings.n_collated_copies);
3507 		    prt_message(IObuff);
3508 
3509 		    /*
3510 		     * Output header if required
3511 		     */
3512 		    if (prt_header_height() > 0)
3513 			prt_header(&settings, page_count + 1 + side,
3514 							prtpos.file_line);
3515 
3516 		    for (page_line = 0; page_line < settings.lines_per_page;
3517 								  ++page_line)
3518 		    {
3519 			prtpos.column = hardcopy_line(&settings,
3520 							   page_line, &prtpos);
3521 			if (prtpos.column == 0)
3522 			{
3523 			    /* finished a file line */
3524 			    prtpos.bytes_printed +=
3525 				  STRLEN(skipwhite(ml_get(prtpos.file_line)));
3526 			    if (++prtpos.file_line > eap->line2)
3527 				break; /* reached the end */
3528 			}
3529 			else if (prtpos.ff)
3530 			{
3531 			    /* Line had a formfeed in it - start new page but
3532 			     * stay on the current line */
3533 			    break;
3534 			}
3535 		    }
3536 
3537 		    if (!mch_print_end_page())
3538 			goto print_fail;
3539 		    if (prtpos.file_line > eap->line2)
3540 			break; /* reached the end */
3541 		}
3542 
3543 		/*
3544 		 * Extra blank page for duplexing with odd number of pages and
3545 		 * more copies to come.
3546 		 */
3547 		if (prtpos.file_line > eap->line2 && settings.duplex
3548 								 && side == 0
3549 		    && uncollated_copies + 1 < settings.n_uncollated_copies)
3550 		{
3551 		    if (!mch_print_blank_page())
3552 			goto print_fail;
3553 		}
3554 	    }
3555 	    if (settings.duplex && prtpos.file_line <= eap->line2)
3556 		++page_count;
3557 
3558 	    /* Remember the position where the next page starts. */
3559 	    page_prtpos = prtpos;
3560 	}
3561 
3562 	sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
3563 	prt_message(IObuff);
3564     }
3565 
3566 print_fail:
3567     if (got_int || settings.user_abort)
3568     {
3569 	sprintf((char *)IObuff, _("Printing aborted"));
3570 	prt_message(IObuff);
3571     }
3572     mch_print_end(&settings);
3573 
3574 print_fail_no_begin:
3575     mch_print_cleanup();
3576 }
3577 
3578 /*
3579  * Print one page line.
3580  * Return the next column to print, or zero if the line is finished.
3581  */
3582     static colnr_T
3583 hardcopy_line(psettings, page_line, ppos)
3584     prt_settings_T	*psettings;
3585     int			page_line;
3586     prt_pos_T		*ppos;
3587 {
3588     colnr_T	col;
3589     char_u	*line;
3590     int		need_break = FALSE;
3591     int		outputlen;
3592     int		tab_spaces;
3593     long_u	print_pos;
3594 #ifdef FEAT_SYN_HL
3595     prt_text_attr_T attr;
3596     int		id;
3597 #endif
3598 
3599     if (ppos->column == 0 || ppos->ff)
3600     {
3601 	print_pos = 0;
3602 	tab_spaces = 0;
3603 	if (!ppos->ff && prt_use_number())
3604 	    prt_line_number(psettings, page_line, ppos->file_line);
3605 	ppos->ff = FALSE;
3606     }
3607     else
3608     {
3609 	/* left over from wrap halfway a tab */
3610 	print_pos = ppos->print_pos;
3611 	tab_spaces = ppos->lead_spaces;
3612     }
3613 
3614     mch_print_start_line(0, page_line);
3615     line = ml_get(ppos->file_line);
3616 
3617     /*
3618      * Loop over the columns until the end of the file line or right margin.
3619      */
3620     for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
3621     {
3622 	outputlen = 1;
3623 #ifdef FEAT_MBYTE
3624 	if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
3625 	    outputlen = 1;
3626 #endif
3627 #ifdef FEAT_SYN_HL
3628 	/*
3629 	 * syntax highlighting stuff.
3630 	 */
3631 	if (psettings->do_syntax)
3632 	{
3633 	    id = syn_get_id(ppos->file_line, (long)col, 1);
3634 	    if (id > 0)
3635 		id = syn_get_final_id(id);
3636 	    else
3637 		id = 0;
3638 	    /* Get the line again, a multi-line regexp may invalidate it. */
3639 	    line = ml_get(ppos->file_line);
3640 
3641 	    if (id != current_syn_id)
3642 	    {
3643 		current_syn_id = id;
3644 		prt_get_attr(id, &attr, psettings->modec);
3645 		prt_set_font(attr.bold, attr.italic, attr.underline);
3646 		prt_set_fg(attr.fg_color);
3647 		prt_set_bg(attr.bg_color);
3648 	    }
3649 	}
3650 #endif /* FEAT_SYN_HL */
3651 
3652 	/*
3653 	 * Appropriately expand any tabs to spaces.
3654 	 */
3655 	if (line[col] == TAB || tab_spaces != 0)
3656 	{
3657 	    if (tab_spaces == 0)
3658 		tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
3659 
3660 	    while (tab_spaces > 0)
3661 	    {
3662 		need_break = mch_print_text_out((char_u *)" ", 1);
3663 		print_pos++;
3664 		tab_spaces--;
3665 		if (need_break)
3666 		    break;
3667 	    }
3668 	    /* Keep the TAB if we didn't finish it. */
3669 	    if (need_break && tab_spaces > 0)
3670 		break;
3671 	}
3672 	else if (line[col] == FF
3673 		&& printer_opts[OPT_PRINT_FORMFEED].present
3674 		&& TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
3675 								       == 'y')
3676 	{
3677 	    ppos->ff = TRUE;
3678 	    need_break = 1;
3679 	}
3680 	else
3681 	{
3682 	    need_break = mch_print_text_out(line + col, outputlen);
3683 #ifdef FEAT_MBYTE
3684 	    if (has_mbyte)
3685 		print_pos += (*mb_ptr2cells)(line + col);
3686 	    else
3687 #endif
3688 		print_pos++;
3689 	}
3690     }
3691 
3692     ppos->lead_spaces = tab_spaces;
3693     ppos->print_pos = print_pos;
3694 
3695     /*
3696      * Start next line of file if we clip lines, or have reached end of the
3697      * line, unless we are doing a formfeed.
3698      */
3699     if (!ppos->ff
3700 	    && (line[col] == NUL
3701 		|| (printer_opts[OPT_PRINT_WRAP].present
3702 		    && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
3703 								     == 'n')))
3704 	return 0;
3705     return col;
3706 }
3707 
3708 # if defined(FEAT_POSTSCRIPT) || defined(PROTO)
3709 
3710 /*
3711  * PS printer stuff.
3712  *
3713  * Sources of information to help maintain the PS printing code:
3714  *
3715  * 1. PostScript Language Reference, 3rd Edition,
3716  *      Addison-Wesley, 1999, ISBN 0-201-37922-8
3717  * 2. PostScript Language Program Design,
3718  *      Addison-Wesley, 1988, ISBN 0-201-14396-8
3719  * 3. PostScript Tutorial and Cookbook,
3720  *      Addison Wesley, 1985, ISBN 0-201-10179-3
3721  * 4. PostScript Language Document Structuring Conventions Specification,
3722  *    version 3.0,
3723  *      Adobe Technote 5001, 25th September 1992
3724  * 5. PostScript Printer Description File Format Specification, Version 4.3,
3725  *      Adobe technote 5003, 9th February 1996
3726  * 6. Adobe Font Metrics File Format Specification, Version 4.1,
3727  *      Adobe Technote 5007, 7th October 1998
3728  *
3729  * Some of these documents can be found in PDF form on Adobe's web site -
3730  * http://www.adobe.com
3731  */
3732 
3733 #define PRT_PS_DEFAULT_DPI	    (72)    /* Default user space resolution */
3734 #define PRT_PS_DEFAULT_FONTSIZE     (10)
3735 #define PRT_PS_DEFAULT_BUFFER_SIZE  (80)
3736 
3737 struct prt_mediasize_S
3738 {
3739     char	*name;
3740     float	width;		/* width and height in points for portrait */
3741     float	height;
3742 };
3743 
3744 #define PRT_MEDIASIZE_LEN  (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
3745 
3746 static struct prt_mediasize_S prt_mediasize[] =
3747 {
3748     {"A4",		595.0,  842.0},
3749     {"letter",		612.0,  792.0},
3750     {"10x14",		720.0, 1008.0},
3751     {"A3",		842.0, 1191.0},
3752     {"A5",		420.0,  595.0},
3753     {"B4",		729.0, 1032.0},
3754     {"B5",		516.0,  729.0},
3755     {"executive",	522.0,  756.0},
3756     {"folio",		595.0,  935.0},
3757     {"ledger",	       1224.0,  792.0},   /* Yes, it is wider than taller! */
3758     {"legal",		612.0, 1008.0},
3759     {"quarto",		610.0,  780.0},
3760     {"statement",	396.0,  612.0},
3761     {"tabloid",		792.0, 1224.0}
3762 };
3763 
3764 /* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
3765 struct prt_ps_font_S
3766 {
3767     int		wx;
3768     int		uline_offset;
3769     int		uline_width;
3770     int		bbox_min_y;
3771     int		bbox_max_y;
3772     char	*(ps_fontname[4]);
3773 };
3774 
3775 #define PRT_PS_FONT_ROMAN	(0)
3776 #define PRT_PS_FONT_BOLD	(1)
3777 #define PRT_PS_FONT_OBLIQUE	(2)
3778 #define PRT_PS_FONT_BOLDOBLIQUE (3)
3779 
3780 static struct prt_ps_font_S prt_ps_font =
3781 {
3782     600,
3783     -100, 50,
3784     -250, 805,
3785     {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
3786 };
3787 
3788 struct prt_ps_resource_S
3789 {
3790     char_u  name[64];
3791     char_u  filename[MAXPATHL + 1];
3792     int     type;
3793     char_u  title[256];
3794     char_u  version[256];
3795 };
3796 
3797 /* Types of PS resource file currently used */
3798 #define PRT_RESOURCE_TYPE_PROCSET   (0)
3799 #define PRT_RESOURCE_TYPE_ENCODING  (1)
3800 
3801 /* The PS prolog file version number has to match - if the prolog file is
3802  * updated, increment the number in the file and here.  Version checking was
3803  * added as of VIM 6.2.
3804  * Table of VIM and prolog versions:
3805  *
3806  * VIM      Prolog
3807  * 6.2      1.3
3808  */
3809 #define PRT_PROLOG_VERSION  ((char_u *)"1.3")
3810 
3811 /* String versions of PS resource types - indexed by constants above so don't
3812  * re-order!
3813  */
3814 static char *resource_types[] =
3815 {
3816     "procset",
3817     "encoding"
3818 };
3819 
3820 /* Strings to look for in a PS resource file */
3821 #define PRT_RESOURCE_HEADER	    "%!PS-Adobe-"
3822 #define PRT_RESOURCE_RESOURCE	    "Resource-"
3823 #define PRT_RESOURCE_PROCSET	    "ProcSet"
3824 #define PRT_RESOURCE_ENCODING	    "Encoding"
3825 #define PRT_RESOURCE_TITLE	    "%%Title:"
3826 #define PRT_RESOURCE_VERSION	    "%%Version:"
3827 
3828 static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
3829 static void prt_write_file __ARGS((char_u *buffer));
3830 static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
3831 static void prt_write_string __ARGS((char *s));
3832 static void prt_write_int __ARGS((int i));
3833 static void prt_write_boolean __ARGS((int b));
3834 static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
3835 static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
3836 static void prt_write_real __ARGS((double val, int prec));
3837 static void prt_def_var __ARGS((char *name, double value, int prec));
3838 static void prt_flush_buffer __ARGS((void));
3839 static void prt_resource_name __ARGS((char_u *filename));
3840 static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
3841 static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
3842 static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
3843 static void prt_dsc_start __ARGS((void));
3844 static void prt_dsc_noarg __ARGS((char *comment));
3845 static void prt_dsc_textline __ARGS((char *comment, char *text));
3846 static void prt_dsc_text __ARGS((char *comment, char *text));
3847 static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
3848 static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
3849 static void prt_dsc_docmedia __ARGS((char *paper_name, double width, double height, double weight, char *colour, char *type));
3850 static void prt_dsc_resources __ARGS((char *comment, char *type, int count, char **strings));
3851 static float to_device_units __ARGS((int idx, double physsize, int def_number));
3852 static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
3853 static void prt_font_metrics __ARGS((int font_scale));
3854 static int prt_get_cpl __ARGS((void));
3855 static int prt_get_lpp __ARGS((void));
3856 static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
3857 
3858 /*
3859  * Variables for the output PostScript file.
3860  */
3861 static FILE *prt_ps_fd;
3862 static int prt_file_error;
3863 static char_u *prt_ps_file_name = NULL;
3864 
3865 /*
3866  * Various offsets and dimensions in default PostScript user space (points).
3867  * Used for text positioning calculations
3868  */
3869 static float prt_page_width;
3870 static float prt_page_height;
3871 static float prt_left_margin;
3872 static float prt_right_margin;
3873 static float prt_top_margin;
3874 static float prt_bottom_margin;
3875 static float prt_line_height;
3876 static float prt_first_line_height;
3877 static float prt_char_width;
3878 static float prt_number_width;
3879 static float prt_bgcol_offset;
3880 static float prt_pos_x_moveto = 0.0;
3881 static float prt_pos_y_moveto = 0.0;
3882 
3883 /*
3884  * Various control variables used to decide when and how to change the
3885  * PostScript graphics state.
3886  */
3887 static int prt_need_moveto;
3888 static int prt_do_moveto;
3889 static int prt_need_font;
3890 static int prt_font;
3891 static int prt_need_underline;
3892 static int prt_underline;
3893 static int prt_do_underline;
3894 static int prt_need_fgcol;
3895 static int prt_fgcol;
3896 static int prt_need_bgcol;
3897 static int prt_do_bgcol;
3898 static int prt_bgcol;
3899 static int prt_new_bgcol;
3900 static int prt_attribute_change;
3901 static int prt_text_count;
3902 static int prt_page_num;
3903 
3904 /*
3905  * Variables controlling physical printing.
3906  */
3907 static int prt_media;
3908 static int prt_portrait;
3909 static int prt_num_copies;
3910 static int prt_duplex;
3911 static int prt_tumble;
3912 static int prt_collate;
3913 
3914 /*
3915  * Buffers used when generating PostScript output
3916  */
3917 static char_u prt_line_buffer[257];
3918 static garray_T prt_ps_buffer;
3919 
3920 # ifdef FEAT_MBYTE
3921 static int prt_do_conv;
3922 static vimconv_T prt_conv;
3923 # endif
3924 
3925     static void
3926 prt_write_file_raw_len(buffer, bytes)
3927     char_u	*buffer;
3928     int		bytes;
3929 {
3930     if (!prt_file_error
3931 	    && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
3932 							     != (size_t)bytes)
3933     {
3934 	EMSG(_("E455: Error writing to PostScript output file"));
3935 	prt_file_error = TRUE;
3936     }
3937 }
3938 
3939     static void
3940 prt_write_file(buffer)
3941     char_u	*buffer;
3942 {
3943     prt_write_file_len(buffer, STRLEN(buffer));
3944 }
3945 
3946     static void
3947 prt_write_file_len(buffer, bytes)
3948     char_u	*buffer;
3949     int		bytes;
3950 {
3951 #ifdef EBCDIC
3952     ebcdic2ascii(buffer, bytes);
3953 #endif
3954     prt_write_file_raw_len(buffer, bytes);
3955 }
3956 
3957 /*
3958  * Write a string.
3959  */
3960     static void
3961 prt_write_string(s)
3962     char	*s;
3963 {
3964     sprintf((char *)prt_line_buffer, "%s", s);
3965     prt_write_file(prt_line_buffer);
3966 }
3967 
3968 /*
3969  * Write an int and a space.
3970  */
3971     static void
3972 prt_write_int(i)
3973     int		i;
3974 {
3975     sprintf((char *)prt_line_buffer, "%d ", i);
3976     prt_write_file(prt_line_buffer);
3977 }
3978 
3979 /*
3980  * Write a boolean and a space.
3981  */
3982     static void
3983 prt_write_boolean(b)
3984     int		b;
3985 {
3986     sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
3987     prt_write_file(prt_line_buffer);
3988 }
3989 
3990 /*
3991  * Write a line to define the font.
3992  */
3993     static void
3994 prt_def_font(new_name, encoding, height, font)
3995     char	*new_name;
3996     char	*encoding;
3997     int		height;
3998     char	*font;
3999 {
4000     sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n", new_name, encoding, font);
4001     prt_write_file(prt_line_buffer);
4002     sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4003 						    new_name, height, new_name);
4004     prt_write_file(prt_line_buffer);
4005 }
4006 
4007 /*
4008  * Convert a real value into an integer and fractional part as integers, with
4009  * the fractional part being in the range [0,10^precision).  The fractional part
4010  * is also rounded based on the precision + 1'th fractional digit.
4011  */
4012     static void
4013 prt_real_bits(real, precision, pinteger, pfraction)
4014     double      real;
4015     int		precision;
4016     int		*pinteger;
4017     int		*pfraction;
4018 {
4019     int     i;
4020     int     integer;
4021     float   fraction;
4022 
4023     integer = (int)real;
4024     fraction = (float)(real - integer);
4025     if (real < (double)integer)
4026 	fraction = -fraction;
4027     for (i = 0; i < precision; i++)
4028 	fraction *= 10.0;
4029 
4030     *pinteger = integer;
4031     *pfraction = (int)(fraction + 0.5);
4032 }
4033 
4034 /*
4035  * Write a real and a space.  Save bytes if real value has no fractional part!
4036  * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4037  * what decimal point character to use, but PS always requires a '.'.
4038  */
4039     static void
4040 prt_write_real(val, prec)
4041     double	val;
4042     int		prec;
4043 {
4044     int     integer;
4045     int     fraction;
4046 
4047     prt_real_bits(val, prec, &integer, &fraction);
4048     /* Emit integer part */
4049     sprintf((char *)prt_line_buffer, "%d", integer);
4050     prt_write_file(prt_line_buffer);
4051     /* Only emit fraction if necessary */
4052     if (fraction != 0)
4053     {
4054 	/* Remove any trailing zeros */
4055 	while ((fraction % 10) == 0)
4056 	{
4057 	    prec--;
4058 	    fraction /= 10;
4059 	}
4060 	/* Emit fraction left padded with zeros */
4061 	sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
4062 	prt_write_file(prt_line_buffer);
4063     }
4064     sprintf((char *)prt_line_buffer, " ");
4065     prt_write_file(prt_line_buffer);
4066 }
4067 
4068 /*
4069  * Write a line to define a numeric variable.
4070  */
4071     static void
4072 prt_def_var(name, value, prec)
4073     char	*name;
4074     double	value;
4075     int		prec;
4076 {
4077     sprintf((char *)prt_line_buffer, "/%s ", name);
4078     prt_write_file(prt_line_buffer);
4079     prt_write_real(value, prec);
4080     sprintf((char *)prt_line_buffer, "d\n");
4081     prt_write_file(prt_line_buffer);
4082 }
4083 
4084 /* Convert size from font space to user space at current font scale */
4085 #define PRT_PS_FONT_TO_USER(scale, size)    ((size) * ((scale)/1000.0))
4086 
4087     static void
4088 prt_flush_buffer()
4089 {
4090     if (prt_ps_buffer.ga_len > 0)
4091     {
4092 	/* Any background color must be drawn first */
4093 	if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
4094 	{
4095 	    int     r, g, b;
4096 
4097 	    if (prt_do_moveto)
4098 	    {
4099 		prt_write_real(prt_pos_x_moveto, 2);
4100 		prt_write_real(prt_pos_y_moveto, 2);
4101 		prt_write_string("m\n");
4102 		prt_do_moveto = FALSE;
4103 	    }
4104 
4105 	    /* Size of rect of background color on which text is printed */
4106 	    prt_write_real(prt_text_count * prt_char_width, 2);
4107 	    prt_write_real(prt_line_height, 2);
4108 
4109 	    /* Lastly add the color of the background */
4110 	    r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
4111 	    g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
4112 	    b = prt_new_bgcol & 0xff;
4113 	    prt_write_real(r / 255.0, 3);
4114 	    prt_write_real(g / 255.0, 3);
4115 	    prt_write_real(b / 255.0, 3);
4116 	    prt_write_string("bg\n");
4117 	}
4118 	/* Draw underlines before the text as it makes it slightly easier to
4119 	 * find the starting point.
4120 	 */
4121 	if (prt_do_underline)
4122 	{
4123 	    if (prt_do_moveto)
4124 	    {
4125 		prt_write_real(prt_pos_x_moveto, 2);
4126 		prt_write_real(prt_pos_y_moveto, 2);
4127 		prt_write_string("m\n");
4128 		prt_do_moveto = FALSE;
4129 	    }
4130 
4131 	    /* Underlining is easy - just need the number of characters to
4132 	     * print. */
4133 	    prt_write_real(prt_text_count * prt_char_width, 2);
4134 	    prt_write_string("ul\n");
4135 	}
4136 	/* Draw the text
4137 	 * Note: we write text out raw - EBCDIC conversion is handled in the
4138 	 * PostScript world via the font encoding vector. */
4139 	prt_write_string("(");
4140 	prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
4141 	prt_write_string(")");
4142 	/* Add a moveto if need be and use the appropriate show procedure */
4143 	if (prt_do_moveto)
4144 	{
4145 	    prt_write_real(prt_pos_x_moveto, 2);
4146 	    prt_write_real(prt_pos_y_moveto, 2);
4147 	    /* moveto and a show */
4148 	    prt_write_string("ms\n");
4149 	    prt_do_moveto = FALSE;
4150 	}
4151 	else /* Simple show */
4152 	    prt_write_string("s\n");
4153 
4154 	ga_clear(&prt_ps_buffer);
4155 	ga_init2(&prt_ps_buffer, (int)sizeof(char), PRT_PS_DEFAULT_BUFFER_SIZE);
4156     }
4157 }
4158 
4159 static char_u *resource_filename;
4160 
4161     static void
4162 prt_resource_name(filename)
4163     char_u  *filename;
4164 {
4165     if (STRLEN(filename) >= MAXPATHL)
4166 	*resource_filename = NUL;
4167     else
4168 	STRCPY(resource_filename, filename);
4169 }
4170 
4171     static int
4172 prt_find_resource(name, resource)
4173     char	*name;
4174     struct prt_ps_resource_S *resource;
4175 {
4176     char_u	buffer[MAXPATHL + 1];
4177 
4178     STRCPY(resource->name, name);
4179     /* Look for named resource file in runtimepath */
4180     STRCPY(buffer, "print");
4181     add_pathsep(buffer);
4182     STRCAT(buffer, name);
4183     STRCAT(buffer, ".ps");
4184     resource_filename = resource->filename;
4185     *resource_filename = NUL;
4186     return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
4187 	    && resource->filename[0] != NUL);
4188 }
4189 
4190 /* PS CR and LF characters have platform independent values */
4191 #define PSLF  (0x0a)
4192 #define PSCR  (0x0d)
4193 
4194 /* Very simple hand crafted parser to get the type, title, and version number of
4195  * a PS resource file so the file details can be added to the DSC header
4196  * comments. */
4197     static int
4198 prt_open_resource(resource)
4199     struct prt_ps_resource_S *resource;
4200 {
4201     FILE	*fd_resource;
4202     char_u	buffer[128];
4203     char_u	*ch = buffer;
4204     char_u	*ch2;
4205 
4206     fd_resource = mch_fopen((char *)resource->filename, READBIN);
4207     if (fd_resource == NULL)
4208     {
4209 	EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
4210 	return FALSE;
4211     }
4212     vim_memset(buffer, NUL, sizeof(buffer));
4213 
4214     /* Parse first line to ensure valid resource file */
4215     (void)fread((char *)buffer, sizeof(char_u), sizeof(buffer),
4216 								 fd_resource);
4217     if (ferror(fd_resource))
4218     {
4219 	EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4220 		resource->filename);
4221 	fclose(fd_resource);
4222 	return FALSE;
4223     }
4224 
4225     if (STRNCMP(ch, PRT_RESOURCE_HEADER, STRLEN(PRT_RESOURCE_HEADER)) != 0)
4226     {
4227 	EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
4228 		resource->filename);
4229 	fclose(fd_resource);
4230 	return FALSE;
4231     }
4232 
4233     /* Skip over any version numbers and following ws */
4234     ch += STRLEN(PRT_RESOURCE_HEADER);
4235     while (!isspace(*ch))
4236 	ch++;
4237     while (isspace(*ch))
4238 	ch++;
4239 
4240     if (STRNCMP(ch, PRT_RESOURCE_RESOURCE, STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
4241     {
4242 	EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4243 		resource->filename);
4244 	fclose(fd_resource);
4245 	return FALSE;
4246     }
4247     ch += STRLEN(PRT_RESOURCE_RESOURCE);
4248 
4249     /* Decide type of resource in the file */
4250     if (STRNCMP(ch, PRT_RESOURCE_PROCSET, STRLEN(PRT_RESOURCE_PROCSET)) == 0)
4251     {
4252 	resource->type = PRT_RESOURCE_TYPE_PROCSET;
4253 	ch += STRLEN(PRT_RESOURCE_PROCSET);
4254     }
4255     else if (STRNCMP(ch, PRT_RESOURCE_ENCODING, STRLEN(PRT_RESOURCE_ENCODING)) == 0)
4256     {
4257 	resource->type = PRT_RESOURCE_TYPE_ENCODING;
4258 	ch += STRLEN(PRT_RESOURCE_ENCODING);
4259     }
4260     else
4261     {
4262 	EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4263 		resource->filename);
4264 	fclose(fd_resource);
4265 	return FALSE;
4266     }
4267 
4268     /* Consume up to and including the CR/LF/CR_LF */
4269     while (*ch != PSCR && *ch != PSLF)
4270 	ch++;
4271     while (*ch == PSCR || *ch == PSLF)
4272 	ch++;
4273 
4274     /* Match %%Title: */
4275     if (STRNCMP(ch, PRT_RESOURCE_TITLE, STRLEN(PRT_RESOURCE_TITLE)) != 0)
4276     {
4277 	EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4278 		resource->filename);
4279 	fclose(fd_resource);
4280 	return FALSE;
4281     }
4282     ch += STRLEN(PRT_RESOURCE_TITLE);
4283 
4284     /* Skip ws after %%Title: */
4285     while (isspace(*ch))
4286 	ch++;
4287 
4288     /* Copy up to the CR/LF/CR_LF */
4289     ch2 = resource->title;
4290     while (*ch != PSCR && *ch != PSLF)
4291 	*ch2++ = *ch++;
4292     *ch2 = '\0';
4293     while (*ch == PSCR || *ch == PSLF)
4294 	ch++;
4295 
4296     /* Match %%Version: */
4297     if (STRNCMP(ch, PRT_RESOURCE_VERSION, STRLEN(PRT_RESOURCE_VERSION)) != 0)
4298     {
4299 	EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4300 		resource->filename);
4301 	fclose(fd_resource);
4302 	return FALSE;
4303     }
4304     ch += STRLEN(PRT_RESOURCE_VERSION);
4305 
4306     /* Skip ws after %%Version: */
4307     while (isspace(*ch))
4308 	ch++;
4309 
4310     /* Copy up to the CR/LF/CR_LF */
4311     ch2 = resource->version;
4312     while (*ch != PSCR && *ch != PSLF)
4313 	*ch2++ = *ch++;
4314     *ch2 = '\0';
4315 
4316     fclose(fd_resource);
4317 
4318     return TRUE;
4319 }
4320 
4321     static int
4322 prt_check_resource(resource, version)
4323     struct prt_ps_resource_S *resource;
4324     char_u  *version;
4325 {
4326     /* Version number m.n should match, the revision number does not matter */
4327     if (STRNCMP(resource->version, version, STRLEN(version)))
4328     {
4329 	EMSG2(_("E621: \"%s\" resource file has wrong version"),
4330 		resource->name);
4331 	return FALSE;
4332     }
4333 
4334     /* Other checks to be added as needed */
4335     return TRUE;
4336 }
4337 
4338     static void
4339 prt_dsc_start()
4340 {
4341     prt_write_string("%!PS-Adobe-3.0\n");
4342 }
4343 
4344     static void
4345 prt_dsc_noarg(comment)
4346     char	*comment;
4347 {
4348     sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
4349     prt_write_file(prt_line_buffer);
4350 }
4351 
4352     static void
4353 prt_dsc_textline(comment, text)
4354     char	*comment;
4355     char	*text;
4356 {
4357     sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
4358     prt_write_file(prt_line_buffer);
4359 }
4360 
4361     static void
4362 prt_dsc_text(comment, text)
4363     char	*comment;
4364     char	*text;
4365 {
4366     /* TODO - should scan 'text' for any chars needing escaping! */
4367     sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
4368     prt_write_file(prt_line_buffer);
4369 }
4370 
4371 #define prt_dsc_atend(c)	prt_dsc_text((c), "atend")
4372 
4373     static void
4374 prt_dsc_ints(comment, count, ints)
4375     char	*comment;
4376     int		count;
4377     int		*ints;
4378 {
4379     int		i;
4380 
4381     sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
4382     prt_write_file(prt_line_buffer);
4383 
4384     for (i = 0; i < count; i++)
4385     {
4386 	sprintf((char *)prt_line_buffer, " %d", ints[i]);
4387 	prt_write_file(prt_line_buffer);
4388     }
4389 
4390     prt_write_string("\n");
4391 }
4392 
4393     static void
4394 prt_dsc_resources(comment, type, count, strings)
4395     char	*comment;	/* if NULL add to previous */
4396     char	*type;
4397     int		count;
4398     char	**strings;
4399 {
4400     int		i;
4401 
4402     if (comment != NULL)
4403 	sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
4404     else
4405 	sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
4406     prt_write_file(prt_line_buffer);
4407 
4408     for (i = 0; i < count; i++)
4409     {
4410 	sprintf((char *)prt_line_buffer, " %s", strings[i]);
4411 	prt_write_file(prt_line_buffer);
4412     }
4413 
4414     prt_write_string("\n");
4415 }
4416 
4417     static void
4418 prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
4419     int		duplex;
4420     int		tumble;
4421     int		collate;
4422     int		color;
4423     int		num_copies;
4424 {
4425     /* Only output the comment if we need to.
4426      * Note: tumble is ignored if we are not duplexing
4427      */
4428     if (!(duplex || collate || color || (num_copies > 1)))
4429 	return;
4430 
4431     sprintf((char *)prt_line_buffer, "%%%%Requirements:");
4432     prt_write_file(prt_line_buffer);
4433 
4434     if (duplex)
4435     {
4436 	prt_write_string(" duplex");
4437 	if (tumble)
4438 	    prt_write_string("(tumble)");
4439     }
4440     if (collate)
4441 	prt_write_string(" collate");
4442     if (color)
4443 	prt_write_string(" color");
4444     if (num_copies > 1)
4445     {
4446 	prt_write_string(" numcopies(");
4447 	/* Note: no space wanted so dont use prt_write_int() */
4448 	sprintf((char *)prt_line_buffer, "%d", num_copies);
4449 	prt_write_file(prt_line_buffer);
4450 	prt_write_string(")");
4451     }
4452     prt_write_string("\n");
4453 }
4454 
4455     static void
4456 prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
4457     char	*paper_name;
4458     double	width;
4459     double	height;
4460     double	weight;
4461     char	*colour;
4462     char	*type;
4463 {
4464     sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
4465     prt_write_file(prt_line_buffer);
4466     prt_write_real(width, 2);
4467     prt_write_real(height, 2);
4468     prt_write_real(weight, 2);
4469     if (colour == NULL)
4470 	prt_write_string("()");
4471     else
4472 	prt_write_string(colour);
4473     prt_write_string(" ");
4474     if (type == NULL)
4475 	prt_write_string("()");
4476     else
4477 	prt_write_string(type);
4478     prt_write_string("\n");
4479 }
4480 
4481     void
4482 mch_print_cleanup()
4483 {
4484 #ifdef FEAT_MBYTE
4485     if (prt_do_conv)
4486     {
4487 	convert_setup(&prt_conv, NULL, NULL);
4488 	prt_do_conv = FALSE;
4489     }
4490 #endif
4491     if (prt_ps_fd != NULL)
4492     {
4493 	fclose(prt_ps_fd);
4494 	prt_ps_fd = NULL;
4495 	prt_file_error = FALSE;
4496     }
4497     if (prt_ps_file_name != NULL)
4498     {
4499 	vim_free(prt_ps_file_name);
4500 	prt_ps_file_name = NULL;
4501     }
4502 }
4503 
4504     static float
4505 to_device_units(idx, physsize, def_number)
4506     int		idx;
4507     double	physsize;
4508     int		def_number;
4509 {
4510     float	ret;
4511     int		u;
4512     int		nr;
4513 
4514     u = prt_get_unit(idx);
4515     if (u == PRT_UNIT_NONE)
4516     {
4517 	u = PRT_UNIT_PERC;
4518 	nr = def_number;
4519     }
4520     else
4521 	nr = printer_opts[idx].number;
4522 
4523     switch (u)
4524     {
4525 	case PRT_UNIT_INCH:
4526 	    ret = (float)(nr * PRT_PS_DEFAULT_DPI);
4527 	    break;
4528 	case PRT_UNIT_MM:
4529 	    ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
4530 	    break;
4531 	case PRT_UNIT_POINT:
4532 	    ret = (float)nr;
4533 	    break;
4534 	case PRT_UNIT_PERC:
4535 	default:
4536 	    ret = (float)(physsize * nr) / 100;
4537 	    break;
4538     }
4539 
4540     return ret;
4541 }
4542 
4543 /*
4544  * Calculate margins for given width and height from printoptions settings.
4545  */
4546     static void
4547 prt_page_margins(width, height, left, right, top, bottom)
4548     double	width;
4549     double	height;
4550     double	*left;
4551     double	*right;
4552     double	*top;
4553     double	*bottom;
4554 {
4555     *left   = to_device_units(OPT_PRINT_LEFT, width, 10);
4556     *right  = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
4557     *top    = height - to_device_units(OPT_PRINT_TOP, height, 5);
4558     *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
4559 }
4560 
4561     static void
4562 prt_font_metrics(font_scale)
4563     int		font_scale;
4564 {
4565     prt_line_height = (float)font_scale;
4566     prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font.wx);
4567 }
4568 
4569 
4570     static int
4571 prt_get_cpl()
4572 {
4573     if (prt_use_number())
4574     {
4575 	prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
4576 	prt_left_margin += prt_number_width;
4577     }
4578     else
4579 	prt_number_width = 0.0;
4580 
4581     return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
4582 }
4583 
4584 /*
4585  * Get number of lines of text that fit on a page (excluding the header).
4586  */
4587     static int
4588 prt_get_lpp()
4589 {
4590     int lpp;
4591 
4592     /*
4593      * Calculate offset to lower left corner of background rect based on actual
4594      * font height (based on its bounding box) and the line height, handling the
4595      * case where the font height can exceed the line height.
4596      */
4597     prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
4598 					   prt_ps_font.bbox_min_y);
4599     if ((prt_ps_font.bbox_max_y - prt_ps_font.bbox_min_y) < 1000.0)
4600     {
4601 	prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
4602 				(1000.0 - (prt_ps_font.bbox_max_y -
4603 					    prt_ps_font.bbox_min_y)) / 2);
4604     }
4605 
4606     /* Get height for topmost line based on background rect offset. */
4607     prt_first_line_height = prt_line_height + prt_bgcol_offset;
4608 
4609     /* Calculate lpp */
4610     lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
4611 
4612     /* Adjust top margin if there is a header */
4613     prt_top_margin -= prt_line_height * prt_header_height();
4614 
4615     return lpp - prt_header_height();
4616 }
4617 
4618 /*ARGSUSED*/
4619     int
4620 mch_print_init(psettings, jobname, forceit)
4621     prt_settings_T *psettings;
4622     char_u	*jobname;
4623     int		forceit;
4624 {
4625     int		i;
4626     char	*paper_name;
4627     int		paper_strlen;
4628     int		fontsize;
4629     char_u	*p;
4630     double      left;
4631     double      right;
4632     double      top;
4633     double      bottom;
4634 
4635 #if 0
4636     /*
4637      * TODO:
4638      * If "forceit" is false: pop up a dialog to select:
4639      *	- printer name
4640      *	- copies
4641      *	- collated/uncollated
4642      *	- duplex off/long side/short side
4643      *	- paper size
4644      *	- portrait/landscape
4645      *	- font size
4646      *
4647      * If "forceit" is true: use the default printer and settings
4648      */
4649     if (forceit)
4650 	s_pd.Flags |= PD_RETURNDEFAULT;
4651 #endif
4652 
4653     /*
4654      * Find the size of the paper and set the margins.
4655      */
4656     prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
4657 	   || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
4658     if (printer_opts[OPT_PRINT_PAPER].present)
4659     {
4660 	paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
4661 	paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
4662     }
4663     else
4664     {
4665 	paper_name = "A4";
4666 	paper_strlen = 2;
4667     }
4668     for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
4669 	if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
4670 		&& STRNICMP(prt_mediasize[i].name, paper_name,
4671 							   paper_strlen) == 0)
4672 	    break;
4673     if (i == PRT_MEDIASIZE_LEN)
4674 	i = 0;
4675     prt_media = i;
4676 
4677     /*
4678      * Set PS pagesize based on media dimensions and print orientation.
4679      * Note: Media and page sizes have defined meanings in PostScript and should
4680      * be kept distinct.  Media is the paper (or transparency, or ...) that is
4681      * printed on, whereas the page size is the area that the PostScript
4682      * interpreter renders into.
4683      */
4684     if (prt_portrait)
4685     {
4686 	prt_page_width = prt_mediasize[i].width;
4687 	prt_page_height = prt_mediasize[i].height;
4688     }
4689     else
4690     {
4691 	prt_page_width = prt_mediasize[i].height;
4692 	prt_page_height = prt_mediasize[i].width;
4693     }
4694 
4695     /*
4696      * Set PS page margins based on the PS pagesize, not the mediasize - this
4697      * needs to be done before the cpl and lpp are calculated.
4698      */
4699     prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
4700 								    &bottom);
4701     prt_left_margin = (float)left;
4702     prt_right_margin = (float)right;
4703     prt_top_margin = (float)top;
4704     prt_bottom_margin = (float)bottom;
4705 
4706     /*
4707      * Set up the font size.
4708      */
4709     fontsize = PRT_PS_DEFAULT_FONTSIZE;
4710     for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
4711 	if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
4712 	    fontsize = atoi((char *)p + 2);
4713     prt_font_metrics(fontsize);
4714 
4715     /*
4716      * Return the number of characters per line, and lines per page for the
4717      * generic print code.
4718      */
4719     psettings->chars_per_line = prt_get_cpl();
4720     psettings->lines_per_page = prt_get_lpp();
4721 
4722     /* Catch margin settings that leave no space for output! */
4723     if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
4724 	return FAIL;
4725 
4726     /*
4727      * Sort out the number of copies to be printed.  PS by default will do
4728      * uncollated copies for you, so once we know how many uncollated copies are
4729      * wanted cache it away and lie to the generic code that we only want one
4730      * uncollated copy.
4731      */
4732     psettings->n_collated_copies = 1;
4733     psettings->n_uncollated_copies = 1;
4734     prt_num_copies = 1;
4735     prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
4736 	    || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
4737     if (prt_collate)
4738     {
4739 	/* TODO: Get number of collated copies wanted. */
4740 	psettings->n_collated_copies = 1;
4741     }
4742     else
4743     {
4744 	/* TODO: Get number of uncollated copies wanted and update the cached
4745 	 * count.
4746 	 */
4747 	prt_num_copies = 1;
4748     }
4749 
4750     psettings->jobname = jobname;
4751 
4752     /*
4753      * Set up printer duplex and tumble based on Duplex option setting - default
4754      * is long sided duplex printing (i.e. no tumble).
4755      */
4756     prt_duplex = TRUE;
4757     prt_tumble = FALSE;
4758     psettings->duplex = 1;
4759     if (printer_opts[OPT_PRINT_DUPLEX].present)
4760     {
4761 	if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
4762 	{
4763 	    prt_duplex = FALSE;
4764 	    psettings->duplex = 0;
4765 	}
4766 	else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
4767 									 == 0)
4768 	    prt_tumble = TRUE;
4769     }
4770 
4771     /* For now user abort not supported */
4772     psettings->user_abort = 0;
4773 
4774     /* If the user didn't specify a file name, use a temp file. */
4775     if (psettings->outfile == NULL)
4776     {
4777 	prt_ps_file_name = vim_tempname('p');
4778 	if (prt_ps_file_name == NULL)
4779 	{
4780 	    EMSG(_(e_notmp));
4781 	    return FAIL;
4782 	}
4783 	prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
4784     }
4785     else
4786     {
4787 	p = expand_env_save(psettings->outfile);
4788 	if (p != NULL)
4789 	{
4790 	    prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
4791 	    vim_free(p);
4792 	}
4793     }
4794     if (prt_ps_fd == NULL)
4795     {
4796 	EMSG(_("E324: Can't open PostScript output file"));
4797 	mch_print_cleanup();
4798 	return FAIL;
4799     }
4800 
4801     ga_init2(&prt_ps_buffer, (int)sizeof(char), PRT_PS_DEFAULT_BUFFER_SIZE);
4802 
4803     prt_page_num = 0;
4804 
4805     prt_attribute_change = FALSE;
4806     prt_need_moveto = FALSE;
4807     prt_need_font = FALSE;
4808     prt_need_fgcol = FALSE;
4809     prt_need_bgcol = FALSE;
4810     prt_need_underline = FALSE;
4811 
4812     prt_file_error = FALSE;
4813 
4814     return OK;
4815 }
4816 
4817     static int
4818 prt_add_resource(resource)
4819     struct prt_ps_resource_S *resource;
4820 {
4821     FILE*	fd_resource;
4822     char_u	resource_buffer[512];
4823     char	*resource_name[1];
4824     size_t	bytes_read;
4825 
4826     fd_resource = mch_fopen((char *)resource->filename, READBIN);
4827     if (fd_resource == NULL)
4828     {
4829 	EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
4830 	return FALSE;
4831     }
4832     resource_name[0] = (char *)resource->title;
4833     prt_dsc_resources("BeginResource",
4834 			    resource_types[resource->type], 1, resource_name);
4835 
4836     prt_dsc_textline("BeginDocument", (char *)resource->filename);
4837 
4838     for (;;)
4839     {
4840 	bytes_read = fread((char *)resource_buffer, sizeof(char_u),
4841 			   sizeof(resource_buffer), fd_resource);
4842 	if (ferror(fd_resource))
4843 	{
4844 	    EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4845 							    resource->filename);
4846 	    fclose(fd_resource);
4847 	    return FALSE;
4848 	}
4849 	if (bytes_read == 0)
4850 	    break;
4851 	prt_write_file_raw_len(resource_buffer, bytes_read);
4852 	if (prt_file_error)
4853 	{
4854 	    fclose(fd_resource);
4855 	    return FALSE;
4856 	}
4857     }
4858     fclose(fd_resource);
4859 
4860     prt_dsc_noarg("EndDocument");
4861 
4862     prt_dsc_noarg("EndResource");
4863 
4864     return TRUE;
4865 }
4866 
4867     int
4868 mch_print_begin(psettings)
4869     prt_settings_T *psettings;
4870 {
4871     time_t	now;
4872     int		bbox[4];
4873     char	*p_time;
4874     char	*resource[1];
4875     double      left;
4876     double      right;
4877     double      top;
4878     double      bottom;
4879     struct prt_ps_resource_S res_prolog;
4880     struct prt_ps_resource_S res_encoding;
4881     char_u      buffer[256];
4882     char_u      *p_encoding;
4883 #ifdef FEAT_MBYTE
4884     int		props;
4885 #endif
4886 
4887     /*
4888      * PS DSC Header comments - no PS code!
4889      */
4890     prt_dsc_start();
4891     prt_dsc_textline("Title", (char *)psettings->jobname);
4892     /* TODO - platform dependent user name retrieval */
4893     prt_dsc_textline("For", "Unknown");
4894     prt_dsc_textline("Creator", VIM_VERSION_LONG);
4895     /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
4896     now = time(NULL);
4897     p_time = ctime(&now);
4898     /* Note: ctime() adds a \n so we have to remove it :-( */
4899     *(vim_strchr((char_u *)p_time, '\n')) = '\0';
4900     prt_dsc_textline("CreationDate", p_time);
4901     prt_dsc_textline("DocumentData", "Clean8Bit");
4902     prt_dsc_textline("Orientation", "Portrait");
4903     prt_dsc_atend("Pages");
4904     prt_dsc_textline("PageOrder", "Ascend");
4905     /* The bbox does not change with orientation - it is always in the default
4906      * user coordinate system!  We have to recalculate right and bottom
4907      * coordinates based on the font metrics for the bbox to be accurate. */
4908     prt_page_margins(prt_mediasize[prt_media].width,
4909 					    prt_mediasize[prt_media].height,
4910 					    &left, &right, &top, &bottom);
4911     bbox[0] = (int)left;
4912     if (prt_portrait)
4913     {
4914 	/* In portrait printing the fixed point is the top left corner so we
4915 	 * derive the bbox from that point.  We have the expected cpl chars
4916 	 * across the media and lpp lines down the media.
4917 	 */
4918 	bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
4919 							    * prt_line_height);
4920 	bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
4921 									+ 0.5);
4922 	bbox[3] = (int)(top + 0.5);
4923     }
4924     else
4925     {
4926 	/* In landscape printing the fixed point is the bottom left corner so we
4927 	 * derive the bbox from that point.  We have lpp chars across the media
4928 	 * and cpl lines up the media.
4929 	 */
4930 	bbox[1] = (int)bottom;
4931 	bbox[2] = (int)(left + ((psettings->lines_per_page
4932 			      + prt_header_height()) * prt_line_height) + 0.5);
4933 	bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
4934 									+ 0.5);
4935     }
4936     prt_dsc_ints("BoundingBox", 4, bbox);
4937     /* The media width and height does not change with landscape printing! */
4938     prt_dsc_docmedia(prt_mediasize[prt_media].name,
4939 				prt_mediasize[prt_media].width,
4940 				prt_mediasize[prt_media].height,
4941 				(double)0, NULL, NULL);
4942     prt_dsc_resources("DocumentNeededResources", "font", 4,
4943 						     prt_ps_font.ps_fontname);
4944 
4945     /* Search for external resources we supply */
4946     if (!prt_find_resource("prolog", &res_prolog))
4947     {
4948 	EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
4949 	return FALSE;
4950     }
4951     if (!prt_open_resource(&res_prolog))
4952 	return FALSE;
4953     if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
4954 	return FALSE;
4955     /* Find an encoding to use for printing.
4956      * Check 'printencoding'. If not set or not found, then use 'encoding'. If
4957      * that cannot be found then default to "latin1".
4958      * Note: VIM specific encoding header is always skipped.
4959      */
4960 #ifdef FEAT_MBYTE
4961     props = enc_canon_props(p_enc);
4962 #endif
4963     p_encoding = enc_skip(p_penc);
4964     if (*p_encoding == NUL
4965 	    || !prt_find_resource((char *)p_encoding, &res_encoding))
4966     {
4967 	/* 'printencoding' not set or not supported - find alternate */
4968 #ifdef FEAT_MBYTE
4969 	p_encoding = enc_skip(p_enc);
4970 	if (!(props & ENC_8BIT)
4971 		|| !prt_find_resource((char *)p_encoding, &res_encoding))
4972 	{
4973 	    /* 8-bit 'encoding' is not supported */
4974 #endif
4975 	    /* Use latin1 as default printing encoding */
4976 	    p_encoding = (char_u *)"latin1";
4977 	    if (!prt_find_resource((char *)p_encoding, &res_encoding))
4978 	    {
4979 		EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
4980 			p_encoding);
4981 		return FALSE;
4982 	    }
4983 #ifdef FEAT_MBYTE
4984 	}
4985 #endif
4986     }
4987     if (!prt_open_resource(&res_encoding))
4988 	return FALSE;
4989     /* For the moment there are no checks on encoding resource files to perform */
4990 #ifdef FEAT_MBYTE
4991     /* Set up encoding conversion if starting from multi-byte */
4992     props = enc_canon_props(p_enc);
4993     if (!(props & ENC_8BIT))
4994     {
4995 	if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
4996 	{
4997 	    EMSG2(_("E620: Unable to convert from multi-byte to \"%s\" encoding"),
4998 		    p_encoding);
4999 	    return FALSE;
5000 	}
5001 	prt_do_conv = TRUE;
5002     }
5003 #endif
5004 
5005     /* List resources supplied */
5006     resource[0] = (char *)buffer;
5007     STRCPY(buffer, res_prolog.title);
5008     STRCAT(buffer, " ");
5009     STRCAT(buffer, res_prolog.version);
5010     prt_dsc_resources("DocumentSuppliedResources", "procset", 1, resource);
5011     STRCPY(buffer, res_encoding.title);
5012     STRCAT(buffer, " ");
5013     STRCAT(buffer, res_encoding.version);
5014     prt_dsc_resources(NULL, "encoding", 1, resource);
5015     prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
5016 #ifdef FEAT_SYN_HL
5017 					psettings->do_syntax
5018 #else
5019 					0
5020 #endif
5021 					, prt_num_copies);
5022     prt_dsc_noarg("EndComments");
5023 
5024     /*
5025      * PS Document page defaults
5026      */
5027     prt_dsc_noarg("BeginDefaults");
5028 
5029     /* List font resources most likely common to all pages */
5030     prt_dsc_resources("PageResources", "font", 4, prt_ps_font.ps_fontname);
5031     /* Paper will be used for all pages */
5032     prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
5033 
5034     prt_dsc_noarg("EndDefaults");
5035 
5036     /*
5037      * PS Document prolog inclusion - all required procsets.
5038      */
5039     prt_dsc_noarg("BeginProlog");
5040 
5041     /* For now there is just the one procset to be included in the PS file. */
5042     if (!prt_add_resource(&res_prolog))
5043 	return FALSE;
5044 
5045     /* There will be only one font encoding to be included in the PS file. */
5046     if (!prt_add_resource(&res_encoding))
5047 	return FALSE;
5048 
5049     prt_dsc_noarg("EndProlog");
5050 
5051     /*
5052      * PS Document setup - must appear after the prolog
5053      */
5054     prt_dsc_noarg("BeginSetup");
5055 
5056     /* Device setup - page size and number of uncollated copies */
5057     prt_write_int((int)prt_mediasize[prt_media].width);
5058     prt_write_int((int)prt_mediasize[prt_media].height);
5059     prt_write_int(0);
5060     prt_write_string("sps\n");
5061     prt_write_int(prt_num_copies);
5062     prt_write_string("nc\n");
5063     prt_write_boolean(prt_duplex);
5064     prt_write_boolean(prt_tumble);
5065     prt_write_string("dt\n");
5066     prt_write_boolean(prt_collate);
5067     prt_write_string("c\n");
5068 
5069     /* Font resource inclusion and definition */
5070     prt_dsc_resources("IncludeResource", "font", 1,
5071 				 &prt_ps_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5072     prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
5073 				  prt_ps_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5074     prt_dsc_resources("IncludeResource", "font", 1,
5075 				  &prt_ps_font.ps_fontname[PRT_PS_FONT_BOLD]);
5076     prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
5077 				   prt_ps_font.ps_fontname[PRT_PS_FONT_BOLD]);
5078     prt_dsc_resources("IncludeResource", "font", 1,
5079 			       &prt_ps_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5080     prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
5081 				prt_ps_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5082     prt_dsc_resources("IncludeResource", "font", 1,
5083 			   &prt_ps_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5084     prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
5085 			    prt_ps_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5086 
5087     /* Misc constant vars used for underlining and background rects */
5088     prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
5089 						prt_ps_font.uline_offset), 2);
5090     prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
5091 						 prt_ps_font.uline_width), 2);
5092     prt_def_var("BO", prt_bgcol_offset, 2);
5093 
5094     prt_dsc_noarg("EndSetup");
5095 
5096     /* Fail if any problems writing out to the PS file */
5097     return !prt_file_error;
5098 }
5099 
5100     void
5101 mch_print_end(psettings)
5102     prt_settings_T *psettings;
5103 {
5104     prt_dsc_noarg("Trailer");
5105 
5106     /*
5107      * Output any info we don't know in toto until we finish
5108      */
5109     prt_dsc_ints("Pages", 1, &prt_page_num);
5110 
5111     prt_dsc_noarg("EOF");
5112 
5113     if (!prt_file_error && psettings->outfile == NULL
5114 					&& !got_int && !psettings->user_abort)
5115     {
5116 	/* Close the file first. */
5117 	if (prt_ps_fd != NULL)
5118 	{
5119 	    fclose(prt_ps_fd);
5120 	    prt_ps_fd = NULL;
5121 	}
5122 	prt_message((char_u *)_("Sending to printer..."));
5123 
5124 	/* Not printing to a file: use 'printexpr' to print the file. */
5125 	if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
5126 	    EMSG(_("E365: Failed to print PostScript file"));
5127 	else
5128 	    prt_message((char_u *)_("Print job sent."));
5129     }
5130 
5131     mch_print_cleanup();
5132 }
5133 
5134     int
5135 mch_print_end_page()
5136 {
5137     prt_flush_buffer();
5138 
5139     prt_write_string("re sp\n");
5140 
5141     prt_dsc_noarg("PageTrailer");
5142 
5143     return !prt_file_error;
5144 }
5145 
5146 /*ARGSUSED*/
5147     int
5148 mch_print_begin_page(str)
5149     char_u	*str;
5150 {
5151     int		page_num[2];
5152 
5153     prt_page_num++;
5154 
5155     page_num[0] = page_num[1] = prt_page_num;
5156     prt_dsc_ints("Page", 2, page_num);
5157 
5158     prt_dsc_noarg("BeginPageSetup");
5159 
5160     prt_write_string("sv\n0 g\nF0 sf\n");
5161     prt_fgcol = PRCOLOR_BLACK;
5162     prt_bgcol = PRCOLOR_WHITE;
5163     prt_font = PRT_PS_FONT_ROMAN;
5164 
5165     /* Set up page transformation for landscape printing. */
5166     if (!prt_portrait)
5167     {
5168 	prt_write_int(-((int)prt_mediasize[prt_media].width));
5169 	prt_write_string("sl\n");
5170     }
5171 
5172     prt_dsc_noarg("EndPageSetup");
5173 
5174     /* We have reset the font attributes, force setting them again. */
5175     curr_bg = (long_u)0xffffffff;
5176     curr_fg = (long_u)0xffffffff;
5177     curr_bold = MAYBE;
5178 
5179     return !prt_file_error;
5180 }
5181 
5182     int
5183 mch_print_blank_page()
5184 {
5185     return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
5186 }
5187 
5188 static float prt_pos_x = 0;
5189 static float prt_pos_y = 0;
5190 
5191     void
5192 mch_print_start_line(margin, page_line)
5193     int		margin;
5194     int		page_line;
5195 {
5196     prt_pos_x = prt_left_margin;
5197     if (margin)
5198 	prt_pos_x -= prt_number_width;
5199 
5200     prt_pos_y = prt_top_margin - prt_first_line_height -
5201 					page_line * prt_line_height;
5202 
5203     prt_attribute_change = TRUE;
5204     prt_need_moveto = TRUE;
5205 }
5206 
5207 /*ARGSUSED*/
5208     int
5209 mch_print_text_out(p, len)
5210     char_u	*p;
5211     int		len;
5212 {
5213     int		need_break;
5214     char_u	ch;
5215     char_u      ch_buff[8];
5216 
5217     /* Output any required changes to the graphics state, after flushing any
5218      * text buffered so far.
5219      */
5220     if (prt_attribute_change)
5221     {
5222 	prt_flush_buffer();
5223 	/* Reset count of number of chars that will be printed */
5224 	prt_text_count = 0;
5225 
5226 	if (prt_need_moveto)
5227 	{
5228 	    prt_pos_x_moveto = prt_pos_x;
5229 	    prt_pos_y_moveto = prt_pos_y;
5230 	    prt_do_moveto = TRUE;
5231 
5232 	    prt_need_moveto = FALSE;
5233 	}
5234 	if (prt_need_font)
5235 	{
5236 	    prt_write_string("F");
5237 	    prt_write_int(prt_font);
5238 	    prt_write_string("sf\n");
5239 	    prt_need_font = FALSE;
5240 	}
5241 	if (prt_need_fgcol)
5242 	{
5243 	    int     r, g, b;
5244 	    r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
5245 	    g = ((unsigned)prt_fgcol & 0xff00) >> 8;
5246 	    b = prt_fgcol & 0xff;
5247 
5248 	    prt_write_real(r / 255.0, 3);
5249 	    if (r == g && g == b)
5250 	    {
5251 		prt_write_string("g\n");
5252 	    }
5253 	    else
5254 	    {
5255 		prt_write_real(g / 255.0, 3);
5256 		prt_write_real(b / 255.0, 3);
5257 		prt_write_string("r\n");
5258 	    }
5259 	    prt_need_fgcol = FALSE;
5260 	}
5261 
5262 	if (prt_bgcol != PRCOLOR_WHITE)
5263 	{
5264 	    prt_new_bgcol = prt_bgcol;
5265 	    if (prt_need_bgcol)
5266 		prt_do_bgcol = TRUE;
5267 	}
5268 	else
5269 	    prt_do_bgcol = FALSE;
5270 	prt_need_bgcol = FALSE;
5271 
5272 	if (prt_need_underline)
5273 	    prt_do_underline = prt_underline;
5274 	prt_need_underline = FALSE;
5275 
5276 	prt_attribute_change = FALSE;
5277     }
5278 
5279 #ifdef FEAT_MBYTE
5280     if (prt_do_conv)
5281     {
5282 	/* Convert from multi-byte to 8-bit encoding */
5283 	p = string_convert(&prt_conv, p, &len);
5284 	if (p == NULL)
5285 	    p = (char_u *)"";
5286     }
5287 #endif
5288     /* Add next character to buffer of characters to output.
5289      * Note: One printed character may require several PS characters to
5290      * represent it, but we only count them as one printed character.
5291      */
5292     ch = *p;
5293     if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
5294     {
5295 	/* Convert non-printing characters to either their escape or octal
5296 	 * sequence, ensures PS sent over a serial line does not interfere with
5297 	 * the comms protocol.
5298 	 * Note: For EBCDIC we need to write out the escape sequences as ASCII
5299 	 * codes!
5300 	 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
5301 	 */
5302 	ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
5303 	switch (ch)
5304 	{
5305 	    case BS:   ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
5306 	    case TAB:  ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
5307 	    case NL:   ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
5308 	    case FF:   ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
5309 	    case CAR:  ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
5310 	    case '(':  ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
5311 	    case ')':  ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
5312 	    case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
5313 
5314 	    default:
5315 		       sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
5316 #ifdef EBCDIC
5317 		       ebcdic2ascii(ch_buff, 3);
5318 #endif
5319 		       ga_append(&prt_ps_buffer, ch_buff[0]);
5320 		       ga_append(&prt_ps_buffer, ch_buff[1]);
5321 		       ga_append(&prt_ps_buffer, ch_buff[2]);
5322 		       break;
5323 	}
5324     }
5325     else
5326     ga_append(&prt_ps_buffer, ch);
5327 
5328 #ifdef FEAT_MBYTE
5329     /* Need to free any translated characters */
5330     if (prt_do_conv && (*p != NUL))
5331 	vim_free(p);
5332 #endif
5333 
5334     prt_text_count++;
5335     prt_pos_x += prt_char_width;
5336 
5337     /* The downside of fp - need a little tolerance in the right margin check */
5338     need_break = (prt_pos_x + prt_char_width > (prt_right_margin + 0.01));
5339 
5340     if (need_break)
5341 	prt_flush_buffer();
5342 
5343     return need_break;
5344 }
5345 
5346     void
5347 mch_print_set_font(iBold, iItalic, iUnderline)
5348     int		iBold;
5349     int		iItalic;
5350     int		iUnderline;
5351 {
5352     int		font = 0;
5353 
5354     if (iBold)
5355 	font |= 0x01;
5356     if (iItalic)
5357 	font |= 0x02;
5358 
5359     if (font != prt_font)
5360     {
5361 	prt_font = font;
5362 	prt_attribute_change = TRUE;
5363 	prt_need_font = TRUE;
5364     }
5365     if (prt_underline != iUnderline)
5366     {
5367 	prt_underline = iUnderline;
5368 	prt_attribute_change = TRUE;
5369 	prt_need_underline = TRUE;
5370     }
5371 }
5372 
5373     void
5374 mch_print_set_bg(bgcol)
5375     long_u	bgcol;
5376 {
5377     prt_bgcol = bgcol;
5378     prt_attribute_change = TRUE;
5379     prt_need_bgcol = TRUE;
5380 }
5381 
5382     void
5383 mch_print_set_fg(fgcol)
5384     long_u	fgcol;
5385 {
5386     if (fgcol != (long_u)prt_fgcol)
5387     {
5388 	prt_fgcol = fgcol;
5389 	prt_attribute_change = TRUE;
5390 	prt_need_fgcol = TRUE;
5391     }
5392 }
5393 
5394 # endif /*FEAT_POSTSCRIPT*/
5395 #endif /*FEAT_PRINTER*/
5396 
5397 #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5398 	&& (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
5399 static char *get_locale_val __ARGS((int what));
5400 
5401     static char *
5402 get_locale_val(what)
5403     int		what;
5404 {
5405     char	*loc;
5406 
5407     /* Obtain the locale value from the libraries.  For DJGPP this is
5408      * redefined and it doesn't use the arguments. */
5409     loc = setlocale(what, NULL);
5410 
5411 # if defined(__BORLANDC__)
5412     if (loc != NULL)
5413     {
5414 	char_u	*p;
5415 
5416 	/* Borland returns something like "LC_CTYPE=<name>\n"
5417 	 * Let's try to fix that bug here... */
5418 	p = vim_strchr(loc, '=');
5419 	if (p != NULL)
5420 	{
5421 	    loc = ++p;
5422 	    while (*p != NUL)	/* remove trailing newline */
5423 	    {
5424 		if (*p < ' ')
5425 		{
5426 		    *p = NUL;
5427 		    break;
5428 		}
5429 		++p;
5430 	    }
5431 	}
5432     }
5433 # endif
5434 
5435     return loc;
5436 }
5437 #endif
5438 
5439 
5440 #ifdef WIN32
5441 /*
5442  * On MS-Windows locale names are strings like "German_Germany.1252", but
5443  * gettext expects "de".  Try to translate one into another here for a few
5444  * supported languages.
5445  */
5446     static char_u *
5447 gettext_lang(char_u *name)
5448 {
5449     int		i;
5450     static char *(mtable[]) = {
5451 			"afrikaans",	"af",
5452 			"czech",	"cs",
5453 			"dutch",	"nl",
5454 			"german",	"de",
5455 			"english_united kingdom", "en_GB",
5456 			"spanish",	"es",
5457 			"french",	"fr",
5458 			"italian",	"it",
5459 			"japanese",	"ja",
5460 			"korean",	"ko",
5461 			"norwegian",	"no",
5462 			"polish",	"pl",
5463 			"russian",	"ru",
5464 			"slovak",	"sk",
5465 			"swedish",	"sv",
5466 			"ukrainian",	"uk",
5467 			"chinese_china", "zh_CN",
5468 			"chinese_taiwan", "zh_TW",
5469 			NULL};
5470 
5471     for (i = 0; mtable[i] != NULL; i += 2)
5472 	if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
5473 	    return mtable[i + 1];
5474     return name;
5475 }
5476 #endif
5477 
5478 #if defined(FEAT_MULTI_LANG) || defined(PROTO)
5479 /*
5480  * Obtain the current messages language.  Used to set the default for
5481  * 'helplang'.  May return NULL or an empty string.
5482  */
5483     char_u *
5484 get_mess_lang()
5485 {
5486     char_u *p;
5487 
5488 # if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
5489 #  if defined(LC_MESSAGES)
5490     p = (char_u *)get_locale_val(LC_MESSAGES);
5491 #  else
5492     /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
5493      * may be set to the LCID number. */
5494     p = (char_u *)get_locale_val(LC_ALL);
5495 #  endif
5496 # else
5497     p = mch_getenv((char_u *)"LC_ALL");
5498     if (p == NULL || *p == NUL)
5499     {
5500 	p = mch_getenv((char_u *)"LC_MESSAGES");
5501 	if (p == NULL || *p == NUL)
5502 	    p = mch_getenv((char_u *)"LANG");
5503     }
5504 # endif
5505 # ifdef WIN32
5506     p = gettext_lang(p);
5507 # endif
5508     return p;
5509 }
5510 #endif
5511 
5512 #if !defined(LC_MESSAGES) \
5513     && (((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5514 		&& (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))) \
5515 	    || defined(FEAT_EVAL))
5516 static char_u *get_mess_env __ARGS((void));
5517 
5518 /*
5519  * Get the language used for messages from the environment.
5520  */
5521     static char_u *
5522 get_mess_env()
5523 {
5524     char_u	*p;
5525 
5526     p = mch_getenv((char_u *)"LC_ALL");
5527     if (p == NULL || *p == NUL)
5528     {
5529 	p = mch_getenv((char_u *)"LC_MESSAGES");
5530 	if (p == NULL || *p == NUL)
5531 	{
5532 	    p = mch_getenv((char_u *)"LANG");
5533 	    if (p != NULL && VIM_ISDIGIT(*p))
5534 		p = NULL;		/* ignore something like "1043" */
5535 # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
5536 	    if (p == NULL || *p == NUL)
5537 		p = (char_u *)get_locale_val(LC_CTYPE);
5538 # endif
5539 	}
5540     }
5541     return p;
5542 }
5543 #endif
5544 
5545 #if defined(FEAT_EVAL) || defined(PROTO)
5546 
5547 /*
5548  * Set the "v:lang" variable according to the current locale setting.
5549  * Also do "v:lc_time"and "v:ctype".
5550  */
5551     void
5552 set_lang_var()
5553 {
5554     char_u	*loc;
5555 
5556 # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
5557     loc = (char_u *)get_locale_val(LC_CTYPE);
5558 # else
5559     /* setlocale() not supported: use the default value */
5560     loc = (char_u *)"C";
5561 # endif
5562     set_vim_var_string(VV_CTYPE, loc, -1);
5563 
5564     /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
5565      * back to LC_CTYPE if it's empty. */
5566 # if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
5567     loc = (char_u *)get_locale_val(LC_MESSAGES);
5568 # else
5569     loc = get_mess_env();
5570 # endif
5571     set_vim_var_string(VV_LANG, loc, -1);
5572 
5573 # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
5574     loc = (char_u *)get_locale_val(LC_TIME);
5575 # endif
5576     set_vim_var_string(VV_LC_TIME, loc, -1);
5577 }
5578 #endif
5579 
5580 #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5581 	&& (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
5582 /*
5583  * ":language":  Set the language (locale).
5584  */
5585     void
5586 ex_language(eap)
5587     exarg_T	*eap;
5588 {
5589     char	*loc;
5590     char_u	*p;
5591     char_u	*name;
5592     int		what = LC_ALL;
5593     char	*whatstr = "";
5594 #ifdef LC_MESSAGES
5595 # define VIM_LC_MESSAGES LC_MESSAGES
5596 #else
5597 # define VIM_LC_MESSAGES 6789
5598 #endif
5599 
5600     name = eap->arg;
5601 
5602     /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
5603      * Allow abbreviation, but require at least 3 characters to avoid
5604      * confusion with a two letter language name "me" or "ct". */
5605     p = skiptowhite(eap->arg);
5606     if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
5607     {
5608 	if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
5609 	{
5610 	    what = VIM_LC_MESSAGES;
5611 	    name = skipwhite(p);
5612 	    whatstr = "messages ";
5613 	}
5614 	else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
5615 	{
5616 	    what = LC_CTYPE;
5617 	    name = skipwhite(p);
5618 	    whatstr = "ctype ";
5619 	}
5620 	else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
5621 	{
5622 	    what = LC_TIME;
5623 	    name = skipwhite(p);
5624 	    whatstr = "time ";
5625 	}
5626     }
5627 
5628     if (*name == NUL)
5629     {
5630 #ifndef LC_MESSAGES
5631 	if (what == VIM_LC_MESSAGES)
5632 	    p = get_mess_env();
5633 	else
5634 #endif
5635 	    p = (char_u *)setlocale(what, NULL);
5636 	if (p == NULL || *p == NUL)
5637 	    p = (char_u *)"Unknown";
5638 	smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
5639     }
5640     else
5641     {
5642 #ifndef LC_MESSAGES
5643 	if (what == VIM_LC_MESSAGES)
5644 	    loc = "";
5645 	else
5646 #endif
5647 	    loc = setlocale(what, (char *)name);
5648 	if (loc == NULL)
5649 	    EMSG2(_("E197: Cannot set language to \"%s\""), name);
5650 	else
5651 	{
5652 #ifdef HAVE_NL_MSG_CAT_CNTR
5653 	    /* Need to do this for GNU gettext, otherwise cached translations
5654 	     * will be used again. */
5655 	    extern int _nl_msg_cat_cntr;
5656 
5657 	    ++_nl_msg_cat_cntr;
5658 #endif
5659 	    /* Reset $LC_ALL, otherwise it would overrule everyting. */
5660 	    vim_setenv((char_u *)"LC_ALL", (char_u *)"");
5661 
5662 	    if (what != LC_TIME)
5663 	    {
5664 		/* Tell gettext() what to translate to.  It apparently doesn't
5665 		 * use the currently effective locale.  Also do this when
5666 		 * FEAT_GETTEXT isn't defined, so that shell commands use this
5667 		 * value. */
5668 		if (what == LC_ALL)
5669 		    vim_setenv((char_u *)"LANG", name);
5670 		if (what != LC_CTYPE)
5671 		{
5672 		    char_u	*mname;
5673 #ifdef WIN32
5674 		    mname = gettext_lang(name);
5675 #else
5676 		    mname = name;
5677 #endif
5678 		    vim_setenv((char_u *)"LC_MESSAGES", mname);
5679 #ifdef FEAT_MULTI_LANG
5680 		    set_helplang_default(mname);
5681 #endif
5682 		}
5683 
5684 		/* Set $LC_CTYPE, because it overrules $LANG, and
5685 		 * gtk_set_locale() calls setlocale() again.  gnome_init()
5686 		 * sets $LC_CTYPE to "en_US" (that's a bug!). */
5687 		if (what != VIM_LC_MESSAGES)
5688 		    vim_setenv((char_u *)"LC_CTYPE", name);
5689 # ifdef FEAT_GUI_GTK
5690 		/* Let GTK know what locale we're using.  Not sure this is
5691 		 * really needed... */
5692 		if (gui.in_use)
5693 		    (void)gtk_set_locale();
5694 # endif
5695 	    }
5696 
5697 # ifdef FEAT_EVAL
5698 	    /* Set v:lang, v:lc_time and v:ctype to the final result. */
5699 	    set_lang_var();
5700 # endif
5701 	}
5702     }
5703 }
5704 
5705 # if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5706 /*
5707  * Function given to ExpandGeneric() to obtain the possible arguments of the
5708  * ":language" command.
5709  */
5710 /*ARGSUSED*/
5711     char_u *
5712 get_lang_arg(xp, idx)
5713     expand_T	*xp;
5714     int		idx;
5715 {
5716     if (idx == 0)
5717 	return (char_u *)"messages";
5718     if (idx == 1)
5719 	return (char_u *)"ctype";
5720     if (idx == 2)
5721 	return (char_u *)"time";
5722     return NULL;
5723 }
5724 # endif
5725 
5726 #endif
5727