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