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