blob: d4cf86b82af6ecc44eec8f9e92308da873a73f4c [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 Moolenaar8299df92004-07-10 09:47:34 +00003809/* Generic font metrics for multi-byte fonts */
3810static struct prt_ps_font_S prt_ps_mb_font =
3811{
3812 1000,
3813 -100, 50,
3814 -250, 805,
3815 {NULL, NULL, NULL, NULL}
3816};
3817
3818/* Pointer to current font set being used */
3819static struct prt_ps_font_S* prt_ps_font;
3820
3821/* Structures to map user named encoding and mapping to PS equivalents for
3822 * building CID font name */
3823struct prt_ps_encoding_S
3824{
3825 char_u *encoding;
3826 char_u *cmap_encoding;
3827 int needs_charset;
3828};
3829
3830struct prt_ps_charset_S
3831{
3832 char *charset;
3833 char *cmap_charset;
3834 int has_charset;
3835};
3836
3837#define CS_JIS_C_1978 (0x01)
3838#define CS_JIS_X_1983 (0x02)
3839#define CS_JIS_X_1990 (0x04)
3840#define CS_NEC (0x08)
3841#define CS_MSWINDOWS (0x10)
3842#define CS_CP932 (0x20)
3843#define CS_KANJITALK6 (0x40)
3844#define CS_KANJITALK7 (0x80)
3845
3846/* Japanese encodings and charsets */
3847static struct prt_ps_encoding_S j_encodings[] =
3848{
3849 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
3850 CS_NEC)},
3851 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
3852 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
3853 CS_KANJITALK6|CS_KANJITALK7)},
3854 {"cp932", "RKSJ", CS_JIS_X_1983},
3855 {"ucs-2", "UCS2", CS_JIS_X_1990},
3856 {"utf-8", "UTF8" , CS_JIS_X_1990}
3857};
3858static struct prt_ps_charset_S j_charsets[] =
3859{
3860 {"JIS_C_1978", "78", CS_JIS_C_1978},
3861 {"JIS_X_1983", NULL, CS_JIS_X_1983},
3862 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
3863 {"NEC", "Ext", CS_NEC},
3864 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
3865 {"CP932", "90ms", CS_JIS_X_1983},
3866 {"KANJITALK6", "83pv", CS_KANJITALK6},
3867 {"KANJITALK7", "90pv", CS_KANJITALK7}
3868};
3869
3870#define CS_GB_2312_80 (0x01)
3871#define CS_GBT_12345_90 (0x02)
3872#define CS_GBK2K (0x04)
3873#define CS_SC_MAC (0x08)
3874#define CS_GBT_90_MAC (0x10)
3875#define CS_GBK (0x20)
3876#define CS_SC_ISO10646 (0x40)
3877
3878/* Simplified Chinese encodings and charsets */
3879static struct prt_ps_encoding_S sc_encodings[] =
3880{
3881 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
3882 {"gb18030", NULL, CS_GBK2K},
3883 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
3884 CS_GBT_90_MAC)},
3885 {"gbk", "EUC", CS_GBK},
3886 {"ucs-2", "UCS2", CS_SC_ISO10646},
3887 {"utf-8", "UTF8", CS_SC_ISO10646}
3888};
3889static struct prt_ps_charset_S sc_charsets[] =
3890{
3891 {"GB_2312-80", "GB", CS_GB_2312_80},
3892 {"GBT_12345-90","GBT", CS_GBT_12345_90},
3893 {"MAC", "GBpc", CS_SC_MAC},
3894 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
3895 {"GBK", "GBK", CS_GBK},
3896 {"GB18030", "GBK2K", CS_GBK2K},
3897 {"ISO10646", "UniGB", CS_SC_ISO10646}
3898};
3899
3900#define CS_CNS_PLANE_1 (0x01)
3901#define CS_CNS_PLANE_2 (0x02)
3902#define CS_CNS_PLANE_1_2 (0x04)
3903#define CS_B5 (0x08)
3904#define CS_ETEN (0x10)
3905#define CS_HK_GCCS (0x20)
3906#define CS_HK_SCS (0x40)
3907#define CS_HK_SCS_ETEN (0x80)
3908#define CS_MTHKL (0x100)
3909#define CS_MTHKS (0x200)
3910#define CS_DLHKL (0x400)
3911#define CS_DLHKS (0x800)
3912#define CS_TC_ISO10646 (0x1000)
3913
3914/* Traditional Chinese encodings and charsets */
3915static struct prt_ps_encoding_S tc_encodings[] =
3916{
3917 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
3918 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
3919 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
3920 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
3921 CS_DLHKS)},
3922 {"cp950", "B5", CS_B5},
3923 {"ucs-2", "UCS2", CS_TC_ISO10646},
3924 {"utf-8", "UTF8", CS_TC_ISO10646},
3925 {"utf-16", "UTF16", CS_TC_ISO10646},
3926 {"utf-32", "UTF32", CS_TC_ISO10646}
3927};
3928static struct prt_ps_charset_S tc_charsets[] =
3929{
3930 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
3931 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
3932 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
3933 {"BIG5", NULL, CS_B5},
3934 {"CP950", NULL, CS_B5},
3935 {"ETEN", "ETen", CS_ETEN},
3936 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
3937 {"SCS", "HKscs", CS_HK_SCS},
3938 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
3939 {"MTHKL", "HKm471", CS_MTHKL},
3940 {"MTHKS", "HKm314", CS_MTHKS},
3941 {"DLHKL", "HKdla", CS_DLHKL},
3942 {"DLHKS", "HKdlb", CS_DLHKS},
3943 {"ISO10646", "UniCNS", CS_TC_ISO10646}
3944};
3945
3946#define CS_KR_X_1992 (0x01)
3947#define CS_KR_MAC (0x02)
3948#define CS_KR_X_1992_MS (0x04)
3949#define CS_KR_ISO10646 (0x08)
3950
3951/* Korean encodings and charsets */
3952static struct prt_ps_encoding_S k_encodings[] =
3953{
3954 {"iso-2022-kr", NULL, CS_KR_X_1992},
3955 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
3956 {"johab", "Johab", CS_KR_X_1992},
3957 {"cp1361", "Johab", CS_KR_X_1992},
3958 {"uhc", "UHC", CS_KR_X_1992_MS},
3959 {"cp949", "UHC", CS_KR_X_1992_MS},
3960 {"ucs-2", "UCS2", CS_KR_ISO10646},
3961 {"utf-8", "UTF8", CS_KR_ISO10646}
3962};
3963static struct prt_ps_charset_S k_charsets[] =
3964{
3965 {"KS_X_1992", "KSC", CS_KR_X_1992},
3966 {"CP1361", "KSC", CS_KR_X_1992},
3967 {"MAC", "KSCpc", CS_KR_MAC},
3968 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
3969 {"CP949", "KSCms", CS_KR_X_1992_MS},
3970 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
3971 {"ISO10646", "UniKS", CS_KR_ISO10646}
3972};
3973
3974/* Collections of encodings and charsets for multi-byte printing */
3975struct prt_ps_mbfont_S
3976{
3977 int num_encodings;
3978 struct prt_ps_encoding_S *encodings;
3979 int num_charsets;
3980 struct prt_ps_charset_S *charsets;
3981 char *ascii_enc;
3982 char *defcs;
3983};
3984
3985static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
3986{
3987 {
3988 NUM_ELEMENTS(j_encodings),
3989 j_encodings,
3990 NUM_ELEMENTS(j_charsets),
3991 j_charsets,
3992 "jis_roman",
3993 "JIS_X_1983"
3994 },
3995 {
3996 NUM_ELEMENTS(sc_encodings),
3997 sc_encodings,
3998 NUM_ELEMENTS(sc_charsets),
3999 sc_charsets,
4000 "gb_roman",
4001 "GB_2312-80"
4002 },
4003 {
4004 NUM_ELEMENTS(tc_encodings),
4005 tc_encodings,
4006 NUM_ELEMENTS(tc_charsets),
4007 tc_charsets,
4008 "cns_roman",
4009 "BIG5"
4010 },
4011 {
4012 NUM_ELEMENTS(k_encodings),
4013 k_encodings,
4014 NUM_ELEMENTS(k_charsets),
4015 k_charsets,
4016 "ks_roman",
4017 "KS_X_1992"
4018 }
4019};
4020
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021struct prt_ps_resource_S
4022{
4023 char_u name[64];
4024 char_u filename[MAXPATHL + 1];
4025 int type;
4026 char_u title[256];
4027 char_u version[256];
4028};
4029
4030/* Types of PS resource file currently used */
4031#define PRT_RESOURCE_TYPE_PROCSET (0)
4032#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004033#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034
4035/* The PS prolog file version number has to match - if the prolog file is
4036 * updated, increment the number in the file and here. Version checking was
4037 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004038 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 * Table of VIM and prolog versions:
4040 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004041 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004043 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004045#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004046#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004047
4048/* String versions of PS resource types - indexed by constants above so don't
4049 * re-order!
4050 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004051static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052{
4053 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004054 "encoding",
4055 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056};
4057
4058/* Strings to look for in a PS resource file */
4059#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4060#define PRT_RESOURCE_RESOURCE "Resource-"
4061#define PRT_RESOURCE_PROCSET "ProcSet"
4062#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004063#define PRT_RESOURCE_CMAP "CMap"
4064
4065
4066/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4067 * read more comments. */
4068#define PRT_DSC_MISC_TYPE (-1)
4069#define PRT_DSC_TITLE_TYPE (1)
4070#define PRT_DSC_VERSION_TYPE (2)
4071#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4072
4073#define PRT_DSC_TITLE "%%Title:"
4074#define PRT_DSC_VERSION "%%Version:"
4075#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4076
4077struct prt_dsc_comment_S
4078{
4079 char_u *string;
4080 int len;
4081 int type;
4082};
4083
4084struct prt_dsc_line_S
4085{
4086 int type;
4087 char_u *string;
4088 int len;
4089};
4090
4091
4092#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4093struct prt_dsc_comment_S prt_dsc_table[] =
4094{
4095 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
4096 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
4097 PRT_DSC_VERSION_TYPE},
4098 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
4099 PRT_DSC_ENDCOMMENTS_TYPE}
4100};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101
4102static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4103static void prt_write_file __ARGS((char_u *buffer));
4104static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4105static void prt_write_string __ARGS((char *s));
4106static void prt_write_int __ARGS((int i));
4107static void prt_write_boolean __ARGS((int b));
4108static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4109static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4110static void prt_write_real __ARGS((double val, int prec));
4111static void prt_def_var __ARGS((char *name, double value, int prec));
4112static void prt_flush_buffer __ARGS((void));
4113static void prt_resource_name __ARGS((char_u *filename));
4114static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4115static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4116static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4117static void prt_dsc_start __ARGS((void));
4118static void prt_dsc_noarg __ARGS((char *comment));
4119static void prt_dsc_textline __ARGS((char *comment, char *text));
4120static void prt_dsc_text __ARGS((char *comment, char *text));
4121static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4122static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4123static 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 +00004124static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4125static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126static float to_device_units __ARGS((int idx, double physsize, int def_number));
4127static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4128static void prt_font_metrics __ARGS((int font_scale));
4129static int prt_get_cpl __ARGS((void));
4130static int prt_get_lpp __ARGS((void));
4131static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004132static int prt_resfile_next_line __ARGS((void));
4133static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4134static int prt_resfile_skip_nonws __ARGS((int offset));
4135static int prt_resfile_skip_ws __ARGS((int offset));
4136static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4137#ifdef FEAT_MBYTE
4138static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4139static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4140static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4141#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142
4143/*
4144 * Variables for the output PostScript file.
4145 */
4146static FILE *prt_ps_fd;
4147static int prt_file_error;
4148static char_u *prt_ps_file_name = NULL;
4149
4150/*
4151 * Various offsets and dimensions in default PostScript user space (points).
4152 * Used for text positioning calculations
4153 */
4154static float prt_page_width;
4155static float prt_page_height;
4156static float prt_left_margin;
4157static float prt_right_margin;
4158static float prt_top_margin;
4159static float prt_bottom_margin;
4160static float prt_line_height;
4161static float prt_first_line_height;
4162static float prt_char_width;
4163static float prt_number_width;
4164static float prt_bgcol_offset;
4165static float prt_pos_x_moveto = 0.0;
4166static float prt_pos_y_moveto = 0.0;
4167
4168/*
4169 * Various control variables used to decide when and how to change the
4170 * PostScript graphics state.
4171 */
4172static int prt_need_moveto;
4173static int prt_do_moveto;
4174static int prt_need_font;
4175static int prt_font;
4176static int prt_need_underline;
4177static int prt_underline;
4178static int prt_do_underline;
4179static int prt_need_fgcol;
4180static int prt_fgcol;
4181static int prt_need_bgcol;
4182static int prt_do_bgcol;
4183static int prt_bgcol;
4184static int prt_new_bgcol;
4185static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004186static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004188static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189
4190/*
4191 * Variables controlling physical printing.
4192 */
4193static int prt_media;
4194static int prt_portrait;
4195static int prt_num_copies;
4196static int prt_duplex;
4197static int prt_tumble;
4198static int prt_collate;
4199
4200/*
4201 * Buffers used when generating PostScript output
4202 */
4203static char_u prt_line_buffer[257];
4204static garray_T prt_ps_buffer;
4205
4206# ifdef FEAT_MBYTE
4207static int prt_do_conv;
4208static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004209
4210static int prt_out_mbyte;
4211static int prt_custom_cmap;
4212static char prt_cmap[80];
4213static int prt_use_courier;
4214static int prt_in_ascii;
4215static int prt_half_width;
4216static char *prt_ascii_encoding;
4217static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218# endif
4219
4220 static void
4221prt_write_file_raw_len(buffer, bytes)
4222 char_u *buffer;
4223 int bytes;
4224{
4225 if (!prt_file_error
4226 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4227 != (size_t)bytes)
4228 {
4229 EMSG(_("E455: Error writing to PostScript output file"));
4230 prt_file_error = TRUE;
4231 }
4232}
4233
4234 static void
4235prt_write_file(buffer)
4236 char_u *buffer;
4237{
4238 prt_write_file_len(buffer, STRLEN(buffer));
4239}
4240
4241 static void
4242prt_write_file_len(buffer, bytes)
4243 char_u *buffer;
4244 int bytes;
4245{
4246#ifdef EBCDIC
4247 ebcdic2ascii(buffer, bytes);
4248#endif
4249 prt_write_file_raw_len(buffer, bytes);
4250}
4251
4252/*
4253 * Write a string.
4254 */
4255 static void
4256prt_write_string(s)
4257 char *s;
4258{
4259 sprintf((char *)prt_line_buffer, "%s", s);
4260 prt_write_file(prt_line_buffer);
4261}
4262
4263/*
4264 * Write an int and a space.
4265 */
4266 static void
4267prt_write_int(i)
4268 int i;
4269{
4270 sprintf((char *)prt_line_buffer, "%d ", i);
4271 prt_write_file(prt_line_buffer);
4272}
4273
4274/*
4275 * Write a boolean and a space.
4276 */
4277 static void
4278prt_write_boolean(b)
4279 int b;
4280{
4281 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4282 prt_write_file(prt_line_buffer);
4283}
4284
4285/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004286 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287 */
4288 static void
4289prt_def_font(new_name, encoding, height, font)
4290 char *new_name;
4291 char *encoding;
4292 int height;
4293 char *font;
4294{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004295 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4296 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004298#ifdef FEAT_MBYTE
4299 if (prt_out_mbyte)
4300 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4301 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4302 else
4303#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4305 new_name, height, new_name);
4306 prt_write_file(prt_line_buffer);
4307}
4308
Bram Moolenaar8299df92004-07-10 09:47:34 +00004309#ifdef FEAT_MBYTE
4310/*
4311 * Write a line to define the CID font.
4312 */
4313 static void
4314prt_def_cidfont(new_name, height, cidfont)
4315 char *new_name;
4316 int height;
4317 char *cidfont;
4318{
4319 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4320 new_name, prt_cmap, cidfont);
4321 prt_write_file(prt_line_buffer);
4322 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4323 new_name);
4324 prt_write_file(prt_line_buffer);
4325}
4326
4327/*
4328 * Write a line to define a duplicate of a CID font
4329 */
4330 static void
4331prt_dup_cidfont(original_name, new_name)
4332 char *original_name;
4333 char *new_name;
4334{
4335 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4336 prt_write_file(prt_line_buffer);
4337}
4338#endif
4339
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340/*
4341 * Convert a real value into an integer and fractional part as integers, with
4342 * the fractional part being in the range [0,10^precision). The fractional part
4343 * is also rounded based on the precision + 1'th fractional digit.
4344 */
4345 static void
4346prt_real_bits(real, precision, pinteger, pfraction)
4347 double real;
4348 int precision;
4349 int *pinteger;
4350 int *pfraction;
4351{
4352 int i;
4353 int integer;
4354 float fraction;
4355
4356 integer = (int)real;
4357 fraction = (float)(real - integer);
4358 if (real < (double)integer)
4359 fraction = -fraction;
4360 for (i = 0; i < precision; i++)
4361 fraction *= 10.0;
4362
4363 *pinteger = integer;
4364 *pfraction = (int)(fraction + 0.5);
4365}
4366
4367/*
4368 * Write a real and a space. Save bytes if real value has no fractional part!
4369 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
4370 * what decimal point character to use, but PS always requires a '.'.
4371 */
4372 static void
4373prt_write_real(val, prec)
4374 double val;
4375 int prec;
4376{
4377 int integer;
4378 int fraction;
4379
4380 prt_real_bits(val, prec, &integer, &fraction);
4381 /* Emit integer part */
4382 sprintf((char *)prt_line_buffer, "%d", integer);
4383 prt_write_file(prt_line_buffer);
4384 /* Only emit fraction if necessary */
4385 if (fraction != 0)
4386 {
4387 /* Remove any trailing zeros */
4388 while ((fraction % 10) == 0)
4389 {
4390 prec--;
4391 fraction /= 10;
4392 }
4393 /* Emit fraction left padded with zeros */
4394 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
4395 prt_write_file(prt_line_buffer);
4396 }
4397 sprintf((char *)prt_line_buffer, " ");
4398 prt_write_file(prt_line_buffer);
4399}
4400
4401/*
4402 * Write a line to define a numeric variable.
4403 */
4404 static void
4405prt_def_var(name, value, prec)
4406 char *name;
4407 double value;
4408 int prec;
4409{
4410 sprintf((char *)prt_line_buffer, "/%s ", name);
4411 prt_write_file(prt_line_buffer);
4412 prt_write_real(value, prec);
4413 sprintf((char *)prt_line_buffer, "d\n");
4414 prt_write_file(prt_line_buffer);
4415}
4416
4417/* Convert size from font space to user space at current font scale */
4418#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
4419
4420 static void
4421prt_flush_buffer()
4422{
4423 if (prt_ps_buffer.ga_len > 0)
4424 {
4425 /* Any background color must be drawn first */
4426 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
4427 {
4428 int r, g, b;
4429
4430 if (prt_do_moveto)
4431 {
4432 prt_write_real(prt_pos_x_moveto, 2);
4433 prt_write_real(prt_pos_y_moveto, 2);
4434 prt_write_string("m\n");
4435 prt_do_moveto = FALSE;
4436 }
4437
4438 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004439 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 prt_write_real(prt_line_height, 2);
4441
4442 /* Lastly add the color of the background */
4443 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
4444 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
4445 b = prt_new_bgcol & 0xff;
4446 prt_write_real(r / 255.0, 3);
4447 prt_write_real(g / 255.0, 3);
4448 prt_write_real(b / 255.0, 3);
4449 prt_write_string("bg\n");
4450 }
4451 /* Draw underlines before the text as it makes it slightly easier to
4452 * find the starting point.
4453 */
4454 if (prt_do_underline)
4455 {
4456 if (prt_do_moveto)
4457 {
4458 prt_write_real(prt_pos_x_moveto, 2);
4459 prt_write_real(prt_pos_y_moveto, 2);
4460 prt_write_string("m\n");
4461 prt_do_moveto = FALSE;
4462 }
4463
Bram Moolenaar8299df92004-07-10 09:47:34 +00004464 /* Underline length of text run */
4465 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 prt_write_string("ul\n");
4467 }
4468 /* Draw the text
4469 * Note: we write text out raw - EBCDIC conversion is handled in the
4470 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004471#ifdef FEAT_MBYTE
4472 if (prt_out_mbyte)
4473 prt_write_string("<");
4474 else
4475#endif
4476 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004478#ifdef FEAT_MBYTE
4479 if (prt_out_mbyte)
4480 prt_write_string(">");
4481 else
4482#endif
4483 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 /* Add a moveto if need be and use the appropriate show procedure */
4485 if (prt_do_moveto)
4486 {
4487 prt_write_real(prt_pos_x_moveto, 2);
4488 prt_write_real(prt_pos_y_moveto, 2);
4489 /* moveto and a show */
4490 prt_write_string("ms\n");
4491 prt_do_moveto = FALSE;
4492 }
4493 else /* Simple show */
4494 prt_write_string("s\n");
4495
4496 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004497 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498 }
4499}
4500
4501static char_u *resource_filename;
4502
4503 static void
4504prt_resource_name(filename)
4505 char_u *filename;
4506{
4507 if (STRLEN(filename) >= MAXPATHL)
4508 *resource_filename = NUL;
4509 else
4510 STRCPY(resource_filename, filename);
4511}
4512
4513 static int
4514prt_find_resource(name, resource)
4515 char *name;
4516 struct prt_ps_resource_S *resource;
4517{
4518 char_u buffer[MAXPATHL + 1];
4519
4520 STRCPY(resource->name, name);
4521 /* Look for named resource file in runtimepath */
4522 STRCPY(buffer, "print");
4523 add_pathsep(buffer);
4524 STRCAT(buffer, name);
4525 STRCAT(buffer, ".ps");
4526 resource_filename = resource->filename;
4527 *resource_filename = NUL;
4528 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
4529 && resource->filename[0] != NUL);
4530}
4531
4532/* PS CR and LF characters have platform independent values */
4533#define PSLF (0x0a)
4534#define PSCR (0x0d)
4535
Bram Moolenaar8299df92004-07-10 09:47:34 +00004536/* Static buffer to read initial comments in a resource file, some can have a
4537 * couple of KB of comments! */
4538#define PRT_FILE_BUFFER_LEN (2048)
4539struct prt_resfile_buffer_S
4540{
4541 char_u buffer[PRT_FILE_BUFFER_LEN];
4542 int len;
4543 int line_start;
4544 int line_end;
4545};
4546
4547static struct prt_resfile_buffer_S prt_resfile;
4548
4549 static int
4550prt_resfile_next_line()
4551{
4552 int index;
4553
4554 /* Move to start of next line and then find end of line */
4555 index = prt_resfile.line_end + 1;
4556 while (index < prt_resfile.len)
4557 {
4558 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
4559 != PSCR)
4560 break;
4561 index++;
4562 }
4563 prt_resfile.line_start = index;
4564
4565 while (index < prt_resfile.len)
4566 {
4567 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
4568 == PSCR)
4569 break;
4570 index++;
4571 }
4572 prt_resfile.line_end = index;
4573
4574 return (index < prt_resfile.len);
4575}
4576
4577 static int
4578prt_resfile_strncmp(offset, string, len)
4579 int offset;
4580 char *string;
4581 int len;
4582{
4583 /* Force not equal if string is longer than remainder of line */
4584 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
4585 return 1;
4586
4587 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
4588 string, len);
4589}
4590
4591 static int
4592prt_resfile_skip_nonws(offset)
4593 int offset;
4594{
4595 int index;
4596
4597 index = prt_resfile.line_start + offset;
4598 while (index < prt_resfile.line_end)
4599 {
4600 if (isspace(prt_resfile.buffer[index]))
4601 return index - prt_resfile.line_start;
4602 index++;
4603 }
4604 return -1;
4605}
4606
4607 static int
4608prt_resfile_skip_ws(offset)
4609 int offset;
4610{
4611 int index;
4612
4613 index = prt_resfile.line_start + offset;
4614 while (index < prt_resfile.line_end)
4615 {
4616 if (!isspace(prt_resfile.buffer[index]))
4617 return index - prt_resfile.line_start;
4618 index++;
4619 }
4620 return -1;
4621}
4622
4623/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
4624 * if a DSC comment is found, else false */
4625 static int
4626prt_next_dsc(p_dsc_line)
4627 struct prt_dsc_line_S *p_dsc_line;
4628{
4629 int comment;
4630 int offset;
4631
4632 /* Move to start of next line */
4633 if (!prt_resfile_next_line())
4634 return FALSE;
4635
4636 /* DSC comments always start %% */
4637 if (prt_resfile_strncmp(0, "%%", 2) != 0)
4638 return FALSE;
4639
4640 /* Find type of DSC comment */
4641 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
4642 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
4643 prt_dsc_table[comment].len) == 0)
4644 break;
4645
4646 if (comment != NUM_ELEMENTS(prt_dsc_table))
4647 {
4648 /* Return type of comment */
4649 p_dsc_line->type = prt_dsc_table[comment].type;
4650 offset = prt_dsc_table[comment].len;
4651 }
4652 else
4653 {
4654 /* Unrecognised DSC comment, skip to ws after comment leader */
4655 p_dsc_line->type = PRT_DSC_MISC_TYPE;
4656 offset = prt_resfile_skip_nonws(0);
4657 if (offset == -1)
4658 return FALSE;
4659 }
4660
4661 /* Skip ws to comment value */
4662 offset = prt_resfile_skip_ws(offset);
4663 if (offset == -1)
4664 return FALSE;
4665
4666 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
4667 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
4668
4669 return TRUE;
4670}
4671
4672/* Improved hand crafted parser to get the type, title, and version number of a
4673 * PS resource file so the file details can be added to the DSC header comments.
4674 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675 static int
4676prt_open_resource(resource)
4677 struct prt_ps_resource_S *resource;
4678{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004679 int offset;
4680 int seen_all;
4681 int seen_title;
4682 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004684 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685
4686 fd_resource = mch_fopen((char *)resource->filename, READBIN);
4687 if (fd_resource == NULL)
4688 {
4689 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
4690 return FALSE;
4691 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004692 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693
4694 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004695 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
4696 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 if (ferror(fd_resource))
4698 {
4699 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
4700 resource->filename);
4701 fclose(fd_resource);
4702 return FALSE;
4703 }
4704
Bram Moolenaar8299df92004-07-10 09:47:34 +00004705 prt_resfile.line_end = -1;
4706 prt_resfile.line_start = 0;
4707 if (!prt_resfile_next_line())
4708 return FALSE;
4709
4710 offset = 0;
4711
4712 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
4713 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714 {
4715 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
4716 resource->filename);
4717 fclose(fd_resource);
4718 return FALSE;
4719 }
4720
4721 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004722 offset += STRLEN(PRT_RESOURCE_HEADER);
4723 offset = prt_resfile_skip_nonws(offset);
4724 if (offset == -1)
4725 return FALSE;
4726 offset = prt_resfile_skip_ws(offset);
4727 if (offset == -1)
4728 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729
Bram Moolenaar8299df92004-07-10 09:47:34 +00004730 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
4731 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 {
4733 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4734 resource->filename);
4735 fclose(fd_resource);
4736 return FALSE;
4737 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00004738 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739
4740 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004741 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
4742 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004744 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
4745 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004747 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
4748 STRLEN(PRT_RESOURCE_CMAP)) == 0)
4749 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750 else
4751 {
4752 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4753 resource->filename);
4754 fclose(fd_resource);
4755 return FALSE;
4756 }
4757
Bram Moolenaar8299df92004-07-10 09:47:34 +00004758 /* Look for title and version of resource */
4759 resource->title[0] = '\0';
4760 resource->version[0] = '\0';
4761 seen_title = FALSE;
4762 seen_version = FALSE;
4763 seen_all = FALSE;
4764 while (!seen_all && prt_next_dsc(&dsc_line))
4765 {
4766 switch (dsc_line.type)
4767 {
4768 case PRT_DSC_TITLE_TYPE:
4769 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
4770 resource->title[dsc_line.len] = '\0';
4771 seen_title = TRUE;
4772 if (seen_version)
4773 seen_all = TRUE;
4774 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775
Bram Moolenaar8299df92004-07-10 09:47:34 +00004776 case PRT_DSC_VERSION_TYPE:
4777 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
4778 resource->version[dsc_line.len] = '\0';
4779 seen_version = TRUE;
4780 if (seen_title)
4781 seen_all = TRUE;
4782 break;
4783
4784 case PRT_DSC_ENDCOMMENTS_TYPE:
4785 /* Wont find title or resource after this comment, stop searching */
4786 seen_all = TRUE;
4787 break;
4788
4789 case PRT_DSC_MISC_TYPE:
4790 /* Not interested in whatever comment this line had */
4791 break;
4792 }
4793 }
4794
4795 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 {
4797 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
4798 resource->filename);
4799 fclose(fd_resource);
4800 return FALSE;
4801 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802
4803 fclose(fd_resource);
4804
4805 return TRUE;
4806}
4807
4808 static int
4809prt_check_resource(resource, version)
4810 struct prt_ps_resource_S *resource;
4811 char_u *version;
4812{
4813 /* Version number m.n should match, the revision number does not matter */
4814 if (STRNCMP(resource->version, version, STRLEN(version)))
4815 {
4816 EMSG2(_("E621: \"%s\" resource file has wrong version"),
4817 resource->name);
4818 return FALSE;
4819 }
4820
4821 /* Other checks to be added as needed */
4822 return TRUE;
4823}
4824
4825 static void
4826prt_dsc_start()
4827{
4828 prt_write_string("%!PS-Adobe-3.0\n");
4829}
4830
4831 static void
4832prt_dsc_noarg(comment)
4833 char *comment;
4834{
4835 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
4836 prt_write_file(prt_line_buffer);
4837}
4838
4839 static void
4840prt_dsc_textline(comment, text)
4841 char *comment;
4842 char *text;
4843{
4844 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
4845 prt_write_file(prt_line_buffer);
4846}
4847
4848 static void
4849prt_dsc_text(comment, text)
4850 char *comment;
4851 char *text;
4852{
4853 /* TODO - should scan 'text' for any chars needing escaping! */
4854 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
4855 prt_write_file(prt_line_buffer);
4856}
4857
4858#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
4859
4860 static void
4861prt_dsc_ints(comment, count, ints)
4862 char *comment;
4863 int count;
4864 int *ints;
4865{
4866 int i;
4867
4868 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
4869 prt_write_file(prt_line_buffer);
4870
4871 for (i = 0; i < count; i++)
4872 {
4873 sprintf((char *)prt_line_buffer, " %d", ints[i]);
4874 prt_write_file(prt_line_buffer);
4875 }
4876
4877 prt_write_string("\n");
4878}
4879
4880 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00004881prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 char *comment; /* if NULL add to previous */
4883 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004884 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 if (comment != NULL)
4887 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
4888 else
4889 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
4890 prt_write_file(prt_line_buffer);
4891
Bram Moolenaar8299df92004-07-10 09:47:34 +00004892 sprintf((char *)prt_line_buffer, " %s\n", string);
4893 prt_write_file(prt_line_buffer);
4894}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895
Bram Moolenaar8299df92004-07-10 09:47:34 +00004896 static void
4897prt_dsc_font_resource(resource, ps_font)
4898 char *resource;
4899 struct prt_ps_font_S *ps_font;
4900{
4901 int i;
4902
4903 prt_dsc_resources(resource, "font",
4904 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
4905 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
4906 if (ps_font->ps_fontname[i] != NULL)
4907 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908}
4909
4910 static void
4911prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
4912 int duplex;
4913 int tumble;
4914 int collate;
4915 int color;
4916 int num_copies;
4917{
4918 /* Only output the comment if we need to.
4919 * Note: tumble is ignored if we are not duplexing
4920 */
4921 if (!(duplex || collate || color || (num_copies > 1)))
4922 return;
4923
4924 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
4925 prt_write_file(prt_line_buffer);
4926
4927 if (duplex)
4928 {
4929 prt_write_string(" duplex");
4930 if (tumble)
4931 prt_write_string("(tumble)");
4932 }
4933 if (collate)
4934 prt_write_string(" collate");
4935 if (color)
4936 prt_write_string(" color");
4937 if (num_copies > 1)
4938 {
4939 prt_write_string(" numcopies(");
4940 /* Note: no space wanted so dont use prt_write_int() */
4941 sprintf((char *)prt_line_buffer, "%d", num_copies);
4942 prt_write_file(prt_line_buffer);
4943 prt_write_string(")");
4944 }
4945 prt_write_string("\n");
4946}
4947
4948 static void
4949prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
4950 char *paper_name;
4951 double width;
4952 double height;
4953 double weight;
4954 char *colour;
4955 char *type;
4956{
4957 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
4958 prt_write_file(prt_line_buffer);
4959 prt_write_real(width, 2);
4960 prt_write_real(height, 2);
4961 prt_write_real(weight, 2);
4962 if (colour == NULL)
4963 prt_write_string("()");
4964 else
4965 prt_write_string(colour);
4966 prt_write_string(" ");
4967 if (type == NULL)
4968 prt_write_string("()");
4969 else
4970 prt_write_string(type);
4971 prt_write_string("\n");
4972}
4973
4974 void
4975mch_print_cleanup()
4976{
4977#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004978 if (prt_out_mbyte)
4979 {
4980 int i;
4981
4982 /* Free off all CID font names created, but first clear duplicate
4983 * pointers to the same string (when the same font is used for more than
4984 * one style).
4985 */
4986 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
4987 {
4988 if (prt_ps_mb_font.ps_fontname[i] != NULL)
4989 vim_free(prt_ps_mb_font.ps_fontname[i]);
4990 prt_ps_mb_font.ps_fontname[i] = NULL;
4991 }
4992 }
4993
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994 if (prt_do_conv)
4995 {
4996 convert_setup(&prt_conv, NULL, NULL);
4997 prt_do_conv = FALSE;
4998 }
4999#endif
5000 if (prt_ps_fd != NULL)
5001 {
5002 fclose(prt_ps_fd);
5003 prt_ps_fd = NULL;
5004 prt_file_error = FALSE;
5005 }
5006 if (prt_ps_file_name != NULL)
5007 {
5008 vim_free(prt_ps_file_name);
5009 prt_ps_file_name = NULL;
5010 }
5011}
5012
5013 static float
5014to_device_units(idx, physsize, def_number)
5015 int idx;
5016 double physsize;
5017 int def_number;
5018{
5019 float ret;
5020 int u;
5021 int nr;
5022
5023 u = prt_get_unit(idx);
5024 if (u == PRT_UNIT_NONE)
5025 {
5026 u = PRT_UNIT_PERC;
5027 nr = def_number;
5028 }
5029 else
5030 nr = printer_opts[idx].number;
5031
5032 switch (u)
5033 {
5034 case PRT_UNIT_INCH:
5035 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5036 break;
5037 case PRT_UNIT_MM:
5038 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5039 break;
5040 case PRT_UNIT_POINT:
5041 ret = (float)nr;
5042 break;
5043 case PRT_UNIT_PERC:
5044 default:
5045 ret = (float)(physsize * nr) / 100;
5046 break;
5047 }
5048
5049 return ret;
5050}
5051
5052/*
5053 * Calculate margins for given width and height from printoptions settings.
5054 */
5055 static void
5056prt_page_margins(width, height, left, right, top, bottom)
5057 double width;
5058 double height;
5059 double *left;
5060 double *right;
5061 double *top;
5062 double *bottom;
5063{
5064 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5065 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5066 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5067 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5068}
5069
5070 static void
5071prt_font_metrics(font_scale)
5072 int font_scale;
5073{
5074 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005075 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005076}
5077
5078
5079 static int
5080prt_get_cpl()
5081{
5082 if (prt_use_number())
5083 {
5084 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005085#ifdef FEAT_MBYTE
5086 /* If we are outputting multi-byte characters then line numbers will be
5087 * printed with half width characters
5088 */
5089 if (prt_out_mbyte)
5090 prt_number_width /= 2;
5091#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092 prt_left_margin += prt_number_width;
5093 }
5094 else
5095 prt_number_width = 0.0;
5096
5097 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5098}
5099
Bram Moolenaar8299df92004-07-10 09:47:34 +00005100 static int
5101prt_build_cid_fontname(font, name, name_len)
5102 int font;
5103 char *name;
5104 int name_len;
5105{
5106 char *fontname;
5107
5108 fontname = alloc(name_len + 1);
5109 if (fontname == NULL)
5110 return FALSE;
5111 STRNCPY(fontname, name, name_len);
5112 fontname[name_len] = '\0';
5113 prt_ps_mb_font.ps_fontname[font] = fontname;
5114
5115 return TRUE;
5116}
5117
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118/*
5119 * Get number of lines of text that fit on a page (excluding the header).
5120 */
5121 static int
5122prt_get_lpp()
5123{
5124 int lpp;
5125
5126 /*
5127 * Calculate offset to lower left corner of background rect based on actual
5128 * font height (based on its bounding box) and the line height, handling the
5129 * case where the font height can exceed the line height.
5130 */
5131 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005132 prt_ps_font->bbox_min_y);
5133 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005134 {
5135 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005136 (1000.0 - (prt_ps_font->bbox_max_y -
5137 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 }
5139
5140 /* Get height for topmost line based on background rect offset. */
5141 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5142
5143 /* Calculate lpp */
5144 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5145
5146 /* Adjust top margin if there is a header */
5147 prt_top_margin -= prt_line_height * prt_header_height();
5148
5149 return lpp - prt_header_height();
5150}
5151
Bram Moolenaar8299df92004-07-10 09:47:34 +00005152#ifdef FEAT_MBYTE
5153 static int
5154prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
5155 char *p_encoding;
5156 struct prt_ps_mbfont_S *p_cmap;
5157 struct prt_ps_encoding_S **pp_mbenc;
5158{
5159 int mbenc;
5160 int enc_len;
5161 struct prt_ps_encoding_S *p_mbenc;
5162
5163 *pp_mbenc = NULL;
5164 /* Look for recognised encoding */
5165 enc_len = STRLEN(p_encoding);
5166 p_mbenc = p_cmap->encodings;
5167 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5168 {
5169 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5170 {
5171 *pp_mbenc = p_mbenc;
5172 return TRUE;
5173 }
5174 p_mbenc++;
5175 }
5176 return FALSE;
5177}
5178
5179 static int
5180prt_match_charset(p_charset, p_cmap, pp_mbchar)
5181 char *p_charset;
5182 struct prt_ps_mbfont_S *p_cmap;
5183 struct prt_ps_charset_S **pp_mbchar;
5184{
5185 int mbchar;
5186 int char_len;
5187 struct prt_ps_charset_S *p_mbchar;
5188
5189 /* Look for recognised character set, using default if one is not given */
5190 if (*p_charset == NUL)
5191 p_charset = p_cmap->defcs;
5192 char_len = STRLEN(p_charset);
5193 p_mbchar = p_cmap->charsets;
5194 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5195 {
5196 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5197 {
5198 *pp_mbchar = p_mbchar;
5199 return TRUE;
5200 }
5201 p_mbchar++;
5202 }
5203 return FALSE;
5204}
5205#endif
5206
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207/*ARGSUSED*/
5208 int
5209mch_print_init(psettings, jobname, forceit)
5210 prt_settings_T *psettings;
5211 char_u *jobname;
5212 int forceit;
5213{
5214 int i;
5215 char *paper_name;
5216 int paper_strlen;
5217 int fontsize;
5218 char_u *p;
5219 double left;
5220 double right;
5221 double top;
5222 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005223#ifdef FEAT_MBYTE
5224 int cmap;
5225 int pmcs_len;
5226 char_u *p_encoding;
5227 struct prt_ps_encoding_S *p_mbenc;
5228 struct prt_ps_encoding_S *p_mbenc_first;
5229 struct prt_ps_charset_S *p_mbchar;
5230#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005231
5232#if 0
5233 /*
5234 * TODO:
5235 * If "forceit" is false: pop up a dialog to select:
5236 * - printer name
5237 * - copies
5238 * - collated/uncollated
5239 * - duplex off/long side/short side
5240 * - paper size
5241 * - portrait/landscape
5242 * - font size
5243 *
5244 * If "forceit" is true: use the default printer and settings
5245 */
5246 if (forceit)
5247 s_pd.Flags |= PD_RETURNDEFAULT;
5248#endif
5249
5250 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005251 * Set up font and encoding.
5252 */
5253#ifdef FEAT_MBYTE
5254 p_encoding = enc_skip(p_penc);
5255 if (*p_encoding == NUL)
5256 p_encoding = enc_skip(p_enc);
5257
5258 /* Look for recognised multi-byte coding, and if the charset is recognised.
5259 * This is to cope with the fact that various unicode encodings are
5260 * supported in more than one of CJK. */
5261 p_mbenc = NULL;
5262 p_mbenc_first = NULL;
5263 p_mbchar = NULL;
5264 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
5265 if (prt_match_encoding(p_encoding, &prt_ps_mbfonts[cmap], &p_mbenc))
5266 {
5267 if (p_mbenc_first == NULL)
5268 p_mbenc_first = p_mbenc;
5269 if (prt_match_charset(p_pmcs, &prt_ps_mbfonts[cmap], &p_mbchar))
5270 break;
5271 }
5272
5273 /* Use first encoding matched if no charset matched */
5274 if (p_mbchar == NULL && p_mbenc_first != NULL)
5275 p_mbenc = p_mbenc_first;
5276
5277 prt_out_mbyte = (p_mbenc != NULL);
5278 if (prt_out_mbyte)
5279 {
5280 /* Build CMap name - will be same for all multi-byte fonts used */
5281 prt_cmap[0] = '\0';
5282
5283 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5284
5285 if (!prt_custom_cmap)
5286 {
5287 /* Check encoding and character set are compatible */
5288 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5289 {
5290 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5291 return FALSE;
5292 }
5293
5294 /* Add charset name if not empty */
5295 if (p_mbchar->cmap_charset != NULL)
5296 {
5297 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5298 STRCAT(prt_cmap, "-");
5299 }
5300 }
5301 else
5302 {
5303 /* Add custom CMap character set name */
5304 pmcs_len = STRLEN(p_pmcs);
5305 if (pmcs_len == 0)
5306 {
5307 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5308 return FALSE;
5309 }
5310 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5311 prt_cmap[pmcs_len] = '\0';
5312 STRCAT(prt_cmap, "-");
5313 }
5314
5315 /* CMap name ends with (optional) encoding name and -H for horizontal */
5316 if (p_mbenc->cmap_encoding != NULL)
5317 {
5318 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5319 STRCAT(prt_cmap, "-");
5320 }
5321 STRCAT(prt_cmap, "H");
5322
5323 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5324 {
5325 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5326 return FALSE;
5327 }
5328
5329 /* Derive CID font names with fallbacks if not defined */
5330 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5331 mbfont_opts[OPT_MBFONT_REGULAR].string,
5332 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5333 return FALSE;
5334 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5335 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5336 mbfont_opts[OPT_MBFONT_BOLD].string,
5337 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5338 return FALSE;
5339 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5340 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5341 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
5342 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
5343 return FALSE;
5344 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
5345 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
5346 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
5347 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
5348 return FALSE;
5349
5350 /* Check if need to use Courier for ASCII code range, and if so pick up
5351 * the encoding to use */
5352 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
5353 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
5354 if (prt_use_courier)
5355 {
5356 /* Use national ASCII variant unless ASCII wanted */
5357 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
5358 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
5359 prt_ascii_encoding = "ascii";
5360 else
5361 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
5362 }
5363
5364 prt_ps_font = &prt_ps_mb_font;
5365 }
5366 else
5367#endif
5368 {
5369#ifdef FEAT_MBYTE
5370 prt_use_courier = FALSE;
5371#endif
5372 prt_ps_font = &prt_ps_courier_font;
5373 }
5374
5375 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005376 * Find the size of the paper and set the margins.
5377 */
5378 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
5379 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
5380 if (printer_opts[OPT_PRINT_PAPER].present)
5381 {
5382 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
5383 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
5384 }
5385 else
5386 {
5387 paper_name = "A4";
5388 paper_strlen = 2;
5389 }
5390 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
5391 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
5392 && STRNICMP(prt_mediasize[i].name, paper_name,
5393 paper_strlen) == 0)
5394 break;
5395 if (i == PRT_MEDIASIZE_LEN)
5396 i = 0;
5397 prt_media = i;
5398
5399 /*
5400 * Set PS pagesize based on media dimensions and print orientation.
5401 * Note: Media and page sizes have defined meanings in PostScript and should
5402 * be kept distinct. Media is the paper (or transparency, or ...) that is
5403 * printed on, whereas the page size is the area that the PostScript
5404 * interpreter renders into.
5405 */
5406 if (prt_portrait)
5407 {
5408 prt_page_width = prt_mediasize[i].width;
5409 prt_page_height = prt_mediasize[i].height;
5410 }
5411 else
5412 {
5413 prt_page_width = prt_mediasize[i].height;
5414 prt_page_height = prt_mediasize[i].width;
5415 }
5416
5417 /*
5418 * Set PS page margins based on the PS pagesize, not the mediasize - this
5419 * needs to be done before the cpl and lpp are calculated.
5420 */
5421 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
5422 &bottom);
5423 prt_left_margin = (float)left;
5424 prt_right_margin = (float)right;
5425 prt_top_margin = (float)top;
5426 prt_bottom_margin = (float)bottom;
5427
5428 /*
5429 * Set up the font size.
5430 */
5431 fontsize = PRT_PS_DEFAULT_FONTSIZE;
5432 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
5433 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
5434 fontsize = atoi((char *)p + 2);
5435 prt_font_metrics(fontsize);
5436
5437 /*
5438 * Return the number of characters per line, and lines per page for the
5439 * generic print code.
5440 */
5441 psettings->chars_per_line = prt_get_cpl();
5442 psettings->lines_per_page = prt_get_lpp();
5443
5444 /* Catch margin settings that leave no space for output! */
5445 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
5446 return FAIL;
5447
5448 /*
5449 * Sort out the number of copies to be printed. PS by default will do
5450 * uncollated copies for you, so once we know how many uncollated copies are
5451 * wanted cache it away and lie to the generic code that we only want one
5452 * uncollated copy.
5453 */
5454 psettings->n_collated_copies = 1;
5455 psettings->n_uncollated_copies = 1;
5456 prt_num_copies = 1;
5457 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
5458 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
5459 if (prt_collate)
5460 {
5461 /* TODO: Get number of collated copies wanted. */
5462 psettings->n_collated_copies = 1;
5463 }
5464 else
5465 {
5466 /* TODO: Get number of uncollated copies wanted and update the cached
5467 * count.
5468 */
5469 prt_num_copies = 1;
5470 }
5471
5472 psettings->jobname = jobname;
5473
5474 /*
5475 * Set up printer duplex and tumble based on Duplex option setting - default
5476 * is long sided duplex printing (i.e. no tumble).
5477 */
5478 prt_duplex = TRUE;
5479 prt_tumble = FALSE;
5480 psettings->duplex = 1;
5481 if (printer_opts[OPT_PRINT_DUPLEX].present)
5482 {
5483 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
5484 {
5485 prt_duplex = FALSE;
5486 psettings->duplex = 0;
5487 }
5488 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
5489 == 0)
5490 prt_tumble = TRUE;
5491 }
5492
5493 /* For now user abort not supported */
5494 psettings->user_abort = 0;
5495
5496 /* If the user didn't specify a file name, use a temp file. */
5497 if (psettings->outfile == NULL)
5498 {
5499 prt_ps_file_name = vim_tempname('p');
5500 if (prt_ps_file_name == NULL)
5501 {
5502 EMSG(_(e_notmp));
5503 return FAIL;
5504 }
5505 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
5506 }
5507 else
5508 {
5509 p = expand_env_save(psettings->outfile);
5510 if (p != NULL)
5511 {
5512 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
5513 vim_free(p);
5514 }
5515 }
5516 if (prt_ps_fd == NULL)
5517 {
5518 EMSG(_("E324: Can't open PostScript output file"));
5519 mch_print_cleanup();
5520 return FAIL;
5521 }
5522
Bram Moolenaar8299df92004-07-10 09:47:34 +00005523 prt_bufsiz = psettings->chars_per_line;
5524#ifdef FEAT_MBYTE
5525 if (prt_out_mbyte)
5526 prt_bufsiz *= 2;
5527#endif
5528 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529
5530 prt_page_num = 0;
5531
5532 prt_attribute_change = FALSE;
5533 prt_need_moveto = FALSE;
5534 prt_need_font = FALSE;
5535 prt_need_fgcol = FALSE;
5536 prt_need_bgcol = FALSE;
5537 prt_need_underline = FALSE;
5538
5539 prt_file_error = FALSE;
5540
5541 return OK;
5542}
5543
5544 static int
5545prt_add_resource(resource)
5546 struct prt_ps_resource_S *resource;
5547{
5548 FILE* fd_resource;
5549 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550 size_t bytes_read;
5551
5552 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5553 if (fd_resource == NULL)
5554 {
5555 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
5556 return FALSE;
5557 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 prt_dsc_resources("BeginResource",
Bram Moolenaar8299df92004-07-10 09:47:34 +00005559 prt_resource_types[resource->type], resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560
5561 prt_dsc_textline("BeginDocument", (char *)resource->filename);
5562
5563 for (;;)
5564 {
5565 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
5566 sizeof(resource_buffer), fd_resource);
5567 if (ferror(fd_resource))
5568 {
5569 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5570 resource->filename);
5571 fclose(fd_resource);
5572 return FALSE;
5573 }
5574 if (bytes_read == 0)
5575 break;
5576 prt_write_file_raw_len(resource_buffer, bytes_read);
5577 if (prt_file_error)
5578 {
5579 fclose(fd_resource);
5580 return FALSE;
5581 }
5582 }
5583 fclose(fd_resource);
5584
5585 prt_dsc_noarg("EndDocument");
5586
5587 prt_dsc_noarg("EndResource");
5588
5589 return TRUE;
5590}
5591
5592 int
5593mch_print_begin(psettings)
5594 prt_settings_T *psettings;
5595{
5596 time_t now;
5597 int bbox[4];
5598 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 double left;
5600 double right;
5601 double top;
5602 double bottom;
5603 struct prt_ps_resource_S res_prolog;
5604 struct prt_ps_resource_S res_encoding;
5605 char_u buffer[256];
5606 char_u *p_encoding;
5607#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005608 struct prt_ps_resource_S res_cidfont;
5609 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610#endif
5611
5612 /*
5613 * PS DSC Header comments - no PS code!
5614 */
5615 prt_dsc_start();
5616 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005617 if (!get_user_name(buffer, 256))
5618 STRCPY(buffer, "Unknown");
5619 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 prt_dsc_textline("Creator", VIM_VERSION_LONG);
5621 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
5622 now = time(NULL);
5623 p_time = ctime(&now);
5624 /* Note: ctime() adds a \n so we have to remove it :-( */
5625 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
5626 prt_dsc_textline("CreationDate", p_time);
5627 prt_dsc_textline("DocumentData", "Clean8Bit");
5628 prt_dsc_textline("Orientation", "Portrait");
5629 prt_dsc_atend("Pages");
5630 prt_dsc_textline("PageOrder", "Ascend");
5631 /* The bbox does not change with orientation - it is always in the default
5632 * user coordinate system! We have to recalculate right and bottom
5633 * coordinates based on the font metrics for the bbox to be accurate. */
5634 prt_page_margins(prt_mediasize[prt_media].width,
5635 prt_mediasize[prt_media].height,
5636 &left, &right, &top, &bottom);
5637 bbox[0] = (int)left;
5638 if (prt_portrait)
5639 {
5640 /* In portrait printing the fixed point is the top left corner so we
5641 * derive the bbox from that point. We have the expected cpl chars
5642 * across the media and lpp lines down the media.
5643 */
5644 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
5645 * prt_line_height);
5646 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
5647 + 0.5);
5648 bbox[3] = (int)(top + 0.5);
5649 }
5650 else
5651 {
5652 /* In landscape printing the fixed point is the bottom left corner so we
5653 * derive the bbox from that point. We have lpp chars across the media
5654 * and cpl lines up the media.
5655 */
5656 bbox[1] = (int)bottom;
5657 bbox[2] = (int)(left + ((psettings->lines_per_page
5658 + prt_header_height()) * prt_line_height) + 0.5);
5659 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
5660 + 0.5);
5661 }
5662 prt_dsc_ints("BoundingBox", 4, bbox);
5663 /* The media width and height does not change with landscape printing! */
5664 prt_dsc_docmedia(prt_mediasize[prt_media].name,
5665 prt_mediasize[prt_media].width,
5666 prt_mediasize[prt_media].height,
5667 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005668 /* Define fonts needed */
5669#ifdef FEAT_MBYTE
5670 if (!prt_out_mbyte || prt_use_courier)
5671#endif
5672 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
5673#ifdef FEAT_MBYTE
5674 if (prt_out_mbyte)
5675 {
5676 prt_dsc_font_resource((prt_use_courier ? NULL
5677 : "DocumentNeededResources"), &prt_ps_mb_font);
5678 if (!prt_custom_cmap)
5679 prt_dsc_resources(NULL, "cmap", prt_cmap);
5680 }
5681#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682
Bram Moolenaar8299df92004-07-10 09:47:34 +00005683 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684 if (!prt_find_resource("prolog", &res_prolog))
5685 {
5686 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
5687 return FALSE;
5688 }
5689 if (!prt_open_resource(&res_prolog))
5690 return FALSE;
5691 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
5692 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005693#ifdef FEAT_MBYTE
5694 if (prt_out_mbyte)
5695 {
5696 /* Look for required version of multi-byte printing procset */
5697 if (!prt_find_resource("cidfont", &res_cidfont))
5698 {
5699 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
5700 return FALSE;
5701 }
5702 if (!prt_open_resource(&res_cidfont))
5703 return FALSE;
5704 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
5705 return FALSE;
5706 }
5707#endif
5708
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 /* Find an encoding to use for printing.
5710 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
5711 * that cannot be found then default to "latin1".
5712 * Note: VIM specific encoding header is always skipped.
5713 */
5714#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005715 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005718 p_encoding = enc_skip(p_penc);
5719 if (*p_encoding == NUL
5720 || !prt_find_resource((char *)p_encoding, &res_encoding))
5721 {
5722 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005723#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005724 int props;
5725
5726 p_encoding = enc_skip(p_enc);
5727 props = enc_canon_props(p_encoding);
5728 if (!(props & ENC_8BIT)
5729 || !prt_find_resource((char *)p_encoding, &res_encoding))
5730 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005732 {
5733 /* Use latin1 as default printing encoding */
5734 p_encoding = (char_u *)"latin1";
5735 if (!prt_find_resource((char *)p_encoding, &res_encoding))
5736 {
5737 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5738 p_encoding);
5739 return FALSE;
5740 }
5741 }
5742 }
5743 if (!prt_open_resource(&res_encoding))
5744 return FALSE;
5745 /* For the moment there are no checks on encoding resource files to
5746 * perform */
5747#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005749 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005751 p_encoding = enc_skip(p_penc);
5752 if (*p_encoding == NUL)
5753 p_encoding = enc_skip(p_enc);
5754 if (prt_use_courier)
5755 {
5756 /* Include ASCII range encoding vector */
5757 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
5758 {
5759 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5760 prt_ascii_encoding);
5761 return FALSE;
5762 }
5763 if (!prt_open_resource(&res_encoding))
5764 return FALSE;
5765 /* For the moment there are no checks on encoding resource files to
5766 * perform */
5767 }
5768 }
5769
5770 prt_conv.vc_type = CONV_NONE;
5771 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
5772 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005773 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
5774 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00005775 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00005776 p_encoding);
5777 return FALSE;
5778 }
5779 prt_do_conv = TRUE;
5780 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005781 prt_do_conv = prt_conv.vc_type != CONV_NONE;
5782
5783 if (prt_out_mbyte && prt_custom_cmap)
5784 {
5785 /* Find user supplied CMap */
5786 if (!prt_find_resource(prt_cmap, &res_cmap))
5787 {
5788 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
5789 prt_cmap);
5790 return FALSE;
5791 }
5792 if (!prt_open_resource(&res_cmap))
5793 return FALSE;
5794 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795#endif
5796
5797 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 STRCPY(buffer, res_prolog.title);
5799 STRCAT(buffer, " ");
5800 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005801 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
5802#ifdef FEAT_MBYTE
5803 if (prt_out_mbyte)
5804 {
5805 STRCPY(buffer, res_cidfont.title);
5806 STRCAT(buffer, " ");
5807 STRCAT(buffer, res_cidfont.version);
5808 prt_dsc_resources(NULL, "procset", buffer);
5809
5810 if (prt_custom_cmap)
5811 {
5812 STRCPY(buffer, res_cmap.title);
5813 STRCAT(buffer, " ");
5814 STRCAT(buffer, res_cmap.version);
5815 prt_dsc_resources(NULL, "cmap", buffer);
5816 }
5817 }
5818 if (!prt_out_mbyte || prt_use_courier)
5819#endif
5820 {
5821 STRCPY(buffer, res_encoding.title);
5822 STRCAT(buffer, " ");
5823 STRCAT(buffer, res_encoding.version);
5824 prt_dsc_resources(NULL, "encoding", buffer);
5825 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005826 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
5827#ifdef FEAT_SYN_HL
5828 psettings->do_syntax
5829#else
5830 0
5831#endif
5832 , prt_num_copies);
5833 prt_dsc_noarg("EndComments");
5834
5835 /*
5836 * PS Document page defaults
5837 */
5838 prt_dsc_noarg("BeginDefaults");
5839
5840 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005841#ifdef FEAT_MBYTE
5842 if (!prt_out_mbyte || prt_use_courier)
5843#endif
5844 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
5845#ifdef FEAT_MBYTE
5846 if (prt_out_mbyte)
5847 {
5848 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
5849 &prt_ps_mb_font);
5850 if (!prt_custom_cmap)
5851 prt_dsc_resources(NULL, "cmap", prt_cmap);
5852 }
5853#endif
5854
Bram Moolenaar071d4272004-06-13 20:20:40 +00005855 /* Paper will be used for all pages */
5856 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
5857
5858 prt_dsc_noarg("EndDefaults");
5859
5860 /*
5861 * PS Document prolog inclusion - all required procsets.
5862 */
5863 prt_dsc_noarg("BeginProlog");
5864
Bram Moolenaar8299df92004-07-10 09:47:34 +00005865 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 if (!prt_add_resource(&res_prolog))
5867 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005868#ifdef FEAT_MBYTE
5869 if (prt_out_mbyte)
5870 {
5871 /* Add CID font procset, and any user supplied CMap */
5872 if (!prt_add_resource(&res_cidfont))
5873 return FALSE;
5874 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
5875 return FALSE;
5876 }
5877#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005878
Bram Moolenaar8299df92004-07-10 09:47:34 +00005879#ifdef FEAT_MBYTE
5880 if (!prt_out_mbyte || prt_use_courier)
5881#endif
5882 /* There will be only one Roman font encoding to be included in the PS
5883 * file. */
5884 if (!prt_add_resource(&res_encoding))
5885 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886
5887 prt_dsc_noarg("EndProlog");
5888
5889 /*
5890 * PS Document setup - must appear after the prolog
5891 */
5892 prt_dsc_noarg("BeginSetup");
5893
5894 /* Device setup - page size and number of uncollated copies */
5895 prt_write_int((int)prt_mediasize[prt_media].width);
5896 prt_write_int((int)prt_mediasize[prt_media].height);
5897 prt_write_int(0);
5898 prt_write_string("sps\n");
5899 prt_write_int(prt_num_copies);
5900 prt_write_string("nc\n");
5901 prt_write_boolean(prt_duplex);
5902 prt_write_boolean(prt_tumble);
5903 prt_write_string("dt\n");
5904 prt_write_boolean(prt_collate);
5905 prt_write_string("c\n");
5906
5907 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005908#ifdef FEAT_MBYTE
5909 if (!prt_out_mbyte || prt_use_courier)
5910 {
5911 /* When using Courier for ASCII range when printing multi-byte, need to
5912 * pick up ASCII encoding to use with it. */
5913 if (prt_use_courier)
5914 p_encoding = prt_ascii_encoding;
5915#endif
5916 prt_dsc_resources("IncludeResource", "font",
5917 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5918 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
5919 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5920 prt_dsc_resources("IncludeResource", "font",
5921 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5922 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
5923 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
5924 prt_dsc_resources("IncludeResource", "font",
5925 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5926 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
5927 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5928 prt_dsc_resources("IncludeResource", "font",
5929 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5930 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
5931 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5932#ifdef FEAT_MBYTE
5933 }
5934 if (prt_out_mbyte)
5935 {
5936 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
5937 * not have an italic form being a western style, so where no font is
5938 * defined for these faces VIM falls back to an existing face.
5939 * Note: if using Courier for the ASCII range then the printout will
5940 * have bold/italic/bolditalic regardless of the setting of printmbfont.
5941 */
5942 prt_dsc_resources("IncludeResource", "font",
5943 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5944 if (!prt_custom_cmap)
5945 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5946 prt_def_cidfont("CF0", (int)prt_line_height,
5947 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
5948
5949 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
5950 {
5951 prt_dsc_resources("IncludeResource", "font",
5952 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5953 if (!prt_custom_cmap)
5954 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5955 prt_def_cidfont("CF1", (int)prt_line_height,
5956 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
5957 }
5958 else
5959 /* Use ROMAN for BOLD */
5960 prt_dup_cidfont("CF0", "CF1");
5961
5962 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
5963 {
5964 prt_dsc_resources("IncludeResource", "font",
5965 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5966 if (!prt_custom_cmap)
5967 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5968 prt_def_cidfont("CF2", (int)prt_line_height,
5969 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
5970 }
5971 else
5972 /* Use ROMAN for OBLIQUE */
5973 prt_dup_cidfont("CF0", "CF2");
5974
5975 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
5976 {
5977 prt_dsc_resources("IncludeResource", "font",
5978 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5979 if (!prt_custom_cmap)
5980 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
5981 prt_def_cidfont("CF3", (int)prt_line_height,
5982 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
5983 }
5984 else
5985 /* Use BOLD for BOLDOBLIQUE */
5986 prt_dup_cidfont("CF1", "CF3");
5987 }
5988#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989
5990 /* Misc constant vars used for underlining and background rects */
5991 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005992 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005993 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005994 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005995 prt_def_var("BO", prt_bgcol_offset, 2);
5996
5997 prt_dsc_noarg("EndSetup");
5998
5999 /* Fail if any problems writing out to the PS file */
6000 return !prt_file_error;
6001}
6002
6003 void
6004mch_print_end(psettings)
6005 prt_settings_T *psettings;
6006{
6007 prt_dsc_noarg("Trailer");
6008
6009 /*
6010 * Output any info we don't know in toto until we finish
6011 */
6012 prt_dsc_ints("Pages", 1, &prt_page_num);
6013
6014 prt_dsc_noarg("EOF");
6015
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006016 /* Write CTRL-D to close serial communication link if used.
6017 * NOTHING MUST BE WRITTEN AFTER THIS! */
6018 prt_write_file(IF_EB("\004", "\067"));
6019
Bram Moolenaar071d4272004-06-13 20:20:40 +00006020 if (!prt_file_error && psettings->outfile == NULL
6021 && !got_int && !psettings->user_abort)
6022 {
6023 /* Close the file first. */
6024 if (prt_ps_fd != NULL)
6025 {
6026 fclose(prt_ps_fd);
6027 prt_ps_fd = NULL;
6028 }
6029 prt_message((char_u *)_("Sending to printer..."));
6030
6031 /* Not printing to a file: use 'printexpr' to print the file. */
6032 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6033 EMSG(_("E365: Failed to print PostScript file"));
6034 else
6035 prt_message((char_u *)_("Print job sent."));
6036 }
6037
6038 mch_print_cleanup();
6039}
6040
6041 int
6042mch_print_end_page()
6043{
6044 prt_flush_buffer();
6045
6046 prt_write_string("re sp\n");
6047
6048 prt_dsc_noarg("PageTrailer");
6049
6050 return !prt_file_error;
6051}
6052
6053/*ARGSUSED*/
6054 int
6055mch_print_begin_page(str)
6056 char_u *str;
6057{
6058 int page_num[2];
6059
6060 prt_page_num++;
6061
6062 page_num[0] = page_num[1] = prt_page_num;
6063 prt_dsc_ints("Page", 2, page_num);
6064
6065 prt_dsc_noarg("BeginPageSetup");
6066
Bram Moolenaar8299df92004-07-10 09:47:34 +00006067 prt_write_string("sv\n0 g\n");
6068#ifdef FEAT_MBYTE
6069 prt_in_ascii = !prt_out_mbyte;
6070 if (prt_out_mbyte)
6071 prt_write_string("CF0 sf\n");
6072 else
6073#endif
6074 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075 prt_fgcol = PRCOLOR_BLACK;
6076 prt_bgcol = PRCOLOR_WHITE;
6077 prt_font = PRT_PS_FONT_ROMAN;
6078
6079 /* Set up page transformation for landscape printing. */
6080 if (!prt_portrait)
6081 {
6082 prt_write_int(-((int)prt_mediasize[prt_media].width));
6083 prt_write_string("sl\n");
6084 }
6085
6086 prt_dsc_noarg("EndPageSetup");
6087
6088 /* We have reset the font attributes, force setting them again. */
6089 curr_bg = (long_u)0xffffffff;
6090 curr_fg = (long_u)0xffffffff;
6091 curr_bold = MAYBE;
6092
6093 return !prt_file_error;
6094}
6095
6096 int
6097mch_print_blank_page()
6098{
6099 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6100}
6101
6102static float prt_pos_x = 0;
6103static float prt_pos_y = 0;
6104
6105 void
6106mch_print_start_line(margin, page_line)
6107 int margin;
6108 int page_line;
6109{
6110 prt_pos_x = prt_left_margin;
6111 if (margin)
6112 prt_pos_x -= prt_number_width;
6113
6114 prt_pos_y = prt_top_margin - prt_first_line_height -
6115 page_line * prt_line_height;
6116
6117 prt_attribute_change = TRUE;
6118 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006119#ifdef FEAT_MBYTE
6120 prt_half_width = FALSE;
6121#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006122}
6123
6124/*ARGSUSED*/
6125 int
6126mch_print_text_out(p, len)
6127 char_u *p;
6128 int len;
6129{
6130 int need_break;
6131 char_u ch;
6132 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006133 float char_width;
6134 float next_pos;
6135#ifdef FEAT_MBYTE
6136 int in_ascii;
6137 int half_width;
6138#endif
6139
6140 char_width = prt_char_width;
6141
6142#ifdef FEAT_MBYTE
6143 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6144 * CJKV font to do what VIM is doing here - use a Roman font for characters
6145 * in the ASCII range, and the origingal CID font for everything else.
6146 * The problem is that GhostScript still (as of 8.13) does not support
6147 * rearranged fonts even though they have been documented by Adobe for 7
6148 * years! If they ever do, a lot of this code will disappear.
6149 */
6150 if (prt_use_courier)
6151 {
6152 in_ascii = (len == 1 && *p < 0x80);
6153 if (prt_in_ascii)
6154 {
6155 if (!in_ascii)
6156 {
6157 /* No longer in ASCII range - need to switch font */
6158 prt_in_ascii = FALSE;
6159 prt_need_font = TRUE;
6160 prt_attribute_change = TRUE;
6161 }
6162 }
6163 else if (in_ascii)
6164 {
6165 /* Now in ASCII range - need to switch font */
6166 prt_in_ascii = TRUE;
6167 prt_need_font = TRUE;
6168 prt_attribute_change = TRUE;
6169 }
6170 }
6171 if (prt_out_mbyte)
6172 {
6173 half_width = ((*mb_ptr2cells)(p) == 1);
6174 if (half_width)
6175 char_width /= 2;
6176 if (prt_half_width)
6177 {
6178 if (!half_width)
6179 {
6180 prt_half_width = FALSE;
6181 prt_pos_x += prt_char_width/4;
6182 prt_need_moveto = TRUE;
6183 prt_attribute_change = TRUE;
6184 }
6185 }
6186 else if (half_width)
6187 {
6188 prt_half_width = TRUE;
6189 prt_pos_x += prt_char_width/4;
6190 prt_need_moveto = TRUE;
6191 prt_attribute_change = TRUE;
6192 }
6193 }
6194#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006195
6196 /* Output any required changes to the graphics state, after flushing any
6197 * text buffered so far.
6198 */
6199 if (prt_attribute_change)
6200 {
6201 prt_flush_buffer();
6202 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006203 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006204
6205 if (prt_need_moveto)
6206 {
6207 prt_pos_x_moveto = prt_pos_x;
6208 prt_pos_y_moveto = prt_pos_y;
6209 prt_do_moveto = TRUE;
6210
6211 prt_need_moveto = FALSE;
6212 }
6213 if (prt_need_font)
6214 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006215#ifdef FEAT_MBYTE
6216 if (!prt_in_ascii)
6217 prt_write_string("CF");
6218 else
6219#endif
6220 prt_write_string("F");
6221 prt_write_int(prt_font);
6222 prt_write_string("sf\n");
6223 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224 }
6225 if (prt_need_fgcol)
6226 {
6227 int r, g, b;
6228 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6229 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6230 b = prt_fgcol & 0xff;
6231
6232 prt_write_real(r / 255.0, 3);
6233 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235 else
6236 {
6237 prt_write_real(g / 255.0, 3);
6238 prt_write_real(b / 255.0, 3);
6239 prt_write_string("r\n");
6240 }
6241 prt_need_fgcol = FALSE;
6242 }
6243
6244 if (prt_bgcol != PRCOLOR_WHITE)
6245 {
6246 prt_new_bgcol = prt_bgcol;
6247 if (prt_need_bgcol)
6248 prt_do_bgcol = TRUE;
6249 }
6250 else
6251 prt_do_bgcol = FALSE;
6252 prt_need_bgcol = FALSE;
6253
6254 if (prt_need_underline)
6255 prt_do_underline = prt_underline;
6256 prt_need_underline = FALSE;
6257
6258 prt_attribute_change = FALSE;
6259 }
6260
6261#ifdef FEAT_MBYTE
6262 if (prt_do_conv)
6263 {
6264 /* Convert from multi-byte to 8-bit encoding */
6265 p = string_convert(&prt_conv, p, &len);
6266 if (p == NULL)
6267 p = (char_u *)"";
6268 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006269
Bram Moolenaar8299df92004-07-10 09:47:34 +00006270 if (prt_out_mbyte)
6271 {
6272 /* Multi-byte character strings are represented more efficiently as hex
6273 * strings when outputting clean 8 bit PS.
6274 */
6275 do
6276 {
6277 ch = prt_hexchar[(*p) >> 4];
6278 ga_append(&prt_ps_buffer, ch);
6279 ch = prt_hexchar[(*p) & 0xf];
6280 ga_append(&prt_ps_buffer, ch);
6281 p++;
6282 }
6283 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284 }
6285 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006286#endif
6287 {
6288 /* Add next character to buffer of characters to output.
6289 * Note: One printed character may require several PS characters to
6290 * represent it, but we only count them as one printed character.
6291 */
6292 ch = *p;
6293 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6294 {
6295 /* Convert non-printing characters to either their escape or octal
6296 * sequence, ensures PS sent over a serial line does not interfere
6297 * with the comms protocol. Note: For EBCDIC we need to write out
6298 * the escape sequences as ASCII codes!
6299 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6300 */
6301 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6302 switch (ch)
6303 {
6304 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6305 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6306 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6307 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6308 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6309 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6310 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6311 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6312
6313 default:
6314 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6315#ifdef EBCDIC
6316 ebcdic2ascii(ch_buff, 3);
6317#endif
6318 ga_append(&prt_ps_buffer, ch_buff[0]);
6319 ga_append(&prt_ps_buffer, ch_buff[1]);
6320 ga_append(&prt_ps_buffer, ch_buff[2]);
6321 break;
6322 }
6323 }
6324 else
6325 ga_append(&prt_ps_buffer, ch);
6326 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327
6328#ifdef FEAT_MBYTE
6329 /* Need to free any translated characters */
6330 if (prt_do_conv && (*p != NUL))
6331 vim_free(p);
6332#endif
6333
Bram Moolenaar8299df92004-07-10 09:47:34 +00006334 prt_text_run += char_width;
6335 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336
Bram Moolenaar8299df92004-07-10 09:47:34 +00006337 /* The downside of fp - use relative error on right margin check */
6338 next_pos = prt_pos_x + prt_char_width;
6339 need_break = (next_pos > prt_right_margin) &&
6340 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341
6342 if (need_break)
6343 prt_flush_buffer();
6344
6345 return need_break;
6346}
6347
6348 void
6349mch_print_set_font(iBold, iItalic, iUnderline)
6350 int iBold;
6351 int iItalic;
6352 int iUnderline;
6353{
6354 int font = 0;
6355
6356 if (iBold)
6357 font |= 0x01;
6358 if (iItalic)
6359 font |= 0x02;
6360
6361 if (font != prt_font)
6362 {
6363 prt_font = font;
6364 prt_attribute_change = TRUE;
6365 prt_need_font = TRUE;
6366 }
6367 if (prt_underline != iUnderline)
6368 {
6369 prt_underline = iUnderline;
6370 prt_attribute_change = TRUE;
6371 prt_need_underline = TRUE;
6372 }
6373}
6374
6375 void
6376mch_print_set_bg(bgcol)
6377 long_u bgcol;
6378{
6379 prt_bgcol = bgcol;
6380 prt_attribute_change = TRUE;
6381 prt_need_bgcol = TRUE;
6382}
6383
6384 void
6385mch_print_set_fg(fgcol)
6386 long_u fgcol;
6387{
6388 if (fgcol != (long_u)prt_fgcol)
6389 {
6390 prt_fgcol = fgcol;
6391 prt_attribute_change = TRUE;
6392 prt_need_fgcol = TRUE;
6393 }
6394}
6395
6396# endif /*FEAT_POSTSCRIPT*/
6397#endif /*FEAT_PRINTER*/
6398
6399#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6400 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
6401static char *get_locale_val __ARGS((int what));
6402
6403 static char *
6404get_locale_val(what)
6405 int what;
6406{
6407 char *loc;
6408
6409 /* Obtain the locale value from the libraries. For DJGPP this is
6410 * redefined and it doesn't use the arguments. */
6411 loc = setlocale(what, NULL);
6412
6413# if defined(__BORLANDC__)
6414 if (loc != NULL)
6415 {
6416 char_u *p;
6417
6418 /* Borland returns something like "LC_CTYPE=<name>\n"
6419 * Let's try to fix that bug here... */
6420 p = vim_strchr(loc, '=');
6421 if (p != NULL)
6422 {
6423 loc = ++p;
6424 while (*p != NUL) /* remove trailing newline */
6425 {
6426 if (*p < ' ')
6427 {
6428 *p = NUL;
6429 break;
6430 }
6431 ++p;
6432 }
6433 }
6434 }
6435# endif
6436
6437 return loc;
6438}
6439#endif
6440
6441
6442#ifdef WIN32
6443/*
6444 * On MS-Windows locale names are strings like "German_Germany.1252", but
6445 * gettext expects "de". Try to translate one into another here for a few
6446 * supported languages.
6447 */
6448 static char_u *
6449gettext_lang(char_u *name)
6450{
6451 int i;
6452 static char *(mtable[]) = {
6453 "afrikaans", "af",
6454 "czech", "cs",
6455 "dutch", "nl",
6456 "german", "de",
6457 "english_united kingdom", "en_GB",
6458 "spanish", "es",
6459 "french", "fr",
6460 "italian", "it",
6461 "japanese", "ja",
6462 "korean", "ko",
6463 "norwegian", "no",
6464 "polish", "pl",
6465 "russian", "ru",
6466 "slovak", "sk",
6467 "swedish", "sv",
6468 "ukrainian", "uk",
6469 "chinese_china", "zh_CN",
6470 "chinese_taiwan", "zh_TW",
6471 NULL};
6472
6473 for (i = 0; mtable[i] != NULL; i += 2)
6474 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
6475 return mtable[i + 1];
6476 return name;
6477}
6478#endif
6479
6480#if defined(FEAT_MULTI_LANG) || defined(PROTO)
6481/*
6482 * Obtain the current messages language. Used to set the default for
6483 * 'helplang'. May return NULL or an empty string.
6484 */
6485 char_u *
6486get_mess_lang()
6487{
6488 char_u *p;
6489
6490# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
6491# if defined(LC_MESSAGES)
6492 p = (char_u *)get_locale_val(LC_MESSAGES);
6493# else
6494 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
6495 * may be set to the LCID number. */
6496 p = (char_u *)get_locale_val(LC_ALL);
6497# endif
6498# else
6499 p = mch_getenv((char_u *)"LC_ALL");
6500 if (p == NULL || *p == NUL)
6501 {
6502 p = mch_getenv((char_u *)"LC_MESSAGES");
6503 if (p == NULL || *p == NUL)
6504 p = mch_getenv((char_u *)"LANG");
6505 }
6506# endif
6507# ifdef WIN32
6508 p = gettext_lang(p);
6509# endif
6510 return p;
6511}
6512#endif
6513
6514#if !defined(LC_MESSAGES) \
6515 && (((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6516 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))) \
6517 || defined(FEAT_EVAL))
6518static char_u *get_mess_env __ARGS((void));
6519
6520/*
6521 * Get the language used for messages from the environment.
6522 */
6523 static char_u *
6524get_mess_env()
6525{
6526 char_u *p;
6527
6528 p = mch_getenv((char_u *)"LC_ALL");
6529 if (p == NULL || *p == NUL)
6530 {
6531 p = mch_getenv((char_u *)"LC_MESSAGES");
6532 if (p == NULL || *p == NUL)
6533 {
6534 p = mch_getenv((char_u *)"LANG");
6535 if (p != NULL && VIM_ISDIGIT(*p))
6536 p = NULL; /* ignore something like "1043" */
6537# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6538 if (p == NULL || *p == NUL)
6539 p = (char_u *)get_locale_val(LC_CTYPE);
6540# endif
6541 }
6542 }
6543 return p;
6544}
6545#endif
6546
6547#if defined(FEAT_EVAL) || defined(PROTO)
6548
6549/*
6550 * Set the "v:lang" variable according to the current locale setting.
6551 * Also do "v:lc_time"and "v:ctype".
6552 */
6553 void
6554set_lang_var()
6555{
6556 char_u *loc;
6557
6558# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6559 loc = (char_u *)get_locale_val(LC_CTYPE);
6560# else
6561 /* setlocale() not supported: use the default value */
6562 loc = (char_u *)"C";
6563# endif
6564 set_vim_var_string(VV_CTYPE, loc, -1);
6565
6566 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
6567 * back to LC_CTYPE if it's empty. */
6568# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
6569 loc = (char_u *)get_locale_val(LC_MESSAGES);
6570# else
6571 loc = get_mess_env();
6572# endif
6573 set_vim_var_string(VV_LANG, loc, -1);
6574
6575# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
6576 loc = (char_u *)get_locale_val(LC_TIME);
6577# endif
6578 set_vim_var_string(VV_LC_TIME, loc, -1);
6579}
6580#endif
6581
6582#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
6583 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
6584/*
6585 * ":language": Set the language (locale).
6586 */
6587 void
6588ex_language(eap)
6589 exarg_T *eap;
6590{
6591 char *loc;
6592 char_u *p;
6593 char_u *name;
6594 int what = LC_ALL;
6595 char *whatstr = "";
6596#ifdef LC_MESSAGES
6597# define VIM_LC_MESSAGES LC_MESSAGES
6598#else
6599# define VIM_LC_MESSAGES 6789
6600#endif
6601
6602 name = eap->arg;
6603
6604 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
6605 * Allow abbreviation, but require at least 3 characters to avoid
6606 * confusion with a two letter language name "me" or "ct". */
6607 p = skiptowhite(eap->arg);
6608 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
6609 {
6610 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
6611 {
6612 what = VIM_LC_MESSAGES;
6613 name = skipwhite(p);
6614 whatstr = "messages ";
6615 }
6616 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
6617 {
6618 what = LC_CTYPE;
6619 name = skipwhite(p);
6620 whatstr = "ctype ";
6621 }
6622 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
6623 {
6624 what = LC_TIME;
6625 name = skipwhite(p);
6626 whatstr = "time ";
6627 }
6628 }
6629
6630 if (*name == NUL)
6631 {
6632#ifndef LC_MESSAGES
6633 if (what == VIM_LC_MESSAGES)
6634 p = get_mess_env();
6635 else
6636#endif
6637 p = (char_u *)setlocale(what, NULL);
6638 if (p == NULL || *p == NUL)
6639 p = (char_u *)"Unknown";
6640 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
6641 }
6642 else
6643 {
6644#ifndef LC_MESSAGES
6645 if (what == VIM_LC_MESSAGES)
6646 loc = "";
6647 else
6648#endif
6649 loc = setlocale(what, (char *)name);
6650 if (loc == NULL)
6651 EMSG2(_("E197: Cannot set language to \"%s\""), name);
6652 else
6653 {
6654#ifdef HAVE_NL_MSG_CAT_CNTR
6655 /* Need to do this for GNU gettext, otherwise cached translations
6656 * will be used again. */
6657 extern int _nl_msg_cat_cntr;
6658
6659 ++_nl_msg_cat_cntr;
6660#endif
6661 /* Reset $LC_ALL, otherwise it would overrule everyting. */
6662 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
6663
6664 if (what != LC_TIME)
6665 {
6666 /* Tell gettext() what to translate to. It apparently doesn't
6667 * use the currently effective locale. Also do this when
6668 * FEAT_GETTEXT isn't defined, so that shell commands use this
6669 * value. */
6670 if (what == LC_ALL)
6671 vim_setenv((char_u *)"LANG", name);
6672 if (what != LC_CTYPE)
6673 {
6674 char_u *mname;
6675#ifdef WIN32
6676 mname = gettext_lang(name);
6677#else
6678 mname = name;
6679#endif
6680 vim_setenv((char_u *)"LC_MESSAGES", mname);
6681#ifdef FEAT_MULTI_LANG
6682 set_helplang_default(mname);
6683#endif
6684 }
6685
6686 /* Set $LC_CTYPE, because it overrules $LANG, and
6687 * gtk_set_locale() calls setlocale() again. gnome_init()
6688 * sets $LC_CTYPE to "en_US" (that's a bug!). */
6689 if (what != VIM_LC_MESSAGES)
6690 vim_setenv((char_u *)"LC_CTYPE", name);
6691# ifdef FEAT_GUI_GTK
6692 /* Let GTK know what locale we're using. Not sure this is
6693 * really needed... */
6694 if (gui.in_use)
6695 (void)gtk_set_locale();
6696# endif
6697 }
6698
6699# ifdef FEAT_EVAL
6700 /* Set v:lang, v:lc_time and v:ctype to the final result. */
6701 set_lang_var();
6702# endif
6703 }
6704 }
6705}
6706
6707# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6708/*
6709 * Function given to ExpandGeneric() to obtain the possible arguments of the
6710 * ":language" command.
6711 */
6712/*ARGSUSED*/
6713 char_u *
6714get_lang_arg(xp, idx)
6715 expand_T *xp;
6716 int idx;
6717{
6718 if (idx == 0)
6719 return (char_u *)"messages";
6720 if (idx == 1)
6721 return (char_u *)"ctype";
6722 if (idx == 2)
6723 return (char_u *)"time";
6724 return NULL;
6725}
6726# endif
6727
6728#endif