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