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