blob: eac69b13cbe004a5d7a5c2bf7449d62aa17d2a35 [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 Moolenaardcaf10e2005-01-21 11:55:25 +00001744 au_event_restore(save_ei);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745#endif
1746}
1747
1748/*
1749 * Add files[count] to the arglist of the current window after arg "after".
1750 * The file names in files[count] must have been allocated and are taken over.
1751 * Files[] itself is not taken over.
1752 * Returns index of first added argument. Returns -1 when failed (out of mem).
1753 */
1754 static int
1755alist_add_list(count, files, after)
1756 int count;
1757 char_u **files;
1758 int after; /* where to add: 0 = before first one */
1759{
1760 int i;
1761
1762 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
1763 {
1764 if (after < 0)
1765 after = 0;
1766 if (after > ARGCOUNT)
1767 after = ARGCOUNT;
1768 if (after < ARGCOUNT)
1769 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
1770 (ARGCOUNT - after) * sizeof(aentry_T));
1771 for (i = 0; i < count; ++i)
1772 {
1773 ARGLIST[after + i].ae_fname = files[i];
1774 ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
1775 }
1776 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001777 if (curwin->w_arg_idx >= after)
1778 ++curwin->w_arg_idx;
1779 return after;
1780 }
1781
1782 for (i = 0; i < count; ++i)
1783 vim_free(files[i]);
1784 return -1;
1785}
1786
1787#endif /* FEAT_LISTCMDS */
1788
1789#ifdef FEAT_EVAL
1790/*
1791 * ":compiler[!] {name}"
1792 */
1793 void
1794ex_compiler(eap)
1795 exarg_T *eap;
1796{
1797 char_u *buf;
1798 char_u *old_cur_comp = NULL;
1799 char_u *p;
1800
1801 if (*eap->arg == NUL)
1802 {
1803 /* List all compiler scripts. */
1804 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
1805 /* ) keep the indenter happy... */
1806 }
1807 else
1808 {
1809 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
1810 if (buf != NULL)
1811 {
1812 if (eap->forceit)
1813 {
1814 /* ":compiler! {name}" sets global options */
1815 do_cmdline_cmd((char_u *)
1816 "command -nargs=* CompilerSet set <args>");
1817 }
1818 else
1819 {
1820 /* ":compiler! {name}" sets local options.
1821 * To remain backwards compatible "current_compiler" is always
1822 * used. A user's compiler plugin may set it, the distributed
1823 * plugin will then skip the settings. Afterwards set
1824 * "b:current_compiler" and restore "current_compiler". */
1825 old_cur_comp = get_var_value((char_u *)"current_compiler");
1826 if (old_cur_comp != NULL)
1827 old_cur_comp = vim_strsave(old_cur_comp);
1828 do_cmdline_cmd((char_u *)
1829 "command -nargs=* CompilerSet setlocal <args>");
1830 }
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00001831 do_unlet((char_u *)"current_compiler", TRUE);
1832 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833
1834 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
1835 if (cmd_runtime(buf, TRUE) == FAIL)
1836 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
1837 vim_free(buf);
1838
1839 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
1840
1841 /* Set "b:current_compiler" from "current_compiler". */
1842 p = get_var_value((char_u *)"current_compiler");
1843 if (p != NULL)
1844 set_internal_string_var((char_u *)"b:current_compiler", p);
1845
1846 /* Restore "current_compiler" for ":compiler {name}". */
1847 if (!eap->forceit)
1848 {
1849 if (old_cur_comp != NULL)
1850 {
1851 set_internal_string_var((char_u *)"current_compiler",
1852 old_cur_comp);
1853 vim_free(old_cur_comp);
1854 }
1855 else
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00001856 do_unlet((char_u *)"current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 }
1858 }
1859 }
1860}
1861#endif
1862
1863/*
1864 * ":runtime {name}"
1865 */
1866 void
1867ex_runtime(eap)
1868 exarg_T *eap;
1869{
1870 cmd_runtime(eap->arg, eap->forceit);
1871}
1872
1873static void source_callback __ARGS((char_u *fname));
1874
1875 static void
1876source_callback(fname)
1877 char_u *fname;
1878{
1879 (void)do_source(fname, FALSE, FALSE);
1880}
1881
1882/*
1883 * Source the file "name" from all directories in 'runtimepath'.
1884 * "name" can contain wildcards.
1885 * When "all" is TRUE, source all files, otherwise only the first one.
1886 * return FAIL when no file could be sourced, OK otherwise.
1887 */
1888 int
1889cmd_runtime(name, all)
1890 char_u *name;
1891 int all;
1892{
1893 return do_in_runtimepath(name, all, source_callback);
1894}
1895
1896/*
1897 * Find "name" in 'runtimepath'. When found, call the "callback" function for
1898 * it.
1899 * When "all" is TRUE repeat for all matches, otherwise only the first one is
1900 * used.
1901 * Returns OK when at least one match found, FAIL otherwise.
1902 */
1903 int
1904do_in_runtimepath(name, all, callback)
1905 char_u *name;
1906 int all;
1907 void (*callback)__ARGS((char_u *fname));
1908{
1909 char_u *rtp;
1910 char_u *np;
1911 char_u *buf;
1912 char_u *rtp_copy;
1913 char_u *tail;
1914 int num_files;
1915 char_u **files;
1916 int i;
1917 int did_one = FALSE;
1918#ifdef AMIGA
1919 struct Process *proc = (struct Process *)FindTask(0L);
1920 APTR save_winptr = proc->pr_WindowPtr;
1921
1922 /* Avoid a requester here for a volume that doesn't exist. */
1923 proc->pr_WindowPtr = (APTR)-1L;
1924#endif
1925
1926 /* Make a copy of 'runtimepath'. Invoking the callback may change the
1927 * value. */
1928 rtp_copy = vim_strsave(p_rtp);
1929 buf = alloc(MAXPATHL);
1930 if (buf != NULL && rtp_copy != NULL)
1931 {
1932 if (p_verbose > 1)
1933 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
1934 (char *)name, (char *)p_rtp);
1935 /* Loop over all entries in 'runtimepath'. */
1936 rtp = rtp_copy;
1937 while (*rtp != NUL && (all || !did_one))
1938 {
1939 /* Copy the path from 'runtimepath' to buf[]. */
1940 copy_option_part(&rtp, buf, MAXPATHL, ",");
1941 if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL)
1942 {
1943 add_pathsep(buf);
1944 tail = buf + STRLEN(buf);
1945
1946 /* Loop over all patterns in "name" */
1947 np = name;
1948 while (*np != NUL && (all || !did_one))
1949 {
1950 /* Append the pattern from "name" to buf[]. */
1951 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
1952 "\t ");
1953
1954 if (p_verbose > 2)
1955 msg_str((char_u *)_("Searching for \"%s\""), buf);
1956
1957 /* Expand wildcards, invoke the callback for each match. */
1958 if (gen_expand_wildcards(1, &buf, &num_files, &files,
1959 EW_FILE) == OK)
1960 {
1961 for (i = 0; i < num_files; ++i)
1962 {
1963 (*callback)(files[i]);
1964 did_one = TRUE;
1965 if (!all)
1966 break;
1967 }
1968 FreeWild(num_files, files);
1969 }
1970 }
1971 }
1972 }
1973 }
1974 vim_free(buf);
1975 vim_free(rtp_copy);
1976 if (p_verbose > 0 && !did_one)
1977 msg_str((char_u *)_("not found in 'runtimepath': \"%s\""), name);
1978
1979#ifdef AMIGA
1980 proc->pr_WindowPtr = save_winptr;
1981#endif
1982
1983 return did_one ? OK : FAIL;
1984}
1985
1986#if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
1987/*
1988 * ":options"
1989 */
1990/*ARGSUSED*/
1991 void
1992ex_options(eap)
1993 exarg_T *eap;
1994{
1995 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
1996}
1997#endif
1998
1999/*
2000 * ":source {fname}"
2001 */
2002 void
2003ex_source(eap)
2004 exarg_T *eap;
2005{
2006#ifdef FEAT_BROWSE
2007 if (cmdmod.browse)
2008 {
2009 char_u *fname = NULL;
2010
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002011 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 NULL, NULL, BROWSE_FILTER_MACROS, NULL);
2013 if (fname != NULL)
2014 {
2015 cmd_source(fname, eap);
2016 vim_free(fname);
2017 }
2018 }
2019 else
2020#endif
2021 cmd_source(eap->arg, eap);
2022}
2023
2024 static void
2025cmd_source(fname, eap)
2026 char_u *fname;
2027 exarg_T *eap;
2028{
2029 if (*fname == NUL)
2030 EMSG(_(e_argreq));
2031
2032 /* ":source!" read vi commands */
2033 else if (eap != NULL && eap->forceit)
2034 /* Need to execute the commands directly when:
2035 * - ":g" command busy
2036 * - after ":argdo", ":windo" or ":bufdo"
2037 * - another command follows
2038 * - inside a loop
2039 */
2040 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2041#ifdef FEAT_EVAL
2042 || eap->cstack->cs_idx >= 0
2043#endif
2044 );
2045
2046 /* ":source" read ex commands */
2047 else if (do_source(fname, FALSE, FALSE) == FAIL)
2048 EMSG2(_(e_notopen), fname);
2049}
2050
2051/*
2052 * ":source" and associated commands.
2053 */
2054/*
2055 * Structure used to store info for each sourced file.
2056 * It is shared between do_source() and getsourceline().
2057 * This is required, because it needs to be handed to do_cmdline() and
2058 * sourcing can be done recursively.
2059 */
2060struct source_cookie
2061{
2062 FILE *fp; /* opened file for sourcing */
2063 char_u *nextline; /* if not NULL: line that was read ahead */
2064 int finished; /* ":finish" used */
2065#if defined (USE_CRNL) || defined (USE_CR)
2066 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
2067 int error; /* TRUE if LF found after CR-LF */
2068#endif
2069#ifdef FEAT_EVAL
2070 linenr_T breakpoint; /* next line with breakpoint or zero */
2071 char_u *fname; /* name of sourced file */
2072 int dbg_tick; /* debug_tick when breakpoint was set */
2073 int level; /* top nesting level of sourced file */
2074#endif
2075#ifdef FEAT_MBYTE
2076 vimconv_T conv; /* type of conversion */
2077#endif
2078};
2079
2080#ifdef FEAT_EVAL
2081/*
2082 * Return the address holding the next breakpoint line for a source cookie.
2083 */
2084 linenr_T *
2085source_breakpoint(cookie)
2086 void *cookie;
2087{
2088 return &((struct source_cookie *)cookie)->breakpoint;
2089}
2090
2091/*
2092 * Return the address holding the debug tick for a source cookie.
2093 */
2094 int *
2095source_dbg_tick(cookie)
2096 void *cookie;
2097{
2098 return &((struct source_cookie *)cookie)->dbg_tick;
2099}
2100
2101/*
2102 * Return the nesting level for a source cookie.
2103 */
2104 int
2105source_level(cookie)
2106 void *cookie;
2107{
2108 return ((struct source_cookie *)cookie)->level;
2109}
2110#endif
2111
2112static char_u *get_one_sourceline __ARGS((struct source_cookie *sp));
2113
2114#ifdef FEAT_EVAL
2115/* Growarray to store the names of sourced scripts.
2116 * For Unix also store the dev/ino, so that we don't have to stat() each
2117 * script when going through the list. */
2118struct scriptstuff
2119{
2120 char_u *name;
2121# ifdef UNIX
2122 int dev;
2123 ino_t ino;
2124# endif
2125};
2126static garray_T script_names = {0, 0, sizeof(struct scriptstuff), 4, NULL};
2127#define SCRIPT_NAME(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].name)
2128#define SCRIPT_DEV(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].dev)
2129#define SCRIPT_INO(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].ino)
2130#endif
2131
2132#if defined(WIN32) && defined(FEAT_CSCOPE)
2133static FILE *fopen_noinh_readbin __ARGS((char *filename));
2134
2135/*
2136 * Special function to open a file without handle inheritance.
2137 */
2138 static FILE *
2139fopen_noinh_readbin(filename)
2140 char *filename;
2141{
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00002142 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002143
2144 if (fd_tmp == -1)
2145 return NULL;
2146 return fdopen(fd_tmp, READBIN);
2147}
2148#endif
2149
2150
2151/*
2152 * do_source: Read the file "fname" and execute its lines as EX commands.
2153 *
2154 * This function may be called recursively!
2155 *
2156 * return FAIL if file could not be opened, OK otherwise
2157 */
2158 int
2159do_source(fname, check_other, is_vimrc)
2160 char_u *fname;
2161 int check_other; /* check for .vimrc and _vimrc */
2162 int is_vimrc; /* call vimrc_found() when file exists */
2163{
2164 struct source_cookie cookie;
2165 char_u *save_sourcing_name;
2166 linenr_T save_sourcing_lnum;
2167 char_u *p;
2168 char_u *fname_exp;
2169 int retval = FAIL;
2170#ifdef FEAT_EVAL
2171 scid_T save_current_SID;
2172 static scid_T last_current_SID = 0;
2173 void *save_funccalp;
2174 int save_debug_break_level = debug_break_level;
2175# ifdef UNIX
2176 struct stat st;
2177 int stat_ok;
2178# endif
2179#endif
2180#ifdef STARTUPTIME
2181 struct timeval tv_rel;
2182 struct timeval tv_start;
2183#endif
2184
2185#ifdef RISCOS
2186 p = mch_munge_fname(fname);
2187#else
2188 p = expand_env_save(fname);
2189#endif
2190 if (p == NULL)
2191 return retval;
2192 fname_exp = fix_fname(p);
2193 vim_free(p);
2194 if (fname_exp == NULL)
2195 return retval;
2196#ifdef MACOS_CLASSIC
2197 slash_n_colon_adjust(fname_exp);
2198#endif
2199 if (mch_isdir(fname_exp))
2200 {
2201 msg_str((char_u *)_("Cannot source a directory: \"%s\""), fname);
2202 goto theend;
2203 }
2204
2205#if defined(WIN32) && defined(FEAT_CSCOPE)
2206 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2207#else
2208 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2209#endif
2210 if (cookie.fp == NULL && check_other)
2211 {
2212 /*
2213 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
2214 * and ".exrc" by "_exrc" or vice versa.
2215 */
2216 p = gettail(fname_exp);
2217 if ((*p == '.' || *p == '_')
2218 && (STRICMP(p + 1, "vimrc") == 0
2219 || STRICMP(p + 1, "gvimrc") == 0
2220 || STRICMP(p + 1, "exrc") == 0))
2221 {
2222 if (*p == '_')
2223 *p = '.';
2224 else
2225 *p = '_';
2226#if defined(WIN32) && defined(FEAT_CSCOPE)
2227 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2228#else
2229 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2230#endif
2231 }
2232 }
2233
2234 if (cookie.fp == NULL)
2235 {
2236 if (p_verbose > 0)
2237 {
2238 if (sourcing_name == NULL)
2239 msg_str((char_u *)_("could not source \"%s\""), fname);
2240 else
2241 smsg((char_u *)_("line %ld: could not source \"%s\""),
2242 sourcing_lnum, fname);
2243 }
2244 goto theend;
2245 }
2246
2247 /*
2248 * The file exists.
2249 * - In verbose mode, give a message.
2250 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
2251 */
2252 if (p_verbose > 1)
2253 {
2254 if (sourcing_name == NULL)
2255 msg_str((char_u *)_("sourcing \"%s\""), fname);
2256 else
2257 smsg((char_u *)_("line %ld: sourcing \"%s\""),
2258 sourcing_lnum, fname);
2259 }
2260 if (is_vimrc)
2261 vimrc_found();
2262
2263#ifdef USE_CRNL
2264 /* If no automatic file format: Set default to CR-NL. */
2265 if (*p_ffs == NUL)
2266 cookie.fileformat = EOL_DOS;
2267 else
2268 cookie.fileformat = EOL_UNKNOWN;
2269 cookie.error = FALSE;
2270#endif
2271
2272#ifdef USE_CR
2273 /* If no automatic file format: Set default to CR. */
2274 if (*p_ffs == NUL)
2275 cookie.fileformat = EOL_MAC;
2276 else
2277 cookie.fileformat = EOL_UNKNOWN;
2278 cookie.error = FALSE;
2279#endif
2280
2281 cookie.nextline = NULL;
2282 cookie.finished = FALSE;
2283
2284#ifdef FEAT_EVAL
2285 /*
2286 * Check if this script has a breakpoint.
2287 */
2288 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
2289 cookie.fname = fname_exp;
2290 cookie.dbg_tick = debug_tick;
2291
2292 cookie.level = ex_nesting_level;
2293#endif
2294#ifdef FEAT_MBYTE
2295 cookie.conv.vc_type = CONV_NONE; /* no conversion */
2296
2297 /* Try reading the first few bytes to check for a UTF-8 BOM. */
2298 {
2299 char_u buf[3];
2300
2301 if (fread((char *)buf, sizeof(char_u), (size_t)3, cookie.fp)
2302 == (size_t)3
2303 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
2304 /* Found BOM, setup conversion and skip over it. */
2305 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
2306 else
2307 /* No BOM found, rewind. */
2308 fseek(cookie.fp, 0L, SEEK_SET);
2309 }
2310#endif
2311
2312 /*
2313 * Keep the sourcing name/lnum, for recursive calls.
2314 */
2315 save_sourcing_name = sourcing_name;
2316 sourcing_name = fname_exp;
2317 save_sourcing_lnum = sourcing_lnum;
2318 sourcing_lnum = 0;
2319
2320#ifdef STARTUPTIME
2321 time_push(&tv_rel, &tv_start);
2322#endif
2323
2324#ifdef FEAT_EVAL
2325 /*
2326 * Check if this script was sourced before to finds its SID.
2327 * If it's new, generate a new SID.
2328 */
2329 save_current_SID = current_SID;
2330# ifdef UNIX
2331 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
2332# endif
2333 for (current_SID = script_names.ga_len; current_SID > 0; --current_SID)
2334 if (SCRIPT_NAME(current_SID) != NULL
2335 && (
2336# ifdef UNIX
2337 /* compare dev/ino when possible, it catches symbolic
2338 * links */
2339 (stat_ok && SCRIPT_DEV(current_SID) != -1)
2340 ? (SCRIPT_DEV(current_SID) == st.st_dev
2341 && SCRIPT_INO(current_SID) == st.st_ino) :
2342# endif
2343 fnamecmp(SCRIPT_NAME(current_SID), fname_exp) == 0))
2344 break;
2345 if (current_SID == 0)
2346 {
2347 current_SID = ++last_current_SID;
2348 if (ga_grow(&script_names, (int)(current_SID - script_names.ga_len))
2349 == OK)
2350 {
2351 while (script_names.ga_len < current_SID)
2352 {
2353 SCRIPT_NAME(script_names.ga_len + 1) = NULL;
2354 ++script_names.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 }
2356 SCRIPT_NAME(current_SID) = fname_exp;
2357# ifdef UNIX
2358 if (stat_ok)
2359 {
2360 SCRIPT_DEV(current_SID) = st.st_dev;
2361 SCRIPT_INO(current_SID) = st.st_ino;
2362 }
2363 else
2364 SCRIPT_DEV(current_SID) = -1;
2365# endif
2366 fname_exp = NULL;
2367 }
2368 /* Allocate the local script variables to use for this script. */
2369 new_script_vars(current_SID);
2370 }
2371
2372 /* Don't use local function variables, if called from a function */
2373 save_funccalp = save_funccal();
2374#endif
2375
2376 /*
2377 * Call do_cmdline, which will call getsourceline() to get the lines.
2378 */
2379 do_cmdline(NULL, getsourceline, (void *)&cookie,
2380 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2381
2382 retval = OK;
2383 fclose(cookie.fp);
2384 vim_free(cookie.nextline);
2385#ifdef FEAT_MBYTE
2386 convert_setup(&cookie.conv, NULL, NULL);
2387#endif
2388
2389 if (got_int)
2390 EMSG(_(e_interr));
2391 sourcing_name = save_sourcing_name;
2392 sourcing_lnum = save_sourcing_lnum;
2393#ifdef FEAT_EVAL
2394 current_SID = save_current_SID;
2395 restore_funccal(save_funccalp);
2396#endif
2397 if (p_verbose > 1)
2398 {
2399 msg_str((char_u *)_("finished sourcing %s"), fname);
2400 if (sourcing_name != NULL)
2401 msg_str((char_u *)_("continuing in %s"), sourcing_name);
2402 }
2403#ifdef STARTUPTIME
2404# ifdef HAVE_SNPRINTF
2405 snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2406# else
2407 sprintf(IObuff, "sourcing %s", fname);
2408# endif
2409 time_msg(IObuff, &tv_start);
2410 time_pop(&tv_rel);
2411#endif
2412
2413#ifdef FEAT_EVAL
2414 /*
2415 * After a "finish" in debug mode, need to break at first command of next
2416 * sourced file.
2417 */
2418 if (save_debug_break_level > ex_nesting_level
2419 && debug_break_level == ex_nesting_level)
2420 ++debug_break_level;
2421#endif
2422
2423theend:
2424 vim_free(fname_exp);
2425 return retval;
2426}
2427
2428#if defined(FEAT_EVAL) || defined(PROTO)
2429/*
2430 * ":scriptnames"
2431 */
2432/*ARGSUSED*/
2433 void
2434ex_scriptnames(eap)
2435 exarg_T *eap;
2436{
2437 int i;
2438
2439 for (i = 1; i <= script_names.ga_len && !got_int; ++i)
2440 if (SCRIPT_NAME(i) != NULL)
2441 smsg((char_u *)"%3d: %s", i, SCRIPT_NAME(i));
2442}
2443
2444# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
2445/*
2446 * Fix slashes in the list of script names for 'shellslash'.
2447 */
2448 void
2449scriptnames_slash_adjust()
2450{
2451 int i;
2452
2453 for (i = 1; i <= script_names.ga_len; ++i)
2454 if (SCRIPT_NAME(i) != NULL)
2455 slash_adjust(SCRIPT_NAME(i));
2456}
2457# endif
2458
2459/*
2460 * Get a pointer to a script name. Used for ":verbose set".
2461 */
2462 char_u *
2463get_scriptname(id)
2464 scid_T id;
2465{
2466 if (id == SID_MODELINE)
2467 return (char_u *)"modeline";
2468 if (id == SID_CMDARG)
2469 return (char_u *)"--cmd argument";
2470 if (id == SID_CARG)
2471 return (char_u *)"-c argument";
2472 if (id == SID_ENV)
2473 return (char_u *)"environment variable";
2474 return SCRIPT_NAME(id);
2475}
2476#endif
2477
2478#if defined(USE_CR) || defined(PROTO)
2479
2480# if defined(__MSL__) && (__MSL__ >= 22)
2481/*
2482 * Newer version of the Metrowerks library handle DOS and UNIX files
2483 * without help.
2484 * Test with earlier versions, MSL 2.2 is the library supplied with
2485 * Codewarrior Pro 2.
2486 */
2487 char *
2488fgets_cr(s, n, stream)
2489 char *s;
2490 int n;
2491 FILE *stream;
2492{
2493 return fgets(s, n, stream);
2494}
2495# else
2496/*
2497 * Version of fgets() which also works for lines ending in a <CR> only
2498 * (Macintosh format).
2499 * For older versions of the Metrowerks library.
2500 * At least CodeWarrior 9 needed this code.
2501 */
2502 char *
2503fgets_cr(s, n, stream)
2504 char *s;
2505 int n;
2506 FILE *stream;
2507{
2508 int c = 0;
2509 int char_read = 0;
2510
2511 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
2512 {
2513 c = fgetc(stream);
2514 s[char_read++] = c;
2515 /* If the file is in DOS format, we need to skip a NL after a CR. I
2516 * thought it was the other way around, but this appears to work... */
2517 if (c == '\n')
2518 {
2519 c = fgetc(stream);
2520 if (c != '\r')
2521 ungetc(c, stream);
2522 }
2523 }
2524
2525 s[char_read] = 0;
2526 if (char_read == 0)
2527 return NULL;
2528
2529 if (feof(stream) && char_read == 1)
2530 return NULL;
2531
2532 return s;
2533}
2534# endif
2535#endif
2536
2537/*
2538 * Get one full line from a sourced file.
2539 * Called by do_cmdline() when it's called from do_source().
2540 *
2541 * Return a pointer to the line in allocated memory.
2542 * Return NULL for end-of-file or some error.
2543 */
2544/* ARGSUSED */
2545 char_u *
2546getsourceline(c, cookie, indent)
2547 int c; /* not used */
2548 void *cookie;
2549 int indent; /* not used */
2550{
2551 struct source_cookie *sp = (struct source_cookie *)cookie;
2552 char_u *line;
2553 char_u *p, *s;
2554
2555#ifdef FEAT_EVAL
2556 /* If breakpoints have been added/deleted need to check for it. */
2557 if (sp->dbg_tick < debug_tick)
2558 {
2559 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2560 sp->dbg_tick = debug_tick;
2561 }
2562#endif
2563 /*
2564 * Get current line. If there is a read-ahead line, use it, otherwise get
2565 * one now.
2566 */
2567 if (sp->finished)
2568 line = NULL;
2569 else if (sp->nextline == NULL)
2570 line = get_one_sourceline(sp);
2571 else
2572 {
2573 line = sp->nextline;
2574 sp->nextline = NULL;
2575 ++sourcing_lnum;
2576 }
2577
2578 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
2579 * contain the 'C' flag. */
2580 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
2581 {
2582 /* compensate for the one line read-ahead */
2583 --sourcing_lnum;
2584 for (;;)
2585 {
2586 sp->nextline = get_one_sourceline(sp);
2587 if (sp->nextline == NULL)
2588 break;
2589 p = skipwhite(sp->nextline);
2590 if (*p != '\\')
2591 break;
2592 s = alloc((int)(STRLEN(line) + STRLEN(p)));
2593 if (s == NULL) /* out of memory */
2594 break;
2595 STRCPY(s, line);
2596 STRCAT(s, p + 1);
2597 vim_free(line);
2598 line = s;
2599 vim_free(sp->nextline);
2600 }
2601 }
2602
2603#ifdef FEAT_MBYTE
2604 if (line != NULL && sp->conv.vc_type != CONV_NONE)
2605 {
2606 /* Convert the encoding of the script line. */
2607 s = string_convert(&sp->conv, line, NULL);
2608 if (s != NULL)
2609 {
2610 vim_free(line);
2611 line = s;
2612 }
2613 }
2614#endif
2615
2616#ifdef FEAT_EVAL
2617 /* Did we encounter a breakpoint? */
2618 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
2619 {
2620 dbg_breakpoint(sp->fname, sourcing_lnum);
2621 /* Find next breakpoint. */
2622 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
2623 sp->dbg_tick = debug_tick;
2624 }
2625#endif
2626
2627 return line;
2628}
2629
2630 static char_u *
2631get_one_sourceline(sp)
2632 struct source_cookie *sp;
2633{
2634 garray_T ga;
2635 int len;
2636 int c;
2637 char_u *buf;
2638#ifdef USE_CRNL
2639 int has_cr; /* CR-LF found */
2640#endif
2641#ifdef USE_CR
2642 char_u *scan;
2643#endif
2644 int have_read = FALSE;
2645
2646 /* use a growarray to store the sourced line */
2647 ga_init2(&ga, 1, 200);
2648
2649 /*
2650 * Loop until there is a finished line (or end-of-file).
2651 */
2652 sourcing_lnum++;
2653 for (;;)
2654 {
2655 /* make room to read at least 80 (more) characters */
2656 if (ga_grow(&ga, 80) == FAIL)
2657 break;
2658 buf = (char_u *)ga.ga_data;
2659
2660#ifdef USE_CR
2661 if (sp->fileformat == EOL_MAC)
2662 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00002663 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2664 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 break;
2666 }
2667 else
2668#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00002669 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
2670 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 break;
2672 len = (int)STRLEN(buf);
2673#ifdef USE_CRNL
2674 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
2675 * CTRL-Z by its own, or after a NL. */
2676 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
2677 && sp->fileformat == EOL_DOS
2678 && buf[len - 1] == Ctrl_Z)
2679 {
2680 buf[len - 1] = NUL;
2681 break;
2682 }
2683#endif
2684
2685#ifdef USE_CR
2686 /* If the read doesn't stop on a new line, and there's
2687 * some CR then we assume a Mac format */
2688 if (sp->fileformat == EOL_UNKNOWN)
2689 {
2690 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
2691 sp->fileformat = EOL_MAC;
2692 else
2693 sp->fileformat = EOL_UNIX;
2694 }
2695
2696 if (sp->fileformat == EOL_MAC)
2697 {
2698 scan = vim_strchr(buf, '\r');
2699
2700 if (scan != NULL)
2701 {
2702 *scan = '\n';
2703 if (*(scan + 1) != 0)
2704 {
2705 *(scan + 1) = 0;
2706 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
2707 }
2708 }
2709 len = STRLEN(buf);
2710 }
2711#endif
2712
2713 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 ga.ga_len = len;
2715
2716 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00002717 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 continue;
2719
2720 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
2721 {
2722#ifdef USE_CRNL
2723 has_cr = (len >= 2 && buf[len - 2] == '\r');
2724 if (sp->fileformat == EOL_UNKNOWN)
2725 {
2726 if (has_cr)
2727 sp->fileformat = EOL_DOS;
2728 else
2729 sp->fileformat = EOL_UNIX;
2730 }
2731
2732 if (sp->fileformat == EOL_DOS)
2733 {
2734 if (has_cr) /* replace trailing CR */
2735 {
2736 buf[len - 2] = '\n';
2737 --len;
2738 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739 }
2740 else /* lines like ":map xx yy^M" will have failed */
2741 {
2742 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002743 {
2744 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002746 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747 sp->error = TRUE;
2748 sp->fileformat = EOL_UNIX;
2749 }
2750 }
2751#endif
2752 /* The '\n' is escaped if there is an odd number of ^V's just
2753 * before it, first set "c" just before the 'V's and then check
2754 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
2755 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
2756 ;
2757 if ((len & 1) != (c & 1)) /* escaped NL, read more */
2758 {
2759 sourcing_lnum++;
2760 continue;
2761 }
2762
2763 buf[len - 1] = NUL; /* remove the NL */
2764 }
2765
2766 /*
2767 * Check for ^C here now and then, so recursive :so can be broken.
2768 */
2769 line_breakcheck();
2770 break;
2771 }
2772
2773 if (have_read)
2774 return (char_u *)ga.ga_data;
2775
2776 vim_free(ga.ga_data);
2777 return NULL;
2778}
2779
2780/*
2781 * ":scriptencoding": Set encoding conversion for a sourced script.
2782 * Without the multi-byte feature it's simply ignored.
2783 */
2784/*ARGSUSED*/
2785 void
2786ex_scriptencoding(eap)
2787 exarg_T *eap;
2788{
2789#ifdef FEAT_MBYTE
2790 struct source_cookie *sp;
2791 char_u *name;
2792
2793 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
2794 {
2795 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
2796 return;
2797 }
2798
2799 if (*eap->arg != NUL)
2800 {
2801 name = enc_canonize(eap->arg);
2802 if (name == NULL) /* out of memory */
2803 return;
2804 }
2805 else
2806 name = eap->arg;
2807
2808 /* Setup for conversion from the specified encoding to 'encoding'. */
2809 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
2810 convert_setup(&sp->conv, name, p_enc);
2811
2812 if (name != eap->arg)
2813 vim_free(name);
2814#endif
2815}
2816
2817#if defined(FEAT_EVAL) || defined(PROTO)
2818/*
2819 * ":finish": Mark a sourced file as finished.
2820 */
2821 void
2822ex_finish(eap)
2823 exarg_T *eap;
2824{
2825 if (getline_equal(eap->getline, eap->cookie, getsourceline))
2826 do_finish(eap, FALSE);
2827 else
2828 EMSG(_("E168: :finish used outside of a sourced file"));
2829}
2830
2831/*
2832 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
2833 * Also called for a pending finish at the ":endtry" or after returning from
2834 * an extra do_cmdline(). "reanimate" is used in the latter case.
2835 */
2836 void
2837do_finish(eap, reanimate)
2838 exarg_T *eap;
2839 int reanimate;
2840{
2841 int idx;
2842
2843 if (reanimate)
2844 ((struct source_cookie *)getline_cookie(eap->getline,
2845 eap->cookie))->finished = FALSE;
2846
2847 /*
2848 * Cleanup (and inactivate) conditionals, but stop when a try conditional
2849 * not in its finally clause (which then is to be executed next) is found.
2850 * In this case, make the ":finish" pending for execution at the ":endtry".
2851 * Otherwise, finish normally.
2852 */
2853 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
2854 if (idx >= 0)
2855 {
2856 eap->cstack->cs_pending[idx] = CSTP_FINISH;
2857 report_make_pending(CSTP_FINISH, NULL);
2858 }
2859 else
2860 ((struct source_cookie *)getline_cookie(eap->getline,
2861 eap->cookie))->finished = TRUE;
2862}
2863
2864
2865/*
2866 * Return TRUE when a sourced file had the ":finish" command: Don't give error
2867 * message for missing ":endif".
2868 * Return FALSE when not sourcing a file.
2869 */
2870 int
2871source_finished(getline, cookie)
2872 char_u *(*getline) __ARGS((int, void *, int));
2873 void *cookie;
2874{
2875 return (getline_equal(getline, cookie, getsourceline)
2876 && ((struct source_cookie *)getline_cookie(
2877 getline, cookie))->finished);
2878}
2879#endif
2880
2881#if defined(FEAT_LISTCMDS) || defined(PROTO)
2882/*
2883 * ":checktime [buffer]"
2884 */
2885 void
2886ex_checktime(eap)
2887 exarg_T *eap;
2888{
2889 buf_T *buf;
2890 int save_no_check_timestamps = no_check_timestamps;
2891
2892 no_check_timestamps = 0;
2893 if (eap->addr_count == 0) /* default is all buffers */
2894 check_timestamps(FALSE);
2895 else
2896 {
2897 buf = buflist_findnr((int)eap->line2);
2898 if (buf != NULL) /* cannot happen? */
2899 (void)buf_check_timestamp(buf, FALSE);
2900 }
2901 no_check_timestamps = save_no_check_timestamps;
2902}
2903#endif
2904
2905#if defined(FEAT_PRINTER) || defined(PROTO)
2906/*
2907 * Printing code (Machine-independent.)
2908 * To implement printing on a platform, the following functions must be
2909 * defined:
2910 *
2911 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
2912 * Called once. Code should display printer dialogue (if appropriate) and
2913 * determine printer font and margin settings. Reset has_color if the printer
2914 * doesn't support colors at all.
2915 * Returns FAIL to abort.
2916 *
2917 * int mch_print_begin(prt_settings_T *settings)
2918 * Called to start the print job.
2919 * Return FALSE to abort.
2920 *
2921 * int mch_print_begin_page(char_u *msg)
2922 * Called at the start of each page.
2923 * "msg" indicates the progress of the print job, can be NULL.
2924 * Return FALSE to abort.
2925 *
2926 * int mch_print_end_page()
2927 * Called at the end of each page.
2928 * Return FALSE to abort.
2929 *
2930 * int mch_print_blank_page()
2931 * Called to generate a blank page for collated, duplex, multiple copy
2932 * document. Return FALSE to abort.
2933 *
2934 * void mch_print_end(prt_settings_T *psettings)
2935 * Called at normal end of print job.
2936 *
2937 * void mch_print_cleanup()
2938 * Called if print job ends normally or is abandoned. Free any memory, close
2939 * devices and handles. Also called when mch_print_begin() fails, but not
2940 * when mch_print_init() fails.
2941 *
2942 * void mch_print_set_font(int Bold, int Italic, int Underline);
2943 * Called whenever the font style changes.
2944 *
2945 * void mch_print_set_bg(long bgcol);
2946 * Called to set the background color for the following text. Parameter is an
2947 * RGB value.
2948 *
2949 * void mch_print_set_fg(long fgcol);
2950 * Called to set the foreground color for the following text. Parameter is an
2951 * RGB value.
2952 *
2953 * mch_print_start_line(int margin, int page_line)
2954 * Sets the current position at the start of line "page_line".
2955 * If margin is TRUE start in the left margin (for header and line number).
2956 *
2957 * int mch_print_text_out(char_u *p, int len);
2958 * Output one character of text p[len] at the current position.
2959 * Return TRUE if there is no room for another character in the same line.
2960 *
2961 * Note that the generic code has no idea of margins. The machine code should
2962 * simply make the page look smaller! The header and the line numbers are
2963 * printed in the margin.
2964 */
2965
2966#ifdef FEAT_SYN_HL
2967static const long_u cterm_color_8[8] =
2968{
2969 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
2970 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
2971};
2972
2973static const long_u cterm_color_16[16] =
2974{
2975 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
2976 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
2977 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
2978 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
2979};
2980
2981static int current_syn_id;
2982#endif
2983
2984#define PRCOLOR_BLACK (long_u)0
2985#define PRCOLOR_WHITE (long_u)0xFFFFFFL
2986
2987static int curr_italic;
2988static int curr_bold;
2989static int curr_underline;
2990static long_u curr_bg;
2991static long_u curr_fg;
2992static int page_count;
2993
2994/*
2995 * These values determine the print position on a page.
2996 */
2997typedef struct
2998{
2999 int lead_spaces; /* remaining spaces for a TAB */
3000 int print_pos; /* virtual column for computing TABs */
3001 colnr_T column; /* byte column */
3002 linenr_T file_line; /* line nr in the buffer */
3003 long_u bytes_printed; /* bytes printed so far */
3004 int ff; /* seen form feed character */
3005} prt_pos_T;
3006
3007#ifdef FEAT_SYN_HL
3008static long_u darken_rgb __ARGS((long_u rgb));
3009static long_u prt_get_term_color __ARGS((int colorindex));
3010#endif
3011static void prt_set_fg __ARGS((long_u fg));
3012static void prt_set_bg __ARGS((long_u bg));
3013static void prt_set_font __ARGS((int bold, int italic, int underline));
3014static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3015static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3016static void prt_message __ARGS((char_u *s));
3017static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3018static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3019
3020#ifdef FEAT_SYN_HL
3021/*
3022 * If using a dark background, the colors will probably be too bright to show
3023 * up well on white paper, so reduce their brightness.
3024 */
3025 static long_u
3026darken_rgb(rgb)
3027 long_u rgb;
3028{
3029 return ((rgb >> 17) << 16)
3030 + (((rgb & 0xff00) >> 9) << 8)
3031 + ((rgb & 0xff) >> 1);
3032}
3033
3034 static long_u
3035prt_get_term_color(colorindex)
3036 int colorindex;
3037{
3038 /* TODO: Should check for xterm with 88 or 256 colors. */
3039 if (t_colors > 8)
3040 return cterm_color_16[colorindex % 16];
3041 return cterm_color_8[colorindex % 8];
3042}
3043
3044 static void
3045prt_get_attr(hl_id, pattr, modec)
3046 int hl_id;
3047 prt_text_attr_T* pattr;
3048 int modec;
3049{
3050 int colorindex;
3051 long_u fg_color;
3052 long_u bg_color;
3053 char *color;
3054
3055 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3056 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3057 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
3058
3059# ifdef FEAT_GUI
3060 if (gui.in_use)
3061 {
3062 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3063 if (bg_color == PRCOLOR_BLACK)
3064 bg_color = PRCOLOR_WHITE;
3065
3066 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3067 }
3068 else
3069# endif
3070 {
3071 bg_color = PRCOLOR_WHITE;
3072
3073 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3074 if (color == NULL)
3075 colorindex = 0;
3076 else
3077 colorindex = atoi(color);
3078
3079 if (colorindex >= 0 && colorindex < t_colors)
3080 fg_color = prt_get_term_color(colorindex);
3081 else
3082 fg_color = PRCOLOR_BLACK;
3083 }
3084
3085 if (fg_color == PRCOLOR_WHITE)
3086 fg_color = PRCOLOR_BLACK;
3087 else if (*p_bg == 'd')
3088 fg_color = darken_rgb(fg_color);
3089
3090 pattr->fg_color = fg_color;
3091 pattr->bg_color = bg_color;
3092}
3093#endif /* FEAT_SYN_HL */
3094
3095 static void
3096prt_set_fg(fg)
3097 long_u fg;
3098{
3099 if (fg != curr_fg)
3100 {
3101 curr_fg = fg;
3102 mch_print_set_fg(fg);
3103 }
3104}
3105
3106 static void
3107prt_set_bg(bg)
3108 long_u bg;
3109{
3110 if (bg != curr_bg)
3111 {
3112 curr_bg = bg;
3113 mch_print_set_bg(bg);
3114 }
3115}
3116
3117 static void
3118prt_set_font(bold, italic, underline)
3119 int bold;
3120 int italic;
3121 int underline;
3122{
3123 if (curr_bold != bold
3124 || curr_italic != italic
3125 || curr_underline != underline)
3126 {
3127 curr_underline = underline;
3128 curr_italic = italic;
3129 curr_bold = bold;
3130 mch_print_set_font(bold, italic, underline);
3131 }
3132}
3133
3134/*
3135 * Print the line number in the left margin.
3136 */
3137 static void
3138prt_line_number(psettings, page_line, lnum)
3139 prt_settings_T *psettings;
3140 int page_line;
3141 linenr_T lnum;
3142{
3143 int i;
3144 char_u tbuf[20];
3145
3146 prt_set_fg(psettings->number.fg_color);
3147 prt_set_bg(psettings->number.bg_color);
3148 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3149 mch_print_start_line(TRUE, page_line);
3150
3151 /* Leave two spaces between the number and the text; depends on
3152 * PRINT_NUMBER_WIDTH. */
3153 sprintf((char *)tbuf, "%6ld", (long)lnum);
3154 for (i = 0; i < 6; i++)
3155 (void)mch_print_text_out(&tbuf[i], 1);
3156
3157#ifdef FEAT_SYN_HL
3158 if (psettings->do_syntax)
3159 /* Set colors for next character. */
3160 current_syn_id = -1;
3161 else
3162#endif
3163 {
3164 /* Set colors and font back to normal. */
3165 prt_set_fg(PRCOLOR_BLACK);
3166 prt_set_bg(PRCOLOR_WHITE);
3167 prt_set_font(FALSE, FALSE, FALSE);
3168 }
3169}
3170
3171static linenr_T printer_page_num;
3172
3173 int
3174get_printer_page_num()
3175{
3176 return printer_page_num;
3177}
3178
3179/*
3180 * Get the currently effective header height.
3181 */
3182 int
3183prt_header_height()
3184{
3185 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3186 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3187 return 2;
3188}
3189
3190/*
3191 * Return TRUE if using a line number for printing.
3192 */
3193 int
3194prt_use_number()
3195{
3196 return (printer_opts[OPT_PRINT_NUMBER].present
3197 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3198}
3199
3200/*
3201 * Return the unit used in a margin item in 'printoptions'.
3202 * Returns PRT_UNIT_NONE if not recognized.
3203 */
3204 int
3205prt_get_unit(idx)
3206 int idx;
3207{
3208 int u = PRT_UNIT_NONE;
3209 int i;
3210 static char *(units[4]) = PRT_UNIT_NAMES;
3211
3212 if (printer_opts[idx].present)
3213 for (i = 0; i < 4; ++i)
3214 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3215 {
3216 u = i;
3217 break;
3218 }
3219 return u;
3220}
3221
3222/*
3223 * Print the page header.
3224 */
3225/*ARGSUSED*/
3226 static void
3227prt_header(psettings, pagenum, lnum)
3228 prt_settings_T *psettings;
3229 int pagenum;
3230 linenr_T lnum;
3231{
3232 int width = psettings->chars_per_line;
3233 int page_line;
3234 char_u *tbuf;
3235 char_u *p;
3236#ifdef FEAT_MBYTE
3237 int l;
3238#endif
3239
3240 /* Also use the space for the line number. */
3241 if (prt_use_number())
3242 width += PRINT_NUMBER_WIDTH;
3243
3244 tbuf = alloc(width + IOSIZE);
3245 if (tbuf == NULL)
3246 return;
3247
3248#ifdef FEAT_STL_OPT
3249 if (*p_header != NUL)
3250 {
3251 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3252
3253 /*
3254 * Need to (temporarily) set current line number and first/last line
3255 * number on the 'window'. Since we don't know how long the page is,
3256 * set the first and current line number to the top line, and guess
3257 * that the page length is 64.
3258 */
3259 tmp_lnum = curwin->w_cursor.lnum;
3260 tmp_topline = curwin->w_topline;
3261 tmp_botline = curwin->w_botline;
3262 curwin->w_cursor.lnum = lnum;
3263 curwin->w_topline = lnum;
3264 curwin->w_botline = lnum + 63;
3265 printer_page_num = pagenum;
3266
3267 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3268 p_header, ' ', width, NULL);
3269
3270 /* Reset line numbers */
3271 curwin->w_cursor.lnum = tmp_lnum;
3272 curwin->w_topline = tmp_topline;
3273 curwin->w_botline = tmp_botline;
3274 }
3275 else
3276#endif
3277 sprintf((char *)tbuf, _("Page %d"), pagenum);
3278
3279 prt_set_fg(PRCOLOR_BLACK);
3280 prt_set_bg(PRCOLOR_WHITE);
3281 prt_set_font(TRUE, FALSE, FALSE);
3282
3283 /* Use a negative line number to indicate printing in the top margin. */
3284 page_line = 0 - prt_header_height();
3285 mch_print_start_line(TRUE, page_line);
3286 for (p = tbuf; *p != NUL; )
3287 {
3288 if (mch_print_text_out(p,
3289#ifdef FEAT_MBYTE
3290 (l = (*mb_ptr2len_check)(p))
3291#else
3292 1
3293#endif
3294 ))
3295 {
3296 ++page_line;
3297 if (page_line >= 0) /* out of room in header */
3298 break;
3299 mch_print_start_line(TRUE, page_line);
3300 }
3301#ifdef FEAT_MBYTE
3302 p += l;
3303#else
3304 p++;
3305#endif
3306 }
3307
3308 vim_free(tbuf);
3309
3310#ifdef FEAT_SYN_HL
3311 if (psettings->do_syntax)
3312 /* Set colors for next character. */
3313 current_syn_id = -1;
3314 else
3315#endif
3316 {
3317 /* Set colors and font back to normal. */
3318 prt_set_fg(PRCOLOR_BLACK);
3319 prt_set_bg(PRCOLOR_WHITE);
3320 prt_set_font(FALSE, FALSE, FALSE);
3321 }
3322}
3323
3324/*
3325 * Display a print status message.
3326 */
3327 static void
3328prt_message(s)
3329 char_u *s;
3330{
3331 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3332 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3333 out_flush();
3334}
3335
3336 void
3337ex_hardcopy(eap)
3338 exarg_T *eap;
3339{
3340 linenr_T lnum;
3341 int collated_copies, uncollated_copies;
3342 prt_settings_T settings;
3343 long_u bytes_to_print = 0;
3344 int page_line;
3345 int jobsplit;
3346 int id;
3347
3348 memset(&settings, 0, sizeof(prt_settings_T));
3349 settings.has_color = TRUE;
3350
3351# ifdef FEAT_POSTSCRIPT
3352 if (*eap->arg == '>')
3353 {
3354 char_u *errormsg = NULL;
3355
3356 /* Expand things like "%.ps". */
3357 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3358 {
3359 if (errormsg != NULL)
3360 EMSG(errormsg);
3361 return;
3362 }
3363 settings.outfile = skipwhite(eap->arg + 1);
3364 }
3365 else if (*eap->arg != NUL)
3366 settings.arguments = eap->arg;
3367# endif
3368
3369 /*
3370 * Initialise for printing. Ask the user for settings, unless forceit is
3371 * set.
3372 * The mch_print_init() code should set up margins if applicable. (It may
3373 * not be a real printer - for example the engine might generate HTML or
3374 * PS.)
3375 */
3376 if (mch_print_init(&settings,
3377 curbuf->b_fname == NULL
3378 ? (char_u *)buf_spname(curbuf)
3379 : curbuf->b_sfname == NULL
3380 ? curbuf->b_fname
3381 : curbuf->b_sfname,
3382 eap->forceit) == FAIL)
3383 return;
3384
3385#ifdef FEAT_SYN_HL
3386# ifdef FEAT_GUI
3387 if (gui.in_use)
3388 settings.modec = 'g';
3389 else
3390# endif
3391 if (t_colors > 1)
3392 settings.modec = 'c';
3393 else
3394 settings.modec = 't';
3395
3396 if (!syntax_present(curbuf))
3397 settings.do_syntax = FALSE;
3398 else if (printer_opts[OPT_PRINT_SYNTAX].present
3399 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
3400 settings.do_syntax =
3401 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
3402 else
3403 settings.do_syntax = settings.has_color;
3404#endif
3405
3406 /* Set up printing attributes for line numbers */
3407 settings.number.fg_color = PRCOLOR_BLACK;
3408 settings.number.bg_color = PRCOLOR_WHITE;
3409 settings.number.bold = FALSE;
3410 settings.number.italic = TRUE;
3411 settings.number.underline = FALSE;
3412#ifdef FEAT_SYN_HL
3413 /*
3414 * Syntax highlighting of line numbers.
3415 */
3416 if (prt_use_number() && settings.do_syntax)
3417 {
3418 id = syn_name2id((char_u *)"LineNr");
3419 if (id > 0)
3420 id = syn_get_final_id(id);
3421
3422 prt_get_attr(id, &settings.number, settings.modec);
3423 }
3424#endif /* FEAT_SYN_HL */
3425
3426 /*
3427 * Estimate the total lines to be printed
3428 */
3429 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
3430 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
3431 if (bytes_to_print == 0)
3432 {
3433 MSG(_("No text to be printed"));
3434 goto print_fail_no_begin;
3435 }
3436
3437 /* Set colors and font to normal. */
3438 curr_bg = (long_u)0xffffffffL;
3439 curr_fg = (long_u)0xffffffffL;
3440 curr_italic = MAYBE;
3441 curr_bold = MAYBE;
3442 curr_underline = MAYBE;
3443
3444 prt_set_fg(PRCOLOR_BLACK);
3445 prt_set_bg(PRCOLOR_WHITE);
3446 prt_set_font(FALSE, FALSE, FALSE);
3447#ifdef FEAT_SYN_HL
3448 current_syn_id = -1;
3449#endif
3450
3451 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
3452 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
3453
3454 if (!mch_print_begin(&settings))
3455 goto print_fail_no_begin;
3456
3457 /*
3458 * Loop over collated copies: 1 2 3, 1 2 3, ...
3459 */
3460 page_count = 0;
3461 for (collated_copies = 0;
3462 collated_copies < settings.n_collated_copies;
3463 collated_copies++)
3464 {
3465 prt_pos_T prtpos; /* current print position */
3466 prt_pos_T page_prtpos; /* print position at page start */
3467 int side;
3468
3469 memset(&page_prtpos, 0, sizeof(prt_pos_T));
3470 page_prtpos.file_line = eap->line1;
3471 prtpos = page_prtpos;
3472
3473 if (jobsplit && collated_copies > 0)
3474 {
3475 /* Splitting jobs: Stop a previous job and start a new one. */
3476 mch_print_end(&settings);
3477 if (!mch_print_begin(&settings))
3478 goto print_fail_no_begin;
3479 }
3480
3481 /*
3482 * Loop over all pages in the print job: 1 2 3 ...
3483 */
3484 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
3485 {
3486 /*
3487 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
3488 * For duplex: 12 12 12 34 34 34, ...
3489 */
3490 for (uncollated_copies = 0;
3491 uncollated_copies < settings.n_uncollated_copies;
3492 uncollated_copies++)
3493 {
3494 /* Set the print position to the start of this page. */
3495 prtpos = page_prtpos;
3496
3497 /*
3498 * Do front and rear side of a page.
3499 */
3500 for (side = 0; side <= settings.duplex; ++side)
3501 {
3502 /*
3503 * Print one page.
3504 */
3505
3506 /* Check for interrupt character every page. */
3507 ui_breakcheck();
3508 if (got_int || settings.user_abort)
3509 goto print_fail;
3510
3511 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
3512 page_count + 1 + side,
3513 prtpos.bytes_printed > 1000000
3514 ? (int)(prtpos.bytes_printed /
3515 (bytes_to_print / 100))
3516 : (int)((prtpos.bytes_printed * 100)
3517 / bytes_to_print));
3518 if (!mch_print_begin_page(IObuff))
3519 goto print_fail;
3520
3521 if (settings.n_collated_copies > 1)
3522 sprintf((char *)IObuff + STRLEN(IObuff),
3523 _(" Copy %d of %d"),
3524 collated_copies + 1,
3525 settings.n_collated_copies);
3526 prt_message(IObuff);
3527
3528 /*
3529 * Output header if required
3530 */
3531 if (prt_header_height() > 0)
3532 prt_header(&settings, page_count + 1 + side,
3533 prtpos.file_line);
3534
3535 for (page_line = 0; page_line < settings.lines_per_page;
3536 ++page_line)
3537 {
3538 prtpos.column = hardcopy_line(&settings,
3539 page_line, &prtpos);
3540 if (prtpos.column == 0)
3541 {
3542 /* finished a file line */
3543 prtpos.bytes_printed +=
3544 STRLEN(skipwhite(ml_get(prtpos.file_line)));
3545 if (++prtpos.file_line > eap->line2)
3546 break; /* reached the end */
3547 }
3548 else if (prtpos.ff)
3549 {
3550 /* Line had a formfeed in it - start new page but
3551 * stay on the current line */
3552 break;
3553 }
3554 }
3555
3556 if (!mch_print_end_page())
3557 goto print_fail;
3558 if (prtpos.file_line > eap->line2)
3559 break; /* reached the end */
3560 }
3561
3562 /*
3563 * Extra blank page for duplexing with odd number of pages and
3564 * more copies to come.
3565 */
3566 if (prtpos.file_line > eap->line2 && settings.duplex
3567 && side == 0
3568 && uncollated_copies + 1 < settings.n_uncollated_copies)
3569 {
3570 if (!mch_print_blank_page())
3571 goto print_fail;
3572 }
3573 }
3574 if (settings.duplex && prtpos.file_line <= eap->line2)
3575 ++page_count;
3576
3577 /* Remember the position where the next page starts. */
3578 page_prtpos = prtpos;
3579 }
3580
3581 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
3582 prt_message(IObuff);
3583 }
3584
3585print_fail:
3586 if (got_int || settings.user_abort)
3587 {
3588 sprintf((char *)IObuff, _("Printing aborted"));
3589 prt_message(IObuff);
3590 }
3591 mch_print_end(&settings);
3592
3593print_fail_no_begin:
3594 mch_print_cleanup();
3595}
3596
3597/*
3598 * Print one page line.
3599 * Return the next column to print, or zero if the line is finished.
3600 */
3601 static colnr_T
3602hardcopy_line(psettings, page_line, ppos)
3603 prt_settings_T *psettings;
3604 int page_line;
3605 prt_pos_T *ppos;
3606{
3607 colnr_T col;
3608 char_u *line;
3609 int need_break = FALSE;
3610 int outputlen;
3611 int tab_spaces;
3612 long_u print_pos;
3613#ifdef FEAT_SYN_HL
3614 prt_text_attr_T attr;
3615 int id;
3616#endif
3617
3618 if (ppos->column == 0 || ppos->ff)
3619 {
3620 print_pos = 0;
3621 tab_spaces = 0;
3622 if (!ppos->ff && prt_use_number())
3623 prt_line_number(psettings, page_line, ppos->file_line);
3624 ppos->ff = FALSE;
3625 }
3626 else
3627 {
3628 /* left over from wrap halfway a tab */
3629 print_pos = ppos->print_pos;
3630 tab_spaces = ppos->lead_spaces;
3631 }
3632
3633 mch_print_start_line(0, page_line);
3634 line = ml_get(ppos->file_line);
3635
3636 /*
3637 * Loop over the columns until the end of the file line or right margin.
3638 */
3639 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
3640 {
3641 outputlen = 1;
3642#ifdef FEAT_MBYTE
3643 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
3644 outputlen = 1;
3645#endif
3646#ifdef FEAT_SYN_HL
3647 /*
3648 * syntax highlighting stuff.
3649 */
3650 if (psettings->do_syntax)
3651 {
3652 id = syn_get_id(ppos->file_line, (long)col, 1);
3653 if (id > 0)
3654 id = syn_get_final_id(id);
3655 else
3656 id = 0;
3657 /* Get the line again, a multi-line regexp may invalidate it. */
3658 line = ml_get(ppos->file_line);
3659
3660 if (id != current_syn_id)
3661 {
3662 current_syn_id = id;
3663 prt_get_attr(id, &attr, psettings->modec);
3664 prt_set_font(attr.bold, attr.italic, attr.underline);
3665 prt_set_fg(attr.fg_color);
3666 prt_set_bg(attr.bg_color);
3667 }
3668 }
3669#endif /* FEAT_SYN_HL */
3670
3671 /*
3672 * Appropriately expand any tabs to spaces.
3673 */
3674 if (line[col] == TAB || tab_spaces != 0)
3675 {
3676 if (tab_spaces == 0)
3677 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
3678
3679 while (tab_spaces > 0)
3680 {
3681 need_break = mch_print_text_out((char_u *)" ", 1);
3682 print_pos++;
3683 tab_spaces--;
3684 if (need_break)
3685 break;
3686 }
3687 /* Keep the TAB if we didn't finish it. */
3688 if (need_break && tab_spaces > 0)
3689 break;
3690 }
3691 else if (line[col] == FF
3692 && printer_opts[OPT_PRINT_FORMFEED].present
3693 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
3694 == 'y')
3695 {
3696 ppos->ff = TRUE;
3697 need_break = 1;
3698 }
3699 else
3700 {
3701 need_break = mch_print_text_out(line + col, outputlen);
3702#ifdef FEAT_MBYTE
3703 if (has_mbyte)
3704 print_pos += (*mb_ptr2cells)(line + col);
3705 else
3706#endif
3707 print_pos++;
3708 }
3709 }
3710
3711 ppos->lead_spaces = tab_spaces;
3712 ppos->print_pos = print_pos;
3713
3714 /*
3715 * Start next line of file if we clip lines, or have reached end of the
3716 * line, unless we are doing a formfeed.
3717 */
3718 if (!ppos->ff
3719 && (line[col] == NUL
3720 || (printer_opts[OPT_PRINT_WRAP].present
3721 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
3722 == 'n')))
3723 return 0;
3724 return col;
3725}
3726
3727# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
3728
3729/*
3730 * PS printer stuff.
3731 *
3732 * Sources of information to help maintain the PS printing code:
3733 *
3734 * 1. PostScript Language Reference, 3rd Edition,
3735 * Addison-Wesley, 1999, ISBN 0-201-37922-8
3736 * 2. PostScript Language Program Design,
3737 * Addison-Wesley, 1988, ISBN 0-201-14396-8
3738 * 3. PostScript Tutorial and Cookbook,
3739 * Addison Wesley, 1985, ISBN 0-201-10179-3
3740 * 4. PostScript Language Document Structuring Conventions Specification,
3741 * version 3.0,
3742 * Adobe Technote 5001, 25th September 1992
3743 * 5. PostScript Printer Description File Format Specification, Version 4.3,
3744 * Adobe technote 5003, 9th February 1996
3745 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
3746 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00003747 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
3748 * Adobe Technote 5014, 8th October 1996
3749 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
3750 * Adoboe Technote 5094, 8th September, 2001
3751 * 9. CJKV Information Processing, 2nd Edition,
3752 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 *
3754 * Some of these documents can be found in PDF form on Adobe's web site -
3755 * http://www.adobe.com
3756 */
3757
Bram Moolenaar8299df92004-07-10 09:47:34 +00003758#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
3759
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
3761#define PRT_PS_DEFAULT_FONTSIZE (10)
3762#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
3763
3764struct prt_mediasize_S
3765{
3766 char *name;
3767 float width; /* width and height in points for portrait */
3768 float height;
3769};
3770
3771#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
3772
3773static struct prt_mediasize_S prt_mediasize[] =
3774{
3775 {"A4", 595.0, 842.0},
3776 {"letter", 612.0, 792.0},
3777 {"10x14", 720.0, 1008.0},
3778 {"A3", 842.0, 1191.0},
3779 {"A5", 420.0, 595.0},
3780 {"B4", 729.0, 1032.0},
3781 {"B5", 516.0, 729.0},
3782 {"executive", 522.0, 756.0},
3783 {"folio", 595.0, 935.0},
3784 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
3785 {"legal", 612.0, 1008.0},
3786 {"quarto", 610.0, 780.0},
3787 {"statement", 396.0, 612.0},
3788 {"tabloid", 792.0, 1224.0}
3789};
3790
3791/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
3792struct prt_ps_font_S
3793{
3794 int wx;
3795 int uline_offset;
3796 int uline_width;
3797 int bbox_min_y;
3798 int bbox_max_y;
3799 char *(ps_fontname[4]);
3800};
3801
3802#define PRT_PS_FONT_ROMAN (0)
3803#define PRT_PS_FONT_BOLD (1)
3804#define PRT_PS_FONT_OBLIQUE (2)
3805#define PRT_PS_FONT_BOLDOBLIQUE (3)
3806
Bram Moolenaar8299df92004-07-10 09:47:34 +00003807/* Standard font metrics for Courier family */
3808static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809{
3810 600,
3811 -100, 50,
3812 -250, 805,
3813 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
3814};
3815
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003816#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00003817/* Generic font metrics for multi-byte fonts */
3818static struct prt_ps_font_S prt_ps_mb_font =
3819{
3820 1000,
3821 -100, 50,
3822 -250, 805,
3823 {NULL, NULL, NULL, NULL}
3824};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003825#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00003826
3827/* Pointer to current font set being used */
3828static struct prt_ps_font_S* prt_ps_font;
3829
3830/* Structures to map user named encoding and mapping to PS equivalents for
3831 * building CID font name */
3832struct prt_ps_encoding_S
3833{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003834 char *encoding;
3835 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00003836 int needs_charset;
3837};
3838
3839struct prt_ps_charset_S
3840{
3841 char *charset;
3842 char *cmap_charset;
3843 int has_charset;
3844};
3845
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00003846#ifdef FEAT_MBYTE
3847
Bram Moolenaar8299df92004-07-10 09:47:34 +00003848#define CS_JIS_C_1978 (0x01)
3849#define CS_JIS_X_1983 (0x02)
3850#define CS_JIS_X_1990 (0x04)
3851#define CS_NEC (0x08)
3852#define CS_MSWINDOWS (0x10)
3853#define CS_CP932 (0x20)
3854#define CS_KANJITALK6 (0x40)
3855#define CS_KANJITALK7 (0x80)
3856
3857/* Japanese encodings and charsets */
3858static struct prt_ps_encoding_S j_encodings[] =
3859{
3860 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
3861 CS_NEC)},
3862 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
3863 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
3864 CS_KANJITALK6|CS_KANJITALK7)},
3865 {"cp932", "RKSJ", CS_JIS_X_1983},
3866 {"ucs-2", "UCS2", CS_JIS_X_1990},
3867 {"utf-8", "UTF8" , CS_JIS_X_1990}
3868};
3869static struct prt_ps_charset_S j_charsets[] =
3870{
3871 {"JIS_C_1978", "78", CS_JIS_C_1978},
3872 {"JIS_X_1983", NULL, CS_JIS_X_1983},
3873 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
3874 {"NEC", "Ext", CS_NEC},
3875 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
3876 {"CP932", "90ms", CS_JIS_X_1983},
3877 {"KANJITALK6", "83pv", CS_KANJITALK6},
3878 {"KANJITALK7", "90pv", CS_KANJITALK7}
3879};
3880
3881#define CS_GB_2312_80 (0x01)
3882#define CS_GBT_12345_90 (0x02)
3883#define CS_GBK2K (0x04)
3884#define CS_SC_MAC (0x08)
3885#define CS_GBT_90_MAC (0x10)
3886#define CS_GBK (0x20)
3887#define CS_SC_ISO10646 (0x40)
3888
3889/* Simplified Chinese encodings and charsets */
3890static struct prt_ps_encoding_S sc_encodings[] =
3891{
3892 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
3893 {"gb18030", NULL, CS_GBK2K},
3894 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
3895 CS_GBT_90_MAC)},
3896 {"gbk", "EUC", CS_GBK},
3897 {"ucs-2", "UCS2", CS_SC_ISO10646},
3898 {"utf-8", "UTF8", CS_SC_ISO10646}
3899};
3900static struct prt_ps_charset_S sc_charsets[] =
3901{
3902 {"GB_2312-80", "GB", CS_GB_2312_80},
3903 {"GBT_12345-90","GBT", CS_GBT_12345_90},
3904 {"MAC", "GBpc", CS_SC_MAC},
3905 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
3906 {"GBK", "GBK", CS_GBK},
3907 {"GB18030", "GBK2K", CS_GBK2K},
3908 {"ISO10646", "UniGB", CS_SC_ISO10646}
3909};
3910
3911#define CS_CNS_PLANE_1 (0x01)
3912#define CS_CNS_PLANE_2 (0x02)
3913#define CS_CNS_PLANE_1_2 (0x04)
3914#define CS_B5 (0x08)
3915#define CS_ETEN (0x10)
3916#define CS_HK_GCCS (0x20)
3917#define CS_HK_SCS (0x40)
3918#define CS_HK_SCS_ETEN (0x80)
3919#define CS_MTHKL (0x100)
3920#define CS_MTHKS (0x200)
3921#define CS_DLHKL (0x400)
3922#define CS_DLHKS (0x800)
3923#define CS_TC_ISO10646 (0x1000)
3924
3925/* Traditional Chinese encodings and charsets */
3926static struct prt_ps_encoding_S tc_encodings[] =
3927{
3928 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
3929 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
3930 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
3931 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
3932 CS_DLHKS)},
3933 {"cp950", "B5", CS_B5},
3934 {"ucs-2", "UCS2", CS_TC_ISO10646},
3935 {"utf-8", "UTF8", CS_TC_ISO10646},
3936 {"utf-16", "UTF16", CS_TC_ISO10646},
3937 {"utf-32", "UTF32", CS_TC_ISO10646}
3938};
3939static struct prt_ps_charset_S tc_charsets[] =
3940{
3941 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
3942 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
3943 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
3944 {"BIG5", NULL, CS_B5},
3945 {"CP950", NULL, CS_B5},
3946 {"ETEN", "ETen", CS_ETEN},
3947 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
3948 {"SCS", "HKscs", CS_HK_SCS},
3949 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
3950 {"MTHKL", "HKm471", CS_MTHKL},
3951 {"MTHKS", "HKm314", CS_MTHKS},
3952 {"DLHKL", "HKdla", CS_DLHKL},
3953 {"DLHKS", "HKdlb", CS_DLHKS},
3954 {"ISO10646", "UniCNS", CS_TC_ISO10646}
3955};
3956
3957#define CS_KR_X_1992 (0x01)
3958#define CS_KR_MAC (0x02)
3959#define CS_KR_X_1992_MS (0x04)
3960#define CS_KR_ISO10646 (0x08)
3961
3962/* Korean encodings and charsets */
3963static struct prt_ps_encoding_S k_encodings[] =
3964{
3965 {"iso-2022-kr", NULL, CS_KR_X_1992},
3966 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
3967 {"johab", "Johab", CS_KR_X_1992},
3968 {"cp1361", "Johab", CS_KR_X_1992},
3969 {"uhc", "UHC", CS_KR_X_1992_MS},
3970 {"cp949", "UHC", CS_KR_X_1992_MS},
3971 {"ucs-2", "UCS2", CS_KR_ISO10646},
3972 {"utf-8", "UTF8", CS_KR_ISO10646}
3973};
3974static struct prt_ps_charset_S k_charsets[] =
3975{
3976 {"KS_X_1992", "KSC", CS_KR_X_1992},
3977 {"CP1361", "KSC", CS_KR_X_1992},
3978 {"MAC", "KSCpc", CS_KR_MAC},
3979 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
3980 {"CP949", "KSCms", CS_KR_X_1992_MS},
3981 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
3982 {"ISO10646", "UniKS", CS_KR_ISO10646}
3983};
3984
3985/* Collections of encodings and charsets for multi-byte printing */
3986struct prt_ps_mbfont_S
3987{
3988 int num_encodings;
3989 struct prt_ps_encoding_S *encodings;
3990 int num_charsets;
3991 struct prt_ps_charset_S *charsets;
3992 char *ascii_enc;
3993 char *defcs;
3994};
3995
3996static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
3997{
3998 {
3999 NUM_ELEMENTS(j_encodings),
4000 j_encodings,
4001 NUM_ELEMENTS(j_charsets),
4002 j_charsets,
4003 "jis_roman",
4004 "JIS_X_1983"
4005 },
4006 {
4007 NUM_ELEMENTS(sc_encodings),
4008 sc_encodings,
4009 NUM_ELEMENTS(sc_charsets),
4010 sc_charsets,
4011 "gb_roman",
4012 "GB_2312-80"
4013 },
4014 {
4015 NUM_ELEMENTS(tc_encodings),
4016 tc_encodings,
4017 NUM_ELEMENTS(tc_charsets),
4018 tc_charsets,
4019 "cns_roman",
4020 "BIG5"
4021 },
4022 {
4023 NUM_ELEMENTS(k_encodings),
4024 k_encodings,
4025 NUM_ELEMENTS(k_charsets),
4026 k_charsets,
4027 "ks_roman",
4028 "KS_X_1992"
4029 }
4030};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004031#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004032
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033struct prt_ps_resource_S
4034{
4035 char_u name[64];
4036 char_u filename[MAXPATHL + 1];
4037 int type;
4038 char_u title[256];
4039 char_u version[256];
4040};
4041
4042/* Types of PS resource file currently used */
4043#define PRT_RESOURCE_TYPE_PROCSET (0)
4044#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004045#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046
4047/* The PS prolog file version number has to match - if the prolog file is
4048 * updated, increment the number in the file and here. Version checking was
4049 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004050 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 * Table of VIM and prolog versions:
4052 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004053 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004055 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004057#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004058#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059
4060/* String versions of PS resource types - indexed by constants above so don't
4061 * re-order!
4062 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004063static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064{
4065 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004066 "encoding",
4067 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068};
4069
4070/* Strings to look for in a PS resource file */
4071#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4072#define PRT_RESOURCE_RESOURCE "Resource-"
4073#define PRT_RESOURCE_PROCSET "ProcSet"
4074#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004075#define PRT_RESOURCE_CMAP "CMap"
4076
4077
4078/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4079 * read more comments. */
4080#define PRT_DSC_MISC_TYPE (-1)
4081#define PRT_DSC_TITLE_TYPE (1)
4082#define PRT_DSC_VERSION_TYPE (2)
4083#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4084
4085#define PRT_DSC_TITLE "%%Title:"
4086#define PRT_DSC_VERSION "%%Version:"
4087#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4088
4089struct prt_dsc_comment_S
4090{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004091 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004092 int len;
4093 int type;
4094};
4095
4096struct prt_dsc_line_S
4097{
4098 int type;
4099 char_u *string;
4100 int len;
4101};
4102
4103
4104#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4105struct prt_dsc_comment_S prt_dsc_table[] =
4106{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004107 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004108 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004109 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004110 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004111 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004112};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113
4114static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4115static void prt_write_file __ARGS((char_u *buffer));
4116static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4117static void prt_write_string __ARGS((char *s));
4118static void prt_write_int __ARGS((int i));
4119static void prt_write_boolean __ARGS((int b));
4120static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4121static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4122static void prt_write_real __ARGS((double val, int prec));
4123static void prt_def_var __ARGS((char *name, double value, int prec));
4124static void prt_flush_buffer __ARGS((void));
4125static void prt_resource_name __ARGS((char_u *filename));
4126static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4127static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4128static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4129static void prt_dsc_start __ARGS((void));
4130static void prt_dsc_noarg __ARGS((char *comment));
4131static void prt_dsc_textline __ARGS((char *comment, char *text));
4132static void prt_dsc_text __ARGS((char *comment, char *text));
4133static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4134static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4135static 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 +00004136static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4137static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138static float to_device_units __ARGS((int idx, double physsize, int def_number));
4139static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4140static void prt_font_metrics __ARGS((int font_scale));
4141static int prt_get_cpl __ARGS((void));
4142static int prt_get_lpp __ARGS((void));
4143static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004144static int prt_resfile_next_line __ARGS((void));
4145static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4146static int prt_resfile_skip_nonws __ARGS((int offset));
4147static int prt_resfile_skip_ws __ARGS((int offset));
4148static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4149#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004150static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004151static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4152static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4153static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4154#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155
4156/*
4157 * Variables for the output PostScript file.
4158 */
4159static FILE *prt_ps_fd;
4160static int prt_file_error;
4161static char_u *prt_ps_file_name = NULL;
4162
4163/*
4164 * Various offsets and dimensions in default PostScript user space (points).
4165 * Used for text positioning calculations
4166 */
4167static float prt_page_width;
4168static float prt_page_height;
4169static float prt_left_margin;
4170static float prt_right_margin;
4171static float prt_top_margin;
4172static float prt_bottom_margin;
4173static float prt_line_height;
4174static float prt_first_line_height;
4175static float prt_char_width;
4176static float prt_number_width;
4177static float prt_bgcol_offset;
4178static float prt_pos_x_moveto = 0.0;
4179static float prt_pos_y_moveto = 0.0;
4180
4181/*
4182 * Various control variables used to decide when and how to change the
4183 * PostScript graphics state.
4184 */
4185static int prt_need_moveto;
4186static int prt_do_moveto;
4187static int prt_need_font;
4188static int prt_font;
4189static int prt_need_underline;
4190static int prt_underline;
4191static int prt_do_underline;
4192static int prt_need_fgcol;
4193static int prt_fgcol;
4194static int prt_need_bgcol;
4195static int prt_do_bgcol;
4196static int prt_bgcol;
4197static int prt_new_bgcol;
4198static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004199static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004201static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202
4203/*
4204 * Variables controlling physical printing.
4205 */
4206static int prt_media;
4207static int prt_portrait;
4208static int prt_num_copies;
4209static int prt_duplex;
4210static int prt_tumble;
4211static int prt_collate;
4212
4213/*
4214 * Buffers used when generating PostScript output
4215 */
4216static char_u prt_line_buffer[257];
4217static garray_T prt_ps_buffer;
4218
4219# ifdef FEAT_MBYTE
4220static int prt_do_conv;
4221static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004222
4223static int prt_out_mbyte;
4224static int prt_custom_cmap;
4225static char prt_cmap[80];
4226static int prt_use_courier;
4227static int prt_in_ascii;
4228static int prt_half_width;
4229static char *prt_ascii_encoding;
4230static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004231# endif
4232
4233 static void
4234prt_write_file_raw_len(buffer, bytes)
4235 char_u *buffer;
4236 int bytes;
4237{
4238 if (!prt_file_error
4239 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4240 != (size_t)bytes)
4241 {
4242 EMSG(_("E455: Error writing to PostScript output file"));
4243 prt_file_error = TRUE;
4244 }
4245}
4246
4247 static void
4248prt_write_file(buffer)
4249 char_u *buffer;
4250{
4251 prt_write_file_len(buffer, STRLEN(buffer));
4252}
4253
4254 static void
4255prt_write_file_len(buffer, bytes)
4256 char_u *buffer;
4257 int bytes;
4258{
4259#ifdef EBCDIC
4260 ebcdic2ascii(buffer, bytes);
4261#endif
4262 prt_write_file_raw_len(buffer, bytes);
4263}
4264
4265/*
4266 * Write a string.
4267 */
4268 static void
4269prt_write_string(s)
4270 char *s;
4271{
4272 sprintf((char *)prt_line_buffer, "%s", s);
4273 prt_write_file(prt_line_buffer);
4274}
4275
4276/*
4277 * Write an int and a space.
4278 */
4279 static void
4280prt_write_int(i)
4281 int i;
4282{
4283 sprintf((char *)prt_line_buffer, "%d ", i);
4284 prt_write_file(prt_line_buffer);
4285}
4286
4287/*
4288 * Write a boolean and a space.
4289 */
4290 static void
4291prt_write_boolean(b)
4292 int b;
4293{
4294 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4295 prt_write_file(prt_line_buffer);
4296}
4297
4298/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004299 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300 */
4301 static void
4302prt_def_font(new_name, encoding, height, font)
4303 char *new_name;
4304 char *encoding;
4305 int height;
4306 char *font;
4307{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004308 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4309 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004311#ifdef FEAT_MBYTE
4312 if (prt_out_mbyte)
4313 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4314 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4315 else
4316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4318 new_name, height, new_name);
4319 prt_write_file(prt_line_buffer);
4320}
4321
Bram Moolenaar8299df92004-07-10 09:47:34 +00004322#ifdef FEAT_MBYTE
4323/*
4324 * Write a line to define the CID font.
4325 */
4326 static void
4327prt_def_cidfont(new_name, height, cidfont)
4328 char *new_name;
4329 int height;
4330 char *cidfont;
4331{
4332 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4333 new_name, prt_cmap, cidfont);
4334 prt_write_file(prt_line_buffer);
4335 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4336 new_name);
4337 prt_write_file(prt_line_buffer);
4338}
4339
4340/*
4341 * Write a line to define a duplicate of a CID font
4342 */
4343 static void
4344prt_dup_cidfont(original_name, new_name)
4345 char *original_name;
4346 char *new_name;
4347{
4348 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4349 prt_write_file(prt_line_buffer);
4350}
4351#endif
4352
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353/*
4354 * Convert a real value into an integer and fractional part as integers, with
4355 * the fractional part being in the range [0,10^precision). The fractional part
4356 * is also rounded based on the precision + 1'th fractional digit.
4357 */
4358 static void
4359prt_real_bits(real, precision, pinteger, pfraction)
4360 double real;
4361 int precision;
4362 int *pinteger;
4363 int *pfraction;
4364{
4365 int i;
4366 int integer;
4367 float fraction;
4368
4369 integer = (int)real;
4370 fraction = (float)(real - integer);
4371 if (real < (double)integer)
4372 fraction = -fraction;
4373 for (i = 0; i < precision; i++)
4374 fraction *= 10.0;
4375
4376 *pinteger = integer;
4377 *pfraction = (int)(fraction + 0.5);
4378}
4379
4380/*
4381 * Write a real and a space. Save bytes if real value has no fractional part!
4382 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4383 * what decimal point character to use, but PS always requires a '.'.
4384 */
4385 static void
4386prt_write_real(val, prec)
4387 double val;
4388 int prec;
4389{
4390 int integer;
4391 int fraction;
4392
4393 prt_real_bits(val, prec, &integer, &fraction);
4394 /* Emit integer part */
4395 sprintf((char *)prt_line_buffer, "%d", integer);
4396 prt_write_file(prt_line_buffer);
4397 /* Only emit fraction if necessary */
4398 if (fraction != 0)
4399 {
4400 /* Remove any trailing zeros */
4401 while ((fraction % 10) == 0)
4402 {
4403 prec--;
4404 fraction /= 10;
4405 }
4406 /* Emit fraction left padded with zeros */
4407 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
4408 prt_write_file(prt_line_buffer);
4409 }
4410 sprintf((char *)prt_line_buffer, " ");
4411 prt_write_file(prt_line_buffer);
4412}
4413
4414/*
4415 * Write a line to define a numeric variable.
4416 */
4417 static void
4418prt_def_var(name, value, prec)
4419 char *name;
4420 double value;
4421 int prec;
4422{
4423 sprintf((char *)prt_line_buffer, "/%s ", name);
4424 prt_write_file(prt_line_buffer);
4425 prt_write_real(value, prec);
4426 sprintf((char *)prt_line_buffer, "d\n");
4427 prt_write_file(prt_line_buffer);
4428}
4429
4430/* Convert size from font space to user space at current font scale */
4431#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
4432
4433 static void
4434prt_flush_buffer()
4435{
4436 if (prt_ps_buffer.ga_len > 0)
4437 {
4438 /* Any background color must be drawn first */
4439 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
4440 {
4441 int r, g, b;
4442
4443 if (prt_do_moveto)
4444 {
4445 prt_write_real(prt_pos_x_moveto, 2);
4446 prt_write_real(prt_pos_y_moveto, 2);
4447 prt_write_string("m\n");
4448 prt_do_moveto = FALSE;
4449 }
4450
4451 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004452 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453 prt_write_real(prt_line_height, 2);
4454
4455 /* Lastly add the color of the background */
4456 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
4457 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
4458 b = prt_new_bgcol & 0xff;
4459 prt_write_real(r / 255.0, 3);
4460 prt_write_real(g / 255.0, 3);
4461 prt_write_real(b / 255.0, 3);
4462 prt_write_string("bg\n");
4463 }
4464 /* Draw underlines before the text as it makes it slightly easier to
4465 * find the starting point.
4466 */
4467 if (prt_do_underline)
4468 {
4469 if (prt_do_moveto)
4470 {
4471 prt_write_real(prt_pos_x_moveto, 2);
4472 prt_write_real(prt_pos_y_moveto, 2);
4473 prt_write_string("m\n");
4474 prt_do_moveto = FALSE;
4475 }
4476
Bram Moolenaar8299df92004-07-10 09:47:34 +00004477 /* Underline length of text run */
4478 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 prt_write_string("ul\n");
4480 }
4481 /* Draw the text
4482 * Note: we write text out raw - EBCDIC conversion is handled in the
4483 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004484#ifdef FEAT_MBYTE
4485 if (prt_out_mbyte)
4486 prt_write_string("<");
4487 else
4488#endif
4489 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004491#ifdef FEAT_MBYTE
4492 if (prt_out_mbyte)
4493 prt_write_string(">");
4494 else
4495#endif
4496 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 /* Add a moveto if need be and use the appropriate show procedure */
4498 if (prt_do_moveto)
4499 {
4500 prt_write_real(prt_pos_x_moveto, 2);
4501 prt_write_real(prt_pos_y_moveto, 2);
4502 /* moveto and a show */
4503 prt_write_string("ms\n");
4504 prt_do_moveto = FALSE;
4505 }
4506 else /* Simple show */
4507 prt_write_string("s\n");
4508
4509 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004510 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 }
4512}
4513
4514static char_u *resource_filename;
4515
4516 static void
4517prt_resource_name(filename)
4518 char_u *filename;
4519{
4520 if (STRLEN(filename) >= MAXPATHL)
4521 *resource_filename = NUL;
4522 else
4523 STRCPY(resource_filename, filename);
4524}
4525
4526 static int
4527prt_find_resource(name, resource)
4528 char *name;
4529 struct prt_ps_resource_S *resource;
4530{
4531 char_u buffer[MAXPATHL + 1];
4532
4533 STRCPY(resource->name, name);
4534 /* Look for named resource file in runtimepath */
4535 STRCPY(buffer, "print");
4536 add_pathsep(buffer);
4537 STRCAT(buffer, name);
4538 STRCAT(buffer, ".ps");
4539 resource_filename = resource->filename;
4540 *resource_filename = NUL;
4541 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
4542 && resource->filename[0] != NUL);
4543}
4544
4545/* PS CR and LF characters have platform independent values */
4546#define PSLF (0x0a)
4547#define PSCR (0x0d)
4548
Bram Moolenaar8299df92004-07-10 09:47:34 +00004549/* Static buffer to read initial comments in a resource file, some can have a
4550 * couple of KB of comments! */
4551#define PRT_FILE_BUFFER_LEN (2048)
4552struct prt_resfile_buffer_S
4553{
4554 char_u buffer[PRT_FILE_BUFFER_LEN];
4555 int len;
4556 int line_start;
4557 int line_end;
4558};
4559
4560static struct prt_resfile_buffer_S prt_resfile;
4561
4562 static int
4563prt_resfile_next_line()
4564{
4565 int index;
4566
4567 /* Move to start of next line and then find end of line */
4568 index = prt_resfile.line_end + 1;
4569 while (index < prt_resfile.len)
4570 {
4571 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
4572 != PSCR)
4573 break;
4574 index++;
4575 }
4576 prt_resfile.line_start = index;
4577
4578 while (index < prt_resfile.len)
4579 {
4580 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
4581 == PSCR)
4582 break;
4583 index++;
4584 }
4585 prt_resfile.line_end = index;
4586
4587 return (index < prt_resfile.len);
4588}
4589
4590 static int
4591prt_resfile_strncmp(offset, string, len)
4592 int offset;
4593 char *string;
4594 int len;
4595{
4596 /* Force not equal if string is longer than remainder of line */
4597 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
4598 return 1;
4599
4600 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
4601 string, len);
4602}
4603
4604 static int
4605prt_resfile_skip_nonws(offset)
4606 int offset;
4607{
4608 int index;
4609
4610 index = prt_resfile.line_start + offset;
4611 while (index < prt_resfile.line_end)
4612 {
4613 if (isspace(prt_resfile.buffer[index]))
4614 return index - prt_resfile.line_start;
4615 index++;
4616 }
4617 return -1;
4618}
4619
4620 static int
4621prt_resfile_skip_ws(offset)
4622 int offset;
4623{
4624 int index;
4625
4626 index = prt_resfile.line_start + offset;
4627 while (index < prt_resfile.line_end)
4628 {
4629 if (!isspace(prt_resfile.buffer[index]))
4630 return index - prt_resfile.line_start;
4631 index++;
4632 }
4633 return -1;
4634}
4635
4636/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
4637 * if a DSC comment is found, else false */
4638 static int
4639prt_next_dsc(p_dsc_line)
4640 struct prt_dsc_line_S *p_dsc_line;
4641{
4642 int comment;
4643 int offset;
4644
4645 /* Move to start of next line */
4646 if (!prt_resfile_next_line())
4647 return FALSE;
4648
4649 /* DSC comments always start %% */
4650 if (prt_resfile_strncmp(0, "%%", 2) != 0)
4651 return FALSE;
4652
4653 /* Find type of DSC comment */
4654 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
4655 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
4656 prt_dsc_table[comment].len) == 0)
4657 break;
4658
4659 if (comment != NUM_ELEMENTS(prt_dsc_table))
4660 {
4661 /* Return type of comment */
4662 p_dsc_line->type = prt_dsc_table[comment].type;
4663 offset = prt_dsc_table[comment].len;
4664 }
4665 else
4666 {
4667 /* Unrecognised DSC comment, skip to ws after comment leader */
4668 p_dsc_line->type = PRT_DSC_MISC_TYPE;
4669 offset = prt_resfile_skip_nonws(0);
4670 if (offset == -1)
4671 return FALSE;
4672 }
4673
4674 /* Skip ws to comment value */
4675 offset = prt_resfile_skip_ws(offset);
4676 if (offset == -1)
4677 return FALSE;
4678
4679 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
4680 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
4681
4682 return TRUE;
4683}
4684
4685/* Improved hand crafted parser to get the type, title, and version number of a
4686 * PS resource file so the file details can be added to the DSC header comments.
4687 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 static int
4689prt_open_resource(resource)
4690 struct prt_ps_resource_S *resource;
4691{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004692 int offset;
4693 int seen_all;
4694 int seen_title;
4695 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004697 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698
4699 fd_resource = mch_fopen((char *)resource->filename, READBIN);
4700 if (fd_resource == NULL)
4701 {
4702 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
4703 return FALSE;
4704 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004705 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706
4707 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004708 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
4709 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 if (ferror(fd_resource))
4711 {
4712 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4713 resource->filename);
4714 fclose(fd_resource);
4715 return FALSE;
4716 }
4717
Bram Moolenaar8299df92004-07-10 09:47:34 +00004718 prt_resfile.line_end = -1;
4719 prt_resfile.line_start = 0;
4720 if (!prt_resfile_next_line())
4721 return FALSE;
4722
4723 offset = 0;
4724
4725 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
4726 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004727 {
4728 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
4729 resource->filename);
4730 fclose(fd_resource);
4731 return FALSE;
4732 }
4733
4734 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004735 offset += STRLEN(PRT_RESOURCE_HEADER);
4736 offset = prt_resfile_skip_nonws(offset);
4737 if (offset == -1)
4738 return FALSE;
4739 offset = prt_resfile_skip_ws(offset);
4740 if (offset == -1)
4741 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742
Bram Moolenaar8299df92004-07-10 09:47:34 +00004743 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
4744 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 {
4746 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4747 resource->filename);
4748 fclose(fd_resource);
4749 return FALSE;
4750 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004751 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752
4753 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004754 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
4755 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004757 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
4758 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004760 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
4761 STRLEN(PRT_RESOURCE_CMAP)) == 0)
4762 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004763 else
4764 {
4765 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4766 resource->filename);
4767 fclose(fd_resource);
4768 return FALSE;
4769 }
4770
Bram Moolenaar8299df92004-07-10 09:47:34 +00004771 /* Look for title and version of resource */
4772 resource->title[0] = '\0';
4773 resource->version[0] = '\0';
4774 seen_title = FALSE;
4775 seen_version = FALSE;
4776 seen_all = FALSE;
4777 while (!seen_all && prt_next_dsc(&dsc_line))
4778 {
4779 switch (dsc_line.type)
4780 {
4781 case PRT_DSC_TITLE_TYPE:
4782 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
4783 resource->title[dsc_line.len] = '\0';
4784 seen_title = TRUE;
4785 if (seen_version)
4786 seen_all = TRUE;
4787 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788
Bram Moolenaar8299df92004-07-10 09:47:34 +00004789 case PRT_DSC_VERSION_TYPE:
4790 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
4791 resource->version[dsc_line.len] = '\0';
4792 seen_version = TRUE;
4793 if (seen_title)
4794 seen_all = TRUE;
4795 break;
4796
4797 case PRT_DSC_ENDCOMMENTS_TYPE:
4798 /* Wont find title or resource after this comment, stop searching */
4799 seen_all = TRUE;
4800 break;
4801
4802 case PRT_DSC_MISC_TYPE:
4803 /* Not interested in whatever comment this line had */
4804 break;
4805 }
4806 }
4807
4808 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 {
4810 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4811 resource->filename);
4812 fclose(fd_resource);
4813 return FALSE;
4814 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815
4816 fclose(fd_resource);
4817
4818 return TRUE;
4819}
4820
4821 static int
4822prt_check_resource(resource, version)
4823 struct prt_ps_resource_S *resource;
4824 char_u *version;
4825{
4826 /* Version number m.n should match, the revision number does not matter */
4827 if (STRNCMP(resource->version, version, STRLEN(version)))
4828 {
4829 EMSG2(_("E621: \"%s\" resource file has wrong version"),
4830 resource->name);
4831 return FALSE;
4832 }
4833
4834 /* Other checks to be added as needed */
4835 return TRUE;
4836}
4837
4838 static void
4839prt_dsc_start()
4840{
4841 prt_write_string("%!PS-Adobe-3.0\n");
4842}
4843
4844 static void
4845prt_dsc_noarg(comment)
4846 char *comment;
4847{
4848 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
4849 prt_write_file(prt_line_buffer);
4850}
4851
4852 static void
4853prt_dsc_textline(comment, text)
4854 char *comment;
4855 char *text;
4856{
4857 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
4858 prt_write_file(prt_line_buffer);
4859}
4860
4861 static void
4862prt_dsc_text(comment, text)
4863 char *comment;
4864 char *text;
4865{
4866 /* TODO - should scan 'text' for any chars needing escaping! */
4867 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
4868 prt_write_file(prt_line_buffer);
4869}
4870
4871#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
4872
4873 static void
4874prt_dsc_ints(comment, count, ints)
4875 char *comment;
4876 int count;
4877 int *ints;
4878{
4879 int i;
4880
4881 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
4882 prt_write_file(prt_line_buffer);
4883
4884 for (i = 0; i < count; i++)
4885 {
4886 sprintf((char *)prt_line_buffer, " %d", ints[i]);
4887 prt_write_file(prt_line_buffer);
4888 }
4889
4890 prt_write_string("\n");
4891}
4892
4893 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00004894prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 char *comment; /* if NULL add to previous */
4896 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004897 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 if (comment != NULL)
4900 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
4901 else
4902 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
4903 prt_write_file(prt_line_buffer);
4904
Bram Moolenaar8299df92004-07-10 09:47:34 +00004905 sprintf((char *)prt_line_buffer, " %s\n", string);
4906 prt_write_file(prt_line_buffer);
4907}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908
Bram Moolenaar8299df92004-07-10 09:47:34 +00004909 static void
4910prt_dsc_font_resource(resource, ps_font)
4911 char *resource;
4912 struct prt_ps_font_S *ps_font;
4913{
4914 int i;
4915
4916 prt_dsc_resources(resource, "font",
4917 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
4918 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
4919 if (ps_font->ps_fontname[i] != NULL)
4920 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921}
4922
4923 static void
4924prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
4925 int duplex;
4926 int tumble;
4927 int collate;
4928 int color;
4929 int num_copies;
4930{
4931 /* Only output the comment if we need to.
4932 * Note: tumble is ignored if we are not duplexing
4933 */
4934 if (!(duplex || collate || color || (num_copies > 1)))
4935 return;
4936
4937 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
4938 prt_write_file(prt_line_buffer);
4939
4940 if (duplex)
4941 {
4942 prt_write_string(" duplex");
4943 if (tumble)
4944 prt_write_string("(tumble)");
4945 }
4946 if (collate)
4947 prt_write_string(" collate");
4948 if (color)
4949 prt_write_string(" color");
4950 if (num_copies > 1)
4951 {
4952 prt_write_string(" numcopies(");
4953 /* Note: no space wanted so dont use prt_write_int() */
4954 sprintf((char *)prt_line_buffer, "%d", num_copies);
4955 prt_write_file(prt_line_buffer);
4956 prt_write_string(")");
4957 }
4958 prt_write_string("\n");
4959}
4960
4961 static void
4962prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
4963 char *paper_name;
4964 double width;
4965 double height;
4966 double weight;
4967 char *colour;
4968 char *type;
4969{
4970 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
4971 prt_write_file(prt_line_buffer);
4972 prt_write_real(width, 2);
4973 prt_write_real(height, 2);
4974 prt_write_real(weight, 2);
4975 if (colour == NULL)
4976 prt_write_string("()");
4977 else
4978 prt_write_string(colour);
4979 prt_write_string(" ");
4980 if (type == NULL)
4981 prt_write_string("()");
4982 else
4983 prt_write_string(type);
4984 prt_write_string("\n");
4985}
4986
4987 void
4988mch_print_cleanup()
4989{
4990#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004991 if (prt_out_mbyte)
4992 {
4993 int i;
4994
4995 /* Free off all CID font names created, but first clear duplicate
4996 * pointers to the same string (when the same font is used for more than
4997 * one style).
4998 */
4999 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5000 {
5001 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5002 vim_free(prt_ps_mb_font.ps_fontname[i]);
5003 prt_ps_mb_font.ps_fontname[i] = NULL;
5004 }
5005 }
5006
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007 if (prt_do_conv)
5008 {
5009 convert_setup(&prt_conv, NULL, NULL);
5010 prt_do_conv = FALSE;
5011 }
5012#endif
5013 if (prt_ps_fd != NULL)
5014 {
5015 fclose(prt_ps_fd);
5016 prt_ps_fd = NULL;
5017 prt_file_error = FALSE;
5018 }
5019 if (prt_ps_file_name != NULL)
5020 {
5021 vim_free(prt_ps_file_name);
5022 prt_ps_file_name = NULL;
5023 }
5024}
5025
5026 static float
5027to_device_units(idx, physsize, def_number)
5028 int idx;
5029 double physsize;
5030 int def_number;
5031{
5032 float ret;
5033 int u;
5034 int nr;
5035
5036 u = prt_get_unit(idx);
5037 if (u == PRT_UNIT_NONE)
5038 {
5039 u = PRT_UNIT_PERC;
5040 nr = def_number;
5041 }
5042 else
5043 nr = printer_opts[idx].number;
5044
5045 switch (u)
5046 {
5047 case PRT_UNIT_INCH:
5048 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5049 break;
5050 case PRT_UNIT_MM:
5051 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5052 break;
5053 case PRT_UNIT_POINT:
5054 ret = (float)nr;
5055 break;
5056 case PRT_UNIT_PERC:
5057 default:
5058 ret = (float)(physsize * nr) / 100;
5059 break;
5060 }
5061
5062 return ret;
5063}
5064
5065/*
5066 * Calculate margins for given width and height from printoptions settings.
5067 */
5068 static void
5069prt_page_margins(width, height, left, right, top, bottom)
5070 double width;
5071 double height;
5072 double *left;
5073 double *right;
5074 double *top;
5075 double *bottom;
5076{
5077 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5078 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5079 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5080 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5081}
5082
5083 static void
5084prt_font_metrics(font_scale)
5085 int font_scale;
5086{
5087 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005088 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089}
5090
5091
5092 static int
5093prt_get_cpl()
5094{
5095 if (prt_use_number())
5096 {
5097 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005098#ifdef FEAT_MBYTE
5099 /* If we are outputting multi-byte characters then line numbers will be
5100 * printed with half width characters
5101 */
5102 if (prt_out_mbyte)
5103 prt_number_width /= 2;
5104#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005105 prt_left_margin += prt_number_width;
5106 }
5107 else
5108 prt_number_width = 0.0;
5109
5110 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5111}
5112
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005113#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005114 static int
5115prt_build_cid_fontname(font, name, name_len)
5116 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005117 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005118 int name_len;
5119{
5120 char *fontname;
5121
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005122 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005123 if (fontname == NULL)
5124 return FALSE;
5125 STRNCPY(fontname, name, name_len);
5126 fontname[name_len] = '\0';
5127 prt_ps_mb_font.ps_fontname[font] = fontname;
5128
5129 return TRUE;
5130}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005131#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005132
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133/*
5134 * Get number of lines of text that fit on a page (excluding the header).
5135 */
5136 static int
5137prt_get_lpp()
5138{
5139 int lpp;
5140
5141 /*
5142 * Calculate offset to lower left corner of background rect based on actual
5143 * font height (based on its bounding box) and the line height, handling the
5144 * case where the font height can exceed the line height.
5145 */
5146 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005147 prt_ps_font->bbox_min_y);
5148 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005149 {
5150 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005151 (1000.0 - (prt_ps_font->bbox_max_y -
5152 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153 }
5154
5155 /* Get height for topmost line based on background rect offset. */
5156 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5157
5158 /* Calculate lpp */
5159 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5160
5161 /* Adjust top margin if there is a header */
5162 prt_top_margin -= prt_line_height * prt_header_height();
5163
5164 return lpp - prt_header_height();
5165}
5166
Bram Moolenaar8299df92004-07-10 09:47:34 +00005167#ifdef FEAT_MBYTE
5168 static int
5169prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005170 char *p_encoding;
5171 struct prt_ps_mbfont_S *p_cmap;
5172 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005173{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005174 int mbenc;
5175 int enc_len;
5176 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005177
5178 *pp_mbenc = NULL;
5179 /* Look for recognised encoding */
5180 enc_len = STRLEN(p_encoding);
5181 p_mbenc = p_cmap->encodings;
5182 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5183 {
5184 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5185 {
5186 *pp_mbenc = p_mbenc;
5187 return TRUE;
5188 }
5189 p_mbenc++;
5190 }
5191 return FALSE;
5192}
5193
5194 static int
5195prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005196 char *p_charset;
5197 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005198 struct prt_ps_charset_S **pp_mbchar;
5199{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005200 int mbchar;
5201 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005202 struct prt_ps_charset_S *p_mbchar;
5203
5204 /* Look for recognised character set, using default if one is not given */
5205 if (*p_charset == NUL)
5206 p_charset = p_cmap->defcs;
5207 char_len = STRLEN(p_charset);
5208 p_mbchar = p_cmap->charsets;
5209 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5210 {
5211 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5212 {
5213 *pp_mbchar = p_mbchar;
5214 return TRUE;
5215 }
5216 p_mbchar++;
5217 }
5218 return FALSE;
5219}
5220#endif
5221
Bram Moolenaar071d4272004-06-13 20:20:40 +00005222/*ARGSUSED*/
5223 int
5224mch_print_init(psettings, jobname, forceit)
5225 prt_settings_T *psettings;
5226 char_u *jobname;
5227 int forceit;
5228{
5229 int i;
5230 char *paper_name;
5231 int paper_strlen;
5232 int fontsize;
5233 char_u *p;
5234 double left;
5235 double right;
5236 double top;
5237 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005238#ifdef FEAT_MBYTE
5239 int cmap;
5240 int pmcs_len;
5241 char_u *p_encoding;
5242 struct prt_ps_encoding_S *p_mbenc;
5243 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005244 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005245#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005246
5247#if 0
5248 /*
5249 * TODO:
5250 * If "forceit" is false: pop up a dialog to select:
5251 * - printer name
5252 * - copies
5253 * - collated/uncollated
5254 * - duplex off/long side/short side
5255 * - paper size
5256 * - portrait/landscape
5257 * - font size
5258 *
5259 * If "forceit" is true: use the default printer and settings
5260 */
5261 if (forceit)
5262 s_pd.Flags |= PD_RETURNDEFAULT;
5263#endif
5264
5265 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005266 * Set up font and encoding.
5267 */
5268#ifdef FEAT_MBYTE
5269 p_encoding = enc_skip(p_penc);
5270 if (*p_encoding == NUL)
5271 p_encoding = enc_skip(p_enc);
5272
5273 /* Look for recognised multi-byte coding, and if the charset is recognised.
5274 * This is to cope with the fact that various unicode encodings are
5275 * supported in more than one of CJK. */
5276 p_mbenc = NULL;
5277 p_mbenc_first = NULL;
5278 p_mbchar = NULL;
5279 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005280 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5281 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005282 {
5283 if (p_mbenc_first == NULL)
5284 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005285 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5286 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005287 break;
5288 }
5289
5290 /* Use first encoding matched if no charset matched */
5291 if (p_mbchar == NULL && p_mbenc_first != NULL)
5292 p_mbenc = p_mbenc_first;
5293
5294 prt_out_mbyte = (p_mbenc != NULL);
5295 if (prt_out_mbyte)
5296 {
5297 /* Build CMap name - will be same for all multi-byte fonts used */
5298 prt_cmap[0] = '\0';
5299
5300 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5301
5302 if (!prt_custom_cmap)
5303 {
5304 /* Check encoding and character set are compatible */
5305 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5306 {
5307 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5308 return FALSE;
5309 }
5310
5311 /* Add charset name if not empty */
5312 if (p_mbchar->cmap_charset != NULL)
5313 {
5314 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5315 STRCAT(prt_cmap, "-");
5316 }
5317 }
5318 else
5319 {
5320 /* Add custom CMap character set name */
5321 pmcs_len = STRLEN(p_pmcs);
5322 if (pmcs_len == 0)
5323 {
5324 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5325 return FALSE;
5326 }
5327 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5328 prt_cmap[pmcs_len] = '\0';
5329 STRCAT(prt_cmap, "-");
5330 }
5331
5332 /* CMap name ends with (optional) encoding name and -H for horizontal */
5333 if (p_mbenc->cmap_encoding != NULL)
5334 {
5335 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5336 STRCAT(prt_cmap, "-");
5337 }
5338 STRCAT(prt_cmap, "H");
5339
5340 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5341 {
5342 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5343 return FALSE;
5344 }
5345
5346 /* Derive CID font names with fallbacks if not defined */
5347 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5348 mbfont_opts[OPT_MBFONT_REGULAR].string,
5349 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5350 return FALSE;
5351 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5352 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5353 mbfont_opts[OPT_MBFONT_BOLD].string,
5354 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5355 return FALSE;
5356 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5357 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5358 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
5359 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
5360 return FALSE;
5361 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
5362 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005363 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
5364 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005365 return FALSE;
5366
5367 /* Check if need to use Courier for ASCII code range, and if so pick up
5368 * the encoding to use */
5369 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
5370 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
5371 if (prt_use_courier)
5372 {
5373 /* Use national ASCII variant unless ASCII wanted */
5374 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
5375 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
5376 prt_ascii_encoding = "ascii";
5377 else
5378 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
5379 }
5380
5381 prt_ps_font = &prt_ps_mb_font;
5382 }
5383 else
5384#endif
5385 {
5386#ifdef FEAT_MBYTE
5387 prt_use_courier = FALSE;
5388#endif
5389 prt_ps_font = &prt_ps_courier_font;
5390 }
5391
5392 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393 * Find the size of the paper and set the margins.
5394 */
5395 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
5396 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
5397 if (printer_opts[OPT_PRINT_PAPER].present)
5398 {
5399 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
5400 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
5401 }
5402 else
5403 {
5404 paper_name = "A4";
5405 paper_strlen = 2;
5406 }
5407 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
5408 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
5409 && STRNICMP(prt_mediasize[i].name, paper_name,
5410 paper_strlen) == 0)
5411 break;
5412 if (i == PRT_MEDIASIZE_LEN)
5413 i = 0;
5414 prt_media = i;
5415
5416 /*
5417 * Set PS pagesize based on media dimensions and print orientation.
5418 * Note: Media and page sizes have defined meanings in PostScript and should
5419 * be kept distinct. Media is the paper (or transparency, or ...) that is
5420 * printed on, whereas the page size is the area that the PostScript
5421 * interpreter renders into.
5422 */
5423 if (prt_portrait)
5424 {
5425 prt_page_width = prt_mediasize[i].width;
5426 prt_page_height = prt_mediasize[i].height;
5427 }
5428 else
5429 {
5430 prt_page_width = prt_mediasize[i].height;
5431 prt_page_height = prt_mediasize[i].width;
5432 }
5433
5434 /*
5435 * Set PS page margins based on the PS pagesize, not the mediasize - this
5436 * needs to be done before the cpl and lpp are calculated.
5437 */
5438 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
5439 &bottom);
5440 prt_left_margin = (float)left;
5441 prt_right_margin = (float)right;
5442 prt_top_margin = (float)top;
5443 prt_bottom_margin = (float)bottom;
5444
5445 /*
5446 * Set up the font size.
5447 */
5448 fontsize = PRT_PS_DEFAULT_FONTSIZE;
5449 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
5450 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
5451 fontsize = atoi((char *)p + 2);
5452 prt_font_metrics(fontsize);
5453
5454 /*
5455 * Return the number of characters per line, and lines per page for the
5456 * generic print code.
5457 */
5458 psettings->chars_per_line = prt_get_cpl();
5459 psettings->lines_per_page = prt_get_lpp();
5460
5461 /* Catch margin settings that leave no space for output! */
5462 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
5463 return FAIL;
5464
5465 /*
5466 * Sort out the number of copies to be printed. PS by default will do
5467 * uncollated copies for you, so once we know how many uncollated copies are
5468 * wanted cache it away and lie to the generic code that we only want one
5469 * uncollated copy.
5470 */
5471 psettings->n_collated_copies = 1;
5472 psettings->n_uncollated_copies = 1;
5473 prt_num_copies = 1;
5474 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
5475 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
5476 if (prt_collate)
5477 {
5478 /* TODO: Get number of collated copies wanted. */
5479 psettings->n_collated_copies = 1;
5480 }
5481 else
5482 {
5483 /* TODO: Get number of uncollated copies wanted and update the cached
5484 * count.
5485 */
5486 prt_num_copies = 1;
5487 }
5488
5489 psettings->jobname = jobname;
5490
5491 /*
5492 * Set up printer duplex and tumble based on Duplex option setting - default
5493 * is long sided duplex printing (i.e. no tumble).
5494 */
5495 prt_duplex = TRUE;
5496 prt_tumble = FALSE;
5497 psettings->duplex = 1;
5498 if (printer_opts[OPT_PRINT_DUPLEX].present)
5499 {
5500 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
5501 {
5502 prt_duplex = FALSE;
5503 psettings->duplex = 0;
5504 }
5505 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
5506 == 0)
5507 prt_tumble = TRUE;
5508 }
5509
5510 /* For now user abort not supported */
5511 psettings->user_abort = 0;
5512
5513 /* If the user didn't specify a file name, use a temp file. */
5514 if (psettings->outfile == NULL)
5515 {
5516 prt_ps_file_name = vim_tempname('p');
5517 if (prt_ps_file_name == NULL)
5518 {
5519 EMSG(_(e_notmp));
5520 return FAIL;
5521 }
5522 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
5523 }
5524 else
5525 {
5526 p = expand_env_save(psettings->outfile);
5527 if (p != NULL)
5528 {
5529 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
5530 vim_free(p);
5531 }
5532 }
5533 if (prt_ps_fd == NULL)
5534 {
5535 EMSG(_("E324: Can't open PostScript output file"));
5536 mch_print_cleanup();
5537 return FAIL;
5538 }
5539
Bram Moolenaar8299df92004-07-10 09:47:34 +00005540 prt_bufsiz = psettings->chars_per_line;
5541#ifdef FEAT_MBYTE
5542 if (prt_out_mbyte)
5543 prt_bufsiz *= 2;
5544#endif
5545 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546
5547 prt_page_num = 0;
5548
5549 prt_attribute_change = FALSE;
5550 prt_need_moveto = FALSE;
5551 prt_need_font = FALSE;
5552 prt_need_fgcol = FALSE;
5553 prt_need_bgcol = FALSE;
5554 prt_need_underline = FALSE;
5555
5556 prt_file_error = FALSE;
5557
5558 return OK;
5559}
5560
5561 static int
5562prt_add_resource(resource)
5563 struct prt_ps_resource_S *resource;
5564{
5565 FILE* fd_resource;
5566 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567 size_t bytes_read;
5568
5569 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5570 if (fd_resource == NULL)
5571 {
5572 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
5573 return FALSE;
5574 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005575 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
5576 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577
5578 prt_dsc_textline("BeginDocument", (char *)resource->filename);
5579
5580 for (;;)
5581 {
5582 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
5583 sizeof(resource_buffer), fd_resource);
5584 if (ferror(fd_resource))
5585 {
5586 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5587 resource->filename);
5588 fclose(fd_resource);
5589 return FALSE;
5590 }
5591 if (bytes_read == 0)
5592 break;
5593 prt_write_file_raw_len(resource_buffer, bytes_read);
5594 if (prt_file_error)
5595 {
5596 fclose(fd_resource);
5597 return FALSE;
5598 }
5599 }
5600 fclose(fd_resource);
5601
5602 prt_dsc_noarg("EndDocument");
5603
5604 prt_dsc_noarg("EndResource");
5605
5606 return TRUE;
5607}
5608
5609 int
5610mch_print_begin(psettings)
5611 prt_settings_T *psettings;
5612{
5613 time_t now;
5614 int bbox[4];
5615 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616 double left;
5617 double right;
5618 double top;
5619 double bottom;
5620 struct prt_ps_resource_S res_prolog;
5621 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005622 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005623 char_u *p_encoding;
5624#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005625 struct prt_ps_resource_S res_cidfont;
5626 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627#endif
5628
5629 /*
5630 * PS DSC Header comments - no PS code!
5631 */
5632 prt_dsc_start();
5633 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005634 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005635 STRCPY(buffer, "Unknown");
5636 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 prt_dsc_textline("Creator", VIM_VERSION_LONG);
5638 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
5639 now = time(NULL);
5640 p_time = ctime(&now);
5641 /* Note: ctime() adds a \n so we have to remove it :-( */
5642 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
5643 prt_dsc_textline("CreationDate", p_time);
5644 prt_dsc_textline("DocumentData", "Clean8Bit");
5645 prt_dsc_textline("Orientation", "Portrait");
5646 prt_dsc_atend("Pages");
5647 prt_dsc_textline("PageOrder", "Ascend");
5648 /* The bbox does not change with orientation - it is always in the default
5649 * user coordinate system! We have to recalculate right and bottom
5650 * coordinates based on the font metrics for the bbox to be accurate. */
5651 prt_page_margins(prt_mediasize[prt_media].width,
5652 prt_mediasize[prt_media].height,
5653 &left, &right, &top, &bottom);
5654 bbox[0] = (int)left;
5655 if (prt_portrait)
5656 {
5657 /* In portrait printing the fixed point is the top left corner so we
5658 * derive the bbox from that point. We have the expected cpl chars
5659 * across the media and lpp lines down the media.
5660 */
5661 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
5662 * prt_line_height);
5663 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
5664 + 0.5);
5665 bbox[3] = (int)(top + 0.5);
5666 }
5667 else
5668 {
5669 /* In landscape printing the fixed point is the bottom left corner so we
5670 * derive the bbox from that point. We have lpp chars across the media
5671 * and cpl lines up the media.
5672 */
5673 bbox[1] = (int)bottom;
5674 bbox[2] = (int)(left + ((psettings->lines_per_page
5675 + prt_header_height()) * prt_line_height) + 0.5);
5676 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
5677 + 0.5);
5678 }
5679 prt_dsc_ints("BoundingBox", 4, bbox);
5680 /* The media width and height does not change with landscape printing! */
5681 prt_dsc_docmedia(prt_mediasize[prt_media].name,
5682 prt_mediasize[prt_media].width,
5683 prt_mediasize[prt_media].height,
5684 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005685 /* Define fonts needed */
5686#ifdef FEAT_MBYTE
5687 if (!prt_out_mbyte || prt_use_courier)
5688#endif
5689 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
5690#ifdef FEAT_MBYTE
5691 if (prt_out_mbyte)
5692 {
5693 prt_dsc_font_resource((prt_use_courier ? NULL
5694 : "DocumentNeededResources"), &prt_ps_mb_font);
5695 if (!prt_custom_cmap)
5696 prt_dsc_resources(NULL, "cmap", prt_cmap);
5697 }
5698#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699
Bram Moolenaar8299df92004-07-10 09:47:34 +00005700 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701 if (!prt_find_resource("prolog", &res_prolog))
5702 {
5703 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
5704 return FALSE;
5705 }
5706 if (!prt_open_resource(&res_prolog))
5707 return FALSE;
5708 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
5709 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005710#ifdef FEAT_MBYTE
5711 if (prt_out_mbyte)
5712 {
5713 /* Look for required version of multi-byte printing procset */
5714 if (!prt_find_resource("cidfont", &res_cidfont))
5715 {
5716 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
5717 return FALSE;
5718 }
5719 if (!prt_open_resource(&res_cidfont))
5720 return FALSE;
5721 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
5722 return FALSE;
5723 }
5724#endif
5725
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 /* Find an encoding to use for printing.
5727 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
5728 * that cannot be found then default to "latin1".
5729 * Note: VIM specific encoding header is always skipped.
5730 */
5731#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005732 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005735 p_encoding = enc_skip(p_penc);
5736 if (*p_encoding == NUL
5737 || !prt_find_resource((char *)p_encoding, &res_encoding))
5738 {
5739 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005741 int props;
5742
5743 p_encoding = enc_skip(p_enc);
5744 props = enc_canon_props(p_encoding);
5745 if (!(props & ENC_8BIT)
5746 || !prt_find_resource((char *)p_encoding, &res_encoding))
5747 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005749 {
5750 /* Use latin1 as default printing encoding */
5751 p_encoding = (char_u *)"latin1";
5752 if (!prt_find_resource((char *)p_encoding, &res_encoding))
5753 {
5754 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5755 p_encoding);
5756 return FALSE;
5757 }
5758 }
5759 }
5760 if (!prt_open_resource(&res_encoding))
5761 return FALSE;
5762 /* For the moment there are no checks on encoding resource files to
5763 * perform */
5764#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005766 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005767 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005768 p_encoding = enc_skip(p_penc);
5769 if (*p_encoding == NUL)
5770 p_encoding = enc_skip(p_enc);
5771 if (prt_use_courier)
5772 {
5773 /* Include ASCII range encoding vector */
5774 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
5775 {
5776 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005777 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005778 return FALSE;
5779 }
5780 if (!prt_open_resource(&res_encoding))
5781 return FALSE;
5782 /* For the moment there are no checks on encoding resource files to
5783 * perform */
5784 }
5785 }
5786
5787 prt_conv.vc_type = CONV_NONE;
5788 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
5789 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005790 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
5791 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005792 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 p_encoding);
5794 return FALSE;
5795 }
5796 prt_do_conv = TRUE;
5797 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005798 prt_do_conv = prt_conv.vc_type != CONV_NONE;
5799
5800 if (prt_out_mbyte && prt_custom_cmap)
5801 {
5802 /* Find user supplied CMap */
5803 if (!prt_find_resource(prt_cmap, &res_cmap))
5804 {
5805 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005806 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005807 return FALSE;
5808 }
5809 if (!prt_open_resource(&res_cmap))
5810 return FALSE;
5811 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812#endif
5813
5814 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005815 STRCPY(buffer, res_prolog.title);
5816 STRCAT(buffer, " ");
5817 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005818 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
5819#ifdef FEAT_MBYTE
5820 if (prt_out_mbyte)
5821 {
5822 STRCPY(buffer, res_cidfont.title);
5823 STRCAT(buffer, " ");
5824 STRCAT(buffer, res_cidfont.version);
5825 prt_dsc_resources(NULL, "procset", buffer);
5826
5827 if (prt_custom_cmap)
5828 {
5829 STRCPY(buffer, res_cmap.title);
5830 STRCAT(buffer, " ");
5831 STRCAT(buffer, res_cmap.version);
5832 prt_dsc_resources(NULL, "cmap", buffer);
5833 }
5834 }
5835 if (!prt_out_mbyte || prt_use_courier)
5836#endif
5837 {
5838 STRCPY(buffer, res_encoding.title);
5839 STRCAT(buffer, " ");
5840 STRCAT(buffer, res_encoding.version);
5841 prt_dsc_resources(NULL, "encoding", buffer);
5842 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
5844#ifdef FEAT_SYN_HL
5845 psettings->do_syntax
5846#else
5847 0
5848#endif
5849 , prt_num_copies);
5850 prt_dsc_noarg("EndComments");
5851
5852 /*
5853 * PS Document page defaults
5854 */
5855 prt_dsc_noarg("BeginDefaults");
5856
5857 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005858#ifdef FEAT_MBYTE
5859 if (!prt_out_mbyte || prt_use_courier)
5860#endif
5861 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
5862#ifdef FEAT_MBYTE
5863 if (prt_out_mbyte)
5864 {
5865 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
5866 &prt_ps_mb_font);
5867 if (!prt_custom_cmap)
5868 prt_dsc_resources(NULL, "cmap", prt_cmap);
5869 }
5870#endif
5871
Bram Moolenaar071d4272004-06-13 20:20:40 +00005872 /* Paper will be used for all pages */
5873 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
5874
5875 prt_dsc_noarg("EndDefaults");
5876
5877 /*
5878 * PS Document prolog inclusion - all required procsets.
5879 */
5880 prt_dsc_noarg("BeginProlog");
5881
Bram Moolenaar8299df92004-07-10 09:47:34 +00005882 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005883 if (!prt_add_resource(&res_prolog))
5884 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005885#ifdef FEAT_MBYTE
5886 if (prt_out_mbyte)
5887 {
5888 /* Add CID font procset, and any user supplied CMap */
5889 if (!prt_add_resource(&res_cidfont))
5890 return FALSE;
5891 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
5892 return FALSE;
5893 }
5894#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895
Bram Moolenaar8299df92004-07-10 09:47:34 +00005896#ifdef FEAT_MBYTE
5897 if (!prt_out_mbyte || prt_use_courier)
5898#endif
5899 /* There will be only one Roman font encoding to be included in the PS
5900 * file. */
5901 if (!prt_add_resource(&res_encoding))
5902 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903
5904 prt_dsc_noarg("EndProlog");
5905
5906 /*
5907 * PS Document setup - must appear after the prolog
5908 */
5909 prt_dsc_noarg("BeginSetup");
5910
5911 /* Device setup - page size and number of uncollated copies */
5912 prt_write_int((int)prt_mediasize[prt_media].width);
5913 prt_write_int((int)prt_mediasize[prt_media].height);
5914 prt_write_int(0);
5915 prt_write_string("sps\n");
5916 prt_write_int(prt_num_copies);
5917 prt_write_string("nc\n");
5918 prt_write_boolean(prt_duplex);
5919 prt_write_boolean(prt_tumble);
5920 prt_write_string("dt\n");
5921 prt_write_boolean(prt_collate);
5922 prt_write_string("c\n");
5923
5924 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005925#ifdef FEAT_MBYTE
5926 if (!prt_out_mbyte || prt_use_courier)
5927 {
5928 /* When using Courier for ASCII range when printing multi-byte, need to
5929 * pick up ASCII encoding to use with it. */
5930 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005931 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005932#endif
5933 prt_dsc_resources("IncludeResource", "font",
5934 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5935 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
5936 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5937 prt_dsc_resources("IncludeResource", "font",
5938 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5939 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
5940 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5941 prt_dsc_resources("IncludeResource", "font",
5942 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5943 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
5944 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5945 prt_dsc_resources("IncludeResource", "font",
5946 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5947 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
5948 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5949#ifdef FEAT_MBYTE
5950 }
5951 if (prt_out_mbyte)
5952 {
5953 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
5954 * not have an italic form being a western style, so where no font is
5955 * defined for these faces VIM falls back to an existing face.
5956 * Note: if using Courier for the ASCII range then the printout will
5957 * have bold/italic/bolditalic regardless of the setting of printmbfont.
5958 */
5959 prt_dsc_resources("IncludeResource", "font",
5960 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5961 if (!prt_custom_cmap)
5962 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5963 prt_def_cidfont("CF0", (int)prt_line_height,
5964 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5965
5966 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
5967 {
5968 prt_dsc_resources("IncludeResource", "font",
5969 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5970 if (!prt_custom_cmap)
5971 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5972 prt_def_cidfont("CF1", (int)prt_line_height,
5973 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5974 }
5975 else
5976 /* Use ROMAN for BOLD */
5977 prt_dup_cidfont("CF0", "CF1");
5978
5979 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
5980 {
5981 prt_dsc_resources("IncludeResource", "font",
5982 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5983 if (!prt_custom_cmap)
5984 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5985 prt_def_cidfont("CF2", (int)prt_line_height,
5986 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5987 }
5988 else
5989 /* Use ROMAN for OBLIQUE */
5990 prt_dup_cidfont("CF0", "CF2");
5991
5992 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
5993 {
5994 prt_dsc_resources("IncludeResource", "font",
5995 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5996 if (!prt_custom_cmap)
5997 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5998 prt_def_cidfont("CF3", (int)prt_line_height,
5999 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6000 }
6001 else
6002 /* Use BOLD for BOLDOBLIQUE */
6003 prt_dup_cidfont("CF1", "CF3");
6004 }
6005#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006
6007 /* Misc constant vars used for underlining and background rects */
6008 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006009 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006010 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006011 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006012 prt_def_var("BO", prt_bgcol_offset, 2);
6013
6014 prt_dsc_noarg("EndSetup");
6015
6016 /* Fail if any problems writing out to the PS file */
6017 return !prt_file_error;
6018}
6019
6020 void
6021mch_print_end(psettings)
6022 prt_settings_T *psettings;
6023{
6024 prt_dsc_noarg("Trailer");
6025
6026 /*
6027 * Output any info we don't know in toto until we finish
6028 */
6029 prt_dsc_ints("Pages", 1, &prt_page_num);
6030
6031 prt_dsc_noarg("EOF");
6032
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006033 /* Write CTRL-D to close serial communication link if used.
6034 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006035 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006036
Bram Moolenaar071d4272004-06-13 20:20:40 +00006037 if (!prt_file_error && psettings->outfile == NULL
6038 && !got_int && !psettings->user_abort)
6039 {
6040 /* Close the file first. */
6041 if (prt_ps_fd != NULL)
6042 {
6043 fclose(prt_ps_fd);
6044 prt_ps_fd = NULL;
6045 }
6046 prt_message((char_u *)_("Sending to printer..."));
6047
6048 /* Not printing to a file: use 'printexpr' to print the file. */
6049 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6050 EMSG(_("E365: Failed to print PostScript file"));
6051 else
6052 prt_message((char_u *)_("Print job sent."));
6053 }
6054
6055 mch_print_cleanup();
6056}
6057
6058 int
6059mch_print_end_page()
6060{
6061 prt_flush_buffer();
6062
6063 prt_write_string("re sp\n");
6064
6065 prt_dsc_noarg("PageTrailer");
6066
6067 return !prt_file_error;
6068}
6069
6070/*ARGSUSED*/
6071 int
6072mch_print_begin_page(str)
6073 char_u *str;
6074{
6075 int page_num[2];
6076
6077 prt_page_num++;
6078
6079 page_num[0] = page_num[1] = prt_page_num;
6080 prt_dsc_ints("Page", 2, page_num);
6081
6082 prt_dsc_noarg("BeginPageSetup");
6083
Bram Moolenaar8299df92004-07-10 09:47:34 +00006084 prt_write_string("sv\n0 g\n");
6085#ifdef FEAT_MBYTE
6086 prt_in_ascii = !prt_out_mbyte;
6087 if (prt_out_mbyte)
6088 prt_write_string("CF0 sf\n");
6089 else
6090#endif
6091 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006092 prt_fgcol = PRCOLOR_BLACK;
6093 prt_bgcol = PRCOLOR_WHITE;
6094 prt_font = PRT_PS_FONT_ROMAN;
6095
6096 /* Set up page transformation for landscape printing. */
6097 if (!prt_portrait)
6098 {
6099 prt_write_int(-((int)prt_mediasize[prt_media].width));
6100 prt_write_string("sl\n");
6101 }
6102
6103 prt_dsc_noarg("EndPageSetup");
6104
6105 /* We have reset the font attributes, force setting them again. */
6106 curr_bg = (long_u)0xffffffff;
6107 curr_fg = (long_u)0xffffffff;
6108 curr_bold = MAYBE;
6109
6110 return !prt_file_error;
6111}
6112
6113 int
6114mch_print_blank_page()
6115{
6116 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6117}
6118
6119static float prt_pos_x = 0;
6120static float prt_pos_y = 0;
6121
6122 void
6123mch_print_start_line(margin, page_line)
6124 int margin;
6125 int page_line;
6126{
6127 prt_pos_x = prt_left_margin;
6128 if (margin)
6129 prt_pos_x -= prt_number_width;
6130
6131 prt_pos_y = prt_top_margin - prt_first_line_height -
6132 page_line * prt_line_height;
6133
6134 prt_attribute_change = TRUE;
6135 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006136#ifdef FEAT_MBYTE
6137 prt_half_width = FALSE;
6138#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139}
6140
6141/*ARGSUSED*/
6142 int
6143mch_print_text_out(p, len)
6144 char_u *p;
6145 int len;
6146{
6147 int need_break;
6148 char_u ch;
6149 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006150 float char_width;
6151 float next_pos;
6152#ifdef FEAT_MBYTE
6153 int in_ascii;
6154 int half_width;
6155#endif
6156
6157 char_width = prt_char_width;
6158
6159#ifdef FEAT_MBYTE
6160 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6161 * CJKV font to do what VIM is doing here - use a Roman font for characters
6162 * in the ASCII range, and the origingal CID font for everything else.
6163 * The problem is that GhostScript still (as of 8.13) does not support
6164 * rearranged fonts even though they have been documented by Adobe for 7
6165 * years! If they ever do, a lot of this code will disappear.
6166 */
6167 if (prt_use_courier)
6168 {
6169 in_ascii = (len == 1 && *p < 0x80);
6170 if (prt_in_ascii)
6171 {
6172 if (!in_ascii)
6173 {
6174 /* No longer in ASCII range - need to switch font */
6175 prt_in_ascii = FALSE;
6176 prt_need_font = TRUE;
6177 prt_attribute_change = TRUE;
6178 }
6179 }
6180 else if (in_ascii)
6181 {
6182 /* Now in ASCII range - need to switch font */
6183 prt_in_ascii = TRUE;
6184 prt_need_font = TRUE;
6185 prt_attribute_change = TRUE;
6186 }
6187 }
6188 if (prt_out_mbyte)
6189 {
6190 half_width = ((*mb_ptr2cells)(p) == 1);
6191 if (half_width)
6192 char_width /= 2;
6193 if (prt_half_width)
6194 {
6195 if (!half_width)
6196 {
6197 prt_half_width = FALSE;
6198 prt_pos_x += prt_char_width/4;
6199 prt_need_moveto = TRUE;
6200 prt_attribute_change = TRUE;
6201 }
6202 }
6203 else if (half_width)
6204 {
6205 prt_half_width = TRUE;
6206 prt_pos_x += prt_char_width/4;
6207 prt_need_moveto = TRUE;
6208 prt_attribute_change = TRUE;
6209 }
6210 }
6211#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212
6213 /* Output any required changes to the graphics state, after flushing any
6214 * text buffered so far.
6215 */
6216 if (prt_attribute_change)
6217 {
6218 prt_flush_buffer();
6219 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006220 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006221
6222 if (prt_need_moveto)
6223 {
6224 prt_pos_x_moveto = prt_pos_x;
6225 prt_pos_y_moveto = prt_pos_y;
6226 prt_do_moveto = TRUE;
6227
6228 prt_need_moveto = FALSE;
6229 }
6230 if (prt_need_font)
6231 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006232#ifdef FEAT_MBYTE
6233 if (!prt_in_ascii)
6234 prt_write_string("CF");
6235 else
6236#endif
6237 prt_write_string("F");
6238 prt_write_int(prt_font);
6239 prt_write_string("sf\n");
6240 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006241 }
6242 if (prt_need_fgcol)
6243 {
6244 int r, g, b;
6245 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6246 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6247 b = prt_fgcol & 0xff;
6248
6249 prt_write_real(r / 255.0, 3);
6250 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006251 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 else
6253 {
6254 prt_write_real(g / 255.0, 3);
6255 prt_write_real(b / 255.0, 3);
6256 prt_write_string("r\n");
6257 }
6258 prt_need_fgcol = FALSE;
6259 }
6260
6261 if (prt_bgcol != PRCOLOR_WHITE)
6262 {
6263 prt_new_bgcol = prt_bgcol;
6264 if (prt_need_bgcol)
6265 prt_do_bgcol = TRUE;
6266 }
6267 else
6268 prt_do_bgcol = FALSE;
6269 prt_need_bgcol = FALSE;
6270
6271 if (prt_need_underline)
6272 prt_do_underline = prt_underline;
6273 prt_need_underline = FALSE;
6274
6275 prt_attribute_change = FALSE;
6276 }
6277
6278#ifdef FEAT_MBYTE
6279 if (prt_do_conv)
6280 {
6281 /* Convert from multi-byte to 8-bit encoding */
6282 p = string_convert(&prt_conv, p, &len);
6283 if (p == NULL)
6284 p = (char_u *)"";
6285 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286
Bram Moolenaar8299df92004-07-10 09:47:34 +00006287 if (prt_out_mbyte)
6288 {
6289 /* Multi-byte character strings are represented more efficiently as hex
6290 * strings when outputting clean 8 bit PS.
6291 */
6292 do
6293 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006294 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006295 ga_append(&prt_ps_buffer, ch);
6296 ch = prt_hexchar[(*p) & 0xf];
6297 ga_append(&prt_ps_buffer, ch);
6298 p++;
6299 }
6300 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 }
6302 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006303#endif
6304 {
6305 /* Add next character to buffer of characters to output.
6306 * Note: One printed character may require several PS characters to
6307 * represent it, but we only count them as one printed character.
6308 */
6309 ch = *p;
6310 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6311 {
6312 /* Convert non-printing characters to either their escape or octal
6313 * sequence, ensures PS sent over a serial line does not interfere
6314 * with the comms protocol. Note: For EBCDIC we need to write out
6315 * the escape sequences as ASCII codes!
6316 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6317 */
6318 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6319 switch (ch)
6320 {
6321 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6322 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6323 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6324 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6325 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6326 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6327 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6328 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6329
6330 default:
6331 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6332#ifdef EBCDIC
6333 ebcdic2ascii(ch_buff, 3);
6334#endif
6335 ga_append(&prt_ps_buffer, ch_buff[0]);
6336 ga_append(&prt_ps_buffer, ch_buff[1]);
6337 ga_append(&prt_ps_buffer, ch_buff[2]);
6338 break;
6339 }
6340 }
6341 else
6342 ga_append(&prt_ps_buffer, ch);
6343 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344
6345#ifdef FEAT_MBYTE
6346 /* Need to free any translated characters */
6347 if (prt_do_conv && (*p != NUL))
6348 vim_free(p);
6349#endif
6350
Bram Moolenaar8299df92004-07-10 09:47:34 +00006351 prt_text_run += char_width;
6352 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353
Bram Moolenaar8299df92004-07-10 09:47:34 +00006354 /* The downside of fp - use relative error on right margin check */
6355 next_pos = prt_pos_x + prt_char_width;
6356 need_break = (next_pos > prt_right_margin) &&
6357 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006358
6359 if (need_break)
6360 prt_flush_buffer();
6361
6362 return need_break;
6363}
6364
6365 void
6366mch_print_set_font(iBold, iItalic, iUnderline)
6367 int iBold;
6368 int iItalic;
6369 int iUnderline;
6370{
6371 int font = 0;
6372
6373 if (iBold)
6374 font |= 0x01;
6375 if (iItalic)
6376 font |= 0x02;
6377
6378 if (font != prt_font)
6379 {
6380 prt_font = font;
6381 prt_attribute_change = TRUE;
6382 prt_need_font = TRUE;
6383 }
6384 if (prt_underline != iUnderline)
6385 {
6386 prt_underline = iUnderline;
6387 prt_attribute_change = TRUE;
6388 prt_need_underline = TRUE;
6389 }
6390}
6391
6392 void
6393mch_print_set_bg(bgcol)
6394 long_u bgcol;
6395{
6396 prt_bgcol = bgcol;
6397 prt_attribute_change = TRUE;
6398 prt_need_bgcol = TRUE;
6399}
6400
6401 void
6402mch_print_set_fg(fgcol)
6403 long_u fgcol;
6404{
6405 if (fgcol != (long_u)prt_fgcol)
6406 {
6407 prt_fgcol = fgcol;
6408 prt_attribute_change = TRUE;
6409 prt_need_fgcol = TRUE;
6410 }
6411}
6412
6413# endif /*FEAT_POSTSCRIPT*/
6414#endif /*FEAT_PRINTER*/
6415
6416#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6417 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
6418static char *get_locale_val __ARGS((int what));
6419
6420 static char *
6421get_locale_val(what)
6422 int what;
6423{
6424 char *loc;
6425
6426 /* Obtain the locale value from the libraries. For DJGPP this is
6427 * redefined and it doesn't use the arguments. */
6428 loc = setlocale(what, NULL);
6429
6430# if defined(__BORLANDC__)
6431 if (loc != NULL)
6432 {
6433 char_u *p;
6434
6435 /* Borland returns something like "LC_CTYPE=<name>\n"
6436 * Let's try to fix that bug here... */
6437 p = vim_strchr(loc, '=');
6438 if (p != NULL)
6439 {
6440 loc = ++p;
6441 while (*p != NUL) /* remove trailing newline */
6442 {
6443 if (*p < ' ')
6444 {
6445 *p = NUL;
6446 break;
6447 }
6448 ++p;
6449 }
6450 }
6451 }
6452# endif
6453
6454 return loc;
6455}
6456#endif
6457
6458
6459#ifdef WIN32
6460/*
6461 * On MS-Windows locale names are strings like "German_Germany.1252", but
6462 * gettext expects "de". Try to translate one into another here for a few
6463 * supported languages.
6464 */
6465 static char_u *
6466gettext_lang(char_u *name)
6467{
6468 int i;
6469 static char *(mtable[]) = {
6470 "afrikaans", "af",
6471 "czech", "cs",
6472 "dutch", "nl",
6473 "german", "de",
6474 "english_united kingdom", "en_GB",
6475 "spanish", "es",
6476 "french", "fr",
6477 "italian", "it",
6478 "japanese", "ja",
6479 "korean", "ko",
6480 "norwegian", "no",
6481 "polish", "pl",
6482 "russian", "ru",
6483 "slovak", "sk",
6484 "swedish", "sv",
6485 "ukrainian", "uk",
6486 "chinese_china", "zh_CN",
6487 "chinese_taiwan", "zh_TW",
6488 NULL};
6489
6490 for (i = 0; mtable[i] != NULL; i += 2)
6491 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
6492 return mtable[i + 1];
6493 return name;
6494}
6495#endif
6496
6497#if defined(FEAT_MULTI_LANG) || defined(PROTO)
6498/*
6499 * Obtain the current messages language. Used to set the default for
6500 * 'helplang'. May return NULL or an empty string.
6501 */
6502 char_u *
6503get_mess_lang()
6504{
6505 char_u *p;
6506
6507# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
6508# if defined(LC_MESSAGES)
6509 p = (char_u *)get_locale_val(LC_MESSAGES);
6510# else
6511 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
6512 * may be set to the LCID number. */
6513 p = (char_u *)get_locale_val(LC_ALL);
6514# endif
6515# else
6516 p = mch_getenv((char_u *)"LC_ALL");
6517 if (p == NULL || *p == NUL)
6518 {
6519 p = mch_getenv((char_u *)"LC_MESSAGES");
6520 if (p == NULL || *p == NUL)
6521 p = mch_getenv((char_u *)"LANG");
6522 }
6523# endif
6524# ifdef WIN32
6525 p = gettext_lang(p);
6526# endif
6527 return p;
6528}
6529#endif
6530
Bram Moolenaardef9e822004-12-31 20:58:58 +00006531/* Complicated #if; matches with where get_mess_env() is used below. */
6532#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6533 && defined(LC_MESSAGES))) \
6534 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6535 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
6536 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006537static char_u *get_mess_env __ARGS((void));
6538
6539/*
6540 * Get the language used for messages from the environment.
6541 */
6542 static char_u *
6543get_mess_env()
6544{
6545 char_u *p;
6546
6547 p = mch_getenv((char_u *)"LC_ALL");
6548 if (p == NULL || *p == NUL)
6549 {
6550 p = mch_getenv((char_u *)"LC_MESSAGES");
6551 if (p == NULL || *p == NUL)
6552 {
6553 p = mch_getenv((char_u *)"LANG");
6554 if (p != NULL && VIM_ISDIGIT(*p))
6555 p = NULL; /* ignore something like "1043" */
6556# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6557 if (p == NULL || *p == NUL)
6558 p = (char_u *)get_locale_val(LC_CTYPE);
6559# endif
6560 }
6561 }
6562 return p;
6563}
6564#endif
6565
6566#if defined(FEAT_EVAL) || defined(PROTO)
6567
6568/*
6569 * Set the "v:lang" variable according to the current locale setting.
6570 * Also do "v:lc_time"and "v:ctype".
6571 */
6572 void
6573set_lang_var()
6574{
6575 char_u *loc;
6576
6577# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6578 loc = (char_u *)get_locale_val(LC_CTYPE);
6579# else
6580 /* setlocale() not supported: use the default value */
6581 loc = (char_u *)"C";
6582# endif
6583 set_vim_var_string(VV_CTYPE, loc, -1);
6584
6585 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
6586 * back to LC_CTYPE if it's empty. */
6587# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
6588 loc = (char_u *)get_locale_val(LC_MESSAGES);
6589# else
6590 loc = get_mess_env();
6591# endif
6592 set_vim_var_string(VV_LANG, loc, -1);
6593
6594# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6595 loc = (char_u *)get_locale_val(LC_TIME);
6596# endif
6597 set_vim_var_string(VV_LC_TIME, loc, -1);
6598}
6599#endif
6600
6601#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6602 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
6603/*
6604 * ":language": Set the language (locale).
6605 */
6606 void
6607ex_language(eap)
6608 exarg_T *eap;
6609{
6610 char *loc;
6611 char_u *p;
6612 char_u *name;
6613 int what = LC_ALL;
6614 char *whatstr = "";
6615#ifdef LC_MESSAGES
6616# define VIM_LC_MESSAGES LC_MESSAGES
6617#else
6618# define VIM_LC_MESSAGES 6789
6619#endif
6620
6621 name = eap->arg;
6622
6623 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
6624 * Allow abbreviation, but require at least 3 characters to avoid
6625 * confusion with a two letter language name "me" or "ct". */
6626 p = skiptowhite(eap->arg);
6627 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
6628 {
6629 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
6630 {
6631 what = VIM_LC_MESSAGES;
6632 name = skipwhite(p);
6633 whatstr = "messages ";
6634 }
6635 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
6636 {
6637 what = LC_CTYPE;
6638 name = skipwhite(p);
6639 whatstr = "ctype ";
6640 }
6641 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
6642 {
6643 what = LC_TIME;
6644 name = skipwhite(p);
6645 whatstr = "time ";
6646 }
6647 }
6648
6649 if (*name == NUL)
6650 {
6651#ifndef LC_MESSAGES
6652 if (what == VIM_LC_MESSAGES)
6653 p = get_mess_env();
6654 else
6655#endif
6656 p = (char_u *)setlocale(what, NULL);
6657 if (p == NULL || *p == NUL)
6658 p = (char_u *)"Unknown";
6659 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
6660 }
6661 else
6662 {
6663#ifndef LC_MESSAGES
6664 if (what == VIM_LC_MESSAGES)
6665 loc = "";
6666 else
6667#endif
6668 loc = setlocale(what, (char *)name);
6669 if (loc == NULL)
6670 EMSG2(_("E197: Cannot set language to \"%s\""), name);
6671 else
6672 {
6673#ifdef HAVE_NL_MSG_CAT_CNTR
6674 /* Need to do this for GNU gettext, otherwise cached translations
6675 * will be used again. */
6676 extern int _nl_msg_cat_cntr;
6677
6678 ++_nl_msg_cat_cntr;
6679#endif
6680 /* Reset $LC_ALL, otherwise it would overrule everyting. */
6681 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
6682
6683 if (what != LC_TIME)
6684 {
6685 /* Tell gettext() what to translate to. It apparently doesn't
6686 * use the currently effective locale. Also do this when
6687 * FEAT_GETTEXT isn't defined, so that shell commands use this
6688 * value. */
6689 if (what == LC_ALL)
6690 vim_setenv((char_u *)"LANG", name);
6691 if (what != LC_CTYPE)
6692 {
6693 char_u *mname;
6694#ifdef WIN32
6695 mname = gettext_lang(name);
6696#else
6697 mname = name;
6698#endif
6699 vim_setenv((char_u *)"LC_MESSAGES", mname);
6700#ifdef FEAT_MULTI_LANG
6701 set_helplang_default(mname);
6702#endif
6703 }
6704
6705 /* Set $LC_CTYPE, because it overrules $LANG, and
6706 * gtk_set_locale() calls setlocale() again. gnome_init()
6707 * sets $LC_CTYPE to "en_US" (that's a bug!). */
6708 if (what != VIM_LC_MESSAGES)
6709 vim_setenv((char_u *)"LC_CTYPE", name);
6710# ifdef FEAT_GUI_GTK
6711 /* Let GTK know what locale we're using. Not sure this is
6712 * really needed... */
6713 if (gui.in_use)
6714 (void)gtk_set_locale();
6715# endif
6716 }
6717
6718# ifdef FEAT_EVAL
6719 /* Set v:lang, v:lc_time and v:ctype to the final result. */
6720 set_lang_var();
6721# endif
6722 }
6723 }
6724}
6725
6726# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6727/*
6728 * Function given to ExpandGeneric() to obtain the possible arguments of the
6729 * ":language" command.
6730 */
6731/*ARGSUSED*/
6732 char_u *
6733get_lang_arg(xp, idx)
6734 expand_T *xp;
6735 int idx;
6736{
6737 if (idx == 0)
6738 return (char_u *)"messages";
6739 if (idx == 1)
6740 return (char_u *)"ctype";
6741 if (idx == 2)
6742 return (char_u *)"time";
6743 return NULL;
6744}
6745# endif
6746
6747#endif