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