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