blob: f9e1e5a93ce69fdb0bb2c3d0e18df842365bf4f6 [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
Bram Moolenaar05159a02005-02-26 23:04:13 +000028#ifdef FEAT_EVAL
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000029/* Growarray to store info about already sourced scripts.
Bram Moolenaar05159a02005-02-26 23:04:13 +000030 * For Unix also store the dev/ino, so that we don't have to stat() each
31 * script when going through the list. */
32typedef struct scriptitem_S
33{
34 char_u *sn_name;
35# ifdef UNIX
36 int sn_dev;
37 ino_t sn_ino;
38# endif
39# ifdef FEAT_PROFILE
40 int sn_prof_on; /* TRUE when script is/was profiled */
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000041 int sn_pr_force; /* forceit: profile functions in this script */
Bram Moolenaar05159a02005-02-26 23:04:13 +000042 proftime_T sn_pr_child; /* time set when going into first child */
43 int sn_pr_nest; /* nesting for sn_pr_child */
44 /* profiling the script as a whole */
45 int sn_pr_count; /* nr of times sourced */
46 proftime_T sn_pr_total; /* time spend in script + children */
47 proftime_T sn_pr_self; /* time spend in script itself */
48 proftime_T sn_pr_start; /* time at script start */
49 proftime_T sn_pr_children; /* time in children after script start */
50 /* profiling the script per line */
51 garray_T sn_prl_ga; /* things stored for every line */
52 proftime_T sn_prl_start; /* start time for current line */
53 proftime_T sn_prl_children; /* time spent in children for this line */
54 proftime_T sn_prl_wait; /* wait start time for current line */
55 int sn_prl_idx; /* index of line being timed; -1 if none */
56 int sn_prl_execed; /* line being timed was executed */
57# endif
58} scriptitem_T;
59
60static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
61#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
62
63# ifdef FEAT_PROFILE
64/* Struct used in sn_prl_ga for every line of a script. */
65typedef struct sn_prl_S
66{
67 int snp_count; /* nr of times line was executed */
68 proftime_T sn_prl_total; /* time spend in a line + children */
69 proftime_T sn_prl_self; /* time spend in a line itself */
70} sn_prl_T;
71
72# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
73# endif
74#endif
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#if defined(FEAT_EVAL) || defined(PROTO)
77static int debug_greedy = FALSE; /* batch mode debugging: don't save
78 and restore typeahead. */
79
80/*
81 * do_debug(): Debug mode.
82 * Repeatedly get Ex commands, until told to continue normal execution.
83 */
84 void
85do_debug(cmd)
86 char_u *cmd;
87{
88 int save_msg_scroll = msg_scroll;
89 int save_State = State;
90 int save_did_emsg = did_emsg;
91 int save_cmd_silent = cmd_silent;
92 int save_msg_silent = msg_silent;
93 int save_emsg_silent = emsg_silent;
94 int save_redir_off = redir_off;
95 tasave_T typeaheadbuf;
96# ifdef FEAT_EX_EXTRA
97 int save_ex_normal_busy;
98# endif
99 int n;
100 char_u *cmdline = NULL;
101 char_u *p;
102 char *tail = NULL;
103 static int last_cmd = 0;
104#define CMD_CONT 1
105#define CMD_NEXT 2
106#define CMD_STEP 3
107#define CMD_FINISH 4
108#define CMD_QUIT 5
109#define CMD_INTERRUPT 6
110
111#ifdef ALWAYS_USE_GUI
112 /* Can't do this when there is no terminal for input/output. */
113 if (!gui.in_use)
114 {
115 /* Break as soon as possible. */
116 debug_break_level = 9999;
117 return;
118 }
119#endif
120
121 /* Make sure we are in raw mode and start termcap mode. Might have side
122 * effects... */
123 settmode(TMODE_RAW);
124 starttermcap();
125
126 ++RedrawingDisabled; /* don't redisplay the window */
127 ++no_wait_return; /* don't wait for return */
128 did_emsg = FALSE; /* don't use error from debugged stuff */
129 cmd_silent = FALSE; /* display commands */
130 msg_silent = FALSE; /* display messages */
131 emsg_silent = FALSE; /* display error messages */
132 redir_off = TRUE; /* don't redirect debug commands */
133
134 State = NORMAL;
135#ifdef FEAT_SNIFF
136 want_sniff_request = 0; /* No K_SNIFF wanted */
137#endif
138
139 if (!debug_did_msg)
140 MSG(_("Entering Debug mode. Type \"cont\" to continue."));
141 if (sourcing_name != NULL)
142 msg(sourcing_name);
143 if (sourcing_lnum != 0)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +0000144 {
145 char_u buf[IOSIZE];
146
147 /* Truncate the command, the whole must fit in IObuff. */
148 STRNCPY(buf, cmd, IOSIZE - 50);
149 buf[IOSIZE - 50] = NUL;
150 smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, buf);
151 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 else
153 msg_str((char_u *)_("cmd: %s"), cmd);
154
155 /*
156 * Repeat getting a command and executing it.
157 */
158 for (;;)
159 {
160 msg_scroll = TRUE;
161 need_wait_return = FALSE;
162#ifdef FEAT_SNIFF
163 ProcessSniffRequests();
164#endif
165 /* Save the current typeahead buffer and replace it with an empty one.
166 * This makes sure we get input from the user here and don't interfere
167 * with the commands being executed. Reset "ex_normal_busy" to avoid
168 * the side effects of using ":normal". Save the stuff buffer and make
169 * it empty. */
170# ifdef FEAT_EX_EXTRA
171 save_ex_normal_busy = ex_normal_busy;
172 ex_normal_busy = 0;
173# endif
174 if (!debug_greedy)
175 save_typeahead(&typeaheadbuf);
176
177 cmdline = getcmdline_prompt('>', NULL, 0);
178
179 if (!debug_greedy)
180 restore_typeahead(&typeaheadbuf);
181# ifdef FEAT_EX_EXTRA
182 ex_normal_busy = save_ex_normal_busy;
183# endif
184
185 cmdline_row = msg_row;
186 if (cmdline != NULL)
187 {
188 /* If this is a debug command, set "last_cmd".
189 * If not, reset "last_cmd".
190 * For a blank line use previous command. */
191 p = skipwhite(cmdline);
192 if (*p != NUL)
193 {
194 switch (*p)
195 {
196 case 'c': last_cmd = CMD_CONT;
197 tail = "ont";
198 break;
199 case 'n': last_cmd = CMD_NEXT;
200 tail = "ext";
201 break;
202 case 's': last_cmd = CMD_STEP;
203 tail = "tep";
204 break;
205 case 'f': last_cmd = CMD_FINISH;
206 tail = "inish";
207 break;
208 case 'q': last_cmd = CMD_QUIT;
209 tail = "uit";
210 break;
211 case 'i': last_cmd = CMD_INTERRUPT;
212 tail = "nterrupt";
213 break;
214 default: last_cmd = 0;
215 }
216 if (last_cmd != 0)
217 {
218 /* Check that the tail matches. */
219 ++p;
220 while (*p != NUL && *p == *tail)
221 {
222 ++p;
223 ++tail;
224 }
225 if (ASCII_ISALPHA(*p))
226 last_cmd = 0;
227 }
228 }
229
230 if (last_cmd != 0)
231 {
232 /* Execute debug command: decided where to break next and
233 * return. */
234 switch (last_cmd)
235 {
236 case CMD_CONT:
237 debug_break_level = -1;
238 break;
239 case CMD_NEXT:
240 debug_break_level = ex_nesting_level;
241 break;
242 case CMD_STEP:
243 debug_break_level = 9999;
244 break;
245 case CMD_FINISH:
246 debug_break_level = ex_nesting_level - 1;
247 break;
248 case CMD_QUIT:
249 got_int = TRUE;
250 debug_break_level = -1;
251 break;
252 case CMD_INTERRUPT:
253 got_int = TRUE;
254 debug_break_level = 9999;
255 /* Do not repeat ">interrupt" cmd, continue stepping. */
256 last_cmd = CMD_STEP;
257 break;
258 }
259 break;
260 }
261
262 /* don't debug this command */
263 n = debug_break_level;
264 debug_break_level = -1;
265 (void)do_cmdline(cmdline, getexline, NULL,
266 DOCMD_VERBOSE|DOCMD_EXCRESET);
267 debug_break_level = n;
268
269 vim_free(cmdline);
270 }
271 lines_left = Rows - 1;
272 }
273 vim_free(cmdline);
274
275 --RedrawingDisabled;
276 --no_wait_return;
277 redraw_all_later(NOT_VALID);
278 need_wait_return = FALSE;
279 msg_scroll = save_msg_scroll;
280 lines_left = Rows - 1;
281 State = save_State;
282 did_emsg = save_did_emsg;
283 cmd_silent = save_cmd_silent;
284 msg_silent = save_msg_silent;
285 emsg_silent = save_emsg_silent;
286 redir_off = save_redir_off;
287
288 /* Only print the message again when typing a command before coming back
289 * here. */
290 debug_did_msg = TRUE;
291}
292
293/*
294 * ":debug".
295 */
296 void
297ex_debug(eap)
298 exarg_T *eap;
299{
300 int debug_break_level_save = debug_break_level;
301
302 debug_break_level = 9999;
303 do_cmdline_cmd(eap->arg);
304 debug_break_level = debug_break_level_save;
305}
306
307static char_u *debug_breakpoint_name = NULL;
308static linenr_T debug_breakpoint_lnum;
309
310/*
311 * When debugging or a breakpoint is set on a skipped command, no debug prompt
312 * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
313 * debug_skipped_name is then set to the source name in the breakpoint case. If
314 * a skipped command decides itself that a debug prompt should be displayed, it
315 * can do so by calling dbg_check_skipped().
316 */
317static int debug_skipped;
318static char_u *debug_skipped_name;
319
320/*
321 * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
322 * at or below the break level. But only when the line is actually
323 * executed. Return TRUE and set breakpoint_name for skipped commands that
324 * decide to execute something themselves.
325 * Called from do_one_cmd() before executing a command.
326 */
327 void
328dbg_check_breakpoint(eap)
329 exarg_T *eap;
330{
331 char_u *p;
332
333 debug_skipped = FALSE;
334 if (debug_breakpoint_name != NULL)
335 {
336 if (!eap->skip)
337 {
338 /* replace K_SNR with "<SNR>" */
339 if (debug_breakpoint_name[0] == K_SPECIAL
340 && debug_breakpoint_name[1] == KS_EXTRA
341 && debug_breakpoint_name[2] == (int)KE_SNR)
342 p = (char_u *)"<SNR>";
343 else
344 p = (char_u *)"";
345 smsg((char_u *)_("Breakpoint in \"%s%s\" line %ld"), p,
346 debug_breakpoint_name + (*p == NUL ? 0 : 3),
347 (long)debug_breakpoint_lnum);
348 debug_breakpoint_name = NULL;
349 do_debug(eap->cmd);
350 }
351 else
352 {
353 debug_skipped = TRUE;
354 debug_skipped_name = debug_breakpoint_name;
355 debug_breakpoint_name = NULL;
356 }
357 }
358 else if (ex_nesting_level <= debug_break_level)
359 {
360 if (!eap->skip)
361 do_debug(eap->cmd);
362 else
363 {
364 debug_skipped = TRUE;
365 debug_skipped_name = NULL;
366 }
367 }
368}
369
370/*
371 * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
372 * set. Return TRUE when the debug mode is entered this time.
373 */
374 int
375dbg_check_skipped(eap)
376 exarg_T *eap;
377{
378 int prev_got_int;
379
380 if (debug_skipped)
381 {
382 /*
383 * Save the value of got_int and reset it. We don't want a previous
384 * interruption cause flushing the input buffer.
385 */
386 prev_got_int = got_int;
387 got_int = FALSE;
388 debug_breakpoint_name = debug_skipped_name;
389 /* eap->skip is TRUE */
390 eap->skip = FALSE;
391 (void)dbg_check_breakpoint(eap);
392 eap->skip = TRUE;
393 got_int |= prev_got_int;
394 return TRUE;
395 }
396 return FALSE;
397}
398
399/*
400 * The list of breakpoints: dbg_breakp.
401 * This is a grow-array of structs.
402 */
403struct debuggy
404{
405 int dbg_nr; /* breakpoint number */
406 int dbg_type; /* DBG_FUNC or DBG_FILE */
407 char_u *dbg_name; /* function or file name */
408 regprog_T *dbg_prog; /* regexp program */
409 linenr_T dbg_lnum; /* line number in function or file */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000410 int dbg_forceit; /* ! used */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411};
412
413static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
Bram Moolenaar05159a02005-02-26 23:04:13 +0000414#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
415#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416static int last_breakp = 0; /* nr of last defined breakpoint */
417
Bram Moolenaar05159a02005-02-26 23:04:13 +0000418#ifdef FEAT_PROFILE
419/* Profiling uses file and func names similar to breakpoints. */
420static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
421#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422#define DBG_FUNC 1
423#define DBG_FILE 2
424
Bram Moolenaar05159a02005-02-26 23:04:13 +0000425static int dbg_parsearg __ARGS((char_u *arg, garray_T *gap));
426static linenr_T debuggy_find __ARGS((int file,char_u *fname, linenr_T after, garray_T *gap, int *fp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427
428/*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000429 * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
430 * in the entry just after the last one in dbg_breakp. Note that "dbg_name"
431 * is allocated.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000432 * Returns FAIL for failure.
433 */
434 static int
Bram Moolenaar05159a02005-02-26 23:04:13 +0000435dbg_parsearg(arg, gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000436 char_u *arg;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000437 garray_T *gap; /* either &dbg_breakp or &prof_ga */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000438{
439 char_u *p = arg;
440 char_u *q;
441 struct debuggy *bp;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000442 int here = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443
Bram Moolenaar05159a02005-02-26 23:04:13 +0000444 if (ga_grow(gap, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 return FAIL;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000446 bp = &DEBUGGY(gap, gap->ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447
448 /* Find "func" or "file". */
449 if (STRNCMP(p, "func", 4) == 0)
450 bp->dbg_type = DBG_FUNC;
451 else if (STRNCMP(p, "file", 4) == 0)
452 bp->dbg_type = DBG_FILE;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000453 else if (
454#ifdef FEAT_PROFILE
455 gap != &prof_ga &&
456#endif
457 STRNCMP(p, "here", 4) == 0)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000458 {
459 if (curbuf->b_ffname == NULL)
460 {
461 EMSG(_(e_noname));
462 return FAIL;
463 }
464 bp->dbg_type = DBG_FILE;
465 here = TRUE;
466 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467 else
468 {
469 EMSG2(_(e_invarg2), p);
470 return FAIL;
471 }
472 p = skipwhite(p + 4);
473
474 /* Find optional line number. */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000475 if (here)
476 bp->dbg_lnum = curwin->w_cursor.lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000477 else if (
478#ifdef FEAT_PROFILE
479 gap != &prof_ga &&
480#endif
481 VIM_ISDIGIT(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000482 {
483 bp->dbg_lnum = getdigits(&p);
484 p = skipwhite(p);
485 }
486 else
487 bp->dbg_lnum = 0;
488
489 /* Find the function or file name. Don't accept a function name with (). */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000490 if ((!here && *p == NUL)
491 || (here && *p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL))
493 {
494 EMSG2(_(e_invarg2), arg);
495 return FAIL;
496 }
497
498 if (bp->dbg_type == DBG_FUNC)
499 bp->dbg_name = vim_strsave(p);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000500 else if (here)
501 bp->dbg_name = vim_strsave(curbuf->b_ffname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 else
503 {
504 /* Expand the file name in the same way as do_source(). This means
505 * doing it twice, so that $DIR/file gets expanded when $DIR is
506 * "~/dir". */
507#ifdef RISCOS
508 q = mch_munge_fname(p);
509#else
510 q = expand_env_save(p);
511#endif
512 if (q == NULL)
513 return FAIL;
514#ifdef RISCOS
515 p = mch_munge_fname(q);
516#else
517 p = expand_env_save(q);
518#endif
519 vim_free(q);
520 if (p == NULL)
521 return FAIL;
Bram Moolenaar843ee412004-06-30 16:16:41 +0000522 if (*p != '*')
523 {
524 bp->dbg_name = fix_fname(p);
525 vim_free(p);
526 }
527 else
528 bp->dbg_name = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529#ifdef MACOS_CLASSIC
530 if (bp->dbg_name != NULL)
531 slash_n_colon_adjust(bp->dbg_name);
532#endif
533 }
534
535 if (bp->dbg_name == NULL)
536 return FAIL;
537 return OK;
538}
539
540/*
541 * ":breakadd".
542 */
543 void
544ex_breakadd(eap)
545 exarg_T *eap;
546{
547 struct debuggy *bp;
548 char_u *pat;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000549 garray_T *gap;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550
Bram Moolenaar05159a02005-02-26 23:04:13 +0000551 gap = &dbg_breakp;
552#ifdef FEAT_PROFILE
553 if (eap->cmdidx == CMD_profile)
554 gap = &prof_ga;
555#endif
556
557 if (dbg_parsearg(eap->arg, gap) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000559 bp = &DEBUGGY(gap, gap->ga_len);
560 bp->dbg_forceit = eap->forceit;
561
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
563 if (pat != NULL)
564 {
565 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
566 vim_free(pat);
567 }
568 if (pat == NULL || bp->dbg_prog == NULL)
569 vim_free(bp->dbg_name);
570 else
571 {
572 if (bp->dbg_lnum == 0) /* default line number is 1 */
573 bp->dbg_lnum = 1;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000574#ifdef FEAT_PROFILE
575 if (eap->cmdidx != CMD_profile)
576#endif
577 {
578 DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
579 ++debug_tick;
580 }
581 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 }
583 }
584}
585
586/*
587 * ":debuggreedy".
588 */
589 void
590ex_debuggreedy(eap)
591 exarg_T *eap;
592{
593 if (eap->addr_count == 0 || eap->line2 != 0)
594 debug_greedy = TRUE;
595 else
596 debug_greedy = FALSE;
597}
598
599/*
600 * ":breakdel".
601 */
602 void
603ex_breakdel(eap)
604 exarg_T *eap;
605{
606 struct debuggy *bp, *bpi;
607 int nr;
608 int todel = -1;
609 int i;
610 linenr_T best_lnum = 0;
611
612 if (vim_isdigit(*eap->arg))
613 {
614 /* ":breakdel {nr}" */
615 nr = atol((char *)eap->arg);
616 for (i = 0; i < dbg_breakp.ga_len; ++i)
617 if (BREAKP(i).dbg_nr == nr)
618 {
619 todel = i;
620 break;
621 }
622 }
623 else
624 {
625 /* ":breakdel {func|file} [lnum] {name}" */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000626 if (dbg_parsearg(eap->arg, &dbg_breakp) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000627 return;
628 bp = &BREAKP(dbg_breakp.ga_len);
629 for (i = 0; i < dbg_breakp.ga_len; ++i)
630 {
631 bpi = &BREAKP(i);
632 if (bp->dbg_type == bpi->dbg_type
633 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
634 && (bp->dbg_lnum == bpi->dbg_lnum
635 || (bp->dbg_lnum == 0
636 && (best_lnum == 0
637 || bpi->dbg_lnum < best_lnum))))
638 {
639 todel = i;
640 best_lnum = bpi->dbg_lnum;
641 }
642 }
643 vim_free(bp->dbg_name);
644 }
645
646 if (todel < 0)
647 EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
648 else
649 {
650 vim_free(BREAKP(todel).dbg_name);
651 vim_free(BREAKP(todel).dbg_prog);
652 --dbg_breakp.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 if (todel < dbg_breakp.ga_len)
654 mch_memmove(&BREAKP(todel), &BREAKP(todel + 1),
655 (dbg_breakp.ga_len - todel) * sizeof(struct debuggy));
656 ++debug_tick;
657 }
658}
659
660/*
661 * ":breaklist".
662 */
663/*ARGSUSED*/
664 void
665ex_breaklist(eap)
666 exarg_T *eap;
667{
668 struct debuggy *bp;
669 int i;
670
671 if (dbg_breakp.ga_len == 0)
672 MSG(_("No breakpoints defined"));
673 else
674 for (i = 0; i < dbg_breakp.ga_len; ++i)
675 {
676 bp = &BREAKP(i);
677 smsg((char_u *)_("%3d %s %s line %ld"),
678 bp->dbg_nr,
679 bp->dbg_type == DBG_FUNC ? "func" : "file",
680 bp->dbg_name,
681 (long)bp->dbg_lnum);
682 }
683}
684
685/*
686 * Find a breakpoint for a function or sourced file.
687 * Returns line number at which to break; zero when no matching breakpoint.
688 */
689 linenr_T
690dbg_find_breakpoint(file, fname, after)
691 int file; /* TRUE for a file, FALSE for a function */
692 char_u *fname; /* file or function name */
693 linenr_T after; /* after this line number */
694{
Bram Moolenaar05159a02005-02-26 23:04:13 +0000695 return debuggy_find(file, fname, after, &dbg_breakp, NULL);
696}
697
698#if defined(FEAT_PROFILE) || defined(PROTO)
699/*
700 * Return TRUE if profiling is on for a function or sourced file.
701 */
702 int
703has_profiling(file, fname, fp)
704 int file; /* TRUE for a file, FALSE for a function */
705 char_u *fname; /* file or function name */
706 int *fp; /* return: forceit */
707{
708 return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
709 != (linenr_T)0);
710}
711#endif
712
713/*
714 * Common code for dbg_find_breakpoint() and has_profiling().
715 */
716 static linenr_T
717debuggy_find(file, fname, after, gap, fp)
718 int file; /* TRUE for a file, FALSE for a function */
719 char_u *fname; /* file or function name */
720 linenr_T after; /* after this line number */
721 garray_T *gap; /* either &dbg_breakp or &prof_ga */
722 int *fp; /* if not NULL: return forceit */
723{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724 struct debuggy *bp;
725 int i;
726 linenr_T lnum = 0;
727 regmatch_T regmatch;
728 char_u *name = fname;
729 int prev_got_int;
730
Bram Moolenaar05159a02005-02-26 23:04:13 +0000731 /* Return quickly when there are no breakpoints. */
732 if (gap->ga_len == 0)
733 return (linenr_T)0;
734
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 /* Replace K_SNR in function name with "<SNR>". */
736 if (!file && fname[0] == K_SPECIAL)
737 {
738 name = alloc((unsigned)STRLEN(fname) + 3);
739 if (name == NULL)
740 name = fname;
741 else
742 {
743 STRCPY(name, "<SNR>");
744 STRCPY(name + 5, fname + 3);
745 }
746 }
747
Bram Moolenaar05159a02005-02-26 23:04:13 +0000748 for (i = 0; i < gap->ga_len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000750 /* Skip entries that are not useful or are for a line that is beyond
751 * an already found breakpoint. */
752 bp = &DEBUGGY(gap, i);
753 if (((bp->dbg_type == DBG_FILE) == file && (
754#ifdef FEAT_PROFILE
755 gap == &prof_ga ||
756#endif
757 (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758 {
759 regmatch.regprog = bp->dbg_prog;
760 regmatch.rm_ic = FALSE;
761 /*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000762 * Save the value of got_int and reset it. We don't want a
763 * previous interruption cancel matching, only hitting CTRL-C
764 * while matching should abort it.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 */
766 prev_got_int = got_int;
767 got_int = FALSE;
768 if (vim_regexec(&regmatch, name, (colnr_T)0))
Bram Moolenaar05159a02005-02-26 23:04:13 +0000769 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 lnum = bp->dbg_lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000771 if (fp != NULL)
772 *fp = bp->dbg_forceit;
773 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 got_int |= prev_got_int;
775 }
776 }
777 if (name != fname)
778 vim_free(name);
779
780 return lnum;
781}
782
783/*
784 * Called when a breakpoint was encountered.
785 */
786 void
787dbg_breakpoint(name, lnum)
788 char_u *name;
789 linenr_T lnum;
790{
791 /* We need to check if this line is actually executed in do_one_cmd() */
792 debug_breakpoint_name = name;
793 debug_breakpoint_lnum = lnum;
794}
Bram Moolenaar05159a02005-02-26 23:04:13 +0000795
796
797# if defined(FEAT_PROFILE) || defined(PROTO)
798/*
799 * Functions for profiling.
800 */
801static void script_do_profile __ARGS((scriptitem_T *si));
802static void script_dump_profile __ARGS((FILE *fd));
803static proftime_T prof_wait_time;
804
805/*
806 * Set the time in "tm" to zero.
807 */
808 void
809profile_zero(tm)
810 proftime_T *tm;
811{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000812# ifdef WIN3264
813 tm->QuadPart = 0;
814# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000815 tm->tv_usec = 0;
816 tm->tv_sec = 0;
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000817# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000818}
819
820/*
821 * Store the current time in "tm".
822 */
823 void
824profile_start(tm)
825 proftime_T *tm;
826{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000827# ifdef WIN3264
828 QueryPerformanceCounter(tm);
829# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000830 gettimeofday(tm, NULL);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000831# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000832}
833
834/*
835 * Compute the elapsed time from "tm" till now and store in "tm".
836 */
837 void
838profile_end(tm)
839 proftime_T *tm;
840{
841 proftime_T now;
842
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000843# ifdef WIN3264
844 QueryPerformanceCounter(&now);
845 tm->QuadPart = now.QuadPart - tm->QuadPart;
846# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000847 gettimeofday(&now, NULL);
848 tm->tv_usec = now.tv_usec - tm->tv_usec;
849 tm->tv_sec = now.tv_sec - tm->tv_sec;
850 if (tm->tv_usec < 0)
851 {
852 tm->tv_usec += 1000000;
853 --tm->tv_sec;
854 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000855# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000856}
857
858/*
859 * Subtract the time "tm2" from "tm".
860 */
861 void
862profile_sub(tm, tm2)
863 proftime_T *tm, *tm2;
864{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000865# ifdef WIN3264
866 tm->QuadPart -= tm2->QuadPart;
867# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000868 tm->tv_usec -= tm2->tv_usec;
869 tm->tv_sec -= tm2->tv_sec;
870 if (tm->tv_usec < 0)
871 {
872 tm->tv_usec += 1000000;
873 --tm->tv_sec;
874 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000875# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000876}
877
878/*
879 * Add the time "tm2" to "tm".
880 */
881 void
882profile_add(tm, tm2)
883 proftime_T *tm, *tm2;
884{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000885# ifdef WIN3264
886 tm->QuadPart += tm2->QuadPart;
887# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000888 tm->tv_usec += tm2->tv_usec;
889 tm->tv_sec += tm2->tv_sec;
890 if (tm->tv_usec >= 1000000)
891 {
892 tm->tv_usec -= 1000000;
893 ++tm->tv_sec;
894 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000895# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000896}
897
898/*
899 * Get the current waittime.
900 */
901 void
902profile_get_wait(tm)
903 proftime_T *tm;
904{
905 *tm = prof_wait_time;
906}
907
908/*
909 * Subtract the passed waittime since "tm" from "tma".
910 */
911 void
912profile_sub_wait(tm, tma)
913 proftime_T *tm, *tma;
914{
915 proftime_T tm3 = prof_wait_time;
916
917 profile_sub(&tm3, tm);
918 profile_sub(tma, &tm3);
919}
920
921/*
922 * Return TRUE if "tm1" and "tm2" are equal.
923 */
924 int
925profile_equal(tm1, tm2)
926 proftime_T *tm1, *tm2;
927{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000928# ifdef WIN3264
929 return (tm1->QuadPart == tm2->QuadPart);
930# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000931 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000932# endif
933}
934
935/*
936 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
937 */
938 int
939profile_cmp(tm1, tm2)
940 proftime_T *tm1, *tm2;
941{
942# ifdef WIN3264
943 return (int)(tm2->QuadPart - tm1->QuadPart);
944# else
945 if (tm1->tv_sec == tm2->tv_sec)
946 return tm2->tv_usec - tm1->tv_usec;
947 return tm2->tv_sec - tm1->tv_sec;
948# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000949}
950
951/*
952 * Return a string that represents a time.
953 * Uses a static buffer!
954 */
955 char *
956profile_msg(tm)
957 proftime_T *tm;
958{
959 static char buf[50];
960
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000961# ifdef WIN3264
962 LARGE_INTEGER fr;
963
964 QueryPerformanceFrequency(&fr);
965 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
966# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000967 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000968#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000969 return buf;
970}
971
972static char_u *profile_fname = NULL;
973
974/*
975 * ":profile cmd args"
976 */
977 void
978ex_profile(eap)
979 exarg_T *eap;
980{
981 char_u *e;
982 int len;
983
984 e = skiptowhite(eap->arg);
985 len = e - eap->arg;
986 e = skipwhite(e);
987
988 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
989 {
990 vim_free(profile_fname);
991 profile_fname = vim_strsave(e);
992 do_profiling = TRUE;
993 profile_zero(&prof_wait_time);
994 set_vim_var_nr(VV_PROFILING, 1L);
995 }
996 else if (!do_profiling)
997 EMSG(_("E750: First use :profile start <fname>"));
998 else
999 {
1000 /* The rest is similar to ":breakadd". */
1001 ex_breakadd(eap);
1002 }
1003}
1004
1005/*
1006 * Dump the profiling info.
1007 */
1008 void
1009profile_dump()
1010{
1011 FILE *fd;
1012
1013 if (profile_fname != NULL)
1014 {
1015 fd = fopen((char *)profile_fname, "w");
1016 if (fd == NULL)
1017 EMSG2(_(e_notopen), profile_fname);
1018 else
1019 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00001020 script_dump_profile(fd);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001021 func_dump_profile(fd);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001022 fclose(fd);
1023 }
1024 }
1025}
1026
1027/*
1028 * Start profiling script "fp".
1029 */
1030 static void
1031script_do_profile(si)
1032 scriptitem_T *si;
1033{
1034 si->sn_pr_count = 0;
1035 profile_zero(&si->sn_pr_total);
1036 profile_zero(&si->sn_pr_self);
1037
1038 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
1039 si->sn_prl_idx = -1;
1040 si->sn_prof_on = TRUE;
1041 si->sn_pr_nest = 0;
1042}
1043
1044/*
1045 * save time when starting to invoke another script or function.
1046 */
1047 void
1048script_prof_save(tm)
1049 proftime_T *tm; /* place to store wait time */
1050{
1051 scriptitem_T *si;
1052
1053 if (current_SID > 0 && current_SID <= script_items.ga_len)
1054 {
1055 si = &SCRIPT_ITEM(current_SID);
1056 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
1057 profile_start(&si->sn_pr_child);
1058 }
1059 profile_get_wait(tm);
1060}
1061
1062/*
1063 * Count time spent in children after invoking another script or function.
1064 */
1065 void
1066script_prof_restore(tm)
1067 proftime_T *tm;
1068{
1069 scriptitem_T *si;
1070
1071 if (current_SID > 0 && current_SID <= script_items.ga_len)
1072 {
1073 si = &SCRIPT_ITEM(current_SID);
1074 if (si->sn_prof_on && --si->sn_pr_nest == 0)
1075 {
1076 profile_end(&si->sn_pr_child);
1077 profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */
1078 profile_add(&si->sn_pr_children, &si->sn_pr_child);
1079 profile_add(&si->sn_prl_children, &si->sn_pr_child);
1080 }
1081 }
1082}
1083
1084static proftime_T inchar_time;
1085
1086/*
1087 * Called when starting to wait for the user to type a character.
1088 */
1089 void
1090prof_inchar_enter()
1091{
1092 profile_start(&inchar_time);
1093}
1094
1095/*
1096 * Called when finished waiting for the user to type a character.
1097 */
1098 void
1099prof_inchar_exit()
1100{
1101 profile_end(&inchar_time);
1102 profile_add(&prof_wait_time, &inchar_time);
1103}
1104
1105/*
1106 * Dump the profiling results for all scripts in file "fd".
1107 */
1108 static void
1109script_dump_profile(fd)
1110 FILE *fd;
1111{
1112 int id;
1113 scriptitem_T *si;
1114 int i;
1115 FILE *sfd;
1116 sn_prl_T *pp;
1117
1118 for (id = 1; id <= script_items.ga_len; ++id)
1119 {
1120 si = &SCRIPT_ITEM(id);
1121 if (si->sn_prof_on)
1122 {
1123 fprintf(fd, "SCRIPT %s\n", si->sn_name);
1124 if (si->sn_pr_count == 1)
1125 fprintf(fd, "Sourced 1 time\n");
1126 else
1127 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
1128 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
1129 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
1130 fprintf(fd, "\n");
1131 fprintf(fd, "count total (s) self (s)\n");
1132
1133 sfd = fopen((char *)si->sn_name, "r");
1134 if (sfd == NULL)
1135 fprintf(fd, "Cannot open file!\n");
1136 else
1137 {
1138 for (i = 0; i < si->sn_prl_ga.ga_len; ++i)
1139 {
1140 if (vim_fgets(IObuff, IOSIZE, sfd))
1141 break;
1142 pp = &PRL_ITEM(si, i);
1143 if (pp->snp_count > 0)
1144 {
1145 fprintf(fd, "%5d ", pp->snp_count);
1146 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1147 fprintf(fd, " ");
1148 else
1149 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1150 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1151 }
1152 else
1153 fprintf(fd, " ");
1154 fprintf(fd, "%s", IObuff);
1155 }
1156 fclose(sfd);
1157 }
1158 fprintf(fd, "\n");
1159 }
1160 }
1161}
1162
1163/*
1164 * Return TRUE when a function defined in the current script should be
1165 * profiled.
1166 */
1167 int
1168prof_def_func()
1169{
1170 scriptitem_T *si = &SCRIPT_ITEM(current_SID);
1171
1172 return si->sn_pr_force;
1173}
1174
1175# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176#endif
1177
1178/*
1179 * If 'autowrite' option set, try to write the file.
1180 * Careful: autocommands may make "buf" invalid!
1181 *
1182 * return FAIL for failure, OK otherwise
1183 */
1184 int
1185autowrite(buf, forceit)
1186 buf_T *buf;
1187 int forceit;
1188{
1189 if (!(p_aw || p_awa) || !p_write
1190#ifdef FEAT_QUICKFIX
1191 /* never autowrite a "nofile" or "nowrite" buffer */
1192 || bt_dontwrite(buf)
1193#endif
1194 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
1195 return FAIL;
1196 return buf_write_all(buf, forceit);
1197}
1198
1199/*
1200 * flush all buffers, except the ones that are readonly
1201 */
1202 void
1203autowrite_all()
1204{
1205 buf_T *buf;
1206
1207 if (!(p_aw || p_awa) || !p_write)
1208 return;
1209 for (buf = firstbuf; buf; buf = buf->b_next)
1210 if (bufIsChanged(buf) && !buf->b_p_ro)
1211 {
1212 (void)buf_write_all(buf, FALSE);
1213#ifdef FEAT_AUTOCMD
1214 /* an autocommand may have deleted the buffer */
1215 if (!buf_valid(buf))
1216 buf = firstbuf;
1217#endif
1218 }
1219}
1220
1221/*
1222 * return TRUE if buffer was changed and cannot be abandoned.
1223 */
1224/*ARGSUSED*/
1225 int
1226check_changed(buf, checkaw, mult_win, forceit, allbuf)
1227 buf_T *buf;
1228 int checkaw; /* do autowrite if buffer was changed */
1229 int mult_win; /* check also when several wins for the buf */
1230 int forceit;
1231 int allbuf; /* may write all buffers */
1232{
1233 if ( !forceit
1234 && bufIsChanged(buf)
1235 && (mult_win || buf->b_nwindows <= 1)
1236 && (!checkaw || autowrite(buf, forceit) == FAIL))
1237 {
1238#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1239 if ((p_confirm || cmdmod.confirm) && p_write)
1240 {
1241 buf_T *buf2;
1242 int count = 0;
1243
1244 if (allbuf)
1245 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1246 if (bufIsChanged(buf2)
1247 && (buf2->b_ffname != NULL
1248# ifdef FEAT_BROWSE
1249 || cmdmod.browse
1250# endif
1251 ))
1252 ++count;
1253# ifdef FEAT_AUTOCMD
1254 if (!buf_valid(buf))
1255 /* Autocommand deleted buffer, oops! It's not changed now. */
1256 return FALSE;
1257# endif
1258 dialog_changed(buf, count > 1);
1259# ifdef FEAT_AUTOCMD
1260 if (!buf_valid(buf))
1261 /* Autocommand deleted buffer, oops! It's not changed now. */
1262 return FALSE;
1263# endif
1264 return bufIsChanged(buf);
1265 }
1266#endif
1267 EMSG(_(e_nowrtmsg));
1268 return TRUE;
1269 }
1270 return FALSE;
1271}
1272
1273#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
1274
1275#if defined(FEAT_BROWSE) || defined(PROTO)
1276/*
1277 * When wanting to write a file without a file name, ask the user for a name.
1278 */
1279 void
1280browse_save_fname(buf)
1281 buf_T *buf;
1282{
1283 if (buf->b_fname == NULL)
1284 {
1285 char_u *fname;
1286
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001287 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
1288 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 if (fname != NULL)
1290 {
1291 if (setfname(buf, fname, NULL, TRUE) == OK)
1292 buf->b_flags |= BF_NOTEDITED;
1293 vim_free(fname);
1294 }
1295 }
1296}
1297#endif
1298
1299/*
1300 * Ask the user what to do when abondoning a changed buffer.
1301 * Must check 'write' option first!
1302 */
1303 void
1304dialog_changed(buf, checkall)
1305 buf_T *buf;
1306 int checkall; /* may abandon all changed buffers */
1307{
1308 char_u buff[IOSIZE];
1309 int ret;
1310 buf_T *buf2;
1311
1312 dialog_msg(buff, _("Save changes to \"%.*s\"?"),
1313 (buf->b_fname != NULL) ?
1314 buf->b_fname : (char_u *)_("Untitled"));
1315 if (checkall)
1316 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
1317 else
1318 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
1319
1320 if (ret == VIM_YES)
1321 {
1322#ifdef FEAT_BROWSE
1323 /* May get file name, when there is none */
1324 browse_save_fname(buf);
1325#endif
1326 if (buf->b_fname != NULL) /* didn't hit Cancel */
1327 (void)buf_write_all(buf, FALSE);
1328 }
1329 else if (ret == VIM_NO)
1330 {
1331 unchanged(buf, TRUE);
1332 }
1333 else if (ret == VIM_ALL)
1334 {
1335 /*
1336 * Write all modified files that can be written.
1337 * Skip readonly buffers, these need to be confirmed
1338 * individually.
1339 */
1340 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1341 {
1342 if (bufIsChanged(buf2)
1343 && (buf2->b_ffname != NULL
1344#ifdef FEAT_BROWSE
1345 || cmdmod.browse
1346#endif
1347 )
1348 && !buf2->b_p_ro)
1349 {
1350#ifdef FEAT_BROWSE
1351 /* May get file name, when there is none */
1352 browse_save_fname(buf2);
1353#endif
1354 if (buf2->b_fname != NULL) /* didn't hit Cancel */
1355 (void)buf_write_all(buf2, FALSE);
1356#ifdef FEAT_AUTOCMD
1357 /* an autocommand may have deleted the buffer */
1358 if (!buf_valid(buf2))
1359 buf2 = firstbuf;
1360#endif
1361 }
1362 }
1363 }
1364 else if (ret == VIM_DISCARDALL)
1365 {
1366 /*
1367 * mark all buffers as unchanged
1368 */
1369 for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next)
1370 unchanged(buf2, TRUE);
1371 }
1372}
1373#endif
1374
1375/*
1376 * Return TRUE if the buffer "buf" can be abandoned, either by making it
1377 * hidden, autowriting it or unloading it.
1378 */
1379 int
1380can_abandon(buf, forceit)
1381 buf_T *buf;
1382 int forceit;
1383{
1384 return ( P_HID(buf)
1385 || !bufIsChanged(buf)
1386 || buf->b_nwindows > 1
1387 || autowrite(buf, forceit) == OK
1388 || forceit);
1389}
1390
1391/*
1392 * Return TRUE if any buffer was changed and cannot be abandoned.
1393 * That changed buffer becomes the current buffer.
1394 */
1395 int
1396check_changed_any(hidden)
1397 int hidden; /* Only check hidden buffers */
1398{
1399 buf_T *buf;
1400 int save;
1401#ifdef FEAT_WINDOWS
1402 win_T *wp;
1403#endif
1404
1405 for (;;)
1406 {
1407 /* check curbuf first: if it was changed we can't abandon it */
1408 if (!hidden && curbufIsChanged())
1409 buf = curbuf;
1410 else
1411 {
1412 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1413 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
1414 break;
1415 }
1416 if (buf == NULL) /* No buffers changed */
1417 return FALSE;
1418
1419 if (check_changed(buf, p_awa, TRUE, FALSE, TRUE) && buf_valid(buf))
1420 break; /* didn't save - still changes */
1421 }
1422
1423 exiting = FALSE;
1424#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1425 /*
1426 * When ":confirm" used, don't give an error message.
1427 */
1428 if (!(p_confirm || cmdmod.confirm))
1429#endif
1430 {
1431 /* There must be a wait_return for this message, do_buffer()
1432 * may cause a redraw. But wait_return() is a no-op when vgetc()
1433 * is busy (Quit used from window menu), then make sure we don't
1434 * cause a scroll up. */
1435 if (vgetc_busy)
1436 {
1437 msg_row = cmdline_row;
1438 msg_col = 0;
1439 msg_didout = FALSE;
1440 }
1441 if (EMSG2(_("E162: No write since last change for buffer \"%s\""),
1442 buf_spname(buf) != NULL ? (char_u *)buf_spname(buf) :
1443 buf->b_fname))
1444 {
1445 save = no_wait_return;
1446 no_wait_return = FALSE;
1447 wait_return(FALSE);
1448 no_wait_return = save;
1449 }
1450 }
1451
1452#ifdef FEAT_WINDOWS
1453 /* Try to find a window that contains the buffer. */
1454 if (buf != curbuf)
1455 for (wp = firstwin; wp != NULL; wp = wp->w_next)
1456 if (wp->w_buffer == buf)
1457 {
1458 win_goto(wp);
1459# ifdef FEAT_AUTOCMD
1460 /* Paranoia: did autocms wipe out the buffer with changes? */
1461 if (!buf_valid(buf))
1462 return TRUE;
1463# endif
1464 break;
1465 }
1466#endif
1467
1468 /* Open the changed buffer in the current window. */
1469 if (buf != curbuf)
1470 set_curbuf(buf, DOBUF_GOTO);
1471
1472 return TRUE;
1473}
1474
1475/*
1476 * return FAIL if there is no file name, OK if there is one
1477 * give error message for FAIL
1478 */
1479 int
1480check_fname()
1481{
1482 if (curbuf->b_ffname == NULL)
1483 {
1484 EMSG(_(e_noname));
1485 return FAIL;
1486 }
1487 return OK;
1488}
1489
1490/*
1491 * flush the contents of a buffer, unless it has no file name
1492 *
1493 * return FAIL for failure, OK otherwise
1494 */
1495 int
1496buf_write_all(buf, forceit)
1497 buf_T *buf;
1498 int forceit;
1499{
1500 int retval;
1501#ifdef FEAT_AUTOCMD
1502 buf_T *old_curbuf = curbuf;
1503#endif
1504
1505 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
1506 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
1507 FALSE, forceit, TRUE, FALSE));
1508#ifdef FEAT_AUTOCMD
1509 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001510 {
1511 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001513 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514#endif
1515 return retval;
1516}
1517
1518/*
1519 * Code to handle the argument list.
1520 */
1521
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001522static char_u *do_one_arg __ARGS((char_u *str));
1523static int do_arglist __ARGS((char_u *str, int what, int after));
1524static void alist_check_arg_idx __ARGS((void));
1525static int editing_arg_idx __ARGS((win_T *win));
1526#ifdef FEAT_LISTCMDS
1527static int alist_add_list __ARGS((int count, char_u **files, int after));
1528#endif
1529#define AL_SET 1
1530#define AL_ADD 2
1531#define AL_DEL 3
1532
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001534 * Isolate one argument, taking backticks.
1535 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 * Return a pointer to the start of the next argument.
1537 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001538 static char_u *
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539do_one_arg(str)
1540 char_u *str;
1541{
1542 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543 int inbacktick;
1544
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 inbacktick = FALSE;
1546 for (p = str; *str; ++str)
1547 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001548 /* When the backslash is used for escaping the special meaning of a
1549 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 if (rem_backslash(str))
1551 {
1552 *p++ = *str++;
1553 *p++ = *str;
1554 }
1555 else
1556 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001557 /* An item ends at a space not in backticks */
1558 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001560 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001562 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563 }
1564 }
1565 str = skipwhite(str);
1566 *p = NUL;
1567
1568 return str;
1569}
1570
Bram Moolenaar86b68352004-12-27 21:59:20 +00001571/*
1572 * Separate the arguments in "str" and return a list of pointers in the
1573 * growarray "gap".
1574 */
1575 int
1576get_arglist(gap, str)
1577 garray_T *gap;
1578 char_u *str;
1579{
1580 ga_init2(gap, (int)sizeof(char_u *), 20);
1581 while (*str != NUL)
1582 {
1583 if (ga_grow(gap, 1) == FAIL)
1584 {
1585 ga_clear(gap);
1586 return FAIL;
1587 }
1588 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
1589
1590 /* Isolate one argument, change it in-place, put a NUL after it. */
1591 str = do_one_arg(str);
1592 }
1593 return OK;
1594}
1595
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001596#if defined(FEAT_QUICKFIX) || (defined(FEAT_SYN_HL) && defined(FEAT_MBYTE)) \
1597 || defined(PROTO)
1598/*
1599 * Parse a list of arguments (file names), expand them and return in
1600 * "fnames[fcountp]".
1601 * Return FAIL or OK.
1602 */
1603 int
1604get_arglist_exp(str, fcountp, fnamesp)
1605 char_u *str;
1606 int *fcountp;
1607 char_u ***fnamesp;
1608{
1609 garray_T ga;
1610 int i;
1611
1612 if (get_arglist(&ga, str) == FAIL)
1613 return FAIL;
1614 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
1615 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
1616 ga_clear(&ga);
1617 return i;
1618}
1619#endif
1620
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621#if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
1622/*
1623 * Redefine the argument list.
1624 */
1625 void
1626set_arglist(str)
1627 char_u *str;
1628{
1629 do_arglist(str, AL_SET, 0);
1630}
1631#endif
1632
1633/*
1634 * "what" == AL_SET: Redefine the argument list to 'str'.
1635 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
1636 * "what" == AL_DEL: remove files in 'str' from the argument list.
1637 *
1638 * Return FAIL for failure, OK otherwise.
1639 */
1640/*ARGSUSED*/
1641 static int
1642do_arglist(str, what, after)
1643 char_u *str;
1644 int what;
1645 int after; /* 0 means before first one */
1646{
1647 garray_T new_ga;
1648 int exp_count;
1649 char_u **exp_files;
1650 int i;
1651#ifdef FEAT_LISTCMDS
1652 char_u *p;
1653 int match;
1654#endif
1655
1656 /*
1657 * Collect all file name arguments in "new_ga".
1658 */
Bram Moolenaar86b68352004-12-27 21:59:20 +00001659 if (get_arglist(&new_ga, str) == FAIL)
1660 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661
1662#ifdef FEAT_LISTCMDS
1663 if (what == AL_DEL)
1664 {
1665 regmatch_T regmatch;
1666 int didone;
1667
1668 /*
1669 * Delete the items: use each item as a regexp and find a match in the
1670 * argument list.
1671 */
1672#ifdef CASE_INSENSITIVE_FILENAME
1673 regmatch.rm_ic = TRUE; /* Always ignore case */
1674#else
1675 regmatch.rm_ic = FALSE; /* Never ignore case */
1676#endif
1677 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
1678 {
1679 p = ((char_u **)new_ga.ga_data)[i];
1680 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
1681 if (p == NULL)
1682 break;
1683 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1684 if (regmatch.regprog == NULL)
1685 {
1686 vim_free(p);
1687 break;
1688 }
1689
1690 didone = FALSE;
1691 for (match = 0; match < ARGCOUNT; ++match)
1692 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1693 (colnr_T)0))
1694 {
1695 didone = TRUE;
1696 vim_free(ARGLIST[match].ae_fname);
1697 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
1698 (ARGCOUNT - match - 1) * sizeof(aentry_T));
1699 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001700 if (curwin->w_arg_idx > match)
1701 --curwin->w_arg_idx;
1702 --match;
1703 }
1704
1705 vim_free(regmatch.regprog);
1706 vim_free(p);
1707 if (!didone)
1708 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
1709 }
1710 ga_clear(&new_ga);
1711 }
1712 else
1713#endif
1714 {
1715 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1716 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1717 ga_clear(&new_ga);
1718 if (i == FAIL)
1719 return FAIL;
1720 if (exp_count == 0)
1721 {
1722 EMSG(_(e_nomatch));
1723 return FAIL;
1724 }
1725
1726#ifdef FEAT_LISTCMDS
1727 if (what == AL_ADD)
1728 {
1729 (void)alist_add_list(exp_count, exp_files, after);
1730 vim_free(exp_files);
1731 }
1732 else /* what == AL_SET */
1733#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00001734 alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735 }
1736
1737 alist_check_arg_idx();
1738
1739 return OK;
1740}
1741
1742/*
1743 * Check the validity of the arg_idx for each other window.
1744 */
1745 static void
1746alist_check_arg_idx()
1747{
1748#ifdef FEAT_WINDOWS
1749 win_T *win;
1750
1751 for (win = firstwin; win != NULL; win = win->w_next)
1752 if (win->w_alist == curwin->w_alist)
1753 check_arg_idx(win);
1754#else
1755 check_arg_idx(curwin);
1756#endif
1757}
1758
1759/*
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001760 * Return TRUE if window "win" is editing then file at the current argument
1761 * index.
1762 */
1763 static int
1764editing_arg_idx(win)
1765 win_T *win;
1766{
1767 return !(win->w_arg_idx >= WARGCOUNT(win)
1768 || (win->w_buffer->b_fnum
1769 != WARGLIST(win)[win->w_arg_idx].ae_fnum
1770 && (win->w_buffer->b_ffname == NULL
1771 || !(fullpathcmp(
1772 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1773 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
1774}
1775
1776/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001777 * Check if window "win" is editing the w_arg_idx file in its argument list.
1778 */
1779 void
1780check_arg_idx(win)
1781 win_T *win;
1782{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001783 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 {
1785 /* We are not editing the current entry in the argument list.
1786 * Set "arg_had_last" if we are editing the last one. */
1787 win->w_arg_idx_invalid = TRUE;
1788 if (win->w_arg_idx != WARGCOUNT(win) - 1
1789 && arg_had_last == FALSE
1790#ifdef FEAT_WINDOWS
1791 && ALIST(win) == &global_alist
1792#endif
1793 && GARGCOUNT > 0
1794 && win->w_arg_idx < GARGCOUNT
1795 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1796 || (win->w_buffer->b_ffname != NULL
1797 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
1798 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
1799 arg_had_last = TRUE;
1800 }
1801 else
1802 {
1803 /* We are editing the current entry in the argument list.
1804 * Set "arg_had_last" if it's also the last one */
1805 win->w_arg_idx_invalid = FALSE;
1806 if (win->w_arg_idx == WARGCOUNT(win) - 1
1807#ifdef FEAT_WINDOWS
1808 && win->w_alist == &global_alist
1809#endif
1810 )
1811 arg_had_last = TRUE;
1812 }
1813}
1814
1815/*
1816 * ":args", ":argslocal" and ":argsglobal".
1817 */
1818 void
1819ex_args(eap)
1820 exarg_T *eap;
1821{
1822 int i;
1823
1824 if (eap->cmdidx != CMD_args)
1825 {
1826#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1827 alist_unlink(ALIST(curwin));
1828 if (eap->cmdidx == CMD_argglobal)
1829 ALIST(curwin) = &global_alist;
1830 else /* eap->cmdidx == CMD_arglocal */
1831 alist_new();
1832#else
1833 ex_ni(eap);
1834 return;
1835#endif
1836 }
1837
1838 if (!ends_excmd(*eap->arg))
1839 {
1840 /*
1841 * ":args file ..": define new argument list, handle like ":next"
1842 * Also for ":argslocal file .." and ":argsglobal file ..".
1843 */
1844 ex_next(eap);
1845 }
1846 else
1847#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1848 if (eap->cmdidx == CMD_args)
1849#endif
1850 {
1851 /*
1852 * ":args": list arguments.
1853 */
1854 if (ARGCOUNT > 0)
1855 {
1856 /* Overwrite the command, for a short list there is no scrolling
1857 * required and no wait_return(). */
1858 gotocmdline(TRUE);
1859 for (i = 0; i < ARGCOUNT; ++i)
1860 {
1861 if (i == curwin->w_arg_idx)
1862 msg_putchar('[');
1863 msg_outtrans(alist_name(&ARGLIST[i]));
1864 if (i == curwin->w_arg_idx)
1865 msg_putchar(']');
1866 msg_putchar(' ');
1867 }
1868 }
1869 }
1870#if defined(FEAT_WINDOWS) && defined(FEAT_LISTCMDS)
1871 else if (eap->cmdidx == CMD_arglocal)
1872 {
1873 garray_T *gap = &curwin->w_alist->al_ga;
1874
1875 /*
1876 * ":argslocal": make a local copy of the global argument list.
1877 */
1878 if (ga_grow(gap, GARGCOUNT) == OK)
1879 for (i = 0; i < GARGCOUNT; ++i)
1880 if (GARGLIST[i].ae_fname != NULL)
1881 {
1882 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1883 vim_strsave(GARGLIST[i].ae_fname);
1884 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1885 GARGLIST[i].ae_fnum;
1886 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 }
1888 }
1889#endif
1890}
1891
1892/*
1893 * ":previous", ":sprevious", ":Next" and ":sNext".
1894 */
1895 void
1896ex_previous(eap)
1897 exarg_T *eap;
1898{
1899 /* If past the last one already, go to the last one. */
1900 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
1901 do_argfile(eap, ARGCOUNT - 1);
1902 else
1903 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1904}
1905
1906/*
1907 * ":rewind", ":first", ":sfirst" and ":srewind".
1908 */
1909 void
1910ex_rewind(eap)
1911 exarg_T *eap;
1912{
1913 do_argfile(eap, 0);
1914}
1915
1916/*
1917 * ":last" and ":slast".
1918 */
1919 void
1920ex_last(eap)
1921 exarg_T *eap;
1922{
1923 do_argfile(eap, ARGCOUNT - 1);
1924}
1925
1926/*
1927 * ":argument" and ":sargument".
1928 */
1929 void
1930ex_argument(eap)
1931 exarg_T *eap;
1932{
1933 int i;
1934
1935 if (eap->addr_count > 0)
1936 i = eap->line2 - 1;
1937 else
1938 i = curwin->w_arg_idx;
1939 do_argfile(eap, i);
1940}
1941
1942/*
1943 * Edit file "argn" of the argument lists.
1944 */
1945 void
1946do_argfile(eap, argn)
1947 exarg_T *eap;
1948 int argn;
1949{
1950 int other;
1951 char_u *p;
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00001952 int old_arg_idx = curwin->w_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953
1954 if (argn < 0 || argn >= ARGCOUNT)
1955 {
1956 if (ARGCOUNT <= 1)
1957 EMSG(_("E163: There is only one file to edit"));
1958 else if (argn < 0)
1959 EMSG(_("E164: Cannot go before first file"));
1960 else
1961 EMSG(_("E165: Cannot go beyond last file"));
1962 }
1963 else
1964 {
1965 setpcmark();
1966#ifdef FEAT_GUI
1967 need_mouse_correct = TRUE;
1968#endif
1969
1970#ifdef FEAT_WINDOWS
1971 if (*eap->cmd == 's') /* split window first */
1972 {
1973 if (win_split(0, 0) == FAIL)
1974 return;
1975# ifdef FEAT_SCROLLBIND
1976 curwin->w_p_scb = FALSE;
1977# endif
1978 }
1979 else
1980#endif
1981 {
1982 /*
1983 * if 'hidden' set, only check for changed file when re-editing
1984 * the same buffer
1985 */
1986 other = TRUE;
1987 if (P_HID(curbuf))
1988 {
1989 p = fix_fname(alist_name(&ARGLIST[argn]));
1990 other = otherfile(p);
1991 vim_free(p);
1992 }
1993 if ((!P_HID(curbuf) || !other)
1994 && check_changed(curbuf, TRUE, !other, eap->forceit, FALSE))
1995 return;
1996 }
1997
1998 curwin->w_arg_idx = argn;
1999 if (argn == ARGCOUNT - 1
2000#ifdef FEAT_WINDOWS
2001 && curwin->w_alist == &global_alist
2002#endif
2003 )
2004 arg_had_last = TRUE;
2005
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002006 /* Edit the file; always use the last known line number.
2007 * When it fails (e.g. Abort for already edited file) restore the
2008 * argument index. */
2009 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 eap, ECMD_LAST,
2011 (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) +
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002012 (eap->forceit ? ECMD_FORCEIT : 0)) == FAIL)
2013 curwin->w_arg_idx = old_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 /* like Vi: set the mark where the cursor is in the file. */
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002015 else if (eap->cmdidx != CMD_argdo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 setmark('\'');
2017 }
2018}
2019
2020/*
2021 * ":next", and commands that behave like it.
2022 */
2023 void
2024ex_next(eap)
2025 exarg_T *eap;
2026{
2027 int i;
2028
2029 /*
2030 * check for changed buffer now, if this fails the argument list is not
2031 * redefined.
2032 */
2033 if ( P_HID(curbuf)
2034 || eap->cmdidx == CMD_snext
2035 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
2036 {
2037 if (*eap->arg != NUL) /* redefine file list */
2038 {
2039 if (do_arglist(eap->arg, AL_SET, 0) == FAIL)
2040 return;
2041 i = 0;
2042 }
2043 else
2044 i = curwin->w_arg_idx + (int)eap->line2;
2045 do_argfile(eap, i);
2046 }
2047}
2048
2049#ifdef FEAT_LISTCMDS
2050/*
2051 * ":argedit"
2052 */
2053 void
2054ex_argedit(eap)
2055 exarg_T *eap;
2056{
2057 int fnum;
2058 int i;
2059 char_u *s;
2060
2061 /* Add the argument to the buffer list and get the buffer number. */
2062 fnum = buflist_add(eap->arg, BLN_LISTED);
2063
2064 /* Check if this argument is already in the argument list. */
2065 for (i = 0; i < ARGCOUNT; ++i)
2066 if (ARGLIST[i].ae_fnum == fnum)
2067 break;
2068 if (i == ARGCOUNT)
2069 {
2070 /* Can't find it, add it to the argument list. */
2071 s = vim_strsave(eap->arg);
2072 if (s == NULL)
2073 return;
2074 i = alist_add_list(1, &s,
2075 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
2076 if (i < 0)
2077 return;
2078 curwin->w_arg_idx = i;
2079 }
2080
2081 alist_check_arg_idx();
2082
2083 /* Edit the argument. */
2084 do_argfile(eap, i);
2085}
2086
2087/*
2088 * ":argadd"
2089 */
2090 void
2091ex_argadd(eap)
2092 exarg_T *eap;
2093{
2094 do_arglist(eap->arg, AL_ADD,
2095 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
2096#ifdef FEAT_TITLE
2097 maketitle();
2098#endif
2099}
2100
2101/*
2102 * ":argdelete"
2103 */
2104 void
2105ex_argdelete(eap)
2106 exarg_T *eap;
2107{
2108 int i;
2109 int n;
2110
2111 if (eap->addr_count > 0)
2112 {
2113 /* ":1,4argdel": Delete all arguments in the range. */
2114 if (eap->line2 > ARGCOUNT)
2115 eap->line2 = ARGCOUNT;
2116 n = eap->line2 - eap->line1 + 1;
2117 if (*eap->arg != NUL || n <= 0)
2118 EMSG(_(e_invarg));
2119 else
2120 {
2121 for (i = eap->line1; i <= eap->line2; ++i)
2122 vim_free(ARGLIST[i - 1].ae_fname);
2123 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
2124 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
2125 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002126 if (curwin->w_arg_idx >= eap->line2)
2127 curwin->w_arg_idx -= n;
2128 else if (curwin->w_arg_idx > eap->line1)
2129 curwin->w_arg_idx = eap->line1;
2130 }
2131 }
2132 else if (*eap->arg == NUL)
2133 EMSG(_(e_argreq));
2134 else
2135 do_arglist(eap->arg, AL_DEL, 0);
2136#ifdef FEAT_TITLE
2137 maketitle();
2138#endif
2139}
2140
2141/*
2142 * ":argdo", ":windo", ":bufdo"
2143 */
2144 void
2145ex_listdo(eap)
2146 exarg_T *eap;
2147{
2148 int i;
2149#ifdef FEAT_WINDOWS
2150 win_T *win;
2151#endif
2152 buf_T *buf;
2153 int next_fnum = 0;
2154#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
2155 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002157 char_u *p_shm_save;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158
2159#ifndef FEAT_WINDOWS
2160 if (eap->cmdidx == CMD_windo)
2161 {
2162 ex_ni(eap);
2163 return;
2164 }
2165#endif
2166
2167#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
2168 if (eap->cmdidx != CMD_windo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00002169 /* Don't do syntax HL autocommands. Skipping the syntax file is a
2170 * great speed improvement. */
2171 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002172#endif
2173
2174 if (eap->cmdidx == CMD_windo
2175 || P_HID(curbuf)
2176 || !check_changed(curbuf, TRUE, FALSE, eap->forceit, FALSE))
2177 {
2178 /* start at the first argument/window/buffer */
2179 i = 0;
2180#ifdef FEAT_WINDOWS
2181 win = firstwin;
2182#endif
2183 /* set pcmark now */
2184 if (eap->cmdidx == CMD_bufdo)
2185 goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
2186 else
2187 setpcmark();
2188 listcmd_busy = TRUE; /* avoids setting pcmark below */
2189
2190 while (!got_int)
2191 {
2192 if (eap->cmdidx == CMD_argdo)
2193 {
2194 /* go to argument "i" */
2195 if (i == ARGCOUNT)
2196 break;
2197 /* Don't call do_argfile() when already there, it will try
2198 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002199 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002200 {
2201 /* Clear 'shm' to avoid that the file message overwrites
2202 * any output from the command. */
2203 p_shm_save = vim_strsave(p_shm);
2204 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002206 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2207 vim_free(p_shm_save);
2208 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 if (curwin->w_arg_idx != i)
2210 break;
2211 ++i;
2212 }
2213#ifdef FEAT_WINDOWS
2214 else if (eap->cmdidx == CMD_windo)
2215 {
2216 /* go to window "win" */
2217 if (!win_valid(win))
2218 break;
2219 win_goto(win);
2220 win = win->w_next;
2221 }
2222#endif
2223 else if (eap->cmdidx == CMD_bufdo)
2224 {
2225 /* Remember the number of the next listed buffer, in case
2226 * ":bwipe" is used or autocommands do something strange. */
2227 next_fnum = -1;
2228 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
2229 if (buf->b_p_bl)
2230 {
2231 next_fnum = buf->b_fnum;
2232 break;
2233 }
2234 }
2235
2236 /* execute the command */
2237 do_cmdline(eap->arg, eap->getline, eap->cookie,
2238 DOCMD_VERBOSE + DOCMD_NOWAIT);
2239
2240 if (eap->cmdidx == CMD_bufdo)
2241 {
2242 /* Done? */
2243 if (next_fnum < 0)
2244 break;
2245 /* Check if the buffer still exists. */
2246 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2247 if (buf->b_fnum == next_fnum)
2248 break;
2249 if (buf == NULL)
2250 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002251
2252 /* Go to the next buffer. Clear 'shm' to avoid that the file
2253 * message overwrites any output from the command. */
2254 p_shm_save = vim_strsave(p_shm);
2255 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002257 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2258 vim_free(p_shm_save);
2259
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260 /* If autocommands took us elsewhere, quit here */
2261 if (curbuf->b_fnum != next_fnum)
2262 break;
2263 }
2264
2265 if (eap->cmdidx == CMD_windo)
2266 {
2267 validate_cursor(); /* cursor may have moved */
2268#ifdef FEAT_SCROLLBIND
2269 /* required when 'scrollbind' has been set */
2270 if (curwin->w_p_scb)
2271 do_check_scrollbind(TRUE);
2272#endif
2273 }
2274 }
2275 listcmd_busy = FALSE;
2276 }
2277
2278#if defined(FEAT_AUTOCMD) && defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002279 if (save_ei != NULL)
2280 {
2281 au_event_restore(save_ei);
2282 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
2283 curbuf->b_fname, TRUE, curbuf);
2284 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285#endif
2286}
2287
2288/*
2289 * Add files[count] to the arglist of the current window after arg "after".
2290 * The file names in files[count] must have been allocated and are taken over.
2291 * Files[] itself is not taken over.
2292 * Returns index of first added argument. Returns -1 when failed (out of mem).
2293 */
2294 static int
2295alist_add_list(count, files, after)
2296 int count;
2297 char_u **files;
2298 int after; /* where to add: 0 = before first one */
2299{
2300 int i;
2301
2302 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
2303 {
2304 if (after < 0)
2305 after = 0;
2306 if (after > ARGCOUNT)
2307 after = ARGCOUNT;
2308 if (after < ARGCOUNT)
2309 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
2310 (ARGCOUNT - after) * sizeof(aentry_T));
2311 for (i = 0; i < count; ++i)
2312 {
2313 ARGLIST[after + i].ae_fname = files[i];
2314 ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
2315 }
2316 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317 if (curwin->w_arg_idx >= after)
2318 ++curwin->w_arg_idx;
2319 return after;
2320 }
2321
2322 for (i = 0; i < count; ++i)
2323 vim_free(files[i]);
2324 return -1;
2325}
2326
2327#endif /* FEAT_LISTCMDS */
2328
2329#ifdef FEAT_EVAL
2330/*
2331 * ":compiler[!] {name}"
2332 */
2333 void
2334ex_compiler(eap)
2335 exarg_T *eap;
2336{
2337 char_u *buf;
2338 char_u *old_cur_comp = NULL;
2339 char_u *p;
2340
2341 if (*eap->arg == NUL)
2342 {
2343 /* List all compiler scripts. */
2344 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
2345 /* ) keep the indenter happy... */
2346 }
2347 else
2348 {
2349 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
2350 if (buf != NULL)
2351 {
2352 if (eap->forceit)
2353 {
2354 /* ":compiler! {name}" sets global options */
2355 do_cmdline_cmd((char_u *)
2356 "command -nargs=* CompilerSet set <args>");
2357 }
2358 else
2359 {
2360 /* ":compiler! {name}" sets local options.
2361 * To remain backwards compatible "current_compiler" is always
2362 * used. A user's compiler plugin may set it, the distributed
2363 * plugin will then skip the settings. Afterwards set
2364 * "b:current_compiler" and restore "current_compiler". */
2365 old_cur_comp = get_var_value((char_u *)"current_compiler");
2366 if (old_cur_comp != NULL)
2367 old_cur_comp = vim_strsave(old_cur_comp);
2368 do_cmdline_cmd((char_u *)
2369 "command -nargs=* CompilerSet setlocal <args>");
2370 }
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00002371 do_unlet((char_u *)"current_compiler", TRUE);
2372 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373
2374 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
2375 if (cmd_runtime(buf, TRUE) == FAIL)
2376 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
2377 vim_free(buf);
2378
2379 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
2380
2381 /* Set "b:current_compiler" from "current_compiler". */
2382 p = get_var_value((char_u *)"current_compiler");
2383 if (p != NULL)
2384 set_internal_string_var((char_u *)"b:current_compiler", p);
2385
2386 /* Restore "current_compiler" for ":compiler {name}". */
2387 if (!eap->forceit)
2388 {
2389 if (old_cur_comp != NULL)
2390 {
2391 set_internal_string_var((char_u *)"current_compiler",
2392 old_cur_comp);
2393 vim_free(old_cur_comp);
2394 }
2395 else
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00002396 do_unlet((char_u *)"current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002397 }
2398 }
2399 }
2400}
2401#endif
2402
2403/*
2404 * ":runtime {name}"
2405 */
2406 void
2407ex_runtime(eap)
2408 exarg_T *eap;
2409{
2410 cmd_runtime(eap->arg, eap->forceit);
2411}
2412
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002413static void source_callback __ARGS((char_u *fname, void *cookie));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002415/*ARGSUSED*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 static void
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002417source_callback(fname, cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 char_u *fname;
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002419 void *cookie;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420{
2421 (void)do_source(fname, FALSE, FALSE);
2422}
2423
2424/*
2425 * Source the file "name" from all directories in 'runtimepath'.
2426 * "name" can contain wildcards.
2427 * When "all" is TRUE, source all files, otherwise only the first one.
2428 * return FAIL when no file could be sourced, OK otherwise.
2429 */
2430 int
2431cmd_runtime(name, all)
2432 char_u *name;
2433 int all;
2434{
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002435 return do_in_runtimepath(name, all, source_callback, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002436}
2437
2438/*
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002439 * Find "name" in 'runtimepath'. When found, invoke the callback function for
2440 * it: callback(fname, "cookie")
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441 * When "all" is TRUE repeat for all matches, otherwise only the first one is
2442 * used.
2443 * Returns OK when at least one match found, FAIL otherwise.
2444 */
2445 int
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002446do_in_runtimepath(name, all, callback, cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 char_u *name;
2448 int all;
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002449 void (*callback)__ARGS((char_u *fname, void *ck));
2450 void *cookie;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451{
2452 char_u *rtp;
2453 char_u *np;
2454 char_u *buf;
2455 char_u *rtp_copy;
2456 char_u *tail;
2457 int num_files;
2458 char_u **files;
2459 int i;
2460 int did_one = FALSE;
2461#ifdef AMIGA
2462 struct Process *proc = (struct Process *)FindTask(0L);
2463 APTR save_winptr = proc->pr_WindowPtr;
2464
2465 /* Avoid a requester here for a volume that doesn't exist. */
2466 proc->pr_WindowPtr = (APTR)-1L;
2467#endif
2468
2469 /* Make a copy of 'runtimepath'. Invoking the callback may change the
2470 * value. */
2471 rtp_copy = vim_strsave(p_rtp);
2472 buf = alloc(MAXPATHL);
2473 if (buf != NULL && rtp_copy != NULL)
2474 {
2475 if (p_verbose > 1)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002476 {
2477 if (STRLEN(name) + STRLEN(p_rtp) > IOSIZE - 100)
2478 MSG(_("Searching for a long name in 'runtimepath'"));
2479 else
2480 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 (char *)name, (char *)p_rtp);
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002482 }
2483
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 /* Loop over all entries in 'runtimepath'. */
2485 rtp = rtp_copy;
2486 while (*rtp != NUL && (all || !did_one))
2487 {
2488 /* Copy the path from 'runtimepath' to buf[]. */
2489 copy_option_part(&rtp, buf, MAXPATHL, ",");
2490 if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL)
2491 {
2492 add_pathsep(buf);
2493 tail = buf + STRLEN(buf);
2494
2495 /* Loop over all patterns in "name" */
2496 np = name;
2497 while (*np != NUL && (all || !did_one))
2498 {
2499 /* Append the pattern from "name" to buf[]. */
2500 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
2501 "\t ");
2502
2503 if (p_verbose > 2)
2504 msg_str((char_u *)_("Searching for \"%s\""), buf);
2505
2506 /* Expand wildcards, invoke the callback for each match. */
2507 if (gen_expand_wildcards(1, &buf, &num_files, &files,
2508 EW_FILE) == OK)
2509 {
2510 for (i = 0; i < num_files; ++i)
2511 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002512 (*callback)(files[i], cookie);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 did_one = TRUE;
2514 if (!all)
2515 break;
2516 }
2517 FreeWild(num_files, files);
2518 }
2519 }
2520 }
2521 }
2522 }
2523 vim_free(buf);
2524 vim_free(rtp_copy);
2525 if (p_verbose > 0 && !did_one)
2526 msg_str((char_u *)_("not found in 'runtimepath': \"%s\""), name);
2527
2528#ifdef AMIGA
2529 proc->pr_WindowPtr = save_winptr;
2530#endif
2531
2532 return did_one ? OK : FAIL;
2533}
2534
2535#if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
2536/*
2537 * ":options"
2538 */
2539/*ARGSUSED*/
2540 void
2541ex_options(eap)
2542 exarg_T *eap;
2543{
2544 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
2545}
2546#endif
2547
2548/*
2549 * ":source {fname}"
2550 */
2551 void
2552ex_source(eap)
2553 exarg_T *eap;
2554{
2555#ifdef FEAT_BROWSE
2556 if (cmdmod.browse)
2557 {
2558 char_u *fname = NULL;
2559
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002560 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 NULL, NULL, BROWSE_FILTER_MACROS, NULL);
2562 if (fname != NULL)
2563 {
2564 cmd_source(fname, eap);
2565 vim_free(fname);
2566 }
2567 }
2568 else
2569#endif
2570 cmd_source(eap->arg, eap);
2571}
2572
2573 static void
2574cmd_source(fname, eap)
2575 char_u *fname;
2576 exarg_T *eap;
2577{
2578 if (*fname == NUL)
2579 EMSG(_(e_argreq));
2580
2581 /* ":source!" read vi commands */
2582 else if (eap != NULL && eap->forceit)
2583 /* Need to execute the commands directly when:
2584 * - ":g" command busy
2585 * - after ":argdo", ":windo" or ":bufdo"
2586 * - another command follows
2587 * - inside a loop
2588 */
2589 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2590#ifdef FEAT_EVAL
2591 || eap->cstack->cs_idx >= 0
2592#endif
2593 );
2594
2595 /* ":source" read ex commands */
2596 else if (do_source(fname, FALSE, FALSE) == FAIL)
2597 EMSG2(_(e_notopen), fname);
2598}
2599
2600/*
2601 * ":source" and associated commands.
2602 */
2603/*
2604 * Structure used to store info for each sourced file.
2605 * It is shared between do_source() and getsourceline().
2606 * This is required, because it needs to be handed to do_cmdline() and
2607 * sourcing can be done recursively.
2608 */
2609struct source_cookie
2610{
2611 FILE *fp; /* opened file for sourcing */
2612 char_u *nextline; /* if not NULL: line that was read ahead */
2613 int finished; /* ":finish" used */
2614#if defined (USE_CRNL) || defined (USE_CR)
2615 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
2616 int error; /* TRUE if LF found after CR-LF */
2617#endif
2618#ifdef FEAT_EVAL
2619 linenr_T breakpoint; /* next line with breakpoint or zero */
2620 char_u *fname; /* name of sourced file */
2621 int dbg_tick; /* debug_tick when breakpoint was set */
2622 int level; /* top nesting level of sourced file */
2623#endif
2624#ifdef FEAT_MBYTE
2625 vimconv_T conv; /* type of conversion */
2626#endif
2627};
2628
2629#ifdef FEAT_EVAL
2630/*
2631 * Return the address holding the next breakpoint line for a source cookie.
2632 */
2633 linenr_T *
2634source_breakpoint(cookie)
2635 void *cookie;
2636{
2637 return &((struct source_cookie *)cookie)->breakpoint;
2638}
2639
2640/*
2641 * Return the address holding the debug tick for a source cookie.
2642 */
2643 int *
2644source_dbg_tick(cookie)
2645 void *cookie;
2646{
2647 return &((struct source_cookie *)cookie)->dbg_tick;
2648}
2649
2650/*
2651 * Return the nesting level for a source cookie.
2652 */
2653 int
2654source_level(cookie)
2655 void *cookie;
2656{
2657 return ((struct source_cookie *)cookie)->level;
2658}
2659#endif
2660
2661static char_u *get_one_sourceline __ARGS((struct source_cookie *sp));
2662
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663#if defined(WIN32) && defined(FEAT_CSCOPE)
2664static FILE *fopen_noinh_readbin __ARGS((char *filename));
2665
2666/*
2667 * Special function to open a file without handle inheritance.
2668 */
2669 static FILE *
2670fopen_noinh_readbin(filename)
2671 char *filename;
2672{
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00002673 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674
2675 if (fd_tmp == -1)
2676 return NULL;
2677 return fdopen(fd_tmp, READBIN);
2678}
2679#endif
2680
2681
2682/*
2683 * do_source: Read the file "fname" and execute its lines as EX commands.
2684 *
2685 * This function may be called recursively!
2686 *
2687 * return FAIL if file could not be opened, OK otherwise
2688 */
2689 int
2690do_source(fname, check_other, is_vimrc)
2691 char_u *fname;
2692 int check_other; /* check for .vimrc and _vimrc */
2693 int is_vimrc; /* call vimrc_found() when file exists */
2694{
2695 struct source_cookie cookie;
2696 char_u *save_sourcing_name;
2697 linenr_T save_sourcing_lnum;
2698 char_u *p;
2699 char_u *fname_exp;
2700 int retval = FAIL;
2701#ifdef FEAT_EVAL
2702 scid_T save_current_SID;
2703 static scid_T last_current_SID = 0;
2704 void *save_funccalp;
2705 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002706 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707# ifdef UNIX
2708 struct stat st;
2709 int stat_ok;
2710# endif
2711#endif
2712#ifdef STARTUPTIME
2713 struct timeval tv_rel;
2714 struct timeval tv_start;
2715#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002716#ifdef FEAT_PROFILE
2717 proftime_T wait_start;
2718#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719
2720#ifdef RISCOS
2721 p = mch_munge_fname(fname);
2722#else
2723 p = expand_env_save(fname);
2724#endif
2725 if (p == NULL)
2726 return retval;
2727 fname_exp = fix_fname(p);
2728 vim_free(p);
2729 if (fname_exp == NULL)
2730 return retval;
2731#ifdef MACOS_CLASSIC
2732 slash_n_colon_adjust(fname_exp);
2733#endif
2734 if (mch_isdir(fname_exp))
2735 {
2736 msg_str((char_u *)_("Cannot source a directory: \"%s\""), fname);
2737 goto theend;
2738 }
2739
2740#if defined(WIN32) && defined(FEAT_CSCOPE)
2741 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2742#else
2743 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2744#endif
2745 if (cookie.fp == NULL && check_other)
2746 {
2747 /*
2748 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
2749 * and ".exrc" by "_exrc" or vice versa.
2750 */
2751 p = gettail(fname_exp);
2752 if ((*p == '.' || *p == '_')
2753 && (STRICMP(p + 1, "vimrc") == 0
2754 || STRICMP(p + 1, "gvimrc") == 0
2755 || STRICMP(p + 1, "exrc") == 0))
2756 {
2757 if (*p == '_')
2758 *p = '.';
2759 else
2760 *p = '_';
2761#if defined(WIN32) && defined(FEAT_CSCOPE)
2762 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
2763#else
2764 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
2765#endif
2766 }
2767 }
2768
2769 if (cookie.fp == NULL)
2770 {
2771 if (p_verbose > 0)
2772 {
2773 if (sourcing_name == NULL)
2774 msg_str((char_u *)_("could not source \"%s\""), fname);
2775 else
2776 smsg((char_u *)_("line %ld: could not source \"%s\""),
2777 sourcing_lnum, fname);
2778 }
2779 goto theend;
2780 }
2781
2782 /*
2783 * The file exists.
2784 * - In verbose mode, give a message.
2785 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
2786 */
2787 if (p_verbose > 1)
2788 {
2789 if (sourcing_name == NULL)
2790 msg_str((char_u *)_("sourcing \"%s\""), fname);
2791 else
2792 smsg((char_u *)_("line %ld: sourcing \"%s\""),
2793 sourcing_lnum, fname);
2794 }
2795 if (is_vimrc)
2796 vimrc_found();
2797
2798#ifdef USE_CRNL
2799 /* If no automatic file format: Set default to CR-NL. */
2800 if (*p_ffs == NUL)
2801 cookie.fileformat = EOL_DOS;
2802 else
2803 cookie.fileformat = EOL_UNKNOWN;
2804 cookie.error = FALSE;
2805#endif
2806
2807#ifdef USE_CR
2808 /* If no automatic file format: Set default to CR. */
2809 if (*p_ffs == NUL)
2810 cookie.fileformat = EOL_MAC;
2811 else
2812 cookie.fileformat = EOL_UNKNOWN;
2813 cookie.error = FALSE;
2814#endif
2815
2816 cookie.nextline = NULL;
2817 cookie.finished = FALSE;
2818
2819#ifdef FEAT_EVAL
2820 /*
2821 * Check if this script has a breakpoint.
2822 */
2823 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
2824 cookie.fname = fname_exp;
2825 cookie.dbg_tick = debug_tick;
2826
2827 cookie.level = ex_nesting_level;
2828#endif
2829#ifdef FEAT_MBYTE
2830 cookie.conv.vc_type = CONV_NONE; /* no conversion */
2831
2832 /* Try reading the first few bytes to check for a UTF-8 BOM. */
2833 {
2834 char_u buf[3];
2835
2836 if (fread((char *)buf, sizeof(char_u), (size_t)3, cookie.fp)
2837 == (size_t)3
2838 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
2839 /* Found BOM, setup conversion and skip over it. */
2840 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
2841 else
2842 /* No BOM found, rewind. */
2843 fseek(cookie.fp, 0L, SEEK_SET);
2844 }
2845#endif
2846
2847 /*
2848 * Keep the sourcing name/lnum, for recursive calls.
2849 */
2850 save_sourcing_name = sourcing_name;
2851 sourcing_name = fname_exp;
2852 save_sourcing_lnum = sourcing_lnum;
2853 sourcing_lnum = 0;
2854
2855#ifdef STARTUPTIME
2856 time_push(&tv_rel, &tv_start);
2857#endif
2858
2859#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00002860# ifdef FEAT_PROFILE
2861 if (do_profiling)
2862 prof_child_enter(&wait_start); /* entering a child now */
2863# endif
2864
2865 /* Don't use local function variables, if called from a function.
2866 * Also starts profiling timer for nested script. */
2867 save_funccalp = save_funccal();
2868
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 /*
2870 * Check if this script was sourced before to finds its SID.
2871 * If it's new, generate a new SID.
2872 */
2873 save_current_SID = current_SID;
2874# ifdef UNIX
2875 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
2876# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002877 for (current_SID = script_items.ga_len; current_SID > 0; --current_SID)
2878 {
2879 si = &SCRIPT_ITEM(current_SID);
2880 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 && (
2882# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00002883 /* Compare dev/ino when possible, it catches symbolic
2884 * links. Also compare file names, the inode may change
2885 * when the file was edited. */
Bram Moolenaar05159a02005-02-26 23:04:13 +00002886 ((stat_ok && si->sn_dev != -1)
2887 && (si->sn_dev == st.st_dev
2888 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00002890 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002892 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 if (current_SID == 0)
2894 {
2895 current_SID = ++last_current_SID;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002896 if (ga_grow(&script_items, (int)(current_SID - script_items.ga_len))
2897 == FAIL)
2898 goto almosttheend;
2899 while (script_items.ga_len < current_SID)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00002901 ++script_items.ga_len;
2902 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
2903# ifdef FEAT_PROFILE
2904 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00002907 si = &SCRIPT_ITEM(current_SID);
2908 si->sn_name = fname_exp;
2909 fname_exp = NULL;
2910# ifdef UNIX
2911 if (stat_ok)
2912 {
2913 si->sn_dev = st.st_dev;
2914 si->sn_ino = st.st_ino;
2915 }
2916 else
2917 si->sn_dev = -1;
2918# endif
2919
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 /* Allocate the local script variables to use for this script. */
2921 new_script_vars(current_SID);
2922 }
2923
Bram Moolenaar05159a02005-02-26 23:04:13 +00002924# ifdef FEAT_PROFILE
2925 if (do_profiling)
2926 {
2927 int forceit;
2928
2929 /* Check if we do profiling for this script. */
2930 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
2931 {
2932 script_do_profile(si);
2933 si->sn_pr_force = forceit;
2934 }
2935 if (si->sn_prof_on)
2936 {
2937 ++si->sn_pr_count;
2938 profile_start(&si->sn_pr_start);
2939 profile_zero(&si->sn_pr_children);
2940 }
2941 }
2942# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943#endif
2944
2945 /*
2946 * Call do_cmdline, which will call getsourceline() to get the lines.
2947 */
2948 do_cmdline(NULL, getsourceline, (void *)&cookie,
2949 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
2950
2951 retval = OK;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002952
2953#ifdef FEAT_PROFILE
2954 if (do_profiling)
2955 {
2956 /* Get "si" again, "script_items" may have been reallocated. */
2957 si = &SCRIPT_ITEM(current_SID);
2958 if (si->sn_prof_on)
2959 {
2960 profile_end(&si->sn_pr_start);
2961 profile_sub_wait(&wait_start, &si->sn_pr_start);
2962 profile_add(&si->sn_pr_total, &si->sn_pr_start);
2963 profile_add(&si->sn_pr_self, &si->sn_pr_start);
2964 profile_sub(&si->sn_pr_self, &si->sn_pr_children);
2965 }
2966 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967#endif
2968
2969 if (got_int)
2970 EMSG(_(e_interr));
2971 sourcing_name = save_sourcing_name;
2972 sourcing_lnum = save_sourcing_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002973 if (p_verbose > 1)
2974 {
2975 msg_str((char_u *)_("finished sourcing %s"), fname);
2976 if (sourcing_name != NULL)
2977 msg_str((char_u *)_("continuing in %s"), sourcing_name);
2978 }
2979#ifdef STARTUPTIME
2980# ifdef HAVE_SNPRINTF
2981 snprintf(IObuff, IOSIZE, "sourcing %s", fname);
2982# else
2983 sprintf(IObuff, "sourcing %s", fname);
2984# endif
2985 time_msg(IObuff, &tv_start);
2986 time_pop(&tv_rel);
2987#endif
2988
2989#ifdef FEAT_EVAL
2990 /*
2991 * After a "finish" in debug mode, need to break at first command of next
2992 * sourced file.
2993 */
2994 if (save_debug_break_level > ex_nesting_level
2995 && debug_break_level == ex_nesting_level)
2996 ++debug_break_level;
2997#endif
2998
Bram Moolenaar05159a02005-02-26 23:04:13 +00002999#ifdef FEAT_EVAL
3000almosttheend:
3001 current_SID = save_current_SID;
3002 restore_funccal(save_funccalp);
3003# ifdef FEAT_PROFILE
3004 if (do_profiling)
3005 prof_child_exit(&wait_start); /* leaving a child now */
3006# endif
3007#endif
3008 fclose(cookie.fp);
3009 vim_free(cookie.nextline);
3010#ifdef FEAT_MBYTE
3011 convert_setup(&cookie.conv, NULL, NULL);
3012#endif
3013
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014theend:
3015 vim_free(fname_exp);
3016 return retval;
3017}
3018
3019#if defined(FEAT_EVAL) || defined(PROTO)
3020/*
3021 * ":scriptnames"
3022 */
3023/*ARGSUSED*/
3024 void
3025ex_scriptnames(eap)
3026 exarg_T *eap;
3027{
3028 int i;
3029
Bram Moolenaar05159a02005-02-26 23:04:13 +00003030 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
3031 if (SCRIPT_ITEM(i).sn_name != NULL)
3032 smsg((char_u *)"%3d: %s", i, SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033}
3034
3035# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
3036/*
3037 * Fix slashes in the list of script names for 'shellslash'.
3038 */
3039 void
3040scriptnames_slash_adjust()
3041{
3042 int i;
3043
Bram Moolenaar05159a02005-02-26 23:04:13 +00003044 for (i = 1; i <= script_items.ga_len; ++i)
3045 if (SCRIPT_ITEM(i).sn_name != NULL)
3046 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047}
3048# endif
3049
3050/*
3051 * Get a pointer to a script name. Used for ":verbose set".
3052 */
3053 char_u *
3054get_scriptname(id)
3055 scid_T id;
3056{
3057 if (id == SID_MODELINE)
3058 return (char_u *)"modeline";
3059 if (id == SID_CMDARG)
3060 return (char_u *)"--cmd argument";
3061 if (id == SID_CARG)
3062 return (char_u *)"-c argument";
3063 if (id == SID_ENV)
3064 return (char_u *)"environment variable";
Bram Moolenaar05159a02005-02-26 23:04:13 +00003065 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066}
Bram Moolenaar05159a02005-02-26 23:04:13 +00003067
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068#endif
3069
3070#if defined(USE_CR) || defined(PROTO)
3071
3072# if defined(__MSL__) && (__MSL__ >= 22)
3073/*
3074 * Newer version of the Metrowerks library handle DOS and UNIX files
3075 * without help.
3076 * Test with earlier versions, MSL 2.2 is the library supplied with
3077 * Codewarrior Pro 2.
3078 */
3079 char *
3080fgets_cr(s, n, stream)
3081 char *s;
3082 int n;
3083 FILE *stream;
3084{
3085 return fgets(s, n, stream);
3086}
3087# else
3088/*
3089 * Version of fgets() which also works for lines ending in a <CR> only
3090 * (Macintosh format).
3091 * For older versions of the Metrowerks library.
3092 * At least CodeWarrior 9 needed this code.
3093 */
3094 char *
3095fgets_cr(s, n, stream)
3096 char *s;
3097 int n;
3098 FILE *stream;
3099{
3100 int c = 0;
3101 int char_read = 0;
3102
3103 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
3104 {
3105 c = fgetc(stream);
3106 s[char_read++] = c;
3107 /* If the file is in DOS format, we need to skip a NL after a CR. I
3108 * thought it was the other way around, but this appears to work... */
3109 if (c == '\n')
3110 {
3111 c = fgetc(stream);
3112 if (c != '\r')
3113 ungetc(c, stream);
3114 }
3115 }
3116
3117 s[char_read] = 0;
3118 if (char_read == 0)
3119 return NULL;
3120
3121 if (feof(stream) && char_read == 1)
3122 return NULL;
3123
3124 return s;
3125}
3126# endif
3127#endif
3128
3129/*
3130 * Get one full line from a sourced file.
3131 * Called by do_cmdline() when it's called from do_source().
3132 *
3133 * Return a pointer to the line in allocated memory.
3134 * Return NULL for end-of-file or some error.
3135 */
3136/* ARGSUSED */
3137 char_u *
3138getsourceline(c, cookie, indent)
3139 int c; /* not used */
3140 void *cookie;
3141 int indent; /* not used */
3142{
3143 struct source_cookie *sp = (struct source_cookie *)cookie;
3144 char_u *line;
3145 char_u *p, *s;
3146
3147#ifdef FEAT_EVAL
3148 /* If breakpoints have been added/deleted need to check for it. */
3149 if (sp->dbg_tick < debug_tick)
3150 {
3151 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3152 sp->dbg_tick = debug_tick;
3153 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00003154# ifdef FEAT_PROFILE
3155 if (do_profiling)
3156 script_line_end();
3157# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003158#endif
3159 /*
3160 * Get current line. If there is a read-ahead line, use it, otherwise get
3161 * one now.
3162 */
3163 if (sp->finished)
3164 line = NULL;
3165 else if (sp->nextline == NULL)
3166 line = get_one_sourceline(sp);
3167 else
3168 {
3169 line = sp->nextline;
3170 sp->nextline = NULL;
3171 ++sourcing_lnum;
3172 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003173#ifdef FEAT_PROFILE
3174 if (line != NULL && do_profiling)
3175 script_line_start();
3176#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177
3178 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
3179 * contain the 'C' flag. */
3180 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
3181 {
3182 /* compensate for the one line read-ahead */
3183 --sourcing_lnum;
3184 for (;;)
3185 {
3186 sp->nextline = get_one_sourceline(sp);
3187 if (sp->nextline == NULL)
3188 break;
3189 p = skipwhite(sp->nextline);
3190 if (*p != '\\')
3191 break;
3192 s = alloc((int)(STRLEN(line) + STRLEN(p)));
3193 if (s == NULL) /* out of memory */
3194 break;
3195 STRCPY(s, line);
3196 STRCAT(s, p + 1);
3197 vim_free(line);
3198 line = s;
3199 vim_free(sp->nextline);
3200 }
3201 }
3202
3203#ifdef FEAT_MBYTE
3204 if (line != NULL && sp->conv.vc_type != CONV_NONE)
3205 {
3206 /* Convert the encoding of the script line. */
3207 s = string_convert(&sp->conv, line, NULL);
3208 if (s != NULL)
3209 {
3210 vim_free(line);
3211 line = s;
3212 }
3213 }
3214#endif
3215
3216#ifdef FEAT_EVAL
3217 /* Did we encounter a breakpoint? */
3218 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
3219 {
3220 dbg_breakpoint(sp->fname, sourcing_lnum);
3221 /* Find next breakpoint. */
3222 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3223 sp->dbg_tick = debug_tick;
3224 }
3225#endif
3226
3227 return line;
3228}
3229
3230 static char_u *
3231get_one_sourceline(sp)
3232 struct source_cookie *sp;
3233{
3234 garray_T ga;
3235 int len;
3236 int c;
3237 char_u *buf;
3238#ifdef USE_CRNL
3239 int has_cr; /* CR-LF found */
3240#endif
3241#ifdef USE_CR
3242 char_u *scan;
3243#endif
3244 int have_read = FALSE;
3245
3246 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003247 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248
3249 /*
3250 * Loop until there is a finished line (or end-of-file).
3251 */
3252 sourcing_lnum++;
3253 for (;;)
3254 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003255 /* make room to read at least 120 (more) characters */
3256 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 break;
3258 buf = (char_u *)ga.ga_data;
3259
3260#ifdef USE_CR
3261 if (sp->fileformat == EOL_MAC)
3262 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00003263 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3264 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265 break;
3266 }
3267 else
3268#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00003269 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3270 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003272 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273#ifdef USE_CRNL
3274 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
3275 * CTRL-Z by its own, or after a NL. */
3276 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
3277 && sp->fileformat == EOL_DOS
3278 && buf[len - 1] == Ctrl_Z)
3279 {
3280 buf[len - 1] = NUL;
3281 break;
3282 }
3283#endif
3284
3285#ifdef USE_CR
3286 /* If the read doesn't stop on a new line, and there's
3287 * some CR then we assume a Mac format */
3288 if (sp->fileformat == EOL_UNKNOWN)
3289 {
3290 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
3291 sp->fileformat = EOL_MAC;
3292 else
3293 sp->fileformat = EOL_UNIX;
3294 }
3295
3296 if (sp->fileformat == EOL_MAC)
3297 {
3298 scan = vim_strchr(buf, '\r');
3299
3300 if (scan != NULL)
3301 {
3302 *scan = '\n';
3303 if (*(scan + 1) != 0)
3304 {
3305 *(scan + 1) = 0;
3306 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
3307 }
3308 }
3309 len = STRLEN(buf);
3310 }
3311#endif
3312
3313 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 ga.ga_len = len;
3315
3316 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00003317 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318 continue;
3319
3320 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
3321 {
3322#ifdef USE_CRNL
3323 has_cr = (len >= 2 && buf[len - 2] == '\r');
3324 if (sp->fileformat == EOL_UNKNOWN)
3325 {
3326 if (has_cr)
3327 sp->fileformat = EOL_DOS;
3328 else
3329 sp->fileformat = EOL_UNIX;
3330 }
3331
3332 if (sp->fileformat == EOL_DOS)
3333 {
3334 if (has_cr) /* replace trailing CR */
3335 {
3336 buf[len - 2] = '\n';
3337 --len;
3338 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339 }
3340 else /* lines like ":map xx yy^M" will have failed */
3341 {
3342 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003343 {
3344 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003346 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 sp->error = TRUE;
3348 sp->fileformat = EOL_UNIX;
3349 }
3350 }
3351#endif
3352 /* The '\n' is escaped if there is an odd number of ^V's just
3353 * before it, first set "c" just before the 'V's and then check
3354 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
3355 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
3356 ;
3357 if ((len & 1) != (c & 1)) /* escaped NL, read more */
3358 {
3359 sourcing_lnum++;
3360 continue;
3361 }
3362
3363 buf[len - 1] = NUL; /* remove the NL */
3364 }
3365
3366 /*
3367 * Check for ^C here now and then, so recursive :so can be broken.
3368 */
3369 line_breakcheck();
3370 break;
3371 }
3372
3373 if (have_read)
3374 return (char_u *)ga.ga_data;
3375
3376 vim_free(ga.ga_data);
3377 return NULL;
3378}
3379
Bram Moolenaar05159a02005-02-26 23:04:13 +00003380#if defined(FEAT_PROFILE) || defined(PROTO)
3381/*
3382 * Called when starting to read a script line.
3383 * "sourcing_lnum" must be correct!
3384 * When skipping lines it may not actually be executed, but we won't find out
3385 * until later and we need to store the time now.
3386 */
3387 void
3388script_line_start()
3389{
3390 scriptitem_T *si;
3391 sn_prl_T *pp;
3392
3393 if (current_SID <= 0 || current_SID > script_items.ga_len)
3394 return;
3395 si = &SCRIPT_ITEM(current_SID);
3396 if (si->sn_prof_on && sourcing_lnum >= 1)
3397 {
3398 /* Grow the array before starting the timer, so that the time spend
3399 * here isn't counted. */
3400 ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
3401 si->sn_prl_idx = sourcing_lnum - 1;
3402 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
3403 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
3404 {
3405 /* Zero counters for a line that was not used before. */
3406 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
3407 pp->snp_count = 0;
3408 profile_zero(&pp->sn_prl_total);
3409 profile_zero(&pp->sn_prl_self);
3410 ++si->sn_prl_ga.ga_len;
3411 }
3412 si->sn_prl_execed = FALSE;
3413 profile_start(&si->sn_prl_start);
3414 profile_zero(&si->sn_prl_children);
3415 profile_get_wait(&si->sn_prl_wait);
3416 }
3417}
3418
3419/*
3420 * Called when actually executing a function line.
3421 */
3422 void
3423script_line_exec()
3424{
3425 scriptitem_T *si;
3426
3427 if (current_SID <= 0 || current_SID > script_items.ga_len)
3428 return;
3429 si = &SCRIPT_ITEM(current_SID);
3430 if (si->sn_prof_on && si->sn_prl_idx >= 0)
3431 si->sn_prl_execed = TRUE;
3432}
3433
3434/*
3435 * Called when done with a function line.
3436 */
3437 void
3438script_line_end()
3439{
3440 scriptitem_T *si;
3441 sn_prl_T *pp;
3442
3443 if (current_SID <= 0 || current_SID > script_items.ga_len)
3444 return;
3445 si = &SCRIPT_ITEM(current_SID);
3446 if (si->sn_prof_on && si->sn_prl_idx >= 0
3447 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
3448 {
3449 if (si->sn_prl_execed)
3450 {
3451 pp = &PRL_ITEM(si, si->sn_prl_idx);
3452 ++pp->snp_count;
3453 profile_end(&si->sn_prl_start);
3454 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
3455 profile_add(&pp->sn_prl_self, &si->sn_prl_start);
3456 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
3457 profile_sub(&pp->sn_prl_self, &si->sn_prl_children);
3458 }
3459 si->sn_prl_idx = -1;
3460 }
3461}
3462#endif
3463
Bram Moolenaar071d4272004-06-13 20:20:40 +00003464/*
3465 * ":scriptencoding": Set encoding conversion for a sourced script.
3466 * Without the multi-byte feature it's simply ignored.
3467 */
3468/*ARGSUSED*/
3469 void
3470ex_scriptencoding(eap)
3471 exarg_T *eap;
3472{
3473#ifdef FEAT_MBYTE
3474 struct source_cookie *sp;
3475 char_u *name;
3476
3477 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
3478 {
3479 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
3480 return;
3481 }
3482
3483 if (*eap->arg != NUL)
3484 {
3485 name = enc_canonize(eap->arg);
3486 if (name == NULL) /* out of memory */
3487 return;
3488 }
3489 else
3490 name = eap->arg;
3491
3492 /* Setup for conversion from the specified encoding to 'encoding'. */
3493 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
3494 convert_setup(&sp->conv, name, p_enc);
3495
3496 if (name != eap->arg)
3497 vim_free(name);
3498#endif
3499}
3500
3501#if defined(FEAT_EVAL) || defined(PROTO)
3502/*
3503 * ":finish": Mark a sourced file as finished.
3504 */
3505 void
3506ex_finish(eap)
3507 exarg_T *eap;
3508{
3509 if (getline_equal(eap->getline, eap->cookie, getsourceline))
3510 do_finish(eap, FALSE);
3511 else
3512 EMSG(_("E168: :finish used outside of a sourced file"));
3513}
3514
3515/*
3516 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
3517 * Also called for a pending finish at the ":endtry" or after returning from
3518 * an extra do_cmdline(). "reanimate" is used in the latter case.
3519 */
3520 void
3521do_finish(eap, reanimate)
3522 exarg_T *eap;
3523 int reanimate;
3524{
3525 int idx;
3526
3527 if (reanimate)
3528 ((struct source_cookie *)getline_cookie(eap->getline,
3529 eap->cookie))->finished = FALSE;
3530
3531 /*
3532 * Cleanup (and inactivate) conditionals, but stop when a try conditional
3533 * not in its finally clause (which then is to be executed next) is found.
3534 * In this case, make the ":finish" pending for execution at the ":endtry".
3535 * Otherwise, finish normally.
3536 */
3537 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
3538 if (idx >= 0)
3539 {
3540 eap->cstack->cs_pending[idx] = CSTP_FINISH;
3541 report_make_pending(CSTP_FINISH, NULL);
3542 }
3543 else
3544 ((struct source_cookie *)getline_cookie(eap->getline,
3545 eap->cookie))->finished = TRUE;
3546}
3547
3548
3549/*
3550 * Return TRUE when a sourced file had the ":finish" command: Don't give error
3551 * message for missing ":endif".
3552 * Return FALSE when not sourcing a file.
3553 */
3554 int
3555source_finished(getline, cookie)
3556 char_u *(*getline) __ARGS((int, void *, int));
3557 void *cookie;
3558{
3559 return (getline_equal(getline, cookie, getsourceline)
3560 && ((struct source_cookie *)getline_cookie(
3561 getline, cookie))->finished);
3562}
3563#endif
3564
3565#if defined(FEAT_LISTCMDS) || defined(PROTO)
3566/*
3567 * ":checktime [buffer]"
3568 */
3569 void
3570ex_checktime(eap)
3571 exarg_T *eap;
3572{
3573 buf_T *buf;
3574 int save_no_check_timestamps = no_check_timestamps;
3575
3576 no_check_timestamps = 0;
3577 if (eap->addr_count == 0) /* default is all buffers */
3578 check_timestamps(FALSE);
3579 else
3580 {
3581 buf = buflist_findnr((int)eap->line2);
3582 if (buf != NULL) /* cannot happen? */
3583 (void)buf_check_timestamp(buf, FALSE);
3584 }
3585 no_check_timestamps = save_no_check_timestamps;
3586}
3587#endif
3588
3589#if defined(FEAT_PRINTER) || defined(PROTO)
3590/*
3591 * Printing code (Machine-independent.)
3592 * To implement printing on a platform, the following functions must be
3593 * defined:
3594 *
3595 * int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
3596 * Called once. Code should display printer dialogue (if appropriate) and
3597 * determine printer font and margin settings. Reset has_color if the printer
3598 * doesn't support colors at all.
3599 * Returns FAIL to abort.
3600 *
3601 * int mch_print_begin(prt_settings_T *settings)
3602 * Called to start the print job.
3603 * Return FALSE to abort.
3604 *
3605 * int mch_print_begin_page(char_u *msg)
3606 * Called at the start of each page.
3607 * "msg" indicates the progress of the print job, can be NULL.
3608 * Return FALSE to abort.
3609 *
3610 * int mch_print_end_page()
3611 * Called at the end of each page.
3612 * Return FALSE to abort.
3613 *
3614 * int mch_print_blank_page()
3615 * Called to generate a blank page for collated, duplex, multiple copy
3616 * document. Return FALSE to abort.
3617 *
3618 * void mch_print_end(prt_settings_T *psettings)
3619 * Called at normal end of print job.
3620 *
3621 * void mch_print_cleanup()
3622 * Called if print job ends normally or is abandoned. Free any memory, close
3623 * devices and handles. Also called when mch_print_begin() fails, but not
3624 * when mch_print_init() fails.
3625 *
3626 * void mch_print_set_font(int Bold, int Italic, int Underline);
3627 * Called whenever the font style changes.
3628 *
3629 * void mch_print_set_bg(long bgcol);
3630 * Called to set the background color for the following text. Parameter is an
3631 * RGB value.
3632 *
3633 * void mch_print_set_fg(long fgcol);
3634 * Called to set the foreground color for the following text. Parameter is an
3635 * RGB value.
3636 *
3637 * mch_print_start_line(int margin, int page_line)
3638 * Sets the current position at the start of line "page_line".
3639 * If margin is TRUE start in the left margin (for header and line number).
3640 *
3641 * int mch_print_text_out(char_u *p, int len);
3642 * Output one character of text p[len] at the current position.
3643 * Return TRUE if there is no room for another character in the same line.
3644 *
3645 * Note that the generic code has no idea of margins. The machine code should
3646 * simply make the page look smaller! The header and the line numbers are
3647 * printed in the margin.
3648 */
3649
3650#ifdef FEAT_SYN_HL
3651static const long_u cterm_color_8[8] =
3652{
3653 (long_u)0x000000L, (long_u)0xff0000L, (long_u)0x00ff00L, (long_u)0xffff00L,
3654 (long_u)0x0000ffL, (long_u)0xff00ffL, (long_u)0x00ffffL, (long_u)0xffffffL
3655};
3656
3657static const long_u cterm_color_16[16] =
3658{
3659 (long_u)0x000000L, (long_u)0x0000c0L, (long_u)0x008000L, (long_u)0x004080L,
3660 (long_u)0xc00000L, (long_u)0xc000c0L, (long_u)0x808000L, (long_u)0xc0c0c0L,
3661 (long_u)0x808080L, (long_u)0x6060ffL, (long_u)0x00ff00L, (long_u)0x00ffffL,
3662 (long_u)0xff8080L, (long_u)0xff40ffL, (long_u)0xffff00L, (long_u)0xffffffL
3663};
3664
3665static int current_syn_id;
3666#endif
3667
3668#define PRCOLOR_BLACK (long_u)0
3669#define PRCOLOR_WHITE (long_u)0xFFFFFFL
3670
3671static int curr_italic;
3672static int curr_bold;
3673static int curr_underline;
3674static long_u curr_bg;
3675static long_u curr_fg;
3676static int page_count;
3677
3678/*
3679 * These values determine the print position on a page.
3680 */
3681typedef struct
3682{
3683 int lead_spaces; /* remaining spaces for a TAB */
3684 int print_pos; /* virtual column for computing TABs */
3685 colnr_T column; /* byte column */
3686 linenr_T file_line; /* line nr in the buffer */
3687 long_u bytes_printed; /* bytes printed so far */
3688 int ff; /* seen form feed character */
3689} prt_pos_T;
3690
3691#ifdef FEAT_SYN_HL
3692static long_u darken_rgb __ARGS((long_u rgb));
3693static long_u prt_get_term_color __ARGS((int colorindex));
3694#endif
3695static void prt_set_fg __ARGS((long_u fg));
3696static void prt_set_bg __ARGS((long_u bg));
3697static void prt_set_font __ARGS((int bold, int italic, int underline));
3698static void prt_line_number __ARGS((prt_settings_T *psettings, int page_line, linenr_T lnum));
3699static void prt_header __ARGS((prt_settings_T *psettings, int pagenum, linenr_T lnum));
3700static void prt_message __ARGS((char_u *s));
3701static colnr_T hardcopy_line __ARGS((prt_settings_T *psettings, int page_line, prt_pos_T *ppos));
3702static void prt_get_attr __ARGS((int hl_id, prt_text_attr_T* pattr, int modec));
3703
3704#ifdef FEAT_SYN_HL
3705/*
3706 * If using a dark background, the colors will probably be too bright to show
3707 * up well on white paper, so reduce their brightness.
3708 */
3709 static long_u
3710darken_rgb(rgb)
3711 long_u rgb;
3712{
3713 return ((rgb >> 17) << 16)
3714 + (((rgb & 0xff00) >> 9) << 8)
3715 + ((rgb & 0xff) >> 1);
3716}
3717
3718 static long_u
3719prt_get_term_color(colorindex)
3720 int colorindex;
3721{
3722 /* TODO: Should check for xterm with 88 or 256 colors. */
3723 if (t_colors > 8)
3724 return cterm_color_16[colorindex % 16];
3725 return cterm_color_8[colorindex % 8];
3726}
3727
3728 static void
3729prt_get_attr(hl_id, pattr, modec)
3730 int hl_id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003731 prt_text_attr_T *pattr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 int modec;
3733{
3734 int colorindex;
3735 long_u fg_color;
3736 long_u bg_color;
3737 char *color;
3738
3739 pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
3740 pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
3741 pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003742 pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743
3744# ifdef FEAT_GUI
3745 if (gui.in_use)
3746 {
3747 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3748 if (bg_color == PRCOLOR_BLACK)
3749 bg_color = PRCOLOR_WHITE;
3750
3751 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3752 }
3753 else
3754# endif
3755 {
3756 bg_color = PRCOLOR_WHITE;
3757
3758 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3759 if (color == NULL)
3760 colorindex = 0;
3761 else
3762 colorindex = atoi(color);
3763
3764 if (colorindex >= 0 && colorindex < t_colors)
3765 fg_color = prt_get_term_color(colorindex);
3766 else
3767 fg_color = PRCOLOR_BLACK;
3768 }
3769
3770 if (fg_color == PRCOLOR_WHITE)
3771 fg_color = PRCOLOR_BLACK;
3772 else if (*p_bg == 'd')
3773 fg_color = darken_rgb(fg_color);
3774
3775 pattr->fg_color = fg_color;
3776 pattr->bg_color = bg_color;
3777}
3778#endif /* FEAT_SYN_HL */
3779
3780 static void
3781prt_set_fg(fg)
3782 long_u fg;
3783{
3784 if (fg != curr_fg)
3785 {
3786 curr_fg = fg;
3787 mch_print_set_fg(fg);
3788 }
3789}
3790
3791 static void
3792prt_set_bg(bg)
3793 long_u bg;
3794{
3795 if (bg != curr_bg)
3796 {
3797 curr_bg = bg;
3798 mch_print_set_bg(bg);
3799 }
3800}
3801
3802 static void
3803prt_set_font(bold, italic, underline)
3804 int bold;
3805 int italic;
3806 int underline;
3807{
3808 if (curr_bold != bold
3809 || curr_italic != italic
3810 || curr_underline != underline)
3811 {
3812 curr_underline = underline;
3813 curr_italic = italic;
3814 curr_bold = bold;
3815 mch_print_set_font(bold, italic, underline);
3816 }
3817}
3818
3819/*
3820 * Print the line number in the left margin.
3821 */
3822 static void
3823prt_line_number(psettings, page_line, lnum)
3824 prt_settings_T *psettings;
3825 int page_line;
3826 linenr_T lnum;
3827{
3828 int i;
3829 char_u tbuf[20];
3830
3831 prt_set_fg(psettings->number.fg_color);
3832 prt_set_bg(psettings->number.bg_color);
3833 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3834 mch_print_start_line(TRUE, page_line);
3835
3836 /* Leave two spaces between the number and the text; depends on
3837 * PRINT_NUMBER_WIDTH. */
3838 sprintf((char *)tbuf, "%6ld", (long)lnum);
3839 for (i = 0; i < 6; i++)
3840 (void)mch_print_text_out(&tbuf[i], 1);
3841
3842#ifdef FEAT_SYN_HL
3843 if (psettings->do_syntax)
3844 /* Set colors for next character. */
3845 current_syn_id = -1;
3846 else
3847#endif
3848 {
3849 /* Set colors and font back to normal. */
3850 prt_set_fg(PRCOLOR_BLACK);
3851 prt_set_bg(PRCOLOR_WHITE);
3852 prt_set_font(FALSE, FALSE, FALSE);
3853 }
3854}
3855
3856static linenr_T printer_page_num;
3857
3858 int
3859get_printer_page_num()
3860{
3861 return printer_page_num;
3862}
3863
3864/*
3865 * Get the currently effective header height.
3866 */
3867 int
3868prt_header_height()
3869{
3870 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3871 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3872 return 2;
3873}
3874
3875/*
3876 * Return TRUE if using a line number for printing.
3877 */
3878 int
3879prt_use_number()
3880{
3881 return (printer_opts[OPT_PRINT_NUMBER].present
3882 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3883}
3884
3885/*
3886 * Return the unit used in a margin item in 'printoptions'.
3887 * Returns PRT_UNIT_NONE if not recognized.
3888 */
3889 int
3890prt_get_unit(idx)
3891 int idx;
3892{
3893 int u = PRT_UNIT_NONE;
3894 int i;
3895 static char *(units[4]) = PRT_UNIT_NAMES;
3896
3897 if (printer_opts[idx].present)
3898 for (i = 0; i < 4; ++i)
3899 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3900 {
3901 u = i;
3902 break;
3903 }
3904 return u;
3905}
3906
3907/*
3908 * Print the page header.
3909 */
3910/*ARGSUSED*/
3911 static void
3912prt_header(psettings, pagenum, lnum)
3913 prt_settings_T *psettings;
3914 int pagenum;
3915 linenr_T lnum;
3916{
3917 int width = psettings->chars_per_line;
3918 int page_line;
3919 char_u *tbuf;
3920 char_u *p;
3921#ifdef FEAT_MBYTE
3922 int l;
3923#endif
3924
3925 /* Also use the space for the line number. */
3926 if (prt_use_number())
3927 width += PRINT_NUMBER_WIDTH;
3928
3929 tbuf = alloc(width + IOSIZE);
3930 if (tbuf == NULL)
3931 return;
3932
3933#ifdef FEAT_STL_OPT
3934 if (*p_header != NUL)
3935 {
3936 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3937
3938 /*
3939 * Need to (temporarily) set current line number and first/last line
3940 * number on the 'window'. Since we don't know how long the page is,
3941 * set the first and current line number to the top line, and guess
3942 * that the page length is 64.
3943 */
3944 tmp_lnum = curwin->w_cursor.lnum;
3945 tmp_topline = curwin->w_topline;
3946 tmp_botline = curwin->w_botline;
3947 curwin->w_cursor.lnum = lnum;
3948 curwin->w_topline = lnum;
3949 curwin->w_botline = lnum + 63;
3950 printer_page_num = pagenum;
3951
3952 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3953 p_header, ' ', width, NULL);
3954
3955 /* Reset line numbers */
3956 curwin->w_cursor.lnum = tmp_lnum;
3957 curwin->w_topline = tmp_topline;
3958 curwin->w_botline = tmp_botline;
3959 }
3960 else
3961#endif
3962 sprintf((char *)tbuf, _("Page %d"), pagenum);
3963
3964 prt_set_fg(PRCOLOR_BLACK);
3965 prt_set_bg(PRCOLOR_WHITE);
3966 prt_set_font(TRUE, FALSE, FALSE);
3967
3968 /* Use a negative line number to indicate printing in the top margin. */
3969 page_line = 0 - prt_header_height();
3970 mch_print_start_line(TRUE, page_line);
3971 for (p = tbuf; *p != NUL; )
3972 {
3973 if (mch_print_text_out(p,
3974#ifdef FEAT_MBYTE
3975 (l = (*mb_ptr2len_check)(p))
3976#else
3977 1
3978#endif
3979 ))
3980 {
3981 ++page_line;
3982 if (page_line >= 0) /* out of room in header */
3983 break;
3984 mch_print_start_line(TRUE, page_line);
3985 }
3986#ifdef FEAT_MBYTE
3987 p += l;
3988#else
3989 p++;
3990#endif
3991 }
3992
3993 vim_free(tbuf);
3994
3995#ifdef FEAT_SYN_HL
3996 if (psettings->do_syntax)
3997 /* Set colors for next character. */
3998 current_syn_id = -1;
3999 else
4000#endif
4001 {
4002 /* Set colors and font back to normal. */
4003 prt_set_fg(PRCOLOR_BLACK);
4004 prt_set_bg(PRCOLOR_WHITE);
4005 prt_set_font(FALSE, FALSE, FALSE);
4006 }
4007}
4008
4009/*
4010 * Display a print status message.
4011 */
4012 static void
4013prt_message(s)
4014 char_u *s;
4015{
4016 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
4017 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
4018 out_flush();
4019}
4020
4021 void
4022ex_hardcopy(eap)
4023 exarg_T *eap;
4024{
4025 linenr_T lnum;
4026 int collated_copies, uncollated_copies;
4027 prt_settings_T settings;
4028 long_u bytes_to_print = 0;
4029 int page_line;
4030 int jobsplit;
4031 int id;
4032
4033 memset(&settings, 0, sizeof(prt_settings_T));
4034 settings.has_color = TRUE;
4035
4036# ifdef FEAT_POSTSCRIPT
4037 if (*eap->arg == '>')
4038 {
4039 char_u *errormsg = NULL;
4040
4041 /* Expand things like "%.ps". */
4042 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
4043 {
4044 if (errormsg != NULL)
4045 EMSG(errormsg);
4046 return;
4047 }
4048 settings.outfile = skipwhite(eap->arg + 1);
4049 }
4050 else if (*eap->arg != NUL)
4051 settings.arguments = eap->arg;
4052# endif
4053
4054 /*
4055 * Initialise for printing. Ask the user for settings, unless forceit is
4056 * set.
4057 * The mch_print_init() code should set up margins if applicable. (It may
4058 * not be a real printer - for example the engine might generate HTML or
4059 * PS.)
4060 */
4061 if (mch_print_init(&settings,
4062 curbuf->b_fname == NULL
4063 ? (char_u *)buf_spname(curbuf)
4064 : curbuf->b_sfname == NULL
4065 ? curbuf->b_fname
4066 : curbuf->b_sfname,
4067 eap->forceit) == FAIL)
4068 return;
4069
4070#ifdef FEAT_SYN_HL
4071# ifdef FEAT_GUI
4072 if (gui.in_use)
4073 settings.modec = 'g';
4074 else
4075# endif
4076 if (t_colors > 1)
4077 settings.modec = 'c';
4078 else
4079 settings.modec = 't';
4080
4081 if (!syntax_present(curbuf))
4082 settings.do_syntax = FALSE;
4083 else if (printer_opts[OPT_PRINT_SYNTAX].present
4084 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
4085 settings.do_syntax =
4086 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
4087 else
4088 settings.do_syntax = settings.has_color;
4089#endif
4090
4091 /* Set up printing attributes for line numbers */
4092 settings.number.fg_color = PRCOLOR_BLACK;
4093 settings.number.bg_color = PRCOLOR_WHITE;
4094 settings.number.bold = FALSE;
4095 settings.number.italic = TRUE;
4096 settings.number.underline = FALSE;
4097#ifdef FEAT_SYN_HL
4098 /*
4099 * Syntax highlighting of line numbers.
4100 */
4101 if (prt_use_number() && settings.do_syntax)
4102 {
4103 id = syn_name2id((char_u *)"LineNr");
4104 if (id > 0)
4105 id = syn_get_final_id(id);
4106
4107 prt_get_attr(id, &settings.number, settings.modec);
4108 }
4109#endif /* FEAT_SYN_HL */
4110
4111 /*
4112 * Estimate the total lines to be printed
4113 */
4114 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
4115 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
4116 if (bytes_to_print == 0)
4117 {
4118 MSG(_("No text to be printed"));
4119 goto print_fail_no_begin;
4120 }
4121
4122 /* Set colors and font to normal. */
4123 curr_bg = (long_u)0xffffffffL;
4124 curr_fg = (long_u)0xffffffffL;
4125 curr_italic = MAYBE;
4126 curr_bold = MAYBE;
4127 curr_underline = MAYBE;
4128
4129 prt_set_fg(PRCOLOR_BLACK);
4130 prt_set_bg(PRCOLOR_WHITE);
4131 prt_set_font(FALSE, FALSE, FALSE);
4132#ifdef FEAT_SYN_HL
4133 current_syn_id = -1;
4134#endif
4135
4136 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
4137 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
4138
4139 if (!mch_print_begin(&settings))
4140 goto print_fail_no_begin;
4141
4142 /*
4143 * Loop over collated copies: 1 2 3, 1 2 3, ...
4144 */
4145 page_count = 0;
4146 for (collated_copies = 0;
4147 collated_copies < settings.n_collated_copies;
4148 collated_copies++)
4149 {
4150 prt_pos_T prtpos; /* current print position */
4151 prt_pos_T page_prtpos; /* print position at page start */
4152 int side;
4153
4154 memset(&page_prtpos, 0, sizeof(prt_pos_T));
4155 page_prtpos.file_line = eap->line1;
4156 prtpos = page_prtpos;
4157
4158 if (jobsplit && collated_copies > 0)
4159 {
4160 /* Splitting jobs: Stop a previous job and start a new one. */
4161 mch_print_end(&settings);
4162 if (!mch_print_begin(&settings))
4163 goto print_fail_no_begin;
4164 }
4165
4166 /*
4167 * Loop over all pages in the print job: 1 2 3 ...
4168 */
4169 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
4170 {
4171 /*
4172 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
4173 * For duplex: 12 12 12 34 34 34, ...
4174 */
4175 for (uncollated_copies = 0;
4176 uncollated_copies < settings.n_uncollated_copies;
4177 uncollated_copies++)
4178 {
4179 /* Set the print position to the start of this page. */
4180 prtpos = page_prtpos;
4181
4182 /*
4183 * Do front and rear side of a page.
4184 */
4185 for (side = 0; side <= settings.duplex; ++side)
4186 {
4187 /*
4188 * Print one page.
4189 */
4190
4191 /* Check for interrupt character every page. */
4192 ui_breakcheck();
4193 if (got_int || settings.user_abort)
4194 goto print_fail;
4195
4196 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
4197 page_count + 1 + side,
4198 prtpos.bytes_printed > 1000000
4199 ? (int)(prtpos.bytes_printed /
4200 (bytes_to_print / 100))
4201 : (int)((prtpos.bytes_printed * 100)
4202 / bytes_to_print));
4203 if (!mch_print_begin_page(IObuff))
4204 goto print_fail;
4205
4206 if (settings.n_collated_copies > 1)
4207 sprintf((char *)IObuff + STRLEN(IObuff),
4208 _(" Copy %d of %d"),
4209 collated_copies + 1,
4210 settings.n_collated_copies);
4211 prt_message(IObuff);
4212
4213 /*
4214 * Output header if required
4215 */
4216 if (prt_header_height() > 0)
4217 prt_header(&settings, page_count + 1 + side,
4218 prtpos.file_line);
4219
4220 for (page_line = 0; page_line < settings.lines_per_page;
4221 ++page_line)
4222 {
4223 prtpos.column = hardcopy_line(&settings,
4224 page_line, &prtpos);
4225 if (prtpos.column == 0)
4226 {
4227 /* finished a file line */
4228 prtpos.bytes_printed +=
4229 STRLEN(skipwhite(ml_get(prtpos.file_line)));
4230 if (++prtpos.file_line > eap->line2)
4231 break; /* reached the end */
4232 }
4233 else if (prtpos.ff)
4234 {
4235 /* Line had a formfeed in it - start new page but
4236 * stay on the current line */
4237 break;
4238 }
4239 }
4240
4241 if (!mch_print_end_page())
4242 goto print_fail;
4243 if (prtpos.file_line > eap->line2)
4244 break; /* reached the end */
4245 }
4246
4247 /*
4248 * Extra blank page for duplexing with odd number of pages and
4249 * more copies to come.
4250 */
4251 if (prtpos.file_line > eap->line2 && settings.duplex
4252 && side == 0
4253 && uncollated_copies + 1 < settings.n_uncollated_copies)
4254 {
4255 if (!mch_print_blank_page())
4256 goto print_fail;
4257 }
4258 }
4259 if (settings.duplex && prtpos.file_line <= eap->line2)
4260 ++page_count;
4261
4262 /* Remember the position where the next page starts. */
4263 page_prtpos = prtpos;
4264 }
4265
4266 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
4267 prt_message(IObuff);
4268 }
4269
4270print_fail:
4271 if (got_int || settings.user_abort)
4272 {
4273 sprintf((char *)IObuff, _("Printing aborted"));
4274 prt_message(IObuff);
4275 }
4276 mch_print_end(&settings);
4277
4278print_fail_no_begin:
4279 mch_print_cleanup();
4280}
4281
4282/*
4283 * Print one page line.
4284 * Return the next column to print, or zero if the line is finished.
4285 */
4286 static colnr_T
4287hardcopy_line(psettings, page_line, ppos)
4288 prt_settings_T *psettings;
4289 int page_line;
4290 prt_pos_T *ppos;
4291{
4292 colnr_T col;
4293 char_u *line;
4294 int need_break = FALSE;
4295 int outputlen;
4296 int tab_spaces;
4297 long_u print_pos;
4298#ifdef FEAT_SYN_HL
4299 prt_text_attr_T attr;
4300 int id;
4301#endif
4302
4303 if (ppos->column == 0 || ppos->ff)
4304 {
4305 print_pos = 0;
4306 tab_spaces = 0;
4307 if (!ppos->ff && prt_use_number())
4308 prt_line_number(psettings, page_line, ppos->file_line);
4309 ppos->ff = FALSE;
4310 }
4311 else
4312 {
4313 /* left over from wrap halfway a tab */
4314 print_pos = ppos->print_pos;
4315 tab_spaces = ppos->lead_spaces;
4316 }
4317
4318 mch_print_start_line(0, page_line);
4319 line = ml_get(ppos->file_line);
4320
4321 /*
4322 * Loop over the columns until the end of the file line or right margin.
4323 */
4324 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
4325 {
4326 outputlen = 1;
4327#ifdef FEAT_MBYTE
4328 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
4329 outputlen = 1;
4330#endif
4331#ifdef FEAT_SYN_HL
4332 /*
4333 * syntax highlighting stuff.
4334 */
4335 if (psettings->do_syntax)
4336 {
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00004337 id = syn_get_id(ppos->file_line, col, 1, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338 if (id > 0)
4339 id = syn_get_final_id(id);
4340 else
4341 id = 0;
4342 /* Get the line again, a multi-line regexp may invalidate it. */
4343 line = ml_get(ppos->file_line);
4344
4345 if (id != current_syn_id)
4346 {
4347 current_syn_id = id;
4348 prt_get_attr(id, &attr, psettings->modec);
4349 prt_set_font(attr.bold, attr.italic, attr.underline);
4350 prt_set_fg(attr.fg_color);
4351 prt_set_bg(attr.bg_color);
4352 }
4353 }
4354#endif /* FEAT_SYN_HL */
4355
4356 /*
4357 * Appropriately expand any tabs to spaces.
4358 */
4359 if (line[col] == TAB || tab_spaces != 0)
4360 {
4361 if (tab_spaces == 0)
4362 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
4363
4364 while (tab_spaces > 0)
4365 {
4366 need_break = mch_print_text_out((char_u *)" ", 1);
4367 print_pos++;
4368 tab_spaces--;
4369 if (need_break)
4370 break;
4371 }
4372 /* Keep the TAB if we didn't finish it. */
4373 if (need_break && tab_spaces > 0)
4374 break;
4375 }
4376 else if (line[col] == FF
4377 && printer_opts[OPT_PRINT_FORMFEED].present
4378 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
4379 == 'y')
4380 {
4381 ppos->ff = TRUE;
4382 need_break = 1;
4383 }
4384 else
4385 {
4386 need_break = mch_print_text_out(line + col, outputlen);
4387#ifdef FEAT_MBYTE
4388 if (has_mbyte)
4389 print_pos += (*mb_ptr2cells)(line + col);
4390 else
4391#endif
4392 print_pos++;
4393 }
4394 }
4395
4396 ppos->lead_spaces = tab_spaces;
4397 ppos->print_pos = print_pos;
4398
4399 /*
4400 * Start next line of file if we clip lines, or have reached end of the
4401 * line, unless we are doing a formfeed.
4402 */
4403 if (!ppos->ff
4404 && (line[col] == NUL
4405 || (printer_opts[OPT_PRINT_WRAP].present
4406 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
4407 == 'n')))
4408 return 0;
4409 return col;
4410}
4411
4412# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
4413
4414/*
4415 * PS printer stuff.
4416 *
4417 * Sources of information to help maintain the PS printing code:
4418 *
4419 * 1. PostScript Language Reference, 3rd Edition,
4420 * Addison-Wesley, 1999, ISBN 0-201-37922-8
4421 * 2. PostScript Language Program Design,
4422 * Addison-Wesley, 1988, ISBN 0-201-14396-8
4423 * 3. PostScript Tutorial and Cookbook,
4424 * Addison Wesley, 1985, ISBN 0-201-10179-3
4425 * 4. PostScript Language Document Structuring Conventions Specification,
4426 * version 3.0,
4427 * Adobe Technote 5001, 25th September 1992
4428 * 5. PostScript Printer Description File Format Specification, Version 4.3,
4429 * Adobe technote 5003, 9th February 1996
4430 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
4431 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00004432 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
4433 * Adobe Technote 5014, 8th October 1996
4434 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
4435 * Adoboe Technote 5094, 8th September, 2001
4436 * 9. CJKV Information Processing, 2nd Edition,
4437 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 *
4439 * Some of these documents can be found in PDF form on Adobe's web site -
4440 * http://www.adobe.com
4441 */
4442
Bram Moolenaar8299df92004-07-10 09:47:34 +00004443#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
4444
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
4446#define PRT_PS_DEFAULT_FONTSIZE (10)
4447#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
4448
4449struct prt_mediasize_S
4450{
4451 char *name;
4452 float width; /* width and height in points for portrait */
4453 float height;
4454};
4455
4456#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
4457
4458static struct prt_mediasize_S prt_mediasize[] =
4459{
4460 {"A4", 595.0, 842.0},
4461 {"letter", 612.0, 792.0},
4462 {"10x14", 720.0, 1008.0},
4463 {"A3", 842.0, 1191.0},
4464 {"A5", 420.0, 595.0},
4465 {"B4", 729.0, 1032.0},
4466 {"B5", 516.0, 729.0},
4467 {"executive", 522.0, 756.0},
4468 {"folio", 595.0, 935.0},
4469 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
4470 {"legal", 612.0, 1008.0},
4471 {"quarto", 610.0, 780.0},
4472 {"statement", 396.0, 612.0},
4473 {"tabloid", 792.0, 1224.0}
4474};
4475
4476/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
4477struct prt_ps_font_S
4478{
4479 int wx;
4480 int uline_offset;
4481 int uline_width;
4482 int bbox_min_y;
4483 int bbox_max_y;
4484 char *(ps_fontname[4]);
4485};
4486
4487#define PRT_PS_FONT_ROMAN (0)
4488#define PRT_PS_FONT_BOLD (1)
4489#define PRT_PS_FONT_OBLIQUE (2)
4490#define PRT_PS_FONT_BOLDOBLIQUE (3)
4491
Bram Moolenaar8299df92004-07-10 09:47:34 +00004492/* Standard font metrics for Courier family */
4493static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494{
4495 600,
4496 -100, 50,
4497 -250, 805,
4498 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
4499};
4500
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004501#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004502/* Generic font metrics for multi-byte fonts */
4503static struct prt_ps_font_S prt_ps_mb_font =
4504{
4505 1000,
4506 -100, 50,
4507 -250, 805,
4508 {NULL, NULL, NULL, NULL}
4509};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004510#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00004511
4512/* Pointer to current font set being used */
4513static struct prt_ps_font_S* prt_ps_font;
4514
4515/* Structures to map user named encoding and mapping to PS equivalents for
4516 * building CID font name */
4517struct prt_ps_encoding_S
4518{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004519 char *encoding;
4520 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004521 int needs_charset;
4522};
4523
4524struct prt_ps_charset_S
4525{
4526 char *charset;
4527 char *cmap_charset;
4528 int has_charset;
4529};
4530
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004531#ifdef FEAT_MBYTE
4532
Bram Moolenaar8299df92004-07-10 09:47:34 +00004533#define CS_JIS_C_1978 (0x01)
4534#define CS_JIS_X_1983 (0x02)
4535#define CS_JIS_X_1990 (0x04)
4536#define CS_NEC (0x08)
4537#define CS_MSWINDOWS (0x10)
4538#define CS_CP932 (0x20)
4539#define CS_KANJITALK6 (0x40)
4540#define CS_KANJITALK7 (0x80)
4541
4542/* Japanese encodings and charsets */
4543static struct prt_ps_encoding_S j_encodings[] =
4544{
4545 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
4546 CS_NEC)},
4547 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
4548 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
4549 CS_KANJITALK6|CS_KANJITALK7)},
4550 {"cp932", "RKSJ", CS_JIS_X_1983},
4551 {"ucs-2", "UCS2", CS_JIS_X_1990},
4552 {"utf-8", "UTF8" , CS_JIS_X_1990}
4553};
4554static struct prt_ps_charset_S j_charsets[] =
4555{
4556 {"JIS_C_1978", "78", CS_JIS_C_1978},
4557 {"JIS_X_1983", NULL, CS_JIS_X_1983},
4558 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
4559 {"NEC", "Ext", CS_NEC},
4560 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
4561 {"CP932", "90ms", CS_JIS_X_1983},
4562 {"KANJITALK6", "83pv", CS_KANJITALK6},
4563 {"KANJITALK7", "90pv", CS_KANJITALK7}
4564};
4565
4566#define CS_GB_2312_80 (0x01)
4567#define CS_GBT_12345_90 (0x02)
4568#define CS_GBK2K (0x04)
4569#define CS_SC_MAC (0x08)
4570#define CS_GBT_90_MAC (0x10)
4571#define CS_GBK (0x20)
4572#define CS_SC_ISO10646 (0x40)
4573
4574/* Simplified Chinese encodings and charsets */
4575static struct prt_ps_encoding_S sc_encodings[] =
4576{
4577 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
4578 {"gb18030", NULL, CS_GBK2K},
4579 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
4580 CS_GBT_90_MAC)},
4581 {"gbk", "EUC", CS_GBK},
4582 {"ucs-2", "UCS2", CS_SC_ISO10646},
4583 {"utf-8", "UTF8", CS_SC_ISO10646}
4584};
4585static struct prt_ps_charset_S sc_charsets[] =
4586{
4587 {"GB_2312-80", "GB", CS_GB_2312_80},
4588 {"GBT_12345-90","GBT", CS_GBT_12345_90},
4589 {"MAC", "GBpc", CS_SC_MAC},
4590 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
4591 {"GBK", "GBK", CS_GBK},
4592 {"GB18030", "GBK2K", CS_GBK2K},
4593 {"ISO10646", "UniGB", CS_SC_ISO10646}
4594};
4595
4596#define CS_CNS_PLANE_1 (0x01)
4597#define CS_CNS_PLANE_2 (0x02)
4598#define CS_CNS_PLANE_1_2 (0x04)
4599#define CS_B5 (0x08)
4600#define CS_ETEN (0x10)
4601#define CS_HK_GCCS (0x20)
4602#define CS_HK_SCS (0x40)
4603#define CS_HK_SCS_ETEN (0x80)
4604#define CS_MTHKL (0x100)
4605#define CS_MTHKS (0x200)
4606#define CS_DLHKL (0x400)
4607#define CS_DLHKS (0x800)
4608#define CS_TC_ISO10646 (0x1000)
4609
4610/* Traditional Chinese encodings and charsets */
4611static struct prt_ps_encoding_S tc_encodings[] =
4612{
4613 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
4614 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
4615 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
4616 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
4617 CS_DLHKS)},
4618 {"cp950", "B5", CS_B5},
4619 {"ucs-2", "UCS2", CS_TC_ISO10646},
4620 {"utf-8", "UTF8", CS_TC_ISO10646},
4621 {"utf-16", "UTF16", CS_TC_ISO10646},
4622 {"utf-32", "UTF32", CS_TC_ISO10646}
4623};
4624static struct prt_ps_charset_S tc_charsets[] =
4625{
4626 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
4627 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
4628 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
4629 {"BIG5", NULL, CS_B5},
4630 {"CP950", NULL, CS_B5},
4631 {"ETEN", "ETen", CS_ETEN},
4632 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
4633 {"SCS", "HKscs", CS_HK_SCS},
4634 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
4635 {"MTHKL", "HKm471", CS_MTHKL},
4636 {"MTHKS", "HKm314", CS_MTHKS},
4637 {"DLHKL", "HKdla", CS_DLHKL},
4638 {"DLHKS", "HKdlb", CS_DLHKS},
4639 {"ISO10646", "UniCNS", CS_TC_ISO10646}
4640};
4641
4642#define CS_KR_X_1992 (0x01)
4643#define CS_KR_MAC (0x02)
4644#define CS_KR_X_1992_MS (0x04)
4645#define CS_KR_ISO10646 (0x08)
4646
4647/* Korean encodings and charsets */
4648static struct prt_ps_encoding_S k_encodings[] =
4649{
4650 {"iso-2022-kr", NULL, CS_KR_X_1992},
4651 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
4652 {"johab", "Johab", CS_KR_X_1992},
4653 {"cp1361", "Johab", CS_KR_X_1992},
4654 {"uhc", "UHC", CS_KR_X_1992_MS},
4655 {"cp949", "UHC", CS_KR_X_1992_MS},
4656 {"ucs-2", "UCS2", CS_KR_ISO10646},
4657 {"utf-8", "UTF8", CS_KR_ISO10646}
4658};
4659static struct prt_ps_charset_S k_charsets[] =
4660{
4661 {"KS_X_1992", "KSC", CS_KR_X_1992},
4662 {"CP1361", "KSC", CS_KR_X_1992},
4663 {"MAC", "KSCpc", CS_KR_MAC},
4664 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
4665 {"CP949", "KSCms", CS_KR_X_1992_MS},
4666 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
4667 {"ISO10646", "UniKS", CS_KR_ISO10646}
4668};
4669
4670/* Collections of encodings and charsets for multi-byte printing */
4671struct prt_ps_mbfont_S
4672{
4673 int num_encodings;
4674 struct prt_ps_encoding_S *encodings;
4675 int num_charsets;
4676 struct prt_ps_charset_S *charsets;
4677 char *ascii_enc;
4678 char *defcs;
4679};
4680
4681static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4682{
4683 {
4684 NUM_ELEMENTS(j_encodings),
4685 j_encodings,
4686 NUM_ELEMENTS(j_charsets),
4687 j_charsets,
4688 "jis_roman",
4689 "JIS_X_1983"
4690 },
4691 {
4692 NUM_ELEMENTS(sc_encodings),
4693 sc_encodings,
4694 NUM_ELEMENTS(sc_charsets),
4695 sc_charsets,
4696 "gb_roman",
4697 "GB_2312-80"
4698 },
4699 {
4700 NUM_ELEMENTS(tc_encodings),
4701 tc_encodings,
4702 NUM_ELEMENTS(tc_charsets),
4703 tc_charsets,
4704 "cns_roman",
4705 "BIG5"
4706 },
4707 {
4708 NUM_ELEMENTS(k_encodings),
4709 k_encodings,
4710 NUM_ELEMENTS(k_charsets),
4711 k_charsets,
4712 "ks_roman",
4713 "KS_X_1992"
4714 }
4715};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004716#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004717
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718struct prt_ps_resource_S
4719{
4720 char_u name[64];
4721 char_u filename[MAXPATHL + 1];
4722 int type;
4723 char_u title[256];
4724 char_u version[256];
4725};
4726
4727/* Types of PS resource file currently used */
4728#define PRT_RESOURCE_TYPE_PROCSET (0)
4729#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004730#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731
4732/* The PS prolog file version number has to match - if the prolog file is
4733 * updated, increment the number in the file and here. Version checking was
4734 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004735 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 * Table of VIM and prolog versions:
4737 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004738 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004740 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004741 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004742#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004743#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744
4745/* String versions of PS resource types - indexed by constants above so don't
4746 * re-order!
4747 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004748static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004749{
4750 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004751 "encoding",
4752 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753};
4754
4755/* Strings to look for in a PS resource file */
4756#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4757#define PRT_RESOURCE_RESOURCE "Resource-"
4758#define PRT_RESOURCE_PROCSET "ProcSet"
4759#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004760#define PRT_RESOURCE_CMAP "CMap"
4761
4762
4763/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4764 * read more comments. */
4765#define PRT_DSC_MISC_TYPE (-1)
4766#define PRT_DSC_TITLE_TYPE (1)
4767#define PRT_DSC_VERSION_TYPE (2)
4768#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4769
4770#define PRT_DSC_TITLE "%%Title:"
4771#define PRT_DSC_VERSION "%%Version:"
4772#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4773
4774struct prt_dsc_comment_S
4775{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004776 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004777 int len;
4778 int type;
4779};
4780
4781struct prt_dsc_line_S
4782{
4783 int type;
4784 char_u *string;
4785 int len;
4786};
4787
4788
4789#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4790struct prt_dsc_comment_S prt_dsc_table[] =
4791{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004792 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004793 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004794 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004795 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004796 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004797};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798
4799static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4800static void prt_write_file __ARGS((char_u *buffer));
4801static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4802static void prt_write_string __ARGS((char *s));
4803static void prt_write_int __ARGS((int i));
4804static void prt_write_boolean __ARGS((int b));
4805static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4806static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4807static void prt_write_real __ARGS((double val, int prec));
4808static void prt_def_var __ARGS((char *name, double value, int prec));
4809static void prt_flush_buffer __ARGS((void));
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00004810static void prt_resource_name __ARGS((char_u *filename, void *cookie));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4812static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4813static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4814static void prt_dsc_start __ARGS((void));
4815static void prt_dsc_noarg __ARGS((char *comment));
4816static void prt_dsc_textline __ARGS((char *comment, char *text));
4817static void prt_dsc_text __ARGS((char *comment, char *text));
4818static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4819static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4820static 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 +00004821static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4822static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823static float to_device_units __ARGS((int idx, double physsize, int def_number));
4824static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4825static void prt_font_metrics __ARGS((int font_scale));
4826static int prt_get_cpl __ARGS((void));
4827static int prt_get_lpp __ARGS((void));
4828static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004829static int prt_resfile_next_line __ARGS((void));
4830static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4831static int prt_resfile_skip_nonws __ARGS((int offset));
4832static int prt_resfile_skip_ws __ARGS((int offset));
4833static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4834#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004835static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004836static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4837static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4838static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4839#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840
4841/*
4842 * Variables for the output PostScript file.
4843 */
4844static FILE *prt_ps_fd;
4845static int prt_file_error;
4846static char_u *prt_ps_file_name = NULL;
4847
4848/*
4849 * Various offsets and dimensions in default PostScript user space (points).
4850 * Used for text positioning calculations
4851 */
4852static float prt_page_width;
4853static float prt_page_height;
4854static float prt_left_margin;
4855static float prt_right_margin;
4856static float prt_top_margin;
4857static float prt_bottom_margin;
4858static float prt_line_height;
4859static float prt_first_line_height;
4860static float prt_char_width;
4861static float prt_number_width;
4862static float prt_bgcol_offset;
4863static float prt_pos_x_moveto = 0.0;
4864static float prt_pos_y_moveto = 0.0;
4865
4866/*
4867 * Various control variables used to decide when and how to change the
4868 * PostScript graphics state.
4869 */
4870static int prt_need_moveto;
4871static int prt_do_moveto;
4872static int prt_need_font;
4873static int prt_font;
4874static int prt_need_underline;
4875static int prt_underline;
4876static int prt_do_underline;
4877static int prt_need_fgcol;
4878static int prt_fgcol;
4879static int prt_need_bgcol;
4880static int prt_do_bgcol;
4881static int prt_bgcol;
4882static int prt_new_bgcol;
4883static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004884static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004886static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887
4888/*
4889 * Variables controlling physical printing.
4890 */
4891static int prt_media;
4892static int prt_portrait;
4893static int prt_num_copies;
4894static int prt_duplex;
4895static int prt_tumble;
4896static int prt_collate;
4897
4898/*
4899 * Buffers used when generating PostScript output
4900 */
4901static char_u prt_line_buffer[257];
4902static garray_T prt_ps_buffer;
4903
4904# ifdef FEAT_MBYTE
4905static int prt_do_conv;
4906static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004907
4908static int prt_out_mbyte;
4909static int prt_custom_cmap;
4910static char prt_cmap[80];
4911static int prt_use_courier;
4912static int prt_in_ascii;
4913static int prt_half_width;
4914static char *prt_ascii_encoding;
4915static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916# endif
4917
4918 static void
4919prt_write_file_raw_len(buffer, bytes)
4920 char_u *buffer;
4921 int bytes;
4922{
4923 if (!prt_file_error
4924 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4925 != (size_t)bytes)
4926 {
4927 EMSG(_("E455: Error writing to PostScript output file"));
4928 prt_file_error = TRUE;
4929 }
4930}
4931
4932 static void
4933prt_write_file(buffer)
4934 char_u *buffer;
4935{
4936 prt_write_file_len(buffer, STRLEN(buffer));
4937}
4938
4939 static void
4940prt_write_file_len(buffer, bytes)
4941 char_u *buffer;
4942 int bytes;
4943{
4944#ifdef EBCDIC
4945 ebcdic2ascii(buffer, bytes);
4946#endif
4947 prt_write_file_raw_len(buffer, bytes);
4948}
4949
4950/*
4951 * Write a string.
4952 */
4953 static void
4954prt_write_string(s)
4955 char *s;
4956{
4957 sprintf((char *)prt_line_buffer, "%s", s);
4958 prt_write_file(prt_line_buffer);
4959}
4960
4961/*
4962 * Write an int and a space.
4963 */
4964 static void
4965prt_write_int(i)
4966 int i;
4967{
4968 sprintf((char *)prt_line_buffer, "%d ", i);
4969 prt_write_file(prt_line_buffer);
4970}
4971
4972/*
4973 * Write a boolean and a space.
4974 */
4975 static void
4976prt_write_boolean(b)
4977 int b;
4978{
4979 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4980 prt_write_file(prt_line_buffer);
4981}
4982
4983/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004984 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 */
4986 static void
4987prt_def_font(new_name, encoding, height, font)
4988 char *new_name;
4989 char *encoding;
4990 int height;
4991 char *font;
4992{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004993 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4994 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004996#ifdef FEAT_MBYTE
4997 if (prt_out_mbyte)
4998 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4999 new_name, height, 500./prt_ps_courier_font.wx, new_name);
5000 else
5001#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
5003 new_name, height, new_name);
5004 prt_write_file(prt_line_buffer);
5005}
5006
Bram Moolenaar8299df92004-07-10 09:47:34 +00005007#ifdef FEAT_MBYTE
5008/*
5009 * Write a line to define the CID font.
5010 */
5011 static void
5012prt_def_cidfont(new_name, height, cidfont)
5013 char *new_name;
5014 int height;
5015 char *cidfont;
5016{
5017 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
5018 new_name, prt_cmap, cidfont);
5019 prt_write_file(prt_line_buffer);
5020 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
5021 new_name);
5022 prt_write_file(prt_line_buffer);
5023}
5024
5025/*
5026 * Write a line to define a duplicate of a CID font
5027 */
5028 static void
5029prt_dup_cidfont(original_name, new_name)
5030 char *original_name;
5031 char *new_name;
5032{
5033 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
5034 prt_write_file(prt_line_buffer);
5035}
5036#endif
5037
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038/*
5039 * Convert a real value into an integer and fractional part as integers, with
5040 * the fractional part being in the range [0,10^precision). The fractional part
5041 * is also rounded based on the precision + 1'th fractional digit.
5042 */
5043 static void
5044prt_real_bits(real, precision, pinteger, pfraction)
5045 double real;
5046 int precision;
5047 int *pinteger;
5048 int *pfraction;
5049{
5050 int i;
5051 int integer;
5052 float fraction;
5053
5054 integer = (int)real;
5055 fraction = (float)(real - integer);
5056 if (real < (double)integer)
5057 fraction = -fraction;
5058 for (i = 0; i < precision; i++)
5059 fraction *= 10.0;
5060
5061 *pinteger = integer;
5062 *pfraction = (int)(fraction + 0.5);
5063}
5064
5065/*
5066 * Write a real and a space. Save bytes if real value has no fractional part!
5067 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
5068 * what decimal point character to use, but PS always requires a '.'.
5069 */
5070 static void
5071prt_write_real(val, prec)
5072 double val;
5073 int prec;
5074{
5075 int integer;
5076 int fraction;
5077
5078 prt_real_bits(val, prec, &integer, &fraction);
5079 /* Emit integer part */
5080 sprintf((char *)prt_line_buffer, "%d", integer);
5081 prt_write_file(prt_line_buffer);
5082 /* Only emit fraction if necessary */
5083 if (fraction != 0)
5084 {
5085 /* Remove any trailing zeros */
5086 while ((fraction % 10) == 0)
5087 {
5088 prec--;
5089 fraction /= 10;
5090 }
5091 /* Emit fraction left padded with zeros */
5092 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
5093 prt_write_file(prt_line_buffer);
5094 }
5095 sprintf((char *)prt_line_buffer, " ");
5096 prt_write_file(prt_line_buffer);
5097}
5098
5099/*
5100 * Write a line to define a numeric variable.
5101 */
5102 static void
5103prt_def_var(name, value, prec)
5104 char *name;
5105 double value;
5106 int prec;
5107{
5108 sprintf((char *)prt_line_buffer, "/%s ", name);
5109 prt_write_file(prt_line_buffer);
5110 prt_write_real(value, prec);
5111 sprintf((char *)prt_line_buffer, "d\n");
5112 prt_write_file(prt_line_buffer);
5113}
5114
5115/* Convert size from font space to user space at current font scale */
5116#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
5117
5118 static void
5119prt_flush_buffer()
5120{
5121 if (prt_ps_buffer.ga_len > 0)
5122 {
5123 /* Any background color must be drawn first */
5124 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
5125 {
5126 int r, g, b;
5127
5128 if (prt_do_moveto)
5129 {
5130 prt_write_real(prt_pos_x_moveto, 2);
5131 prt_write_real(prt_pos_y_moveto, 2);
5132 prt_write_string("m\n");
5133 prt_do_moveto = FALSE;
5134 }
5135
5136 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005137 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 prt_write_real(prt_line_height, 2);
5139
5140 /* Lastly add the color of the background */
5141 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
5142 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
5143 b = prt_new_bgcol & 0xff;
5144 prt_write_real(r / 255.0, 3);
5145 prt_write_real(g / 255.0, 3);
5146 prt_write_real(b / 255.0, 3);
5147 prt_write_string("bg\n");
5148 }
5149 /* Draw underlines before the text as it makes it slightly easier to
5150 * find the starting point.
5151 */
5152 if (prt_do_underline)
5153 {
5154 if (prt_do_moveto)
5155 {
5156 prt_write_real(prt_pos_x_moveto, 2);
5157 prt_write_real(prt_pos_y_moveto, 2);
5158 prt_write_string("m\n");
5159 prt_do_moveto = FALSE;
5160 }
5161
Bram Moolenaar8299df92004-07-10 09:47:34 +00005162 /* Underline length of text run */
5163 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164 prt_write_string("ul\n");
5165 }
5166 /* Draw the text
5167 * Note: we write text out raw - EBCDIC conversion is handled in the
5168 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005169#ifdef FEAT_MBYTE
5170 if (prt_out_mbyte)
5171 prt_write_string("<");
5172 else
5173#endif
5174 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005175 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005176#ifdef FEAT_MBYTE
5177 if (prt_out_mbyte)
5178 prt_write_string(">");
5179 else
5180#endif
5181 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 /* Add a moveto if need be and use the appropriate show procedure */
5183 if (prt_do_moveto)
5184 {
5185 prt_write_real(prt_pos_x_moveto, 2);
5186 prt_write_real(prt_pos_y_moveto, 2);
5187 /* moveto and a show */
5188 prt_write_string("ms\n");
5189 prt_do_moveto = FALSE;
5190 }
5191 else /* Simple show */
5192 prt_write_string("s\n");
5193
5194 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005195 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005196 }
5197}
5198
Bram Moolenaar071d4272004-06-13 20:20:40 +00005199
5200 static void
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005201prt_resource_name(filename, cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005202 char_u *filename;
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005203 void *cookie;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204{
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005205 char_u *resource_filename = cookie;
5206
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207 if (STRLEN(filename) >= MAXPATHL)
5208 *resource_filename = NUL;
5209 else
5210 STRCPY(resource_filename, filename);
5211}
5212
5213 static int
5214prt_find_resource(name, resource)
5215 char *name;
5216 struct prt_ps_resource_S *resource;
5217{
5218 char_u buffer[MAXPATHL + 1];
5219
5220 STRCPY(resource->name, name);
5221 /* Look for named resource file in runtimepath */
5222 STRCPY(buffer, "print");
5223 add_pathsep(buffer);
5224 STRCAT(buffer, name);
5225 STRCAT(buffer, ".ps");
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00005226 resource->filename[0] = NUL;
5227 return (do_in_runtimepath(buffer, FALSE, prt_resource_name,
5228 resource->filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 && resource->filename[0] != NUL);
5230}
5231
5232/* PS CR and LF characters have platform independent values */
5233#define PSLF (0x0a)
5234#define PSCR (0x0d)
5235
Bram Moolenaar8299df92004-07-10 09:47:34 +00005236/* Static buffer to read initial comments in a resource file, some can have a
5237 * couple of KB of comments! */
5238#define PRT_FILE_BUFFER_LEN (2048)
5239struct prt_resfile_buffer_S
5240{
5241 char_u buffer[PRT_FILE_BUFFER_LEN];
5242 int len;
5243 int line_start;
5244 int line_end;
5245};
5246
5247static struct prt_resfile_buffer_S prt_resfile;
5248
5249 static int
5250prt_resfile_next_line()
5251{
5252 int index;
5253
5254 /* Move to start of next line and then find end of line */
5255 index = prt_resfile.line_end + 1;
5256 while (index < prt_resfile.len)
5257 {
5258 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
5259 != PSCR)
5260 break;
5261 index++;
5262 }
5263 prt_resfile.line_start = index;
5264
5265 while (index < prt_resfile.len)
5266 {
5267 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
5268 == PSCR)
5269 break;
5270 index++;
5271 }
5272 prt_resfile.line_end = index;
5273
5274 return (index < prt_resfile.len);
5275}
5276
5277 static int
5278prt_resfile_strncmp(offset, string, len)
5279 int offset;
5280 char *string;
5281 int len;
5282{
5283 /* Force not equal if string is longer than remainder of line */
5284 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
5285 return 1;
5286
5287 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
5288 string, len);
5289}
5290
5291 static int
5292prt_resfile_skip_nonws(offset)
5293 int offset;
5294{
5295 int index;
5296
5297 index = prt_resfile.line_start + offset;
5298 while (index < prt_resfile.line_end)
5299 {
5300 if (isspace(prt_resfile.buffer[index]))
5301 return index - prt_resfile.line_start;
5302 index++;
5303 }
5304 return -1;
5305}
5306
5307 static int
5308prt_resfile_skip_ws(offset)
5309 int offset;
5310{
5311 int index;
5312
5313 index = prt_resfile.line_start + offset;
5314 while (index < prt_resfile.line_end)
5315 {
5316 if (!isspace(prt_resfile.buffer[index]))
5317 return index - prt_resfile.line_start;
5318 index++;
5319 }
5320 return -1;
5321}
5322
5323/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
5324 * if a DSC comment is found, else false */
5325 static int
5326prt_next_dsc(p_dsc_line)
5327 struct prt_dsc_line_S *p_dsc_line;
5328{
5329 int comment;
5330 int offset;
5331
5332 /* Move to start of next line */
5333 if (!prt_resfile_next_line())
5334 return FALSE;
5335
5336 /* DSC comments always start %% */
5337 if (prt_resfile_strncmp(0, "%%", 2) != 0)
5338 return FALSE;
5339
5340 /* Find type of DSC comment */
5341 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
5342 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
5343 prt_dsc_table[comment].len) == 0)
5344 break;
5345
5346 if (comment != NUM_ELEMENTS(prt_dsc_table))
5347 {
5348 /* Return type of comment */
5349 p_dsc_line->type = prt_dsc_table[comment].type;
5350 offset = prt_dsc_table[comment].len;
5351 }
5352 else
5353 {
5354 /* Unrecognised DSC comment, skip to ws after comment leader */
5355 p_dsc_line->type = PRT_DSC_MISC_TYPE;
5356 offset = prt_resfile_skip_nonws(0);
5357 if (offset == -1)
5358 return FALSE;
5359 }
5360
5361 /* Skip ws to comment value */
5362 offset = prt_resfile_skip_ws(offset);
5363 if (offset == -1)
5364 return FALSE;
5365
5366 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
5367 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
5368
5369 return TRUE;
5370}
5371
5372/* Improved hand crafted parser to get the type, title, and version number of a
5373 * PS resource file so the file details can be added to the DSC header comments.
5374 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005375 static int
5376prt_open_resource(resource)
5377 struct prt_ps_resource_S *resource;
5378{
Bram Moolenaar8299df92004-07-10 09:47:34 +00005379 int offset;
5380 int seen_all;
5381 int seen_title;
5382 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005383 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005384 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005385
5386 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5387 if (fd_resource == NULL)
5388 {
5389 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
5390 return FALSE;
5391 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005392 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393
5394 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005395 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
5396 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397 if (ferror(fd_resource))
5398 {
5399 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5400 resource->filename);
5401 fclose(fd_resource);
5402 return FALSE;
5403 }
5404
Bram Moolenaar8299df92004-07-10 09:47:34 +00005405 prt_resfile.line_end = -1;
5406 prt_resfile.line_start = 0;
5407 if (!prt_resfile_next_line())
5408 return FALSE;
5409
5410 offset = 0;
5411
5412 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
5413 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005414 {
5415 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
5416 resource->filename);
5417 fclose(fd_resource);
5418 return FALSE;
5419 }
5420
5421 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005422 offset += STRLEN(PRT_RESOURCE_HEADER);
5423 offset = prt_resfile_skip_nonws(offset);
5424 if (offset == -1)
5425 return FALSE;
5426 offset = prt_resfile_skip_ws(offset);
5427 if (offset == -1)
5428 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429
Bram Moolenaar8299df92004-07-10 09:47:34 +00005430 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
5431 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005432 {
5433 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5434 resource->filename);
5435 fclose(fd_resource);
5436 return FALSE;
5437 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005438 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439
5440 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005441 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
5442 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005444 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
5445 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005446 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005447 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
5448 STRLEN(PRT_RESOURCE_CMAP)) == 0)
5449 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 else
5451 {
5452 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5453 resource->filename);
5454 fclose(fd_resource);
5455 return FALSE;
5456 }
5457
Bram Moolenaar8299df92004-07-10 09:47:34 +00005458 /* Look for title and version of resource */
5459 resource->title[0] = '\0';
5460 resource->version[0] = '\0';
5461 seen_title = FALSE;
5462 seen_version = FALSE;
5463 seen_all = FALSE;
5464 while (!seen_all && prt_next_dsc(&dsc_line))
5465 {
5466 switch (dsc_line.type)
5467 {
5468 case PRT_DSC_TITLE_TYPE:
5469 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
5470 resource->title[dsc_line.len] = '\0';
5471 seen_title = TRUE;
5472 if (seen_version)
5473 seen_all = TRUE;
5474 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475
Bram Moolenaar8299df92004-07-10 09:47:34 +00005476 case PRT_DSC_VERSION_TYPE:
5477 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
5478 resource->version[dsc_line.len] = '\0';
5479 seen_version = TRUE;
5480 if (seen_title)
5481 seen_all = TRUE;
5482 break;
5483
5484 case PRT_DSC_ENDCOMMENTS_TYPE:
5485 /* Wont find title or resource after this comment, stop searching */
5486 seen_all = TRUE;
5487 break;
5488
5489 case PRT_DSC_MISC_TYPE:
5490 /* Not interested in whatever comment this line had */
5491 break;
5492 }
5493 }
5494
5495 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005496 {
5497 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5498 resource->filename);
5499 fclose(fd_resource);
5500 return FALSE;
5501 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502
5503 fclose(fd_resource);
5504
5505 return TRUE;
5506}
5507
5508 static int
5509prt_check_resource(resource, version)
5510 struct prt_ps_resource_S *resource;
5511 char_u *version;
5512{
5513 /* Version number m.n should match, the revision number does not matter */
5514 if (STRNCMP(resource->version, version, STRLEN(version)))
5515 {
5516 EMSG2(_("E621: \"%s\" resource file has wrong version"),
5517 resource->name);
5518 return FALSE;
5519 }
5520
5521 /* Other checks to be added as needed */
5522 return TRUE;
5523}
5524
5525 static void
5526prt_dsc_start()
5527{
5528 prt_write_string("%!PS-Adobe-3.0\n");
5529}
5530
5531 static void
5532prt_dsc_noarg(comment)
5533 char *comment;
5534{
5535 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
5536 prt_write_file(prt_line_buffer);
5537}
5538
5539 static void
5540prt_dsc_textline(comment, text)
5541 char *comment;
5542 char *text;
5543{
5544 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
5545 prt_write_file(prt_line_buffer);
5546}
5547
5548 static void
5549prt_dsc_text(comment, text)
5550 char *comment;
5551 char *text;
5552{
5553 /* TODO - should scan 'text' for any chars needing escaping! */
5554 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
5555 prt_write_file(prt_line_buffer);
5556}
5557
5558#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
5559
5560 static void
5561prt_dsc_ints(comment, count, ints)
5562 char *comment;
5563 int count;
5564 int *ints;
5565{
5566 int i;
5567
5568 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
5569 prt_write_file(prt_line_buffer);
5570
5571 for (i = 0; i < count; i++)
5572 {
5573 sprintf((char *)prt_line_buffer, " %d", ints[i]);
5574 prt_write_file(prt_line_buffer);
5575 }
5576
5577 prt_write_string("\n");
5578}
5579
5580 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00005581prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 char *comment; /* if NULL add to previous */
5583 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005584 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586 if (comment != NULL)
5587 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
5588 else
5589 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
5590 prt_write_file(prt_line_buffer);
5591
Bram Moolenaar8299df92004-07-10 09:47:34 +00005592 sprintf((char *)prt_line_buffer, " %s\n", string);
5593 prt_write_file(prt_line_buffer);
5594}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595
Bram Moolenaar8299df92004-07-10 09:47:34 +00005596 static void
5597prt_dsc_font_resource(resource, ps_font)
5598 char *resource;
5599 struct prt_ps_font_S *ps_font;
5600{
5601 int i;
5602
5603 prt_dsc_resources(resource, "font",
5604 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
5605 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
5606 if (ps_font->ps_fontname[i] != NULL)
5607 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608}
5609
5610 static void
5611prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
5612 int duplex;
5613 int tumble;
5614 int collate;
5615 int color;
5616 int num_copies;
5617{
5618 /* Only output the comment if we need to.
5619 * Note: tumble is ignored if we are not duplexing
5620 */
5621 if (!(duplex || collate || color || (num_copies > 1)))
5622 return;
5623
5624 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
5625 prt_write_file(prt_line_buffer);
5626
5627 if (duplex)
5628 {
5629 prt_write_string(" duplex");
5630 if (tumble)
5631 prt_write_string("(tumble)");
5632 }
5633 if (collate)
5634 prt_write_string(" collate");
5635 if (color)
5636 prt_write_string(" color");
5637 if (num_copies > 1)
5638 {
5639 prt_write_string(" numcopies(");
5640 /* Note: no space wanted so dont use prt_write_int() */
5641 sprintf((char *)prt_line_buffer, "%d", num_copies);
5642 prt_write_file(prt_line_buffer);
5643 prt_write_string(")");
5644 }
5645 prt_write_string("\n");
5646}
5647
5648 static void
5649prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
5650 char *paper_name;
5651 double width;
5652 double height;
5653 double weight;
5654 char *colour;
5655 char *type;
5656{
5657 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
5658 prt_write_file(prt_line_buffer);
5659 prt_write_real(width, 2);
5660 prt_write_real(height, 2);
5661 prt_write_real(weight, 2);
5662 if (colour == NULL)
5663 prt_write_string("()");
5664 else
5665 prt_write_string(colour);
5666 prt_write_string(" ");
5667 if (type == NULL)
5668 prt_write_string("()");
5669 else
5670 prt_write_string(type);
5671 prt_write_string("\n");
5672}
5673
5674 void
5675mch_print_cleanup()
5676{
5677#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005678 if (prt_out_mbyte)
5679 {
5680 int i;
5681
5682 /* Free off all CID font names created, but first clear duplicate
5683 * pointers to the same string (when the same font is used for more than
5684 * one style).
5685 */
5686 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5687 {
5688 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5689 vim_free(prt_ps_mb_font.ps_fontname[i]);
5690 prt_ps_mb_font.ps_fontname[i] = NULL;
5691 }
5692 }
5693
Bram Moolenaar071d4272004-06-13 20:20:40 +00005694 if (prt_do_conv)
5695 {
5696 convert_setup(&prt_conv, NULL, NULL);
5697 prt_do_conv = FALSE;
5698 }
5699#endif
5700 if (prt_ps_fd != NULL)
5701 {
5702 fclose(prt_ps_fd);
5703 prt_ps_fd = NULL;
5704 prt_file_error = FALSE;
5705 }
5706 if (prt_ps_file_name != NULL)
5707 {
5708 vim_free(prt_ps_file_name);
5709 prt_ps_file_name = NULL;
5710 }
5711}
5712
5713 static float
5714to_device_units(idx, physsize, def_number)
5715 int idx;
5716 double physsize;
5717 int def_number;
5718{
5719 float ret;
5720 int u;
5721 int nr;
5722
5723 u = prt_get_unit(idx);
5724 if (u == PRT_UNIT_NONE)
5725 {
5726 u = PRT_UNIT_PERC;
5727 nr = def_number;
5728 }
5729 else
5730 nr = printer_opts[idx].number;
5731
5732 switch (u)
5733 {
5734 case PRT_UNIT_INCH:
5735 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5736 break;
5737 case PRT_UNIT_MM:
5738 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5739 break;
5740 case PRT_UNIT_POINT:
5741 ret = (float)nr;
5742 break;
5743 case PRT_UNIT_PERC:
5744 default:
5745 ret = (float)(physsize * nr) / 100;
5746 break;
5747 }
5748
5749 return ret;
5750}
5751
5752/*
5753 * Calculate margins for given width and height from printoptions settings.
5754 */
5755 static void
5756prt_page_margins(width, height, left, right, top, bottom)
5757 double width;
5758 double height;
5759 double *left;
5760 double *right;
5761 double *top;
5762 double *bottom;
5763{
5764 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5765 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5766 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5767 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5768}
5769
5770 static void
5771prt_font_metrics(font_scale)
5772 int font_scale;
5773{
5774 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005775 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005776}
5777
5778
5779 static int
5780prt_get_cpl()
5781{
5782 if (prt_use_number())
5783 {
5784 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005785#ifdef FEAT_MBYTE
5786 /* If we are outputting multi-byte characters then line numbers will be
5787 * printed with half width characters
5788 */
5789 if (prt_out_mbyte)
5790 prt_number_width /= 2;
5791#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005792 prt_left_margin += prt_number_width;
5793 }
5794 else
5795 prt_number_width = 0.0;
5796
5797 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5798}
5799
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005800#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005801 static int
5802prt_build_cid_fontname(font, name, name_len)
5803 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005804 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005805 int name_len;
5806{
5807 char *fontname;
5808
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005809 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005810 if (fontname == NULL)
5811 return FALSE;
5812 STRNCPY(fontname, name, name_len);
5813 fontname[name_len] = '\0';
5814 prt_ps_mb_font.ps_fontname[font] = fontname;
5815
5816 return TRUE;
5817}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005818#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005819
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820/*
5821 * Get number of lines of text that fit on a page (excluding the header).
5822 */
5823 static int
5824prt_get_lpp()
5825{
5826 int lpp;
5827
5828 /*
5829 * Calculate offset to lower left corner of background rect based on actual
5830 * font height (based on its bounding box) and the line height, handling the
5831 * case where the font height can exceed the line height.
5832 */
5833 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005834 prt_ps_font->bbox_min_y);
5835 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005836 {
5837 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005838 (1000.0 - (prt_ps_font->bbox_max_y -
5839 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 }
5841
5842 /* Get height for topmost line based on background rect offset. */
5843 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5844
5845 /* Calculate lpp */
5846 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5847
5848 /* Adjust top margin if there is a header */
5849 prt_top_margin -= prt_line_height * prt_header_height();
5850
5851 return lpp - prt_header_height();
5852}
5853
Bram Moolenaar8299df92004-07-10 09:47:34 +00005854#ifdef FEAT_MBYTE
5855 static int
5856prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005857 char *p_encoding;
5858 struct prt_ps_mbfont_S *p_cmap;
5859 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005860{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005861 int mbenc;
5862 int enc_len;
5863 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005864
5865 *pp_mbenc = NULL;
5866 /* Look for recognised encoding */
5867 enc_len = STRLEN(p_encoding);
5868 p_mbenc = p_cmap->encodings;
5869 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5870 {
5871 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5872 {
5873 *pp_mbenc = p_mbenc;
5874 return TRUE;
5875 }
5876 p_mbenc++;
5877 }
5878 return FALSE;
5879}
5880
5881 static int
5882prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005883 char *p_charset;
5884 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005885 struct prt_ps_charset_S **pp_mbchar;
5886{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005887 int mbchar;
5888 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005889 struct prt_ps_charset_S *p_mbchar;
5890
5891 /* Look for recognised character set, using default if one is not given */
5892 if (*p_charset == NUL)
5893 p_charset = p_cmap->defcs;
5894 char_len = STRLEN(p_charset);
5895 p_mbchar = p_cmap->charsets;
5896 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5897 {
5898 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5899 {
5900 *pp_mbchar = p_mbchar;
5901 return TRUE;
5902 }
5903 p_mbchar++;
5904 }
5905 return FALSE;
5906}
5907#endif
5908
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909/*ARGSUSED*/
5910 int
5911mch_print_init(psettings, jobname, forceit)
5912 prt_settings_T *psettings;
5913 char_u *jobname;
5914 int forceit;
5915{
5916 int i;
5917 char *paper_name;
5918 int paper_strlen;
5919 int fontsize;
5920 char_u *p;
5921 double left;
5922 double right;
5923 double top;
5924 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005925#ifdef FEAT_MBYTE
5926 int cmap;
5927 int pmcs_len;
5928 char_u *p_encoding;
5929 struct prt_ps_encoding_S *p_mbenc;
5930 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005931 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005932#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005933
5934#if 0
5935 /*
5936 * TODO:
5937 * If "forceit" is false: pop up a dialog to select:
5938 * - printer name
5939 * - copies
5940 * - collated/uncollated
5941 * - duplex off/long side/short side
5942 * - paper size
5943 * - portrait/landscape
5944 * - font size
5945 *
5946 * If "forceit" is true: use the default printer and settings
5947 */
5948 if (forceit)
5949 s_pd.Flags |= PD_RETURNDEFAULT;
5950#endif
5951
5952 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005953 * Set up font and encoding.
5954 */
5955#ifdef FEAT_MBYTE
5956 p_encoding = enc_skip(p_penc);
5957 if (*p_encoding == NUL)
5958 p_encoding = enc_skip(p_enc);
5959
5960 /* Look for recognised multi-byte coding, and if the charset is recognised.
5961 * This is to cope with the fact that various unicode encodings are
5962 * supported in more than one of CJK. */
5963 p_mbenc = NULL;
5964 p_mbenc_first = NULL;
5965 p_mbchar = NULL;
5966 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005967 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5968 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005969 {
5970 if (p_mbenc_first == NULL)
5971 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005972 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5973 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005974 break;
5975 }
5976
5977 /* Use first encoding matched if no charset matched */
5978 if (p_mbchar == NULL && p_mbenc_first != NULL)
5979 p_mbenc = p_mbenc_first;
5980
5981 prt_out_mbyte = (p_mbenc != NULL);
5982 if (prt_out_mbyte)
5983 {
5984 /* Build CMap name - will be same for all multi-byte fonts used */
5985 prt_cmap[0] = '\0';
5986
5987 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5988
5989 if (!prt_custom_cmap)
5990 {
5991 /* Check encoding and character set are compatible */
5992 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5993 {
5994 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5995 return FALSE;
5996 }
5997
5998 /* Add charset name if not empty */
5999 if (p_mbchar->cmap_charset != NULL)
6000 {
6001 STRCAT(prt_cmap, p_mbchar->cmap_charset);
6002 STRCAT(prt_cmap, "-");
6003 }
6004 }
6005 else
6006 {
6007 /* Add custom CMap character set name */
6008 pmcs_len = STRLEN(p_pmcs);
6009 if (pmcs_len == 0)
6010 {
6011 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
6012 return FALSE;
6013 }
6014 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
6015 prt_cmap[pmcs_len] = '\0';
6016 STRCAT(prt_cmap, "-");
6017 }
6018
6019 /* CMap name ends with (optional) encoding name and -H for horizontal */
6020 if (p_mbenc->cmap_encoding != NULL)
6021 {
6022 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
6023 STRCAT(prt_cmap, "-");
6024 }
6025 STRCAT(prt_cmap, "H");
6026
6027 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
6028 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00006029 EMSG(_("E675: No default font specified for multi-byte printing."));
Bram Moolenaar8299df92004-07-10 09:47:34 +00006030 return FALSE;
6031 }
6032
6033 /* Derive CID font names with fallbacks if not defined */
6034 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
6035 mbfont_opts[OPT_MBFONT_REGULAR].string,
6036 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
6037 return FALSE;
6038 if (mbfont_opts[OPT_MBFONT_BOLD].present)
6039 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
6040 mbfont_opts[OPT_MBFONT_BOLD].string,
6041 mbfont_opts[OPT_MBFONT_BOLD].strlen))
6042 return FALSE;
6043 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
6044 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
6045 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
6046 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
6047 return FALSE;
6048 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
6049 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006050 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
6051 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006052 return FALSE;
6053
6054 /* Check if need to use Courier for ASCII code range, and if so pick up
6055 * the encoding to use */
6056 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
6057 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
6058 if (prt_use_courier)
6059 {
6060 /* Use national ASCII variant unless ASCII wanted */
6061 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
6062 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
6063 prt_ascii_encoding = "ascii";
6064 else
6065 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
6066 }
6067
6068 prt_ps_font = &prt_ps_mb_font;
6069 }
6070 else
6071#endif
6072 {
6073#ifdef FEAT_MBYTE
6074 prt_use_courier = FALSE;
6075#endif
6076 prt_ps_font = &prt_ps_courier_font;
6077 }
6078
6079 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006080 * Find the size of the paper and set the margins.
6081 */
6082 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
6083 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
6084 if (printer_opts[OPT_PRINT_PAPER].present)
6085 {
6086 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
6087 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
6088 }
6089 else
6090 {
6091 paper_name = "A4";
6092 paper_strlen = 2;
6093 }
6094 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
6095 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
6096 && STRNICMP(prt_mediasize[i].name, paper_name,
6097 paper_strlen) == 0)
6098 break;
6099 if (i == PRT_MEDIASIZE_LEN)
6100 i = 0;
6101 prt_media = i;
6102
6103 /*
6104 * Set PS pagesize based on media dimensions and print orientation.
6105 * Note: Media and page sizes have defined meanings in PostScript and should
6106 * be kept distinct. Media is the paper (or transparency, or ...) that is
6107 * printed on, whereas the page size is the area that the PostScript
6108 * interpreter renders into.
6109 */
6110 if (prt_portrait)
6111 {
6112 prt_page_width = prt_mediasize[i].width;
6113 prt_page_height = prt_mediasize[i].height;
6114 }
6115 else
6116 {
6117 prt_page_width = prt_mediasize[i].height;
6118 prt_page_height = prt_mediasize[i].width;
6119 }
6120
6121 /*
6122 * Set PS page margins based on the PS pagesize, not the mediasize - this
6123 * needs to be done before the cpl and lpp are calculated.
6124 */
6125 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
6126 &bottom);
6127 prt_left_margin = (float)left;
6128 prt_right_margin = (float)right;
6129 prt_top_margin = (float)top;
6130 prt_bottom_margin = (float)bottom;
6131
6132 /*
6133 * Set up the font size.
6134 */
6135 fontsize = PRT_PS_DEFAULT_FONTSIZE;
6136 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
6137 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
6138 fontsize = atoi((char *)p + 2);
6139 prt_font_metrics(fontsize);
6140
6141 /*
6142 * Return the number of characters per line, and lines per page for the
6143 * generic print code.
6144 */
6145 psettings->chars_per_line = prt_get_cpl();
6146 psettings->lines_per_page = prt_get_lpp();
6147
6148 /* Catch margin settings that leave no space for output! */
6149 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
6150 return FAIL;
6151
6152 /*
6153 * Sort out the number of copies to be printed. PS by default will do
6154 * uncollated copies for you, so once we know how many uncollated copies are
6155 * wanted cache it away and lie to the generic code that we only want one
6156 * uncollated copy.
6157 */
6158 psettings->n_collated_copies = 1;
6159 psettings->n_uncollated_copies = 1;
6160 prt_num_copies = 1;
6161 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
6162 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
6163 if (prt_collate)
6164 {
6165 /* TODO: Get number of collated copies wanted. */
6166 psettings->n_collated_copies = 1;
6167 }
6168 else
6169 {
6170 /* TODO: Get number of uncollated copies wanted and update the cached
6171 * count.
6172 */
6173 prt_num_copies = 1;
6174 }
6175
6176 psettings->jobname = jobname;
6177
6178 /*
6179 * Set up printer duplex and tumble based on Duplex option setting - default
6180 * is long sided duplex printing (i.e. no tumble).
6181 */
6182 prt_duplex = TRUE;
6183 prt_tumble = FALSE;
6184 psettings->duplex = 1;
6185 if (printer_opts[OPT_PRINT_DUPLEX].present)
6186 {
6187 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
6188 {
6189 prt_duplex = FALSE;
6190 psettings->duplex = 0;
6191 }
6192 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
6193 == 0)
6194 prt_tumble = TRUE;
6195 }
6196
6197 /* For now user abort not supported */
6198 psettings->user_abort = 0;
6199
6200 /* If the user didn't specify a file name, use a temp file. */
6201 if (psettings->outfile == NULL)
6202 {
6203 prt_ps_file_name = vim_tempname('p');
6204 if (prt_ps_file_name == NULL)
6205 {
6206 EMSG(_(e_notmp));
6207 return FAIL;
6208 }
6209 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
6210 }
6211 else
6212 {
6213 p = expand_env_save(psettings->outfile);
6214 if (p != NULL)
6215 {
6216 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
6217 vim_free(p);
6218 }
6219 }
6220 if (prt_ps_fd == NULL)
6221 {
6222 EMSG(_("E324: Can't open PostScript output file"));
6223 mch_print_cleanup();
6224 return FAIL;
6225 }
6226
Bram Moolenaar8299df92004-07-10 09:47:34 +00006227 prt_bufsiz = psettings->chars_per_line;
6228#ifdef FEAT_MBYTE
6229 if (prt_out_mbyte)
6230 prt_bufsiz *= 2;
6231#endif
6232 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006233
6234 prt_page_num = 0;
6235
6236 prt_attribute_change = FALSE;
6237 prt_need_moveto = FALSE;
6238 prt_need_font = FALSE;
6239 prt_need_fgcol = FALSE;
6240 prt_need_bgcol = FALSE;
6241 prt_need_underline = FALSE;
6242
6243 prt_file_error = FALSE;
6244
6245 return OK;
6246}
6247
6248 static int
6249prt_add_resource(resource)
6250 struct prt_ps_resource_S *resource;
6251{
6252 FILE* fd_resource;
6253 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254 size_t bytes_read;
6255
6256 fd_resource = mch_fopen((char *)resource->filename, READBIN);
6257 if (fd_resource == NULL)
6258 {
6259 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
6260 return FALSE;
6261 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006262 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
6263 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264
6265 prt_dsc_textline("BeginDocument", (char *)resource->filename);
6266
6267 for (;;)
6268 {
6269 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
6270 sizeof(resource_buffer), fd_resource);
6271 if (ferror(fd_resource))
6272 {
6273 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
6274 resource->filename);
6275 fclose(fd_resource);
6276 return FALSE;
6277 }
6278 if (bytes_read == 0)
6279 break;
6280 prt_write_file_raw_len(resource_buffer, bytes_read);
6281 if (prt_file_error)
6282 {
6283 fclose(fd_resource);
6284 return FALSE;
6285 }
6286 }
6287 fclose(fd_resource);
6288
6289 prt_dsc_noarg("EndDocument");
6290
6291 prt_dsc_noarg("EndResource");
6292
6293 return TRUE;
6294}
6295
6296 int
6297mch_print_begin(psettings)
6298 prt_settings_T *psettings;
6299{
6300 time_t now;
6301 int bbox[4];
6302 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006303 double left;
6304 double right;
6305 double top;
6306 double bottom;
6307 struct prt_ps_resource_S res_prolog;
6308 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006309 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 char_u *p_encoding;
6311#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006312 struct prt_ps_resource_S res_cidfont;
6313 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314#endif
6315
6316 /*
6317 * PS DSC Header comments - no PS code!
6318 */
6319 prt_dsc_start();
6320 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006321 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006322 STRCPY(buffer, "Unknown");
6323 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006324 prt_dsc_textline("Creator", VIM_VERSION_LONG);
6325 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
6326 now = time(NULL);
6327 p_time = ctime(&now);
6328 /* Note: ctime() adds a \n so we have to remove it :-( */
6329 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
6330 prt_dsc_textline("CreationDate", p_time);
6331 prt_dsc_textline("DocumentData", "Clean8Bit");
6332 prt_dsc_textline("Orientation", "Portrait");
6333 prt_dsc_atend("Pages");
6334 prt_dsc_textline("PageOrder", "Ascend");
6335 /* The bbox does not change with orientation - it is always in the default
6336 * user coordinate system! We have to recalculate right and bottom
6337 * coordinates based on the font metrics for the bbox to be accurate. */
6338 prt_page_margins(prt_mediasize[prt_media].width,
6339 prt_mediasize[prt_media].height,
6340 &left, &right, &top, &bottom);
6341 bbox[0] = (int)left;
6342 if (prt_portrait)
6343 {
6344 /* In portrait printing the fixed point is the top left corner so we
6345 * derive the bbox from that point. We have the expected cpl chars
6346 * across the media and lpp lines down the media.
6347 */
6348 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
6349 * prt_line_height);
6350 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
6351 + 0.5);
6352 bbox[3] = (int)(top + 0.5);
6353 }
6354 else
6355 {
6356 /* In landscape printing the fixed point is the bottom left corner so we
6357 * derive the bbox from that point. We have lpp chars across the media
6358 * and cpl lines up the media.
6359 */
6360 bbox[1] = (int)bottom;
6361 bbox[2] = (int)(left + ((psettings->lines_per_page
6362 + prt_header_height()) * prt_line_height) + 0.5);
6363 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
6364 + 0.5);
6365 }
6366 prt_dsc_ints("BoundingBox", 4, bbox);
6367 /* The media width and height does not change with landscape printing! */
6368 prt_dsc_docmedia(prt_mediasize[prt_media].name,
6369 prt_mediasize[prt_media].width,
6370 prt_mediasize[prt_media].height,
6371 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006372 /* Define fonts needed */
6373#ifdef FEAT_MBYTE
6374 if (!prt_out_mbyte || prt_use_courier)
6375#endif
6376 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
6377#ifdef FEAT_MBYTE
6378 if (prt_out_mbyte)
6379 {
6380 prt_dsc_font_resource((prt_use_courier ? NULL
6381 : "DocumentNeededResources"), &prt_ps_mb_font);
6382 if (!prt_custom_cmap)
6383 prt_dsc_resources(NULL, "cmap", prt_cmap);
6384 }
6385#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386
Bram Moolenaar8299df92004-07-10 09:47:34 +00006387 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388 if (!prt_find_resource("prolog", &res_prolog))
6389 {
6390 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
6391 return FALSE;
6392 }
6393 if (!prt_open_resource(&res_prolog))
6394 return FALSE;
6395 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
6396 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006397#ifdef FEAT_MBYTE
6398 if (prt_out_mbyte)
6399 {
6400 /* Look for required version of multi-byte printing procset */
6401 if (!prt_find_resource("cidfont", &res_cidfont))
6402 {
6403 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
6404 return FALSE;
6405 }
6406 if (!prt_open_resource(&res_cidfont))
6407 return FALSE;
6408 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
6409 return FALSE;
6410 }
6411#endif
6412
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413 /* Find an encoding to use for printing.
6414 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
6415 * that cannot be found then default to "latin1".
6416 * Note: VIM specific encoding header is always skipped.
6417 */
6418#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006419 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006421#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006422 p_encoding = enc_skip(p_penc);
6423 if (*p_encoding == NUL
6424 || !prt_find_resource((char *)p_encoding, &res_encoding))
6425 {
6426 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006428 int props;
6429
6430 p_encoding = enc_skip(p_enc);
6431 props = enc_canon_props(p_encoding);
6432 if (!(props & ENC_8BIT)
6433 || !prt_find_resource((char *)p_encoding, &res_encoding))
6434 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006435#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006436 {
6437 /* Use latin1 as default printing encoding */
6438 p_encoding = (char_u *)"latin1";
6439 if (!prt_find_resource((char *)p_encoding, &res_encoding))
6440 {
6441 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
6442 p_encoding);
6443 return FALSE;
6444 }
6445 }
6446 }
6447 if (!prt_open_resource(&res_encoding))
6448 return FALSE;
6449 /* For the moment there are no checks on encoding resource files to
6450 * perform */
6451#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006453 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006455 p_encoding = enc_skip(p_penc);
6456 if (*p_encoding == NUL)
6457 p_encoding = enc_skip(p_enc);
6458 if (prt_use_courier)
6459 {
6460 /* Include ASCII range encoding vector */
6461 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
6462 {
6463 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006464 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006465 return FALSE;
6466 }
6467 if (!prt_open_resource(&res_encoding))
6468 return FALSE;
6469 /* For the moment there are no checks on encoding resource files to
6470 * perform */
6471 }
6472 }
6473
6474 prt_conv.vc_type = CONV_NONE;
6475 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
6476 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
6478 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006479 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480 p_encoding);
6481 return FALSE;
6482 }
6483 prt_do_conv = TRUE;
6484 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006485 prt_do_conv = prt_conv.vc_type != CONV_NONE;
6486
6487 if (prt_out_mbyte && prt_custom_cmap)
6488 {
6489 /* Find user supplied CMap */
6490 if (!prt_find_resource(prt_cmap, &res_cmap))
6491 {
6492 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006493 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006494 return FALSE;
6495 }
6496 if (!prt_open_resource(&res_cmap))
6497 return FALSE;
6498 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499#endif
6500
6501 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006502 STRCPY(buffer, res_prolog.title);
6503 STRCAT(buffer, " ");
6504 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006505 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
6506#ifdef FEAT_MBYTE
6507 if (prt_out_mbyte)
6508 {
6509 STRCPY(buffer, res_cidfont.title);
6510 STRCAT(buffer, " ");
6511 STRCAT(buffer, res_cidfont.version);
6512 prt_dsc_resources(NULL, "procset", buffer);
6513
6514 if (prt_custom_cmap)
6515 {
6516 STRCPY(buffer, res_cmap.title);
6517 STRCAT(buffer, " ");
6518 STRCAT(buffer, res_cmap.version);
6519 prt_dsc_resources(NULL, "cmap", buffer);
6520 }
6521 }
6522 if (!prt_out_mbyte || prt_use_courier)
6523#endif
6524 {
6525 STRCPY(buffer, res_encoding.title);
6526 STRCAT(buffer, " ");
6527 STRCAT(buffer, res_encoding.version);
6528 prt_dsc_resources(NULL, "encoding", buffer);
6529 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
6531#ifdef FEAT_SYN_HL
6532 psettings->do_syntax
6533#else
6534 0
6535#endif
6536 , prt_num_copies);
6537 prt_dsc_noarg("EndComments");
6538
6539 /*
6540 * PS Document page defaults
6541 */
6542 prt_dsc_noarg("BeginDefaults");
6543
6544 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006545#ifdef FEAT_MBYTE
6546 if (!prt_out_mbyte || prt_use_courier)
6547#endif
6548 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
6549#ifdef FEAT_MBYTE
6550 if (prt_out_mbyte)
6551 {
6552 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
6553 &prt_ps_mb_font);
6554 if (!prt_custom_cmap)
6555 prt_dsc_resources(NULL, "cmap", prt_cmap);
6556 }
6557#endif
6558
Bram Moolenaar071d4272004-06-13 20:20:40 +00006559 /* Paper will be used for all pages */
6560 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
6561
6562 prt_dsc_noarg("EndDefaults");
6563
6564 /*
6565 * PS Document prolog inclusion - all required procsets.
6566 */
6567 prt_dsc_noarg("BeginProlog");
6568
Bram Moolenaar8299df92004-07-10 09:47:34 +00006569 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570 if (!prt_add_resource(&res_prolog))
6571 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006572#ifdef FEAT_MBYTE
6573 if (prt_out_mbyte)
6574 {
6575 /* Add CID font procset, and any user supplied CMap */
6576 if (!prt_add_resource(&res_cidfont))
6577 return FALSE;
6578 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
6579 return FALSE;
6580 }
6581#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582
Bram Moolenaar8299df92004-07-10 09:47:34 +00006583#ifdef FEAT_MBYTE
6584 if (!prt_out_mbyte || prt_use_courier)
6585#endif
6586 /* There will be only one Roman font encoding to be included in the PS
6587 * file. */
6588 if (!prt_add_resource(&res_encoding))
6589 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590
6591 prt_dsc_noarg("EndProlog");
6592
6593 /*
6594 * PS Document setup - must appear after the prolog
6595 */
6596 prt_dsc_noarg("BeginSetup");
6597
6598 /* Device setup - page size and number of uncollated copies */
6599 prt_write_int((int)prt_mediasize[prt_media].width);
6600 prt_write_int((int)prt_mediasize[prt_media].height);
6601 prt_write_int(0);
6602 prt_write_string("sps\n");
6603 prt_write_int(prt_num_copies);
6604 prt_write_string("nc\n");
6605 prt_write_boolean(prt_duplex);
6606 prt_write_boolean(prt_tumble);
6607 prt_write_string("dt\n");
6608 prt_write_boolean(prt_collate);
6609 prt_write_string("c\n");
6610
6611 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006612#ifdef FEAT_MBYTE
6613 if (!prt_out_mbyte || prt_use_courier)
6614 {
6615 /* When using Courier for ASCII range when printing multi-byte, need to
6616 * pick up ASCII encoding to use with it. */
6617 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006618 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006619#endif
6620 prt_dsc_resources("IncludeResource", "font",
6621 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6622 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
6623 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6624 prt_dsc_resources("IncludeResource", "font",
6625 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6626 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
6627 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6628 prt_dsc_resources("IncludeResource", "font",
6629 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6630 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
6631 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6632 prt_dsc_resources("IncludeResource", "font",
6633 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6634 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
6635 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6636#ifdef FEAT_MBYTE
6637 }
6638 if (prt_out_mbyte)
6639 {
6640 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
6641 * not have an italic form being a western style, so where no font is
6642 * defined for these faces VIM falls back to an existing face.
6643 * Note: if using Courier for the ASCII range then the printout will
6644 * have bold/italic/bolditalic regardless of the setting of printmbfont.
6645 */
6646 prt_dsc_resources("IncludeResource", "font",
6647 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6648 if (!prt_custom_cmap)
6649 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6650 prt_def_cidfont("CF0", (int)prt_line_height,
6651 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6652
6653 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
6654 {
6655 prt_dsc_resources("IncludeResource", "font",
6656 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6657 if (!prt_custom_cmap)
6658 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6659 prt_def_cidfont("CF1", (int)prt_line_height,
6660 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6661 }
6662 else
6663 /* Use ROMAN for BOLD */
6664 prt_dup_cidfont("CF0", "CF1");
6665
6666 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
6667 {
6668 prt_dsc_resources("IncludeResource", "font",
6669 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6670 if (!prt_custom_cmap)
6671 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6672 prt_def_cidfont("CF2", (int)prt_line_height,
6673 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6674 }
6675 else
6676 /* Use ROMAN for OBLIQUE */
6677 prt_dup_cidfont("CF0", "CF2");
6678
6679 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
6680 {
6681 prt_dsc_resources("IncludeResource", "font",
6682 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6683 if (!prt_custom_cmap)
6684 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6685 prt_def_cidfont("CF3", (int)prt_line_height,
6686 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6687 }
6688 else
6689 /* Use BOLD for BOLDOBLIQUE */
6690 prt_dup_cidfont("CF1", "CF3");
6691 }
6692#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006693
6694 /* Misc constant vars used for underlining and background rects */
6695 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006696 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006698 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699 prt_def_var("BO", prt_bgcol_offset, 2);
6700
6701 prt_dsc_noarg("EndSetup");
6702
6703 /* Fail if any problems writing out to the PS file */
6704 return !prt_file_error;
6705}
6706
6707 void
6708mch_print_end(psettings)
6709 prt_settings_T *psettings;
6710{
6711 prt_dsc_noarg("Trailer");
6712
6713 /*
6714 * Output any info we don't know in toto until we finish
6715 */
6716 prt_dsc_ints("Pages", 1, &prt_page_num);
6717
6718 prt_dsc_noarg("EOF");
6719
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006720 /* Write CTRL-D to close serial communication link if used.
6721 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006722 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006723
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724 if (!prt_file_error && psettings->outfile == NULL
6725 && !got_int && !psettings->user_abort)
6726 {
6727 /* Close the file first. */
6728 if (prt_ps_fd != NULL)
6729 {
6730 fclose(prt_ps_fd);
6731 prt_ps_fd = NULL;
6732 }
6733 prt_message((char_u *)_("Sending to printer..."));
6734
6735 /* Not printing to a file: use 'printexpr' to print the file. */
6736 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6737 EMSG(_("E365: Failed to print PostScript file"));
6738 else
6739 prt_message((char_u *)_("Print job sent."));
6740 }
6741
6742 mch_print_cleanup();
6743}
6744
6745 int
6746mch_print_end_page()
6747{
6748 prt_flush_buffer();
6749
6750 prt_write_string("re sp\n");
6751
6752 prt_dsc_noarg("PageTrailer");
6753
6754 return !prt_file_error;
6755}
6756
6757/*ARGSUSED*/
6758 int
6759mch_print_begin_page(str)
6760 char_u *str;
6761{
6762 int page_num[2];
6763
6764 prt_page_num++;
6765
6766 page_num[0] = page_num[1] = prt_page_num;
6767 prt_dsc_ints("Page", 2, page_num);
6768
6769 prt_dsc_noarg("BeginPageSetup");
6770
Bram Moolenaar8299df92004-07-10 09:47:34 +00006771 prt_write_string("sv\n0 g\n");
6772#ifdef FEAT_MBYTE
6773 prt_in_ascii = !prt_out_mbyte;
6774 if (prt_out_mbyte)
6775 prt_write_string("CF0 sf\n");
6776 else
6777#endif
6778 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 prt_fgcol = PRCOLOR_BLACK;
6780 prt_bgcol = PRCOLOR_WHITE;
6781 prt_font = PRT_PS_FONT_ROMAN;
6782
6783 /* Set up page transformation for landscape printing. */
6784 if (!prt_portrait)
6785 {
6786 prt_write_int(-((int)prt_mediasize[prt_media].width));
6787 prt_write_string("sl\n");
6788 }
6789
6790 prt_dsc_noarg("EndPageSetup");
6791
6792 /* We have reset the font attributes, force setting them again. */
6793 curr_bg = (long_u)0xffffffff;
6794 curr_fg = (long_u)0xffffffff;
6795 curr_bold = MAYBE;
6796
6797 return !prt_file_error;
6798}
6799
6800 int
6801mch_print_blank_page()
6802{
6803 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6804}
6805
6806static float prt_pos_x = 0;
6807static float prt_pos_y = 0;
6808
6809 void
6810mch_print_start_line(margin, page_line)
6811 int margin;
6812 int page_line;
6813{
6814 prt_pos_x = prt_left_margin;
6815 if (margin)
6816 prt_pos_x -= prt_number_width;
6817
6818 prt_pos_y = prt_top_margin - prt_first_line_height -
6819 page_line * prt_line_height;
6820
6821 prt_attribute_change = TRUE;
6822 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006823#ifdef FEAT_MBYTE
6824 prt_half_width = FALSE;
6825#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006826}
6827
6828/*ARGSUSED*/
6829 int
6830mch_print_text_out(p, len)
6831 char_u *p;
6832 int len;
6833{
6834 int need_break;
6835 char_u ch;
6836 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006837 float char_width;
6838 float next_pos;
6839#ifdef FEAT_MBYTE
6840 int in_ascii;
6841 int half_width;
6842#endif
6843
6844 char_width = prt_char_width;
6845
6846#ifdef FEAT_MBYTE
6847 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6848 * CJKV font to do what VIM is doing here - use a Roman font for characters
6849 * in the ASCII range, and the origingal CID font for everything else.
6850 * The problem is that GhostScript still (as of 8.13) does not support
6851 * rearranged fonts even though they have been documented by Adobe for 7
6852 * years! If they ever do, a lot of this code will disappear.
6853 */
6854 if (prt_use_courier)
6855 {
6856 in_ascii = (len == 1 && *p < 0x80);
6857 if (prt_in_ascii)
6858 {
6859 if (!in_ascii)
6860 {
6861 /* No longer in ASCII range - need to switch font */
6862 prt_in_ascii = FALSE;
6863 prt_need_font = TRUE;
6864 prt_attribute_change = TRUE;
6865 }
6866 }
6867 else if (in_ascii)
6868 {
6869 /* Now in ASCII range - need to switch font */
6870 prt_in_ascii = TRUE;
6871 prt_need_font = TRUE;
6872 prt_attribute_change = TRUE;
6873 }
6874 }
6875 if (prt_out_mbyte)
6876 {
6877 half_width = ((*mb_ptr2cells)(p) == 1);
6878 if (half_width)
6879 char_width /= 2;
6880 if (prt_half_width)
6881 {
6882 if (!half_width)
6883 {
6884 prt_half_width = FALSE;
6885 prt_pos_x += prt_char_width/4;
6886 prt_need_moveto = TRUE;
6887 prt_attribute_change = TRUE;
6888 }
6889 }
6890 else if (half_width)
6891 {
6892 prt_half_width = TRUE;
6893 prt_pos_x += prt_char_width/4;
6894 prt_need_moveto = TRUE;
6895 prt_attribute_change = TRUE;
6896 }
6897 }
6898#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006899
6900 /* Output any required changes to the graphics state, after flushing any
6901 * text buffered so far.
6902 */
6903 if (prt_attribute_change)
6904 {
6905 prt_flush_buffer();
6906 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006907 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006908
6909 if (prt_need_moveto)
6910 {
6911 prt_pos_x_moveto = prt_pos_x;
6912 prt_pos_y_moveto = prt_pos_y;
6913 prt_do_moveto = TRUE;
6914
6915 prt_need_moveto = FALSE;
6916 }
6917 if (prt_need_font)
6918 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006919#ifdef FEAT_MBYTE
6920 if (!prt_in_ascii)
6921 prt_write_string("CF");
6922 else
6923#endif
6924 prt_write_string("F");
6925 prt_write_int(prt_font);
6926 prt_write_string("sf\n");
6927 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928 }
6929 if (prt_need_fgcol)
6930 {
6931 int r, g, b;
6932 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6933 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6934 b = prt_fgcol & 0xff;
6935
6936 prt_write_real(r / 255.0, 3);
6937 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006938 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006939 else
6940 {
6941 prt_write_real(g / 255.0, 3);
6942 prt_write_real(b / 255.0, 3);
6943 prt_write_string("r\n");
6944 }
6945 prt_need_fgcol = FALSE;
6946 }
6947
6948 if (prt_bgcol != PRCOLOR_WHITE)
6949 {
6950 prt_new_bgcol = prt_bgcol;
6951 if (prt_need_bgcol)
6952 prt_do_bgcol = TRUE;
6953 }
6954 else
6955 prt_do_bgcol = FALSE;
6956 prt_need_bgcol = FALSE;
6957
6958 if (prt_need_underline)
6959 prt_do_underline = prt_underline;
6960 prt_need_underline = FALSE;
6961
6962 prt_attribute_change = FALSE;
6963 }
6964
6965#ifdef FEAT_MBYTE
6966 if (prt_do_conv)
6967 {
6968 /* Convert from multi-byte to 8-bit encoding */
6969 p = string_convert(&prt_conv, p, &len);
6970 if (p == NULL)
6971 p = (char_u *)"";
6972 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006973
Bram Moolenaar8299df92004-07-10 09:47:34 +00006974 if (prt_out_mbyte)
6975 {
6976 /* Multi-byte character strings are represented more efficiently as hex
6977 * strings when outputting clean 8 bit PS.
6978 */
6979 do
6980 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006981 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006982 ga_append(&prt_ps_buffer, ch);
6983 ch = prt_hexchar[(*p) & 0xf];
6984 ga_append(&prt_ps_buffer, ch);
6985 p++;
6986 }
6987 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006988 }
6989 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006990#endif
6991 {
6992 /* Add next character to buffer of characters to output.
6993 * Note: One printed character may require several PS characters to
6994 * represent it, but we only count them as one printed character.
6995 */
6996 ch = *p;
6997 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6998 {
6999 /* Convert non-printing characters to either their escape or octal
7000 * sequence, ensures PS sent over a serial line does not interfere
7001 * with the comms protocol. Note: For EBCDIC we need to write out
7002 * the escape sequences as ASCII codes!
7003 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
7004 */
7005 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
7006 switch (ch)
7007 {
7008 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
7009 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
7010 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
7011 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
7012 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
7013 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
7014 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
7015 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
7016
7017 default:
7018 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
7019#ifdef EBCDIC
7020 ebcdic2ascii(ch_buff, 3);
7021#endif
7022 ga_append(&prt_ps_buffer, ch_buff[0]);
7023 ga_append(&prt_ps_buffer, ch_buff[1]);
7024 ga_append(&prt_ps_buffer, ch_buff[2]);
7025 break;
7026 }
7027 }
7028 else
7029 ga_append(&prt_ps_buffer, ch);
7030 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007031
7032#ifdef FEAT_MBYTE
7033 /* Need to free any translated characters */
7034 if (prt_do_conv && (*p != NUL))
7035 vim_free(p);
7036#endif
7037
Bram Moolenaar8299df92004-07-10 09:47:34 +00007038 prt_text_run += char_width;
7039 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007040
Bram Moolenaar8299df92004-07-10 09:47:34 +00007041 /* The downside of fp - use relative error on right margin check */
7042 next_pos = prt_pos_x + prt_char_width;
7043 need_break = (next_pos > prt_right_margin) &&
7044 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007045
7046 if (need_break)
7047 prt_flush_buffer();
7048
7049 return need_break;
7050}
7051
7052 void
7053mch_print_set_font(iBold, iItalic, iUnderline)
7054 int iBold;
7055 int iItalic;
7056 int iUnderline;
7057{
7058 int font = 0;
7059
7060 if (iBold)
7061 font |= 0x01;
7062 if (iItalic)
7063 font |= 0x02;
7064
7065 if (font != prt_font)
7066 {
7067 prt_font = font;
7068 prt_attribute_change = TRUE;
7069 prt_need_font = TRUE;
7070 }
7071 if (prt_underline != iUnderline)
7072 {
7073 prt_underline = iUnderline;
7074 prt_attribute_change = TRUE;
7075 prt_need_underline = TRUE;
7076 }
7077}
7078
7079 void
7080mch_print_set_bg(bgcol)
7081 long_u bgcol;
7082{
7083 prt_bgcol = bgcol;
7084 prt_attribute_change = TRUE;
7085 prt_need_bgcol = TRUE;
7086}
7087
7088 void
7089mch_print_set_fg(fgcol)
7090 long_u fgcol;
7091{
7092 if (fgcol != (long_u)prt_fgcol)
7093 {
7094 prt_fgcol = fgcol;
7095 prt_attribute_change = TRUE;
7096 prt_need_fgcol = TRUE;
7097 }
7098}
7099
7100# endif /*FEAT_POSTSCRIPT*/
7101#endif /*FEAT_PRINTER*/
7102
7103#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7104 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
7105static char *get_locale_val __ARGS((int what));
7106
7107 static char *
7108get_locale_val(what)
7109 int what;
7110{
7111 char *loc;
7112
7113 /* Obtain the locale value from the libraries. For DJGPP this is
7114 * redefined and it doesn't use the arguments. */
7115 loc = setlocale(what, NULL);
7116
7117# if defined(__BORLANDC__)
7118 if (loc != NULL)
7119 {
7120 char_u *p;
7121
7122 /* Borland returns something like "LC_CTYPE=<name>\n"
7123 * Let's try to fix that bug here... */
7124 p = vim_strchr(loc, '=');
7125 if (p != NULL)
7126 {
7127 loc = ++p;
7128 while (*p != NUL) /* remove trailing newline */
7129 {
7130 if (*p < ' ')
7131 {
7132 *p = NUL;
7133 break;
7134 }
7135 ++p;
7136 }
7137 }
7138 }
7139# endif
7140
7141 return loc;
7142}
7143#endif
7144
7145
7146#ifdef WIN32
7147/*
7148 * On MS-Windows locale names are strings like "German_Germany.1252", but
7149 * gettext expects "de". Try to translate one into another here for a few
7150 * supported languages.
7151 */
7152 static char_u *
7153gettext_lang(char_u *name)
7154{
7155 int i;
7156 static char *(mtable[]) = {
7157 "afrikaans", "af",
7158 "czech", "cs",
7159 "dutch", "nl",
7160 "german", "de",
7161 "english_united kingdom", "en_GB",
7162 "spanish", "es",
7163 "french", "fr",
7164 "italian", "it",
7165 "japanese", "ja",
7166 "korean", "ko",
7167 "norwegian", "no",
7168 "polish", "pl",
7169 "russian", "ru",
7170 "slovak", "sk",
7171 "swedish", "sv",
7172 "ukrainian", "uk",
7173 "chinese_china", "zh_CN",
7174 "chinese_taiwan", "zh_TW",
7175 NULL};
7176
7177 for (i = 0; mtable[i] != NULL; i += 2)
7178 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
7179 return mtable[i + 1];
7180 return name;
7181}
7182#endif
7183
7184#if defined(FEAT_MULTI_LANG) || defined(PROTO)
7185/*
7186 * Obtain the current messages language. Used to set the default for
7187 * 'helplang'. May return NULL or an empty string.
7188 */
7189 char_u *
7190get_mess_lang()
7191{
7192 char_u *p;
7193
7194# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
7195# if defined(LC_MESSAGES)
7196 p = (char_u *)get_locale_val(LC_MESSAGES);
7197# else
7198 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
7199 * may be set to the LCID number. */
7200 p = (char_u *)get_locale_val(LC_ALL);
7201# endif
7202# else
7203 p = mch_getenv((char_u *)"LC_ALL");
7204 if (p == NULL || *p == NUL)
7205 {
7206 p = mch_getenv((char_u *)"LC_MESSAGES");
7207 if (p == NULL || *p == NUL)
7208 p = mch_getenv((char_u *)"LANG");
7209 }
7210# endif
7211# ifdef WIN32
7212 p = gettext_lang(p);
7213# endif
7214 return p;
7215}
7216#endif
7217
Bram Moolenaardef9e822004-12-31 20:58:58 +00007218/* Complicated #if; matches with where get_mess_env() is used below. */
7219#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7220 && defined(LC_MESSAGES))) \
7221 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7222 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
7223 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007224static char_u *get_mess_env __ARGS((void));
7225
7226/*
7227 * Get the language used for messages from the environment.
7228 */
7229 static char_u *
7230get_mess_env()
7231{
7232 char_u *p;
7233
7234 p = mch_getenv((char_u *)"LC_ALL");
7235 if (p == NULL || *p == NUL)
7236 {
7237 p = mch_getenv((char_u *)"LC_MESSAGES");
7238 if (p == NULL || *p == NUL)
7239 {
7240 p = mch_getenv((char_u *)"LANG");
7241 if (p != NULL && VIM_ISDIGIT(*p))
7242 p = NULL; /* ignore something like "1043" */
7243# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7244 if (p == NULL || *p == NUL)
7245 p = (char_u *)get_locale_val(LC_CTYPE);
7246# endif
7247 }
7248 }
7249 return p;
7250}
7251#endif
7252
7253#if defined(FEAT_EVAL) || defined(PROTO)
7254
7255/*
7256 * Set the "v:lang" variable according to the current locale setting.
7257 * Also do "v:lc_time"and "v:ctype".
7258 */
7259 void
7260set_lang_var()
7261{
7262 char_u *loc;
7263
7264# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7265 loc = (char_u *)get_locale_val(LC_CTYPE);
7266# else
7267 /* setlocale() not supported: use the default value */
7268 loc = (char_u *)"C";
7269# endif
7270 set_vim_var_string(VV_CTYPE, loc, -1);
7271
7272 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
7273 * back to LC_CTYPE if it's empty. */
7274# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
7275 loc = (char_u *)get_locale_val(LC_MESSAGES);
7276# else
7277 loc = get_mess_env();
7278# endif
7279 set_vim_var_string(VV_LANG, loc, -1);
7280
7281# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7282 loc = (char_u *)get_locale_val(LC_TIME);
7283# endif
7284 set_vim_var_string(VV_LC_TIME, loc, -1);
7285}
7286#endif
7287
7288#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7289 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
7290/*
7291 * ":language": Set the language (locale).
7292 */
7293 void
7294ex_language(eap)
7295 exarg_T *eap;
7296{
7297 char *loc;
7298 char_u *p;
7299 char_u *name;
7300 int what = LC_ALL;
7301 char *whatstr = "";
7302#ifdef LC_MESSAGES
7303# define VIM_LC_MESSAGES LC_MESSAGES
7304#else
7305# define VIM_LC_MESSAGES 6789
7306#endif
7307
7308 name = eap->arg;
7309
7310 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
7311 * Allow abbreviation, but require at least 3 characters to avoid
7312 * confusion with a two letter language name "me" or "ct". */
7313 p = skiptowhite(eap->arg);
7314 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
7315 {
7316 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
7317 {
7318 what = VIM_LC_MESSAGES;
7319 name = skipwhite(p);
7320 whatstr = "messages ";
7321 }
7322 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
7323 {
7324 what = LC_CTYPE;
7325 name = skipwhite(p);
7326 whatstr = "ctype ";
7327 }
7328 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
7329 {
7330 what = LC_TIME;
7331 name = skipwhite(p);
7332 whatstr = "time ";
7333 }
7334 }
7335
7336 if (*name == NUL)
7337 {
7338#ifndef LC_MESSAGES
7339 if (what == VIM_LC_MESSAGES)
7340 p = get_mess_env();
7341 else
7342#endif
7343 p = (char_u *)setlocale(what, NULL);
7344 if (p == NULL || *p == NUL)
7345 p = (char_u *)"Unknown";
7346 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
7347 }
7348 else
7349 {
7350#ifndef LC_MESSAGES
7351 if (what == VIM_LC_MESSAGES)
7352 loc = "";
7353 else
7354#endif
7355 loc = setlocale(what, (char *)name);
7356 if (loc == NULL)
7357 EMSG2(_("E197: Cannot set language to \"%s\""), name);
7358 else
7359 {
7360#ifdef HAVE_NL_MSG_CAT_CNTR
7361 /* Need to do this for GNU gettext, otherwise cached translations
7362 * will be used again. */
7363 extern int _nl_msg_cat_cntr;
7364
7365 ++_nl_msg_cat_cntr;
7366#endif
7367 /* Reset $LC_ALL, otherwise it would overrule everyting. */
7368 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
7369
7370 if (what != LC_TIME)
7371 {
7372 /* Tell gettext() what to translate to. It apparently doesn't
7373 * use the currently effective locale. Also do this when
7374 * FEAT_GETTEXT isn't defined, so that shell commands use this
7375 * value. */
7376 if (what == LC_ALL)
7377 vim_setenv((char_u *)"LANG", name);
7378 if (what != LC_CTYPE)
7379 {
7380 char_u *mname;
7381#ifdef WIN32
7382 mname = gettext_lang(name);
7383#else
7384 mname = name;
7385#endif
7386 vim_setenv((char_u *)"LC_MESSAGES", mname);
7387#ifdef FEAT_MULTI_LANG
7388 set_helplang_default(mname);
7389#endif
7390 }
7391
7392 /* Set $LC_CTYPE, because it overrules $LANG, and
7393 * gtk_set_locale() calls setlocale() again. gnome_init()
7394 * sets $LC_CTYPE to "en_US" (that's a bug!). */
7395 if (what != VIM_LC_MESSAGES)
7396 vim_setenv((char_u *)"LC_CTYPE", name);
7397# ifdef FEAT_GUI_GTK
7398 /* Let GTK know what locale we're using. Not sure this is
7399 * really needed... */
7400 if (gui.in_use)
7401 (void)gtk_set_locale();
7402# endif
7403 }
7404
7405# ifdef FEAT_EVAL
7406 /* Set v:lang, v:lc_time and v:ctype to the final result. */
7407 set_lang_var();
7408# endif
7409 }
7410 }
7411}
7412
7413# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
7414/*
7415 * Function given to ExpandGeneric() to obtain the possible arguments of the
7416 * ":language" command.
7417 */
7418/*ARGSUSED*/
7419 char_u *
7420get_lang_arg(xp, idx)
7421 expand_T *xp;
7422 int idx;
7423{
7424 if (idx == 0)
7425 return (char_u *)"messages";
7426 if (idx == 1)
7427 return (char_u *)"ctype";
7428 if (idx == 2)
7429 return (char_u *)"time";
7430 return NULL;
7431}
7432# endif
7433
7434#endif