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