blob: 82557febb7f9a0da4b7d034d3409237d318ff551 [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
2342 /* compare dev/ino when possible, it catches symbolic
2343 * links */
2344 (stat_ok && SCRIPT_DEV(current_SID) != -1)
2345 ? (SCRIPT_DEV(current_SID) == st.st_dev
2346 && SCRIPT_INO(current_SID) == st.st_ino) :
2347# endif
2348 fnamecmp(SCRIPT_NAME(current_SID), fname_exp) == 0))
2349 break;
2350 if (current_SID == 0)
2351 {
2352 current_SID = ++last_current_SID;
2353 if (ga_grow(&script_names, (int)(current_SID - script_names.ga_len))
2354 == OK)
2355 {
2356 while (script_names.ga_len < current_SID)
2357 {
2358 SCRIPT_NAME(script_names.ga_len + 1) = NULL;
2359 ++script_names.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 }
2361 SCRIPT_NAME(current_SID) = fname_exp;
2362# ifdef UNIX
2363 if (stat_ok)
2364 {
2365 SCRIPT_DEV(current_SID) = st.st_dev;
2366 SCRIPT_INO(current_SID) = st.st_ino;
2367 }
2368 else
2369 SCRIPT_DEV(current_SID) = -1;
2370# endif
2371 fname_exp = NULL;
2372 }
2373 /* Allocate the local script variables to use for this script. */
2374 new_script_vars(current_SID);
2375 }
2376
2377 /* Don't use local function variables, if called from a function */
2378 save_funccalp = save_funccal();
2379#endif
2380
2381 /*
2382 * Call do_cmdline, which will call getsourceline() to get the lines.
2383 */
2384 do_cmdline(NULL, getsourceline, (void *)&cookie,
2385 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2386
2387 retval = OK;
2388 fclose(cookie.fp);
2389 vim_free(cookie.nextline);
2390#ifdef FEAT_MBYTE
2391 convert_setup(&cookie.conv, NULL, NULL);
2392#endif
2393
2394 if (got_int)
2395 EMSG(_(e_interr));
2396 sourcing_name = save_sourcing_name;
2397 sourcing_lnum = save_sourcing_lnum;
2398#ifdef FEAT_EVAL
2399 current_SID = save_current_SID;
2400 restore_funccal(save_funccalp);
2401#endif
2402 if (p_verbose > 1)
2403 {
2404 msg_str((char_u *)_("finished sourcing %s"), fname);
2405 if (sourcing_name != NULL)
2406 msg_str((char_u *)_("continuing in %s"), sourcing_name);
2407 }
2408#ifdef STARTUPTIME
2409# ifdef HAVE_SNPRINTF
2410 snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2411# else
2412 sprintf(IObuff, "sourcing %s", fname);
2413# endif
2414 time_msg(IObuff, &tv_start);
2415 time_pop(&tv_rel);
2416#endif
2417
2418#ifdef FEAT_EVAL
2419 /*
2420 * After a "finish" in debug mode, need to break at first command of next
2421 * sourced file.
2422 */
2423 if (save_debug_break_level > ex_nesting_level
2424 && debug_break_level == ex_nesting_level)
2425 ++debug_break_level;
2426#endif
2427
2428theend:
2429 vim_free(fname_exp);
2430 return retval;
2431}
2432
2433#if defined(FEAT_EVAL) || defined(PROTO)
2434/*
2435 * ":scriptnames"
2436 */
2437/*ARGSUSED*/
2438 void
2439ex_scriptnames(eap)
2440 exarg_T *eap;
2441{
2442 int i;
2443
2444 for (i = 1; i <= script_names.ga_len && !got_int; ++i)
2445 if (SCRIPT_NAME(i) != NULL)
2446 smsg((char_u *)"%3d: %s", i, SCRIPT_NAME(i));
2447}
2448
2449# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2450/*
2451 * Fix slashes in the list of script names for 'shellslash'.
2452 */
2453 void
2454scriptnames_slash_adjust()
2455{
2456 int i;
2457
2458 for (i = 1; i <= script_names.ga_len; ++i)
2459 if (SCRIPT_NAME(i) != NULL)
2460 slash_adjust(SCRIPT_NAME(i));
2461}
2462# endif
2463
2464/*
2465 * Get a pointer to a script name. Used for ":verbose set".
2466 */
2467 char_u *
2468get_scriptname(id)
2469 scid_T id;
2470{
2471 if (id == SID_MODELINE)
2472 return (char_u *)"modeline";
2473 if (id == SID_CMDARG)
2474 return (char_u *)"--cmd argument";
2475 if (id == SID_CARG)
2476 return (char_u *)"-c argument";
2477 if (id == SID_ENV)
2478 return (char_u *)"environment variable";
2479 return SCRIPT_NAME(id);
2480}
2481#endif
2482
2483#if defined(USE_CR) || defined(PROTO)
2484
2485# if defined(__MSL__) && (__MSL__ >= 22)
2486/*
2487 * Newer version of the Metrowerks library handle DOS and UNIX files
2488 * without help.
2489 * Test with earlier versions, MSL 2.2 is the library supplied with
2490 * Codewarrior Pro 2.
2491 */
2492 char *
2493fgets_cr(s, n, stream)
2494 char *s;
2495 int n;
2496 FILE *stream;
2497{
2498 return fgets(s, n, stream);
2499}
2500# else
2501/*
2502 * Version of fgets() which also works for lines ending in a <CR> only
2503 * (Macintosh format).
2504 * For older versions of the Metrowerks library.
2505 * At least CodeWarrior 9 needed this code.
2506 */
2507 char *
2508fgets_cr(s, n, stream)
2509 char *s;
2510 int n;
2511 FILE *stream;
2512{
2513 int c = 0;
2514 int char_read = 0;
2515
2516 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
2517 {
2518 c = fgetc(stream);
2519 s[char_read++] = c;
2520 /* If the file is in DOS format, we need to skip a NL after a CR. I
2521 * thought it was the other way around, but this appears to work... */
2522 if (c == '\n')
2523 {
2524 c = fgetc(stream);
2525 if (c != '\r')
2526 ungetc(c, stream);
2527 }
2528 }
2529
2530 s[char_read] = 0;
2531 if (char_read == 0)
2532 return NULL;
2533
2534 if (feof(stream) && char_read == 1)
2535 return NULL;
2536
2537 return s;
2538}
2539# endif
2540#endif
2541
2542/*
2543 * Get one full line from a sourced file.
2544 * Called by do_cmdline() when it's called from do_source().
2545 *
2546 * Return a pointer to the line in allocated memory.
2547 * Return NULL for end-of-file or some error.
2548 */
2549/* ARGSUSED */
2550 char_u *
2551getsourceline(c, cookie, indent)
2552 int c; /* not used */
2553 void *cookie;
2554 int indent; /* not used */
2555{
2556 struct source_cookie *sp = (struct source_cookie *)cookie;
2557 char_u *line;
2558 char_u *p, *s;
2559
2560#ifdef FEAT_EVAL
2561 /* If breakpoints have been added/deleted need to check for it. */
2562 if (sp->dbg_tick < debug_tick)
2563 {
2564 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2565 sp->dbg_tick = debug_tick;
2566 }
2567#endif
2568 /*
2569 * Get current line. If there is a read-ahead line, use it, otherwise get
2570 * one now.
2571 */
2572 if (sp->finished)
2573 line = NULL;
2574 else if (sp->nextline == NULL)
2575 line = get_one_sourceline(sp);
2576 else
2577 {
2578 line = sp->nextline;
2579 sp->nextline = NULL;
2580 ++sourcing_lnum;
2581 }
2582
2583 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
2584 * contain the 'C' flag. */
2585 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
2586 {
2587 /* compensate for the one line read-ahead */
2588 --sourcing_lnum;
2589 for (;;)
2590 {
2591 sp->nextline = get_one_sourceline(sp);
2592 if (sp->nextline == NULL)
2593 break;
2594 p = skipwhite(sp->nextline);
2595 if (*p != '\\')
2596 break;
2597 s = alloc((int)(STRLEN(line) + STRLEN(p)));
2598 if (s == NULL) /* out of memory */
2599 break;
2600 STRCPY(s, line);
2601 STRCAT(s, p + 1);
2602 vim_free(line);
2603 line = s;
2604 vim_free(sp->nextline);
2605 }
2606 }
2607
2608#ifdef FEAT_MBYTE
2609 if (line != NULL && sp->conv.vc_type != CONV_NONE)
2610 {
2611 /* Convert the encoding of the script line. */
2612 s = string_convert(&sp->conv, line, NULL);
2613 if (s != NULL)
2614 {
2615 vim_free(line);
2616 line = s;
2617 }
2618 }
2619#endif
2620
2621#ifdef FEAT_EVAL
2622 /* Did we encounter a breakpoint? */
2623 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
2624 {
2625 dbg_breakpoint(sp->fname, sourcing_lnum);
2626 /* Find next breakpoint. */
2627 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2628 sp->dbg_tick = debug_tick;
2629 }
2630#endif
2631
2632 return line;
2633}
2634
2635 static char_u *
2636get_one_sourceline(sp)
2637 struct source_cookie *sp;
2638{
2639 garray_T ga;
2640 int len;
2641 int c;
2642 char_u *buf;
2643#ifdef USE_CRNL
2644 int has_cr; /* CR-LF found */
2645#endif
2646#ifdef USE_CR
2647 char_u *scan;
2648#endif
2649 int have_read = FALSE;
2650
2651 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002652 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653
2654 /*
2655 * Loop until there is a finished line (or end-of-file).
2656 */
2657 sourcing_lnum++;
2658 for (;;)
2659 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002660 /* make room to read at least 120 (more) characters */
2661 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002662 break;
2663 buf = (char_u *)ga.ga_data;
2664
2665#ifdef USE_CR
2666 if (sp->fileformat == EOL_MAC)
2667 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00002668 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2669 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 break;
2671 }
2672 else
2673#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00002674 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2675 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002676 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002677 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678#ifdef USE_CRNL
2679 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
2680 * CTRL-Z by its own, or after a NL. */
2681 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
2682 && sp->fileformat == EOL_DOS
2683 && buf[len - 1] == Ctrl_Z)
2684 {
2685 buf[len - 1] = NUL;
2686 break;
2687 }
2688#endif
2689
2690#ifdef USE_CR
2691 /* If the read doesn't stop on a new line, and there's
2692 * some CR then we assume a Mac format */
2693 if (sp->fileformat == EOL_UNKNOWN)
2694 {
2695 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
2696 sp->fileformat = EOL_MAC;
2697 else
2698 sp->fileformat = EOL_UNIX;
2699 }
2700
2701 if (sp->fileformat == EOL_MAC)
2702 {
2703 scan = vim_strchr(buf, '\r');
2704
2705 if (scan != NULL)
2706 {
2707 *scan = '\n';
2708 if (*(scan + 1) != 0)
2709 {
2710 *(scan + 1) = 0;
2711 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
2712 }
2713 }
2714 len = STRLEN(buf);
2715 }
2716#endif
2717
2718 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 ga.ga_len = len;
2720
2721 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00002722 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 continue;
2724
2725 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
2726 {
2727#ifdef USE_CRNL
2728 has_cr = (len >= 2 && buf[len - 2] == '\r');
2729 if (sp->fileformat == EOL_UNKNOWN)
2730 {
2731 if (has_cr)
2732 sp->fileformat = EOL_DOS;
2733 else
2734 sp->fileformat = EOL_UNIX;
2735 }
2736
2737 if (sp->fileformat == EOL_DOS)
2738 {
2739 if (has_cr) /* replace trailing CR */
2740 {
2741 buf[len - 2] = '\n';
2742 --len;
2743 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744 }
2745 else /* lines like ":map xx yy^M" will have failed */
2746 {
2747 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002748 {
2749 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002751 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 sp->error = TRUE;
2753 sp->fileformat = EOL_UNIX;
2754 }
2755 }
2756#endif
2757 /* The '\n' is escaped if there is an odd number of ^V's just
2758 * before it, first set "c" just before the 'V's and then check
2759 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
2760 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
2761 ;
2762 if ((len & 1) != (c & 1)) /* escaped NL, read more */
2763 {
2764 sourcing_lnum++;
2765 continue;
2766 }
2767
2768 buf[len - 1] = NUL; /* remove the NL */
2769 }
2770
2771 /*
2772 * Check for ^C here now and then, so recursive :so can be broken.
2773 */
2774 line_breakcheck();
2775 break;
2776 }
2777
2778 if (have_read)
2779 return (char_u *)ga.ga_data;
2780
2781 vim_free(ga.ga_data);
2782 return NULL;
2783}
2784
2785/*
2786 * ":scriptencoding": Set encoding conversion for a sourced script.
2787 * Without the multi-byte feature it's simply ignored.
2788 */
2789/*ARGSUSED*/
2790 void
2791ex_scriptencoding(eap)
2792 exarg_T *eap;
2793{
2794#ifdef FEAT_MBYTE
2795 struct source_cookie *sp;
2796 char_u *name;
2797
2798 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
2799 {
2800 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
2801 return;
2802 }
2803
2804 if (*eap->arg != NUL)
2805 {
2806 name = enc_canonize(eap->arg);
2807 if (name == NULL) /* out of memory */
2808 return;
2809 }
2810 else
2811 name = eap->arg;
2812
2813 /* Setup for conversion from the specified encoding to 'encoding'. */
2814 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
2815 convert_setup(&sp->conv, name, p_enc);
2816
2817 if (name != eap->arg)
2818 vim_free(name);
2819#endif
2820}
2821
2822#if defined(FEAT_EVAL) || defined(PROTO)
2823/*
2824 * ":finish": Mark a sourced file as finished.
2825 */
2826 void
2827ex_finish(eap)
2828 exarg_T *eap;
2829{
2830 if (getline_equal(eap->getline, eap->cookie, getsourceline))
2831 do_finish(eap, FALSE);
2832 else
2833 EMSG(_("E168: :finish used outside of a sourced file"));
2834}
2835
2836/*
2837 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
2838 * Also called for a pending finish at the ":endtry" or after returning from
2839 * an extra do_cmdline(). "reanimate" is used in the latter case.
2840 */
2841 void
2842do_finish(eap, reanimate)
2843 exarg_T *eap;
2844 int reanimate;
2845{
2846 int idx;
2847
2848 if (reanimate)
2849 ((struct source_cookie *)getline_cookie(eap->getline,
2850 eap->cookie))->finished = FALSE;
2851
2852 /*
2853 * Cleanup (and inactivate) conditionals, but stop when a try conditional
2854 * not in its finally clause (which then is to be executed next) is found.
2855 * In this case, make the ":finish" pending for execution at the ":endtry".
2856 * Otherwise, finish normally.
2857 */
2858 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
2859 if (idx >= 0)
2860 {
2861 eap->cstack->cs_pending[idx] = CSTP_FINISH;
2862 report_make_pending(CSTP_FINISH, NULL);
2863 }
2864 else
2865 ((struct source_cookie *)getline_cookie(eap->getline,
2866 eap->cookie))->finished = TRUE;
2867}
2868
2869
2870/*
2871 * Return TRUE when a sourced file had the ":finish" command: Don't give error
2872 * message for missing ":endif".
2873 * Return FALSE when not sourcing a file.
2874 */
2875 int
2876source_finished(getline, cookie)
2877 char_u *(*getline) __ARGS((int, void *, int));
2878 void *cookie;
2879{
2880 return (getline_equal(getline, cookie, getsourceline)
2881 && ((struct source_cookie *)getline_cookie(
2882 getline, cookie))->finished);
2883}
2884#endif
2885
2886#if defined(FEAT_LISTCMDS) || defined(PROTO)
2887/*
2888 * ":checktime [buffer]"
2889 */
2890 void
2891ex_checktime(eap)
2892 exarg_T *eap;
2893{
2894 buf_T *buf;
2895 int save_no_check_timestamps = no_check_timestamps;
2896
2897 no_check_timestamps = 0;
2898 if (eap->addr_count == 0) /* default is all buffers */
2899 check_timestamps(FALSE);
2900 else
2901 {
2902 buf = buflist_findnr((int)eap->line2);
2903 if (buf != NULL) /* cannot happen? */
2904 (void)buf_check_timestamp(buf, FALSE);
2905 }
2906 no_check_timestamps = save_no_check_timestamps;
2907}
2908#endif
2909
2910#if defined(FEAT_PRINTER) || defined(PROTO)
2911/*
2912 * Printing code (Machine-independent.)
2913 * To implement printing on a platform, the following functions must be
2914 * defined:
2915 *
2916 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
2917 * Called once. Code should display printer dialogue (if appropriate) and
2918 * determine printer font and margin settings. Reset has_color if the printer
2919 * doesn't support colors at all.
2920 * Returns FAIL to abort.
2921 *
2922 * int mch_print_begin(prt_settings_T *settings)
2923 * Called to start the print job.
2924 * Return FALSE to abort.
2925 *
2926 * int mch_print_begin_page(char_u *msg)
2927 * Called at the start of each page.
2928 * "msg" indicates the progress of the print job, can be NULL.
2929 * Return FALSE to abort.
2930 *
2931 * int mch_print_end_page()
2932 * Called at the end of each page.
2933 * Return FALSE to abort.
2934 *
2935 * int mch_print_blank_page()
2936 * Called to generate a blank page for collated, duplex, multiple copy
2937 * document. Return FALSE to abort.
2938 *
2939 * void mch_print_end(prt_settings_T *psettings)
2940 * Called at normal end of print job.
2941 *
2942 * void mch_print_cleanup()
2943 * Called if print job ends normally or is abandoned. Free any memory, close
2944 * devices and handles. Also called when mch_print_begin() fails, but not
2945 * when mch_print_init() fails.
2946 *
2947 * void mch_print_set_font(int Bold, int Italic, int Underline);
2948 * Called whenever the font style changes.
2949 *
2950 * void mch_print_set_bg(long bgcol);
2951 * Called to set the background color for the following text. Parameter is an
2952 * RGB value.
2953 *
2954 * void mch_print_set_fg(long fgcol);
2955 * Called to set the foreground color for the following text. Parameter is an
2956 * RGB value.
2957 *
2958 * mch_print_start_line(int margin, int page_line)
2959 * Sets the current position at the start of line "page_line".
2960 * If margin is TRUE start in the left margin (for header and line number).
2961 *
2962 * int mch_print_text_out(char_u *p, int len);
2963 * Output one character of text p[len] at the current position.
2964 * Return TRUE if there is no room for another character in the same line.
2965 *
2966 * Note that the generic code has no idea of margins. The machine code should
2967 * simply make the page look smaller! The header and the line numbers are
2968 * printed in the margin.
2969 */
2970
2971#ifdef FEAT_SYN_HL
2972static const long_u cterm_color_8[8] =
2973{
2974 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
2975 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
2976};
2977
2978static const long_u cterm_color_16[16] =
2979{
2980 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
2981 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
2982 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
2983 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
2984};
2985
2986static int current_syn_id;
2987#endif
2988
2989#define PRCOLOR_BLACK (long_u)0
2990#define PRCOLOR_WHITE (long_u)0xFFFFFFL
2991
2992static int curr_italic;
2993static int curr_bold;
2994static int curr_underline;
2995static long_u curr_bg;
2996static long_u curr_fg;
2997static int page_count;
2998
2999/*
3000 * These values determine the print position on a page.
3001 */
3002typedef struct
3003{
3004 int lead_spaces; /* remaining spaces for a TAB */
3005 int print_pos; /* virtual column for computing TABs */
3006 colnr_T column; /* byte column */
3007 linenr_T file_line; /* line nr in the buffer */
3008 long_u bytes_printed; /* bytes printed so far */
3009 int ff; /* seen form feed character */
3010} prt_pos_T;
3011
3012#ifdef FEAT_SYN_HL
3013static long_u darken_rgb __ARGS((long_u rgb));
3014static long_u prt_get_term_color __ARGS((int colorindex));
3015#endif
3016static void prt_set_fg __ARGS((long_u fg));
3017static void prt_set_bg __ARGS((long_u bg));
3018static void prt_set_font __ARGS((int bold, int italic, int underline));
3019static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3020static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3021static void prt_message __ARGS((char_u *s));
3022static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3023static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3024
3025#ifdef FEAT_SYN_HL
3026/*
3027 * If using a dark background, the colors will probably be too bright to show
3028 * up well on white paper, so reduce their brightness.
3029 */
3030 static long_u
3031darken_rgb(rgb)
3032 long_u rgb;
3033{
3034 return ((rgb >> 17) << 16)
3035 + (((rgb & 0xff00) >> 9) << 8)
3036 + ((rgb & 0xff) >> 1);
3037}
3038
3039 static long_u
3040prt_get_term_color(colorindex)
3041 int colorindex;
3042{
3043 /* TODO: Should check for xterm with 88 or 256 colors. */
3044 if (t_colors > 8)
3045 return cterm_color_16[colorindex % 16];
3046 return cterm_color_8[colorindex % 8];
3047}
3048
3049 static void
3050prt_get_attr(hl_id, pattr, modec)
3051 int hl_id;
3052 prt_text_attr_T* pattr;
3053 int modec;
3054{
3055 int colorindex;
3056 long_u fg_color;
3057 long_u bg_color;
3058 char *color;
3059
3060 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3061 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3062 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
3063
3064# ifdef FEAT_GUI
3065 if (gui.in_use)
3066 {
3067 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3068 if (bg_color == PRCOLOR_BLACK)
3069 bg_color = PRCOLOR_WHITE;
3070
3071 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3072 }
3073 else
3074# endif
3075 {
3076 bg_color = PRCOLOR_WHITE;
3077
3078 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3079 if (color == NULL)
3080 colorindex = 0;
3081 else
3082 colorindex = atoi(color);
3083
3084 if (colorindex >= 0 && colorindex < t_colors)
3085 fg_color = prt_get_term_color(colorindex);
3086 else
3087 fg_color = PRCOLOR_BLACK;
3088 }
3089
3090 if (fg_color == PRCOLOR_WHITE)
3091 fg_color = PRCOLOR_BLACK;
3092 else if (*p_bg == 'd')
3093 fg_color = darken_rgb(fg_color);
3094
3095 pattr->fg_color = fg_color;
3096 pattr->bg_color = bg_color;
3097}
3098#endif /* FEAT_SYN_HL */
3099
3100 static void
3101prt_set_fg(fg)
3102 long_u fg;
3103{
3104 if (fg != curr_fg)
3105 {
3106 curr_fg = fg;
3107 mch_print_set_fg(fg);
3108 }
3109}
3110
3111 static void
3112prt_set_bg(bg)
3113 long_u bg;
3114{
3115 if (bg != curr_bg)
3116 {
3117 curr_bg = bg;
3118 mch_print_set_bg(bg);
3119 }
3120}
3121
3122 static void
3123prt_set_font(bold, italic, underline)
3124 int bold;
3125 int italic;
3126 int underline;
3127{
3128 if (curr_bold != bold
3129 || curr_italic != italic
3130 || curr_underline != underline)
3131 {
3132 curr_underline = underline;
3133 curr_italic = italic;
3134 curr_bold = bold;
3135 mch_print_set_font(bold, italic, underline);
3136 }
3137}
3138
3139/*
3140 * Print the line number in the left margin.
3141 */
3142 static void
3143prt_line_number(psettings, page_line, lnum)
3144 prt_settings_T *psettings;
3145 int page_line;
3146 linenr_T lnum;
3147{
3148 int i;
3149 char_u tbuf[20];
3150
3151 prt_set_fg(psettings->number.fg_color);
3152 prt_set_bg(psettings->number.bg_color);
3153 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3154 mch_print_start_line(TRUE, page_line);
3155
3156 /* Leave two spaces between the number and the text; depends on
3157 * PRINT_NUMBER_WIDTH. */
3158 sprintf((char *)tbuf, "%6ld", (long)lnum);
3159 for (i = 0; i < 6; i++)
3160 (void)mch_print_text_out(&tbuf[i], 1);
3161
3162#ifdef FEAT_SYN_HL
3163 if (psettings->do_syntax)
3164 /* Set colors for next character. */
3165 current_syn_id = -1;
3166 else
3167#endif
3168 {
3169 /* Set colors and font back to normal. */
3170 prt_set_fg(PRCOLOR_BLACK);
3171 prt_set_bg(PRCOLOR_WHITE);
3172 prt_set_font(FALSE, FALSE, FALSE);
3173 }
3174}
3175
3176static linenr_T printer_page_num;
3177
3178 int
3179get_printer_page_num()
3180{
3181 return printer_page_num;
3182}
3183
3184/*
3185 * Get the currently effective header height.
3186 */
3187 int
3188prt_header_height()
3189{
3190 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3191 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3192 return 2;
3193}
3194
3195/*
3196 * Return TRUE if using a line number for printing.
3197 */
3198 int
3199prt_use_number()
3200{
3201 return (printer_opts[OPT_PRINT_NUMBER].present
3202 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3203}
3204
3205/*
3206 * Return the unit used in a margin item in 'printoptions'.
3207 * Returns PRT_UNIT_NONE if not recognized.
3208 */
3209 int
3210prt_get_unit(idx)
3211 int idx;
3212{
3213 int u = PRT_UNIT_NONE;
3214 int i;
3215 static char *(units[4]) = PRT_UNIT_NAMES;
3216
3217 if (printer_opts[idx].present)
3218 for (i = 0; i < 4; ++i)
3219 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3220 {
3221 u = i;
3222 break;
3223 }
3224 return u;
3225}
3226
3227/*
3228 * Print the page header.
3229 */
3230/*ARGSUSED*/
3231 static void
3232prt_header(psettings, pagenum, lnum)
3233 prt_settings_T *psettings;
3234 int pagenum;
3235 linenr_T lnum;
3236{
3237 int width = psettings->chars_per_line;
3238 int page_line;
3239 char_u *tbuf;
3240 char_u *p;
3241#ifdef FEAT_MBYTE
3242 int l;
3243#endif
3244
3245 /* Also use the space for the line number. */
3246 if (prt_use_number())
3247 width += PRINT_NUMBER_WIDTH;
3248
3249 tbuf = alloc(width + IOSIZE);
3250 if (tbuf == NULL)
3251 return;
3252
3253#ifdef FEAT_STL_OPT
3254 if (*p_header != NUL)
3255 {
3256 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3257
3258 /*
3259 * Need to (temporarily) set current line number and first/last line
3260 * number on the 'window'. Since we don't know how long the page is,
3261 * set the first and current line number to the top line, and guess
3262 * that the page length is 64.
3263 */
3264 tmp_lnum = curwin->w_cursor.lnum;
3265 tmp_topline = curwin->w_topline;
3266 tmp_botline = curwin->w_botline;
3267 curwin->w_cursor.lnum = lnum;
3268 curwin->w_topline = lnum;
3269 curwin->w_botline = lnum + 63;
3270 printer_page_num = pagenum;
3271
3272 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3273 p_header, ' ', width, NULL);
3274
3275 /* Reset line numbers */
3276 curwin->w_cursor.lnum = tmp_lnum;
3277 curwin->w_topline = tmp_topline;
3278 curwin->w_botline = tmp_botline;
3279 }
3280 else
3281#endif
3282 sprintf((char *)tbuf, _("Page %d"), pagenum);
3283
3284 prt_set_fg(PRCOLOR_BLACK);
3285 prt_set_bg(PRCOLOR_WHITE);
3286 prt_set_font(TRUE, FALSE, FALSE);
3287
3288 /* Use a negative line number to indicate printing in the top margin. */
3289 page_line = 0 - prt_header_height();
3290 mch_print_start_line(TRUE, page_line);
3291 for (p = tbuf; *p != NUL; )
3292 {
3293 if (mch_print_text_out(p,
3294#ifdef FEAT_MBYTE
3295 (l = (*mb_ptr2len_check)(p))
3296#else
3297 1
3298#endif
3299 ))
3300 {
3301 ++page_line;
3302 if (page_line >= 0) /* out of room in header */
3303 break;
3304 mch_print_start_line(TRUE, page_line);
3305 }
3306#ifdef FEAT_MBYTE
3307 p += l;
3308#else
3309 p++;
3310#endif
3311 }
3312
3313 vim_free(tbuf);
3314
3315#ifdef FEAT_SYN_HL
3316 if (psettings->do_syntax)
3317 /* Set colors for next character. */
3318 current_syn_id = -1;
3319 else
3320#endif
3321 {
3322 /* Set colors and font back to normal. */
3323 prt_set_fg(PRCOLOR_BLACK);
3324 prt_set_bg(PRCOLOR_WHITE);
3325 prt_set_font(FALSE, FALSE, FALSE);
3326 }
3327}
3328
3329/*
3330 * Display a print status message.
3331 */
3332 static void
3333prt_message(s)
3334 char_u *s;
3335{
3336 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3337 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3338 out_flush();
3339}
3340
3341 void
3342ex_hardcopy(eap)
3343 exarg_T *eap;
3344{
3345 linenr_T lnum;
3346 int collated_copies, uncollated_copies;
3347 prt_settings_T settings;
3348 long_u bytes_to_print = 0;
3349 int page_line;
3350 int jobsplit;
3351 int id;
3352
3353 memset(&settings, 0, sizeof(prt_settings_T));
3354 settings.has_color = TRUE;
3355
3356# ifdef FEAT_POSTSCRIPT
3357 if (*eap->arg == '>')
3358 {
3359 char_u *errormsg = NULL;
3360
3361 /* Expand things like "%.ps". */
3362 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3363 {
3364 if (errormsg != NULL)
3365 EMSG(errormsg);
3366 return;
3367 }
3368 settings.outfile = skipwhite(eap->arg + 1);
3369 }
3370 else if (*eap->arg != NUL)
3371 settings.arguments = eap->arg;
3372# endif
3373
3374 /*
3375 * Initialise for printing. Ask the user for settings, unless forceit is
3376 * set.
3377 * The mch_print_init() code should set up margins if applicable. (It may
3378 * not be a real printer - for example the engine might generate HTML or
3379 * PS.)
3380 */
3381 if (mch_print_init(&settings,
3382 curbuf->b_fname == NULL
3383 ? (char_u *)buf_spname(curbuf)
3384 : curbuf->b_sfname == NULL
3385 ? curbuf->b_fname
3386 : curbuf->b_sfname,
3387 eap->forceit) == FAIL)
3388 return;
3389
3390#ifdef FEAT_SYN_HL
3391# ifdef FEAT_GUI
3392 if (gui.in_use)
3393 settings.modec = 'g';
3394 else
3395# endif
3396 if (t_colors > 1)
3397 settings.modec = 'c';
3398 else
3399 settings.modec = 't';
3400
3401 if (!syntax_present(curbuf))
3402 settings.do_syntax = FALSE;
3403 else if (printer_opts[OPT_PRINT_SYNTAX].present
3404 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
3405 settings.do_syntax =
3406 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
3407 else
3408 settings.do_syntax = settings.has_color;
3409#endif
3410
3411 /* Set up printing attributes for line numbers */
3412 settings.number.fg_color = PRCOLOR_BLACK;
3413 settings.number.bg_color = PRCOLOR_WHITE;
3414 settings.number.bold = FALSE;
3415 settings.number.italic = TRUE;
3416 settings.number.underline = FALSE;
3417#ifdef FEAT_SYN_HL
3418 /*
3419 * Syntax highlighting of line numbers.
3420 */
3421 if (prt_use_number() && settings.do_syntax)
3422 {
3423 id = syn_name2id((char_u *)"LineNr");
3424 if (id > 0)
3425 id = syn_get_final_id(id);
3426
3427 prt_get_attr(id, &settings.number, settings.modec);
3428 }
3429#endif /* FEAT_SYN_HL */
3430
3431 /*
3432 * Estimate the total lines to be printed
3433 */
3434 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
3435 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
3436 if (bytes_to_print == 0)
3437 {
3438 MSG(_("No text to be printed"));
3439 goto print_fail_no_begin;
3440 }
3441
3442 /* Set colors and font to normal. */
3443 curr_bg = (long_u)0xffffffffL;
3444 curr_fg = (long_u)0xffffffffL;
3445 curr_italic = MAYBE;
3446 curr_bold = MAYBE;
3447 curr_underline = MAYBE;
3448
3449 prt_set_fg(PRCOLOR_BLACK);
3450 prt_set_bg(PRCOLOR_WHITE);
3451 prt_set_font(FALSE, FALSE, FALSE);
3452#ifdef FEAT_SYN_HL
3453 current_syn_id = -1;
3454#endif
3455
3456 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
3457 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
3458
3459 if (!mch_print_begin(&settings))
3460 goto print_fail_no_begin;
3461
3462 /*
3463 * Loop over collated copies: 1 2 3, 1 2 3, ...
3464 */
3465 page_count = 0;
3466 for (collated_copies = 0;
3467 collated_copies < settings.n_collated_copies;
3468 collated_copies++)
3469 {
3470 prt_pos_T prtpos; /* current print position */
3471 prt_pos_T page_prtpos; /* print position at page start */
3472 int side;
3473
3474 memset(&page_prtpos, 0, sizeof(prt_pos_T));
3475 page_prtpos.file_line = eap->line1;
3476 prtpos = page_prtpos;
3477
3478 if (jobsplit && collated_copies > 0)
3479 {
3480 /* Splitting jobs: Stop a previous job and start a new one. */
3481 mch_print_end(&settings);
3482 if (!mch_print_begin(&settings))
3483 goto print_fail_no_begin;
3484 }
3485
3486 /*
3487 * Loop over all pages in the print job: 1 2 3 ...
3488 */
3489 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
3490 {
3491 /*
3492 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
3493 * For duplex: 12 12 12 34 34 34, ...
3494 */
3495 for (uncollated_copies = 0;
3496 uncollated_copies < settings.n_uncollated_copies;
3497 uncollated_copies++)
3498 {
3499 /* Set the print position to the start of this page. */
3500 prtpos = page_prtpos;
3501
3502 /*
3503 * Do front and rear side of a page.
3504 */
3505 for (side = 0; side <= settings.duplex; ++side)
3506 {
3507 /*
3508 * Print one page.
3509 */
3510
3511 /* Check for interrupt character every page. */
3512 ui_breakcheck();
3513 if (got_int || settings.user_abort)
3514 goto print_fail;
3515
3516 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
3517 page_count + 1 + side,
3518 prtpos.bytes_printed > 1000000
3519 ? (int)(prtpos.bytes_printed /
3520 (bytes_to_print / 100))
3521 : (int)((prtpos.bytes_printed * 100)
3522 / bytes_to_print));
3523 if (!mch_print_begin_page(IObuff))
3524 goto print_fail;
3525
3526 if (settings.n_collated_copies > 1)
3527 sprintf((char *)IObuff + STRLEN(IObuff),
3528 _(" Copy %d of %d"),
3529 collated_copies + 1,
3530 settings.n_collated_copies);
3531 prt_message(IObuff);
3532
3533 /*
3534 * Output header if required
3535 */
3536 if (prt_header_height() > 0)
3537 prt_header(&settings, page_count + 1 + side,
3538 prtpos.file_line);
3539
3540 for (page_line = 0; page_line < settings.lines_per_page;
3541 ++page_line)
3542 {
3543 prtpos.column = hardcopy_line(&settings,
3544 page_line, &prtpos);
3545 if (prtpos.column == 0)
3546 {
3547 /* finished a file line */
3548 prtpos.bytes_printed +=
3549 STRLEN(skipwhite(ml_get(prtpos.file_line)));
3550 if (++prtpos.file_line > eap->line2)
3551 break; /* reached the end */
3552 }
3553 else if (prtpos.ff)
3554 {
3555 /* Line had a formfeed in it - start new page but
3556 * stay on the current line */
3557 break;
3558 }
3559 }
3560
3561 if (!mch_print_end_page())
3562 goto print_fail;
3563 if (prtpos.file_line > eap->line2)
3564 break; /* reached the end */
3565 }
3566
3567 /*
3568 * Extra blank page for duplexing with odd number of pages and
3569 * more copies to come.
3570 */
3571 if (prtpos.file_line > eap->line2 && settings.duplex
3572 && side == 0
3573 && uncollated_copies + 1 < settings.n_uncollated_copies)
3574 {
3575 if (!mch_print_blank_page())
3576 goto print_fail;
3577 }
3578 }
3579 if (settings.duplex && prtpos.file_line <= eap->line2)
3580 ++page_count;
3581
3582 /* Remember the position where the next page starts. */
3583 page_prtpos = prtpos;
3584 }
3585
3586 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
3587 prt_message(IObuff);
3588 }
3589
3590print_fail:
3591 if (got_int || settings.user_abort)
3592 {
3593 sprintf((char *)IObuff, _("Printing aborted"));
3594 prt_message(IObuff);
3595 }
3596 mch_print_end(&settings);
3597
3598print_fail_no_begin:
3599 mch_print_cleanup();
3600}
3601
3602/*
3603 * Print one page line.
3604 * Return the next column to print, or zero if the line is finished.
3605 */
3606 static colnr_T
3607hardcopy_line(psettings, page_line, ppos)
3608 prt_settings_T *psettings;
3609 int page_line;
3610 prt_pos_T *ppos;
3611{
3612 colnr_T col;
3613 char_u *line;
3614 int need_break = FALSE;
3615 int outputlen;
3616 int tab_spaces;
3617 long_u print_pos;
3618#ifdef FEAT_SYN_HL
3619 prt_text_attr_T attr;
3620 int id;
3621#endif
3622
3623 if (ppos->column == 0 || ppos->ff)
3624 {
3625 print_pos = 0;
3626 tab_spaces = 0;
3627 if (!ppos->ff && prt_use_number())
3628 prt_line_number(psettings, page_line, ppos->file_line);
3629 ppos->ff = FALSE;
3630 }
3631 else
3632 {
3633 /* left over from wrap halfway a tab */
3634 print_pos = ppos->print_pos;
3635 tab_spaces = ppos->lead_spaces;
3636 }
3637
3638 mch_print_start_line(0, page_line);
3639 line = ml_get(ppos->file_line);
3640
3641 /*
3642 * Loop over the columns until the end of the file line or right margin.
3643 */
3644 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
3645 {
3646 outputlen = 1;
3647#ifdef FEAT_MBYTE
3648 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
3649 outputlen = 1;
3650#endif
3651#ifdef FEAT_SYN_HL
3652 /*
3653 * syntax highlighting stuff.
3654 */
3655 if (psettings->do_syntax)
3656 {
3657 id = syn_get_id(ppos->file_line, (long)col, 1);
3658 if (id > 0)
3659 id = syn_get_final_id(id);
3660 else
3661 id = 0;
3662 /* Get the line again, a multi-line regexp may invalidate it. */
3663 line = ml_get(ppos->file_line);
3664
3665 if (id != current_syn_id)
3666 {
3667 current_syn_id = id;
3668 prt_get_attr(id, &attr, psettings->modec);
3669 prt_set_font(attr.bold, attr.italic, attr.underline);
3670 prt_set_fg(attr.fg_color);
3671 prt_set_bg(attr.bg_color);
3672 }
3673 }
3674#endif /* FEAT_SYN_HL */
3675
3676 /*
3677 * Appropriately expand any tabs to spaces.
3678 */
3679 if (line[col] == TAB || tab_spaces != 0)
3680 {
3681 if (tab_spaces == 0)
3682 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
3683
3684 while (tab_spaces > 0)
3685 {
3686 need_break = mch_print_text_out((char_u *)" ", 1);
3687 print_pos++;
3688 tab_spaces--;
3689 if (need_break)
3690 break;
3691 }
3692 /* Keep the TAB if we didn't finish it. */
3693 if (need_break && tab_spaces > 0)
3694 break;
3695 }
3696 else if (line[col] == FF
3697 && printer_opts[OPT_PRINT_FORMFEED].present
3698 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
3699 == 'y')
3700 {
3701 ppos->ff = TRUE;
3702 need_break = 1;
3703 }
3704 else
3705 {
3706 need_break = mch_print_text_out(line + col, outputlen);
3707#ifdef FEAT_MBYTE
3708 if (has_mbyte)
3709 print_pos += (*mb_ptr2cells)(line + col);
3710 else
3711#endif
3712 print_pos++;
3713 }
3714 }
3715
3716 ppos->lead_spaces = tab_spaces;
3717 ppos->print_pos = print_pos;
3718
3719 /*
3720 * Start next line of file if we clip lines, or have reached end of the
3721 * line, unless we are doing a formfeed.
3722 */
3723 if (!ppos->ff
3724 && (line[col] == NUL
3725 || (printer_opts[OPT_PRINT_WRAP].present
3726 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
3727 == 'n')))
3728 return 0;
3729 return col;
3730}
3731
3732# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
3733
3734/*
3735 * PS printer stuff.
3736 *
3737 * Sources of information to help maintain the PS printing code:
3738 *
3739 * 1. PostScript Language Reference, 3rd Edition,
3740 * Addison-Wesley, 1999, ISBN 0-201-37922-8
3741 * 2. PostScript Language Program Design,
3742 * Addison-Wesley, 1988, ISBN 0-201-14396-8
3743 * 3. PostScript Tutorial and Cookbook,
3744 * Addison Wesley, 1985, ISBN 0-201-10179-3
3745 * 4. PostScript Language Document Structuring Conventions Specification,
3746 * version 3.0,
3747 * Adobe Technote 5001, 25th September 1992
3748 * 5. PostScript Printer Description File Format Specification, Version 4.3,
3749 * Adobe technote 5003, 9th February 1996
3750 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
3751 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00003752 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
3753 * Adobe Technote 5014, 8th October 1996
3754 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
3755 * Adoboe Technote 5094, 8th September, 2001
3756 * 9. CJKV Information Processing, 2nd Edition,
3757 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 *
3759 * Some of these documents can be found in PDF form on Adobe's web site -
3760 * http://www.adobe.com
3761 */
3762
Bram Moolenaar8299df92004-07-10 09:47:34 +00003763#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
3764
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
3766#define PRT_PS_DEFAULT_FONTSIZE (10)
3767#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
3768
3769struct prt_mediasize_S
3770{
3771 char *name;
3772 float width; /* width and height in points for portrait */
3773 float height;
3774};
3775
3776#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
3777
3778static struct prt_mediasize_S prt_mediasize[] =
3779{
3780 {"A4", 595.0, 842.0},
3781 {"letter", 612.0, 792.0},
3782 {"10x14", 720.0, 1008.0},
3783 {"A3", 842.0, 1191.0},
3784 {"A5", 420.0, 595.0},
3785 {"B4", 729.0, 1032.0},
3786 {"B5", 516.0, 729.0},
3787 {"executive", 522.0, 756.0},
3788 {"folio", 595.0, 935.0},
3789 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
3790 {"legal", 612.0, 1008.0},
3791 {"quarto", 610.0, 780.0},
3792 {"statement", 396.0, 612.0},
3793 {"tabloid", 792.0, 1224.0}
3794};
3795
3796/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
3797struct prt_ps_font_S
3798{
3799 int wx;
3800 int uline_offset;
3801 int uline_width;
3802 int bbox_min_y;
3803 int bbox_max_y;
3804 char *(ps_fontname[4]);
3805};
3806
3807#define PRT_PS_FONT_ROMAN (0)
3808#define PRT_PS_FONT_BOLD (1)
3809#define PRT_PS_FONT_OBLIQUE (2)
3810#define PRT_PS_FONT_BOLDOBLIQUE (3)
3811
Bram Moolenaar8299df92004-07-10 09:47:34 +00003812/* Standard font metrics for Courier family */
3813static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814{
3815 600,
3816 -100, 50,
3817 -250, 805,
3818 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
3819};
3820
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003821#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00003822/* Generic font metrics for multi-byte fonts */
3823static struct prt_ps_font_S prt_ps_mb_font =
3824{
3825 1000,
3826 -100, 50,
3827 -250, 805,
3828 {NULL, NULL, NULL, NULL}
3829};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003830#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00003831
3832/* Pointer to current font set being used */
3833static struct prt_ps_font_S* prt_ps_font;
3834
3835/* Structures to map user named encoding and mapping to PS equivalents for
3836 * building CID font name */
3837struct prt_ps_encoding_S
3838{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003839 char *encoding;
3840 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00003841 int needs_charset;
3842};
3843
3844struct prt_ps_charset_S
3845{
3846 char *charset;
3847 char *cmap_charset;
3848 int has_charset;
3849};
3850
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003851#ifdef FEAT_MBYTE
3852
Bram Moolenaar8299df92004-07-10 09:47:34 +00003853#define CS_JIS_C_1978 (0x01)
3854#define CS_JIS_X_1983 (0x02)
3855#define CS_JIS_X_1990 (0x04)
3856#define CS_NEC (0x08)
3857#define CS_MSWINDOWS (0x10)
3858#define CS_CP932 (0x20)
3859#define CS_KANJITALK6 (0x40)
3860#define CS_KANJITALK7 (0x80)
3861
3862/* Japanese encodings and charsets */
3863static struct prt_ps_encoding_S j_encodings[] =
3864{
3865 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
3866 CS_NEC)},
3867 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
3868 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
3869 CS_KANJITALK6|CS_KANJITALK7)},
3870 {"cp932", "RKSJ", CS_JIS_X_1983},
3871 {"ucs-2", "UCS2", CS_JIS_X_1990},
3872 {"utf-8", "UTF8" , CS_JIS_X_1990}
3873};
3874static struct prt_ps_charset_S j_charsets[] =
3875{
3876 {"JIS_C_1978", "78", CS_JIS_C_1978},
3877 {"JIS_X_1983", NULL, CS_JIS_X_1983},
3878 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
3879 {"NEC", "Ext", CS_NEC},
3880 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
3881 {"CP932", "90ms", CS_JIS_X_1983},
3882 {"KANJITALK6", "83pv", CS_KANJITALK6},
3883 {"KANJITALK7", "90pv", CS_KANJITALK7}
3884};
3885
3886#define CS_GB_2312_80 (0x01)
3887#define CS_GBT_12345_90 (0x02)
3888#define CS_GBK2K (0x04)
3889#define CS_SC_MAC (0x08)
3890#define CS_GBT_90_MAC (0x10)
3891#define CS_GBK (0x20)
3892#define CS_SC_ISO10646 (0x40)
3893
3894/* Simplified Chinese encodings and charsets */
3895static struct prt_ps_encoding_S sc_encodings[] =
3896{
3897 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
3898 {"gb18030", NULL, CS_GBK2K},
3899 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
3900 CS_GBT_90_MAC)},
3901 {"gbk", "EUC", CS_GBK},
3902 {"ucs-2", "UCS2", CS_SC_ISO10646},
3903 {"utf-8", "UTF8", CS_SC_ISO10646}
3904};
3905static struct prt_ps_charset_S sc_charsets[] =
3906{
3907 {"GB_2312-80", "GB", CS_GB_2312_80},
3908 {"GBT_12345-90","GBT", CS_GBT_12345_90},
3909 {"MAC", "GBpc", CS_SC_MAC},
3910 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
3911 {"GBK", "GBK", CS_GBK},
3912 {"GB18030", "GBK2K", CS_GBK2K},
3913 {"ISO10646", "UniGB", CS_SC_ISO10646}
3914};
3915
3916#define CS_CNS_PLANE_1 (0x01)
3917#define CS_CNS_PLANE_2 (0x02)
3918#define CS_CNS_PLANE_1_2 (0x04)
3919#define CS_B5 (0x08)
3920#define CS_ETEN (0x10)
3921#define CS_HK_GCCS (0x20)
3922#define CS_HK_SCS (0x40)
3923#define CS_HK_SCS_ETEN (0x80)
3924#define CS_MTHKL (0x100)
3925#define CS_MTHKS (0x200)
3926#define CS_DLHKL (0x400)
3927#define CS_DLHKS (0x800)
3928#define CS_TC_ISO10646 (0x1000)
3929
3930/* Traditional Chinese encodings and charsets */
3931static struct prt_ps_encoding_S tc_encodings[] =
3932{
3933 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
3934 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
3935 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
3936 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
3937 CS_DLHKS)},
3938 {"cp950", "B5", CS_B5},
3939 {"ucs-2", "UCS2", CS_TC_ISO10646},
3940 {"utf-8", "UTF8", CS_TC_ISO10646},
3941 {"utf-16", "UTF16", CS_TC_ISO10646},
3942 {"utf-32", "UTF32", CS_TC_ISO10646}
3943};
3944static struct prt_ps_charset_S tc_charsets[] =
3945{
3946 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
3947 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
3948 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
3949 {"BIG5", NULL, CS_B5},
3950 {"CP950", NULL, CS_B5},
3951 {"ETEN", "ETen", CS_ETEN},
3952 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
3953 {"SCS", "HKscs", CS_HK_SCS},
3954 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
3955 {"MTHKL", "HKm471", CS_MTHKL},
3956 {"MTHKS", "HKm314", CS_MTHKS},
3957 {"DLHKL", "HKdla", CS_DLHKL},
3958 {"DLHKS", "HKdlb", CS_DLHKS},
3959 {"ISO10646", "UniCNS", CS_TC_ISO10646}
3960};
3961
3962#define CS_KR_X_1992 (0x01)
3963#define CS_KR_MAC (0x02)
3964#define CS_KR_X_1992_MS (0x04)
3965#define CS_KR_ISO10646 (0x08)
3966
3967/* Korean encodings and charsets */
3968static struct prt_ps_encoding_S k_encodings[] =
3969{
3970 {"iso-2022-kr", NULL, CS_KR_X_1992},
3971 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
3972 {"johab", "Johab", CS_KR_X_1992},
3973 {"cp1361", "Johab", CS_KR_X_1992},
3974 {"uhc", "UHC", CS_KR_X_1992_MS},
3975 {"cp949", "UHC", CS_KR_X_1992_MS},
3976 {"ucs-2", "UCS2", CS_KR_ISO10646},
3977 {"utf-8", "UTF8", CS_KR_ISO10646}
3978};
3979static struct prt_ps_charset_S k_charsets[] =
3980{
3981 {"KS_X_1992", "KSC", CS_KR_X_1992},
3982 {"CP1361", "KSC", CS_KR_X_1992},
3983 {"MAC", "KSCpc", CS_KR_MAC},
3984 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
3985 {"CP949", "KSCms", CS_KR_X_1992_MS},
3986 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
3987 {"ISO10646", "UniKS", CS_KR_ISO10646}
3988};
3989
3990/* Collections of encodings and charsets for multi-byte printing */
3991struct prt_ps_mbfont_S
3992{
3993 int num_encodings;
3994 struct prt_ps_encoding_S *encodings;
3995 int num_charsets;
3996 struct prt_ps_charset_S *charsets;
3997 char *ascii_enc;
3998 char *defcs;
3999};
4000
4001static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4002{
4003 {
4004 NUM_ELEMENTS(j_encodings),
4005 j_encodings,
4006 NUM_ELEMENTS(j_charsets),
4007 j_charsets,
4008 "jis_roman",
4009 "JIS_X_1983"
4010 },
4011 {
4012 NUM_ELEMENTS(sc_encodings),
4013 sc_encodings,
4014 NUM_ELEMENTS(sc_charsets),
4015 sc_charsets,
4016 "gb_roman",
4017 "GB_2312-80"
4018 },
4019 {
4020 NUM_ELEMENTS(tc_encodings),
4021 tc_encodings,
4022 NUM_ELEMENTS(tc_charsets),
4023 tc_charsets,
4024 "cns_roman",
4025 "BIG5"
4026 },
4027 {
4028 NUM_ELEMENTS(k_encodings),
4029 k_encodings,
4030 NUM_ELEMENTS(k_charsets),
4031 k_charsets,
4032 "ks_roman",
4033 "KS_X_1992"
4034 }
4035};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004036#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004037
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038struct prt_ps_resource_S
4039{
4040 char_u name[64];
4041 char_u filename[MAXPATHL + 1];
4042 int type;
4043 char_u title[256];
4044 char_u version[256];
4045};
4046
4047/* Types of PS resource file currently used */
4048#define PRT_RESOURCE_TYPE_PROCSET (0)
4049#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004050#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051
4052/* The PS prolog file version number has to match - if the prolog file is
4053 * updated, increment the number in the file and here. Version checking was
4054 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004055 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 * Table of VIM and prolog versions:
4057 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004058 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004060 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004062#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004063#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064
4065/* String versions of PS resource types - indexed by constants above so don't
4066 * re-order!
4067 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004068static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069{
4070 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004071 "encoding",
4072 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073};
4074
4075/* Strings to look for in a PS resource file */
4076#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4077#define PRT_RESOURCE_RESOURCE "Resource-"
4078#define PRT_RESOURCE_PROCSET "ProcSet"
4079#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004080#define PRT_RESOURCE_CMAP "CMap"
4081
4082
4083/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4084 * read more comments. */
4085#define PRT_DSC_MISC_TYPE (-1)
4086#define PRT_DSC_TITLE_TYPE (1)
4087#define PRT_DSC_VERSION_TYPE (2)
4088#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4089
4090#define PRT_DSC_TITLE "%%Title:"
4091#define PRT_DSC_VERSION "%%Version:"
4092#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4093
4094struct prt_dsc_comment_S
4095{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004096 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004097 int len;
4098 int type;
4099};
4100
4101struct prt_dsc_line_S
4102{
4103 int type;
4104 char_u *string;
4105 int len;
4106};
4107
4108
4109#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4110struct prt_dsc_comment_S prt_dsc_table[] =
4111{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004112 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004113 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004114 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004115 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004116 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004117};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118
4119static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4120static void prt_write_file __ARGS((char_u *buffer));
4121static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4122static void prt_write_string __ARGS((char *s));
4123static void prt_write_int __ARGS((int i));
4124static void prt_write_boolean __ARGS((int b));
4125static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4126static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4127static void prt_write_real __ARGS((double val, int prec));
4128static void prt_def_var __ARGS((char *name, double value, int prec));
4129static void prt_flush_buffer __ARGS((void));
4130static void prt_resource_name __ARGS((char_u *filename));
4131static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4132static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4133static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4134static void prt_dsc_start __ARGS((void));
4135static void prt_dsc_noarg __ARGS((char *comment));
4136static void prt_dsc_textline __ARGS((char *comment, char *text));
4137static void prt_dsc_text __ARGS((char *comment, char *text));
4138static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4139static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4140static 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 +00004141static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4142static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004143static float to_device_units __ARGS((int idx, double physsize, int def_number));
4144static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4145static void prt_font_metrics __ARGS((int font_scale));
4146static int prt_get_cpl __ARGS((void));
4147static int prt_get_lpp __ARGS((void));
4148static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004149static int prt_resfile_next_line __ARGS((void));
4150static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4151static int prt_resfile_skip_nonws __ARGS((int offset));
4152static int prt_resfile_skip_ws __ARGS((int offset));
4153static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4154#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004155static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004156static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4157static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4158static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4159#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160
4161/*
4162 * Variables for the output PostScript file.
4163 */
4164static FILE *prt_ps_fd;
4165static int prt_file_error;
4166static char_u *prt_ps_file_name = NULL;
4167
4168/*
4169 * Various offsets and dimensions in default PostScript user space (points).
4170 * Used for text positioning calculations
4171 */
4172static float prt_page_width;
4173static float prt_page_height;
4174static float prt_left_margin;
4175static float prt_right_margin;
4176static float prt_top_margin;
4177static float prt_bottom_margin;
4178static float prt_line_height;
4179static float prt_first_line_height;
4180static float prt_char_width;
4181static float prt_number_width;
4182static float prt_bgcol_offset;
4183static float prt_pos_x_moveto = 0.0;
4184static float prt_pos_y_moveto = 0.0;
4185
4186/*
4187 * Various control variables used to decide when and how to change the
4188 * PostScript graphics state.
4189 */
4190static int prt_need_moveto;
4191static int prt_do_moveto;
4192static int prt_need_font;
4193static int prt_font;
4194static int prt_need_underline;
4195static int prt_underline;
4196static int prt_do_underline;
4197static int prt_need_fgcol;
4198static int prt_fgcol;
4199static int prt_need_bgcol;
4200static int prt_do_bgcol;
4201static int prt_bgcol;
4202static int prt_new_bgcol;
4203static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004204static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004206static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207
4208/*
4209 * Variables controlling physical printing.
4210 */
4211static int prt_media;
4212static int prt_portrait;
4213static int prt_num_copies;
4214static int prt_duplex;
4215static int prt_tumble;
4216static int prt_collate;
4217
4218/*
4219 * Buffers used when generating PostScript output
4220 */
4221static char_u prt_line_buffer[257];
4222static garray_T prt_ps_buffer;
4223
4224# ifdef FEAT_MBYTE
4225static int prt_do_conv;
4226static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004227
4228static int prt_out_mbyte;
4229static int prt_custom_cmap;
4230static char prt_cmap[80];
4231static int prt_use_courier;
4232static int prt_in_ascii;
4233static int prt_half_width;
4234static char *prt_ascii_encoding;
4235static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236# endif
4237
4238 static void
4239prt_write_file_raw_len(buffer, bytes)
4240 char_u *buffer;
4241 int bytes;
4242{
4243 if (!prt_file_error
4244 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4245 != (size_t)bytes)
4246 {
4247 EMSG(_("E455: Error writing to PostScript output file"));
4248 prt_file_error = TRUE;
4249 }
4250}
4251
4252 static void
4253prt_write_file(buffer)
4254 char_u *buffer;
4255{
4256 prt_write_file_len(buffer, STRLEN(buffer));
4257}
4258
4259 static void
4260prt_write_file_len(buffer, bytes)
4261 char_u *buffer;
4262 int bytes;
4263{
4264#ifdef EBCDIC
4265 ebcdic2ascii(buffer, bytes);
4266#endif
4267 prt_write_file_raw_len(buffer, bytes);
4268}
4269
4270/*
4271 * Write a string.
4272 */
4273 static void
4274prt_write_string(s)
4275 char *s;
4276{
4277 sprintf((char *)prt_line_buffer, "%s", s);
4278 prt_write_file(prt_line_buffer);
4279}
4280
4281/*
4282 * Write an int and a space.
4283 */
4284 static void
4285prt_write_int(i)
4286 int i;
4287{
4288 sprintf((char *)prt_line_buffer, "%d ", i);
4289 prt_write_file(prt_line_buffer);
4290}
4291
4292/*
4293 * Write a boolean and a space.
4294 */
4295 static void
4296prt_write_boolean(b)
4297 int b;
4298{
4299 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4300 prt_write_file(prt_line_buffer);
4301}
4302
4303/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004304 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 */
4306 static void
4307prt_def_font(new_name, encoding, height, font)
4308 char *new_name;
4309 char *encoding;
4310 int height;
4311 char *font;
4312{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004313 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4314 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004316#ifdef FEAT_MBYTE
4317 if (prt_out_mbyte)
4318 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4319 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4320 else
4321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4323 new_name, height, new_name);
4324 prt_write_file(prt_line_buffer);
4325}
4326
Bram Moolenaar8299df92004-07-10 09:47:34 +00004327#ifdef FEAT_MBYTE
4328/*
4329 * Write a line to define the CID font.
4330 */
4331 static void
4332prt_def_cidfont(new_name, height, cidfont)
4333 char *new_name;
4334 int height;
4335 char *cidfont;
4336{
4337 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4338 new_name, prt_cmap, cidfont);
4339 prt_write_file(prt_line_buffer);
4340 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4341 new_name);
4342 prt_write_file(prt_line_buffer);
4343}
4344
4345/*
4346 * Write a line to define a duplicate of a CID font
4347 */
4348 static void
4349prt_dup_cidfont(original_name, new_name)
4350 char *original_name;
4351 char *new_name;
4352{
4353 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4354 prt_write_file(prt_line_buffer);
4355}
4356#endif
4357
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358/*
4359 * Convert a real value into an integer and fractional part as integers, with
4360 * the fractional part being in the range [0,10^precision). The fractional part
4361 * is also rounded based on the precision + 1'th fractional digit.
4362 */
4363 static void
4364prt_real_bits(real, precision, pinteger, pfraction)
4365 double real;
4366 int precision;
4367 int *pinteger;
4368 int *pfraction;
4369{
4370 int i;
4371 int integer;
4372 float fraction;
4373
4374 integer = (int)real;
4375 fraction = (float)(real - integer);
4376 if (real < (double)integer)
4377 fraction = -fraction;
4378 for (i = 0; i < precision; i++)
4379 fraction *= 10.0;
4380
4381 *pinteger = integer;
4382 *pfraction = (int)(fraction + 0.5);
4383}
4384
4385/*
4386 * Write a real and a space. Save bytes if real value has no fractional part!
4387 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4388 * what decimal point character to use, but PS always requires a '.'.
4389 */
4390 static void
4391prt_write_real(val, prec)
4392 double val;
4393 int prec;
4394{
4395 int integer;
4396 int fraction;
4397
4398 prt_real_bits(val, prec, &integer, &fraction);
4399 /* Emit integer part */
4400 sprintf((char *)prt_line_buffer, "%d", integer);
4401 prt_write_file(prt_line_buffer);
4402 /* Only emit fraction if necessary */
4403 if (fraction != 0)
4404 {
4405 /* Remove any trailing zeros */
4406 while ((fraction % 10) == 0)
4407 {
4408 prec--;
4409 fraction /= 10;
4410 }
4411 /* Emit fraction left padded with zeros */
4412 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
4413 prt_write_file(prt_line_buffer);
4414 }
4415 sprintf((char *)prt_line_buffer, " ");
4416 prt_write_file(prt_line_buffer);
4417}
4418
4419/*
4420 * Write a line to define a numeric variable.
4421 */
4422 static void
4423prt_def_var(name, value, prec)
4424 char *name;
4425 double value;
4426 int prec;
4427{
4428 sprintf((char *)prt_line_buffer, "/%s ", name);
4429 prt_write_file(prt_line_buffer);
4430 prt_write_real(value, prec);
4431 sprintf((char *)prt_line_buffer, "d\n");
4432 prt_write_file(prt_line_buffer);
4433}
4434
4435/* Convert size from font space to user space at current font scale */
4436#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
4437
4438 static void
4439prt_flush_buffer()
4440{
4441 if (prt_ps_buffer.ga_len > 0)
4442 {
4443 /* Any background color must be drawn first */
4444 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
4445 {
4446 int r, g, b;
4447
4448 if (prt_do_moveto)
4449 {
4450 prt_write_real(prt_pos_x_moveto, 2);
4451 prt_write_real(prt_pos_y_moveto, 2);
4452 prt_write_string("m\n");
4453 prt_do_moveto = FALSE;
4454 }
4455
4456 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004457 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458 prt_write_real(prt_line_height, 2);
4459
4460 /* Lastly add the color of the background */
4461 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
4462 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
4463 b = prt_new_bgcol & 0xff;
4464 prt_write_real(r / 255.0, 3);
4465 prt_write_real(g / 255.0, 3);
4466 prt_write_real(b / 255.0, 3);
4467 prt_write_string("bg\n");
4468 }
4469 /* Draw underlines before the text as it makes it slightly easier to
4470 * find the starting point.
4471 */
4472 if (prt_do_underline)
4473 {
4474 if (prt_do_moveto)
4475 {
4476 prt_write_real(prt_pos_x_moveto, 2);
4477 prt_write_real(prt_pos_y_moveto, 2);
4478 prt_write_string("m\n");
4479 prt_do_moveto = FALSE;
4480 }
4481
Bram Moolenaar8299df92004-07-10 09:47:34 +00004482 /* Underline length of text run */
4483 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 prt_write_string("ul\n");
4485 }
4486 /* Draw the text
4487 * Note: we write text out raw - EBCDIC conversion is handled in the
4488 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004489#ifdef FEAT_MBYTE
4490 if (prt_out_mbyte)
4491 prt_write_string("<");
4492 else
4493#endif
4494 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004496#ifdef FEAT_MBYTE
4497 if (prt_out_mbyte)
4498 prt_write_string(">");
4499 else
4500#endif
4501 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502 /* Add a moveto if need be and use the appropriate show procedure */
4503 if (prt_do_moveto)
4504 {
4505 prt_write_real(prt_pos_x_moveto, 2);
4506 prt_write_real(prt_pos_y_moveto, 2);
4507 /* moveto and a show */
4508 prt_write_string("ms\n");
4509 prt_do_moveto = FALSE;
4510 }
4511 else /* Simple show */
4512 prt_write_string("s\n");
4513
4514 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004515 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 }
4517}
4518
4519static char_u *resource_filename;
4520
4521 static void
4522prt_resource_name(filename)
4523 char_u *filename;
4524{
4525 if (STRLEN(filename) >= MAXPATHL)
4526 *resource_filename = NUL;
4527 else
4528 STRCPY(resource_filename, filename);
4529}
4530
4531 static int
4532prt_find_resource(name, resource)
4533 char *name;
4534 struct prt_ps_resource_S *resource;
4535{
4536 char_u buffer[MAXPATHL + 1];
4537
4538 STRCPY(resource->name, name);
4539 /* Look for named resource file in runtimepath */
4540 STRCPY(buffer, "print");
4541 add_pathsep(buffer);
4542 STRCAT(buffer, name);
4543 STRCAT(buffer, ".ps");
4544 resource_filename = resource->filename;
4545 *resource_filename = NUL;
4546 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
4547 && resource->filename[0] != NUL);
4548}
4549
4550/* PS CR and LF characters have platform independent values */
4551#define PSLF (0x0a)
4552#define PSCR (0x0d)
4553
Bram Moolenaar8299df92004-07-10 09:47:34 +00004554/* Static buffer to read initial comments in a resource file, some can have a
4555 * couple of KB of comments! */
4556#define PRT_FILE_BUFFER_LEN (2048)
4557struct prt_resfile_buffer_S
4558{
4559 char_u buffer[PRT_FILE_BUFFER_LEN];
4560 int len;
4561 int line_start;
4562 int line_end;
4563};
4564
4565static struct prt_resfile_buffer_S prt_resfile;
4566
4567 static int
4568prt_resfile_next_line()
4569{
4570 int index;
4571
4572 /* Move to start of next line and then find end of line */
4573 index = prt_resfile.line_end + 1;
4574 while (index < prt_resfile.len)
4575 {
4576 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
4577 != PSCR)
4578 break;
4579 index++;
4580 }
4581 prt_resfile.line_start = index;
4582
4583 while (index < prt_resfile.len)
4584 {
4585 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
4586 == PSCR)
4587 break;
4588 index++;
4589 }
4590 prt_resfile.line_end = index;
4591
4592 return (index < prt_resfile.len);
4593}
4594
4595 static int
4596prt_resfile_strncmp(offset, string, len)
4597 int offset;
4598 char *string;
4599 int len;
4600{
4601 /* Force not equal if string is longer than remainder of line */
4602 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
4603 return 1;
4604
4605 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
4606 string, len);
4607}
4608
4609 static int
4610prt_resfile_skip_nonws(offset)
4611 int offset;
4612{
4613 int index;
4614
4615 index = prt_resfile.line_start + offset;
4616 while (index < prt_resfile.line_end)
4617 {
4618 if (isspace(prt_resfile.buffer[index]))
4619 return index - prt_resfile.line_start;
4620 index++;
4621 }
4622 return -1;
4623}
4624
4625 static int
4626prt_resfile_skip_ws(offset)
4627 int offset;
4628{
4629 int index;
4630
4631 index = prt_resfile.line_start + offset;
4632 while (index < prt_resfile.line_end)
4633 {
4634 if (!isspace(prt_resfile.buffer[index]))
4635 return index - prt_resfile.line_start;
4636 index++;
4637 }
4638 return -1;
4639}
4640
4641/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
4642 * if a DSC comment is found, else false */
4643 static int
4644prt_next_dsc(p_dsc_line)
4645 struct prt_dsc_line_S *p_dsc_line;
4646{
4647 int comment;
4648 int offset;
4649
4650 /* Move to start of next line */
4651 if (!prt_resfile_next_line())
4652 return FALSE;
4653
4654 /* DSC comments always start %% */
4655 if (prt_resfile_strncmp(0, "%%", 2) != 0)
4656 return FALSE;
4657
4658 /* Find type of DSC comment */
4659 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
4660 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
4661 prt_dsc_table[comment].len) == 0)
4662 break;
4663
4664 if (comment != NUM_ELEMENTS(prt_dsc_table))
4665 {
4666 /* Return type of comment */
4667 p_dsc_line->type = prt_dsc_table[comment].type;
4668 offset = prt_dsc_table[comment].len;
4669 }
4670 else
4671 {
4672 /* Unrecognised DSC comment, skip to ws after comment leader */
4673 p_dsc_line->type = PRT_DSC_MISC_TYPE;
4674 offset = prt_resfile_skip_nonws(0);
4675 if (offset == -1)
4676 return FALSE;
4677 }
4678
4679 /* Skip ws to comment value */
4680 offset = prt_resfile_skip_ws(offset);
4681 if (offset == -1)
4682 return FALSE;
4683
4684 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
4685 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
4686
4687 return TRUE;
4688}
4689
4690/* Improved hand crafted parser to get the type, title, and version number of a
4691 * PS resource file so the file details can be added to the DSC header comments.
4692 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 static int
4694prt_open_resource(resource)
4695 struct prt_ps_resource_S *resource;
4696{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004697 int offset;
4698 int seen_all;
4699 int seen_title;
4700 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004701 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004702 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703
4704 fd_resource = mch_fopen((char *)resource->filename, READBIN);
4705 if (fd_resource == NULL)
4706 {
4707 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
4708 return FALSE;
4709 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004710 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004711
4712 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004713 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
4714 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004715 if (ferror(fd_resource))
4716 {
4717 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4718 resource->filename);
4719 fclose(fd_resource);
4720 return FALSE;
4721 }
4722
Bram Moolenaar8299df92004-07-10 09:47:34 +00004723 prt_resfile.line_end = -1;
4724 prt_resfile.line_start = 0;
4725 if (!prt_resfile_next_line())
4726 return FALSE;
4727
4728 offset = 0;
4729
4730 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
4731 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 {
4733 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
4734 resource->filename);
4735 fclose(fd_resource);
4736 return FALSE;
4737 }
4738
4739 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004740 offset += STRLEN(PRT_RESOURCE_HEADER);
4741 offset = prt_resfile_skip_nonws(offset);
4742 if (offset == -1)
4743 return FALSE;
4744 offset = prt_resfile_skip_ws(offset);
4745 if (offset == -1)
4746 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747
Bram Moolenaar8299df92004-07-10 09:47:34 +00004748 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
4749 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750 {
4751 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4752 resource->filename);
4753 fclose(fd_resource);
4754 return FALSE;
4755 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004756 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757
4758 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004759 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
4760 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004762 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
4763 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004765 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
4766 STRLEN(PRT_RESOURCE_CMAP)) == 0)
4767 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 else
4769 {
4770 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4771 resource->filename);
4772 fclose(fd_resource);
4773 return FALSE;
4774 }
4775
Bram Moolenaar8299df92004-07-10 09:47:34 +00004776 /* Look for title and version of resource */
4777 resource->title[0] = '\0';
4778 resource->version[0] = '\0';
4779 seen_title = FALSE;
4780 seen_version = FALSE;
4781 seen_all = FALSE;
4782 while (!seen_all && prt_next_dsc(&dsc_line))
4783 {
4784 switch (dsc_line.type)
4785 {
4786 case PRT_DSC_TITLE_TYPE:
4787 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
4788 resource->title[dsc_line.len] = '\0';
4789 seen_title = TRUE;
4790 if (seen_version)
4791 seen_all = TRUE;
4792 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793
Bram Moolenaar8299df92004-07-10 09:47:34 +00004794 case PRT_DSC_VERSION_TYPE:
4795 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
4796 resource->version[dsc_line.len] = '\0';
4797 seen_version = TRUE;
4798 if (seen_title)
4799 seen_all = TRUE;
4800 break;
4801
4802 case PRT_DSC_ENDCOMMENTS_TYPE:
4803 /* Wont find title or resource after this comment, stop searching */
4804 seen_all = TRUE;
4805 break;
4806
4807 case PRT_DSC_MISC_TYPE:
4808 /* Not interested in whatever comment this line had */
4809 break;
4810 }
4811 }
4812
4813 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814 {
4815 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4816 resource->filename);
4817 fclose(fd_resource);
4818 return FALSE;
4819 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820
4821 fclose(fd_resource);
4822
4823 return TRUE;
4824}
4825
4826 static int
4827prt_check_resource(resource, version)
4828 struct prt_ps_resource_S *resource;
4829 char_u *version;
4830{
4831 /* Version number m.n should match, the revision number does not matter */
4832 if (STRNCMP(resource->version, version, STRLEN(version)))
4833 {
4834 EMSG2(_("E621: \"%s\" resource file has wrong version"),
4835 resource->name);
4836 return FALSE;
4837 }
4838
4839 /* Other checks to be added as needed */
4840 return TRUE;
4841}
4842
4843 static void
4844prt_dsc_start()
4845{
4846 prt_write_string("%!PS-Adobe-3.0\n");
4847}
4848
4849 static void
4850prt_dsc_noarg(comment)
4851 char *comment;
4852{
4853 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
4854 prt_write_file(prt_line_buffer);
4855}
4856
4857 static void
4858prt_dsc_textline(comment, text)
4859 char *comment;
4860 char *text;
4861{
4862 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
4863 prt_write_file(prt_line_buffer);
4864}
4865
4866 static void
4867prt_dsc_text(comment, text)
4868 char *comment;
4869 char *text;
4870{
4871 /* TODO - should scan 'text' for any chars needing escaping! */
4872 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
4873 prt_write_file(prt_line_buffer);
4874}
4875
4876#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
4877
4878 static void
4879prt_dsc_ints(comment, count, ints)
4880 char *comment;
4881 int count;
4882 int *ints;
4883{
4884 int i;
4885
4886 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
4887 prt_write_file(prt_line_buffer);
4888
4889 for (i = 0; i < count; i++)
4890 {
4891 sprintf((char *)prt_line_buffer, " %d", ints[i]);
4892 prt_write_file(prt_line_buffer);
4893 }
4894
4895 prt_write_string("\n");
4896}
4897
4898 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00004899prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 char *comment; /* if NULL add to previous */
4901 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004902 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004903{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904 if (comment != NULL)
4905 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
4906 else
4907 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
4908 prt_write_file(prt_line_buffer);
4909
Bram Moolenaar8299df92004-07-10 09:47:34 +00004910 sprintf((char *)prt_line_buffer, " %s\n", string);
4911 prt_write_file(prt_line_buffer);
4912}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913
Bram Moolenaar8299df92004-07-10 09:47:34 +00004914 static void
4915prt_dsc_font_resource(resource, ps_font)
4916 char *resource;
4917 struct prt_ps_font_S *ps_font;
4918{
4919 int i;
4920
4921 prt_dsc_resources(resource, "font",
4922 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
4923 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
4924 if (ps_font->ps_fontname[i] != NULL)
4925 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926}
4927
4928 static void
4929prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
4930 int duplex;
4931 int tumble;
4932 int collate;
4933 int color;
4934 int num_copies;
4935{
4936 /* Only output the comment if we need to.
4937 * Note: tumble is ignored if we are not duplexing
4938 */
4939 if (!(duplex || collate || color || (num_copies > 1)))
4940 return;
4941
4942 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
4943 prt_write_file(prt_line_buffer);
4944
4945 if (duplex)
4946 {
4947 prt_write_string(" duplex");
4948 if (tumble)
4949 prt_write_string("(tumble)");
4950 }
4951 if (collate)
4952 prt_write_string(" collate");
4953 if (color)
4954 prt_write_string(" color");
4955 if (num_copies > 1)
4956 {
4957 prt_write_string(" numcopies(");
4958 /* Note: no space wanted so dont use prt_write_int() */
4959 sprintf((char *)prt_line_buffer, "%d", num_copies);
4960 prt_write_file(prt_line_buffer);
4961 prt_write_string(")");
4962 }
4963 prt_write_string("\n");
4964}
4965
4966 static void
4967prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
4968 char *paper_name;
4969 double width;
4970 double height;
4971 double weight;
4972 char *colour;
4973 char *type;
4974{
4975 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
4976 prt_write_file(prt_line_buffer);
4977 prt_write_real(width, 2);
4978 prt_write_real(height, 2);
4979 prt_write_real(weight, 2);
4980 if (colour == NULL)
4981 prt_write_string("()");
4982 else
4983 prt_write_string(colour);
4984 prt_write_string(" ");
4985 if (type == NULL)
4986 prt_write_string("()");
4987 else
4988 prt_write_string(type);
4989 prt_write_string("\n");
4990}
4991
4992 void
4993mch_print_cleanup()
4994{
4995#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004996 if (prt_out_mbyte)
4997 {
4998 int i;
4999
5000 /* Free off all CID font names created, but first clear duplicate
5001 * pointers to the same string (when the same font is used for more than
5002 * one style).
5003 */
5004 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5005 {
5006 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5007 vim_free(prt_ps_mb_font.ps_fontname[i]);
5008 prt_ps_mb_font.ps_fontname[i] = NULL;
5009 }
5010 }
5011
Bram Moolenaar071d4272004-06-13 20:20:40 +00005012 if (prt_do_conv)
5013 {
5014 convert_setup(&prt_conv, NULL, NULL);
5015 prt_do_conv = FALSE;
5016 }
5017#endif
5018 if (prt_ps_fd != NULL)
5019 {
5020 fclose(prt_ps_fd);
5021 prt_ps_fd = NULL;
5022 prt_file_error = FALSE;
5023 }
5024 if (prt_ps_file_name != NULL)
5025 {
5026 vim_free(prt_ps_file_name);
5027 prt_ps_file_name = NULL;
5028 }
5029}
5030
5031 static float
5032to_device_units(idx, physsize, def_number)
5033 int idx;
5034 double physsize;
5035 int def_number;
5036{
5037 float ret;
5038 int u;
5039 int nr;
5040
5041 u = prt_get_unit(idx);
5042 if (u == PRT_UNIT_NONE)
5043 {
5044 u = PRT_UNIT_PERC;
5045 nr = def_number;
5046 }
5047 else
5048 nr = printer_opts[idx].number;
5049
5050 switch (u)
5051 {
5052 case PRT_UNIT_INCH:
5053 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5054 break;
5055 case PRT_UNIT_MM:
5056 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5057 break;
5058 case PRT_UNIT_POINT:
5059 ret = (float)nr;
5060 break;
5061 case PRT_UNIT_PERC:
5062 default:
5063 ret = (float)(physsize * nr) / 100;
5064 break;
5065 }
5066
5067 return ret;
5068}
5069
5070/*
5071 * Calculate margins for given width and height from printoptions settings.
5072 */
5073 static void
5074prt_page_margins(width, height, left, right, top, bottom)
5075 double width;
5076 double height;
5077 double *left;
5078 double *right;
5079 double *top;
5080 double *bottom;
5081{
5082 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5083 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5084 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5085 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5086}
5087
5088 static void
5089prt_font_metrics(font_scale)
5090 int font_scale;
5091{
5092 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005093 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094}
5095
5096
5097 static int
5098prt_get_cpl()
5099{
5100 if (prt_use_number())
5101 {
5102 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005103#ifdef FEAT_MBYTE
5104 /* If we are outputting multi-byte characters then line numbers will be
5105 * printed with half width characters
5106 */
5107 if (prt_out_mbyte)
5108 prt_number_width /= 2;
5109#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 prt_left_margin += prt_number_width;
5111 }
5112 else
5113 prt_number_width = 0.0;
5114
5115 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5116}
5117
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005118#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005119 static int
5120prt_build_cid_fontname(font, name, name_len)
5121 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005122 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005123 int name_len;
5124{
5125 char *fontname;
5126
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005127 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005128 if (fontname == NULL)
5129 return FALSE;
5130 STRNCPY(fontname, name, name_len);
5131 fontname[name_len] = '\0';
5132 prt_ps_mb_font.ps_fontname[font] = fontname;
5133
5134 return TRUE;
5135}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005136#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005137
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138/*
5139 * Get number of lines of text that fit on a page (excluding the header).
5140 */
5141 static int
5142prt_get_lpp()
5143{
5144 int lpp;
5145
5146 /*
5147 * Calculate offset to lower left corner of background rect based on actual
5148 * font height (based on its bounding box) and the line height, handling the
5149 * case where the font height can exceed the line height.
5150 */
5151 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005152 prt_ps_font->bbox_min_y);
5153 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 {
5155 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005156 (1000.0 - (prt_ps_font->bbox_max_y -
5157 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 }
5159
5160 /* Get height for topmost line based on background rect offset. */
5161 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5162
5163 /* Calculate lpp */
5164 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5165
5166 /* Adjust top margin if there is a header */
5167 prt_top_margin -= prt_line_height * prt_header_height();
5168
5169 return lpp - prt_header_height();
5170}
5171
Bram Moolenaar8299df92004-07-10 09:47:34 +00005172#ifdef FEAT_MBYTE
5173 static int
5174prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005175 char *p_encoding;
5176 struct prt_ps_mbfont_S *p_cmap;
5177 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005178{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005179 int mbenc;
5180 int enc_len;
5181 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005182
5183 *pp_mbenc = NULL;
5184 /* Look for recognised encoding */
5185 enc_len = STRLEN(p_encoding);
5186 p_mbenc = p_cmap->encodings;
5187 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5188 {
5189 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5190 {
5191 *pp_mbenc = p_mbenc;
5192 return TRUE;
5193 }
5194 p_mbenc++;
5195 }
5196 return FALSE;
5197}
5198
5199 static int
5200prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005201 char *p_charset;
5202 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005203 struct prt_ps_charset_S **pp_mbchar;
5204{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005205 int mbchar;
5206 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005207 struct prt_ps_charset_S *p_mbchar;
5208
5209 /* Look for recognised character set, using default if one is not given */
5210 if (*p_charset == NUL)
5211 p_charset = p_cmap->defcs;
5212 char_len = STRLEN(p_charset);
5213 p_mbchar = p_cmap->charsets;
5214 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5215 {
5216 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5217 {
5218 *pp_mbchar = p_mbchar;
5219 return TRUE;
5220 }
5221 p_mbchar++;
5222 }
5223 return FALSE;
5224}
5225#endif
5226
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227/*ARGSUSED*/
5228 int
5229mch_print_init(psettings, jobname, forceit)
5230 prt_settings_T *psettings;
5231 char_u *jobname;
5232 int forceit;
5233{
5234 int i;
5235 char *paper_name;
5236 int paper_strlen;
5237 int fontsize;
5238 char_u *p;
5239 double left;
5240 double right;
5241 double top;
5242 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005243#ifdef FEAT_MBYTE
5244 int cmap;
5245 int pmcs_len;
5246 char_u *p_encoding;
5247 struct prt_ps_encoding_S *p_mbenc;
5248 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005249 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005250#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005251
5252#if 0
5253 /*
5254 * TODO:
5255 * If "forceit" is false: pop up a dialog to select:
5256 * - printer name
5257 * - copies
5258 * - collated/uncollated
5259 * - duplex off/long side/short side
5260 * - paper size
5261 * - portrait/landscape
5262 * - font size
5263 *
5264 * If "forceit" is true: use the default printer and settings
5265 */
5266 if (forceit)
5267 s_pd.Flags |= PD_RETURNDEFAULT;
5268#endif
5269
5270 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005271 * Set up font and encoding.
5272 */
5273#ifdef FEAT_MBYTE
5274 p_encoding = enc_skip(p_penc);
5275 if (*p_encoding == NUL)
5276 p_encoding = enc_skip(p_enc);
5277
5278 /* Look for recognised multi-byte coding, and if the charset is recognised.
5279 * This is to cope with the fact that various unicode encodings are
5280 * supported in more than one of CJK. */
5281 p_mbenc = NULL;
5282 p_mbenc_first = NULL;
5283 p_mbchar = NULL;
5284 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005285 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5286 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005287 {
5288 if (p_mbenc_first == NULL)
5289 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005290 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5291 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005292 break;
5293 }
5294
5295 /* Use first encoding matched if no charset matched */
5296 if (p_mbchar == NULL && p_mbenc_first != NULL)
5297 p_mbenc = p_mbenc_first;
5298
5299 prt_out_mbyte = (p_mbenc != NULL);
5300 if (prt_out_mbyte)
5301 {
5302 /* Build CMap name - will be same for all multi-byte fonts used */
5303 prt_cmap[0] = '\0';
5304
5305 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5306
5307 if (!prt_custom_cmap)
5308 {
5309 /* Check encoding and character set are compatible */
5310 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5311 {
5312 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5313 return FALSE;
5314 }
5315
5316 /* Add charset name if not empty */
5317 if (p_mbchar->cmap_charset != NULL)
5318 {
5319 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5320 STRCAT(prt_cmap, "-");
5321 }
5322 }
5323 else
5324 {
5325 /* Add custom CMap character set name */
5326 pmcs_len = STRLEN(p_pmcs);
5327 if (pmcs_len == 0)
5328 {
5329 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5330 return FALSE;
5331 }
5332 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5333 prt_cmap[pmcs_len] = '\0';
5334 STRCAT(prt_cmap, "-");
5335 }
5336
5337 /* CMap name ends with (optional) encoding name and -H for horizontal */
5338 if (p_mbenc->cmap_encoding != NULL)
5339 {
5340 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5341 STRCAT(prt_cmap, "-");
5342 }
5343 STRCAT(prt_cmap, "H");
5344
5345 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5346 {
5347 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5348 return FALSE;
5349 }
5350
5351 /* Derive CID font names with fallbacks if not defined */
5352 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5353 mbfont_opts[OPT_MBFONT_REGULAR].string,
5354 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5355 return FALSE;
5356 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5357 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5358 mbfont_opts[OPT_MBFONT_BOLD].string,
5359 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5360 return FALSE;
5361 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5362 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5363 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
5364 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
5365 return FALSE;
5366 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
5367 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005368 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
5369 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005370 return FALSE;
5371
5372 /* Check if need to use Courier for ASCII code range, and if so pick up
5373 * the encoding to use */
5374 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
5375 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
5376 if (prt_use_courier)
5377 {
5378 /* Use national ASCII variant unless ASCII wanted */
5379 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
5380 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
5381 prt_ascii_encoding = "ascii";
5382 else
5383 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
5384 }
5385
5386 prt_ps_font = &prt_ps_mb_font;
5387 }
5388 else
5389#endif
5390 {
5391#ifdef FEAT_MBYTE
5392 prt_use_courier = FALSE;
5393#endif
5394 prt_ps_font = &prt_ps_courier_font;
5395 }
5396
5397 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398 * Find the size of the paper and set the margins.
5399 */
5400 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
5401 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
5402 if (printer_opts[OPT_PRINT_PAPER].present)
5403 {
5404 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
5405 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
5406 }
5407 else
5408 {
5409 paper_name = "A4";
5410 paper_strlen = 2;
5411 }
5412 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
5413 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
5414 && STRNICMP(prt_mediasize[i].name, paper_name,
5415 paper_strlen) == 0)
5416 break;
5417 if (i == PRT_MEDIASIZE_LEN)
5418 i = 0;
5419 prt_media = i;
5420
5421 /*
5422 * Set PS pagesize based on media dimensions and print orientation.
5423 * Note: Media and page sizes have defined meanings in PostScript and should
5424 * be kept distinct. Media is the paper (or transparency, or ...) that is
5425 * printed on, whereas the page size is the area that the PostScript
5426 * interpreter renders into.
5427 */
5428 if (prt_portrait)
5429 {
5430 prt_page_width = prt_mediasize[i].width;
5431 prt_page_height = prt_mediasize[i].height;
5432 }
5433 else
5434 {
5435 prt_page_width = prt_mediasize[i].height;
5436 prt_page_height = prt_mediasize[i].width;
5437 }
5438
5439 /*
5440 * Set PS page margins based on the PS pagesize, not the mediasize - this
5441 * needs to be done before the cpl and lpp are calculated.
5442 */
5443 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
5444 &bottom);
5445 prt_left_margin = (float)left;
5446 prt_right_margin = (float)right;
5447 prt_top_margin = (float)top;
5448 prt_bottom_margin = (float)bottom;
5449
5450 /*
5451 * Set up the font size.
5452 */
5453 fontsize = PRT_PS_DEFAULT_FONTSIZE;
5454 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
5455 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
5456 fontsize = atoi((char *)p + 2);
5457 prt_font_metrics(fontsize);
5458
5459 /*
5460 * Return the number of characters per line, and lines per page for the
5461 * generic print code.
5462 */
5463 psettings->chars_per_line = prt_get_cpl();
5464 psettings->lines_per_page = prt_get_lpp();
5465
5466 /* Catch margin settings that leave no space for output! */
5467 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
5468 return FAIL;
5469
5470 /*
5471 * Sort out the number of copies to be printed. PS by default will do
5472 * uncollated copies for you, so once we know how many uncollated copies are
5473 * wanted cache it away and lie to the generic code that we only want one
5474 * uncollated copy.
5475 */
5476 psettings->n_collated_copies = 1;
5477 psettings->n_uncollated_copies = 1;
5478 prt_num_copies = 1;
5479 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
5480 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
5481 if (prt_collate)
5482 {
5483 /* TODO: Get number of collated copies wanted. */
5484 psettings->n_collated_copies = 1;
5485 }
5486 else
5487 {
5488 /* TODO: Get number of uncollated copies wanted and update the cached
5489 * count.
5490 */
5491 prt_num_copies = 1;
5492 }
5493
5494 psettings->jobname = jobname;
5495
5496 /*
5497 * Set up printer duplex and tumble based on Duplex option setting - default
5498 * is long sided duplex printing (i.e. no tumble).
5499 */
5500 prt_duplex = TRUE;
5501 prt_tumble = FALSE;
5502 psettings->duplex = 1;
5503 if (printer_opts[OPT_PRINT_DUPLEX].present)
5504 {
5505 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
5506 {
5507 prt_duplex = FALSE;
5508 psettings->duplex = 0;
5509 }
5510 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
5511 == 0)
5512 prt_tumble = TRUE;
5513 }
5514
5515 /* For now user abort not supported */
5516 psettings->user_abort = 0;
5517
5518 /* If the user didn't specify a file name, use a temp file. */
5519 if (psettings->outfile == NULL)
5520 {
5521 prt_ps_file_name = vim_tempname('p');
5522 if (prt_ps_file_name == NULL)
5523 {
5524 EMSG(_(e_notmp));
5525 return FAIL;
5526 }
5527 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
5528 }
5529 else
5530 {
5531 p = expand_env_save(psettings->outfile);
5532 if (p != NULL)
5533 {
5534 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
5535 vim_free(p);
5536 }
5537 }
5538 if (prt_ps_fd == NULL)
5539 {
5540 EMSG(_("E324: Can't open PostScript output file"));
5541 mch_print_cleanup();
5542 return FAIL;
5543 }
5544
Bram Moolenaar8299df92004-07-10 09:47:34 +00005545 prt_bufsiz = psettings->chars_per_line;
5546#ifdef FEAT_MBYTE
5547 if (prt_out_mbyte)
5548 prt_bufsiz *= 2;
5549#endif
5550 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551
5552 prt_page_num = 0;
5553
5554 prt_attribute_change = FALSE;
5555 prt_need_moveto = FALSE;
5556 prt_need_font = FALSE;
5557 prt_need_fgcol = FALSE;
5558 prt_need_bgcol = FALSE;
5559 prt_need_underline = FALSE;
5560
5561 prt_file_error = FALSE;
5562
5563 return OK;
5564}
5565
5566 static int
5567prt_add_resource(resource)
5568 struct prt_ps_resource_S *resource;
5569{
5570 FILE* fd_resource;
5571 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 size_t bytes_read;
5573
5574 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5575 if (fd_resource == NULL)
5576 {
5577 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
5578 return FALSE;
5579 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005580 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
5581 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582
5583 prt_dsc_textline("BeginDocument", (char *)resource->filename);
5584
5585 for (;;)
5586 {
5587 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
5588 sizeof(resource_buffer), fd_resource);
5589 if (ferror(fd_resource))
5590 {
5591 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5592 resource->filename);
5593 fclose(fd_resource);
5594 return FALSE;
5595 }
5596 if (bytes_read == 0)
5597 break;
5598 prt_write_file_raw_len(resource_buffer, bytes_read);
5599 if (prt_file_error)
5600 {
5601 fclose(fd_resource);
5602 return FALSE;
5603 }
5604 }
5605 fclose(fd_resource);
5606
5607 prt_dsc_noarg("EndDocument");
5608
5609 prt_dsc_noarg("EndResource");
5610
5611 return TRUE;
5612}
5613
5614 int
5615mch_print_begin(psettings)
5616 prt_settings_T *psettings;
5617{
5618 time_t now;
5619 int bbox[4];
5620 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 double left;
5622 double right;
5623 double top;
5624 double bottom;
5625 struct prt_ps_resource_S res_prolog;
5626 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005627 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 char_u *p_encoding;
5629#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005630 struct prt_ps_resource_S res_cidfont;
5631 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632#endif
5633
5634 /*
5635 * PS DSC Header comments - no PS code!
5636 */
5637 prt_dsc_start();
5638 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005639 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005640 STRCPY(buffer, "Unknown");
5641 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642 prt_dsc_textline("Creator", VIM_VERSION_LONG);
5643 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
5644 now = time(NULL);
5645 p_time = ctime(&now);
5646 /* Note: ctime() adds a \n so we have to remove it :-( */
5647 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
5648 prt_dsc_textline("CreationDate", p_time);
5649 prt_dsc_textline("DocumentData", "Clean8Bit");
5650 prt_dsc_textline("Orientation", "Portrait");
5651 prt_dsc_atend("Pages");
5652 prt_dsc_textline("PageOrder", "Ascend");
5653 /* The bbox does not change with orientation - it is always in the default
5654 * user coordinate system! We have to recalculate right and bottom
5655 * coordinates based on the font metrics for the bbox to be accurate. */
5656 prt_page_margins(prt_mediasize[prt_media].width,
5657 prt_mediasize[prt_media].height,
5658 &left, &right, &top, &bottom);
5659 bbox[0] = (int)left;
5660 if (prt_portrait)
5661 {
5662 /* In portrait printing the fixed point is the top left corner so we
5663 * derive the bbox from that point. We have the expected cpl chars
5664 * across the media and lpp lines down the media.
5665 */
5666 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
5667 * prt_line_height);
5668 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
5669 + 0.5);
5670 bbox[3] = (int)(top + 0.5);
5671 }
5672 else
5673 {
5674 /* In landscape printing the fixed point is the bottom left corner so we
5675 * derive the bbox from that point. We have lpp chars across the media
5676 * and cpl lines up the media.
5677 */
5678 bbox[1] = (int)bottom;
5679 bbox[2] = (int)(left + ((psettings->lines_per_page
5680 + prt_header_height()) * prt_line_height) + 0.5);
5681 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
5682 + 0.5);
5683 }
5684 prt_dsc_ints("BoundingBox", 4, bbox);
5685 /* The media width and height does not change with landscape printing! */
5686 prt_dsc_docmedia(prt_mediasize[prt_media].name,
5687 prt_mediasize[prt_media].width,
5688 prt_mediasize[prt_media].height,
5689 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005690 /* Define fonts needed */
5691#ifdef FEAT_MBYTE
5692 if (!prt_out_mbyte || prt_use_courier)
5693#endif
5694 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
5695#ifdef FEAT_MBYTE
5696 if (prt_out_mbyte)
5697 {
5698 prt_dsc_font_resource((prt_use_courier ? NULL
5699 : "DocumentNeededResources"), &prt_ps_mb_font);
5700 if (!prt_custom_cmap)
5701 prt_dsc_resources(NULL, "cmap", prt_cmap);
5702 }
5703#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704
Bram Moolenaar8299df92004-07-10 09:47:34 +00005705 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 if (!prt_find_resource("prolog", &res_prolog))
5707 {
5708 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
5709 return FALSE;
5710 }
5711 if (!prt_open_resource(&res_prolog))
5712 return FALSE;
5713 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
5714 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005715#ifdef FEAT_MBYTE
5716 if (prt_out_mbyte)
5717 {
5718 /* Look for required version of multi-byte printing procset */
5719 if (!prt_find_resource("cidfont", &res_cidfont))
5720 {
5721 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
5722 return FALSE;
5723 }
5724 if (!prt_open_resource(&res_cidfont))
5725 return FALSE;
5726 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
5727 return FALSE;
5728 }
5729#endif
5730
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 /* Find an encoding to use for printing.
5732 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
5733 * that cannot be found then default to "latin1".
5734 * Note: VIM specific encoding header is always skipped.
5735 */
5736#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005737 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005740 p_encoding = enc_skip(p_penc);
5741 if (*p_encoding == NUL
5742 || !prt_find_resource((char *)p_encoding, &res_encoding))
5743 {
5744 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005746 int props;
5747
5748 p_encoding = enc_skip(p_enc);
5749 props = enc_canon_props(p_encoding);
5750 if (!(props & ENC_8BIT)
5751 || !prt_find_resource((char *)p_encoding, &res_encoding))
5752 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005754 {
5755 /* Use latin1 as default printing encoding */
5756 p_encoding = (char_u *)"latin1";
5757 if (!prt_find_resource((char *)p_encoding, &res_encoding))
5758 {
5759 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5760 p_encoding);
5761 return FALSE;
5762 }
5763 }
5764 }
5765 if (!prt_open_resource(&res_encoding))
5766 return FALSE;
5767 /* For the moment there are no checks on encoding resource files to
5768 * perform */
5769#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005771 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005773 p_encoding = enc_skip(p_penc);
5774 if (*p_encoding == NUL)
5775 p_encoding = enc_skip(p_enc);
5776 if (prt_use_courier)
5777 {
5778 /* Include ASCII range encoding vector */
5779 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
5780 {
5781 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005782 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005783 return FALSE;
5784 }
5785 if (!prt_open_resource(&res_encoding))
5786 return FALSE;
5787 /* For the moment there are no checks on encoding resource files to
5788 * perform */
5789 }
5790 }
5791
5792 prt_conv.vc_type = CONV_NONE;
5793 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
5794 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
5796 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005797 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 p_encoding);
5799 return FALSE;
5800 }
5801 prt_do_conv = TRUE;
5802 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005803 prt_do_conv = prt_conv.vc_type != CONV_NONE;
5804
5805 if (prt_out_mbyte && prt_custom_cmap)
5806 {
5807 /* Find user supplied CMap */
5808 if (!prt_find_resource(prt_cmap, &res_cmap))
5809 {
5810 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005811 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005812 return FALSE;
5813 }
5814 if (!prt_open_resource(&res_cmap))
5815 return FALSE;
5816 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817#endif
5818
5819 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820 STRCPY(buffer, res_prolog.title);
5821 STRCAT(buffer, " ");
5822 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005823 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
5824#ifdef FEAT_MBYTE
5825 if (prt_out_mbyte)
5826 {
5827 STRCPY(buffer, res_cidfont.title);
5828 STRCAT(buffer, " ");
5829 STRCAT(buffer, res_cidfont.version);
5830 prt_dsc_resources(NULL, "procset", buffer);
5831
5832 if (prt_custom_cmap)
5833 {
5834 STRCPY(buffer, res_cmap.title);
5835 STRCAT(buffer, " ");
5836 STRCAT(buffer, res_cmap.version);
5837 prt_dsc_resources(NULL, "cmap", buffer);
5838 }
5839 }
5840 if (!prt_out_mbyte || prt_use_courier)
5841#endif
5842 {
5843 STRCPY(buffer, res_encoding.title);
5844 STRCAT(buffer, " ");
5845 STRCAT(buffer, res_encoding.version);
5846 prt_dsc_resources(NULL, "encoding", buffer);
5847 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005848 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
5849#ifdef FEAT_SYN_HL
5850 psettings->do_syntax
5851#else
5852 0
5853#endif
5854 , prt_num_copies);
5855 prt_dsc_noarg("EndComments");
5856
5857 /*
5858 * PS Document page defaults
5859 */
5860 prt_dsc_noarg("BeginDefaults");
5861
5862 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005863#ifdef FEAT_MBYTE
5864 if (!prt_out_mbyte || prt_use_courier)
5865#endif
5866 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
5867#ifdef FEAT_MBYTE
5868 if (prt_out_mbyte)
5869 {
5870 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
5871 &prt_ps_mb_font);
5872 if (!prt_custom_cmap)
5873 prt_dsc_resources(NULL, "cmap", prt_cmap);
5874 }
5875#endif
5876
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 /* Paper will be used for all pages */
5878 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
5879
5880 prt_dsc_noarg("EndDefaults");
5881
5882 /*
5883 * PS Document prolog inclusion - all required procsets.
5884 */
5885 prt_dsc_noarg("BeginProlog");
5886
Bram Moolenaar8299df92004-07-10 09:47:34 +00005887 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005888 if (!prt_add_resource(&res_prolog))
5889 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005890#ifdef FEAT_MBYTE
5891 if (prt_out_mbyte)
5892 {
5893 /* Add CID font procset, and any user supplied CMap */
5894 if (!prt_add_resource(&res_cidfont))
5895 return FALSE;
5896 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
5897 return FALSE;
5898 }
5899#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900
Bram Moolenaar8299df92004-07-10 09:47:34 +00005901#ifdef FEAT_MBYTE
5902 if (!prt_out_mbyte || prt_use_courier)
5903#endif
5904 /* There will be only one Roman font encoding to be included in the PS
5905 * file. */
5906 if (!prt_add_resource(&res_encoding))
5907 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908
5909 prt_dsc_noarg("EndProlog");
5910
5911 /*
5912 * PS Document setup - must appear after the prolog
5913 */
5914 prt_dsc_noarg("BeginSetup");
5915
5916 /* Device setup - page size and number of uncollated copies */
5917 prt_write_int((int)prt_mediasize[prt_media].width);
5918 prt_write_int((int)prt_mediasize[prt_media].height);
5919 prt_write_int(0);
5920 prt_write_string("sps\n");
5921 prt_write_int(prt_num_copies);
5922 prt_write_string("nc\n");
5923 prt_write_boolean(prt_duplex);
5924 prt_write_boolean(prt_tumble);
5925 prt_write_string("dt\n");
5926 prt_write_boolean(prt_collate);
5927 prt_write_string("c\n");
5928
5929 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005930#ifdef FEAT_MBYTE
5931 if (!prt_out_mbyte || prt_use_courier)
5932 {
5933 /* When using Courier for ASCII range when printing multi-byte, need to
5934 * pick up ASCII encoding to use with it. */
5935 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005936 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005937#endif
5938 prt_dsc_resources("IncludeResource", "font",
5939 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5940 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
5941 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5942 prt_dsc_resources("IncludeResource", "font",
5943 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5944 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
5945 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5946 prt_dsc_resources("IncludeResource", "font",
5947 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5948 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
5949 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5950 prt_dsc_resources("IncludeResource", "font",
5951 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5952 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
5953 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5954#ifdef FEAT_MBYTE
5955 }
5956 if (prt_out_mbyte)
5957 {
5958 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
5959 * not have an italic form being a western style, so where no font is
5960 * defined for these faces VIM falls back to an existing face.
5961 * Note: if using Courier for the ASCII range then the printout will
5962 * have bold/italic/bolditalic regardless of the setting of printmbfont.
5963 */
5964 prt_dsc_resources("IncludeResource", "font",
5965 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5966 if (!prt_custom_cmap)
5967 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5968 prt_def_cidfont("CF0", (int)prt_line_height,
5969 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5970
5971 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
5972 {
5973 prt_dsc_resources("IncludeResource", "font",
5974 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5975 if (!prt_custom_cmap)
5976 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5977 prt_def_cidfont("CF1", (int)prt_line_height,
5978 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5979 }
5980 else
5981 /* Use ROMAN for BOLD */
5982 prt_dup_cidfont("CF0", "CF1");
5983
5984 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
5985 {
5986 prt_dsc_resources("IncludeResource", "font",
5987 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5988 if (!prt_custom_cmap)
5989 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5990 prt_def_cidfont("CF2", (int)prt_line_height,
5991 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5992 }
5993 else
5994 /* Use ROMAN for OBLIQUE */
5995 prt_dup_cidfont("CF0", "CF2");
5996
5997 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
5998 {
5999 prt_dsc_resources("IncludeResource", "font",
6000 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6001 if (!prt_custom_cmap)
6002 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6003 prt_def_cidfont("CF3", (int)prt_line_height,
6004 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6005 }
6006 else
6007 /* Use BOLD for BOLDOBLIQUE */
6008 prt_dup_cidfont("CF1", "CF3");
6009 }
6010#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011
6012 /* Misc constant vars used for underlining and background rects */
6013 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006014 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006016 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017 prt_def_var("BO", prt_bgcol_offset, 2);
6018
6019 prt_dsc_noarg("EndSetup");
6020
6021 /* Fail if any problems writing out to the PS file */
6022 return !prt_file_error;
6023}
6024
6025 void
6026mch_print_end(psettings)
6027 prt_settings_T *psettings;
6028{
6029 prt_dsc_noarg("Trailer");
6030
6031 /*
6032 * Output any info we don't know in toto until we finish
6033 */
6034 prt_dsc_ints("Pages", 1, &prt_page_num);
6035
6036 prt_dsc_noarg("EOF");
6037
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006038 /* Write CTRL-D to close serial communication link if used.
6039 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006040 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006041
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 if (!prt_file_error && psettings->outfile == NULL
6043 && !got_int && !psettings->user_abort)
6044 {
6045 /* Close the file first. */
6046 if (prt_ps_fd != NULL)
6047 {
6048 fclose(prt_ps_fd);
6049 prt_ps_fd = NULL;
6050 }
6051 prt_message((char_u *)_("Sending to printer..."));
6052
6053 /* Not printing to a file: use 'printexpr' to print the file. */
6054 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6055 EMSG(_("E365: Failed to print PostScript file"));
6056 else
6057 prt_message((char_u *)_("Print job sent."));
6058 }
6059
6060 mch_print_cleanup();
6061}
6062
6063 int
6064mch_print_end_page()
6065{
6066 prt_flush_buffer();
6067
6068 prt_write_string("re sp\n");
6069
6070 prt_dsc_noarg("PageTrailer");
6071
6072 return !prt_file_error;
6073}
6074
6075/*ARGSUSED*/
6076 int
6077mch_print_begin_page(str)
6078 char_u *str;
6079{
6080 int page_num[2];
6081
6082 prt_page_num++;
6083
6084 page_num[0] = page_num[1] = prt_page_num;
6085 prt_dsc_ints("Page", 2, page_num);
6086
6087 prt_dsc_noarg("BeginPageSetup");
6088
Bram Moolenaar8299df92004-07-10 09:47:34 +00006089 prt_write_string("sv\n0 g\n");
6090#ifdef FEAT_MBYTE
6091 prt_in_ascii = !prt_out_mbyte;
6092 if (prt_out_mbyte)
6093 prt_write_string("CF0 sf\n");
6094 else
6095#endif
6096 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097 prt_fgcol = PRCOLOR_BLACK;
6098 prt_bgcol = PRCOLOR_WHITE;
6099 prt_font = PRT_PS_FONT_ROMAN;
6100
6101 /* Set up page transformation for landscape printing. */
6102 if (!prt_portrait)
6103 {
6104 prt_write_int(-((int)prt_mediasize[prt_media].width));
6105 prt_write_string("sl\n");
6106 }
6107
6108 prt_dsc_noarg("EndPageSetup");
6109
6110 /* We have reset the font attributes, force setting them again. */
6111 curr_bg = (long_u)0xffffffff;
6112 curr_fg = (long_u)0xffffffff;
6113 curr_bold = MAYBE;
6114
6115 return !prt_file_error;
6116}
6117
6118 int
6119mch_print_blank_page()
6120{
6121 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6122}
6123
6124static float prt_pos_x = 0;
6125static float prt_pos_y = 0;
6126
6127 void
6128mch_print_start_line(margin, page_line)
6129 int margin;
6130 int page_line;
6131{
6132 prt_pos_x = prt_left_margin;
6133 if (margin)
6134 prt_pos_x -= prt_number_width;
6135
6136 prt_pos_y = prt_top_margin - prt_first_line_height -
6137 page_line * prt_line_height;
6138
6139 prt_attribute_change = TRUE;
6140 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006141#ifdef FEAT_MBYTE
6142 prt_half_width = FALSE;
6143#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144}
6145
6146/*ARGSUSED*/
6147 int
6148mch_print_text_out(p, len)
6149 char_u *p;
6150 int len;
6151{
6152 int need_break;
6153 char_u ch;
6154 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006155 float char_width;
6156 float next_pos;
6157#ifdef FEAT_MBYTE
6158 int in_ascii;
6159 int half_width;
6160#endif
6161
6162 char_width = prt_char_width;
6163
6164#ifdef FEAT_MBYTE
6165 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6166 * CJKV font to do what VIM is doing here - use a Roman font for characters
6167 * in the ASCII range, and the origingal CID font for everything else.
6168 * The problem is that GhostScript still (as of 8.13) does not support
6169 * rearranged fonts even though they have been documented by Adobe for 7
6170 * years! If they ever do, a lot of this code will disappear.
6171 */
6172 if (prt_use_courier)
6173 {
6174 in_ascii = (len == 1 && *p < 0x80);
6175 if (prt_in_ascii)
6176 {
6177 if (!in_ascii)
6178 {
6179 /* No longer in ASCII range - need to switch font */
6180 prt_in_ascii = FALSE;
6181 prt_need_font = TRUE;
6182 prt_attribute_change = TRUE;
6183 }
6184 }
6185 else if (in_ascii)
6186 {
6187 /* Now in ASCII range - need to switch font */
6188 prt_in_ascii = TRUE;
6189 prt_need_font = TRUE;
6190 prt_attribute_change = TRUE;
6191 }
6192 }
6193 if (prt_out_mbyte)
6194 {
6195 half_width = ((*mb_ptr2cells)(p) == 1);
6196 if (half_width)
6197 char_width /= 2;
6198 if (prt_half_width)
6199 {
6200 if (!half_width)
6201 {
6202 prt_half_width = FALSE;
6203 prt_pos_x += prt_char_width/4;
6204 prt_need_moveto = TRUE;
6205 prt_attribute_change = TRUE;
6206 }
6207 }
6208 else if (half_width)
6209 {
6210 prt_half_width = TRUE;
6211 prt_pos_x += prt_char_width/4;
6212 prt_need_moveto = TRUE;
6213 prt_attribute_change = TRUE;
6214 }
6215 }
6216#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006217
6218 /* Output any required changes to the graphics state, after flushing any
6219 * text buffered so far.
6220 */
6221 if (prt_attribute_change)
6222 {
6223 prt_flush_buffer();
6224 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006225 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226
6227 if (prt_need_moveto)
6228 {
6229 prt_pos_x_moveto = prt_pos_x;
6230 prt_pos_y_moveto = prt_pos_y;
6231 prt_do_moveto = TRUE;
6232
6233 prt_need_moveto = FALSE;
6234 }
6235 if (prt_need_font)
6236 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006237#ifdef FEAT_MBYTE
6238 if (!prt_in_ascii)
6239 prt_write_string("CF");
6240 else
6241#endif
6242 prt_write_string("F");
6243 prt_write_int(prt_font);
6244 prt_write_string("sf\n");
6245 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246 }
6247 if (prt_need_fgcol)
6248 {
6249 int r, g, b;
6250 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6251 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6252 b = prt_fgcol & 0xff;
6253
6254 prt_write_real(r / 255.0, 3);
6255 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 else
6258 {
6259 prt_write_real(g / 255.0, 3);
6260 prt_write_real(b / 255.0, 3);
6261 prt_write_string("r\n");
6262 }
6263 prt_need_fgcol = FALSE;
6264 }
6265
6266 if (prt_bgcol != PRCOLOR_WHITE)
6267 {
6268 prt_new_bgcol = prt_bgcol;
6269 if (prt_need_bgcol)
6270 prt_do_bgcol = TRUE;
6271 }
6272 else
6273 prt_do_bgcol = FALSE;
6274 prt_need_bgcol = FALSE;
6275
6276 if (prt_need_underline)
6277 prt_do_underline = prt_underline;
6278 prt_need_underline = FALSE;
6279
6280 prt_attribute_change = FALSE;
6281 }
6282
6283#ifdef FEAT_MBYTE
6284 if (prt_do_conv)
6285 {
6286 /* Convert from multi-byte to 8-bit encoding */
6287 p = string_convert(&prt_conv, p, &len);
6288 if (p == NULL)
6289 p = (char_u *)"";
6290 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
Bram Moolenaar8299df92004-07-10 09:47:34 +00006292 if (prt_out_mbyte)
6293 {
6294 /* Multi-byte character strings are represented more efficiently as hex
6295 * strings when outputting clean 8 bit PS.
6296 */
6297 do
6298 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006299 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006300 ga_append(&prt_ps_buffer, ch);
6301 ch = prt_hexchar[(*p) & 0xf];
6302 ga_append(&prt_ps_buffer, ch);
6303 p++;
6304 }
6305 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006306 }
6307 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006308#endif
6309 {
6310 /* Add next character to buffer of characters to output.
6311 * Note: One printed character may require several PS characters to
6312 * represent it, but we only count them as one printed character.
6313 */
6314 ch = *p;
6315 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6316 {
6317 /* Convert non-printing characters to either their escape or octal
6318 * sequence, ensures PS sent over a serial line does not interfere
6319 * with the comms protocol. Note: For EBCDIC we need to write out
6320 * the escape sequences as ASCII codes!
6321 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6322 */
6323 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6324 switch (ch)
6325 {
6326 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6327 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6328 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6329 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6330 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6331 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6332 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6333 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6334
6335 default:
6336 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6337#ifdef EBCDIC
6338 ebcdic2ascii(ch_buff, 3);
6339#endif
6340 ga_append(&prt_ps_buffer, ch_buff[0]);
6341 ga_append(&prt_ps_buffer, ch_buff[1]);
6342 ga_append(&prt_ps_buffer, ch_buff[2]);
6343 break;
6344 }
6345 }
6346 else
6347 ga_append(&prt_ps_buffer, ch);
6348 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006349
6350#ifdef FEAT_MBYTE
6351 /* Need to free any translated characters */
6352 if (prt_do_conv && (*p != NUL))
6353 vim_free(p);
6354#endif
6355
Bram Moolenaar8299df92004-07-10 09:47:34 +00006356 prt_text_run += char_width;
6357 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358
Bram Moolenaar8299df92004-07-10 09:47:34 +00006359 /* The downside of fp - use relative error on right margin check */
6360 next_pos = prt_pos_x + prt_char_width;
6361 need_break = (next_pos > prt_right_margin) &&
6362 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006363
6364 if (need_break)
6365 prt_flush_buffer();
6366
6367 return need_break;
6368}
6369
6370 void
6371mch_print_set_font(iBold, iItalic, iUnderline)
6372 int iBold;
6373 int iItalic;
6374 int iUnderline;
6375{
6376 int font = 0;
6377
6378 if (iBold)
6379 font |= 0x01;
6380 if (iItalic)
6381 font |= 0x02;
6382
6383 if (font != prt_font)
6384 {
6385 prt_font = font;
6386 prt_attribute_change = TRUE;
6387 prt_need_font = TRUE;
6388 }
6389 if (prt_underline != iUnderline)
6390 {
6391 prt_underline = iUnderline;
6392 prt_attribute_change = TRUE;
6393 prt_need_underline = TRUE;
6394 }
6395}
6396
6397 void
6398mch_print_set_bg(bgcol)
6399 long_u bgcol;
6400{
6401 prt_bgcol = bgcol;
6402 prt_attribute_change = TRUE;
6403 prt_need_bgcol = TRUE;
6404}
6405
6406 void
6407mch_print_set_fg(fgcol)
6408 long_u fgcol;
6409{
6410 if (fgcol != (long_u)prt_fgcol)
6411 {
6412 prt_fgcol = fgcol;
6413 prt_attribute_change = TRUE;
6414 prt_need_fgcol = TRUE;
6415 }
6416}
6417
6418# endif /*FEAT_POSTSCRIPT*/
6419#endif /*FEAT_PRINTER*/
6420
6421#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6422 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
6423static char *get_locale_val __ARGS((int what));
6424
6425 static char *
6426get_locale_val(what)
6427 int what;
6428{
6429 char *loc;
6430
6431 /* Obtain the locale value from the libraries. For DJGPP this is
6432 * redefined and it doesn't use the arguments. */
6433 loc = setlocale(what, NULL);
6434
6435# if defined(__BORLANDC__)
6436 if (loc != NULL)
6437 {
6438 char_u *p;
6439
6440 /* Borland returns something like "LC_CTYPE=<name>\n"
6441 * Let's try to fix that bug here... */
6442 p = vim_strchr(loc, '=');
6443 if (p != NULL)
6444 {
6445 loc = ++p;
6446 while (*p != NUL) /* remove trailing newline */
6447 {
6448 if (*p < ' ')
6449 {
6450 *p = NUL;
6451 break;
6452 }
6453 ++p;
6454 }
6455 }
6456 }
6457# endif
6458
6459 return loc;
6460}
6461#endif
6462
6463
6464#ifdef WIN32
6465/*
6466 * On MS-Windows locale names are strings like "German_Germany.1252", but
6467 * gettext expects "de". Try to translate one into another here for a few
6468 * supported languages.
6469 */
6470 static char_u *
6471gettext_lang(char_u *name)
6472{
6473 int i;
6474 static char *(mtable[]) = {
6475 "afrikaans", "af",
6476 "czech", "cs",
6477 "dutch", "nl",
6478 "german", "de",
6479 "english_united kingdom", "en_GB",
6480 "spanish", "es",
6481 "french", "fr",
6482 "italian", "it",
6483 "japanese", "ja",
6484 "korean", "ko",
6485 "norwegian", "no",
6486 "polish", "pl",
6487 "russian", "ru",
6488 "slovak", "sk",
6489 "swedish", "sv",
6490 "ukrainian", "uk",
6491 "chinese_china", "zh_CN",
6492 "chinese_taiwan", "zh_TW",
6493 NULL};
6494
6495 for (i = 0; mtable[i] != NULL; i += 2)
6496 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
6497 return mtable[i + 1];
6498 return name;
6499}
6500#endif
6501
6502#if defined(FEAT_MULTI_LANG) || defined(PROTO)
6503/*
6504 * Obtain the current messages language. Used to set the default for
6505 * 'helplang'. May return NULL or an empty string.
6506 */
6507 char_u *
6508get_mess_lang()
6509{
6510 char_u *p;
6511
6512# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
6513# if defined(LC_MESSAGES)
6514 p = (char_u *)get_locale_val(LC_MESSAGES);
6515# else
6516 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
6517 * may be set to the LCID number. */
6518 p = (char_u *)get_locale_val(LC_ALL);
6519# endif
6520# else
6521 p = mch_getenv((char_u *)"LC_ALL");
6522 if (p == NULL || *p == NUL)
6523 {
6524 p = mch_getenv((char_u *)"LC_MESSAGES");
6525 if (p == NULL || *p == NUL)
6526 p = mch_getenv((char_u *)"LANG");
6527 }
6528# endif
6529# ifdef WIN32
6530 p = gettext_lang(p);
6531# endif
6532 return p;
6533}
6534#endif
6535
Bram Moolenaardef9e822004-12-31 20:58:58 +00006536/* Complicated #if; matches with where get_mess_env() is used below. */
6537#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6538 && defined(LC_MESSAGES))) \
6539 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6540 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
6541 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542static char_u *get_mess_env __ARGS((void));
6543
6544/*
6545 * Get the language used for messages from the environment.
6546 */
6547 static char_u *
6548get_mess_env()
6549{
6550 char_u *p;
6551
6552 p = mch_getenv((char_u *)"LC_ALL");
6553 if (p == NULL || *p == NUL)
6554 {
6555 p = mch_getenv((char_u *)"LC_MESSAGES");
6556 if (p == NULL || *p == NUL)
6557 {
6558 p = mch_getenv((char_u *)"LANG");
6559 if (p != NULL && VIM_ISDIGIT(*p))
6560 p = NULL; /* ignore something like "1043" */
6561# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6562 if (p == NULL || *p == NUL)
6563 p = (char_u *)get_locale_val(LC_CTYPE);
6564# endif
6565 }
6566 }
6567 return p;
6568}
6569#endif
6570
6571#if defined(FEAT_EVAL) || defined(PROTO)
6572
6573/*
6574 * Set the "v:lang" variable according to the current locale setting.
6575 * Also do "v:lc_time"and "v:ctype".
6576 */
6577 void
6578set_lang_var()
6579{
6580 char_u *loc;
6581
6582# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6583 loc = (char_u *)get_locale_val(LC_CTYPE);
6584# else
6585 /* setlocale() not supported: use the default value */
6586 loc = (char_u *)"C";
6587# endif
6588 set_vim_var_string(VV_CTYPE, loc, -1);
6589
6590 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
6591 * back to LC_CTYPE if it's empty. */
6592# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
6593 loc = (char_u *)get_locale_val(LC_MESSAGES);
6594# else
6595 loc = get_mess_env();
6596# endif
6597 set_vim_var_string(VV_LANG, loc, -1);
6598
6599# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6600 loc = (char_u *)get_locale_val(LC_TIME);
6601# endif
6602 set_vim_var_string(VV_LC_TIME, loc, -1);
6603}
6604#endif
6605
6606#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6607 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
6608/*
6609 * ":language": Set the language (locale).
6610 */
6611 void
6612ex_language(eap)
6613 exarg_T *eap;
6614{
6615 char *loc;
6616 char_u *p;
6617 char_u *name;
6618 int what = LC_ALL;
6619 char *whatstr = "";
6620#ifdef LC_MESSAGES
6621# define VIM_LC_MESSAGES LC_MESSAGES
6622#else
6623# define VIM_LC_MESSAGES 6789
6624#endif
6625
6626 name = eap->arg;
6627
6628 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
6629 * Allow abbreviation, but require at least 3 characters to avoid
6630 * confusion with a two letter language name "me" or "ct". */
6631 p = skiptowhite(eap->arg);
6632 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
6633 {
6634 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
6635 {
6636 what = VIM_LC_MESSAGES;
6637 name = skipwhite(p);
6638 whatstr = "messages ";
6639 }
6640 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
6641 {
6642 what = LC_CTYPE;
6643 name = skipwhite(p);
6644 whatstr = "ctype ";
6645 }
6646 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
6647 {
6648 what = LC_TIME;
6649 name = skipwhite(p);
6650 whatstr = "time ";
6651 }
6652 }
6653
6654 if (*name == NUL)
6655 {
6656#ifndef LC_MESSAGES
6657 if (what == VIM_LC_MESSAGES)
6658 p = get_mess_env();
6659 else
6660#endif
6661 p = (char_u *)setlocale(what, NULL);
6662 if (p == NULL || *p == NUL)
6663 p = (char_u *)"Unknown";
6664 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
6665 }
6666 else
6667 {
6668#ifndef LC_MESSAGES
6669 if (what == VIM_LC_MESSAGES)
6670 loc = "";
6671 else
6672#endif
6673 loc = setlocale(what, (char *)name);
6674 if (loc == NULL)
6675 EMSG2(_("E197: Cannot set language to \"%s\""), name);
6676 else
6677 {
6678#ifdef HAVE_NL_MSG_CAT_CNTR
6679 /* Need to do this for GNU gettext, otherwise cached translations
6680 * will be used again. */
6681 extern int _nl_msg_cat_cntr;
6682
6683 ++_nl_msg_cat_cntr;
6684#endif
6685 /* Reset $LC_ALL, otherwise it would overrule everyting. */
6686 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
6687
6688 if (what != LC_TIME)
6689 {
6690 /* Tell gettext() what to translate to. It apparently doesn't
6691 * use the currently effective locale. Also do this when
6692 * FEAT_GETTEXT isn't defined, so that shell commands use this
6693 * value. */
6694 if (what == LC_ALL)
6695 vim_setenv((char_u *)"LANG", name);
6696 if (what != LC_CTYPE)
6697 {
6698 char_u *mname;
6699#ifdef WIN32
6700 mname = gettext_lang(name);
6701#else
6702 mname = name;
6703#endif
6704 vim_setenv((char_u *)"LC_MESSAGES", mname);
6705#ifdef FEAT_MULTI_LANG
6706 set_helplang_default(mname);
6707#endif
6708 }
6709
6710 /* Set $LC_CTYPE, because it overrules $LANG, and
6711 * gtk_set_locale() calls setlocale() again. gnome_init()
6712 * sets $LC_CTYPE to "en_US" (that's a bug!). */
6713 if (what != VIM_LC_MESSAGES)
6714 vim_setenv((char_u *)"LC_CTYPE", name);
6715# ifdef FEAT_GUI_GTK
6716 /* Let GTK know what locale we're using. Not sure this is
6717 * really needed... */
6718 if (gui.in_use)
6719 (void)gtk_set_locale();
6720# endif
6721 }
6722
6723# ifdef FEAT_EVAL
6724 /* Set v:lang, v:lc_time and v:ctype to the final result. */
6725 set_lang_var();
6726# endif
6727 }
6728 }
6729}
6730
6731# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6732/*
6733 * Function given to ExpandGeneric() to obtain the possible arguments of the
6734 * ":language" command.
6735 */
6736/*ARGSUSED*/
6737 char_u *
6738get_lang_arg(xp, idx)
6739 expand_T *xp;
6740 int idx;
6741{
6742 if (idx == 0)
6743 return (char_u *)"messages";
6744 if (idx == 1)
6745 return (char_u *)"ctype";
6746 if (idx == 2)
6747 return (char_u *)"time";
6748 return NULL;
6749}
6750# endif
6751
6752#endif