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