blob: 6c8ece95b1739a05f4cb947143dfc180568ac15c [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 Moolenaar843ee412004-06-30 16:16:41 +0000450 if (*p != '*')
451 {
452 bp->dbg_name = fix_fname(p);
453 vim_free(p);
454 }
455 else
456 bp->dbg_name = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457#ifdef MACOS_CLASSIC
458 if (bp->dbg_name != NULL)
459 slash_n_colon_adjust(bp->dbg_name);
460#endif
461 }
462
463 if (bp->dbg_name == NULL)
464 return FAIL;
465 return OK;
466}
467
468/*
469 * ":breakadd".
470 */
471 void
472ex_breakadd(eap)
473 exarg_T *eap;
474{
475 struct debuggy *bp;
476 char_u *pat;
477
478 if (dbg_parsearg(eap->arg) == OK)
479 {
480 bp = &BREAKP(dbg_breakp.ga_len);
481 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
482 if (pat != NULL)
483 {
484 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
485 vim_free(pat);
486 }
487 if (pat == NULL || bp->dbg_prog == NULL)
488 vim_free(bp->dbg_name);
489 else
490 {
491 if (bp->dbg_lnum == 0) /* default line number is 1 */
492 bp->dbg_lnum = 1;
493 BREAKP(dbg_breakp.ga_len++).dbg_nr = ++last_breakp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 ++debug_tick;
495 }
496 }
497}
498
499/*
500 * ":debuggreedy".
501 */
502 void
503ex_debuggreedy(eap)
504 exarg_T *eap;
505{
506 if (eap->addr_count == 0 || eap->line2 != 0)
507 debug_greedy = TRUE;
508 else
509 debug_greedy = FALSE;
510}
511
512/*
513 * ":breakdel".
514 */
515 void
516ex_breakdel(eap)
517 exarg_T *eap;
518{
519 struct debuggy *bp, *bpi;
520 int nr;
521 int todel = -1;
522 int i;
523 linenr_T best_lnum = 0;
524
525 if (vim_isdigit(*eap->arg))
526 {
527 /* ":breakdel {nr}" */
528 nr = atol((char *)eap->arg);
529 for (i = 0; i < dbg_breakp.ga_len; ++i)
530 if (BREAKP(i).dbg_nr == nr)
531 {
532 todel = i;
533 break;
534 }
535 }
536 else
537 {
538 /* ":breakdel {func|file} [lnum] {name}" */
539 if (dbg_parsearg(eap->arg) == FAIL)
540 return;
541 bp = &BREAKP(dbg_breakp.ga_len);
542 for (i = 0; i < dbg_breakp.ga_len; ++i)
543 {
544 bpi = &BREAKP(i);
545 if (bp->dbg_type == bpi->dbg_type
546 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
547 && (bp->dbg_lnum == bpi->dbg_lnum
548 || (bp->dbg_lnum == 0
549 && (best_lnum == 0
550 || bpi->dbg_lnum < best_lnum))))
551 {
552 todel = i;
553 best_lnum = bpi->dbg_lnum;
554 }
555 }
556 vim_free(bp->dbg_name);
557 }
558
559 if (todel < 0)
560 EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
561 else
562 {
563 vim_free(BREAKP(todel).dbg_name);
564 vim_free(BREAKP(todel).dbg_prog);
565 --dbg_breakp.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 if (todel < dbg_breakp.ga_len)
567 mch_memmove(&BREAKP(todel), &BREAKP(todel + 1),
568 (dbg_breakp.ga_len - todel) * sizeof(struct debuggy));
569 ++debug_tick;
570 }
571}
572
573/*
574 * ":breaklist".
575 */
576/*ARGSUSED*/
577 void
578ex_breaklist(eap)
579 exarg_T *eap;
580{
581 struct debuggy *bp;
582 int i;
583
584 if (dbg_breakp.ga_len == 0)
585 MSG(_("No breakpoints defined"));
586 else
587 for (i = 0; i < dbg_breakp.ga_len; ++i)
588 {
589 bp = &BREAKP(i);
590 smsg((char_u *)_("%3d %s %s line %ld"),
591 bp->dbg_nr,
592 bp->dbg_type == DBG_FUNC ? "func" : "file",
593 bp->dbg_name,
594 (long)bp->dbg_lnum);
595 }
596}
597
598/*
599 * Find a breakpoint for a function or sourced file.
600 * Returns line number at which to break; zero when no matching breakpoint.
601 */
602 linenr_T
603dbg_find_breakpoint(file, fname, after)
604 int file; /* TRUE for a file, FALSE for a function */
605 char_u *fname; /* file or function name */
606 linenr_T after; /* after this line number */
607{
608 struct debuggy *bp;
609 int i;
610 linenr_T lnum = 0;
611 regmatch_T regmatch;
612 char_u *name = fname;
613 int prev_got_int;
614
615 /* Replace K_SNR in function name with "<SNR>". */
616 if (!file && fname[0] == K_SPECIAL)
617 {
618 name = alloc((unsigned)STRLEN(fname) + 3);
619 if (name == NULL)
620 name = fname;
621 else
622 {
623 STRCPY(name, "<SNR>");
624 STRCPY(name + 5, fname + 3);
625 }
626 }
627
628 for (i = 0; i < dbg_breakp.ga_len; ++i)
629 {
630 /* skip entries that are not useful or are for a line that is beyond
631 * an already found breakpoint */
632 bp = &BREAKP(i);
633 if ((bp->dbg_type == DBG_FILE) == file
634 && bp->dbg_lnum > after
635 && (lnum == 0 || bp->dbg_lnum < lnum))
636 {
637 regmatch.regprog = bp->dbg_prog;
638 regmatch.rm_ic = FALSE;
639 /*
640 * Save the value of got_int and reset it. We don't want a previous
641 * interruption cancel matching, only hitting CTRL-C while matching
642 * should abort it.
643 */
644 prev_got_int = got_int;
645 got_int = FALSE;
646 if (vim_regexec(&regmatch, name, (colnr_T)0))
647 lnum = bp->dbg_lnum;
648 got_int |= prev_got_int;
649 }
650 }
651 if (name != fname)
652 vim_free(name);
653
654 return lnum;
655}
656
657/*
658 * Called when a breakpoint was encountered.
659 */
660 void
661dbg_breakpoint(name, lnum)
662 char_u *name;
663 linenr_T lnum;
664{
665 /* We need to check if this line is actually executed in do_one_cmd() */
666 debug_breakpoint_name = name;
667 debug_breakpoint_lnum = lnum;
668}
669#endif
670
671/*
672 * If 'autowrite' option set, try to write the file.
673 * Careful: autocommands may make "buf" invalid!
674 *
675 * return FAIL for failure, OK otherwise
676 */
677 int
678autowrite(buf, forceit)
679 buf_T *buf;
680 int forceit;
681{
682 if (!(p_aw || p_awa) || !p_write
683#ifdef FEAT_QUICKFIX
684 /* never autowrite a "nofile" or "nowrite" buffer */
685 || bt_dontwrite(buf)
686#endif
687 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
688 return FAIL;
689 return buf_write_all(buf, forceit);
690}
691
692/*
693 * flush all buffers, except the ones that are readonly
694 */
695 void
696autowrite_all()
697{
698 buf_T *buf;
699
700 if (!(p_aw || p_awa) || !p_write)
701 return;
702 for (buf = firstbuf; buf; buf = buf->b_next)
703 if (bufIsChanged(buf) && !buf->b_p_ro)
704 {
705 (void)buf_write_all(buf, FALSE);
706#ifdef FEAT_AUTOCMD
707 /* an autocommand may have deleted the buffer */
708 if (!buf_valid(buf))
709 buf = firstbuf;
710#endif
711 }
712}
713
714/*
715 * return TRUE if buffer was changed and cannot be abandoned.
716 */
717/*ARGSUSED*/
718 int
719check_changed(buf, checkaw, mult_win, forceit, allbuf)
720 buf_T *buf;
721 int checkaw; /* do autowrite if buffer was changed */
722 int mult_win; /* check also when several wins for the buf */
723 int forceit;
724 int allbuf; /* may write all buffers */
725{
726 if ( !forceit
727 && bufIsChanged(buf)
728 && (mult_win || buf->b_nwindows <= 1)
729 && (!checkaw || autowrite(buf, forceit) == FAIL))
730 {
731#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
732 if ((p_confirm || cmdmod.confirm) && p_write)
733 {
734 buf_T *buf2;
735 int count = 0;
736
737 if (allbuf)
738 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
739 if (bufIsChanged(buf2)
740 && (buf2->b_ffname != NULL
741# ifdef FEAT_BROWSE
742 || cmdmod.browse
743# endif
744 ))
745 ++count;
746# ifdef FEAT_AUTOCMD
747 if (!buf_valid(buf))
748 /* Autocommand deleted buffer, oops! It's not changed now. */
749 return FALSE;
750# endif
751 dialog_changed(buf, count > 1);
752# ifdef FEAT_AUTOCMD
753 if (!buf_valid(buf))
754 /* Autocommand deleted buffer, oops! It's not changed now. */
755 return FALSE;
756# endif
757 return bufIsChanged(buf);
758 }
759#endif
760 EMSG(_(e_nowrtmsg));
761 return TRUE;
762 }
763 return FALSE;
764}
765
766#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
767
768#if defined(FEAT_BROWSE) || defined(PROTO)
769/*
770 * When wanting to write a file without a file name, ask the user for a name.
771 */
772 void
773browse_save_fname(buf)
774 buf_T *buf;
775{
776 if (buf->b_fname == NULL)
777 {
778 char_u *fname;
779
Bram Moolenaar7b0294c2004-10-11 10:16:09 +0000780 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
781 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 if (fname != NULL)
783 {
784 if (setfname(buf, fname, NULL, TRUE) == OK)
785 buf->b_flags |= BF_NOTEDITED;
786 vim_free(fname);
787 }
788 }
789}
790#endif
791
792/*
793 * Ask the user what to do when abondoning a changed buffer.
794 * Must check 'write' option first!
795 */
796 void
797dialog_changed(buf, checkall)
798 buf_T *buf;
799 int checkall; /* may abandon all changed buffers */
800{
801 char_u buff[IOSIZE];
802 int ret;
803 buf_T *buf2;
804
805 dialog_msg(buff, _("Save changes to \"%.*s\"?"),
806 (buf->b_fname != NULL) ?
807 buf->b_fname : (char_u *)_("Untitled"));
808 if (checkall)
809 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
810 else
811 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
812
813 if (ret == VIM_YES)
814 {
815#ifdef FEAT_BROWSE
816 /* May get file name, when there is none */
817 browse_save_fname(buf);
818#endif
819 if (buf->b_fname != NULL) /* didn't hit Cancel */
820 (void)buf_write_all(buf, FALSE);
821 }
822 else if (ret == VIM_NO)
823 {
824 unchanged(buf, TRUE);
825 }
826 else if (ret == VIM_ALL)
827 {
828 /*
829 * Write all modified files that can be written.
830 * Skip readonly buffers, these need to be confirmed
831 * individually.
832 */
833 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
834 {
835 if (bufIsChanged(buf2)
836 && (buf2->b_ffname != NULL
837#ifdef FEAT_BROWSE
838 || cmdmod.browse
839#endif
840 )
841 && !buf2->b_p_ro)
842 {
843#ifdef FEAT_BROWSE
844 /* May get file name, when there is none */
845 browse_save_fname(buf2);
846#endif
847 if (buf2->b_fname != NULL) /* didn't hit Cancel */
848 (void)buf_write_all(buf2, FALSE);
849#ifdef FEAT_AUTOCMD
850 /* an autocommand may have deleted the buffer */
851 if (!buf_valid(buf2))
852 buf2 = firstbuf;
853#endif
854 }
855 }
856 }
857 else if (ret == VIM_DISCARDALL)
858 {
859 /*
860 * mark all buffers as unchanged
861 */
862 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
863 unchanged(buf2, TRUE);
864 }
865}
866#endif
867
868/*
869 * Return TRUE if the buffer "buf" can be abandoned, either by making it
870 * hidden, autowriting it or unloading it.
871 */
872 int
873can_abandon(buf, forceit)
874 buf_T *buf;
875 int forceit;
876{
877 return ( P_HID(buf)
878 || !bufIsChanged(buf)
879 || buf->b_nwindows > 1
880 || autowrite(buf, forceit) == OK
881 || forceit);
882}
883
884/*
885 * Return TRUE if any buffer was changed and cannot be abandoned.
886 * That changed buffer becomes the current buffer.
887 */
888 int
889check_changed_any(hidden)
890 int hidden; /* Only check hidden buffers */
891{
892 buf_T *buf;
893 int save;
894#ifdef FEAT_WINDOWS
895 win_T *wp;
896#endif
897
898 for (;;)
899 {
900 /* check curbuf first: if it was changed we can't abandon it */
901 if (!hidden && curbufIsChanged())
902 buf = curbuf;
903 else
904 {
905 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
906 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
907 break;
908 }
909 if (buf == NULL) /* No buffers changed */
910 return FALSE;
911
912 if (check_changed(buf, p_awa, TRUE, FALSE, TRUE) && buf_valid(buf))
913 break; /* didn't save - still changes */
914 }
915
916 exiting = FALSE;
917#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
918 /*
919 * When ":confirm" used, don't give an error message.
920 */
921 if (!(p_confirm || cmdmod.confirm))
922#endif
923 {
924 /* There must be a wait_return for this message, do_buffer()
925 * may cause a redraw. But wait_return() is a no-op when vgetc()
926 * is busy (Quit used from window menu), then make sure we don't
927 * cause a scroll up. */
928 if (vgetc_busy)
929 {
930 msg_row = cmdline_row;
931 msg_col = 0;
932 msg_didout = FALSE;
933 }
934 if (EMSG2(_("E162: No write since last change for buffer \"%s\""),
935 buf_spname(buf) != NULL ? (char_u *)buf_spname(buf) :
936 buf->b_fname))
937 {
938 save = no_wait_return;
939 no_wait_return = FALSE;
940 wait_return(FALSE);
941 no_wait_return = save;
942 }
943 }
944
945#ifdef FEAT_WINDOWS
946 /* Try to find a window that contains the buffer. */
947 if (buf != curbuf)
948 for (wp = firstwin; wp != NULL; wp = wp->w_next)
949 if (wp->w_buffer == buf)
950 {
951 win_goto(wp);
952# ifdef FEAT_AUTOCMD
953 /* Paranoia: did autocms wipe out the buffer with changes? */
954 if (!buf_valid(buf))
955 return TRUE;
956# endif
957 break;
958 }
959#endif
960
961 /* Open the changed buffer in the current window. */
962 if (buf != curbuf)
963 set_curbuf(buf, DOBUF_GOTO);
964
965 return TRUE;
966}
967
968/*
969 * return FAIL if there is no file name, OK if there is one
970 * give error message for FAIL
971 */
972 int
973check_fname()
974{
975 if (curbuf->b_ffname == NULL)
976 {
977 EMSG(_(e_noname));
978 return FAIL;
979 }
980 return OK;
981}
982
983/*
984 * flush the contents of a buffer, unless it has no file name
985 *
986 * return FAIL for failure, OK otherwise
987 */
988 int
989buf_write_all(buf, forceit)
990 buf_T *buf;
991 int forceit;
992{
993 int retval;
994#ifdef FEAT_AUTOCMD
995 buf_T *old_curbuf = curbuf;
996#endif
997
998 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
999 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
1000 FALSE, forceit, TRUE, FALSE));
1001#ifdef FEAT_AUTOCMD
1002 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001003 {
1004 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001006 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001007#endif
1008 return retval;
1009}
1010
1011/*
1012 * Code to handle the argument list.
1013 */
1014
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001015static char_u *do_one_arg __ARGS((char_u *str));
1016static int do_arglist __ARGS((char_u *str, int what, int after));
1017static void alist_check_arg_idx __ARGS((void));
1018static int editing_arg_idx __ARGS((win_T *win));
1019#ifdef FEAT_LISTCMDS
1020static int alist_add_list __ARGS((int count, char_u **files, int after));
1021#endif
1022#define AL_SET 1
1023#define AL_ADD 2
1024#define AL_DEL 3
1025
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001027 * Isolate one argument, taking backticks.
1028 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 * Return a pointer to the start of the next argument.
1030 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001031 static char_u *
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032do_one_arg(str)
1033 char_u *str;
1034{
1035 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 int inbacktick;
1037
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038 inbacktick = FALSE;
1039 for (p = str; *str; ++str)
1040 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001041 /* When the backslash is used for escaping the special meaning of a
1042 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001043 if (rem_backslash(str))
1044 {
1045 *p++ = *str++;
1046 *p++ = *str;
1047 }
1048 else
1049 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001050 /* An item ends at a space not in backticks */
1051 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001053 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001055 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001056 }
1057 }
1058 str = skipwhite(str);
1059 *p = NUL;
1060
1061 return str;
1062}
1063
Bram Moolenaar86b68352004-12-27 21:59:20 +00001064/*
1065 * Separate the arguments in "str" and return a list of pointers in the
1066 * growarray "gap".
1067 */
1068 int
1069get_arglist(gap, str)
1070 garray_T *gap;
1071 char_u *str;
1072{
1073 ga_init2(gap, (int)sizeof(char_u *), 20);
1074 while (*str != NUL)
1075 {
1076 if (ga_grow(gap, 1) == FAIL)
1077 {
1078 ga_clear(gap);
1079 return FAIL;
1080 }
1081 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
1082
1083 /* Isolate one argument, change it in-place, put a NUL after it. */
1084 str = do_one_arg(str);
1085 }
1086 return OK;
1087}
1088
Bram Moolenaar071d4272004-06-13 20:20:40 +00001089#if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
1090/*
1091 * Redefine the argument list.
1092 */
1093 void
1094set_arglist(str)
1095 char_u *str;
1096{
1097 do_arglist(str, AL_SET, 0);
1098}
1099#endif
1100
1101/*
1102 * "what" == AL_SET: Redefine the argument list to 'str'.
1103 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
1104 * "what" == AL_DEL: remove files in 'str' from the argument list.
1105 *
1106 * Return FAIL for failure, OK otherwise.
1107 */
1108/*ARGSUSED*/
1109 static int
1110do_arglist(str, what, after)
1111 char_u *str;
1112 int what;
1113 int after; /* 0 means before first one */
1114{
1115 garray_T new_ga;
1116 int exp_count;
1117 char_u **exp_files;
1118 int i;
1119#ifdef FEAT_LISTCMDS
1120 char_u *p;
1121 int match;
1122#endif
1123
1124 /*
1125 * Collect all file name arguments in "new_ga".
1126 */
Bram Moolenaar86b68352004-12-27 21:59:20 +00001127 if (get_arglist(&new_ga, str) == FAIL)
1128 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129
1130#ifdef FEAT_LISTCMDS
1131 if (what == AL_DEL)
1132 {
1133 regmatch_T regmatch;
1134 int didone;
1135
1136 /*
1137 * Delete the items: use each item as a regexp and find a match in the
1138 * argument list.
1139 */
1140#ifdef CASE_INSENSITIVE_FILENAME
1141 regmatch.rm_ic = TRUE; /* Always ignore case */
1142#else
1143 regmatch.rm_ic = FALSE; /* Never ignore case */
1144#endif
1145 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
1146 {
1147 p = ((char_u **)new_ga.ga_data)[i];
1148 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
1149 if (p == NULL)
1150 break;
1151 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1152 if (regmatch.regprog == NULL)
1153 {
1154 vim_free(p);
1155 break;
1156 }
1157
1158 didone = FALSE;
1159 for (match = 0; match < ARGCOUNT; ++match)
1160 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1161 (colnr_T)0))
1162 {
1163 didone = TRUE;
1164 vim_free(ARGLIST[match].ae_fname);
1165 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
1166 (ARGCOUNT - match - 1) * sizeof(aentry_T));
1167 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 if (curwin->w_arg_idx > match)
1169 --curwin->w_arg_idx;
1170 --match;
1171 }
1172
1173 vim_free(regmatch.regprog);
1174 vim_free(p);
1175 if (!didone)
1176 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
1177 }
1178 ga_clear(&new_ga);
1179 }
1180 else
1181#endif
1182 {
1183 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1184 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1185 ga_clear(&new_ga);
1186 if (i == FAIL)
1187 return FAIL;
1188 if (exp_count == 0)
1189 {
1190 EMSG(_(e_nomatch));
1191 return FAIL;
1192 }
1193
1194#ifdef FEAT_LISTCMDS
1195 if (what == AL_ADD)
1196 {
1197 (void)alist_add_list(exp_count, exp_files, after);
1198 vim_free(exp_files);
1199 }
1200 else /* what == AL_SET */
1201#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00001202 alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001203 }
1204
1205 alist_check_arg_idx();
1206
1207 return OK;
1208}
1209
1210/*
1211 * Check the validity of the arg_idx for each other window.
1212 */
1213 static void
1214alist_check_arg_idx()
1215{
1216#ifdef FEAT_WINDOWS
1217 win_T *win;
1218
1219 for (win = firstwin; win != NULL; win = win->w_next)
1220 if (win->w_alist == curwin->w_alist)
1221 check_arg_idx(win);
1222#else
1223 check_arg_idx(curwin);
1224#endif
1225}
1226
1227/*
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001228 * Return TRUE if window "win" is editing then file at the current argument
1229 * index.
1230 */
1231 static int
1232editing_arg_idx(win)
1233 win_T *win;
1234{
1235 return !(win->w_arg_idx >= WARGCOUNT(win)
1236 || (win->w_buffer->b_fnum
1237 != WARGLIST(win)[win->w_arg_idx].ae_fnum
1238 && (win->w_buffer->b_ffname == NULL
1239 || !(fullpathcmp(
1240 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1241 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
1242}
1243
1244/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 * Check if window "win" is editing the w_arg_idx file in its argument list.
1246 */
1247 void
1248check_arg_idx(win)
1249 win_T *win;
1250{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001251 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 {
1253 /* We are not editing the current entry in the argument list.
1254 * Set "arg_had_last" if we are editing the last one. */
1255 win->w_arg_idx_invalid = TRUE;
1256 if (win->w_arg_idx != WARGCOUNT(win) - 1
1257 && arg_had_last == FALSE
1258#ifdef FEAT_WINDOWS
1259 && ALIST(win) == &global_alist
1260#endif
1261 && GARGCOUNT > 0
1262 && win->w_arg_idx < GARGCOUNT
1263 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1264 || (win->w_buffer->b_ffname != NULL
1265 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
1266 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
1267 arg_had_last = TRUE;
1268 }
1269 else
1270 {
1271 /* We are editing the current entry in the argument list.
1272 * Set "arg_had_last" if it's also the last one */
1273 win->w_arg_idx_invalid = FALSE;
1274 if (win->w_arg_idx == WARGCOUNT(win) - 1
1275#ifdef FEAT_WINDOWS
1276 && win->w_alist == &global_alist
1277#endif
1278 )
1279 arg_had_last = TRUE;
1280 }
1281}
1282
1283/*
1284 * ":args", ":argslocal" and ":argsglobal".
1285 */
1286 void
1287ex_args(eap)
1288 exarg_T *eap;
1289{
1290 int i;
1291
1292 if (eap->cmdidx != CMD_args)
1293 {
1294#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1295 alist_unlink(ALIST(curwin));
1296 if (eap->cmdidx == CMD_argglobal)
1297 ALIST(curwin) = &global_alist;
1298 else /* eap->cmdidx == CMD_arglocal */
1299 alist_new();
1300#else
1301 ex_ni(eap);
1302 return;
1303#endif
1304 }
1305
1306 if (!ends_excmd(*eap->arg))
1307 {
1308 /*
1309 * ":args file ..": define new argument list, handle like ":next"
1310 * Also for ":argslocal file .." and ":argsglobal file ..".
1311 */
1312 ex_next(eap);
1313 }
1314 else
1315#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1316 if (eap->cmdidx == CMD_args)
1317#endif
1318 {
1319 /*
1320 * ":args": list arguments.
1321 */
1322 if (ARGCOUNT > 0)
1323 {
1324 /* Overwrite the command, for a short list there is no scrolling
1325 * required and no wait_return(). */
1326 gotocmdline(TRUE);
1327 for (i = 0; i < ARGCOUNT; ++i)
1328 {
1329 if (i == curwin->w_arg_idx)
1330 msg_putchar('[');
1331 msg_outtrans(alist_name(&ARGLIST[i]));
1332 if (i == curwin->w_arg_idx)
1333 msg_putchar(']');
1334 msg_putchar(' ');
1335 }
1336 }
1337 }
1338#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1339 else if (eap->cmdidx == CMD_arglocal)
1340 {
1341 garray_T *gap = &curwin->w_alist->al_ga;
1342
1343 /*
1344 * ":argslocal": make a local copy of the global argument list.
1345 */
1346 if (ga_grow(gap, GARGCOUNT) == OK)
1347 for (i = 0; i < GARGCOUNT; ++i)
1348 if (GARGLIST[i].ae_fname != NULL)
1349 {
1350 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1351 vim_strsave(GARGLIST[i].ae_fname);
1352 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1353 GARGLIST[i].ae_fnum;
1354 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 }
1356 }
1357#endif
1358}
1359
1360/*
1361 * ":previous", ":sprevious", ":Next" and ":sNext".
1362 */
1363 void
1364ex_previous(eap)
1365 exarg_T *eap;
1366{
1367 /* If past the last one already, go to the last one. */
1368 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
1369 do_argfile(eap, ARGCOUNT - 1);
1370 else
1371 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1372}
1373
1374/*
1375 * ":rewind", ":first", ":sfirst" and ":srewind".
1376 */
1377 void
1378ex_rewind(eap)
1379 exarg_T *eap;
1380{
1381 do_argfile(eap, 0);
1382}
1383
1384/*
1385 * ":last" and ":slast".
1386 */
1387 void
1388ex_last(eap)
1389 exarg_T *eap;
1390{
1391 do_argfile(eap, ARGCOUNT - 1);
1392}
1393
1394/*
1395 * ":argument" and ":sargument".
1396 */
1397 void
1398ex_argument(eap)
1399 exarg_T *eap;
1400{
1401 int i;
1402
1403 if (eap->addr_count > 0)
1404 i = eap->line2 - 1;
1405 else
1406 i = curwin->w_arg_idx;
1407 do_argfile(eap, i);
1408}
1409
1410/*
1411 * Edit file "argn" of the argument lists.
1412 */
1413 void
1414do_argfile(eap, argn)
1415 exarg_T *eap;
1416 int argn;
1417{
1418 int other;
1419 char_u *p;
1420
1421 if (argn < 0 || argn >= ARGCOUNT)
1422 {
1423 if (ARGCOUNT <= 1)
1424 EMSG(_("E163: There is only one file to edit"));
1425 else if (argn < 0)
1426 EMSG(_("E164: Cannot go before first file"));
1427 else
1428 EMSG(_("E165: Cannot go beyond last file"));
1429 }
1430 else
1431 {
1432 setpcmark();
1433#ifdef FEAT_GUI
1434 need_mouse_correct = TRUE;
1435#endif
1436
1437#ifdef FEAT_WINDOWS
1438 if (*eap->cmd == 's') /* split window first */
1439 {
1440 if (win_split(0, 0) == FAIL)
1441 return;
1442# ifdef FEAT_SCROLLBIND
1443 curwin->w_p_scb = FALSE;
1444# endif
1445 }
1446 else
1447#endif
1448 {
1449 /*
1450 * if 'hidden' set, only check for changed file when re-editing
1451 * the same buffer
1452 */
1453 other = TRUE;
1454 if (P_HID(curbuf))
1455 {
1456 p = fix_fname(alist_name(&ARGLIST[argn]));
1457 other = otherfile(p);
1458 vim_free(p);
1459 }
1460 if ((!P_HID(curbuf) || !other)
1461 && check_changed(curbuf, TRUE, !other, eap->forceit, FALSE))
1462 return;
1463 }
1464
1465 curwin->w_arg_idx = argn;
1466 if (argn == ARGCOUNT - 1
1467#ifdef FEAT_WINDOWS
1468 && curwin->w_alist == &global_alist
1469#endif
1470 )
1471 arg_had_last = TRUE;
1472
1473 /* Edit the file; always use the last known line number. */
1474 (void)do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
1475 eap, ECMD_LAST,
1476 (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) +
1477 (eap->forceit ? ECMD_FORCEIT : 0));
1478
1479 /* like Vi: set the mark where the cursor is in the file. */
1480 if (eap->cmdidx != CMD_argdo)
1481 setmark('\'');
1482 }
1483}
1484
1485/*
1486 * ":next", and commands that behave like it.
1487 */
1488 void
1489ex_next(eap)
1490 exarg_T *eap;
1491{
1492 int i;
1493
1494 /*
1495 * check for changed buffer now, if this fails the argument list is not
1496 * redefined.
1497 */
1498 if ( P_HID(curbuf)
1499 || eap->cmdidx == CMD_snext
1500 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
1501 {
1502 if (*eap->arg != NUL) /* redefine file list */
1503 {
1504 if (do_arglist(eap->arg, AL_SET, 0) == FAIL)
1505 return;
1506 i = 0;
1507 }
1508 else
1509 i = curwin->w_arg_idx + (int)eap->line2;
1510 do_argfile(eap, i);
1511 }
1512}
1513
1514#ifdef FEAT_LISTCMDS
1515/*
1516 * ":argedit"
1517 */
1518 void
1519ex_argedit(eap)
1520 exarg_T *eap;
1521{
1522 int fnum;
1523 int i;
1524 char_u *s;
1525
1526 /* Add the argument to the buffer list and get the buffer number. */
1527 fnum = buflist_add(eap->arg, BLN_LISTED);
1528
1529 /* Check if this argument is already in the argument list. */
1530 for (i = 0; i < ARGCOUNT; ++i)
1531 if (ARGLIST[i].ae_fnum == fnum)
1532 break;
1533 if (i == ARGCOUNT)
1534 {
1535 /* Can't find it, add it to the argument list. */
1536 s = vim_strsave(eap->arg);
1537 if (s == NULL)
1538 return;
1539 i = alist_add_list(1, &s,
1540 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1541 if (i < 0)
1542 return;
1543 curwin->w_arg_idx = i;
1544 }
1545
1546 alist_check_arg_idx();
1547
1548 /* Edit the argument. */
1549 do_argfile(eap, i);
1550}
1551
1552/*
1553 * ":argadd"
1554 */
1555 void
1556ex_argadd(eap)
1557 exarg_T *eap;
1558{
1559 do_arglist(eap->arg, AL_ADD,
1560 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1561#ifdef FEAT_TITLE
1562 maketitle();
1563#endif
1564}
1565
1566/*
1567 * ":argdelete"
1568 */
1569 void
1570ex_argdelete(eap)
1571 exarg_T *eap;
1572{
1573 int i;
1574 int n;
1575
1576 if (eap->addr_count > 0)
1577 {
1578 /* ":1,4argdel": Delete all arguments in the range. */
1579 if (eap->line2 > ARGCOUNT)
1580 eap->line2 = ARGCOUNT;
1581 n = eap->line2 - eap->line1 + 1;
1582 if (*eap->arg != NUL || n <= 0)
1583 EMSG(_(e_invarg));
1584 else
1585 {
1586 for (i = eap->line1; i <= eap->line2; ++i)
1587 vim_free(ARGLIST[i - 1].ae_fname);
1588 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
1589 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
1590 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 if (curwin->w_arg_idx >= eap->line2)
1592 curwin->w_arg_idx -= n;
1593 else if (curwin->w_arg_idx > eap->line1)
1594 curwin->w_arg_idx = eap->line1;
1595 }
1596 }
1597 else if (*eap->arg == NUL)
1598 EMSG(_(e_argreq));
1599 else
1600 do_arglist(eap->arg, AL_DEL, 0);
1601#ifdef FEAT_TITLE
1602 maketitle();
1603#endif
1604}
1605
1606/*
1607 * ":argdo", ":windo", ":bufdo"
1608 */
1609 void
1610ex_listdo(eap)
1611 exarg_T *eap;
1612{
1613 int i;
1614#ifdef FEAT_WINDOWS
1615 win_T *win;
1616#endif
1617 buf_T *buf;
1618 int next_fnum = 0;
1619#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
1620 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001622 char_u *p_shm_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623
1624#ifndef FEAT_WINDOWS
1625 if (eap->cmdidx == CMD_windo)
1626 {
1627 ex_ni(eap);
1628 return;
1629 }
1630#endif
1631
1632#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
1633 if (eap->cmdidx != CMD_windo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00001634 /* Don't do syntax HL autocommands. Skipping the syntax file is a
1635 * great speed improvement. */
1636 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001637#endif
1638
1639 if (eap->cmdidx == CMD_windo
1640 || P_HID(curbuf)
1641 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
1642 {
1643 /* start at the first argument/window/buffer */
1644 i = 0;
1645#ifdef FEAT_WINDOWS
1646 win = firstwin;
1647#endif
1648 /* set pcmark now */
1649 if (eap->cmdidx == CMD_bufdo)
1650 goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
1651 else
1652 setpcmark();
1653 listcmd_busy = TRUE; /* avoids setting pcmark below */
1654
1655 while (!got_int)
1656 {
1657 if (eap->cmdidx == CMD_argdo)
1658 {
1659 /* go to argument "i" */
1660 if (i == ARGCOUNT)
1661 break;
1662 /* Don't call do_argfile() when already there, it will try
1663 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001664 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001665 {
1666 /* Clear 'shm' to avoid that the file message overwrites
1667 * any output from the command. */
1668 p_shm_save = vim_strsave(p_shm);
1669 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001671 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
1672 vim_free(p_shm_save);
1673 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674 if (curwin->w_arg_idx != i)
1675 break;
1676 ++i;
1677 }
1678#ifdef FEAT_WINDOWS
1679 else if (eap->cmdidx == CMD_windo)
1680 {
1681 /* go to window "win" */
1682 if (!win_valid(win))
1683 break;
1684 win_goto(win);
1685 win = win->w_next;
1686 }
1687#endif
1688 else if (eap->cmdidx == CMD_bufdo)
1689 {
1690 /* Remember the number of the next listed buffer, in case
1691 * ":bwipe" is used or autocommands do something strange. */
1692 next_fnum = -1;
1693 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
1694 if (buf->b_p_bl)
1695 {
1696 next_fnum = buf->b_fnum;
1697 break;
1698 }
1699 }
1700
1701 /* execute the command */
1702 do_cmdline(eap->arg, eap->getline, eap->cookie,
1703 DOCMD_VERBOSE + DOCMD_NOWAIT);
1704
1705 if (eap->cmdidx == CMD_bufdo)
1706 {
1707 /* Done? */
1708 if (next_fnum < 0)
1709 break;
1710 /* Check if the buffer still exists. */
1711 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1712 if (buf->b_fnum == next_fnum)
1713 break;
1714 if (buf == NULL)
1715 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001716
1717 /* Go to the next buffer. Clear 'shm' to avoid that the file
1718 * message overwrites any output from the command. */
1719 p_shm_save = vim_strsave(p_shm);
1720 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001722 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
1723 vim_free(p_shm_save);
1724
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725 /* If autocommands took us elsewhere, quit here */
1726 if (curbuf->b_fnum != next_fnum)
1727 break;
1728 }
1729
1730 if (eap->cmdidx == CMD_windo)
1731 {
1732 validate_cursor(); /* cursor may have moved */
1733#ifdef FEAT_SCROLLBIND
1734 /* required when 'scrollbind' has been set */
1735 if (curwin->w_p_scb)
1736 do_check_scrollbind(TRUE);
1737#endif
1738 }
1739 }
1740 listcmd_busy = FALSE;
1741 }
1742
1743#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00001744 if (save_ei != NULL)
1745 {
1746 au_event_restore(save_ei);
1747 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
1748 curbuf->b_fname, TRUE, curbuf);
1749 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750#endif
1751}
1752
1753/*
1754 * Add files[count] to the arglist of the current window after arg "after".
1755 * The file names in files[count] must have been allocated and are taken over.
1756 * Files[] itself is not taken over.
1757 * Returns index of first added argument. Returns -1 when failed (out of mem).
1758 */
1759 static int
1760alist_add_list(count, files, after)
1761 int count;
1762 char_u **files;
1763 int after; /* where to add: 0 = before first one */
1764{
1765 int i;
1766
1767 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
1768 {
1769 if (after < 0)
1770 after = 0;
1771 if (after > ARGCOUNT)
1772 after = ARGCOUNT;
1773 if (after < ARGCOUNT)
1774 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
1775 (ARGCOUNT - after) * sizeof(aentry_T));
1776 for (i = 0; i < count; ++i)
1777 {
1778 ARGLIST[after + i].ae_fname = files[i];
1779 ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
1780 }
1781 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 if (curwin->w_arg_idx >= after)
1783 ++curwin->w_arg_idx;
1784 return after;
1785 }
1786
1787 for (i = 0; i < count; ++i)
1788 vim_free(files[i]);
1789 return -1;
1790}
1791
1792#endif /* FEAT_LISTCMDS */
1793
1794#ifdef FEAT_EVAL
1795/*
1796 * ":compiler[!] {name}"
1797 */
1798 void
1799ex_compiler(eap)
1800 exarg_T *eap;
1801{
1802 char_u *buf;
1803 char_u *old_cur_comp = NULL;
1804 char_u *p;
1805
1806 if (*eap->arg == NUL)
1807 {
1808 /* List all compiler scripts. */
1809 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
1810 /* ) keep the indenter happy... */
1811 }
1812 else
1813 {
1814 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
1815 if (buf != NULL)
1816 {
1817 if (eap->forceit)
1818 {
1819 /* ":compiler! {name}" sets global options */
1820 do_cmdline_cmd((char_u *)
1821 "command -nargs=* CompilerSet set <args>");
1822 }
1823 else
1824 {
1825 /* ":compiler! {name}" sets local options.
1826 * To remain backwards compatible "current_compiler" is always
1827 * used. A user's compiler plugin may set it, the distributed
1828 * plugin will then skip the settings. Afterwards set
1829 * "b:current_compiler" and restore "current_compiler". */
1830 old_cur_comp = get_var_value((char_u *)"current_compiler");
1831 if (old_cur_comp != NULL)
1832 old_cur_comp = vim_strsave(old_cur_comp);
1833 do_cmdline_cmd((char_u *)
1834 "command -nargs=* CompilerSet setlocal <args>");
1835 }
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00001836 do_unlet((char_u *)"current_compiler", TRUE);
1837 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838
1839 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
1840 if (cmd_runtime(buf, TRUE) == FAIL)
1841 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
1842 vim_free(buf);
1843
1844 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
1845
1846 /* Set "b:current_compiler" from "current_compiler". */
1847 p = get_var_value((char_u *)"current_compiler");
1848 if (p != NULL)
1849 set_internal_string_var((char_u *)"b:current_compiler", p);
1850
1851 /* Restore "current_compiler" for ":compiler {name}". */
1852 if (!eap->forceit)
1853 {
1854 if (old_cur_comp != NULL)
1855 {
1856 set_internal_string_var((char_u *)"current_compiler",
1857 old_cur_comp);
1858 vim_free(old_cur_comp);
1859 }
1860 else
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00001861 do_unlet((char_u *)"current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862 }
1863 }
1864 }
1865}
1866#endif
1867
1868/*
1869 * ":runtime {name}"
1870 */
1871 void
1872ex_runtime(eap)
1873 exarg_T *eap;
1874{
1875 cmd_runtime(eap->arg, eap->forceit);
1876}
1877
1878static void source_callback __ARGS((char_u *fname));
1879
1880 static void
1881source_callback(fname)
1882 char_u *fname;
1883{
1884 (void)do_source(fname, FALSE, FALSE);
1885}
1886
1887/*
1888 * Source the file "name" from all directories in 'runtimepath'.
1889 * "name" can contain wildcards.
1890 * When "all" is TRUE, source all files, otherwise only the first one.
1891 * return FAIL when no file could be sourced, OK otherwise.
1892 */
1893 int
1894cmd_runtime(name, all)
1895 char_u *name;
1896 int all;
1897{
1898 return do_in_runtimepath(name, all, source_callback);
1899}
1900
1901/*
1902 * Find "name" in 'runtimepath'. When found, call the "callback" function for
1903 * it.
1904 * When "all" is TRUE repeat for all matches, otherwise only the first one is
1905 * used.
1906 * Returns OK when at least one match found, FAIL otherwise.
1907 */
1908 int
1909do_in_runtimepath(name, all, callback)
1910 char_u *name;
1911 int all;
1912 void (*callback)__ARGS((char_u *fname));
1913{
1914 char_u *rtp;
1915 char_u *np;
1916 char_u *buf;
1917 char_u *rtp_copy;
1918 char_u *tail;
1919 int num_files;
1920 char_u **files;
1921 int i;
1922 int did_one = FALSE;
1923#ifdef AMIGA
1924 struct Process *proc = (struct Process *)FindTask(0L);
1925 APTR save_winptr = proc->pr_WindowPtr;
1926
1927 /* Avoid a requester here for a volume that doesn't exist. */
1928 proc->pr_WindowPtr = (APTR)-1L;
1929#endif
1930
1931 /* Make a copy of 'runtimepath'. Invoking the callback may change the
1932 * value. */
1933 rtp_copy = vim_strsave(p_rtp);
1934 buf = alloc(MAXPATHL);
1935 if (buf != NULL && rtp_copy != NULL)
1936 {
1937 if (p_verbose > 1)
1938 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
1939 (char *)name, (char *)p_rtp);
1940 /* Loop over all entries in 'runtimepath'. */
1941 rtp = rtp_copy;
1942 while (*rtp != NUL && (all || !did_one))
1943 {
1944 /* Copy the path from 'runtimepath' to buf[]. */
1945 copy_option_part(&rtp, buf, MAXPATHL, ",");
1946 if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL)
1947 {
1948 add_pathsep(buf);
1949 tail = buf + STRLEN(buf);
1950
1951 /* Loop over all patterns in "name" */
1952 np = name;
1953 while (*np != NUL && (all || !did_one))
1954 {
1955 /* Append the pattern from "name" to buf[]. */
1956 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
1957 "\t ");
1958
1959 if (p_verbose > 2)
1960 msg_str((char_u *)_("Searching for \"%s\""), buf);
1961
1962 /* Expand wildcards, invoke the callback for each match. */
1963 if (gen_expand_wildcards(1, &buf, &num_files, &files,
1964 EW_FILE) == OK)
1965 {
1966 for (i = 0; i < num_files; ++i)
1967 {
1968 (*callback)(files[i]);
1969 did_one = TRUE;
1970 if (!all)
1971 break;
1972 }
1973 FreeWild(num_files, files);
1974 }
1975 }
1976 }
1977 }
1978 }
1979 vim_free(buf);
1980 vim_free(rtp_copy);
1981 if (p_verbose > 0 && !did_one)
1982 msg_str((char_u *)_("not found in 'runtimepath': \"%s\""), name);
1983
1984#ifdef AMIGA
1985 proc->pr_WindowPtr = save_winptr;
1986#endif
1987
1988 return did_one ? OK : FAIL;
1989}
1990
1991#if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
1992/*
1993 * ":options"
1994 */
1995/*ARGSUSED*/
1996 void
1997ex_options(eap)
1998 exarg_T *eap;
1999{
2000 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
2001}
2002#endif
2003
2004/*
2005 * ":source {fname}"
2006 */
2007 void
2008ex_source(eap)
2009 exarg_T *eap;
2010{
2011#ifdef FEAT_BROWSE
2012 if (cmdmod.browse)
2013 {
2014 char_u *fname = NULL;
2015
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002016 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 NULL, NULL, BROWSE_FILTER_MACROS, NULL);
2018 if (fname != NULL)
2019 {
2020 cmd_source(fname, eap);
2021 vim_free(fname);
2022 }
2023 }
2024 else
2025#endif
2026 cmd_source(eap->arg, eap);
2027}
2028
2029 static void
2030cmd_source(fname, eap)
2031 char_u *fname;
2032 exarg_T *eap;
2033{
2034 if (*fname == NUL)
2035 EMSG(_(e_argreq));
2036
2037 /* ":source!" read vi commands */
2038 else if (eap != NULL && eap->forceit)
2039 /* Need to execute the commands directly when:
2040 * - ":g" command busy
2041 * - after ":argdo", ":windo" or ":bufdo"
2042 * - another command follows
2043 * - inside a loop
2044 */
2045 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2046#ifdef FEAT_EVAL
2047 || eap->cstack->cs_idx >= 0
2048#endif
2049 );
2050
2051 /* ":source" read ex commands */
2052 else if (do_source(fname, FALSE, FALSE) == FAIL)
2053 EMSG2(_(e_notopen), fname);
2054}
2055
2056/*
2057 * ":source" and associated commands.
2058 */
2059/*
2060 * Structure used to store info for each sourced file.
2061 * It is shared between do_source() and getsourceline().
2062 * This is required, because it needs to be handed to do_cmdline() and
2063 * sourcing can be done recursively.
2064 */
2065struct source_cookie
2066{
2067 FILE *fp; /* opened file for sourcing */
2068 char_u *nextline; /* if not NULL: line that was read ahead */
2069 int finished; /* ":finish" used */
2070#if defined (USE_CRNL) || defined (USE_CR)
2071 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
2072 int error; /* TRUE if LF found after CR-LF */
2073#endif
2074#ifdef FEAT_EVAL
2075 linenr_T breakpoint; /* next line with breakpoint or zero */
2076 char_u *fname; /* name of sourced file */
2077 int dbg_tick; /* debug_tick when breakpoint was set */
2078 int level; /* top nesting level of sourced file */
2079#endif
2080#ifdef FEAT_MBYTE
2081 vimconv_T conv; /* type of conversion */
2082#endif
2083};
2084
2085#ifdef FEAT_EVAL
2086/*
2087 * Return the address holding the next breakpoint line for a source cookie.
2088 */
2089 linenr_T *
2090source_breakpoint(cookie)
2091 void *cookie;
2092{
2093 return &((struct source_cookie *)cookie)->breakpoint;
2094}
2095
2096/*
2097 * Return the address holding the debug tick for a source cookie.
2098 */
2099 int *
2100source_dbg_tick(cookie)
2101 void *cookie;
2102{
2103 return &((struct source_cookie *)cookie)->dbg_tick;
2104}
2105
2106/*
2107 * Return the nesting level for a source cookie.
2108 */
2109 int
2110source_level(cookie)
2111 void *cookie;
2112{
2113 return ((struct source_cookie *)cookie)->level;
2114}
2115#endif
2116
2117static char_u *get_one_sourceline __ARGS((struct source_cookie *sp));
2118
2119#ifdef FEAT_EVAL
2120/* Growarray to store the names of sourced scripts.
2121 * For Unix also store the dev/ino, so that we don't have to stat() each
2122 * script when going through the list. */
2123struct scriptstuff
2124{
2125 char_u *name;
2126# ifdef UNIX
2127 int dev;
2128 ino_t ino;
2129# endif
2130};
2131static garray_T script_names = {0, 0, sizeof(struct scriptstuff), 4, NULL};
2132#define SCRIPT_NAME(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].name)
2133#define SCRIPT_DEV(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].dev)
2134#define SCRIPT_INO(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].ino)
2135#endif
2136
2137#if defined(WIN32) && defined(FEAT_CSCOPE)
2138static FILE *fopen_noinh_readbin __ARGS((char *filename));
2139
2140/*
2141 * Special function to open a file without handle inheritance.
2142 */
2143 static FILE *
2144fopen_noinh_readbin(filename)
2145 char *filename;
2146{
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00002147 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002148
2149 if (fd_tmp == -1)
2150 return NULL;
2151 return fdopen(fd_tmp, READBIN);
2152}
2153#endif
2154
2155
2156/*
2157 * do_source: Read the file "fname" and execute its lines as EX commands.
2158 *
2159 * This function may be called recursively!
2160 *
2161 * return FAIL if file could not be opened, OK otherwise
2162 */
2163 int
2164do_source(fname, check_other, is_vimrc)
2165 char_u *fname;
2166 int check_other; /* check for .vimrc and _vimrc */
2167 int is_vimrc; /* call vimrc_found() when file exists */
2168{
2169 struct source_cookie cookie;
2170 char_u *save_sourcing_name;
2171 linenr_T save_sourcing_lnum;
2172 char_u *p;
2173 char_u *fname_exp;
2174 int retval = FAIL;
2175#ifdef FEAT_EVAL
2176 scid_T save_current_SID;
2177 static scid_T last_current_SID = 0;
2178 void *save_funccalp;
2179 int save_debug_break_level = debug_break_level;
2180# ifdef UNIX
2181 struct stat st;
2182 int stat_ok;
2183# endif
2184#endif
2185#ifdef STARTUPTIME
2186 struct timeval tv_rel;
2187 struct timeval tv_start;
2188#endif
2189
2190#ifdef RISCOS
2191 p = mch_munge_fname(fname);
2192#else
2193 p = expand_env_save(fname);
2194#endif
2195 if (p == NULL)
2196 return retval;
2197 fname_exp = fix_fname(p);
2198 vim_free(p);
2199 if (fname_exp == NULL)
2200 return retval;
2201#ifdef MACOS_CLASSIC
2202 slash_n_colon_adjust(fname_exp);
2203#endif
2204 if (mch_isdir(fname_exp))
2205 {
2206 msg_str((char_u *)_("Cannot source a directory: \"%s\""), fname);
2207 goto theend;
2208 }
2209
2210#if defined(WIN32) && defined(FEAT_CSCOPE)
2211 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2212#else
2213 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2214#endif
2215 if (cookie.fp == NULL && check_other)
2216 {
2217 /*
2218 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
2219 * and ".exrc" by "_exrc" or vice versa.
2220 */
2221 p = gettail(fname_exp);
2222 if ((*p == '.' || *p == '_')
2223 && (STRICMP(p + 1, "vimrc") == 0
2224 || STRICMP(p + 1, "gvimrc") == 0
2225 || STRICMP(p + 1, "exrc") == 0))
2226 {
2227 if (*p == '_')
2228 *p = '.';
2229 else
2230 *p = '_';
2231#if defined(WIN32) && defined(FEAT_CSCOPE)
2232 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2233#else
2234 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2235#endif
2236 }
2237 }
2238
2239 if (cookie.fp == NULL)
2240 {
2241 if (p_verbose > 0)
2242 {
2243 if (sourcing_name == NULL)
2244 msg_str((char_u *)_("could not source \"%s\""), fname);
2245 else
2246 smsg((char_u *)_("line %ld: could not source \"%s\""),
2247 sourcing_lnum, fname);
2248 }
2249 goto theend;
2250 }
2251
2252 /*
2253 * The file exists.
2254 * - In verbose mode, give a message.
2255 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
2256 */
2257 if (p_verbose > 1)
2258 {
2259 if (sourcing_name == NULL)
2260 msg_str((char_u *)_("sourcing \"%s\""), fname);
2261 else
2262 smsg((char_u *)_("line %ld: sourcing \"%s\""),
2263 sourcing_lnum, fname);
2264 }
2265 if (is_vimrc)
2266 vimrc_found();
2267
2268#ifdef USE_CRNL
2269 /* If no automatic file format: Set default to CR-NL. */
2270 if (*p_ffs == NUL)
2271 cookie.fileformat = EOL_DOS;
2272 else
2273 cookie.fileformat = EOL_UNKNOWN;
2274 cookie.error = FALSE;
2275#endif
2276
2277#ifdef USE_CR
2278 /* If no automatic file format: Set default to CR. */
2279 if (*p_ffs == NUL)
2280 cookie.fileformat = EOL_MAC;
2281 else
2282 cookie.fileformat = EOL_UNKNOWN;
2283 cookie.error = FALSE;
2284#endif
2285
2286 cookie.nextline = NULL;
2287 cookie.finished = FALSE;
2288
2289#ifdef FEAT_EVAL
2290 /*
2291 * Check if this script has a breakpoint.
2292 */
2293 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
2294 cookie.fname = fname_exp;
2295 cookie.dbg_tick = debug_tick;
2296
2297 cookie.level = ex_nesting_level;
2298#endif
2299#ifdef FEAT_MBYTE
2300 cookie.conv.vc_type = CONV_NONE; /* no conversion */
2301
2302 /* Try reading the first few bytes to check for a UTF-8 BOM. */
2303 {
2304 char_u buf[3];
2305
2306 if (fread((char *)buf, sizeof(char_u), (size_t)3, cookie.fp)
2307 == (size_t)3
2308 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
2309 /* Found BOM, setup conversion and skip over it. */
2310 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
2311 else
2312 /* No BOM found, rewind. */
2313 fseek(cookie.fp, 0L, SEEK_SET);
2314 }
2315#endif
2316
2317 /*
2318 * Keep the sourcing name/lnum, for recursive calls.
2319 */
2320 save_sourcing_name = sourcing_name;
2321 sourcing_name = fname_exp;
2322 save_sourcing_lnum = sourcing_lnum;
2323 sourcing_lnum = 0;
2324
2325#ifdef STARTUPTIME
2326 time_push(&tv_rel, &tv_start);
2327#endif
2328
2329#ifdef FEAT_EVAL
2330 /*
2331 * Check if this script was sourced before to finds its SID.
2332 * If it's new, generate a new SID.
2333 */
2334 save_current_SID = current_SID;
2335# ifdef UNIX
2336 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
2337# endif
2338 for (current_SID = script_names.ga_len; current_SID > 0; --current_SID)
2339 if (SCRIPT_NAME(current_SID) != NULL
2340 && (
2341# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00002342 /* Compare dev/ino when possible, it catches symbolic
2343 * links. Also compare file names, the inode may change
2344 * when the file was edited. */
2345 ((stat_ok && SCRIPT_DEV(current_SID) != -1)
2346 && (SCRIPT_DEV(current_SID) == st.st_dev
2347 && SCRIPT_INO(current_SID) == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348# endif
2349 fnamecmp(SCRIPT_NAME(current_SID), fname_exp) == 0))
2350 break;
2351 if (current_SID == 0)
2352 {
2353 current_SID = ++last_current_SID;
2354 if (ga_grow(&script_names, (int)(current_SID - script_names.ga_len))
2355 == OK)
2356 {
2357 while (script_names.ga_len < current_SID)
2358 {
2359 SCRIPT_NAME(script_names.ga_len + 1) = NULL;
2360 ++script_names.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 }
2362 SCRIPT_NAME(current_SID) = fname_exp;
2363# ifdef UNIX
2364 if (stat_ok)
2365 {
2366 SCRIPT_DEV(current_SID) = st.st_dev;
2367 SCRIPT_INO(current_SID) = st.st_ino;
2368 }
2369 else
2370 SCRIPT_DEV(current_SID) = -1;
2371# endif
2372 fname_exp = NULL;
2373 }
2374 /* Allocate the local script variables to use for this script. */
2375 new_script_vars(current_SID);
2376 }
2377
2378 /* Don't use local function variables, if called from a function */
2379 save_funccalp = save_funccal();
2380#endif
2381
2382 /*
2383 * Call do_cmdline, which will call getsourceline() to get the lines.
2384 */
2385 do_cmdline(NULL, getsourceline, (void *)&cookie,
2386 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2387
2388 retval = OK;
2389 fclose(cookie.fp);
2390 vim_free(cookie.nextline);
2391#ifdef FEAT_MBYTE
2392 convert_setup(&cookie.conv, NULL, NULL);
2393#endif
2394
2395 if (got_int)
2396 EMSG(_(e_interr));
2397 sourcing_name = save_sourcing_name;
2398 sourcing_lnum = save_sourcing_lnum;
2399#ifdef FEAT_EVAL
2400 current_SID = save_current_SID;
2401 restore_funccal(save_funccalp);
2402#endif
2403 if (p_verbose > 1)
2404 {
2405 msg_str((char_u *)_("finished sourcing %s"), fname);
2406 if (sourcing_name != NULL)
2407 msg_str((char_u *)_("continuing in %s"), sourcing_name);
2408 }
2409#ifdef STARTUPTIME
2410# ifdef HAVE_SNPRINTF
2411 snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2412# else
2413 sprintf(IObuff, "sourcing %s", fname);
2414# endif
2415 time_msg(IObuff, &tv_start);
2416 time_pop(&tv_rel);
2417#endif
2418
2419#ifdef FEAT_EVAL
2420 /*
2421 * After a "finish" in debug mode, need to break at first command of next
2422 * sourced file.
2423 */
2424 if (save_debug_break_level > ex_nesting_level
2425 && debug_break_level == ex_nesting_level)
2426 ++debug_break_level;
2427#endif
2428
2429theend:
2430 vim_free(fname_exp);
2431 return retval;
2432}
2433
2434#if defined(FEAT_EVAL) || defined(PROTO)
2435/*
2436 * ":scriptnames"
2437 */
2438/*ARGSUSED*/
2439 void
2440ex_scriptnames(eap)
2441 exarg_T *eap;
2442{
2443 int i;
2444
2445 for (i = 1; i <= script_names.ga_len && !got_int; ++i)
2446 if (SCRIPT_NAME(i) != NULL)
2447 smsg((char_u *)"%3d: %s", i, SCRIPT_NAME(i));
2448}
2449
2450# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2451/*
2452 * Fix slashes in the list of script names for 'shellslash'.
2453 */
2454 void
2455scriptnames_slash_adjust()
2456{
2457 int i;
2458
2459 for (i = 1; i <= script_names.ga_len; ++i)
2460 if (SCRIPT_NAME(i) != NULL)
2461 slash_adjust(SCRIPT_NAME(i));
2462}
2463# endif
2464
2465/*
2466 * Get a pointer to a script name. Used for ":verbose set".
2467 */
2468 char_u *
2469get_scriptname(id)
2470 scid_T id;
2471{
2472 if (id == SID_MODELINE)
2473 return (char_u *)"modeline";
2474 if (id == SID_CMDARG)
2475 return (char_u *)"--cmd argument";
2476 if (id == SID_CARG)
2477 return (char_u *)"-c argument";
2478 if (id == SID_ENV)
2479 return (char_u *)"environment variable";
2480 return SCRIPT_NAME(id);
2481}
2482#endif
2483
2484#if defined(USE_CR) || defined(PROTO)
2485
2486# if defined(__MSL__) && (__MSL__ >= 22)
2487/*
2488 * Newer version of the Metrowerks library handle DOS and UNIX files
2489 * without help.
2490 * Test with earlier versions, MSL 2.2 is the library supplied with
2491 * Codewarrior Pro 2.
2492 */
2493 char *
2494fgets_cr(s, n, stream)
2495 char *s;
2496 int n;
2497 FILE *stream;
2498{
2499 return fgets(s, n, stream);
2500}
2501# else
2502/*
2503 * Version of fgets() which also works for lines ending in a <CR> only
2504 * (Macintosh format).
2505 * For older versions of the Metrowerks library.
2506 * At least CodeWarrior 9 needed this code.
2507 */
2508 char *
2509fgets_cr(s, n, stream)
2510 char *s;
2511 int n;
2512 FILE *stream;
2513{
2514 int c = 0;
2515 int char_read = 0;
2516
2517 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
2518 {
2519 c = fgetc(stream);
2520 s[char_read++] = c;
2521 /* If the file is in DOS format, we need to skip a NL after a CR. I
2522 * thought it was the other way around, but this appears to work... */
2523 if (c == '\n')
2524 {
2525 c = fgetc(stream);
2526 if (c != '\r')
2527 ungetc(c, stream);
2528 }
2529 }
2530
2531 s[char_read] = 0;
2532 if (char_read == 0)
2533 return NULL;
2534
2535 if (feof(stream) && char_read == 1)
2536 return NULL;
2537
2538 return s;
2539}
2540# endif
2541#endif
2542
2543/*
2544 * Get one full line from a sourced file.
2545 * Called by do_cmdline() when it's called from do_source().
2546 *
2547 * Return a pointer to the line in allocated memory.
2548 * Return NULL for end-of-file or some error.
2549 */
2550/* ARGSUSED */
2551 char_u *
2552getsourceline(c, cookie, indent)
2553 int c; /* not used */
2554 void *cookie;
2555 int indent; /* not used */
2556{
2557 struct source_cookie *sp = (struct source_cookie *)cookie;
2558 char_u *line;
2559 char_u *p, *s;
2560
2561#ifdef FEAT_EVAL
2562 /* If breakpoints have been added/deleted need to check for it. */
2563 if (sp->dbg_tick < debug_tick)
2564 {
2565 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2566 sp->dbg_tick = debug_tick;
2567 }
2568#endif
2569 /*
2570 * Get current line. If there is a read-ahead line, use it, otherwise get
2571 * one now.
2572 */
2573 if (sp->finished)
2574 line = NULL;
2575 else if (sp->nextline == NULL)
2576 line = get_one_sourceline(sp);
2577 else
2578 {
2579 line = sp->nextline;
2580 sp->nextline = NULL;
2581 ++sourcing_lnum;
2582 }
2583
2584 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
2585 * contain the 'C' flag. */
2586 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
2587 {
2588 /* compensate for the one line read-ahead */
2589 --sourcing_lnum;
2590 for (;;)
2591 {
2592 sp->nextline = get_one_sourceline(sp);
2593 if (sp->nextline == NULL)
2594 break;
2595 p = skipwhite(sp->nextline);
2596 if (*p != '\\')
2597 break;
2598 s = alloc((int)(STRLEN(line) + STRLEN(p)));
2599 if (s == NULL) /* out of memory */
2600 break;
2601 STRCPY(s, line);
2602 STRCAT(s, p + 1);
2603 vim_free(line);
2604 line = s;
2605 vim_free(sp->nextline);
2606 }
2607 }
2608
2609#ifdef FEAT_MBYTE
2610 if (line != NULL && sp->conv.vc_type != CONV_NONE)
2611 {
2612 /* Convert the encoding of the script line. */
2613 s = string_convert(&sp->conv, line, NULL);
2614 if (s != NULL)
2615 {
2616 vim_free(line);
2617 line = s;
2618 }
2619 }
2620#endif
2621
2622#ifdef FEAT_EVAL
2623 /* Did we encounter a breakpoint? */
2624 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
2625 {
2626 dbg_breakpoint(sp->fname, sourcing_lnum);
2627 /* Find next breakpoint. */
2628 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2629 sp->dbg_tick = debug_tick;
2630 }
2631#endif
2632
2633 return line;
2634}
2635
2636 static char_u *
2637get_one_sourceline(sp)
2638 struct source_cookie *sp;
2639{
2640 garray_T ga;
2641 int len;
2642 int c;
2643 char_u *buf;
2644#ifdef USE_CRNL
2645 int has_cr; /* CR-LF found */
2646#endif
2647#ifdef USE_CR
2648 char_u *scan;
2649#endif
2650 int have_read = FALSE;
2651
2652 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002653 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654
2655 /*
2656 * Loop until there is a finished line (or end-of-file).
2657 */
2658 sourcing_lnum++;
2659 for (;;)
2660 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002661 /* make room to read at least 120 (more) characters */
2662 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 break;
2664 buf = (char_u *)ga.ga_data;
2665
2666#ifdef USE_CR
2667 if (sp->fileformat == EOL_MAC)
2668 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00002669 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2670 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 break;
2672 }
2673 else
2674#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00002675 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2676 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002678 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679#ifdef USE_CRNL
2680 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
2681 * CTRL-Z by its own, or after a NL. */
2682 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
2683 && sp->fileformat == EOL_DOS
2684 && buf[len - 1] == Ctrl_Z)
2685 {
2686 buf[len - 1] = NUL;
2687 break;
2688 }
2689#endif
2690
2691#ifdef USE_CR
2692 /* If the read doesn't stop on a new line, and there's
2693 * some CR then we assume a Mac format */
2694 if (sp->fileformat == EOL_UNKNOWN)
2695 {
2696 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
2697 sp->fileformat = EOL_MAC;
2698 else
2699 sp->fileformat = EOL_UNIX;
2700 }
2701
2702 if (sp->fileformat == EOL_MAC)
2703 {
2704 scan = vim_strchr(buf, '\r');
2705
2706 if (scan != NULL)
2707 {
2708 *scan = '\n';
2709 if (*(scan + 1) != 0)
2710 {
2711 *(scan + 1) = 0;
2712 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
2713 }
2714 }
2715 len = STRLEN(buf);
2716 }
2717#endif
2718
2719 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 ga.ga_len = len;
2721
2722 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00002723 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 continue;
2725
2726 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
2727 {
2728#ifdef USE_CRNL
2729 has_cr = (len >= 2 && buf[len - 2] == '\r');
2730 if (sp->fileformat == EOL_UNKNOWN)
2731 {
2732 if (has_cr)
2733 sp->fileformat = EOL_DOS;
2734 else
2735 sp->fileformat = EOL_UNIX;
2736 }
2737
2738 if (sp->fileformat == EOL_DOS)
2739 {
2740 if (has_cr) /* replace trailing CR */
2741 {
2742 buf[len - 2] = '\n';
2743 --len;
2744 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 }
2746 else /* lines like ":map xx yy^M" will have failed */
2747 {
2748 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002749 {
2750 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002752 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002753 sp->error = TRUE;
2754 sp->fileformat = EOL_UNIX;
2755 }
2756 }
2757#endif
2758 /* The '\n' is escaped if there is an odd number of ^V's just
2759 * before it, first set "c" just before the 'V's and then check
2760 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
2761 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
2762 ;
2763 if ((len & 1) != (c & 1)) /* escaped NL, read more */
2764 {
2765 sourcing_lnum++;
2766 continue;
2767 }
2768
2769 buf[len - 1] = NUL; /* remove the NL */
2770 }
2771
2772 /*
2773 * Check for ^C here now and then, so recursive :so can be broken.
2774 */
2775 line_breakcheck();
2776 break;
2777 }
2778
2779 if (have_read)
2780 return (char_u *)ga.ga_data;
2781
2782 vim_free(ga.ga_data);
2783 return NULL;
2784}
2785
2786/*
2787 * ":scriptencoding": Set encoding conversion for a sourced script.
2788 * Without the multi-byte feature it's simply ignored.
2789 */
2790/*ARGSUSED*/
2791 void
2792ex_scriptencoding(eap)
2793 exarg_T *eap;
2794{
2795#ifdef FEAT_MBYTE
2796 struct source_cookie *sp;
2797 char_u *name;
2798
2799 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
2800 {
2801 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
2802 return;
2803 }
2804
2805 if (*eap->arg != NUL)
2806 {
2807 name = enc_canonize(eap->arg);
2808 if (name == NULL) /* out of memory */
2809 return;
2810 }
2811 else
2812 name = eap->arg;
2813
2814 /* Setup for conversion from the specified encoding to 'encoding'. */
2815 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
2816 convert_setup(&sp->conv, name, p_enc);
2817
2818 if (name != eap->arg)
2819 vim_free(name);
2820#endif
2821}
2822
2823#if defined(FEAT_EVAL) || defined(PROTO)
2824/*
2825 * ":finish": Mark a sourced file as finished.
2826 */
2827 void
2828ex_finish(eap)
2829 exarg_T *eap;
2830{
2831 if (getline_equal(eap->getline, eap->cookie, getsourceline))
2832 do_finish(eap, FALSE);
2833 else
2834 EMSG(_("E168: :finish used outside of a sourced file"));
2835}
2836
2837/*
2838 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
2839 * Also called for a pending finish at the ":endtry" or after returning from
2840 * an extra do_cmdline(). "reanimate" is used in the latter case.
2841 */
2842 void
2843do_finish(eap, reanimate)
2844 exarg_T *eap;
2845 int reanimate;
2846{
2847 int idx;
2848
2849 if (reanimate)
2850 ((struct source_cookie *)getline_cookie(eap->getline,
2851 eap->cookie))->finished = FALSE;
2852
2853 /*
2854 * Cleanup (and inactivate) conditionals, but stop when a try conditional
2855 * not in its finally clause (which then is to be executed next) is found.
2856 * In this case, make the ":finish" pending for execution at the ":endtry".
2857 * Otherwise, finish normally.
2858 */
2859 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
2860 if (idx >= 0)
2861 {
2862 eap->cstack->cs_pending[idx] = CSTP_FINISH;
2863 report_make_pending(CSTP_FINISH, NULL);
2864 }
2865 else
2866 ((struct source_cookie *)getline_cookie(eap->getline,
2867 eap->cookie))->finished = TRUE;
2868}
2869
2870
2871/*
2872 * Return TRUE when a sourced file had the ":finish" command: Don't give error
2873 * message for missing ":endif".
2874 * Return FALSE when not sourcing a file.
2875 */
2876 int
2877source_finished(getline, cookie)
2878 char_u *(*getline) __ARGS((int, void *, int));
2879 void *cookie;
2880{
2881 return (getline_equal(getline, cookie, getsourceline)
2882 && ((struct source_cookie *)getline_cookie(
2883 getline, cookie))->finished);
2884}
2885#endif
2886
2887#if defined(FEAT_LISTCMDS) || defined(PROTO)
2888/*
2889 * ":checktime [buffer]"
2890 */
2891 void
2892ex_checktime(eap)
2893 exarg_T *eap;
2894{
2895 buf_T *buf;
2896 int save_no_check_timestamps = no_check_timestamps;
2897
2898 no_check_timestamps = 0;
2899 if (eap->addr_count == 0) /* default is all buffers */
2900 check_timestamps(FALSE);
2901 else
2902 {
2903 buf = buflist_findnr((int)eap->line2);
2904 if (buf != NULL) /* cannot happen? */
2905 (void)buf_check_timestamp(buf, FALSE);
2906 }
2907 no_check_timestamps = save_no_check_timestamps;
2908}
2909#endif
2910
2911#if defined(FEAT_PRINTER) || defined(PROTO)
2912/*
2913 * Printing code (Machine-independent.)
2914 * To implement printing on a platform, the following functions must be
2915 * defined:
2916 *
2917 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
2918 * Called once. Code should display printer dialogue (if appropriate) and
2919 * determine printer font and margin settings. Reset has_color if the printer
2920 * doesn't support colors at all.
2921 * Returns FAIL to abort.
2922 *
2923 * int mch_print_begin(prt_settings_T *settings)
2924 * Called to start the print job.
2925 * Return FALSE to abort.
2926 *
2927 * int mch_print_begin_page(char_u *msg)
2928 * Called at the start of each page.
2929 * "msg" indicates the progress of the print job, can be NULL.
2930 * Return FALSE to abort.
2931 *
2932 * int mch_print_end_page()
2933 * Called at the end of each page.
2934 * Return FALSE to abort.
2935 *
2936 * int mch_print_blank_page()
2937 * Called to generate a blank page for collated, duplex, multiple copy
2938 * document. Return FALSE to abort.
2939 *
2940 * void mch_print_end(prt_settings_T *psettings)
2941 * Called at normal end of print job.
2942 *
2943 * void mch_print_cleanup()
2944 * Called if print job ends normally or is abandoned. Free any memory, close
2945 * devices and handles. Also called when mch_print_begin() fails, but not
2946 * when mch_print_init() fails.
2947 *
2948 * void mch_print_set_font(int Bold, int Italic, int Underline);
2949 * Called whenever the font style changes.
2950 *
2951 * void mch_print_set_bg(long bgcol);
2952 * Called to set the background color for the following text. Parameter is an
2953 * RGB value.
2954 *
2955 * void mch_print_set_fg(long fgcol);
2956 * Called to set the foreground color for the following text. Parameter is an
2957 * RGB value.
2958 *
2959 * mch_print_start_line(int margin, int page_line)
2960 * Sets the current position at the start of line "page_line".
2961 * If margin is TRUE start in the left margin (for header and line number).
2962 *
2963 * int mch_print_text_out(char_u *p, int len);
2964 * Output one character of text p[len] at the current position.
2965 * Return TRUE if there is no room for another character in the same line.
2966 *
2967 * Note that the generic code has no idea of margins. The machine code should
2968 * simply make the page look smaller! The header and the line numbers are
2969 * printed in the margin.
2970 */
2971
2972#ifdef FEAT_SYN_HL
2973static const long_u cterm_color_8[8] =
2974{
2975 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
2976 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
2977};
2978
2979static const long_u cterm_color_16[16] =
2980{
2981 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
2982 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
2983 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
2984 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
2985};
2986
2987static int current_syn_id;
2988#endif
2989
2990#define PRCOLOR_BLACK (long_u)0
2991#define PRCOLOR_WHITE (long_u)0xFFFFFFL
2992
2993static int curr_italic;
2994static int curr_bold;
2995static int curr_underline;
2996static long_u curr_bg;
2997static long_u curr_fg;
2998static int page_count;
2999
3000/*
3001 * These values determine the print position on a page.
3002 */
3003typedef struct
3004{
3005 int lead_spaces; /* remaining spaces for a TAB */
3006 int print_pos; /* virtual column for computing TABs */
3007 colnr_T column; /* byte column */
3008 linenr_T file_line; /* line nr in the buffer */
3009 long_u bytes_printed; /* bytes printed so far */
3010 int ff; /* seen form feed character */
3011} prt_pos_T;
3012
3013#ifdef FEAT_SYN_HL
3014static long_u darken_rgb __ARGS((long_u rgb));
3015static long_u prt_get_term_color __ARGS((int colorindex));
3016#endif
3017static void prt_set_fg __ARGS((long_u fg));
3018static void prt_set_bg __ARGS((long_u bg));
3019static void prt_set_font __ARGS((int bold, int italic, int underline));
3020static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3021static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3022static void prt_message __ARGS((char_u *s));
3023static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3024static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3025
3026#ifdef FEAT_SYN_HL
3027/*
3028 * If using a dark background, the colors will probably be too bright to show
3029 * up well on white paper, so reduce their brightness.
3030 */
3031 static long_u
3032darken_rgb(rgb)
3033 long_u rgb;
3034{
3035 return ((rgb >> 17) << 16)
3036 + (((rgb & 0xff00) >> 9) << 8)
3037 + ((rgb & 0xff) >> 1);
3038}
3039
3040 static long_u
3041prt_get_term_color(colorindex)
3042 int colorindex;
3043{
3044 /* TODO: Should check for xterm with 88 or 256 colors. */
3045 if (t_colors > 8)
3046 return cterm_color_16[colorindex % 16];
3047 return cterm_color_8[colorindex % 8];
3048}
3049
3050 static void
3051prt_get_attr(hl_id, pattr, modec)
3052 int hl_id;
3053 prt_text_attr_T* pattr;
3054 int modec;
3055{
3056 int colorindex;
3057 long_u fg_color;
3058 long_u bg_color;
3059 char *color;
3060
3061 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3062 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3063 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
3064
3065# ifdef FEAT_GUI
3066 if (gui.in_use)
3067 {
3068 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3069 if (bg_color == PRCOLOR_BLACK)
3070 bg_color = PRCOLOR_WHITE;
3071
3072 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3073 }
3074 else
3075# endif
3076 {
3077 bg_color = PRCOLOR_WHITE;
3078
3079 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3080 if (color == NULL)
3081 colorindex = 0;
3082 else
3083 colorindex = atoi(color);
3084
3085 if (colorindex >= 0 && colorindex < t_colors)
3086 fg_color = prt_get_term_color(colorindex);
3087 else
3088 fg_color = PRCOLOR_BLACK;
3089 }
3090
3091 if (fg_color == PRCOLOR_WHITE)
3092 fg_color = PRCOLOR_BLACK;
3093 else if (*p_bg == 'd')
3094 fg_color = darken_rgb(fg_color);
3095
3096 pattr->fg_color = fg_color;
3097 pattr->bg_color = bg_color;
3098}
3099#endif /* FEAT_SYN_HL */
3100
3101 static void
3102prt_set_fg(fg)
3103 long_u fg;
3104{
3105 if (fg != curr_fg)
3106 {
3107 curr_fg = fg;
3108 mch_print_set_fg(fg);
3109 }
3110}
3111
3112 static void
3113prt_set_bg(bg)
3114 long_u bg;
3115{
3116 if (bg != curr_bg)
3117 {
3118 curr_bg = bg;
3119 mch_print_set_bg(bg);
3120 }
3121}
3122
3123 static void
3124prt_set_font(bold, italic, underline)
3125 int bold;
3126 int italic;
3127 int underline;
3128{
3129 if (curr_bold != bold
3130 || curr_italic != italic
3131 || curr_underline != underline)
3132 {
3133 curr_underline = underline;
3134 curr_italic = italic;
3135 curr_bold = bold;
3136 mch_print_set_font(bold, italic, underline);
3137 }
3138}
3139
3140/*
3141 * Print the line number in the left margin.
3142 */
3143 static void
3144prt_line_number(psettings, page_line, lnum)
3145 prt_settings_T *psettings;
3146 int page_line;
3147 linenr_T lnum;
3148{
3149 int i;
3150 char_u tbuf[20];
3151
3152 prt_set_fg(psettings->number.fg_color);
3153 prt_set_bg(psettings->number.bg_color);
3154 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3155 mch_print_start_line(TRUE, page_line);
3156
3157 /* Leave two spaces between the number and the text; depends on
3158 * PRINT_NUMBER_WIDTH. */
3159 sprintf((char *)tbuf, "%6ld", (long)lnum);
3160 for (i = 0; i < 6; i++)
3161 (void)mch_print_text_out(&tbuf[i], 1);
3162
3163#ifdef FEAT_SYN_HL
3164 if (psettings->do_syntax)
3165 /* Set colors for next character. */
3166 current_syn_id = -1;
3167 else
3168#endif
3169 {
3170 /* Set colors and font back to normal. */
3171 prt_set_fg(PRCOLOR_BLACK);
3172 prt_set_bg(PRCOLOR_WHITE);
3173 prt_set_font(FALSE, FALSE, FALSE);
3174 }
3175}
3176
3177static linenr_T printer_page_num;
3178
3179 int
3180get_printer_page_num()
3181{
3182 return printer_page_num;
3183}
3184
3185/*
3186 * Get the currently effective header height.
3187 */
3188 int
3189prt_header_height()
3190{
3191 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3192 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3193 return 2;
3194}
3195
3196/*
3197 * Return TRUE if using a line number for printing.
3198 */
3199 int
3200prt_use_number()
3201{
3202 return (printer_opts[OPT_PRINT_NUMBER].present
3203 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3204}
3205
3206/*
3207 * Return the unit used in a margin item in 'printoptions'.
3208 * Returns PRT_UNIT_NONE if not recognized.
3209 */
3210 int
3211prt_get_unit(idx)
3212 int idx;
3213{
3214 int u = PRT_UNIT_NONE;
3215 int i;
3216 static char *(units[4]) = PRT_UNIT_NAMES;
3217
3218 if (printer_opts[idx].present)
3219 for (i = 0; i < 4; ++i)
3220 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3221 {
3222 u = i;
3223 break;
3224 }
3225 return u;
3226}
3227
3228/*
3229 * Print the page header.
3230 */
3231/*ARGSUSED*/
3232 static void
3233prt_header(psettings, pagenum, lnum)
3234 prt_settings_T *psettings;
3235 int pagenum;
3236 linenr_T lnum;
3237{
3238 int width = psettings->chars_per_line;
3239 int page_line;
3240 char_u *tbuf;
3241 char_u *p;
3242#ifdef FEAT_MBYTE
3243 int l;
3244#endif
3245
3246 /* Also use the space for the line number. */
3247 if (prt_use_number())
3248 width += PRINT_NUMBER_WIDTH;
3249
3250 tbuf = alloc(width + IOSIZE);
3251 if (tbuf == NULL)
3252 return;
3253
3254#ifdef FEAT_STL_OPT
3255 if (*p_header != NUL)
3256 {
3257 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3258
3259 /*
3260 * Need to (temporarily) set current line number and first/last line
3261 * number on the 'window'. Since we don't know how long the page is,
3262 * set the first and current line number to the top line, and guess
3263 * that the page length is 64.
3264 */
3265 tmp_lnum = curwin->w_cursor.lnum;
3266 tmp_topline = curwin->w_topline;
3267 tmp_botline = curwin->w_botline;
3268 curwin->w_cursor.lnum = lnum;
3269 curwin->w_topline = lnum;
3270 curwin->w_botline = lnum + 63;
3271 printer_page_num = pagenum;
3272
3273 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3274 p_header, ' ', width, NULL);
3275
3276 /* Reset line numbers */
3277 curwin->w_cursor.lnum = tmp_lnum;
3278 curwin->w_topline = tmp_topline;
3279 curwin->w_botline = tmp_botline;
3280 }
3281 else
3282#endif
3283 sprintf((char *)tbuf, _("Page %d"), pagenum);
3284
3285 prt_set_fg(PRCOLOR_BLACK);
3286 prt_set_bg(PRCOLOR_WHITE);
3287 prt_set_font(TRUE, FALSE, FALSE);
3288
3289 /* Use a negative line number to indicate printing in the top margin. */
3290 page_line = 0 - prt_header_height();
3291 mch_print_start_line(TRUE, page_line);
3292 for (p = tbuf; *p != NUL; )
3293 {
3294 if (mch_print_text_out(p,
3295#ifdef FEAT_MBYTE
3296 (l = (*mb_ptr2len_check)(p))
3297#else
3298 1
3299#endif
3300 ))
3301 {
3302 ++page_line;
3303 if (page_line >= 0) /* out of room in header */
3304 break;
3305 mch_print_start_line(TRUE, page_line);
3306 }
3307#ifdef FEAT_MBYTE
3308 p += l;
3309#else
3310 p++;
3311#endif
3312 }
3313
3314 vim_free(tbuf);
3315
3316#ifdef FEAT_SYN_HL
3317 if (psettings->do_syntax)
3318 /* Set colors for next character. */
3319 current_syn_id = -1;
3320 else
3321#endif
3322 {
3323 /* Set colors and font back to normal. */
3324 prt_set_fg(PRCOLOR_BLACK);
3325 prt_set_bg(PRCOLOR_WHITE);
3326 prt_set_font(FALSE, FALSE, FALSE);
3327 }
3328}
3329
3330/*
3331 * Display a print status message.
3332 */
3333 static void
3334prt_message(s)
3335 char_u *s;
3336{
3337 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3338 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3339 out_flush();
3340}
3341
3342 void
3343ex_hardcopy(eap)
3344 exarg_T *eap;
3345{
3346 linenr_T lnum;
3347 int collated_copies, uncollated_copies;
3348 prt_settings_T settings;
3349 long_u bytes_to_print = 0;
3350 int page_line;
3351 int jobsplit;
3352 int id;
3353
3354 memset(&settings, 0, sizeof(prt_settings_T));
3355 settings.has_color = TRUE;
3356
3357# ifdef FEAT_POSTSCRIPT
3358 if (*eap->arg == '>')
3359 {
3360 char_u *errormsg = NULL;
3361
3362 /* Expand things like "%.ps". */
3363 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3364 {
3365 if (errormsg != NULL)
3366 EMSG(errormsg);
3367 return;
3368 }
3369 settings.outfile = skipwhite(eap->arg + 1);
3370 }
3371 else if (*eap->arg != NUL)
3372 settings.arguments = eap->arg;
3373# endif
3374
3375 /*
3376 * Initialise for printing. Ask the user for settings, unless forceit is
3377 * set.
3378 * The mch_print_init() code should set up margins if applicable. (It may
3379 * not be a real printer - for example the engine might generate HTML or
3380 * PS.)
3381 */
3382 if (mch_print_init(&settings,
3383 curbuf->b_fname == NULL
3384 ? (char_u *)buf_spname(curbuf)
3385 : curbuf->b_sfname == NULL
3386 ? curbuf->b_fname
3387 : curbuf->b_sfname,
3388 eap->forceit) == FAIL)
3389 return;
3390
3391#ifdef FEAT_SYN_HL
3392# ifdef FEAT_GUI
3393 if (gui.in_use)
3394 settings.modec = 'g';
3395 else
3396# endif
3397 if (t_colors > 1)
3398 settings.modec = 'c';
3399 else
3400 settings.modec = 't';
3401
3402 if (!syntax_present(curbuf))
3403 settings.do_syntax = FALSE;
3404 else if (printer_opts[OPT_PRINT_SYNTAX].present
3405 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
3406 settings.do_syntax =
3407 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
3408 else
3409 settings.do_syntax = settings.has_color;
3410#endif
3411
3412 /* Set up printing attributes for line numbers */
3413 settings.number.fg_color = PRCOLOR_BLACK;
3414 settings.number.bg_color = PRCOLOR_WHITE;
3415 settings.number.bold = FALSE;
3416 settings.number.italic = TRUE;
3417 settings.number.underline = FALSE;
3418#ifdef FEAT_SYN_HL
3419 /*
3420 * Syntax highlighting of line numbers.
3421 */
3422 if (prt_use_number() && settings.do_syntax)
3423 {
3424 id = syn_name2id((char_u *)"LineNr");
3425 if (id > 0)
3426 id = syn_get_final_id(id);
3427
3428 prt_get_attr(id, &settings.number, settings.modec);
3429 }
3430#endif /* FEAT_SYN_HL */
3431
3432 /*
3433 * Estimate the total lines to be printed
3434 */
3435 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
3436 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
3437 if (bytes_to_print == 0)
3438 {
3439 MSG(_("No text to be printed"));
3440 goto print_fail_no_begin;
3441 }
3442
3443 /* Set colors and font to normal. */
3444 curr_bg = (long_u)0xffffffffL;
3445 curr_fg = (long_u)0xffffffffL;
3446 curr_italic = MAYBE;
3447 curr_bold = MAYBE;
3448 curr_underline = MAYBE;
3449
3450 prt_set_fg(PRCOLOR_BLACK);
3451 prt_set_bg(PRCOLOR_WHITE);
3452 prt_set_font(FALSE, FALSE, FALSE);
3453#ifdef FEAT_SYN_HL
3454 current_syn_id = -1;
3455#endif
3456
3457 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
3458 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
3459
3460 if (!mch_print_begin(&settings))
3461 goto print_fail_no_begin;
3462
3463 /*
3464 * Loop over collated copies: 1 2 3, 1 2 3, ...
3465 */
3466 page_count = 0;
3467 for (collated_copies = 0;
3468 collated_copies < settings.n_collated_copies;
3469 collated_copies++)
3470 {
3471 prt_pos_T prtpos; /* current print position */
3472 prt_pos_T page_prtpos; /* print position at page start */
3473 int side;
3474
3475 memset(&page_prtpos, 0, sizeof(prt_pos_T));
3476 page_prtpos.file_line = eap->line1;
3477 prtpos = page_prtpos;
3478
3479 if (jobsplit && collated_copies > 0)
3480 {
3481 /* Splitting jobs: Stop a previous job and start a new one. */
3482 mch_print_end(&settings);
3483 if (!mch_print_begin(&settings))
3484 goto print_fail_no_begin;
3485 }
3486
3487 /*
3488 * Loop over all pages in the print job: 1 2 3 ...
3489 */
3490 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
3491 {
3492 /*
3493 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
3494 * For duplex: 12 12 12 34 34 34, ...
3495 */
3496 for (uncollated_copies = 0;
3497 uncollated_copies < settings.n_uncollated_copies;
3498 uncollated_copies++)
3499 {
3500 /* Set the print position to the start of this page. */
3501 prtpos = page_prtpos;
3502
3503 /*
3504 * Do front and rear side of a page.
3505 */
3506 for (side = 0; side <= settings.duplex; ++side)
3507 {
3508 /*
3509 * Print one page.
3510 */
3511
3512 /* Check for interrupt character every page. */
3513 ui_breakcheck();
3514 if (got_int || settings.user_abort)
3515 goto print_fail;
3516
3517 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
3518 page_count + 1 + side,
3519 prtpos.bytes_printed > 1000000
3520 ? (int)(prtpos.bytes_printed /
3521 (bytes_to_print / 100))
3522 : (int)((prtpos.bytes_printed * 100)
3523 / bytes_to_print));
3524 if (!mch_print_begin_page(IObuff))
3525 goto print_fail;
3526
3527 if (settings.n_collated_copies > 1)
3528 sprintf((char *)IObuff + STRLEN(IObuff),
3529 _(" Copy %d of %d"),
3530 collated_copies + 1,
3531 settings.n_collated_copies);
3532 prt_message(IObuff);
3533
3534 /*
3535 * Output header if required
3536 */
3537 if (prt_header_height() > 0)
3538 prt_header(&settings, page_count + 1 + side,
3539 prtpos.file_line);
3540
3541 for (page_line = 0; page_line < settings.lines_per_page;
3542 ++page_line)
3543 {
3544 prtpos.column = hardcopy_line(&settings,
3545 page_line, &prtpos);
3546 if (prtpos.column == 0)
3547 {
3548 /* finished a file line */
3549 prtpos.bytes_printed +=
3550 STRLEN(skipwhite(ml_get(prtpos.file_line)));
3551 if (++prtpos.file_line > eap->line2)
3552 break; /* reached the end */
3553 }
3554 else if (prtpos.ff)
3555 {
3556 /* Line had a formfeed in it - start new page but
3557 * stay on the current line */
3558 break;
3559 }
3560 }
3561
3562 if (!mch_print_end_page())
3563 goto print_fail;
3564 if (prtpos.file_line > eap->line2)
3565 break; /* reached the end */
3566 }
3567
3568 /*
3569 * Extra blank page for duplexing with odd number of pages and
3570 * more copies to come.
3571 */
3572 if (prtpos.file_line > eap->line2 && settings.duplex
3573 && side == 0
3574 && uncollated_copies + 1 < settings.n_uncollated_copies)
3575 {
3576 if (!mch_print_blank_page())
3577 goto print_fail;
3578 }
3579 }
3580 if (settings.duplex && prtpos.file_line <= eap->line2)
3581 ++page_count;
3582
3583 /* Remember the position where the next page starts. */
3584 page_prtpos = prtpos;
3585 }
3586
3587 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
3588 prt_message(IObuff);
3589 }
3590
3591print_fail:
3592 if (got_int || settings.user_abort)
3593 {
3594 sprintf((char *)IObuff, _("Printing aborted"));
3595 prt_message(IObuff);
3596 }
3597 mch_print_end(&settings);
3598
3599print_fail_no_begin:
3600 mch_print_cleanup();
3601}
3602
3603/*
3604 * Print one page line.
3605 * Return the next column to print, or zero if the line is finished.
3606 */
3607 static colnr_T
3608hardcopy_line(psettings, page_line, ppos)
3609 prt_settings_T *psettings;
3610 int page_line;
3611 prt_pos_T *ppos;
3612{
3613 colnr_T col;
3614 char_u *line;
3615 int need_break = FALSE;
3616 int outputlen;
3617 int tab_spaces;
3618 long_u print_pos;
3619#ifdef FEAT_SYN_HL
3620 prt_text_attr_T attr;
3621 int id;
3622#endif
3623
3624 if (ppos->column == 0 || ppos->ff)
3625 {
3626 print_pos = 0;
3627 tab_spaces = 0;
3628 if (!ppos->ff && prt_use_number())
3629 prt_line_number(psettings, page_line, ppos->file_line);
3630 ppos->ff = FALSE;
3631 }
3632 else
3633 {
3634 /* left over from wrap halfway a tab */
3635 print_pos = ppos->print_pos;
3636 tab_spaces = ppos->lead_spaces;
3637 }
3638
3639 mch_print_start_line(0, page_line);
3640 line = ml_get(ppos->file_line);
3641
3642 /*
3643 * Loop over the columns until the end of the file line or right margin.
3644 */
3645 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
3646 {
3647 outputlen = 1;
3648#ifdef FEAT_MBYTE
3649 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
3650 outputlen = 1;
3651#endif
3652#ifdef FEAT_SYN_HL
3653 /*
3654 * syntax highlighting stuff.
3655 */
3656 if (psettings->do_syntax)
3657 {
3658 id = syn_get_id(ppos->file_line, (long)col, 1);
3659 if (id > 0)
3660 id = syn_get_final_id(id);
3661 else
3662 id = 0;
3663 /* Get the line again, a multi-line regexp may invalidate it. */
3664 line = ml_get(ppos->file_line);
3665
3666 if (id != current_syn_id)
3667 {
3668 current_syn_id = id;
3669 prt_get_attr(id, &attr, psettings->modec);
3670 prt_set_font(attr.bold, attr.italic, attr.underline);
3671 prt_set_fg(attr.fg_color);
3672 prt_set_bg(attr.bg_color);
3673 }
3674 }
3675#endif /* FEAT_SYN_HL */
3676
3677 /*
3678 * Appropriately expand any tabs to spaces.
3679 */
3680 if (line[col] == TAB || tab_spaces != 0)
3681 {
3682 if (tab_spaces == 0)
3683 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
3684
3685 while (tab_spaces > 0)
3686 {
3687 need_break = mch_print_text_out((char_u *)" ", 1);
3688 print_pos++;
3689 tab_spaces--;
3690 if (need_break)
3691 break;
3692 }
3693 /* Keep the TAB if we didn't finish it. */
3694 if (need_break && tab_spaces > 0)
3695 break;
3696 }
3697 else if (line[col] == FF
3698 && printer_opts[OPT_PRINT_FORMFEED].present
3699 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
3700 == 'y')
3701 {
3702 ppos->ff = TRUE;
3703 need_break = 1;
3704 }
3705 else
3706 {
3707 need_break = mch_print_text_out(line + col, outputlen);
3708#ifdef FEAT_MBYTE
3709 if (has_mbyte)
3710 print_pos += (*mb_ptr2cells)(line + col);
3711 else
3712#endif
3713 print_pos++;
3714 }
3715 }
3716
3717 ppos->lead_spaces = tab_spaces;
3718 ppos->print_pos = print_pos;
3719
3720 /*
3721 * Start next line of file if we clip lines, or have reached end of the
3722 * line, unless we are doing a formfeed.
3723 */
3724 if (!ppos->ff
3725 && (line[col] == NUL
3726 || (printer_opts[OPT_PRINT_WRAP].present
3727 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
3728 == 'n')))
3729 return 0;
3730 return col;
3731}
3732
3733# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
3734
3735/*
3736 * PS printer stuff.
3737 *
3738 * Sources of information to help maintain the PS printing code:
3739 *
3740 * 1. PostScript Language Reference, 3rd Edition,
3741 * Addison-Wesley, 1999, ISBN 0-201-37922-8
3742 * 2. PostScript Language Program Design,
3743 * Addison-Wesley, 1988, ISBN 0-201-14396-8
3744 * 3. PostScript Tutorial and Cookbook,
3745 * Addison Wesley, 1985, ISBN 0-201-10179-3
3746 * 4. PostScript Language Document Structuring Conventions Specification,
3747 * version 3.0,
3748 * Adobe Technote 5001, 25th September 1992
3749 * 5. PostScript Printer Description File Format Specification, Version 4.3,
3750 * Adobe technote 5003, 9th February 1996
3751 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
3752 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00003753 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
3754 * Adobe Technote 5014, 8th October 1996
3755 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
3756 * Adoboe Technote 5094, 8th September, 2001
3757 * 9. CJKV Information Processing, 2nd Edition,
3758 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759 *
3760 * Some of these documents can be found in PDF form on Adobe's web site -
3761 * http://www.adobe.com
3762 */
3763
Bram Moolenaar8299df92004-07-10 09:47:34 +00003764#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
3765
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
3767#define PRT_PS_DEFAULT_FONTSIZE (10)
3768#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
3769
3770struct prt_mediasize_S
3771{
3772 char *name;
3773 float width; /* width and height in points for portrait */
3774 float height;
3775};
3776
3777#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
3778
3779static struct prt_mediasize_S prt_mediasize[] =
3780{
3781 {"A4", 595.0, 842.0},
3782 {"letter", 612.0, 792.0},
3783 {"10x14", 720.0, 1008.0},
3784 {"A3", 842.0, 1191.0},
3785 {"A5", 420.0, 595.0},
3786 {"B4", 729.0, 1032.0},
3787 {"B5", 516.0, 729.0},
3788 {"executive", 522.0, 756.0},
3789 {"folio", 595.0, 935.0},
3790 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
3791 {"legal", 612.0, 1008.0},
3792 {"quarto", 610.0, 780.0},
3793 {"statement", 396.0, 612.0},
3794 {"tabloid", 792.0, 1224.0}
3795};
3796
3797/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
3798struct prt_ps_font_S
3799{
3800 int wx;
3801 int uline_offset;
3802 int uline_width;
3803 int bbox_min_y;
3804 int bbox_max_y;
3805 char *(ps_fontname[4]);
3806};
3807
3808#define PRT_PS_FONT_ROMAN (0)
3809#define PRT_PS_FONT_BOLD (1)
3810#define PRT_PS_FONT_OBLIQUE (2)
3811#define PRT_PS_FONT_BOLDOBLIQUE (3)
3812
Bram Moolenaar8299df92004-07-10 09:47:34 +00003813/* Standard font metrics for Courier family */
3814static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815{
3816 600,
3817 -100, 50,
3818 -250, 805,
3819 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
3820};
3821
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003822#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00003823/* Generic font metrics for multi-byte fonts */
3824static struct prt_ps_font_S prt_ps_mb_font =
3825{
3826 1000,
3827 -100, 50,
3828 -250, 805,
3829 {NULL, NULL, NULL, NULL}
3830};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003831#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00003832
3833/* Pointer to current font set being used */
3834static struct prt_ps_font_S* prt_ps_font;
3835
3836/* Structures to map user named encoding and mapping to PS equivalents for
3837 * building CID font name */
3838struct prt_ps_encoding_S
3839{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003840 char *encoding;
3841 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00003842 int needs_charset;
3843};
3844
3845struct prt_ps_charset_S
3846{
3847 char *charset;
3848 char *cmap_charset;
3849 int has_charset;
3850};
3851
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003852#ifdef FEAT_MBYTE
3853
Bram Moolenaar8299df92004-07-10 09:47:34 +00003854#define CS_JIS_C_1978 (0x01)
3855#define CS_JIS_X_1983 (0x02)
3856#define CS_JIS_X_1990 (0x04)
3857#define CS_NEC (0x08)
3858#define CS_MSWINDOWS (0x10)
3859#define CS_CP932 (0x20)
3860#define CS_KANJITALK6 (0x40)
3861#define CS_KANJITALK7 (0x80)
3862
3863/* Japanese encodings and charsets */
3864static struct prt_ps_encoding_S j_encodings[] =
3865{
3866 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
3867 CS_NEC)},
3868 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
3869 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
3870 CS_KANJITALK6|CS_KANJITALK7)},
3871 {"cp932", "RKSJ", CS_JIS_X_1983},
3872 {"ucs-2", "UCS2", CS_JIS_X_1990},
3873 {"utf-8", "UTF8" , CS_JIS_X_1990}
3874};
3875static struct prt_ps_charset_S j_charsets[] =
3876{
3877 {"JIS_C_1978", "78", CS_JIS_C_1978},
3878 {"JIS_X_1983", NULL, CS_JIS_X_1983},
3879 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
3880 {"NEC", "Ext", CS_NEC},
3881 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
3882 {"CP932", "90ms", CS_JIS_X_1983},
3883 {"KANJITALK6", "83pv", CS_KANJITALK6},
3884 {"KANJITALK7", "90pv", CS_KANJITALK7}
3885};
3886
3887#define CS_GB_2312_80 (0x01)
3888#define CS_GBT_12345_90 (0x02)
3889#define CS_GBK2K (0x04)
3890#define CS_SC_MAC (0x08)
3891#define CS_GBT_90_MAC (0x10)
3892#define CS_GBK (0x20)
3893#define CS_SC_ISO10646 (0x40)
3894
3895/* Simplified Chinese encodings and charsets */
3896static struct prt_ps_encoding_S sc_encodings[] =
3897{
3898 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
3899 {"gb18030", NULL, CS_GBK2K},
3900 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
3901 CS_GBT_90_MAC)},
3902 {"gbk", "EUC", CS_GBK},
3903 {"ucs-2", "UCS2", CS_SC_ISO10646},
3904 {"utf-8", "UTF8", CS_SC_ISO10646}
3905};
3906static struct prt_ps_charset_S sc_charsets[] =
3907{
3908 {"GB_2312-80", "GB", CS_GB_2312_80},
3909 {"GBT_12345-90","GBT", CS_GBT_12345_90},
3910 {"MAC", "GBpc", CS_SC_MAC},
3911 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
3912 {"GBK", "GBK", CS_GBK},
3913 {"GB18030", "GBK2K", CS_GBK2K},
3914 {"ISO10646", "UniGB", CS_SC_ISO10646}
3915};
3916
3917#define CS_CNS_PLANE_1 (0x01)
3918#define CS_CNS_PLANE_2 (0x02)
3919#define CS_CNS_PLANE_1_2 (0x04)
3920#define CS_B5 (0x08)
3921#define CS_ETEN (0x10)
3922#define CS_HK_GCCS (0x20)
3923#define CS_HK_SCS (0x40)
3924#define CS_HK_SCS_ETEN (0x80)
3925#define CS_MTHKL (0x100)
3926#define CS_MTHKS (0x200)
3927#define CS_DLHKL (0x400)
3928#define CS_DLHKS (0x800)
3929#define CS_TC_ISO10646 (0x1000)
3930
3931/* Traditional Chinese encodings and charsets */
3932static struct prt_ps_encoding_S tc_encodings[] =
3933{
3934 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
3935 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
3936 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
3937 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
3938 CS_DLHKS)},
3939 {"cp950", "B5", CS_B5},
3940 {"ucs-2", "UCS2", CS_TC_ISO10646},
3941 {"utf-8", "UTF8", CS_TC_ISO10646},
3942 {"utf-16", "UTF16", CS_TC_ISO10646},
3943 {"utf-32", "UTF32", CS_TC_ISO10646}
3944};
3945static struct prt_ps_charset_S tc_charsets[] =
3946{
3947 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
3948 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
3949 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
3950 {"BIG5", NULL, CS_B5},
3951 {"CP950", NULL, CS_B5},
3952 {"ETEN", "ETen", CS_ETEN},
3953 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
3954 {"SCS", "HKscs", CS_HK_SCS},
3955 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
3956 {"MTHKL", "HKm471", CS_MTHKL},
3957 {"MTHKS", "HKm314", CS_MTHKS},
3958 {"DLHKL", "HKdla", CS_DLHKL},
3959 {"DLHKS", "HKdlb", CS_DLHKS},
3960 {"ISO10646", "UniCNS", CS_TC_ISO10646}
3961};
3962
3963#define CS_KR_X_1992 (0x01)
3964#define CS_KR_MAC (0x02)
3965#define CS_KR_X_1992_MS (0x04)
3966#define CS_KR_ISO10646 (0x08)
3967
3968/* Korean encodings and charsets */
3969static struct prt_ps_encoding_S k_encodings[] =
3970{
3971 {"iso-2022-kr", NULL, CS_KR_X_1992},
3972 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
3973 {"johab", "Johab", CS_KR_X_1992},
3974 {"cp1361", "Johab", CS_KR_X_1992},
3975 {"uhc", "UHC", CS_KR_X_1992_MS},
3976 {"cp949", "UHC", CS_KR_X_1992_MS},
3977 {"ucs-2", "UCS2", CS_KR_ISO10646},
3978 {"utf-8", "UTF8", CS_KR_ISO10646}
3979};
3980static struct prt_ps_charset_S k_charsets[] =
3981{
3982 {"KS_X_1992", "KSC", CS_KR_X_1992},
3983 {"CP1361", "KSC", CS_KR_X_1992},
3984 {"MAC", "KSCpc", CS_KR_MAC},
3985 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
3986 {"CP949", "KSCms", CS_KR_X_1992_MS},
3987 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
3988 {"ISO10646", "UniKS", CS_KR_ISO10646}
3989};
3990
3991/* Collections of encodings and charsets for multi-byte printing */
3992struct prt_ps_mbfont_S
3993{
3994 int num_encodings;
3995 struct prt_ps_encoding_S *encodings;
3996 int num_charsets;
3997 struct prt_ps_charset_S *charsets;
3998 char *ascii_enc;
3999 char *defcs;
4000};
4001
4002static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4003{
4004 {
4005 NUM_ELEMENTS(j_encodings),
4006 j_encodings,
4007 NUM_ELEMENTS(j_charsets),
4008 j_charsets,
4009 "jis_roman",
4010 "JIS_X_1983"
4011 },
4012 {
4013 NUM_ELEMENTS(sc_encodings),
4014 sc_encodings,
4015 NUM_ELEMENTS(sc_charsets),
4016 sc_charsets,
4017 "gb_roman",
4018 "GB_2312-80"
4019 },
4020 {
4021 NUM_ELEMENTS(tc_encodings),
4022 tc_encodings,
4023 NUM_ELEMENTS(tc_charsets),
4024 tc_charsets,
4025 "cns_roman",
4026 "BIG5"
4027 },
4028 {
4029 NUM_ELEMENTS(k_encodings),
4030 k_encodings,
4031 NUM_ELEMENTS(k_charsets),
4032 k_charsets,
4033 "ks_roman",
4034 "KS_X_1992"
4035 }
4036};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004037#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004038
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039struct prt_ps_resource_S
4040{
4041 char_u name[64];
4042 char_u filename[MAXPATHL + 1];
4043 int type;
4044 char_u title[256];
4045 char_u version[256];
4046};
4047
4048/* Types of PS resource file currently used */
4049#define PRT_RESOURCE_TYPE_PROCSET (0)
4050#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004051#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052
4053/* The PS prolog file version number has to match - if the prolog file is
4054 * updated, increment the number in the file and here. Version checking was
4055 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004056 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057 * Table of VIM and prolog versions:
4058 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004059 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004061 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004063#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004064#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065
4066/* String versions of PS resource types - indexed by constants above so don't
4067 * re-order!
4068 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004069static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070{
4071 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004072 "encoding",
4073 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004074};
4075
4076/* Strings to look for in a PS resource file */
4077#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4078#define PRT_RESOURCE_RESOURCE "Resource-"
4079#define PRT_RESOURCE_PROCSET "ProcSet"
4080#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004081#define PRT_RESOURCE_CMAP "CMap"
4082
4083
4084/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4085 * read more comments. */
4086#define PRT_DSC_MISC_TYPE (-1)
4087#define PRT_DSC_TITLE_TYPE (1)
4088#define PRT_DSC_VERSION_TYPE (2)
4089#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4090
4091#define PRT_DSC_TITLE "%%Title:"
4092#define PRT_DSC_VERSION "%%Version:"
4093#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4094
4095struct prt_dsc_comment_S
4096{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004097 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004098 int len;
4099 int type;
4100};
4101
4102struct prt_dsc_line_S
4103{
4104 int type;
4105 char_u *string;
4106 int len;
4107};
4108
4109
4110#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4111struct prt_dsc_comment_S prt_dsc_table[] =
4112{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004113 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004114 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004115 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004116 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004117 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004118};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119
4120static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4121static void prt_write_file __ARGS((char_u *buffer));
4122static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4123static void prt_write_string __ARGS((char *s));
4124static void prt_write_int __ARGS((int i));
4125static void prt_write_boolean __ARGS((int b));
4126static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4127static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4128static void prt_write_real __ARGS((double val, int prec));
4129static void prt_def_var __ARGS((char *name, double value, int prec));
4130static void prt_flush_buffer __ARGS((void));
4131static void prt_resource_name __ARGS((char_u *filename));
4132static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4133static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4134static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4135static void prt_dsc_start __ARGS((void));
4136static void prt_dsc_noarg __ARGS((char *comment));
4137static void prt_dsc_textline __ARGS((char *comment, char *text));
4138static void prt_dsc_text __ARGS((char *comment, char *text));
4139static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4140static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4141static void prt_dsc_docmedia __ARGS((char *paper_name, double width, double height, double weight, char *colour, char *type));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004142static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4143static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144static float to_device_units __ARGS((int idx, double physsize, int def_number));
4145static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4146static void prt_font_metrics __ARGS((int font_scale));
4147static int prt_get_cpl __ARGS((void));
4148static int prt_get_lpp __ARGS((void));
4149static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004150static int prt_resfile_next_line __ARGS((void));
4151static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4152static int prt_resfile_skip_nonws __ARGS((int offset));
4153static int prt_resfile_skip_ws __ARGS((int offset));
4154static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4155#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004156static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004157static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4158static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4159static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4160#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161
4162/*
4163 * Variables for the output PostScript file.
4164 */
4165static FILE *prt_ps_fd;
4166static int prt_file_error;
4167static char_u *prt_ps_file_name = NULL;
4168
4169/*
4170 * Various offsets and dimensions in default PostScript user space (points).
4171 * Used for text positioning calculations
4172 */
4173static float prt_page_width;
4174static float prt_page_height;
4175static float prt_left_margin;
4176static float prt_right_margin;
4177static float prt_top_margin;
4178static float prt_bottom_margin;
4179static float prt_line_height;
4180static float prt_first_line_height;
4181static float prt_char_width;
4182static float prt_number_width;
4183static float prt_bgcol_offset;
4184static float prt_pos_x_moveto = 0.0;
4185static float prt_pos_y_moveto = 0.0;
4186
4187/*
4188 * Various control variables used to decide when and how to change the
4189 * PostScript graphics state.
4190 */
4191static int prt_need_moveto;
4192static int prt_do_moveto;
4193static int prt_need_font;
4194static int prt_font;
4195static int prt_need_underline;
4196static int prt_underline;
4197static int prt_do_underline;
4198static int prt_need_fgcol;
4199static int prt_fgcol;
4200static int prt_need_bgcol;
4201static int prt_do_bgcol;
4202static int prt_bgcol;
4203static int prt_new_bgcol;
4204static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004205static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004207static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208
4209/*
4210 * Variables controlling physical printing.
4211 */
4212static int prt_media;
4213static int prt_portrait;
4214static int prt_num_copies;
4215static int prt_duplex;
4216static int prt_tumble;
4217static int prt_collate;
4218
4219/*
4220 * Buffers used when generating PostScript output
4221 */
4222static char_u prt_line_buffer[257];
4223static garray_T prt_ps_buffer;
4224
4225# ifdef FEAT_MBYTE
4226static int prt_do_conv;
4227static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004228
4229static int prt_out_mbyte;
4230static int prt_custom_cmap;
4231static char prt_cmap[80];
4232static int prt_use_courier;
4233static int prt_in_ascii;
4234static int prt_half_width;
4235static char *prt_ascii_encoding;
4236static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237# endif
4238
4239 static void
4240prt_write_file_raw_len(buffer, bytes)
4241 char_u *buffer;
4242 int bytes;
4243{
4244 if (!prt_file_error
4245 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4246 != (size_t)bytes)
4247 {
4248 EMSG(_("E455: Error writing to PostScript output file"));
4249 prt_file_error = TRUE;
4250 }
4251}
4252
4253 static void
4254prt_write_file(buffer)
4255 char_u *buffer;
4256{
4257 prt_write_file_len(buffer, STRLEN(buffer));
4258}
4259
4260 static void
4261prt_write_file_len(buffer, bytes)
4262 char_u *buffer;
4263 int bytes;
4264{
4265#ifdef EBCDIC
4266 ebcdic2ascii(buffer, bytes);
4267#endif
4268 prt_write_file_raw_len(buffer, bytes);
4269}
4270
4271/*
4272 * Write a string.
4273 */
4274 static void
4275prt_write_string(s)
4276 char *s;
4277{
4278 sprintf((char *)prt_line_buffer, "%s", s);
4279 prt_write_file(prt_line_buffer);
4280}
4281
4282/*
4283 * Write an int and a space.
4284 */
4285 static void
4286prt_write_int(i)
4287 int i;
4288{
4289 sprintf((char *)prt_line_buffer, "%d ", i);
4290 prt_write_file(prt_line_buffer);
4291}
4292
4293/*
4294 * Write a boolean and a space.
4295 */
4296 static void
4297prt_write_boolean(b)
4298 int b;
4299{
4300 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4301 prt_write_file(prt_line_buffer);
4302}
4303
4304/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004305 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 */
4307 static void
4308prt_def_font(new_name, encoding, height, font)
4309 char *new_name;
4310 char *encoding;
4311 int height;
4312 char *font;
4313{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004314 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4315 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004317#ifdef FEAT_MBYTE
4318 if (prt_out_mbyte)
4319 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4320 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4321 else
4322#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4324 new_name, height, new_name);
4325 prt_write_file(prt_line_buffer);
4326}
4327
Bram Moolenaar8299df92004-07-10 09:47:34 +00004328#ifdef FEAT_MBYTE
4329/*
4330 * Write a line to define the CID font.
4331 */
4332 static void
4333prt_def_cidfont(new_name, height, cidfont)
4334 char *new_name;
4335 int height;
4336 char *cidfont;
4337{
4338 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4339 new_name, prt_cmap, cidfont);
4340 prt_write_file(prt_line_buffer);
4341 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4342 new_name);
4343 prt_write_file(prt_line_buffer);
4344}
4345
4346/*
4347 * Write a line to define a duplicate of a CID font
4348 */
4349 static void
4350prt_dup_cidfont(original_name, new_name)
4351 char *original_name;
4352 char *new_name;
4353{
4354 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4355 prt_write_file(prt_line_buffer);
4356}
4357#endif
4358
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359/*
4360 * Convert a real value into an integer and fractional part as integers, with
4361 * the fractional part being in the range [0,10^precision). The fractional part
4362 * is also rounded based on the precision + 1'th fractional digit.
4363 */
4364 static void
4365prt_real_bits(real, precision, pinteger, pfraction)
4366 double real;
4367 int precision;
4368 int *pinteger;
4369 int *pfraction;
4370{
4371 int i;
4372 int integer;
4373 float fraction;
4374
4375 integer = (int)real;
4376 fraction = (float)(real - integer);
4377 if (real < (double)integer)
4378 fraction = -fraction;
4379 for (i = 0; i < precision; i++)
4380 fraction *= 10.0;
4381
4382 *pinteger = integer;
4383 *pfraction = (int)(fraction + 0.5);
4384}
4385
4386/*
4387 * Write a real and a space. Save bytes if real value has no fractional part!
4388 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4389 * what decimal point character to use, but PS always requires a '.'.
4390 */
4391 static void
4392prt_write_real(val, prec)
4393 double val;
4394 int prec;
4395{
4396 int integer;
4397 int fraction;
4398
4399 prt_real_bits(val, prec, &integer, &fraction);
4400 /* Emit integer part */
4401 sprintf((char *)prt_line_buffer, "%d", integer);
4402 prt_write_file(prt_line_buffer);
4403 /* Only emit fraction if necessary */
4404 if (fraction != 0)
4405 {
4406 /* Remove any trailing zeros */
4407 while ((fraction % 10) == 0)
4408 {
4409 prec--;
4410 fraction /= 10;
4411 }
4412 /* Emit fraction left padded with zeros */
4413 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
4414 prt_write_file(prt_line_buffer);
4415 }
4416 sprintf((char *)prt_line_buffer, " ");
4417 prt_write_file(prt_line_buffer);
4418}
4419
4420/*
4421 * Write a line to define a numeric variable.
4422 */
4423 static void
4424prt_def_var(name, value, prec)
4425 char *name;
4426 double value;
4427 int prec;
4428{
4429 sprintf((char *)prt_line_buffer, "/%s ", name);
4430 prt_write_file(prt_line_buffer);
4431 prt_write_real(value, prec);
4432 sprintf((char *)prt_line_buffer, "d\n");
4433 prt_write_file(prt_line_buffer);
4434}
4435
4436/* Convert size from font space to user space at current font scale */
4437#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
4438
4439 static void
4440prt_flush_buffer()
4441{
4442 if (prt_ps_buffer.ga_len > 0)
4443 {
4444 /* Any background color must be drawn first */
4445 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
4446 {
4447 int r, g, b;
4448
4449 if (prt_do_moveto)
4450 {
4451 prt_write_real(prt_pos_x_moveto, 2);
4452 prt_write_real(prt_pos_y_moveto, 2);
4453 prt_write_string("m\n");
4454 prt_do_moveto = FALSE;
4455 }
4456
4457 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004458 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459 prt_write_real(prt_line_height, 2);
4460
4461 /* Lastly add the color of the background */
4462 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
4463 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
4464 b = prt_new_bgcol & 0xff;
4465 prt_write_real(r / 255.0, 3);
4466 prt_write_real(g / 255.0, 3);
4467 prt_write_real(b / 255.0, 3);
4468 prt_write_string("bg\n");
4469 }
4470 /* Draw underlines before the text as it makes it slightly easier to
4471 * find the starting point.
4472 */
4473 if (prt_do_underline)
4474 {
4475 if (prt_do_moveto)
4476 {
4477 prt_write_real(prt_pos_x_moveto, 2);
4478 prt_write_real(prt_pos_y_moveto, 2);
4479 prt_write_string("m\n");
4480 prt_do_moveto = FALSE;
4481 }
4482
Bram Moolenaar8299df92004-07-10 09:47:34 +00004483 /* Underline length of text run */
4484 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 prt_write_string("ul\n");
4486 }
4487 /* Draw the text
4488 * Note: we write text out raw - EBCDIC conversion is handled in the
4489 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004490#ifdef FEAT_MBYTE
4491 if (prt_out_mbyte)
4492 prt_write_string("<");
4493 else
4494#endif
4495 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004497#ifdef FEAT_MBYTE
4498 if (prt_out_mbyte)
4499 prt_write_string(">");
4500 else
4501#endif
4502 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 /* Add a moveto if need be and use the appropriate show procedure */
4504 if (prt_do_moveto)
4505 {
4506 prt_write_real(prt_pos_x_moveto, 2);
4507 prt_write_real(prt_pos_y_moveto, 2);
4508 /* moveto and a show */
4509 prt_write_string("ms\n");
4510 prt_do_moveto = FALSE;
4511 }
4512 else /* Simple show */
4513 prt_write_string("s\n");
4514
4515 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004516 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517 }
4518}
4519
4520static char_u *resource_filename;
4521
4522 static void
4523prt_resource_name(filename)
4524 char_u *filename;
4525{
4526 if (STRLEN(filename) >= MAXPATHL)
4527 *resource_filename = NUL;
4528 else
4529 STRCPY(resource_filename, filename);
4530}
4531
4532 static int
4533prt_find_resource(name, resource)
4534 char *name;
4535 struct prt_ps_resource_S *resource;
4536{
4537 char_u buffer[MAXPATHL + 1];
4538
4539 STRCPY(resource->name, name);
4540 /* Look for named resource file in runtimepath */
4541 STRCPY(buffer, "print");
4542 add_pathsep(buffer);
4543 STRCAT(buffer, name);
4544 STRCAT(buffer, ".ps");
4545 resource_filename = resource->filename;
4546 *resource_filename = NUL;
4547 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
4548 && resource->filename[0] != NUL);
4549}
4550
4551/* PS CR and LF characters have platform independent values */
4552#define PSLF (0x0a)
4553#define PSCR (0x0d)
4554
Bram Moolenaar8299df92004-07-10 09:47:34 +00004555/* Static buffer to read initial comments in a resource file, some can have a
4556 * couple of KB of comments! */
4557#define PRT_FILE_BUFFER_LEN (2048)
4558struct prt_resfile_buffer_S
4559{
4560 char_u buffer[PRT_FILE_BUFFER_LEN];
4561 int len;
4562 int line_start;
4563 int line_end;
4564};
4565
4566static struct prt_resfile_buffer_S prt_resfile;
4567
4568 static int
4569prt_resfile_next_line()
4570{
4571 int index;
4572
4573 /* Move to start of next line and then find end of line */
4574 index = prt_resfile.line_end + 1;
4575 while (index < prt_resfile.len)
4576 {
4577 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
4578 != PSCR)
4579 break;
4580 index++;
4581 }
4582 prt_resfile.line_start = index;
4583
4584 while (index < prt_resfile.len)
4585 {
4586 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
4587 == PSCR)
4588 break;
4589 index++;
4590 }
4591 prt_resfile.line_end = index;
4592
4593 return (index < prt_resfile.len);
4594}
4595
4596 static int
4597prt_resfile_strncmp(offset, string, len)
4598 int offset;
4599 char *string;
4600 int len;
4601{
4602 /* Force not equal if string is longer than remainder of line */
4603 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
4604 return 1;
4605
4606 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
4607 string, len);
4608}
4609
4610 static int
4611prt_resfile_skip_nonws(offset)
4612 int offset;
4613{
4614 int index;
4615
4616 index = prt_resfile.line_start + offset;
4617 while (index < prt_resfile.line_end)
4618 {
4619 if (isspace(prt_resfile.buffer[index]))
4620 return index - prt_resfile.line_start;
4621 index++;
4622 }
4623 return -1;
4624}
4625
4626 static int
4627prt_resfile_skip_ws(offset)
4628 int offset;
4629{
4630 int index;
4631
4632 index = prt_resfile.line_start + offset;
4633 while (index < prt_resfile.line_end)
4634 {
4635 if (!isspace(prt_resfile.buffer[index]))
4636 return index - prt_resfile.line_start;
4637 index++;
4638 }
4639 return -1;
4640}
4641
4642/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
4643 * if a DSC comment is found, else false */
4644 static int
4645prt_next_dsc(p_dsc_line)
4646 struct prt_dsc_line_S *p_dsc_line;
4647{
4648 int comment;
4649 int offset;
4650
4651 /* Move to start of next line */
4652 if (!prt_resfile_next_line())
4653 return FALSE;
4654
4655 /* DSC comments always start %% */
4656 if (prt_resfile_strncmp(0, "%%", 2) != 0)
4657 return FALSE;
4658
4659 /* Find type of DSC comment */
4660 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
4661 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
4662 prt_dsc_table[comment].len) == 0)
4663 break;
4664
4665 if (comment != NUM_ELEMENTS(prt_dsc_table))
4666 {
4667 /* Return type of comment */
4668 p_dsc_line->type = prt_dsc_table[comment].type;
4669 offset = prt_dsc_table[comment].len;
4670 }
4671 else
4672 {
4673 /* Unrecognised DSC comment, skip to ws after comment leader */
4674 p_dsc_line->type = PRT_DSC_MISC_TYPE;
4675 offset = prt_resfile_skip_nonws(0);
4676 if (offset == -1)
4677 return FALSE;
4678 }
4679
4680 /* Skip ws to comment value */
4681 offset = prt_resfile_skip_ws(offset);
4682 if (offset == -1)
4683 return FALSE;
4684
4685 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
4686 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
4687
4688 return TRUE;
4689}
4690
4691/* Improved hand crafted parser to get the type, title, and version number of a
4692 * PS resource file so the file details can be added to the DSC header comments.
4693 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 static int
4695prt_open_resource(resource)
4696 struct prt_ps_resource_S *resource;
4697{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004698 int offset;
4699 int seen_all;
4700 int seen_title;
4701 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004703 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704
4705 fd_resource = mch_fopen((char *)resource->filename, READBIN);
4706 if (fd_resource == NULL)
4707 {
4708 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
4709 return FALSE;
4710 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004711 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712
4713 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004714 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
4715 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716 if (ferror(fd_resource))
4717 {
4718 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4719 resource->filename);
4720 fclose(fd_resource);
4721 return FALSE;
4722 }
4723
Bram Moolenaar8299df92004-07-10 09:47:34 +00004724 prt_resfile.line_end = -1;
4725 prt_resfile.line_start = 0;
4726 if (!prt_resfile_next_line())
4727 return FALSE;
4728
4729 offset = 0;
4730
4731 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
4732 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 {
4734 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
4735 resource->filename);
4736 fclose(fd_resource);
4737 return FALSE;
4738 }
4739
4740 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004741 offset += STRLEN(PRT_RESOURCE_HEADER);
4742 offset = prt_resfile_skip_nonws(offset);
4743 if (offset == -1)
4744 return FALSE;
4745 offset = prt_resfile_skip_ws(offset);
4746 if (offset == -1)
4747 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748
Bram Moolenaar8299df92004-07-10 09:47:34 +00004749 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
4750 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751 {
4752 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4753 resource->filename);
4754 fclose(fd_resource);
4755 return FALSE;
4756 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004757 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758
4759 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004760 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
4761 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004763 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
4764 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004766 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
4767 STRLEN(PRT_RESOURCE_CMAP)) == 0)
4768 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004769 else
4770 {
4771 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4772 resource->filename);
4773 fclose(fd_resource);
4774 return FALSE;
4775 }
4776
Bram Moolenaar8299df92004-07-10 09:47:34 +00004777 /* Look for title and version of resource */
4778 resource->title[0] = '\0';
4779 resource->version[0] = '\0';
4780 seen_title = FALSE;
4781 seen_version = FALSE;
4782 seen_all = FALSE;
4783 while (!seen_all && prt_next_dsc(&dsc_line))
4784 {
4785 switch (dsc_line.type)
4786 {
4787 case PRT_DSC_TITLE_TYPE:
4788 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
4789 resource->title[dsc_line.len] = '\0';
4790 seen_title = TRUE;
4791 if (seen_version)
4792 seen_all = TRUE;
4793 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794
Bram Moolenaar8299df92004-07-10 09:47:34 +00004795 case PRT_DSC_VERSION_TYPE:
4796 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
4797 resource->version[dsc_line.len] = '\0';
4798 seen_version = TRUE;
4799 if (seen_title)
4800 seen_all = TRUE;
4801 break;
4802
4803 case PRT_DSC_ENDCOMMENTS_TYPE:
4804 /* Wont find title or resource after this comment, stop searching */
4805 seen_all = TRUE;
4806 break;
4807
4808 case PRT_DSC_MISC_TYPE:
4809 /* Not interested in whatever comment this line had */
4810 break;
4811 }
4812 }
4813
4814 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815 {
4816 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4817 resource->filename);
4818 fclose(fd_resource);
4819 return FALSE;
4820 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821
4822 fclose(fd_resource);
4823
4824 return TRUE;
4825}
4826
4827 static int
4828prt_check_resource(resource, version)
4829 struct prt_ps_resource_S *resource;
4830 char_u *version;
4831{
4832 /* Version number m.n should match, the revision number does not matter */
4833 if (STRNCMP(resource->version, version, STRLEN(version)))
4834 {
4835 EMSG2(_("E621: \"%s\" resource file has wrong version"),
4836 resource->name);
4837 return FALSE;
4838 }
4839
4840 /* Other checks to be added as needed */
4841 return TRUE;
4842}
4843
4844 static void
4845prt_dsc_start()
4846{
4847 prt_write_string("%!PS-Adobe-3.0\n");
4848}
4849
4850 static void
4851prt_dsc_noarg(comment)
4852 char *comment;
4853{
4854 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
4855 prt_write_file(prt_line_buffer);
4856}
4857
4858 static void
4859prt_dsc_textline(comment, text)
4860 char *comment;
4861 char *text;
4862{
4863 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
4864 prt_write_file(prt_line_buffer);
4865}
4866
4867 static void
4868prt_dsc_text(comment, text)
4869 char *comment;
4870 char *text;
4871{
4872 /* TODO - should scan 'text' for any chars needing escaping! */
4873 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
4874 prt_write_file(prt_line_buffer);
4875}
4876
4877#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
4878
4879 static void
4880prt_dsc_ints(comment, count, ints)
4881 char *comment;
4882 int count;
4883 int *ints;
4884{
4885 int i;
4886
4887 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
4888 prt_write_file(prt_line_buffer);
4889
4890 for (i = 0; i < count; i++)
4891 {
4892 sprintf((char *)prt_line_buffer, " %d", ints[i]);
4893 prt_write_file(prt_line_buffer);
4894 }
4895
4896 prt_write_string("\n");
4897}
4898
4899 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00004900prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 char *comment; /* if NULL add to previous */
4902 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004903 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 if (comment != NULL)
4906 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
4907 else
4908 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
4909 prt_write_file(prt_line_buffer);
4910
Bram Moolenaar8299df92004-07-10 09:47:34 +00004911 sprintf((char *)prt_line_buffer, " %s\n", string);
4912 prt_write_file(prt_line_buffer);
4913}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914
Bram Moolenaar8299df92004-07-10 09:47:34 +00004915 static void
4916prt_dsc_font_resource(resource, ps_font)
4917 char *resource;
4918 struct prt_ps_font_S *ps_font;
4919{
4920 int i;
4921
4922 prt_dsc_resources(resource, "font",
4923 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
4924 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
4925 if (ps_font->ps_fontname[i] != NULL)
4926 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927}
4928
4929 static void
4930prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
4931 int duplex;
4932 int tumble;
4933 int collate;
4934 int color;
4935 int num_copies;
4936{
4937 /* Only output the comment if we need to.
4938 * Note: tumble is ignored if we are not duplexing
4939 */
4940 if (!(duplex || collate || color || (num_copies > 1)))
4941 return;
4942
4943 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
4944 prt_write_file(prt_line_buffer);
4945
4946 if (duplex)
4947 {
4948 prt_write_string(" duplex");
4949 if (tumble)
4950 prt_write_string("(tumble)");
4951 }
4952 if (collate)
4953 prt_write_string(" collate");
4954 if (color)
4955 prt_write_string(" color");
4956 if (num_copies > 1)
4957 {
4958 prt_write_string(" numcopies(");
4959 /* Note: no space wanted so dont use prt_write_int() */
4960 sprintf((char *)prt_line_buffer, "%d", num_copies);
4961 prt_write_file(prt_line_buffer);
4962 prt_write_string(")");
4963 }
4964 prt_write_string("\n");
4965}
4966
4967 static void
4968prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
4969 char *paper_name;
4970 double width;
4971 double height;
4972 double weight;
4973 char *colour;
4974 char *type;
4975{
4976 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
4977 prt_write_file(prt_line_buffer);
4978 prt_write_real(width, 2);
4979 prt_write_real(height, 2);
4980 prt_write_real(weight, 2);
4981 if (colour == NULL)
4982 prt_write_string("()");
4983 else
4984 prt_write_string(colour);
4985 prt_write_string(" ");
4986 if (type == NULL)
4987 prt_write_string("()");
4988 else
4989 prt_write_string(type);
4990 prt_write_string("\n");
4991}
4992
4993 void
4994mch_print_cleanup()
4995{
4996#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004997 if (prt_out_mbyte)
4998 {
4999 int i;
5000
5001 /* Free off all CID font names created, but first clear duplicate
5002 * pointers to the same string (when the same font is used for more than
5003 * one style).
5004 */
5005 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5006 {
5007 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5008 vim_free(prt_ps_mb_font.ps_fontname[i]);
5009 prt_ps_mb_font.ps_fontname[i] = NULL;
5010 }
5011 }
5012
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013 if (prt_do_conv)
5014 {
5015 convert_setup(&prt_conv, NULL, NULL);
5016 prt_do_conv = FALSE;
5017 }
5018#endif
5019 if (prt_ps_fd != NULL)
5020 {
5021 fclose(prt_ps_fd);
5022 prt_ps_fd = NULL;
5023 prt_file_error = FALSE;
5024 }
5025 if (prt_ps_file_name != NULL)
5026 {
5027 vim_free(prt_ps_file_name);
5028 prt_ps_file_name = NULL;
5029 }
5030}
5031
5032 static float
5033to_device_units(idx, physsize, def_number)
5034 int idx;
5035 double physsize;
5036 int def_number;
5037{
5038 float ret;
5039 int u;
5040 int nr;
5041
5042 u = prt_get_unit(idx);
5043 if (u == PRT_UNIT_NONE)
5044 {
5045 u = PRT_UNIT_PERC;
5046 nr = def_number;
5047 }
5048 else
5049 nr = printer_opts[idx].number;
5050
5051 switch (u)
5052 {
5053 case PRT_UNIT_INCH:
5054 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5055 break;
5056 case PRT_UNIT_MM:
5057 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5058 break;
5059 case PRT_UNIT_POINT:
5060 ret = (float)nr;
5061 break;
5062 case PRT_UNIT_PERC:
5063 default:
5064 ret = (float)(physsize * nr) / 100;
5065 break;
5066 }
5067
5068 return ret;
5069}
5070
5071/*
5072 * Calculate margins for given width and height from printoptions settings.
5073 */
5074 static void
5075prt_page_margins(width, height, left, right, top, bottom)
5076 double width;
5077 double height;
5078 double *left;
5079 double *right;
5080 double *top;
5081 double *bottom;
5082{
5083 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5084 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5085 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5086 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5087}
5088
5089 static void
5090prt_font_metrics(font_scale)
5091 int font_scale;
5092{
5093 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005094 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095}
5096
5097
5098 static int
5099prt_get_cpl()
5100{
5101 if (prt_use_number())
5102 {
5103 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005104#ifdef FEAT_MBYTE
5105 /* If we are outputting multi-byte characters then line numbers will be
5106 * printed with half width characters
5107 */
5108 if (prt_out_mbyte)
5109 prt_number_width /= 2;
5110#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 prt_left_margin += prt_number_width;
5112 }
5113 else
5114 prt_number_width = 0.0;
5115
5116 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5117}
5118
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005119#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005120 static int
5121prt_build_cid_fontname(font, name, name_len)
5122 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005123 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005124 int name_len;
5125{
5126 char *fontname;
5127
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005128 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005129 if (fontname == NULL)
5130 return FALSE;
5131 STRNCPY(fontname, name, name_len);
5132 fontname[name_len] = '\0';
5133 prt_ps_mb_font.ps_fontname[font] = fontname;
5134
5135 return TRUE;
5136}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005137#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005138
Bram Moolenaar071d4272004-06-13 20:20:40 +00005139/*
5140 * Get number of lines of text that fit on a page (excluding the header).
5141 */
5142 static int
5143prt_get_lpp()
5144{
5145 int lpp;
5146
5147 /*
5148 * Calculate offset to lower left corner of background rect based on actual
5149 * font height (based on its bounding box) and the line height, handling the
5150 * case where the font height can exceed the line height.
5151 */
5152 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005153 prt_ps_font->bbox_min_y);
5154 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005155 {
5156 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005157 (1000.0 - (prt_ps_font->bbox_max_y -
5158 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005159 }
5160
5161 /* Get height for topmost line based on background rect offset. */
5162 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5163
5164 /* Calculate lpp */
5165 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5166
5167 /* Adjust top margin if there is a header */
5168 prt_top_margin -= prt_line_height * prt_header_height();
5169
5170 return lpp - prt_header_height();
5171}
5172
Bram Moolenaar8299df92004-07-10 09:47:34 +00005173#ifdef FEAT_MBYTE
5174 static int
5175prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005176 char *p_encoding;
5177 struct prt_ps_mbfont_S *p_cmap;
5178 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005179{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005180 int mbenc;
5181 int enc_len;
5182 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005183
5184 *pp_mbenc = NULL;
5185 /* Look for recognised encoding */
5186 enc_len = STRLEN(p_encoding);
5187 p_mbenc = p_cmap->encodings;
5188 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5189 {
5190 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5191 {
5192 *pp_mbenc = p_mbenc;
5193 return TRUE;
5194 }
5195 p_mbenc++;
5196 }
5197 return FALSE;
5198}
5199
5200 static int
5201prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005202 char *p_charset;
5203 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005204 struct prt_ps_charset_S **pp_mbchar;
5205{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005206 int mbchar;
5207 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005208 struct prt_ps_charset_S *p_mbchar;
5209
5210 /* Look for recognised character set, using default if one is not given */
5211 if (*p_charset == NUL)
5212 p_charset = p_cmap->defcs;
5213 char_len = STRLEN(p_charset);
5214 p_mbchar = p_cmap->charsets;
5215 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5216 {
5217 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5218 {
5219 *pp_mbchar = p_mbchar;
5220 return TRUE;
5221 }
5222 p_mbchar++;
5223 }
5224 return FALSE;
5225}
5226#endif
5227
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228/*ARGSUSED*/
5229 int
5230mch_print_init(psettings, jobname, forceit)
5231 prt_settings_T *psettings;
5232 char_u *jobname;
5233 int forceit;
5234{
5235 int i;
5236 char *paper_name;
5237 int paper_strlen;
5238 int fontsize;
5239 char_u *p;
5240 double left;
5241 double right;
5242 double top;
5243 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005244#ifdef FEAT_MBYTE
5245 int cmap;
5246 int pmcs_len;
5247 char_u *p_encoding;
5248 struct prt_ps_encoding_S *p_mbenc;
5249 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005250 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005251#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005252
5253#if 0
5254 /*
5255 * TODO:
5256 * If "forceit" is false: pop up a dialog to select:
5257 * - printer name
5258 * - copies
5259 * - collated/uncollated
5260 * - duplex off/long side/short side
5261 * - paper size
5262 * - portrait/landscape
5263 * - font size
5264 *
5265 * If "forceit" is true: use the default printer and settings
5266 */
5267 if (forceit)
5268 s_pd.Flags |= PD_RETURNDEFAULT;
5269#endif
5270
5271 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005272 * Set up font and encoding.
5273 */
5274#ifdef FEAT_MBYTE
5275 p_encoding = enc_skip(p_penc);
5276 if (*p_encoding == NUL)
5277 p_encoding = enc_skip(p_enc);
5278
5279 /* Look for recognised multi-byte coding, and if the charset is recognised.
5280 * This is to cope with the fact that various unicode encodings are
5281 * supported in more than one of CJK. */
5282 p_mbenc = NULL;
5283 p_mbenc_first = NULL;
5284 p_mbchar = NULL;
5285 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005286 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5287 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005288 {
5289 if (p_mbenc_first == NULL)
5290 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005291 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5292 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005293 break;
5294 }
5295
5296 /* Use first encoding matched if no charset matched */
5297 if (p_mbchar == NULL && p_mbenc_first != NULL)
5298 p_mbenc = p_mbenc_first;
5299
5300 prt_out_mbyte = (p_mbenc != NULL);
5301 if (prt_out_mbyte)
5302 {
5303 /* Build CMap name - will be same for all multi-byte fonts used */
5304 prt_cmap[0] = '\0';
5305
5306 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5307
5308 if (!prt_custom_cmap)
5309 {
5310 /* Check encoding and character set are compatible */
5311 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5312 {
5313 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5314 return FALSE;
5315 }
5316
5317 /* Add charset name if not empty */
5318 if (p_mbchar->cmap_charset != NULL)
5319 {
5320 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5321 STRCAT(prt_cmap, "-");
5322 }
5323 }
5324 else
5325 {
5326 /* Add custom CMap character set name */
5327 pmcs_len = STRLEN(p_pmcs);
5328 if (pmcs_len == 0)
5329 {
5330 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5331 return FALSE;
5332 }
5333 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5334 prt_cmap[pmcs_len] = '\0';
5335 STRCAT(prt_cmap, "-");
5336 }
5337
5338 /* CMap name ends with (optional) encoding name and -H for horizontal */
5339 if (p_mbenc->cmap_encoding != NULL)
5340 {
5341 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5342 STRCAT(prt_cmap, "-");
5343 }
5344 STRCAT(prt_cmap, "H");
5345
5346 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5347 {
5348 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5349 return FALSE;
5350 }
5351
5352 /* Derive CID font names with fallbacks if not defined */
5353 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5354 mbfont_opts[OPT_MBFONT_REGULAR].string,
5355 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5356 return FALSE;
5357 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5358 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5359 mbfont_opts[OPT_MBFONT_BOLD].string,
5360 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5361 return FALSE;
5362 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5363 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5364 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
5365 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
5366 return FALSE;
5367 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
5368 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005369 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
5370 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005371 return FALSE;
5372
5373 /* Check if need to use Courier for ASCII code range, and if so pick up
5374 * the encoding to use */
5375 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
5376 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
5377 if (prt_use_courier)
5378 {
5379 /* Use national ASCII variant unless ASCII wanted */
5380 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
5381 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
5382 prt_ascii_encoding = "ascii";
5383 else
5384 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
5385 }
5386
5387 prt_ps_font = &prt_ps_mb_font;
5388 }
5389 else
5390#endif
5391 {
5392#ifdef FEAT_MBYTE
5393 prt_use_courier = FALSE;
5394#endif
5395 prt_ps_font = &prt_ps_courier_font;
5396 }
5397
5398 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005399 * Find the size of the paper and set the margins.
5400 */
5401 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
5402 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
5403 if (printer_opts[OPT_PRINT_PAPER].present)
5404 {
5405 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
5406 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
5407 }
5408 else
5409 {
5410 paper_name = "A4";
5411 paper_strlen = 2;
5412 }
5413 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
5414 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
5415 && STRNICMP(prt_mediasize[i].name, paper_name,
5416 paper_strlen) == 0)
5417 break;
5418 if (i == PRT_MEDIASIZE_LEN)
5419 i = 0;
5420 prt_media = i;
5421
5422 /*
5423 * Set PS pagesize based on media dimensions and print orientation.
5424 * Note: Media and page sizes have defined meanings in PostScript and should
5425 * be kept distinct. Media is the paper (or transparency, or ...) that is
5426 * printed on, whereas the page size is the area that the PostScript
5427 * interpreter renders into.
5428 */
5429 if (prt_portrait)
5430 {
5431 prt_page_width = prt_mediasize[i].width;
5432 prt_page_height = prt_mediasize[i].height;
5433 }
5434 else
5435 {
5436 prt_page_width = prt_mediasize[i].height;
5437 prt_page_height = prt_mediasize[i].width;
5438 }
5439
5440 /*
5441 * Set PS page margins based on the PS pagesize, not the mediasize - this
5442 * needs to be done before the cpl and lpp are calculated.
5443 */
5444 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
5445 &bottom);
5446 prt_left_margin = (float)left;
5447 prt_right_margin = (float)right;
5448 prt_top_margin = (float)top;
5449 prt_bottom_margin = (float)bottom;
5450
5451 /*
5452 * Set up the font size.
5453 */
5454 fontsize = PRT_PS_DEFAULT_FONTSIZE;
5455 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
5456 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
5457 fontsize = atoi((char *)p + 2);
5458 prt_font_metrics(fontsize);
5459
5460 /*
5461 * Return the number of characters per line, and lines per page for the
5462 * generic print code.
5463 */
5464 psettings->chars_per_line = prt_get_cpl();
5465 psettings->lines_per_page = prt_get_lpp();
5466
5467 /* Catch margin settings that leave no space for output! */
5468 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
5469 return FAIL;
5470
5471 /*
5472 * Sort out the number of copies to be printed. PS by default will do
5473 * uncollated copies for you, so once we know how many uncollated copies are
5474 * wanted cache it away and lie to the generic code that we only want one
5475 * uncollated copy.
5476 */
5477 psettings->n_collated_copies = 1;
5478 psettings->n_uncollated_copies = 1;
5479 prt_num_copies = 1;
5480 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
5481 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
5482 if (prt_collate)
5483 {
5484 /* TODO: Get number of collated copies wanted. */
5485 psettings->n_collated_copies = 1;
5486 }
5487 else
5488 {
5489 /* TODO: Get number of uncollated copies wanted and update the cached
5490 * count.
5491 */
5492 prt_num_copies = 1;
5493 }
5494
5495 psettings->jobname = jobname;
5496
5497 /*
5498 * Set up printer duplex and tumble based on Duplex option setting - default
5499 * is long sided duplex printing (i.e. no tumble).
5500 */
5501 prt_duplex = TRUE;
5502 prt_tumble = FALSE;
5503 psettings->duplex = 1;
5504 if (printer_opts[OPT_PRINT_DUPLEX].present)
5505 {
5506 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
5507 {
5508 prt_duplex = FALSE;
5509 psettings->duplex = 0;
5510 }
5511 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
5512 == 0)
5513 prt_tumble = TRUE;
5514 }
5515
5516 /* For now user abort not supported */
5517 psettings->user_abort = 0;
5518
5519 /* If the user didn't specify a file name, use a temp file. */
5520 if (psettings->outfile == NULL)
5521 {
5522 prt_ps_file_name = vim_tempname('p');
5523 if (prt_ps_file_name == NULL)
5524 {
5525 EMSG(_(e_notmp));
5526 return FAIL;
5527 }
5528 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
5529 }
5530 else
5531 {
5532 p = expand_env_save(psettings->outfile);
5533 if (p != NULL)
5534 {
5535 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
5536 vim_free(p);
5537 }
5538 }
5539 if (prt_ps_fd == NULL)
5540 {
5541 EMSG(_("E324: Can't open PostScript output file"));
5542 mch_print_cleanup();
5543 return FAIL;
5544 }
5545
Bram Moolenaar8299df92004-07-10 09:47:34 +00005546 prt_bufsiz = psettings->chars_per_line;
5547#ifdef FEAT_MBYTE
5548 if (prt_out_mbyte)
5549 prt_bufsiz *= 2;
5550#endif
5551 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552
5553 prt_page_num = 0;
5554
5555 prt_attribute_change = FALSE;
5556 prt_need_moveto = FALSE;
5557 prt_need_font = FALSE;
5558 prt_need_fgcol = FALSE;
5559 prt_need_bgcol = FALSE;
5560 prt_need_underline = FALSE;
5561
5562 prt_file_error = FALSE;
5563
5564 return OK;
5565}
5566
5567 static int
5568prt_add_resource(resource)
5569 struct prt_ps_resource_S *resource;
5570{
5571 FILE* fd_resource;
5572 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005573 size_t bytes_read;
5574
5575 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5576 if (fd_resource == NULL)
5577 {
5578 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
5579 return FALSE;
5580 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005581 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
5582 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583
5584 prt_dsc_textline("BeginDocument", (char *)resource->filename);
5585
5586 for (;;)
5587 {
5588 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
5589 sizeof(resource_buffer), fd_resource);
5590 if (ferror(fd_resource))
5591 {
5592 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5593 resource->filename);
5594 fclose(fd_resource);
5595 return FALSE;
5596 }
5597 if (bytes_read == 0)
5598 break;
5599 prt_write_file_raw_len(resource_buffer, bytes_read);
5600 if (prt_file_error)
5601 {
5602 fclose(fd_resource);
5603 return FALSE;
5604 }
5605 }
5606 fclose(fd_resource);
5607
5608 prt_dsc_noarg("EndDocument");
5609
5610 prt_dsc_noarg("EndResource");
5611
5612 return TRUE;
5613}
5614
5615 int
5616mch_print_begin(psettings)
5617 prt_settings_T *psettings;
5618{
5619 time_t now;
5620 int bbox[4];
5621 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 double left;
5623 double right;
5624 double top;
5625 double bottom;
5626 struct prt_ps_resource_S res_prolog;
5627 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005628 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005629 char_u *p_encoding;
5630#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005631 struct prt_ps_resource_S res_cidfont;
5632 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633#endif
5634
5635 /*
5636 * PS DSC Header comments - no PS code!
5637 */
5638 prt_dsc_start();
5639 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005640 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005641 STRCPY(buffer, "Unknown");
5642 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643 prt_dsc_textline("Creator", VIM_VERSION_LONG);
5644 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
5645 now = time(NULL);
5646 p_time = ctime(&now);
5647 /* Note: ctime() adds a \n so we have to remove it :-( */
5648 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
5649 prt_dsc_textline("CreationDate", p_time);
5650 prt_dsc_textline("DocumentData", "Clean8Bit");
5651 prt_dsc_textline("Orientation", "Portrait");
5652 prt_dsc_atend("Pages");
5653 prt_dsc_textline("PageOrder", "Ascend");
5654 /* The bbox does not change with orientation - it is always in the default
5655 * user coordinate system! We have to recalculate right and bottom
5656 * coordinates based on the font metrics for the bbox to be accurate. */
5657 prt_page_margins(prt_mediasize[prt_media].width,
5658 prt_mediasize[prt_media].height,
5659 &left, &right, &top, &bottom);
5660 bbox[0] = (int)left;
5661 if (prt_portrait)
5662 {
5663 /* In portrait printing the fixed point is the top left corner so we
5664 * derive the bbox from that point. We have the expected cpl chars
5665 * across the media and lpp lines down the media.
5666 */
5667 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
5668 * prt_line_height);
5669 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
5670 + 0.5);
5671 bbox[3] = (int)(top + 0.5);
5672 }
5673 else
5674 {
5675 /* In landscape printing the fixed point is the bottom left corner so we
5676 * derive the bbox from that point. We have lpp chars across the media
5677 * and cpl lines up the media.
5678 */
5679 bbox[1] = (int)bottom;
5680 bbox[2] = (int)(left + ((psettings->lines_per_page
5681 + prt_header_height()) * prt_line_height) + 0.5);
5682 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
5683 + 0.5);
5684 }
5685 prt_dsc_ints("BoundingBox", 4, bbox);
5686 /* The media width and height does not change with landscape printing! */
5687 prt_dsc_docmedia(prt_mediasize[prt_media].name,
5688 prt_mediasize[prt_media].width,
5689 prt_mediasize[prt_media].height,
5690 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005691 /* Define fonts needed */
5692#ifdef FEAT_MBYTE
5693 if (!prt_out_mbyte || prt_use_courier)
5694#endif
5695 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
5696#ifdef FEAT_MBYTE
5697 if (prt_out_mbyte)
5698 {
5699 prt_dsc_font_resource((prt_use_courier ? NULL
5700 : "DocumentNeededResources"), &prt_ps_mb_font);
5701 if (!prt_custom_cmap)
5702 prt_dsc_resources(NULL, "cmap", prt_cmap);
5703 }
5704#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705
Bram Moolenaar8299df92004-07-10 09:47:34 +00005706 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 if (!prt_find_resource("prolog", &res_prolog))
5708 {
5709 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
5710 return FALSE;
5711 }
5712 if (!prt_open_resource(&res_prolog))
5713 return FALSE;
5714 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
5715 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005716#ifdef FEAT_MBYTE
5717 if (prt_out_mbyte)
5718 {
5719 /* Look for required version of multi-byte printing procset */
5720 if (!prt_find_resource("cidfont", &res_cidfont))
5721 {
5722 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
5723 return FALSE;
5724 }
5725 if (!prt_open_resource(&res_cidfont))
5726 return FALSE;
5727 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
5728 return FALSE;
5729 }
5730#endif
5731
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 /* Find an encoding to use for printing.
5733 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
5734 * that cannot be found then default to "latin1".
5735 * Note: VIM specific encoding header is always skipped.
5736 */
5737#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005738 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005741 p_encoding = enc_skip(p_penc);
5742 if (*p_encoding == NUL
5743 || !prt_find_resource((char *)p_encoding, &res_encoding))
5744 {
5745 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005747 int props;
5748
5749 p_encoding = enc_skip(p_enc);
5750 props = enc_canon_props(p_encoding);
5751 if (!(props & ENC_8BIT)
5752 || !prt_find_resource((char *)p_encoding, &res_encoding))
5753 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005754#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005755 {
5756 /* Use latin1 as default printing encoding */
5757 p_encoding = (char_u *)"latin1";
5758 if (!prt_find_resource((char *)p_encoding, &res_encoding))
5759 {
5760 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5761 p_encoding);
5762 return FALSE;
5763 }
5764 }
5765 }
5766 if (!prt_open_resource(&res_encoding))
5767 return FALSE;
5768 /* For the moment there are no checks on encoding resource files to
5769 * perform */
5770#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005772 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005773 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005774 p_encoding = enc_skip(p_penc);
5775 if (*p_encoding == NUL)
5776 p_encoding = enc_skip(p_enc);
5777 if (prt_use_courier)
5778 {
5779 /* Include ASCII range encoding vector */
5780 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
5781 {
5782 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005783 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005784 return FALSE;
5785 }
5786 if (!prt_open_resource(&res_encoding))
5787 return FALSE;
5788 /* For the moment there are no checks on encoding resource files to
5789 * perform */
5790 }
5791 }
5792
5793 prt_conv.vc_type = CONV_NONE;
5794 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
5795 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005796 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
5797 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005798 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00005799 p_encoding);
5800 return FALSE;
5801 }
5802 prt_do_conv = TRUE;
5803 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005804 prt_do_conv = prt_conv.vc_type != CONV_NONE;
5805
5806 if (prt_out_mbyte && prt_custom_cmap)
5807 {
5808 /* Find user supplied CMap */
5809 if (!prt_find_resource(prt_cmap, &res_cmap))
5810 {
5811 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005812 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005813 return FALSE;
5814 }
5815 if (!prt_open_resource(&res_cmap))
5816 return FALSE;
5817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005818#endif
5819
5820 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 STRCPY(buffer, res_prolog.title);
5822 STRCAT(buffer, " ");
5823 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005824 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
5825#ifdef FEAT_MBYTE
5826 if (prt_out_mbyte)
5827 {
5828 STRCPY(buffer, res_cidfont.title);
5829 STRCAT(buffer, " ");
5830 STRCAT(buffer, res_cidfont.version);
5831 prt_dsc_resources(NULL, "procset", buffer);
5832
5833 if (prt_custom_cmap)
5834 {
5835 STRCPY(buffer, res_cmap.title);
5836 STRCAT(buffer, " ");
5837 STRCAT(buffer, res_cmap.version);
5838 prt_dsc_resources(NULL, "cmap", buffer);
5839 }
5840 }
5841 if (!prt_out_mbyte || prt_use_courier)
5842#endif
5843 {
5844 STRCPY(buffer, res_encoding.title);
5845 STRCAT(buffer, " ");
5846 STRCAT(buffer, res_encoding.version);
5847 prt_dsc_resources(NULL, "encoding", buffer);
5848 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
5850#ifdef FEAT_SYN_HL
5851 psettings->do_syntax
5852#else
5853 0
5854#endif
5855 , prt_num_copies);
5856 prt_dsc_noarg("EndComments");
5857
5858 /*
5859 * PS Document page defaults
5860 */
5861 prt_dsc_noarg("BeginDefaults");
5862
5863 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005864#ifdef FEAT_MBYTE
5865 if (!prt_out_mbyte || prt_use_courier)
5866#endif
5867 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
5868#ifdef FEAT_MBYTE
5869 if (prt_out_mbyte)
5870 {
5871 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
5872 &prt_ps_mb_font);
5873 if (!prt_custom_cmap)
5874 prt_dsc_resources(NULL, "cmap", prt_cmap);
5875 }
5876#endif
5877
Bram Moolenaar071d4272004-06-13 20:20:40 +00005878 /* Paper will be used for all pages */
5879 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
5880
5881 prt_dsc_noarg("EndDefaults");
5882
5883 /*
5884 * PS Document prolog inclusion - all required procsets.
5885 */
5886 prt_dsc_noarg("BeginProlog");
5887
Bram Moolenaar8299df92004-07-10 09:47:34 +00005888 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889 if (!prt_add_resource(&res_prolog))
5890 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005891#ifdef FEAT_MBYTE
5892 if (prt_out_mbyte)
5893 {
5894 /* Add CID font procset, and any user supplied CMap */
5895 if (!prt_add_resource(&res_cidfont))
5896 return FALSE;
5897 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
5898 return FALSE;
5899 }
5900#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901
Bram Moolenaar8299df92004-07-10 09:47:34 +00005902#ifdef FEAT_MBYTE
5903 if (!prt_out_mbyte || prt_use_courier)
5904#endif
5905 /* There will be only one Roman font encoding to be included in the PS
5906 * file. */
5907 if (!prt_add_resource(&res_encoding))
5908 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909
5910 prt_dsc_noarg("EndProlog");
5911
5912 /*
5913 * PS Document setup - must appear after the prolog
5914 */
5915 prt_dsc_noarg("BeginSetup");
5916
5917 /* Device setup - page size and number of uncollated copies */
5918 prt_write_int((int)prt_mediasize[prt_media].width);
5919 prt_write_int((int)prt_mediasize[prt_media].height);
5920 prt_write_int(0);
5921 prt_write_string("sps\n");
5922 prt_write_int(prt_num_copies);
5923 prt_write_string("nc\n");
5924 prt_write_boolean(prt_duplex);
5925 prt_write_boolean(prt_tumble);
5926 prt_write_string("dt\n");
5927 prt_write_boolean(prt_collate);
5928 prt_write_string("c\n");
5929
5930 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005931#ifdef FEAT_MBYTE
5932 if (!prt_out_mbyte || prt_use_courier)
5933 {
5934 /* When using Courier for ASCII range when printing multi-byte, need to
5935 * pick up ASCII encoding to use with it. */
5936 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005937 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005938#endif
5939 prt_dsc_resources("IncludeResource", "font",
5940 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5941 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
5942 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5943 prt_dsc_resources("IncludeResource", "font",
5944 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5945 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
5946 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5947 prt_dsc_resources("IncludeResource", "font",
5948 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5949 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
5950 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5951 prt_dsc_resources("IncludeResource", "font",
5952 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5953 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
5954 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5955#ifdef FEAT_MBYTE
5956 }
5957 if (prt_out_mbyte)
5958 {
5959 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
5960 * not have an italic form being a western style, so where no font is
5961 * defined for these faces VIM falls back to an existing face.
5962 * Note: if using Courier for the ASCII range then the printout will
5963 * have bold/italic/bolditalic regardless of the setting of printmbfont.
5964 */
5965 prt_dsc_resources("IncludeResource", "font",
5966 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5967 if (!prt_custom_cmap)
5968 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5969 prt_def_cidfont("CF0", (int)prt_line_height,
5970 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5971
5972 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
5973 {
5974 prt_dsc_resources("IncludeResource", "font",
5975 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5976 if (!prt_custom_cmap)
5977 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5978 prt_def_cidfont("CF1", (int)prt_line_height,
5979 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5980 }
5981 else
5982 /* Use ROMAN for BOLD */
5983 prt_dup_cidfont("CF0", "CF1");
5984
5985 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
5986 {
5987 prt_dsc_resources("IncludeResource", "font",
5988 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5989 if (!prt_custom_cmap)
5990 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5991 prt_def_cidfont("CF2", (int)prt_line_height,
5992 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5993 }
5994 else
5995 /* Use ROMAN for OBLIQUE */
5996 prt_dup_cidfont("CF0", "CF2");
5997
5998 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
5999 {
6000 prt_dsc_resources("IncludeResource", "font",
6001 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6002 if (!prt_custom_cmap)
6003 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6004 prt_def_cidfont("CF3", (int)prt_line_height,
6005 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6006 }
6007 else
6008 /* Use BOLD for BOLDOBLIQUE */
6009 prt_dup_cidfont("CF1", "CF3");
6010 }
6011#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006012
6013 /* Misc constant vars used for underlining and background rects */
6014 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006015 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006016 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006017 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006018 prt_def_var("BO", prt_bgcol_offset, 2);
6019
6020 prt_dsc_noarg("EndSetup");
6021
6022 /* Fail if any problems writing out to the PS file */
6023 return !prt_file_error;
6024}
6025
6026 void
6027mch_print_end(psettings)
6028 prt_settings_T *psettings;
6029{
6030 prt_dsc_noarg("Trailer");
6031
6032 /*
6033 * Output any info we don't know in toto until we finish
6034 */
6035 prt_dsc_ints("Pages", 1, &prt_page_num);
6036
6037 prt_dsc_noarg("EOF");
6038
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006039 /* Write CTRL-D to close serial communication link if used.
6040 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006041 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006042
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043 if (!prt_file_error && psettings->outfile == NULL
6044 && !got_int && !psettings->user_abort)
6045 {
6046 /* Close the file first. */
6047 if (prt_ps_fd != NULL)
6048 {
6049 fclose(prt_ps_fd);
6050 prt_ps_fd = NULL;
6051 }
6052 prt_message((char_u *)_("Sending to printer..."));
6053
6054 /* Not printing to a file: use 'printexpr' to print the file. */
6055 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6056 EMSG(_("E365: Failed to print PostScript file"));
6057 else
6058 prt_message((char_u *)_("Print job sent."));
6059 }
6060
6061 mch_print_cleanup();
6062}
6063
6064 int
6065mch_print_end_page()
6066{
6067 prt_flush_buffer();
6068
6069 prt_write_string("re sp\n");
6070
6071 prt_dsc_noarg("PageTrailer");
6072
6073 return !prt_file_error;
6074}
6075
6076/*ARGSUSED*/
6077 int
6078mch_print_begin_page(str)
6079 char_u *str;
6080{
6081 int page_num[2];
6082
6083 prt_page_num++;
6084
6085 page_num[0] = page_num[1] = prt_page_num;
6086 prt_dsc_ints("Page", 2, page_num);
6087
6088 prt_dsc_noarg("BeginPageSetup");
6089
Bram Moolenaar8299df92004-07-10 09:47:34 +00006090 prt_write_string("sv\n0 g\n");
6091#ifdef FEAT_MBYTE
6092 prt_in_ascii = !prt_out_mbyte;
6093 if (prt_out_mbyte)
6094 prt_write_string("CF0 sf\n");
6095 else
6096#endif
6097 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006098 prt_fgcol = PRCOLOR_BLACK;
6099 prt_bgcol = PRCOLOR_WHITE;
6100 prt_font = PRT_PS_FONT_ROMAN;
6101
6102 /* Set up page transformation for landscape printing. */
6103 if (!prt_portrait)
6104 {
6105 prt_write_int(-((int)prt_mediasize[prt_media].width));
6106 prt_write_string("sl\n");
6107 }
6108
6109 prt_dsc_noarg("EndPageSetup");
6110
6111 /* We have reset the font attributes, force setting them again. */
6112 curr_bg = (long_u)0xffffffff;
6113 curr_fg = (long_u)0xffffffff;
6114 curr_bold = MAYBE;
6115
6116 return !prt_file_error;
6117}
6118
6119 int
6120mch_print_blank_page()
6121{
6122 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6123}
6124
6125static float prt_pos_x = 0;
6126static float prt_pos_y = 0;
6127
6128 void
6129mch_print_start_line(margin, page_line)
6130 int margin;
6131 int page_line;
6132{
6133 prt_pos_x = prt_left_margin;
6134 if (margin)
6135 prt_pos_x -= prt_number_width;
6136
6137 prt_pos_y = prt_top_margin - prt_first_line_height -
6138 page_line * prt_line_height;
6139
6140 prt_attribute_change = TRUE;
6141 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006142#ifdef FEAT_MBYTE
6143 prt_half_width = FALSE;
6144#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145}
6146
6147/*ARGSUSED*/
6148 int
6149mch_print_text_out(p, len)
6150 char_u *p;
6151 int len;
6152{
6153 int need_break;
6154 char_u ch;
6155 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006156 float char_width;
6157 float next_pos;
6158#ifdef FEAT_MBYTE
6159 int in_ascii;
6160 int half_width;
6161#endif
6162
6163 char_width = prt_char_width;
6164
6165#ifdef FEAT_MBYTE
6166 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6167 * CJKV font to do what VIM is doing here - use a Roman font for characters
6168 * in the ASCII range, and the origingal CID font for everything else.
6169 * The problem is that GhostScript still (as of 8.13) does not support
6170 * rearranged fonts even though they have been documented by Adobe for 7
6171 * years! If they ever do, a lot of this code will disappear.
6172 */
6173 if (prt_use_courier)
6174 {
6175 in_ascii = (len == 1 && *p < 0x80);
6176 if (prt_in_ascii)
6177 {
6178 if (!in_ascii)
6179 {
6180 /* No longer in ASCII range - need to switch font */
6181 prt_in_ascii = FALSE;
6182 prt_need_font = TRUE;
6183 prt_attribute_change = TRUE;
6184 }
6185 }
6186 else if (in_ascii)
6187 {
6188 /* Now in ASCII range - need to switch font */
6189 prt_in_ascii = TRUE;
6190 prt_need_font = TRUE;
6191 prt_attribute_change = TRUE;
6192 }
6193 }
6194 if (prt_out_mbyte)
6195 {
6196 half_width = ((*mb_ptr2cells)(p) == 1);
6197 if (half_width)
6198 char_width /= 2;
6199 if (prt_half_width)
6200 {
6201 if (!half_width)
6202 {
6203 prt_half_width = FALSE;
6204 prt_pos_x += prt_char_width/4;
6205 prt_need_moveto = TRUE;
6206 prt_attribute_change = TRUE;
6207 }
6208 }
6209 else if (half_width)
6210 {
6211 prt_half_width = TRUE;
6212 prt_pos_x += prt_char_width/4;
6213 prt_need_moveto = TRUE;
6214 prt_attribute_change = TRUE;
6215 }
6216 }
6217#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218
6219 /* Output any required changes to the graphics state, after flushing any
6220 * text buffered so far.
6221 */
6222 if (prt_attribute_change)
6223 {
6224 prt_flush_buffer();
6225 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006226 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227
6228 if (prt_need_moveto)
6229 {
6230 prt_pos_x_moveto = prt_pos_x;
6231 prt_pos_y_moveto = prt_pos_y;
6232 prt_do_moveto = TRUE;
6233
6234 prt_need_moveto = FALSE;
6235 }
6236 if (prt_need_font)
6237 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006238#ifdef FEAT_MBYTE
6239 if (!prt_in_ascii)
6240 prt_write_string("CF");
6241 else
6242#endif
6243 prt_write_string("F");
6244 prt_write_int(prt_font);
6245 prt_write_string("sf\n");
6246 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006247 }
6248 if (prt_need_fgcol)
6249 {
6250 int r, g, b;
6251 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6252 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6253 b = prt_fgcol & 0xff;
6254
6255 prt_write_real(r / 255.0, 3);
6256 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006258 else
6259 {
6260 prt_write_real(g / 255.0, 3);
6261 prt_write_real(b / 255.0, 3);
6262 prt_write_string("r\n");
6263 }
6264 prt_need_fgcol = FALSE;
6265 }
6266
6267 if (prt_bgcol != PRCOLOR_WHITE)
6268 {
6269 prt_new_bgcol = prt_bgcol;
6270 if (prt_need_bgcol)
6271 prt_do_bgcol = TRUE;
6272 }
6273 else
6274 prt_do_bgcol = FALSE;
6275 prt_need_bgcol = FALSE;
6276
6277 if (prt_need_underline)
6278 prt_do_underline = prt_underline;
6279 prt_need_underline = FALSE;
6280
6281 prt_attribute_change = FALSE;
6282 }
6283
6284#ifdef FEAT_MBYTE
6285 if (prt_do_conv)
6286 {
6287 /* Convert from multi-byte to 8-bit encoding */
6288 p = string_convert(&prt_conv, p, &len);
6289 if (p == NULL)
6290 p = (char_u *)"";
6291 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006292
Bram Moolenaar8299df92004-07-10 09:47:34 +00006293 if (prt_out_mbyte)
6294 {
6295 /* Multi-byte character strings are represented more efficiently as hex
6296 * strings when outputting clean 8 bit PS.
6297 */
6298 do
6299 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006300 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006301 ga_append(&prt_ps_buffer, ch);
6302 ch = prt_hexchar[(*p) & 0xf];
6303 ga_append(&prt_ps_buffer, ch);
6304 p++;
6305 }
6306 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307 }
6308 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006309#endif
6310 {
6311 /* Add next character to buffer of characters to output.
6312 * Note: One printed character may require several PS characters to
6313 * represent it, but we only count them as one printed character.
6314 */
6315 ch = *p;
6316 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6317 {
6318 /* Convert non-printing characters to either their escape or octal
6319 * sequence, ensures PS sent over a serial line does not interfere
6320 * with the comms protocol. Note: For EBCDIC we need to write out
6321 * the escape sequences as ASCII codes!
6322 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6323 */
6324 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6325 switch (ch)
6326 {
6327 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6328 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6329 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6330 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6331 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6332 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6333 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6334 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6335
6336 default:
6337 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6338#ifdef EBCDIC
6339 ebcdic2ascii(ch_buff, 3);
6340#endif
6341 ga_append(&prt_ps_buffer, ch_buff[0]);
6342 ga_append(&prt_ps_buffer, ch_buff[1]);
6343 ga_append(&prt_ps_buffer, ch_buff[2]);
6344 break;
6345 }
6346 }
6347 else
6348 ga_append(&prt_ps_buffer, ch);
6349 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350
6351#ifdef FEAT_MBYTE
6352 /* Need to free any translated characters */
6353 if (prt_do_conv && (*p != NUL))
6354 vim_free(p);
6355#endif
6356
Bram Moolenaar8299df92004-07-10 09:47:34 +00006357 prt_text_run += char_width;
6358 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359
Bram Moolenaar8299df92004-07-10 09:47:34 +00006360 /* The downside of fp - use relative error on right margin check */
6361 next_pos = prt_pos_x + prt_char_width;
6362 need_break = (next_pos > prt_right_margin) &&
6363 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006364
6365 if (need_break)
6366 prt_flush_buffer();
6367
6368 return need_break;
6369}
6370
6371 void
6372mch_print_set_font(iBold, iItalic, iUnderline)
6373 int iBold;
6374 int iItalic;
6375 int iUnderline;
6376{
6377 int font = 0;
6378
6379 if (iBold)
6380 font |= 0x01;
6381 if (iItalic)
6382 font |= 0x02;
6383
6384 if (font != prt_font)
6385 {
6386 prt_font = font;
6387 prt_attribute_change = TRUE;
6388 prt_need_font = TRUE;
6389 }
6390 if (prt_underline != iUnderline)
6391 {
6392 prt_underline = iUnderline;
6393 prt_attribute_change = TRUE;
6394 prt_need_underline = TRUE;
6395 }
6396}
6397
6398 void
6399mch_print_set_bg(bgcol)
6400 long_u bgcol;
6401{
6402 prt_bgcol = bgcol;
6403 prt_attribute_change = TRUE;
6404 prt_need_bgcol = TRUE;
6405}
6406
6407 void
6408mch_print_set_fg(fgcol)
6409 long_u fgcol;
6410{
6411 if (fgcol != (long_u)prt_fgcol)
6412 {
6413 prt_fgcol = fgcol;
6414 prt_attribute_change = TRUE;
6415 prt_need_fgcol = TRUE;
6416 }
6417}
6418
6419# endif /*FEAT_POSTSCRIPT*/
6420#endif /*FEAT_PRINTER*/
6421
6422#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6423 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
6424static char *get_locale_val __ARGS((int what));
6425
6426 static char *
6427get_locale_val(what)
6428 int what;
6429{
6430 char *loc;
6431
6432 /* Obtain the locale value from the libraries. For DJGPP this is
6433 * redefined and it doesn't use the arguments. */
6434 loc = setlocale(what, NULL);
6435
6436# if defined(__BORLANDC__)
6437 if (loc != NULL)
6438 {
6439 char_u *p;
6440
6441 /* Borland returns something like "LC_CTYPE=<name>\n"
6442 * Let's try to fix that bug here... */
6443 p = vim_strchr(loc, '=');
6444 if (p != NULL)
6445 {
6446 loc = ++p;
6447 while (*p != NUL) /* remove trailing newline */
6448 {
6449 if (*p < ' ')
6450 {
6451 *p = NUL;
6452 break;
6453 }
6454 ++p;
6455 }
6456 }
6457 }
6458# endif
6459
6460 return loc;
6461}
6462#endif
6463
6464
6465#ifdef WIN32
6466/*
6467 * On MS-Windows locale names are strings like "German_Germany.1252", but
6468 * gettext expects "de". Try to translate one into another here for a few
6469 * supported languages.
6470 */
6471 static char_u *
6472gettext_lang(char_u *name)
6473{
6474 int i;
6475 static char *(mtable[]) = {
6476 "afrikaans", "af",
6477 "czech", "cs",
6478 "dutch", "nl",
6479 "german", "de",
6480 "english_united kingdom", "en_GB",
6481 "spanish", "es",
6482 "french", "fr",
6483 "italian", "it",
6484 "japanese", "ja",
6485 "korean", "ko",
6486 "norwegian", "no",
6487 "polish", "pl",
6488 "russian", "ru",
6489 "slovak", "sk",
6490 "swedish", "sv",
6491 "ukrainian", "uk",
6492 "chinese_china", "zh_CN",
6493 "chinese_taiwan", "zh_TW",
6494 NULL};
6495
6496 for (i = 0; mtable[i] != NULL; i += 2)
6497 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
6498 return mtable[i + 1];
6499 return name;
6500}
6501#endif
6502
6503#if defined(FEAT_MULTI_LANG) || defined(PROTO)
6504/*
6505 * Obtain the current messages language. Used to set the default for
6506 * 'helplang'. May return NULL or an empty string.
6507 */
6508 char_u *
6509get_mess_lang()
6510{
6511 char_u *p;
6512
6513# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
6514# if defined(LC_MESSAGES)
6515 p = (char_u *)get_locale_val(LC_MESSAGES);
6516# else
6517 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
6518 * may be set to the LCID number. */
6519 p = (char_u *)get_locale_val(LC_ALL);
6520# endif
6521# else
6522 p = mch_getenv((char_u *)"LC_ALL");
6523 if (p == NULL || *p == NUL)
6524 {
6525 p = mch_getenv((char_u *)"LC_MESSAGES");
6526 if (p == NULL || *p == NUL)
6527 p = mch_getenv((char_u *)"LANG");
6528 }
6529# endif
6530# ifdef WIN32
6531 p = gettext_lang(p);
6532# endif
6533 return p;
6534}
6535#endif
6536
Bram Moolenaardef9e822004-12-31 20:58:58 +00006537/* Complicated #if; matches with where get_mess_env() is used below. */
6538#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6539 && defined(LC_MESSAGES))) \
6540 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6541 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
6542 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543static char_u *get_mess_env __ARGS((void));
6544
6545/*
6546 * Get the language used for messages from the environment.
6547 */
6548 static char_u *
6549get_mess_env()
6550{
6551 char_u *p;
6552
6553 p = mch_getenv((char_u *)"LC_ALL");
6554 if (p == NULL || *p == NUL)
6555 {
6556 p = mch_getenv((char_u *)"LC_MESSAGES");
6557 if (p == NULL || *p == NUL)
6558 {
6559 p = mch_getenv((char_u *)"LANG");
6560 if (p != NULL && VIM_ISDIGIT(*p))
6561 p = NULL; /* ignore something like "1043" */
6562# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6563 if (p == NULL || *p == NUL)
6564 p = (char_u *)get_locale_val(LC_CTYPE);
6565# endif
6566 }
6567 }
6568 return p;
6569}
6570#endif
6571
6572#if defined(FEAT_EVAL) || defined(PROTO)
6573
6574/*
6575 * Set the "v:lang" variable according to the current locale setting.
6576 * Also do "v:lc_time"and "v:ctype".
6577 */
6578 void
6579set_lang_var()
6580{
6581 char_u *loc;
6582
6583# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6584 loc = (char_u *)get_locale_val(LC_CTYPE);
6585# else
6586 /* setlocale() not supported: use the default value */
6587 loc = (char_u *)"C";
6588# endif
6589 set_vim_var_string(VV_CTYPE, loc, -1);
6590
6591 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
6592 * back to LC_CTYPE if it's empty. */
6593# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
6594 loc = (char_u *)get_locale_val(LC_MESSAGES);
6595# else
6596 loc = get_mess_env();
6597# endif
6598 set_vim_var_string(VV_LANG, loc, -1);
6599
6600# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6601 loc = (char_u *)get_locale_val(LC_TIME);
6602# endif
6603 set_vim_var_string(VV_LC_TIME, loc, -1);
6604}
6605#endif
6606
6607#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6608 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
6609/*
6610 * ":language": Set the language (locale).
6611 */
6612 void
6613ex_language(eap)
6614 exarg_T *eap;
6615{
6616 char *loc;
6617 char_u *p;
6618 char_u *name;
6619 int what = LC_ALL;
6620 char *whatstr = "";
6621#ifdef LC_MESSAGES
6622# define VIM_LC_MESSAGES LC_MESSAGES
6623#else
6624# define VIM_LC_MESSAGES 6789
6625#endif
6626
6627 name = eap->arg;
6628
6629 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
6630 * Allow abbreviation, but require at least 3 characters to avoid
6631 * confusion with a two letter language name "me" or "ct". */
6632 p = skiptowhite(eap->arg);
6633 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
6634 {
6635 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
6636 {
6637 what = VIM_LC_MESSAGES;
6638 name = skipwhite(p);
6639 whatstr = "messages ";
6640 }
6641 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
6642 {
6643 what = LC_CTYPE;
6644 name = skipwhite(p);
6645 whatstr = "ctype ";
6646 }
6647 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
6648 {
6649 what = LC_TIME;
6650 name = skipwhite(p);
6651 whatstr = "time ";
6652 }
6653 }
6654
6655 if (*name == NUL)
6656 {
6657#ifndef LC_MESSAGES
6658 if (what == VIM_LC_MESSAGES)
6659 p = get_mess_env();
6660 else
6661#endif
6662 p = (char_u *)setlocale(what, NULL);
6663 if (p == NULL || *p == NUL)
6664 p = (char_u *)"Unknown";
6665 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
6666 }
6667 else
6668 {
6669#ifndef LC_MESSAGES
6670 if (what == VIM_LC_MESSAGES)
6671 loc = "";
6672 else
6673#endif
6674 loc = setlocale(what, (char *)name);
6675 if (loc == NULL)
6676 EMSG2(_("E197: Cannot set language to \"%s\""), name);
6677 else
6678 {
6679#ifdef HAVE_NL_MSG_CAT_CNTR
6680 /* Need to do this for GNU gettext, otherwise cached translations
6681 * will be used again. */
6682 extern int _nl_msg_cat_cntr;
6683
6684 ++_nl_msg_cat_cntr;
6685#endif
6686 /* Reset $LC_ALL, otherwise it would overrule everyting. */
6687 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
6688
6689 if (what != LC_TIME)
6690 {
6691 /* Tell gettext() what to translate to. It apparently doesn't
6692 * use the currently effective locale. Also do this when
6693 * FEAT_GETTEXT isn't defined, so that shell commands use this
6694 * value. */
6695 if (what == LC_ALL)
6696 vim_setenv((char_u *)"LANG", name);
6697 if (what != LC_CTYPE)
6698 {
6699 char_u *mname;
6700#ifdef WIN32
6701 mname = gettext_lang(name);
6702#else
6703 mname = name;
6704#endif
6705 vim_setenv((char_u *)"LC_MESSAGES", mname);
6706#ifdef FEAT_MULTI_LANG
6707 set_helplang_default(mname);
6708#endif
6709 }
6710
6711 /* Set $LC_CTYPE, because it overrules $LANG, and
6712 * gtk_set_locale() calls setlocale() again. gnome_init()
6713 * sets $LC_CTYPE to "en_US" (that's a bug!). */
6714 if (what != VIM_LC_MESSAGES)
6715 vim_setenv((char_u *)"LC_CTYPE", name);
6716# ifdef FEAT_GUI_GTK
6717 /* Let GTK know what locale we're using. Not sure this is
6718 * really needed... */
6719 if (gui.in_use)
6720 (void)gtk_set_locale();
6721# endif
6722 }
6723
6724# ifdef FEAT_EVAL
6725 /* Set v:lang, v:lc_time and v:ctype to the final result. */
6726 set_lang_var();
6727# endif
6728 }
6729 }
6730}
6731
6732# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6733/*
6734 * Function given to ExpandGeneric() to obtain the possible arguments of the
6735 * ":language" command.
6736 */
6737/*ARGSUSED*/
6738 char_u *
6739get_lang_arg(xp, idx)
6740 expand_T *xp;
6741 int idx;
6742{
6743 if (idx == 0)
6744 return (char_u *)"messages";
6745 if (idx == 1)
6746 return (char_u *)"ctype";
6747 if (idx == 2)
6748 return (char_u *)"time";
6749 return NULL;
6750}
6751# endif
6752
6753#endif