blob: 1e7267eb36913fe3833ad9ba0cb7ea308b993a78 [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;
3128 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003129#ifdef FEAT_PROFILE
3130 if (line != NULL && do_profiling)
3131 script_line_start();
3132#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133
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;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003687 prt_text_attr_T *pattr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 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);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003698 pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699
3700# ifdef FEAT_GUI
3701 if (gui.in_use)
3702 {
3703 bg_color = highlight_gui_color_rgb(hl_id, FALSE);
3704 if (bg_color == PRCOLOR_BLACK)
3705 bg_color = PRCOLOR_WHITE;
3706
3707 fg_color = highlight_gui_color_rgb(hl_id, TRUE);
3708 }
3709 else
3710# endif
3711 {
3712 bg_color = PRCOLOR_WHITE;
3713
3714 color = (char *)highlight_color(hl_id, (char_u *)"fg", modec);
3715 if (color == NULL)
3716 colorindex = 0;
3717 else
3718 colorindex = atoi(color);
3719
3720 if (colorindex >= 0 && colorindex < t_colors)
3721 fg_color = prt_get_term_color(colorindex);
3722 else
3723 fg_color = PRCOLOR_BLACK;
3724 }
3725
3726 if (fg_color == PRCOLOR_WHITE)
3727 fg_color = PRCOLOR_BLACK;
3728 else if (*p_bg == 'd')
3729 fg_color = darken_rgb(fg_color);
3730
3731 pattr->fg_color = fg_color;
3732 pattr->bg_color = bg_color;
3733}
3734#endif /* FEAT_SYN_HL */
3735
3736 static void
3737prt_set_fg(fg)
3738 long_u fg;
3739{
3740 if (fg != curr_fg)
3741 {
3742 curr_fg = fg;
3743 mch_print_set_fg(fg);
3744 }
3745}
3746
3747 static void
3748prt_set_bg(bg)
3749 long_u bg;
3750{
3751 if (bg != curr_bg)
3752 {
3753 curr_bg = bg;
3754 mch_print_set_bg(bg);
3755 }
3756}
3757
3758 static void
3759prt_set_font(bold, italic, underline)
3760 int bold;
3761 int italic;
3762 int underline;
3763{
3764 if (curr_bold != bold
3765 || curr_italic != italic
3766 || curr_underline != underline)
3767 {
3768 curr_underline = underline;
3769 curr_italic = italic;
3770 curr_bold = bold;
3771 mch_print_set_font(bold, italic, underline);
3772 }
3773}
3774
3775/*
3776 * Print the line number in the left margin.
3777 */
3778 static void
3779prt_line_number(psettings, page_line, lnum)
3780 prt_settings_T *psettings;
3781 int page_line;
3782 linenr_T lnum;
3783{
3784 int i;
3785 char_u tbuf[20];
3786
3787 prt_set_fg(psettings->number.fg_color);
3788 prt_set_bg(psettings->number.bg_color);
3789 prt_set_font(psettings->number.bold, psettings->number.italic, psettings->number.underline);
3790 mch_print_start_line(TRUE, page_line);
3791
3792 /* Leave two spaces between the number and the text; depends on
3793 * PRINT_NUMBER_WIDTH. */
3794 sprintf((char *)tbuf, "%6ld", (long)lnum);
3795 for (i = 0; i < 6; i++)
3796 (void)mch_print_text_out(&tbuf[i], 1);
3797
3798#ifdef FEAT_SYN_HL
3799 if (psettings->do_syntax)
3800 /* Set colors for next character. */
3801 current_syn_id = -1;
3802 else
3803#endif
3804 {
3805 /* Set colors and font back to normal. */
3806 prt_set_fg(PRCOLOR_BLACK);
3807 prt_set_bg(PRCOLOR_WHITE);
3808 prt_set_font(FALSE, FALSE, FALSE);
3809 }
3810}
3811
3812static linenr_T printer_page_num;
3813
3814 int
3815get_printer_page_num()
3816{
3817 return printer_page_num;
3818}
3819
3820/*
3821 * Get the currently effective header height.
3822 */
3823 int
3824prt_header_height()
3825{
3826 if (printer_opts[OPT_PRINT_HEADERHEIGHT].present)
3827 return printer_opts[OPT_PRINT_HEADERHEIGHT].number;
3828 return 2;
3829}
3830
3831/*
3832 * Return TRUE if using a line number for printing.
3833 */
3834 int
3835prt_use_number()
3836{
3837 return (printer_opts[OPT_PRINT_NUMBER].present
3838 && TOLOWER_ASC(printer_opts[OPT_PRINT_NUMBER].string[0]) == 'y');
3839}
3840
3841/*
3842 * Return the unit used in a margin item in 'printoptions'.
3843 * Returns PRT_UNIT_NONE if not recognized.
3844 */
3845 int
3846prt_get_unit(idx)
3847 int idx;
3848{
3849 int u = PRT_UNIT_NONE;
3850 int i;
3851 static char *(units[4]) = PRT_UNIT_NAMES;
3852
3853 if (printer_opts[idx].present)
3854 for (i = 0; i < 4; ++i)
3855 if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0)
3856 {
3857 u = i;
3858 break;
3859 }
3860 return u;
3861}
3862
3863/*
3864 * Print the page header.
3865 */
3866/*ARGSUSED*/
3867 static void
3868prt_header(psettings, pagenum, lnum)
3869 prt_settings_T *psettings;
3870 int pagenum;
3871 linenr_T lnum;
3872{
3873 int width = psettings->chars_per_line;
3874 int page_line;
3875 char_u *tbuf;
3876 char_u *p;
3877#ifdef FEAT_MBYTE
3878 int l;
3879#endif
3880
3881 /* Also use the space for the line number. */
3882 if (prt_use_number())
3883 width += PRINT_NUMBER_WIDTH;
3884
3885 tbuf = alloc(width + IOSIZE);
3886 if (tbuf == NULL)
3887 return;
3888
3889#ifdef FEAT_STL_OPT
3890 if (*p_header != NUL)
3891 {
3892 linenr_T tmp_lnum, tmp_topline, tmp_botline;
3893
3894 /*
3895 * Need to (temporarily) set current line number and first/last line
3896 * number on the 'window'. Since we don't know how long the page is,
3897 * set the first and current line number to the top line, and guess
3898 * that the page length is 64.
3899 */
3900 tmp_lnum = curwin->w_cursor.lnum;
3901 tmp_topline = curwin->w_topline;
3902 tmp_botline = curwin->w_botline;
3903 curwin->w_cursor.lnum = lnum;
3904 curwin->w_topline = lnum;
3905 curwin->w_botline = lnum + 63;
3906 printer_page_num = pagenum;
3907
3908 build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE),
3909 p_header, ' ', width, NULL);
3910
3911 /* Reset line numbers */
3912 curwin->w_cursor.lnum = tmp_lnum;
3913 curwin->w_topline = tmp_topline;
3914 curwin->w_botline = tmp_botline;
3915 }
3916 else
3917#endif
3918 sprintf((char *)tbuf, _("Page %d"), pagenum);
3919
3920 prt_set_fg(PRCOLOR_BLACK);
3921 prt_set_bg(PRCOLOR_WHITE);
3922 prt_set_font(TRUE, FALSE, FALSE);
3923
3924 /* Use a negative line number to indicate printing in the top margin. */
3925 page_line = 0 - prt_header_height();
3926 mch_print_start_line(TRUE, page_line);
3927 for (p = tbuf; *p != NUL; )
3928 {
3929 if (mch_print_text_out(p,
3930#ifdef FEAT_MBYTE
3931 (l = (*mb_ptr2len_check)(p))
3932#else
3933 1
3934#endif
3935 ))
3936 {
3937 ++page_line;
3938 if (page_line >= 0) /* out of room in header */
3939 break;
3940 mch_print_start_line(TRUE, page_line);
3941 }
3942#ifdef FEAT_MBYTE
3943 p += l;
3944#else
3945 p++;
3946#endif
3947 }
3948
3949 vim_free(tbuf);
3950
3951#ifdef FEAT_SYN_HL
3952 if (psettings->do_syntax)
3953 /* Set colors for next character. */
3954 current_syn_id = -1;
3955 else
3956#endif
3957 {
3958 /* Set colors and font back to normal. */
3959 prt_set_fg(PRCOLOR_BLACK);
3960 prt_set_bg(PRCOLOR_WHITE);
3961 prt_set_font(FALSE, FALSE, FALSE);
3962 }
3963}
3964
3965/*
3966 * Display a print status message.
3967 */
3968 static void
3969prt_message(s)
3970 char_u *s;
3971{
3972 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
3973 screen_puts(s, (int)Rows - 1, 0, hl_attr(HLF_R));
3974 out_flush();
3975}
3976
3977 void
3978ex_hardcopy(eap)
3979 exarg_T *eap;
3980{
3981 linenr_T lnum;
3982 int collated_copies, uncollated_copies;
3983 prt_settings_T settings;
3984 long_u bytes_to_print = 0;
3985 int page_line;
3986 int jobsplit;
3987 int id;
3988
3989 memset(&settings, 0, sizeof(prt_settings_T));
3990 settings.has_color = TRUE;
3991
3992# ifdef FEAT_POSTSCRIPT
3993 if (*eap->arg == '>')
3994 {
3995 char_u *errormsg = NULL;
3996
3997 /* Expand things like "%.ps". */
3998 if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL)
3999 {
4000 if (errormsg != NULL)
4001 EMSG(errormsg);
4002 return;
4003 }
4004 settings.outfile = skipwhite(eap->arg + 1);
4005 }
4006 else if (*eap->arg != NUL)
4007 settings.arguments = eap->arg;
4008# endif
4009
4010 /*
4011 * Initialise for printing. Ask the user for settings, unless forceit is
4012 * set.
4013 * The mch_print_init() code should set up margins if applicable. (It may
4014 * not be a real printer - for example the engine might generate HTML or
4015 * PS.)
4016 */
4017 if (mch_print_init(&settings,
4018 curbuf->b_fname == NULL
4019 ? (char_u *)buf_spname(curbuf)
4020 : curbuf->b_sfname == NULL
4021 ? curbuf->b_fname
4022 : curbuf->b_sfname,
4023 eap->forceit) == FAIL)
4024 return;
4025
4026#ifdef FEAT_SYN_HL
4027# ifdef FEAT_GUI
4028 if (gui.in_use)
4029 settings.modec = 'g';
4030 else
4031# endif
4032 if (t_colors > 1)
4033 settings.modec = 'c';
4034 else
4035 settings.modec = 't';
4036
4037 if (!syntax_present(curbuf))
4038 settings.do_syntax = FALSE;
4039 else if (printer_opts[OPT_PRINT_SYNTAX].present
4040 && TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a')
4041 settings.do_syntax =
4042 (TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) == 'y');
4043 else
4044 settings.do_syntax = settings.has_color;
4045#endif
4046
4047 /* Set up printing attributes for line numbers */
4048 settings.number.fg_color = PRCOLOR_BLACK;
4049 settings.number.bg_color = PRCOLOR_WHITE;
4050 settings.number.bold = FALSE;
4051 settings.number.italic = TRUE;
4052 settings.number.underline = FALSE;
4053#ifdef FEAT_SYN_HL
4054 /*
4055 * Syntax highlighting of line numbers.
4056 */
4057 if (prt_use_number() && settings.do_syntax)
4058 {
4059 id = syn_name2id((char_u *)"LineNr");
4060 if (id > 0)
4061 id = syn_get_final_id(id);
4062
4063 prt_get_attr(id, &settings.number, settings.modec);
4064 }
4065#endif /* FEAT_SYN_HL */
4066
4067 /*
4068 * Estimate the total lines to be printed
4069 */
4070 for (lnum = eap->line1; lnum <= eap->line2; lnum++)
4071 bytes_to_print += (long_u)STRLEN(skipwhite(ml_get(lnum)));
4072 if (bytes_to_print == 0)
4073 {
4074 MSG(_("No text to be printed"));
4075 goto print_fail_no_begin;
4076 }
4077
4078 /* Set colors and font to normal. */
4079 curr_bg = (long_u)0xffffffffL;
4080 curr_fg = (long_u)0xffffffffL;
4081 curr_italic = MAYBE;
4082 curr_bold = MAYBE;
4083 curr_underline = MAYBE;
4084
4085 prt_set_fg(PRCOLOR_BLACK);
4086 prt_set_bg(PRCOLOR_WHITE);
4087 prt_set_font(FALSE, FALSE, FALSE);
4088#ifdef FEAT_SYN_HL
4089 current_syn_id = -1;
4090#endif
4091
4092 jobsplit = (printer_opts[OPT_PRINT_JOBSPLIT].present
4093 && TOLOWER_ASC(printer_opts[OPT_PRINT_JOBSPLIT].string[0]) == 'y');
4094
4095 if (!mch_print_begin(&settings))
4096 goto print_fail_no_begin;
4097
4098 /*
4099 * Loop over collated copies: 1 2 3, 1 2 3, ...
4100 */
4101 page_count = 0;
4102 for (collated_copies = 0;
4103 collated_copies < settings.n_collated_copies;
4104 collated_copies++)
4105 {
4106 prt_pos_T prtpos; /* current print position */
4107 prt_pos_T page_prtpos; /* print position at page start */
4108 int side;
4109
4110 memset(&page_prtpos, 0, sizeof(prt_pos_T));
4111 page_prtpos.file_line = eap->line1;
4112 prtpos = page_prtpos;
4113
4114 if (jobsplit && collated_copies > 0)
4115 {
4116 /* Splitting jobs: Stop a previous job and start a new one. */
4117 mch_print_end(&settings);
4118 if (!mch_print_begin(&settings))
4119 goto print_fail_no_begin;
4120 }
4121
4122 /*
4123 * Loop over all pages in the print job: 1 2 3 ...
4124 */
4125 for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count)
4126 {
4127 /*
4128 * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
4129 * For duplex: 12 12 12 34 34 34, ...
4130 */
4131 for (uncollated_copies = 0;
4132 uncollated_copies < settings.n_uncollated_copies;
4133 uncollated_copies++)
4134 {
4135 /* Set the print position to the start of this page. */
4136 prtpos = page_prtpos;
4137
4138 /*
4139 * Do front and rear side of a page.
4140 */
4141 for (side = 0; side <= settings.duplex; ++side)
4142 {
4143 /*
4144 * Print one page.
4145 */
4146
4147 /* Check for interrupt character every page. */
4148 ui_breakcheck();
4149 if (got_int || settings.user_abort)
4150 goto print_fail;
4151
4152 sprintf((char *)IObuff, _("Printing page %d (%d%%)"),
4153 page_count + 1 + side,
4154 prtpos.bytes_printed > 1000000
4155 ? (int)(prtpos.bytes_printed /
4156 (bytes_to_print / 100))
4157 : (int)((prtpos.bytes_printed * 100)
4158 / bytes_to_print));
4159 if (!mch_print_begin_page(IObuff))
4160 goto print_fail;
4161
4162 if (settings.n_collated_copies > 1)
4163 sprintf((char *)IObuff + STRLEN(IObuff),
4164 _(" Copy %d of %d"),
4165 collated_copies + 1,
4166 settings.n_collated_copies);
4167 prt_message(IObuff);
4168
4169 /*
4170 * Output header if required
4171 */
4172 if (prt_header_height() > 0)
4173 prt_header(&settings, page_count + 1 + side,
4174 prtpos.file_line);
4175
4176 for (page_line = 0; page_line < settings.lines_per_page;
4177 ++page_line)
4178 {
4179 prtpos.column = hardcopy_line(&settings,
4180 page_line, &prtpos);
4181 if (prtpos.column == 0)
4182 {
4183 /* finished a file line */
4184 prtpos.bytes_printed +=
4185 STRLEN(skipwhite(ml_get(prtpos.file_line)));
4186 if (++prtpos.file_line > eap->line2)
4187 break; /* reached the end */
4188 }
4189 else if (prtpos.ff)
4190 {
4191 /* Line had a formfeed in it - start new page but
4192 * stay on the current line */
4193 break;
4194 }
4195 }
4196
4197 if (!mch_print_end_page())
4198 goto print_fail;
4199 if (prtpos.file_line > eap->line2)
4200 break; /* reached the end */
4201 }
4202
4203 /*
4204 * Extra blank page for duplexing with odd number of pages and
4205 * more copies to come.
4206 */
4207 if (prtpos.file_line > eap->line2 && settings.duplex
4208 && side == 0
4209 && uncollated_copies + 1 < settings.n_uncollated_copies)
4210 {
4211 if (!mch_print_blank_page())
4212 goto print_fail;
4213 }
4214 }
4215 if (settings.duplex && prtpos.file_line <= eap->line2)
4216 ++page_count;
4217
4218 /* Remember the position where the next page starts. */
4219 page_prtpos = prtpos;
4220 }
4221
4222 sprintf((char *)IObuff, _("Printed: %s"), settings.jobname);
4223 prt_message(IObuff);
4224 }
4225
4226print_fail:
4227 if (got_int || settings.user_abort)
4228 {
4229 sprintf((char *)IObuff, _("Printing aborted"));
4230 prt_message(IObuff);
4231 }
4232 mch_print_end(&settings);
4233
4234print_fail_no_begin:
4235 mch_print_cleanup();
4236}
4237
4238/*
4239 * Print one page line.
4240 * Return the next column to print, or zero if the line is finished.
4241 */
4242 static colnr_T
4243hardcopy_line(psettings, page_line, ppos)
4244 prt_settings_T *psettings;
4245 int page_line;
4246 prt_pos_T *ppos;
4247{
4248 colnr_T col;
4249 char_u *line;
4250 int need_break = FALSE;
4251 int outputlen;
4252 int tab_spaces;
4253 long_u print_pos;
4254#ifdef FEAT_SYN_HL
4255 prt_text_attr_T attr;
4256 int id;
4257#endif
4258
4259 if (ppos->column == 0 || ppos->ff)
4260 {
4261 print_pos = 0;
4262 tab_spaces = 0;
4263 if (!ppos->ff && prt_use_number())
4264 prt_line_number(psettings, page_line, ppos->file_line);
4265 ppos->ff = FALSE;
4266 }
4267 else
4268 {
4269 /* left over from wrap halfway a tab */
4270 print_pos = ppos->print_pos;
4271 tab_spaces = ppos->lead_spaces;
4272 }
4273
4274 mch_print_start_line(0, page_line);
4275 line = ml_get(ppos->file_line);
4276
4277 /*
4278 * Loop over the columns until the end of the file line or right margin.
4279 */
4280 for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen)
4281 {
4282 outputlen = 1;
4283#ifdef FEAT_MBYTE
4284 if (has_mbyte && (outputlen = (*mb_ptr2len_check)(line + col)) < 1)
4285 outputlen = 1;
4286#endif
4287#ifdef FEAT_SYN_HL
4288 /*
4289 * syntax highlighting stuff.
4290 */
4291 if (psettings->do_syntax)
4292 {
4293 id = syn_get_id(ppos->file_line, (long)col, 1);
4294 if (id > 0)
4295 id = syn_get_final_id(id);
4296 else
4297 id = 0;
4298 /* Get the line again, a multi-line regexp may invalidate it. */
4299 line = ml_get(ppos->file_line);
4300
4301 if (id != current_syn_id)
4302 {
4303 current_syn_id = id;
4304 prt_get_attr(id, &attr, psettings->modec);
4305 prt_set_font(attr.bold, attr.italic, attr.underline);
4306 prt_set_fg(attr.fg_color);
4307 prt_set_bg(attr.bg_color);
4308 }
4309 }
4310#endif /* FEAT_SYN_HL */
4311
4312 /*
4313 * Appropriately expand any tabs to spaces.
4314 */
4315 if (line[col] == TAB || tab_spaces != 0)
4316 {
4317 if (tab_spaces == 0)
4318 tab_spaces = curbuf->b_p_ts - (print_pos % curbuf->b_p_ts);
4319
4320 while (tab_spaces > 0)
4321 {
4322 need_break = mch_print_text_out((char_u *)" ", 1);
4323 print_pos++;
4324 tab_spaces--;
4325 if (need_break)
4326 break;
4327 }
4328 /* Keep the TAB if we didn't finish it. */
4329 if (need_break && tab_spaces > 0)
4330 break;
4331 }
4332 else if (line[col] == FF
4333 && printer_opts[OPT_PRINT_FORMFEED].present
4334 && TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
4335 == 'y')
4336 {
4337 ppos->ff = TRUE;
4338 need_break = 1;
4339 }
4340 else
4341 {
4342 need_break = mch_print_text_out(line + col, outputlen);
4343#ifdef FEAT_MBYTE
4344 if (has_mbyte)
4345 print_pos += (*mb_ptr2cells)(line + col);
4346 else
4347#endif
4348 print_pos++;
4349 }
4350 }
4351
4352 ppos->lead_spaces = tab_spaces;
4353 ppos->print_pos = print_pos;
4354
4355 /*
4356 * Start next line of file if we clip lines, or have reached end of the
4357 * line, unless we are doing a formfeed.
4358 */
4359 if (!ppos->ff
4360 && (line[col] == NUL
4361 || (printer_opts[OPT_PRINT_WRAP].present
4362 && TOLOWER_ASC(printer_opts[OPT_PRINT_WRAP].string[0])
4363 == 'n')))
4364 return 0;
4365 return col;
4366}
4367
4368# if defined(FEAT_POSTSCRIPT) || defined(PROTO)
4369
4370/*
4371 * PS printer stuff.
4372 *
4373 * Sources of information to help maintain the PS printing code:
4374 *
4375 * 1. PostScript Language Reference, 3rd Edition,
4376 * Addison-Wesley, 1999, ISBN 0-201-37922-8
4377 * 2. PostScript Language Program Design,
4378 * Addison-Wesley, 1988, ISBN 0-201-14396-8
4379 * 3. PostScript Tutorial and Cookbook,
4380 * Addison Wesley, 1985, ISBN 0-201-10179-3
4381 * 4. PostScript Language Document Structuring Conventions Specification,
4382 * version 3.0,
4383 * Adobe Technote 5001, 25th September 1992
4384 * 5. PostScript Printer Description File Format Specification, Version 4.3,
4385 * Adobe technote 5003, 9th February 1996
4386 * 6. Adobe Font Metrics File Format Specification, Version 4.1,
4387 * Adobe Technote 5007, 7th October 1998
Bram Moolenaar8299df92004-07-10 09:47:34 +00004388 * 7. Adobe CMap and CIDFont Files Specification, Version 1.0,
4389 * Adobe Technote 5014, 8th October 1996
4390 * 8. Adobe CJKV Character Collections and CMaps for CID-Keyed Fonts,
4391 * Adoboe Technote 5094, 8th September, 2001
4392 * 9. CJKV Information Processing, 2nd Edition,
4393 * O'Reilly, 2002, ISBN 1-56592-224-7
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 *
4395 * Some of these documents can be found in PDF form on Adobe's web site -
4396 * http://www.adobe.com
4397 */
4398
Bram Moolenaar8299df92004-07-10 09:47:34 +00004399#define NUM_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
4400
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401#define PRT_PS_DEFAULT_DPI (72) /* Default user space resolution */
4402#define PRT_PS_DEFAULT_FONTSIZE (10)
4403#define PRT_PS_DEFAULT_BUFFER_SIZE (80)
4404
4405struct prt_mediasize_S
4406{
4407 char *name;
4408 float width; /* width and height in points for portrait */
4409 float height;
4410};
4411
4412#define PRT_MEDIASIZE_LEN (sizeof(prt_mediasize) / sizeof(struct prt_mediasize_S))
4413
4414static struct prt_mediasize_S prt_mediasize[] =
4415{
4416 {"A4", 595.0, 842.0},
4417 {"letter", 612.0, 792.0},
4418 {"10x14", 720.0, 1008.0},
4419 {"A3", 842.0, 1191.0},
4420 {"A5", 420.0, 595.0},
4421 {"B4", 729.0, 1032.0},
4422 {"B5", 516.0, 729.0},
4423 {"executive", 522.0, 756.0},
4424 {"folio", 595.0, 935.0},
4425 {"ledger", 1224.0, 792.0}, /* Yes, it is wider than taller! */
4426 {"legal", 612.0, 1008.0},
4427 {"quarto", 610.0, 780.0},
4428 {"statement", 396.0, 612.0},
4429 {"tabloid", 792.0, 1224.0}
4430};
4431
4432/* PS font names, must be in Roman, Bold, Italic, Bold-Italic order */
4433struct prt_ps_font_S
4434{
4435 int wx;
4436 int uline_offset;
4437 int uline_width;
4438 int bbox_min_y;
4439 int bbox_max_y;
4440 char *(ps_fontname[4]);
4441};
4442
4443#define PRT_PS_FONT_ROMAN (0)
4444#define PRT_PS_FONT_BOLD (1)
4445#define PRT_PS_FONT_OBLIQUE (2)
4446#define PRT_PS_FONT_BOLDOBLIQUE (3)
4447
Bram Moolenaar8299df92004-07-10 09:47:34 +00004448/* Standard font metrics for Courier family */
4449static struct prt_ps_font_S prt_ps_courier_font =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450{
4451 600,
4452 -100, 50,
4453 -250, 805,
4454 {"Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique"}
4455};
4456
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004457#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00004458/* Generic font metrics for multi-byte fonts */
4459static struct prt_ps_font_S prt_ps_mb_font =
4460{
4461 1000,
4462 -100, 50,
4463 -250, 805,
4464 {NULL, NULL, NULL, NULL}
4465};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004466#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00004467
4468/* Pointer to current font set being used */
4469static struct prt_ps_font_S* prt_ps_font;
4470
4471/* Structures to map user named encoding and mapping to PS equivalents for
4472 * building CID font name */
4473struct prt_ps_encoding_S
4474{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004475 char *encoding;
4476 char *cmap_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004477 int needs_charset;
4478};
4479
4480struct prt_ps_charset_S
4481{
4482 char *charset;
4483 char *cmap_charset;
4484 int has_charset;
4485};
4486
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004487#ifdef FEAT_MBYTE
4488
Bram Moolenaar8299df92004-07-10 09:47:34 +00004489#define CS_JIS_C_1978 (0x01)
4490#define CS_JIS_X_1983 (0x02)
4491#define CS_JIS_X_1990 (0x04)
4492#define CS_NEC (0x08)
4493#define CS_MSWINDOWS (0x10)
4494#define CS_CP932 (0x20)
4495#define CS_KANJITALK6 (0x40)
4496#define CS_KANJITALK7 (0x80)
4497
4498/* Japanese encodings and charsets */
4499static struct prt_ps_encoding_S j_encodings[] =
4500{
4501 {"iso-2022-jp", NULL, (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990|
4502 CS_NEC)},
4503 {"euc-jp", "EUC", (CS_JIS_C_1978|CS_JIS_X_1983|CS_JIS_X_1990)},
4504 {"sjis", "RKSJ", (CS_JIS_C_1978|CS_JIS_X_1983|CS_MSWINDOWS|
4505 CS_KANJITALK6|CS_KANJITALK7)},
4506 {"cp932", "RKSJ", CS_JIS_X_1983},
4507 {"ucs-2", "UCS2", CS_JIS_X_1990},
4508 {"utf-8", "UTF8" , CS_JIS_X_1990}
4509};
4510static struct prt_ps_charset_S j_charsets[] =
4511{
4512 {"JIS_C_1978", "78", CS_JIS_C_1978},
4513 {"JIS_X_1983", NULL, CS_JIS_X_1983},
4514 {"JIS_X_1990", "Hojo", CS_JIS_X_1990},
4515 {"NEC", "Ext", CS_NEC},
4516 {"MSWINDOWS", "90ms", CS_MSWINDOWS},
4517 {"CP932", "90ms", CS_JIS_X_1983},
4518 {"KANJITALK6", "83pv", CS_KANJITALK6},
4519 {"KANJITALK7", "90pv", CS_KANJITALK7}
4520};
4521
4522#define CS_GB_2312_80 (0x01)
4523#define CS_GBT_12345_90 (0x02)
4524#define CS_GBK2K (0x04)
4525#define CS_SC_MAC (0x08)
4526#define CS_GBT_90_MAC (0x10)
4527#define CS_GBK (0x20)
4528#define CS_SC_ISO10646 (0x40)
4529
4530/* Simplified Chinese encodings and charsets */
4531static struct prt_ps_encoding_S sc_encodings[] =
4532{
4533 {"iso-2022", NULL, (CS_GB_2312_80|CS_GBT_12345_90)},
4534 {"gb18030", NULL, CS_GBK2K},
4535 {"euc-cn", "EUC", (CS_GB_2312_80|CS_GBT_12345_90|CS_SC_MAC|
4536 CS_GBT_90_MAC)},
4537 {"gbk", "EUC", CS_GBK},
4538 {"ucs-2", "UCS2", CS_SC_ISO10646},
4539 {"utf-8", "UTF8", CS_SC_ISO10646}
4540};
4541static struct prt_ps_charset_S sc_charsets[] =
4542{
4543 {"GB_2312-80", "GB", CS_GB_2312_80},
4544 {"GBT_12345-90","GBT", CS_GBT_12345_90},
4545 {"MAC", "GBpc", CS_SC_MAC},
4546 {"GBT-90_MAC", "GBTpc", CS_GBT_90_MAC},
4547 {"GBK", "GBK", CS_GBK},
4548 {"GB18030", "GBK2K", CS_GBK2K},
4549 {"ISO10646", "UniGB", CS_SC_ISO10646}
4550};
4551
4552#define CS_CNS_PLANE_1 (0x01)
4553#define CS_CNS_PLANE_2 (0x02)
4554#define CS_CNS_PLANE_1_2 (0x04)
4555#define CS_B5 (0x08)
4556#define CS_ETEN (0x10)
4557#define CS_HK_GCCS (0x20)
4558#define CS_HK_SCS (0x40)
4559#define CS_HK_SCS_ETEN (0x80)
4560#define CS_MTHKL (0x100)
4561#define CS_MTHKS (0x200)
4562#define CS_DLHKL (0x400)
4563#define CS_DLHKS (0x800)
4564#define CS_TC_ISO10646 (0x1000)
4565
4566/* Traditional Chinese encodings and charsets */
4567static struct prt_ps_encoding_S tc_encodings[] =
4568{
4569 {"iso-2022", NULL, (CS_CNS_PLANE_1|CS_CNS_PLANE_2)},
4570 {"euc-tw", "EUC", CS_CNS_PLANE_1_2},
4571 {"big5", "B5", (CS_B5|CS_ETEN|CS_HK_GCCS|CS_HK_SCS|
4572 CS_HK_SCS_ETEN|CS_MTHKL|CS_MTHKS|CS_DLHKL|
4573 CS_DLHKS)},
4574 {"cp950", "B5", CS_B5},
4575 {"ucs-2", "UCS2", CS_TC_ISO10646},
4576 {"utf-8", "UTF8", CS_TC_ISO10646},
4577 {"utf-16", "UTF16", CS_TC_ISO10646},
4578 {"utf-32", "UTF32", CS_TC_ISO10646}
4579};
4580static struct prt_ps_charset_S tc_charsets[] =
4581{
4582 {"CNS_1992_1", "CNS1", CS_CNS_PLANE_1},
4583 {"CNS_1992_2", "CNS2", CS_CNS_PLANE_2},
4584 {"CNS_1993", "CNS", CS_CNS_PLANE_1_2},
4585 {"BIG5", NULL, CS_B5},
4586 {"CP950", NULL, CS_B5},
4587 {"ETEN", "ETen", CS_ETEN},
4588 {"HK_GCCS", "HKgccs", CS_HK_GCCS},
4589 {"SCS", "HKscs", CS_HK_SCS},
4590 {"SCS_ETEN", "ETHK", CS_HK_SCS_ETEN},
4591 {"MTHKL", "HKm471", CS_MTHKL},
4592 {"MTHKS", "HKm314", CS_MTHKS},
4593 {"DLHKL", "HKdla", CS_DLHKL},
4594 {"DLHKS", "HKdlb", CS_DLHKS},
4595 {"ISO10646", "UniCNS", CS_TC_ISO10646}
4596};
4597
4598#define CS_KR_X_1992 (0x01)
4599#define CS_KR_MAC (0x02)
4600#define CS_KR_X_1992_MS (0x04)
4601#define CS_KR_ISO10646 (0x08)
4602
4603/* Korean encodings and charsets */
4604static struct prt_ps_encoding_S k_encodings[] =
4605{
4606 {"iso-2022-kr", NULL, CS_KR_X_1992},
4607 {"euc-kr", "EUC", (CS_KR_X_1992|CS_KR_MAC)},
4608 {"johab", "Johab", CS_KR_X_1992},
4609 {"cp1361", "Johab", CS_KR_X_1992},
4610 {"uhc", "UHC", CS_KR_X_1992_MS},
4611 {"cp949", "UHC", CS_KR_X_1992_MS},
4612 {"ucs-2", "UCS2", CS_KR_ISO10646},
4613 {"utf-8", "UTF8", CS_KR_ISO10646}
4614};
4615static struct prt_ps_charset_S k_charsets[] =
4616{
4617 {"KS_X_1992", "KSC", CS_KR_X_1992},
4618 {"CP1361", "KSC", CS_KR_X_1992},
4619 {"MAC", "KSCpc", CS_KR_MAC},
4620 {"MSWINDOWS", "KSCms", CS_KR_X_1992_MS},
4621 {"CP949", "KSCms", CS_KR_X_1992_MS},
4622 {"WANSUNG", "KSCms", CS_KR_X_1992_MS},
4623 {"ISO10646", "UniKS", CS_KR_ISO10646}
4624};
4625
4626/* Collections of encodings and charsets for multi-byte printing */
4627struct prt_ps_mbfont_S
4628{
4629 int num_encodings;
4630 struct prt_ps_encoding_S *encodings;
4631 int num_charsets;
4632 struct prt_ps_charset_S *charsets;
4633 char *ascii_enc;
4634 char *defcs;
4635};
4636
4637static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
4638{
4639 {
4640 NUM_ELEMENTS(j_encodings),
4641 j_encodings,
4642 NUM_ELEMENTS(j_charsets),
4643 j_charsets,
4644 "jis_roman",
4645 "JIS_X_1983"
4646 },
4647 {
4648 NUM_ELEMENTS(sc_encodings),
4649 sc_encodings,
4650 NUM_ELEMENTS(sc_charsets),
4651 sc_charsets,
4652 "gb_roman",
4653 "GB_2312-80"
4654 },
4655 {
4656 NUM_ELEMENTS(tc_encodings),
4657 tc_encodings,
4658 NUM_ELEMENTS(tc_charsets),
4659 tc_charsets,
4660 "cns_roman",
4661 "BIG5"
4662 },
4663 {
4664 NUM_ELEMENTS(k_encodings),
4665 k_encodings,
4666 NUM_ELEMENTS(k_charsets),
4667 k_charsets,
4668 "ks_roman",
4669 "KS_X_1992"
4670 }
4671};
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004672#endif /* FEAT_MBYTE */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004673
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674struct prt_ps_resource_S
4675{
4676 char_u name[64];
4677 char_u filename[MAXPATHL + 1];
4678 int type;
4679 char_u title[256];
4680 char_u version[256];
4681};
4682
4683/* Types of PS resource file currently used */
4684#define PRT_RESOURCE_TYPE_PROCSET (0)
4685#define PRT_RESOURCE_TYPE_ENCODING (1)
Bram Moolenaar8299df92004-07-10 09:47:34 +00004686#define PRT_RESOURCE_TYPE_CMAP (2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004687
4688/* The PS prolog file version number has to match - if the prolog file is
4689 * updated, increment the number in the file and here. Version checking was
4690 * added as of VIM 6.2.
Bram Moolenaar8299df92004-07-10 09:47:34 +00004691 * The CID prolog file version number behaves as per PS prolog.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 * Table of VIM and prolog versions:
4693 *
Bram Moolenaar8299df92004-07-10 09:47:34 +00004694 * VIM Prolog CIDProlog
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 * 6.2 1.3
Bram Moolenaar8299df92004-07-10 09:47:34 +00004696 * 7.0 1.4 1.0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 */
Bram Moolenaar325b7a22004-07-05 15:58:32 +00004698#define PRT_PROLOG_VERSION ((char_u *)"1.4")
Bram Moolenaar8299df92004-07-10 09:47:34 +00004699#define PRT_CID_PROLOG_VERSION ((char_u *)"1.0")
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700
4701/* String versions of PS resource types - indexed by constants above so don't
4702 * re-order!
4703 */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004704static char *prt_resource_types[] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705{
4706 "procset",
Bram Moolenaar8299df92004-07-10 09:47:34 +00004707 "encoding",
4708 "cmap"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004709};
4710
4711/* Strings to look for in a PS resource file */
4712#define PRT_RESOURCE_HEADER "%!PS-Adobe-"
4713#define PRT_RESOURCE_RESOURCE "Resource-"
4714#define PRT_RESOURCE_PROCSET "ProcSet"
4715#define PRT_RESOURCE_ENCODING "Encoding"
Bram Moolenaar8299df92004-07-10 09:47:34 +00004716#define PRT_RESOURCE_CMAP "CMap"
4717
4718
4719/* Data for table based DSC comment recognition, easy to extend if VIM needs to
4720 * read more comments. */
4721#define PRT_DSC_MISC_TYPE (-1)
4722#define PRT_DSC_TITLE_TYPE (1)
4723#define PRT_DSC_VERSION_TYPE (2)
4724#define PRT_DSC_ENDCOMMENTS_TYPE (3)
4725
4726#define PRT_DSC_TITLE "%%Title:"
4727#define PRT_DSC_VERSION "%%Version:"
4728#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
4729
4730struct prt_dsc_comment_S
4731{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004732 char *string;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004733 int len;
4734 int type;
4735};
4736
4737struct prt_dsc_line_S
4738{
4739 int type;
4740 char_u *string;
4741 int len;
4742};
4743
4744
4745#define SIZEOF_CSTR(s) (sizeof(s) - 1)
4746struct prt_dsc_comment_S prt_dsc_table[] =
4747{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004748 {PRT_DSC_TITLE, SIZEOF_CSTR(PRT_DSC_TITLE), PRT_DSC_TITLE_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004749 {PRT_DSC_VERSION, SIZEOF_CSTR(PRT_DSC_VERSION),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004750 PRT_DSC_VERSION_TYPE},
Bram Moolenaar8299df92004-07-10 09:47:34 +00004751 {PRT_DSC_ENDCOMMENTS, SIZEOF_CSTR(PRT_DSC_ENDCOMMENTS),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004752 PRT_DSC_ENDCOMMENTS_TYPE}
Bram Moolenaar8299df92004-07-10 09:47:34 +00004753};
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754
4755static void prt_write_file_raw_len __ARGS((char_u *buffer, int bytes));
4756static void prt_write_file __ARGS((char_u *buffer));
4757static void prt_write_file_len __ARGS((char_u *buffer, int bytes));
4758static void prt_write_string __ARGS((char *s));
4759static void prt_write_int __ARGS((int i));
4760static void prt_write_boolean __ARGS((int b));
4761static void prt_def_font __ARGS((char *new_name, char *encoding, int height, char *font));
4762static void prt_real_bits __ARGS((double real, int precision, int *pinteger, int *pfraction));
4763static void prt_write_real __ARGS((double val, int prec));
4764static void prt_def_var __ARGS((char *name, double value, int prec));
4765static void prt_flush_buffer __ARGS((void));
4766static void prt_resource_name __ARGS((char_u *filename));
4767static int prt_find_resource __ARGS((char *name, struct prt_ps_resource_S *resource));
4768static int prt_open_resource __ARGS((struct prt_ps_resource_S *resource));
4769static int prt_check_resource __ARGS((struct prt_ps_resource_S *resource, char_u *version));
4770static void prt_dsc_start __ARGS((void));
4771static void prt_dsc_noarg __ARGS((char *comment));
4772static void prt_dsc_textline __ARGS((char *comment, char *text));
4773static void prt_dsc_text __ARGS((char *comment, char *text));
4774static void prt_dsc_ints __ARGS((char *comment, int count, int *ints));
4775static void prt_dsc_requirements __ARGS((int duplex, int tumble, int collate, int color, int num_copies));
4776static 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 +00004777static void prt_dsc_resources __ARGS((char *comment, char *type, char *strings));
4778static void prt_dsc_font_resource __ARGS((char *resource, struct prt_ps_font_S *ps_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779static float to_device_units __ARGS((int idx, double physsize, int def_number));
4780static void prt_page_margins __ARGS((double width, double height, double *left, double *right, double *top, double *bottom));
4781static void prt_font_metrics __ARGS((int font_scale));
4782static int prt_get_cpl __ARGS((void));
4783static int prt_get_lpp __ARGS((void));
4784static int prt_add_resource __ARGS((struct prt_ps_resource_S *resource));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004785static int prt_resfile_next_line __ARGS((void));
4786static int prt_resfile_strncmp __ARGS((int offset, char *string, int len));
4787static int prt_resfile_skip_nonws __ARGS((int offset));
4788static int prt_resfile_skip_ws __ARGS((int offset));
4789static int prt_next_dsc __ARGS((struct prt_dsc_line_S *p_dsc_line));
4790#ifdef FEAT_MBYTE
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00004791static int prt_build_cid_fontname __ARGS((int font, char_u *name, int name_len));
Bram Moolenaar8299df92004-07-10 09:47:34 +00004792static void prt_def_cidfont __ARGS((char *new_name, int height, char *cidfont));
4793static int prt_match_encoding __ARGS((char *p_encoding, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_encoding_S **pp_mbenc));
4794static int prt_match_charset __ARGS((char *p_charset, struct prt_ps_mbfont_S *p_cmap, struct prt_ps_charset_S **pp_mbchar));
4795#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796
4797/*
4798 * Variables for the output PostScript file.
4799 */
4800static FILE *prt_ps_fd;
4801static int prt_file_error;
4802static char_u *prt_ps_file_name = NULL;
4803
4804/*
4805 * Various offsets and dimensions in default PostScript user space (points).
4806 * Used for text positioning calculations
4807 */
4808static float prt_page_width;
4809static float prt_page_height;
4810static float prt_left_margin;
4811static float prt_right_margin;
4812static float prt_top_margin;
4813static float prt_bottom_margin;
4814static float prt_line_height;
4815static float prt_first_line_height;
4816static float prt_char_width;
4817static float prt_number_width;
4818static float prt_bgcol_offset;
4819static float prt_pos_x_moveto = 0.0;
4820static float prt_pos_y_moveto = 0.0;
4821
4822/*
4823 * Various control variables used to decide when and how to change the
4824 * PostScript graphics state.
4825 */
4826static int prt_need_moveto;
4827static int prt_do_moveto;
4828static int prt_need_font;
4829static int prt_font;
4830static int prt_need_underline;
4831static int prt_underline;
4832static int prt_do_underline;
4833static int prt_need_fgcol;
4834static int prt_fgcol;
4835static int prt_need_bgcol;
4836static int prt_do_bgcol;
4837static int prt_bgcol;
4838static int prt_new_bgcol;
4839static int prt_attribute_change;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004840static float prt_text_run;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841static int prt_page_num;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004842static int prt_bufsiz;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843
4844/*
4845 * Variables controlling physical printing.
4846 */
4847static int prt_media;
4848static int prt_portrait;
4849static int prt_num_copies;
4850static int prt_duplex;
4851static int prt_tumble;
4852static int prt_collate;
4853
4854/*
4855 * Buffers used when generating PostScript output
4856 */
4857static char_u prt_line_buffer[257];
4858static garray_T prt_ps_buffer;
4859
4860# ifdef FEAT_MBYTE
4861static int prt_do_conv;
4862static vimconv_T prt_conv;
Bram Moolenaar8299df92004-07-10 09:47:34 +00004863
4864static int prt_out_mbyte;
4865static int prt_custom_cmap;
4866static char prt_cmap[80];
4867static int prt_use_courier;
4868static int prt_in_ascii;
4869static int prt_half_width;
4870static char *prt_ascii_encoding;
4871static char_u prt_hexchar[] = "0123456789abcdef";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004872# endif
4873
4874 static void
4875prt_write_file_raw_len(buffer, bytes)
4876 char_u *buffer;
4877 int bytes;
4878{
4879 if (!prt_file_error
4880 && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd)
4881 != (size_t)bytes)
4882 {
4883 EMSG(_("E455: Error writing to PostScript output file"));
4884 prt_file_error = TRUE;
4885 }
4886}
4887
4888 static void
4889prt_write_file(buffer)
4890 char_u *buffer;
4891{
4892 prt_write_file_len(buffer, STRLEN(buffer));
4893}
4894
4895 static void
4896prt_write_file_len(buffer, bytes)
4897 char_u *buffer;
4898 int bytes;
4899{
4900#ifdef EBCDIC
4901 ebcdic2ascii(buffer, bytes);
4902#endif
4903 prt_write_file_raw_len(buffer, bytes);
4904}
4905
4906/*
4907 * Write a string.
4908 */
4909 static void
4910prt_write_string(s)
4911 char *s;
4912{
4913 sprintf((char *)prt_line_buffer, "%s", s);
4914 prt_write_file(prt_line_buffer);
4915}
4916
4917/*
4918 * Write an int and a space.
4919 */
4920 static void
4921prt_write_int(i)
4922 int i;
4923{
4924 sprintf((char *)prt_line_buffer, "%d ", i);
4925 prt_write_file(prt_line_buffer);
4926}
4927
4928/*
4929 * Write a boolean and a space.
4930 */
4931 static void
4932prt_write_boolean(b)
4933 int b;
4934{
4935 sprintf((char *)prt_line_buffer, "%s ", (b ? "T" : "F"));
4936 prt_write_file(prt_line_buffer);
4937}
4938
4939/*
Bram Moolenaar8299df92004-07-10 09:47:34 +00004940 * Write PostScript to re-encode and define the font.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 */
4942 static void
4943prt_def_font(new_name, encoding, height, font)
4944 char *new_name;
4945 char *encoding;
4946 int height;
4947 char *font;
4948{
Bram Moolenaar8299df92004-07-10 09:47:34 +00004949 sprintf((char *)prt_line_buffer, "/_%s /VIM-%s /%s ref\n",
4950 new_name, encoding, font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951 prt_write_file(prt_line_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00004952#ifdef FEAT_MBYTE
4953 if (prt_out_mbyte)
4954 sprintf((char *)prt_line_buffer, "/%s %d %f /_%s sffs\n",
4955 new_name, height, 500./prt_ps_courier_font.wx, new_name);
4956 else
4957#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n",
4959 new_name, height, new_name);
4960 prt_write_file(prt_line_buffer);
4961}
4962
Bram Moolenaar8299df92004-07-10 09:47:34 +00004963#ifdef FEAT_MBYTE
4964/*
4965 * Write a line to define the CID font.
4966 */
4967 static void
4968prt_def_cidfont(new_name, height, cidfont)
4969 char *new_name;
4970 int height;
4971 char *cidfont;
4972{
4973 sprintf((char *)prt_line_buffer, "/_%s /%s[/%s] vim_composefont\n",
4974 new_name, prt_cmap, cidfont);
4975 prt_write_file(prt_line_buffer);
4976 sprintf((char *)prt_line_buffer, "/%s %d /_%s ffs\n", new_name, height,
4977 new_name);
4978 prt_write_file(prt_line_buffer);
4979}
4980
4981/*
4982 * Write a line to define a duplicate of a CID font
4983 */
4984 static void
4985prt_dup_cidfont(original_name, new_name)
4986 char *original_name;
4987 char *new_name;
4988{
4989 sprintf((char *)prt_line_buffer, "/%s %s d\n", new_name, original_name);
4990 prt_write_file(prt_line_buffer);
4991}
4992#endif
4993
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994/*
4995 * Convert a real value into an integer and fractional part as integers, with
4996 * the fractional part being in the range [0,10^precision). The fractional part
4997 * is also rounded based on the precision + 1'th fractional digit.
4998 */
4999 static void
5000prt_real_bits(real, precision, pinteger, pfraction)
5001 double real;
5002 int precision;
5003 int *pinteger;
5004 int *pfraction;
5005{
5006 int i;
5007 int integer;
5008 float fraction;
5009
5010 integer = (int)real;
5011 fraction = (float)(real - integer);
5012 if (real < (double)integer)
5013 fraction = -fraction;
5014 for (i = 0; i < precision; i++)
5015 fraction *= 10.0;
5016
5017 *pinteger = integer;
5018 *pfraction = (int)(fraction + 0.5);
5019}
5020
5021/*
5022 * Write a real and a space. Save bytes if real value has no fractional part!
5023 * We use prt_real_bits() as %f in sprintf uses the locale setting to decide
5024 * what decimal point character to use, but PS always requires a '.'.
5025 */
5026 static void
5027prt_write_real(val, prec)
5028 double val;
5029 int prec;
5030{
5031 int integer;
5032 int fraction;
5033
5034 prt_real_bits(val, prec, &integer, &fraction);
5035 /* Emit integer part */
5036 sprintf((char *)prt_line_buffer, "%d", integer);
5037 prt_write_file(prt_line_buffer);
5038 /* Only emit fraction if necessary */
5039 if (fraction != 0)
5040 {
5041 /* Remove any trailing zeros */
5042 while ((fraction % 10) == 0)
5043 {
5044 prec--;
5045 fraction /= 10;
5046 }
5047 /* Emit fraction left padded with zeros */
5048 sprintf((char *)prt_line_buffer, ".%0*d", prec, fraction);
5049 prt_write_file(prt_line_buffer);
5050 }
5051 sprintf((char *)prt_line_buffer, " ");
5052 prt_write_file(prt_line_buffer);
5053}
5054
5055/*
5056 * Write a line to define a numeric variable.
5057 */
5058 static void
5059prt_def_var(name, value, prec)
5060 char *name;
5061 double value;
5062 int prec;
5063{
5064 sprintf((char *)prt_line_buffer, "/%s ", name);
5065 prt_write_file(prt_line_buffer);
5066 prt_write_real(value, prec);
5067 sprintf((char *)prt_line_buffer, "d\n");
5068 prt_write_file(prt_line_buffer);
5069}
5070
5071/* Convert size from font space to user space at current font scale */
5072#define PRT_PS_FONT_TO_USER(scale, size) ((size) * ((scale)/1000.0))
5073
5074 static void
5075prt_flush_buffer()
5076{
5077 if (prt_ps_buffer.ga_len > 0)
5078 {
5079 /* Any background color must be drawn first */
5080 if (prt_do_bgcol && (prt_new_bgcol != PRCOLOR_WHITE))
5081 {
5082 int r, g, b;
5083
5084 if (prt_do_moveto)
5085 {
5086 prt_write_real(prt_pos_x_moveto, 2);
5087 prt_write_real(prt_pos_y_moveto, 2);
5088 prt_write_string("m\n");
5089 prt_do_moveto = FALSE;
5090 }
5091
5092 /* Size of rect of background color on which text is printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005093 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 prt_write_real(prt_line_height, 2);
5095
5096 /* Lastly add the color of the background */
5097 r = ((unsigned)prt_new_bgcol & 0xff0000) >> 16;
5098 g = ((unsigned)prt_new_bgcol & 0xff00) >> 8;
5099 b = prt_new_bgcol & 0xff;
5100 prt_write_real(r / 255.0, 3);
5101 prt_write_real(g / 255.0, 3);
5102 prt_write_real(b / 255.0, 3);
5103 prt_write_string("bg\n");
5104 }
5105 /* Draw underlines before the text as it makes it slightly easier to
5106 * find the starting point.
5107 */
5108 if (prt_do_underline)
5109 {
5110 if (prt_do_moveto)
5111 {
5112 prt_write_real(prt_pos_x_moveto, 2);
5113 prt_write_real(prt_pos_y_moveto, 2);
5114 prt_write_string("m\n");
5115 prt_do_moveto = FALSE;
5116 }
5117
Bram Moolenaar8299df92004-07-10 09:47:34 +00005118 /* Underline length of text run */
5119 prt_write_real(prt_text_run, 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005120 prt_write_string("ul\n");
5121 }
5122 /* Draw the text
5123 * Note: we write text out raw - EBCDIC conversion is handled in the
5124 * PostScript world via the font encoding vector. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005125#ifdef FEAT_MBYTE
5126 if (prt_out_mbyte)
5127 prt_write_string("<");
5128 else
5129#endif
5130 prt_write_string("(");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 prt_write_file_raw_len(prt_ps_buffer.ga_data, prt_ps_buffer.ga_len);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005132#ifdef FEAT_MBYTE
5133 if (prt_out_mbyte)
5134 prt_write_string(">");
5135 else
5136#endif
5137 prt_write_string(")");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 /* Add a moveto if need be and use the appropriate show procedure */
5139 if (prt_do_moveto)
5140 {
5141 prt_write_real(prt_pos_x_moveto, 2);
5142 prt_write_real(prt_pos_y_moveto, 2);
5143 /* moveto and a show */
5144 prt_write_string("ms\n");
5145 prt_do_moveto = FALSE;
5146 }
5147 else /* Simple show */
5148 prt_write_string("s\n");
5149
5150 ga_clear(&prt_ps_buffer);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005151 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 }
5153}
5154
5155static char_u *resource_filename;
5156
5157 static void
5158prt_resource_name(filename)
5159 char_u *filename;
5160{
5161 if (STRLEN(filename) >= MAXPATHL)
5162 *resource_filename = NUL;
5163 else
5164 STRCPY(resource_filename, filename);
5165}
5166
5167 static int
5168prt_find_resource(name, resource)
5169 char *name;
5170 struct prt_ps_resource_S *resource;
5171{
5172 char_u buffer[MAXPATHL + 1];
5173
5174 STRCPY(resource->name, name);
5175 /* Look for named resource file in runtimepath */
5176 STRCPY(buffer, "print");
5177 add_pathsep(buffer);
5178 STRCAT(buffer, name);
5179 STRCAT(buffer, ".ps");
5180 resource_filename = resource->filename;
5181 *resource_filename = NUL;
5182 return (do_in_runtimepath(buffer, FALSE, prt_resource_name)
5183 && resource->filename[0] != NUL);
5184}
5185
5186/* PS CR and LF characters have platform independent values */
5187#define PSLF (0x0a)
5188#define PSCR (0x0d)
5189
Bram Moolenaar8299df92004-07-10 09:47:34 +00005190/* Static buffer to read initial comments in a resource file, some can have a
5191 * couple of KB of comments! */
5192#define PRT_FILE_BUFFER_LEN (2048)
5193struct prt_resfile_buffer_S
5194{
5195 char_u buffer[PRT_FILE_BUFFER_LEN];
5196 int len;
5197 int line_start;
5198 int line_end;
5199};
5200
5201static struct prt_resfile_buffer_S prt_resfile;
5202
5203 static int
5204prt_resfile_next_line()
5205{
5206 int index;
5207
5208 /* Move to start of next line and then find end of line */
5209 index = prt_resfile.line_end + 1;
5210 while (index < prt_resfile.len)
5211 {
5212 if (prt_resfile.buffer[index] != PSLF && prt_resfile.buffer[index]
5213 != PSCR)
5214 break;
5215 index++;
5216 }
5217 prt_resfile.line_start = index;
5218
5219 while (index < prt_resfile.len)
5220 {
5221 if (prt_resfile.buffer[index] == PSLF || prt_resfile.buffer[index]
5222 == PSCR)
5223 break;
5224 index++;
5225 }
5226 prt_resfile.line_end = index;
5227
5228 return (index < prt_resfile.len);
5229}
5230
5231 static int
5232prt_resfile_strncmp(offset, string, len)
5233 int offset;
5234 char *string;
5235 int len;
5236{
5237 /* Force not equal if string is longer than remainder of line */
5238 if (len > (prt_resfile.line_end - (prt_resfile.line_start + offset)))
5239 return 1;
5240
5241 return STRNCMP(&prt_resfile.buffer[prt_resfile.line_start + offset],
5242 string, len);
5243}
5244
5245 static int
5246prt_resfile_skip_nonws(offset)
5247 int offset;
5248{
5249 int index;
5250
5251 index = prt_resfile.line_start + offset;
5252 while (index < prt_resfile.line_end)
5253 {
5254 if (isspace(prt_resfile.buffer[index]))
5255 return index - prt_resfile.line_start;
5256 index++;
5257 }
5258 return -1;
5259}
5260
5261 static int
5262prt_resfile_skip_ws(offset)
5263 int offset;
5264{
5265 int index;
5266
5267 index = prt_resfile.line_start + offset;
5268 while (index < prt_resfile.line_end)
5269 {
5270 if (!isspace(prt_resfile.buffer[index]))
5271 return index - prt_resfile.line_start;
5272 index++;
5273 }
5274 return -1;
5275}
5276
5277/* prt_next_dsc() - returns detail on next DSC comment line found. Returns true
5278 * if a DSC comment is found, else false */
5279 static int
5280prt_next_dsc(p_dsc_line)
5281 struct prt_dsc_line_S *p_dsc_line;
5282{
5283 int comment;
5284 int offset;
5285
5286 /* Move to start of next line */
5287 if (!prt_resfile_next_line())
5288 return FALSE;
5289
5290 /* DSC comments always start %% */
5291 if (prt_resfile_strncmp(0, "%%", 2) != 0)
5292 return FALSE;
5293
5294 /* Find type of DSC comment */
5295 for (comment = 0; comment < NUM_ELEMENTS(prt_dsc_table); comment++)
5296 if (prt_resfile_strncmp(0, prt_dsc_table[comment].string,
5297 prt_dsc_table[comment].len) == 0)
5298 break;
5299
5300 if (comment != NUM_ELEMENTS(prt_dsc_table))
5301 {
5302 /* Return type of comment */
5303 p_dsc_line->type = prt_dsc_table[comment].type;
5304 offset = prt_dsc_table[comment].len;
5305 }
5306 else
5307 {
5308 /* Unrecognised DSC comment, skip to ws after comment leader */
5309 p_dsc_line->type = PRT_DSC_MISC_TYPE;
5310 offset = prt_resfile_skip_nonws(0);
5311 if (offset == -1)
5312 return FALSE;
5313 }
5314
5315 /* Skip ws to comment value */
5316 offset = prt_resfile_skip_ws(offset);
5317 if (offset == -1)
5318 return FALSE;
5319
5320 p_dsc_line->string = &prt_resfile.buffer[prt_resfile.line_start + offset];
5321 p_dsc_line->len = prt_resfile.line_end - (prt_resfile.line_start + offset);
5322
5323 return TRUE;
5324}
5325
5326/* Improved hand crafted parser to get the type, title, and version number of a
5327 * PS resource file so the file details can be added to the DSC header comments.
5328 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 static int
5330prt_open_resource(resource)
5331 struct prt_ps_resource_S *resource;
5332{
Bram Moolenaar8299df92004-07-10 09:47:34 +00005333 int offset;
5334 int seen_all;
5335 int seen_title;
5336 int seen_version;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 FILE *fd_resource;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005338 struct prt_dsc_line_S dsc_line;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339
5340 fd_resource = mch_fopen((char *)resource->filename, READBIN);
5341 if (fd_resource == NULL)
5342 {
5343 EMSG2(_("E624: Can't open file \"%s\""), resource->filename);
5344 return FALSE;
5345 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005346 vim_memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347
5348 /* Parse first line to ensure valid resource file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005349 prt_resfile.len = fread((char *)prt_resfile.buffer, sizeof(char_u),
5350 PRT_FILE_BUFFER_LEN, fd_resource);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 if (ferror(fd_resource))
5352 {
5353 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
5354 resource->filename);
5355 fclose(fd_resource);
5356 return FALSE;
5357 }
5358
Bram Moolenaar8299df92004-07-10 09:47:34 +00005359 prt_resfile.line_end = -1;
5360 prt_resfile.line_start = 0;
5361 if (!prt_resfile_next_line())
5362 return FALSE;
5363
5364 offset = 0;
5365
5366 if (prt_resfile_strncmp(offset, PRT_RESOURCE_HEADER,
5367 STRLEN(PRT_RESOURCE_HEADER)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005368 {
5369 EMSG2(_("E618: file \"%s\" is not a PostScript resource file"),
5370 resource->filename);
5371 fclose(fd_resource);
5372 return FALSE;
5373 }
5374
5375 /* Skip over any version numbers and following ws */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005376 offset += STRLEN(PRT_RESOURCE_HEADER);
5377 offset = prt_resfile_skip_nonws(offset);
5378 if (offset == -1)
5379 return FALSE;
5380 offset = prt_resfile_skip_ws(offset);
5381 if (offset == -1)
5382 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005383
Bram Moolenaar8299df92004-07-10 09:47:34 +00005384 if (prt_resfile_strncmp(offset, PRT_RESOURCE_RESOURCE,
5385 STRLEN(PRT_RESOURCE_RESOURCE)) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005386 {
5387 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5388 resource->filename);
5389 fclose(fd_resource);
5390 return FALSE;
5391 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00005392 offset += STRLEN(PRT_RESOURCE_RESOURCE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393
5394 /* Decide type of resource in the file */
Bram Moolenaar8299df92004-07-10 09:47:34 +00005395 if (prt_resfile_strncmp(offset, PRT_RESOURCE_PROCSET,
5396 STRLEN(PRT_RESOURCE_PROCSET)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397 resource->type = PRT_RESOURCE_TYPE_PROCSET;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005398 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_ENCODING,
5399 STRLEN(PRT_RESOURCE_ENCODING)) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005400 resource->type = PRT_RESOURCE_TYPE_ENCODING;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005401 else if (prt_resfile_strncmp(offset, PRT_RESOURCE_CMAP,
5402 STRLEN(PRT_RESOURCE_CMAP)) == 0)
5403 resource->type = PRT_RESOURCE_TYPE_CMAP;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404 else
5405 {
5406 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5407 resource->filename);
5408 fclose(fd_resource);
5409 return FALSE;
5410 }
5411
Bram Moolenaar8299df92004-07-10 09:47:34 +00005412 /* Look for title and version of resource */
5413 resource->title[0] = '\0';
5414 resource->version[0] = '\0';
5415 seen_title = FALSE;
5416 seen_version = FALSE;
5417 seen_all = FALSE;
5418 while (!seen_all && prt_next_dsc(&dsc_line))
5419 {
5420 switch (dsc_line.type)
5421 {
5422 case PRT_DSC_TITLE_TYPE:
5423 STRNCPY(resource->title, dsc_line.string, dsc_line.len);
5424 resource->title[dsc_line.len] = '\0';
5425 seen_title = TRUE;
5426 if (seen_version)
5427 seen_all = TRUE;
5428 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429
Bram Moolenaar8299df92004-07-10 09:47:34 +00005430 case PRT_DSC_VERSION_TYPE:
5431 STRNCPY(resource->version, dsc_line.string, dsc_line.len);
5432 resource->version[dsc_line.len] = '\0';
5433 seen_version = TRUE;
5434 if (seen_title)
5435 seen_all = TRUE;
5436 break;
5437
5438 case PRT_DSC_ENDCOMMENTS_TYPE:
5439 /* Wont find title or resource after this comment, stop searching */
5440 seen_all = TRUE;
5441 break;
5442
5443 case PRT_DSC_MISC_TYPE:
5444 /* Not interested in whatever comment this line had */
5445 break;
5446 }
5447 }
5448
5449 if (!seen_title || !seen_version)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 {
5451 EMSG2(_("E619: file \"%s\" is not a supported PostScript resource file"),
5452 resource->filename);
5453 fclose(fd_resource);
5454 return FALSE;
5455 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456
5457 fclose(fd_resource);
5458
5459 return TRUE;
5460}
5461
5462 static int
5463prt_check_resource(resource, version)
5464 struct prt_ps_resource_S *resource;
5465 char_u *version;
5466{
5467 /* Version number m.n should match, the revision number does not matter */
5468 if (STRNCMP(resource->version, version, STRLEN(version)))
5469 {
5470 EMSG2(_("E621: \"%s\" resource file has wrong version"),
5471 resource->name);
5472 return FALSE;
5473 }
5474
5475 /* Other checks to be added as needed */
5476 return TRUE;
5477}
5478
5479 static void
5480prt_dsc_start()
5481{
5482 prt_write_string("%!PS-Adobe-3.0\n");
5483}
5484
5485 static void
5486prt_dsc_noarg(comment)
5487 char *comment;
5488{
5489 sprintf((char *)prt_line_buffer, "%%%%%s\n", comment);
5490 prt_write_file(prt_line_buffer);
5491}
5492
5493 static void
5494prt_dsc_textline(comment, text)
5495 char *comment;
5496 char *text;
5497{
5498 sprintf((char *)prt_line_buffer, "%%%%%s: %s\n", comment, text);
5499 prt_write_file(prt_line_buffer);
5500}
5501
5502 static void
5503prt_dsc_text(comment, text)
5504 char *comment;
5505 char *text;
5506{
5507 /* TODO - should scan 'text' for any chars needing escaping! */
5508 sprintf((char *)prt_line_buffer, "%%%%%s: (%s)\n", comment, text);
5509 prt_write_file(prt_line_buffer);
5510}
5511
5512#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
5513
5514 static void
5515prt_dsc_ints(comment, count, ints)
5516 char *comment;
5517 int count;
5518 int *ints;
5519{
5520 int i;
5521
5522 sprintf((char *)prt_line_buffer, "%%%%%s:", comment);
5523 prt_write_file(prt_line_buffer);
5524
5525 for (i = 0; i < count; i++)
5526 {
5527 sprintf((char *)prt_line_buffer, " %d", ints[i]);
5528 prt_write_file(prt_line_buffer);
5529 }
5530
5531 prt_write_string("\n");
5532}
5533
5534 static void
Bram Moolenaar8299df92004-07-10 09:47:34 +00005535prt_dsc_resources(comment, type, string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 char *comment; /* if NULL add to previous */
5537 char *type;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005538 char *string;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005540 if (comment != NULL)
5541 sprintf((char *)prt_line_buffer, "%%%%%s: %s", comment, type);
5542 else
5543 sprintf((char *)prt_line_buffer, "%%%%+ %s", type);
5544 prt_write_file(prt_line_buffer);
5545
Bram Moolenaar8299df92004-07-10 09:47:34 +00005546 sprintf((char *)prt_line_buffer, " %s\n", string);
5547 prt_write_file(prt_line_buffer);
5548}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549
Bram Moolenaar8299df92004-07-10 09:47:34 +00005550 static void
5551prt_dsc_font_resource(resource, ps_font)
5552 char *resource;
5553 struct prt_ps_font_S *ps_font;
5554{
5555 int i;
5556
5557 prt_dsc_resources(resource, "font",
5558 ps_font->ps_fontname[PRT_PS_FONT_ROMAN]);
5559 for (i = PRT_PS_FONT_BOLD ; i <= PRT_PS_FONT_BOLDOBLIQUE ; i++)
5560 if (ps_font->ps_fontname[i] != NULL)
5561 prt_dsc_resources(NULL, "font", ps_font->ps_fontname[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005562}
5563
5564 static void
5565prt_dsc_requirements(duplex, tumble, collate, color, num_copies)
5566 int duplex;
5567 int tumble;
5568 int collate;
5569 int color;
5570 int num_copies;
5571{
5572 /* Only output the comment if we need to.
5573 * Note: tumble is ignored if we are not duplexing
5574 */
5575 if (!(duplex || collate || color || (num_copies > 1)))
5576 return;
5577
5578 sprintf((char *)prt_line_buffer, "%%%%Requirements:");
5579 prt_write_file(prt_line_buffer);
5580
5581 if (duplex)
5582 {
5583 prt_write_string(" duplex");
5584 if (tumble)
5585 prt_write_string("(tumble)");
5586 }
5587 if (collate)
5588 prt_write_string(" collate");
5589 if (color)
5590 prt_write_string(" color");
5591 if (num_copies > 1)
5592 {
5593 prt_write_string(" numcopies(");
5594 /* Note: no space wanted so dont use prt_write_int() */
5595 sprintf((char *)prt_line_buffer, "%d", num_copies);
5596 prt_write_file(prt_line_buffer);
5597 prt_write_string(")");
5598 }
5599 prt_write_string("\n");
5600}
5601
5602 static void
5603prt_dsc_docmedia(paper_name, width, height, weight, colour, type)
5604 char *paper_name;
5605 double width;
5606 double height;
5607 double weight;
5608 char *colour;
5609 char *type;
5610{
5611 sprintf((char *)prt_line_buffer, "%%%%DocumentMedia: %s ", paper_name);
5612 prt_write_file(prt_line_buffer);
5613 prt_write_real(width, 2);
5614 prt_write_real(height, 2);
5615 prt_write_real(weight, 2);
5616 if (colour == NULL)
5617 prt_write_string("()");
5618 else
5619 prt_write_string(colour);
5620 prt_write_string(" ");
5621 if (type == NULL)
5622 prt_write_string("()");
5623 else
5624 prt_write_string(type);
5625 prt_write_string("\n");
5626}
5627
5628 void
5629mch_print_cleanup()
5630{
5631#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005632 if (prt_out_mbyte)
5633 {
5634 int i;
5635
5636 /* Free off all CID font names created, but first clear duplicate
5637 * pointers to the same string (when the same font is used for more than
5638 * one style).
5639 */
5640 for (i = PRT_PS_FONT_ROMAN; i <= PRT_PS_FONT_BOLDOBLIQUE; i++)
5641 {
5642 if (prt_ps_mb_font.ps_fontname[i] != NULL)
5643 vim_free(prt_ps_mb_font.ps_fontname[i]);
5644 prt_ps_mb_font.ps_fontname[i] = NULL;
5645 }
5646 }
5647
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648 if (prt_do_conv)
5649 {
5650 convert_setup(&prt_conv, NULL, NULL);
5651 prt_do_conv = FALSE;
5652 }
5653#endif
5654 if (prt_ps_fd != NULL)
5655 {
5656 fclose(prt_ps_fd);
5657 prt_ps_fd = NULL;
5658 prt_file_error = FALSE;
5659 }
5660 if (prt_ps_file_name != NULL)
5661 {
5662 vim_free(prt_ps_file_name);
5663 prt_ps_file_name = NULL;
5664 }
5665}
5666
5667 static float
5668to_device_units(idx, physsize, def_number)
5669 int idx;
5670 double physsize;
5671 int def_number;
5672{
5673 float ret;
5674 int u;
5675 int nr;
5676
5677 u = prt_get_unit(idx);
5678 if (u == PRT_UNIT_NONE)
5679 {
5680 u = PRT_UNIT_PERC;
5681 nr = def_number;
5682 }
5683 else
5684 nr = printer_opts[idx].number;
5685
5686 switch (u)
5687 {
5688 case PRT_UNIT_INCH:
5689 ret = (float)(nr * PRT_PS_DEFAULT_DPI);
5690 break;
5691 case PRT_UNIT_MM:
5692 ret = (float)(nr * PRT_PS_DEFAULT_DPI) / (float)25.4;
5693 break;
5694 case PRT_UNIT_POINT:
5695 ret = (float)nr;
5696 break;
5697 case PRT_UNIT_PERC:
5698 default:
5699 ret = (float)(physsize * nr) / 100;
5700 break;
5701 }
5702
5703 return ret;
5704}
5705
5706/*
5707 * Calculate margins for given width and height from printoptions settings.
5708 */
5709 static void
5710prt_page_margins(width, height, left, right, top, bottom)
5711 double width;
5712 double height;
5713 double *left;
5714 double *right;
5715 double *top;
5716 double *bottom;
5717{
5718 *left = to_device_units(OPT_PRINT_LEFT, width, 10);
5719 *right = width - to_device_units(OPT_PRINT_RIGHT, width, 5);
5720 *top = height - to_device_units(OPT_PRINT_TOP, height, 5);
5721 *bottom = to_device_units(OPT_PRINT_BOT, height, 5);
5722}
5723
5724 static void
5725prt_font_metrics(font_scale)
5726 int font_scale;
5727{
5728 prt_line_height = (float)font_scale;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005729 prt_char_width = (float)PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730}
5731
5732
5733 static int
5734prt_get_cpl()
5735{
5736 if (prt_use_number())
5737 {
5738 prt_number_width = PRINT_NUMBER_WIDTH * prt_char_width;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005739#ifdef FEAT_MBYTE
5740 /* If we are outputting multi-byte characters then line numbers will be
5741 * printed with half width characters
5742 */
5743 if (prt_out_mbyte)
5744 prt_number_width /= 2;
5745#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746 prt_left_margin += prt_number_width;
5747 }
5748 else
5749 prt_number_width = 0.0;
5750
5751 return (int)((prt_right_margin - prt_left_margin) / prt_char_width);
5752}
5753
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005754#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00005755 static int
5756prt_build_cid_fontname(font, name, name_len)
5757 int font;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005758 char_u *name;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005759 int name_len;
5760{
5761 char *fontname;
5762
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005763 fontname = (char *)alloc(name_len + 1);
Bram Moolenaar8299df92004-07-10 09:47:34 +00005764 if (fontname == NULL)
5765 return FALSE;
5766 STRNCPY(fontname, name, name_len);
5767 fontname[name_len] = '\0';
5768 prt_ps_mb_font.ps_fontname[font] = fontname;
5769
5770 return TRUE;
5771}
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005772#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00005773
Bram Moolenaar071d4272004-06-13 20:20:40 +00005774/*
5775 * Get number of lines of text that fit on a page (excluding the header).
5776 */
5777 static int
5778prt_get_lpp()
5779{
5780 int lpp;
5781
5782 /*
5783 * Calculate offset to lower left corner of background rect based on actual
5784 * font height (based on its bounding box) and the line height, handling the
5785 * case where the font height can exceed the line height.
5786 */
5787 prt_bgcol_offset = (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005788 prt_ps_font->bbox_min_y);
5789 if ((prt_ps_font->bbox_max_y - prt_ps_font->bbox_min_y) < 1000.0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005790 {
5791 prt_bgcol_offset -= (float)PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00005792 (1000.0 - (prt_ps_font->bbox_max_y -
5793 prt_ps_font->bbox_min_y)) / 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005794 }
5795
5796 /* Get height for topmost line based on background rect offset. */
5797 prt_first_line_height = prt_line_height + prt_bgcol_offset;
5798
5799 /* Calculate lpp */
5800 lpp = (int)((prt_top_margin - prt_bottom_margin) / prt_line_height);
5801
5802 /* Adjust top margin if there is a header */
5803 prt_top_margin -= prt_line_height * prt_header_height();
5804
5805 return lpp - prt_header_height();
5806}
5807
Bram Moolenaar8299df92004-07-10 09:47:34 +00005808#ifdef FEAT_MBYTE
5809 static int
5810prt_match_encoding(p_encoding, p_cmap, pp_mbenc)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005811 char *p_encoding;
5812 struct prt_ps_mbfont_S *p_cmap;
5813 struct prt_ps_encoding_S **pp_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005814{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005815 int mbenc;
5816 int enc_len;
5817 struct prt_ps_encoding_S *p_mbenc;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005818
5819 *pp_mbenc = NULL;
5820 /* Look for recognised encoding */
5821 enc_len = STRLEN(p_encoding);
5822 p_mbenc = p_cmap->encodings;
5823 for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++)
5824 {
5825 if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0)
5826 {
5827 *pp_mbenc = p_mbenc;
5828 return TRUE;
5829 }
5830 p_mbenc++;
5831 }
5832 return FALSE;
5833}
5834
5835 static int
5836prt_match_charset(p_charset, p_cmap, pp_mbchar)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005837 char *p_charset;
5838 struct prt_ps_mbfont_S *p_cmap;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005839 struct prt_ps_charset_S **pp_mbchar;
5840{
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005841 int mbchar;
5842 int char_len;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005843 struct prt_ps_charset_S *p_mbchar;
5844
5845 /* Look for recognised character set, using default if one is not given */
5846 if (*p_charset == NUL)
5847 p_charset = p_cmap->defcs;
5848 char_len = STRLEN(p_charset);
5849 p_mbchar = p_cmap->charsets;
5850 for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++)
5851 {
5852 if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0)
5853 {
5854 *pp_mbchar = p_mbchar;
5855 return TRUE;
5856 }
5857 p_mbchar++;
5858 }
5859 return FALSE;
5860}
5861#endif
5862
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863/*ARGSUSED*/
5864 int
5865mch_print_init(psettings, jobname, forceit)
5866 prt_settings_T *psettings;
5867 char_u *jobname;
5868 int forceit;
5869{
5870 int i;
5871 char *paper_name;
5872 int paper_strlen;
5873 int fontsize;
5874 char_u *p;
5875 double left;
5876 double right;
5877 double top;
5878 double bottom;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005879#ifdef FEAT_MBYTE
5880 int cmap;
5881 int pmcs_len;
5882 char_u *p_encoding;
5883 struct prt_ps_encoding_S *p_mbenc;
5884 struct prt_ps_encoding_S *p_mbenc_first;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005885 struct prt_ps_charset_S *p_mbchar;
Bram Moolenaar8299df92004-07-10 09:47:34 +00005886#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887
5888#if 0
5889 /*
5890 * TODO:
5891 * If "forceit" is false: pop up a dialog to select:
5892 * - printer name
5893 * - copies
5894 * - collated/uncollated
5895 * - duplex off/long side/short side
5896 * - paper size
5897 * - portrait/landscape
5898 * - font size
5899 *
5900 * If "forceit" is true: use the default printer and settings
5901 */
5902 if (forceit)
5903 s_pd.Flags |= PD_RETURNDEFAULT;
5904#endif
5905
5906 /*
Bram Moolenaar8299df92004-07-10 09:47:34 +00005907 * Set up font and encoding.
5908 */
5909#ifdef FEAT_MBYTE
5910 p_encoding = enc_skip(p_penc);
5911 if (*p_encoding == NUL)
5912 p_encoding = enc_skip(p_enc);
5913
5914 /* Look for recognised multi-byte coding, and if the charset is recognised.
5915 * This is to cope with the fact that various unicode encodings are
5916 * supported in more than one of CJK. */
5917 p_mbenc = NULL;
5918 p_mbenc_first = NULL;
5919 p_mbchar = NULL;
5920 for (cmap = 0; cmap < NUM_ELEMENTS(prt_ps_mbfonts); cmap++)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005921 if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap],
5922 &p_mbenc))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005923 {
5924 if (p_mbenc_first == NULL)
5925 p_mbenc_first = p_mbenc;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005926 if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap],
5927 &p_mbchar))
Bram Moolenaar8299df92004-07-10 09:47:34 +00005928 break;
5929 }
5930
5931 /* Use first encoding matched if no charset matched */
5932 if (p_mbchar == NULL && p_mbenc_first != NULL)
5933 p_mbenc = p_mbenc_first;
5934
5935 prt_out_mbyte = (p_mbenc != NULL);
5936 if (prt_out_mbyte)
5937 {
5938 /* Build CMap name - will be same for all multi-byte fonts used */
5939 prt_cmap[0] = '\0';
5940
5941 prt_custom_cmap = prt_out_mbyte && p_mbchar == NULL;
5942
5943 if (!prt_custom_cmap)
5944 {
5945 /* Check encoding and character set are compatible */
5946 if ((p_mbenc->needs_charset&p_mbchar->has_charset) == 0)
5947 {
5948 EMSG(_("E673: Incompatible multi-byte encoding and character set."));
5949 return FALSE;
5950 }
5951
5952 /* Add charset name if not empty */
5953 if (p_mbchar->cmap_charset != NULL)
5954 {
5955 STRCAT(prt_cmap, p_mbchar->cmap_charset);
5956 STRCAT(prt_cmap, "-");
5957 }
5958 }
5959 else
5960 {
5961 /* Add custom CMap character set name */
5962 pmcs_len = STRLEN(p_pmcs);
5963 if (pmcs_len == 0)
5964 {
5965 EMSG(_("E674: printmbcharset cannot be empty with multi-byte encoding."));
5966 return FALSE;
5967 }
5968 STRNCPY(prt_cmap, p_pmcs, STRLEN(p_pmcs));
5969 prt_cmap[pmcs_len] = '\0';
5970 STRCAT(prt_cmap, "-");
5971 }
5972
5973 /* CMap name ends with (optional) encoding name and -H for horizontal */
5974 if (p_mbenc->cmap_encoding != NULL)
5975 {
5976 STRCAT(prt_cmap, p_mbenc->cmap_encoding);
5977 STRCAT(prt_cmap, "-");
5978 }
5979 STRCAT(prt_cmap, "H");
5980
5981 if (!mbfont_opts[OPT_MBFONT_REGULAR].present)
5982 {
5983 EMSG(_("E675: No default font specfifed for multi-byte printing."));
5984 return FALSE;
5985 }
5986
5987 /* Derive CID font names with fallbacks if not defined */
5988 if (!prt_build_cid_fontname(PRT_PS_FONT_ROMAN,
5989 mbfont_opts[OPT_MBFONT_REGULAR].string,
5990 mbfont_opts[OPT_MBFONT_REGULAR].strlen))
5991 return FALSE;
5992 if (mbfont_opts[OPT_MBFONT_BOLD].present)
5993 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLD,
5994 mbfont_opts[OPT_MBFONT_BOLD].string,
5995 mbfont_opts[OPT_MBFONT_BOLD].strlen))
5996 return FALSE;
5997 if (mbfont_opts[OPT_MBFONT_OBLIQUE].present)
5998 if (!prt_build_cid_fontname(PRT_PS_FONT_OBLIQUE,
5999 mbfont_opts[OPT_MBFONT_OBLIQUE].string,
6000 mbfont_opts[OPT_MBFONT_OBLIQUE].strlen))
6001 return FALSE;
6002 if (mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].present)
6003 if (!prt_build_cid_fontname(PRT_PS_FONT_BOLDOBLIQUE,
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006004 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].string,
6005 mbfont_opts[OPT_MBFONT_BOLDOBLIQUE].strlen))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006006 return FALSE;
6007
6008 /* Check if need to use Courier for ASCII code range, and if so pick up
6009 * the encoding to use */
6010 prt_use_courier = mbfont_opts[OPT_MBFONT_USECOURIER].present &&
6011 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_USECOURIER].string[0]) == 'y');
6012 if (prt_use_courier)
6013 {
6014 /* Use national ASCII variant unless ASCII wanted */
6015 if (mbfont_opts[OPT_MBFONT_ASCII].present &&
6016 (TOLOWER_ASC(mbfont_opts[OPT_MBFONT_ASCII].string[0]) == 'y'))
6017 prt_ascii_encoding = "ascii";
6018 else
6019 prt_ascii_encoding = prt_ps_mbfonts[cmap].ascii_enc;
6020 }
6021
6022 prt_ps_font = &prt_ps_mb_font;
6023 }
6024 else
6025#endif
6026 {
6027#ifdef FEAT_MBYTE
6028 prt_use_courier = FALSE;
6029#endif
6030 prt_ps_font = &prt_ps_courier_font;
6031 }
6032
6033 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00006034 * Find the size of the paper and set the margins.
6035 */
6036 prt_portrait = (!printer_opts[OPT_PRINT_PORTRAIT].present
6037 || TOLOWER_ASC(printer_opts[OPT_PRINT_PORTRAIT].string[0]) == 'y');
6038 if (printer_opts[OPT_PRINT_PAPER].present)
6039 {
6040 paper_name = (char *)printer_opts[OPT_PRINT_PAPER].string;
6041 paper_strlen = printer_opts[OPT_PRINT_PAPER].strlen;
6042 }
6043 else
6044 {
6045 paper_name = "A4";
6046 paper_strlen = 2;
6047 }
6048 for (i = 0; i < PRT_MEDIASIZE_LEN; ++i)
6049 if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
6050 && STRNICMP(prt_mediasize[i].name, paper_name,
6051 paper_strlen) == 0)
6052 break;
6053 if (i == PRT_MEDIASIZE_LEN)
6054 i = 0;
6055 prt_media = i;
6056
6057 /*
6058 * Set PS pagesize based on media dimensions and print orientation.
6059 * Note: Media and page sizes have defined meanings in PostScript and should
6060 * be kept distinct. Media is the paper (or transparency, or ...) that is
6061 * printed on, whereas the page size is the area that the PostScript
6062 * interpreter renders into.
6063 */
6064 if (prt_portrait)
6065 {
6066 prt_page_width = prt_mediasize[i].width;
6067 prt_page_height = prt_mediasize[i].height;
6068 }
6069 else
6070 {
6071 prt_page_width = prt_mediasize[i].height;
6072 prt_page_height = prt_mediasize[i].width;
6073 }
6074
6075 /*
6076 * Set PS page margins based on the PS pagesize, not the mediasize - this
6077 * needs to be done before the cpl and lpp are calculated.
6078 */
6079 prt_page_margins(prt_page_width, prt_page_height, &left, &right, &top,
6080 &bottom);
6081 prt_left_margin = (float)left;
6082 prt_right_margin = (float)right;
6083 prt_top_margin = (float)top;
6084 prt_bottom_margin = (float)bottom;
6085
6086 /*
6087 * Set up the font size.
6088 */
6089 fontsize = PRT_PS_DEFAULT_FONTSIZE;
6090 for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p)
6091 if (p[1] == 'h' && VIM_ISDIGIT(p[2]))
6092 fontsize = atoi((char *)p + 2);
6093 prt_font_metrics(fontsize);
6094
6095 /*
6096 * Return the number of characters per line, and lines per page for the
6097 * generic print code.
6098 */
6099 psettings->chars_per_line = prt_get_cpl();
6100 psettings->lines_per_page = prt_get_lpp();
6101
6102 /* Catch margin settings that leave no space for output! */
6103 if (psettings->chars_per_line <= 0 || psettings->lines_per_page <= 0)
6104 return FAIL;
6105
6106 /*
6107 * Sort out the number of copies to be printed. PS by default will do
6108 * uncollated copies for you, so once we know how many uncollated copies are
6109 * wanted cache it away and lie to the generic code that we only want one
6110 * uncollated copy.
6111 */
6112 psettings->n_collated_copies = 1;
6113 psettings->n_uncollated_copies = 1;
6114 prt_num_copies = 1;
6115 prt_collate = (!printer_opts[OPT_PRINT_COLLATE].present
6116 || TOLOWER_ASC(printer_opts[OPT_PRINT_COLLATE].string[0]) == 'y');
6117 if (prt_collate)
6118 {
6119 /* TODO: Get number of collated copies wanted. */
6120 psettings->n_collated_copies = 1;
6121 }
6122 else
6123 {
6124 /* TODO: Get number of uncollated copies wanted and update the cached
6125 * count.
6126 */
6127 prt_num_copies = 1;
6128 }
6129
6130 psettings->jobname = jobname;
6131
6132 /*
6133 * Set up printer duplex and tumble based on Duplex option setting - default
6134 * is long sided duplex printing (i.e. no tumble).
6135 */
6136 prt_duplex = TRUE;
6137 prt_tumble = FALSE;
6138 psettings->duplex = 1;
6139 if (printer_opts[OPT_PRINT_DUPLEX].present)
6140 {
6141 if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0)
6142 {
6143 prt_duplex = FALSE;
6144 psettings->duplex = 0;
6145 }
6146 else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
6147 == 0)
6148 prt_tumble = TRUE;
6149 }
6150
6151 /* For now user abort not supported */
6152 psettings->user_abort = 0;
6153
6154 /* If the user didn't specify a file name, use a temp file. */
6155 if (psettings->outfile == NULL)
6156 {
6157 prt_ps_file_name = vim_tempname('p');
6158 if (prt_ps_file_name == NULL)
6159 {
6160 EMSG(_(e_notmp));
6161 return FAIL;
6162 }
6163 prt_ps_fd = mch_fopen((char *)prt_ps_file_name, WRITEBIN);
6164 }
6165 else
6166 {
6167 p = expand_env_save(psettings->outfile);
6168 if (p != NULL)
6169 {
6170 prt_ps_fd = mch_fopen((char *)p, WRITEBIN);
6171 vim_free(p);
6172 }
6173 }
6174 if (prt_ps_fd == NULL)
6175 {
6176 EMSG(_("E324: Can't open PostScript output file"));
6177 mch_print_cleanup();
6178 return FAIL;
6179 }
6180
Bram Moolenaar8299df92004-07-10 09:47:34 +00006181 prt_bufsiz = psettings->chars_per_line;
6182#ifdef FEAT_MBYTE
6183 if (prt_out_mbyte)
6184 prt_bufsiz *= 2;
6185#endif
6186 ga_init2(&prt_ps_buffer, (int)sizeof(char), prt_bufsiz);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187
6188 prt_page_num = 0;
6189
6190 prt_attribute_change = FALSE;
6191 prt_need_moveto = FALSE;
6192 prt_need_font = FALSE;
6193 prt_need_fgcol = FALSE;
6194 prt_need_bgcol = FALSE;
6195 prt_need_underline = FALSE;
6196
6197 prt_file_error = FALSE;
6198
6199 return OK;
6200}
6201
6202 static int
6203prt_add_resource(resource)
6204 struct prt_ps_resource_S *resource;
6205{
6206 FILE* fd_resource;
6207 char_u resource_buffer[512];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006208 size_t bytes_read;
6209
6210 fd_resource = mch_fopen((char *)resource->filename, READBIN);
6211 if (fd_resource == NULL)
6212 {
6213 EMSG2(_("E456: Can't open file \"%s\""), resource->filename);
6214 return FALSE;
6215 }
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006216 prt_dsc_resources("BeginResource", prt_resource_types[resource->type],
6217 (char *)resource->title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218
6219 prt_dsc_textline("BeginDocument", (char *)resource->filename);
6220
6221 for (;;)
6222 {
6223 bytes_read = fread((char *)resource_buffer, sizeof(char_u),
6224 sizeof(resource_buffer), fd_resource);
6225 if (ferror(fd_resource))
6226 {
6227 EMSG2(_("E457: Can't read PostScript resource file \"%s\""),
6228 resource->filename);
6229 fclose(fd_resource);
6230 return FALSE;
6231 }
6232 if (bytes_read == 0)
6233 break;
6234 prt_write_file_raw_len(resource_buffer, bytes_read);
6235 if (prt_file_error)
6236 {
6237 fclose(fd_resource);
6238 return FALSE;
6239 }
6240 }
6241 fclose(fd_resource);
6242
6243 prt_dsc_noarg("EndDocument");
6244
6245 prt_dsc_noarg("EndResource");
6246
6247 return TRUE;
6248}
6249
6250 int
6251mch_print_begin(psettings)
6252 prt_settings_T *psettings;
6253{
6254 time_t now;
6255 int bbox[4];
6256 char *p_time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 double left;
6258 double right;
6259 double top;
6260 double bottom;
6261 struct prt_ps_resource_S res_prolog;
6262 struct prt_ps_resource_S res_encoding;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006263 char buffer[256];
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 char_u *p_encoding;
6265#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006266 struct prt_ps_resource_S res_cidfont;
6267 struct prt_ps_resource_S res_cmap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006268#endif
6269
6270 /*
6271 * PS DSC Header comments - no PS code!
6272 */
6273 prt_dsc_start();
6274 prt_dsc_textline("Title", (char *)psettings->jobname);
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006275 if (!get_user_name((char_u *)buffer, 256))
Bram Moolenaar8299df92004-07-10 09:47:34 +00006276 STRCPY(buffer, "Unknown");
6277 prt_dsc_textline("For", buffer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006278 prt_dsc_textline("Creator", VIM_VERSION_LONG);
6279 /* Note: to ensure Clean8bit I don't think we can use LC_TIME */
6280 now = time(NULL);
6281 p_time = ctime(&now);
6282 /* Note: ctime() adds a \n so we have to remove it :-( */
6283 *(vim_strchr((char_u *)p_time, '\n')) = '\0';
6284 prt_dsc_textline("CreationDate", p_time);
6285 prt_dsc_textline("DocumentData", "Clean8Bit");
6286 prt_dsc_textline("Orientation", "Portrait");
6287 prt_dsc_atend("Pages");
6288 prt_dsc_textline("PageOrder", "Ascend");
6289 /* The bbox does not change with orientation - it is always in the default
6290 * user coordinate system! We have to recalculate right and bottom
6291 * coordinates based on the font metrics for the bbox to be accurate. */
6292 prt_page_margins(prt_mediasize[prt_media].width,
6293 prt_mediasize[prt_media].height,
6294 &left, &right, &top, &bottom);
6295 bbox[0] = (int)left;
6296 if (prt_portrait)
6297 {
6298 /* In portrait printing the fixed point is the top left corner so we
6299 * derive the bbox from that point. We have the expected cpl chars
6300 * across the media and lpp lines down the media.
6301 */
6302 bbox[1] = (int)(top - (psettings->lines_per_page + prt_header_height())
6303 * prt_line_height);
6304 bbox[2] = (int)(left + psettings->chars_per_line * prt_char_width
6305 + 0.5);
6306 bbox[3] = (int)(top + 0.5);
6307 }
6308 else
6309 {
6310 /* In landscape printing the fixed point is the bottom left corner so we
6311 * derive the bbox from that point. We have lpp chars across the media
6312 * and cpl lines up the media.
6313 */
6314 bbox[1] = (int)bottom;
6315 bbox[2] = (int)(left + ((psettings->lines_per_page
6316 + prt_header_height()) * prt_line_height) + 0.5);
6317 bbox[3] = (int)(bottom + psettings->chars_per_line * prt_char_width
6318 + 0.5);
6319 }
6320 prt_dsc_ints("BoundingBox", 4, bbox);
6321 /* The media width and height does not change with landscape printing! */
6322 prt_dsc_docmedia(prt_mediasize[prt_media].name,
6323 prt_mediasize[prt_media].width,
6324 prt_mediasize[prt_media].height,
6325 (double)0, NULL, NULL);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006326 /* Define fonts needed */
6327#ifdef FEAT_MBYTE
6328 if (!prt_out_mbyte || prt_use_courier)
6329#endif
6330 prt_dsc_font_resource("DocumentNeededResources", &prt_ps_courier_font);
6331#ifdef FEAT_MBYTE
6332 if (prt_out_mbyte)
6333 {
6334 prt_dsc_font_resource((prt_use_courier ? NULL
6335 : "DocumentNeededResources"), &prt_ps_mb_font);
6336 if (!prt_custom_cmap)
6337 prt_dsc_resources(NULL, "cmap", prt_cmap);
6338 }
6339#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340
Bram Moolenaar8299df92004-07-10 09:47:34 +00006341 /* Search for external resources VIM supplies */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342 if (!prt_find_resource("prolog", &res_prolog))
6343 {
6344 EMSG(_("E456: Can't find PostScript resource file \"prolog.ps\""));
6345 return FALSE;
6346 }
6347 if (!prt_open_resource(&res_prolog))
6348 return FALSE;
6349 if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION))
6350 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006351#ifdef FEAT_MBYTE
6352 if (prt_out_mbyte)
6353 {
6354 /* Look for required version of multi-byte printing procset */
6355 if (!prt_find_resource("cidfont", &res_cidfont))
6356 {
6357 EMSG(_("E456: Can't find PostScript resource file \"cidfont.ps\""));
6358 return FALSE;
6359 }
6360 if (!prt_open_resource(&res_cidfont))
6361 return FALSE;
6362 if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION))
6363 return FALSE;
6364 }
6365#endif
6366
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 /* Find an encoding to use for printing.
6368 * Check 'printencoding'. If not set or not found, then use 'encoding'. If
6369 * that cannot be found then default to "latin1".
6370 * Note: VIM specific encoding header is always skipped.
6371 */
6372#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006373 if (!prt_out_mbyte)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006374 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006376 p_encoding = enc_skip(p_penc);
6377 if (*p_encoding == NUL
6378 || !prt_find_resource((char *)p_encoding, &res_encoding))
6379 {
6380 /* 'printencoding' not set or not supported - find alternate */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006381#ifdef FEAT_MBYTE
Bram Moolenaar8299df92004-07-10 09:47:34 +00006382 int props;
6383
6384 p_encoding = enc_skip(p_enc);
6385 props = enc_canon_props(p_encoding);
6386 if (!(props & ENC_8BIT)
6387 || !prt_find_resource((char *)p_encoding, &res_encoding))
6388 /* 8-bit 'encoding' is not supported */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389#endif
Bram Moolenaar8299df92004-07-10 09:47:34 +00006390 {
6391 /* Use latin1 as default printing encoding */
6392 p_encoding = (char_u *)"latin1";
6393 if (!prt_find_resource((char *)p_encoding, &res_encoding))
6394 {
6395 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
6396 p_encoding);
6397 return FALSE;
6398 }
6399 }
6400 }
6401 if (!prt_open_resource(&res_encoding))
6402 return FALSE;
6403 /* For the moment there are no checks on encoding resource files to
6404 * perform */
6405#ifdef FEAT_MBYTE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006406 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006407 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006409 p_encoding = enc_skip(p_penc);
6410 if (*p_encoding == NUL)
6411 p_encoding = enc_skip(p_enc);
6412 if (prt_use_courier)
6413 {
6414 /* Include ASCII range encoding vector */
6415 if (!prt_find_resource(prt_ascii_encoding, &res_encoding))
6416 {
6417 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006418 prt_ascii_encoding);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006419 return FALSE;
6420 }
6421 if (!prt_open_resource(&res_encoding))
6422 return FALSE;
6423 /* For the moment there are no checks on encoding resource files to
6424 * perform */
6425 }
6426 }
6427
6428 prt_conv.vc_type = CONV_NONE;
6429 if (!(enc_canon_props(p_enc) & enc_canon_props(p_encoding) & ENC_8BIT)) {
6430 /* Set up encoding conversion if required */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006431 if (FAIL == convert_setup(&prt_conv, p_enc, p_encoding))
6432 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006433 EMSG2(_("E620: Unable to convert to print encoding \"%s\""),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006434 p_encoding);
6435 return FALSE;
6436 }
6437 prt_do_conv = TRUE;
6438 }
Bram Moolenaar8299df92004-07-10 09:47:34 +00006439 prt_do_conv = prt_conv.vc_type != CONV_NONE;
6440
6441 if (prt_out_mbyte && prt_custom_cmap)
6442 {
6443 /* Find user supplied CMap */
6444 if (!prt_find_resource(prt_cmap, &res_cmap))
6445 {
6446 EMSG2(_("E456: Can't find PostScript resource file \"%s.ps\""),
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006447 prt_cmap);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006448 return FALSE;
6449 }
6450 if (!prt_open_resource(&res_cmap))
6451 return FALSE;
6452 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006453#endif
6454
6455 /* List resources supplied */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456 STRCPY(buffer, res_prolog.title);
6457 STRCAT(buffer, " ");
6458 STRCAT(buffer, res_prolog.version);
Bram Moolenaar8299df92004-07-10 09:47:34 +00006459 prt_dsc_resources("DocumentSuppliedResources", "procset", buffer);
6460#ifdef FEAT_MBYTE
6461 if (prt_out_mbyte)
6462 {
6463 STRCPY(buffer, res_cidfont.title);
6464 STRCAT(buffer, " ");
6465 STRCAT(buffer, res_cidfont.version);
6466 prt_dsc_resources(NULL, "procset", buffer);
6467
6468 if (prt_custom_cmap)
6469 {
6470 STRCPY(buffer, res_cmap.title);
6471 STRCAT(buffer, " ");
6472 STRCAT(buffer, res_cmap.version);
6473 prt_dsc_resources(NULL, "cmap", buffer);
6474 }
6475 }
6476 if (!prt_out_mbyte || prt_use_courier)
6477#endif
6478 {
6479 STRCPY(buffer, res_encoding.title);
6480 STRCAT(buffer, " ");
6481 STRCAT(buffer, res_encoding.version);
6482 prt_dsc_resources(NULL, "encoding", buffer);
6483 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 prt_dsc_requirements(prt_duplex, prt_tumble, prt_collate,
6485#ifdef FEAT_SYN_HL
6486 psettings->do_syntax
6487#else
6488 0
6489#endif
6490 , prt_num_copies);
6491 prt_dsc_noarg("EndComments");
6492
6493 /*
6494 * PS Document page defaults
6495 */
6496 prt_dsc_noarg("BeginDefaults");
6497
6498 /* List font resources most likely common to all pages */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006499#ifdef FEAT_MBYTE
6500 if (!prt_out_mbyte || prt_use_courier)
6501#endif
6502 prt_dsc_font_resource("PageResources", &prt_ps_courier_font);
6503#ifdef FEAT_MBYTE
6504 if (prt_out_mbyte)
6505 {
6506 prt_dsc_font_resource((prt_use_courier ? NULL : "PageResources"),
6507 &prt_ps_mb_font);
6508 if (!prt_custom_cmap)
6509 prt_dsc_resources(NULL, "cmap", prt_cmap);
6510 }
6511#endif
6512
Bram Moolenaar071d4272004-06-13 20:20:40 +00006513 /* Paper will be used for all pages */
6514 prt_dsc_textline("PageMedia", prt_mediasize[prt_media].name);
6515
6516 prt_dsc_noarg("EndDefaults");
6517
6518 /*
6519 * PS Document prolog inclusion - all required procsets.
6520 */
6521 prt_dsc_noarg("BeginProlog");
6522
Bram Moolenaar8299df92004-07-10 09:47:34 +00006523 /* Add required procsets - NOTE: order is important! */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 if (!prt_add_resource(&res_prolog))
6525 return FALSE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006526#ifdef FEAT_MBYTE
6527 if (prt_out_mbyte)
6528 {
6529 /* Add CID font procset, and any user supplied CMap */
6530 if (!prt_add_resource(&res_cidfont))
6531 return FALSE;
6532 if (prt_custom_cmap && !prt_add_resource(&res_cmap))
6533 return FALSE;
6534 }
6535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006536
Bram Moolenaar8299df92004-07-10 09:47:34 +00006537#ifdef FEAT_MBYTE
6538 if (!prt_out_mbyte || prt_use_courier)
6539#endif
6540 /* There will be only one Roman font encoding to be included in the PS
6541 * file. */
6542 if (!prt_add_resource(&res_encoding))
6543 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544
6545 prt_dsc_noarg("EndProlog");
6546
6547 /*
6548 * PS Document setup - must appear after the prolog
6549 */
6550 prt_dsc_noarg("BeginSetup");
6551
6552 /* Device setup - page size and number of uncollated copies */
6553 prt_write_int((int)prt_mediasize[prt_media].width);
6554 prt_write_int((int)prt_mediasize[prt_media].height);
6555 prt_write_int(0);
6556 prt_write_string("sps\n");
6557 prt_write_int(prt_num_copies);
6558 prt_write_string("nc\n");
6559 prt_write_boolean(prt_duplex);
6560 prt_write_boolean(prt_tumble);
6561 prt_write_string("dt\n");
6562 prt_write_boolean(prt_collate);
6563 prt_write_string("c\n");
6564
6565 /* Font resource inclusion and definition */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006566#ifdef FEAT_MBYTE
6567 if (!prt_out_mbyte || prt_use_courier)
6568 {
6569 /* When using Courier for ASCII range when printing multi-byte, need to
6570 * pick up ASCII encoding to use with it. */
6571 if (prt_use_courier)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006572 p_encoding = (char_u *)prt_ascii_encoding;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006573#endif
6574 prt_dsc_resources("IncludeResource", "font",
6575 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6576 prt_def_font("F0", (char *)p_encoding, (int)prt_line_height,
6577 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6578 prt_dsc_resources("IncludeResource", "font",
6579 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6580 prt_def_font("F1", (char *)p_encoding, (int)prt_line_height,
6581 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLD]);
6582 prt_dsc_resources("IncludeResource", "font",
6583 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6584 prt_def_font("F2", (char *)p_encoding, (int)prt_line_height,
6585 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6586 prt_dsc_resources("IncludeResource", "font",
6587 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6588 prt_def_font("F3", (char *)p_encoding, (int)prt_line_height,
6589 prt_ps_courier_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6590#ifdef FEAT_MBYTE
6591 }
6592 if (prt_out_mbyte)
6593 {
6594 /* Define the CID fonts to be used in the job. Typically CJKV fonts do
6595 * not have an italic form being a western style, so where no font is
6596 * defined for these faces VIM falls back to an existing face.
6597 * Note: if using Courier for the ASCII range then the printout will
6598 * have bold/italic/bolditalic regardless of the setting of printmbfont.
6599 */
6600 prt_dsc_resources("IncludeResource", "font",
6601 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6602 if (!prt_custom_cmap)
6603 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6604 prt_def_cidfont("CF0", (int)prt_line_height,
6605 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_ROMAN]);
6606
6607 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD] != NULL)
6608 {
6609 prt_dsc_resources("IncludeResource", "font",
6610 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6611 if (!prt_custom_cmap)
6612 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6613 prt_def_cidfont("CF1", (int)prt_line_height,
6614 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLD]);
6615 }
6616 else
6617 /* Use ROMAN for BOLD */
6618 prt_dup_cidfont("CF0", "CF1");
6619
6620 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE] != NULL)
6621 {
6622 prt_dsc_resources("IncludeResource", "font",
6623 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6624 if (!prt_custom_cmap)
6625 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6626 prt_def_cidfont("CF2", (int)prt_line_height,
6627 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_OBLIQUE]);
6628 }
6629 else
6630 /* Use ROMAN for OBLIQUE */
6631 prt_dup_cidfont("CF0", "CF2");
6632
6633 if (prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE] != NULL)
6634 {
6635 prt_dsc_resources("IncludeResource", "font",
6636 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6637 if (!prt_custom_cmap)
6638 prt_dsc_resources("IncludeResource", "cmap", prt_cmap);
6639 prt_def_cidfont("CF3", (int)prt_line_height,
6640 prt_ps_mb_font.ps_fontname[PRT_PS_FONT_BOLDOBLIQUE]);
6641 }
6642 else
6643 /* Use BOLD for BOLDOBLIQUE */
6644 prt_dup_cidfont("CF1", "CF3");
6645 }
6646#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006647
6648 /* Misc constant vars used for underlining and background rects */
6649 prt_def_var("UO", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006650 prt_ps_font->uline_offset), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651 prt_def_var("UW", PRT_PS_FONT_TO_USER(prt_line_height,
Bram Moolenaar8299df92004-07-10 09:47:34 +00006652 prt_ps_font->uline_width), 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 prt_def_var("BO", prt_bgcol_offset, 2);
6654
6655 prt_dsc_noarg("EndSetup");
6656
6657 /* Fail if any problems writing out to the PS file */
6658 return !prt_file_error;
6659}
6660
6661 void
6662mch_print_end(psettings)
6663 prt_settings_T *psettings;
6664{
6665 prt_dsc_noarg("Trailer");
6666
6667 /*
6668 * Output any info we don't know in toto until we finish
6669 */
6670 prt_dsc_ints("Pages", 1, &prt_page_num);
6671
6672 prt_dsc_noarg("EOF");
6673
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006674 /* Write CTRL-D to close serial communication link if used.
6675 * NOTHING MUST BE WRITTEN AFTER THIS! */
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006676 prt_write_file((char_u *)IF_EB("\004", "\067"));
Bram Moolenaar325b7a22004-07-05 15:58:32 +00006677
Bram Moolenaar071d4272004-06-13 20:20:40 +00006678 if (!prt_file_error && psettings->outfile == NULL
6679 && !got_int && !psettings->user_abort)
6680 {
6681 /* Close the file first. */
6682 if (prt_ps_fd != NULL)
6683 {
6684 fclose(prt_ps_fd);
6685 prt_ps_fd = NULL;
6686 }
6687 prt_message((char_u *)_("Sending to printer..."));
6688
6689 /* Not printing to a file: use 'printexpr' to print the file. */
6690 if (eval_printexpr(prt_ps_file_name, psettings->arguments) == FAIL)
6691 EMSG(_("E365: Failed to print PostScript file"));
6692 else
6693 prt_message((char_u *)_("Print job sent."));
6694 }
6695
6696 mch_print_cleanup();
6697}
6698
6699 int
6700mch_print_end_page()
6701{
6702 prt_flush_buffer();
6703
6704 prt_write_string("re sp\n");
6705
6706 prt_dsc_noarg("PageTrailer");
6707
6708 return !prt_file_error;
6709}
6710
6711/*ARGSUSED*/
6712 int
6713mch_print_begin_page(str)
6714 char_u *str;
6715{
6716 int page_num[2];
6717
6718 prt_page_num++;
6719
6720 page_num[0] = page_num[1] = prt_page_num;
6721 prt_dsc_ints("Page", 2, page_num);
6722
6723 prt_dsc_noarg("BeginPageSetup");
6724
Bram Moolenaar8299df92004-07-10 09:47:34 +00006725 prt_write_string("sv\n0 g\n");
6726#ifdef FEAT_MBYTE
6727 prt_in_ascii = !prt_out_mbyte;
6728 if (prt_out_mbyte)
6729 prt_write_string("CF0 sf\n");
6730 else
6731#endif
6732 prt_write_string("F0 sf\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006733 prt_fgcol = PRCOLOR_BLACK;
6734 prt_bgcol = PRCOLOR_WHITE;
6735 prt_font = PRT_PS_FONT_ROMAN;
6736
6737 /* Set up page transformation for landscape printing. */
6738 if (!prt_portrait)
6739 {
6740 prt_write_int(-((int)prt_mediasize[prt_media].width));
6741 prt_write_string("sl\n");
6742 }
6743
6744 prt_dsc_noarg("EndPageSetup");
6745
6746 /* We have reset the font attributes, force setting them again. */
6747 curr_bg = (long_u)0xffffffff;
6748 curr_fg = (long_u)0xffffffff;
6749 curr_bold = MAYBE;
6750
6751 return !prt_file_error;
6752}
6753
6754 int
6755mch_print_blank_page()
6756{
6757 return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
6758}
6759
6760static float prt_pos_x = 0;
6761static float prt_pos_y = 0;
6762
6763 void
6764mch_print_start_line(margin, page_line)
6765 int margin;
6766 int page_line;
6767{
6768 prt_pos_x = prt_left_margin;
6769 if (margin)
6770 prt_pos_x -= prt_number_width;
6771
6772 prt_pos_y = prt_top_margin - prt_first_line_height -
6773 page_line * prt_line_height;
6774
6775 prt_attribute_change = TRUE;
6776 prt_need_moveto = TRUE;
Bram Moolenaar8299df92004-07-10 09:47:34 +00006777#ifdef FEAT_MBYTE
6778 prt_half_width = FALSE;
6779#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006780}
6781
6782/*ARGSUSED*/
6783 int
6784mch_print_text_out(p, len)
6785 char_u *p;
6786 int len;
6787{
6788 int need_break;
6789 char_u ch;
6790 char_u ch_buff[8];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006791 float char_width;
6792 float next_pos;
6793#ifdef FEAT_MBYTE
6794 int in_ascii;
6795 int half_width;
6796#endif
6797
6798 char_width = prt_char_width;
6799
6800#ifdef FEAT_MBYTE
6801 /* Ideally VIM would create a rearranged CID font to combine a Roman and
6802 * CJKV font to do what VIM is doing here - use a Roman font for characters
6803 * in the ASCII range, and the origingal CID font for everything else.
6804 * The problem is that GhostScript still (as of 8.13) does not support
6805 * rearranged fonts even though they have been documented by Adobe for 7
6806 * years! If they ever do, a lot of this code will disappear.
6807 */
6808 if (prt_use_courier)
6809 {
6810 in_ascii = (len == 1 && *p < 0x80);
6811 if (prt_in_ascii)
6812 {
6813 if (!in_ascii)
6814 {
6815 /* No longer in ASCII range - need to switch font */
6816 prt_in_ascii = FALSE;
6817 prt_need_font = TRUE;
6818 prt_attribute_change = TRUE;
6819 }
6820 }
6821 else if (in_ascii)
6822 {
6823 /* Now in ASCII range - need to switch font */
6824 prt_in_ascii = TRUE;
6825 prt_need_font = TRUE;
6826 prt_attribute_change = TRUE;
6827 }
6828 }
6829 if (prt_out_mbyte)
6830 {
6831 half_width = ((*mb_ptr2cells)(p) == 1);
6832 if (half_width)
6833 char_width /= 2;
6834 if (prt_half_width)
6835 {
6836 if (!half_width)
6837 {
6838 prt_half_width = FALSE;
6839 prt_pos_x += prt_char_width/4;
6840 prt_need_moveto = TRUE;
6841 prt_attribute_change = TRUE;
6842 }
6843 }
6844 else if (half_width)
6845 {
6846 prt_half_width = TRUE;
6847 prt_pos_x += prt_char_width/4;
6848 prt_need_moveto = TRUE;
6849 prt_attribute_change = TRUE;
6850 }
6851 }
6852#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853
6854 /* Output any required changes to the graphics state, after flushing any
6855 * text buffered so far.
6856 */
6857 if (prt_attribute_change)
6858 {
6859 prt_flush_buffer();
6860 /* Reset count of number of chars that will be printed */
Bram Moolenaar8299df92004-07-10 09:47:34 +00006861 prt_text_run = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862
6863 if (prt_need_moveto)
6864 {
6865 prt_pos_x_moveto = prt_pos_x;
6866 prt_pos_y_moveto = prt_pos_y;
6867 prt_do_moveto = TRUE;
6868
6869 prt_need_moveto = FALSE;
6870 }
6871 if (prt_need_font)
6872 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00006873#ifdef FEAT_MBYTE
6874 if (!prt_in_ascii)
6875 prt_write_string("CF");
6876 else
6877#endif
6878 prt_write_string("F");
6879 prt_write_int(prt_font);
6880 prt_write_string("sf\n");
6881 prt_need_font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006882 }
6883 if (prt_need_fgcol)
6884 {
6885 int r, g, b;
6886 r = ((unsigned)prt_fgcol & 0xff0000) >> 16;
6887 g = ((unsigned)prt_fgcol & 0xff00) >> 8;
6888 b = prt_fgcol & 0xff;
6889
6890 prt_write_real(r / 255.0, 3);
6891 if (r == g && g == b)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006892 prt_write_string("g\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00006893 else
6894 {
6895 prt_write_real(g / 255.0, 3);
6896 prt_write_real(b / 255.0, 3);
6897 prt_write_string("r\n");
6898 }
6899 prt_need_fgcol = FALSE;
6900 }
6901
6902 if (prt_bgcol != PRCOLOR_WHITE)
6903 {
6904 prt_new_bgcol = prt_bgcol;
6905 if (prt_need_bgcol)
6906 prt_do_bgcol = TRUE;
6907 }
6908 else
6909 prt_do_bgcol = FALSE;
6910 prt_need_bgcol = FALSE;
6911
6912 if (prt_need_underline)
6913 prt_do_underline = prt_underline;
6914 prt_need_underline = FALSE;
6915
6916 prt_attribute_change = FALSE;
6917 }
6918
6919#ifdef FEAT_MBYTE
6920 if (prt_do_conv)
6921 {
6922 /* Convert from multi-byte to 8-bit encoding */
6923 p = string_convert(&prt_conv, p, &len);
6924 if (p == NULL)
6925 p = (char_u *)"";
6926 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006927
Bram Moolenaar8299df92004-07-10 09:47:34 +00006928 if (prt_out_mbyte)
6929 {
6930 /* Multi-byte character strings are represented more efficiently as hex
6931 * strings when outputting clean 8 bit PS.
6932 */
6933 do
6934 {
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006935 ch = prt_hexchar[(unsigned)(*p) >> 4];
Bram Moolenaar8299df92004-07-10 09:47:34 +00006936 ga_append(&prt_ps_buffer, ch);
6937 ch = prt_hexchar[(*p) & 0xf];
6938 ga_append(&prt_ps_buffer, ch);
6939 p++;
6940 }
6941 while (--len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006942 }
6943 else
Bram Moolenaar8299df92004-07-10 09:47:34 +00006944#endif
6945 {
6946 /* Add next character to buffer of characters to output.
6947 * Note: One printed character may require several PS characters to
6948 * represent it, but we only count them as one printed character.
6949 */
6950 ch = *p;
6951 if (ch < 32 || ch == '(' || ch == ')' || ch == '\\')
6952 {
6953 /* Convert non-printing characters to either their escape or octal
6954 * sequence, ensures PS sent over a serial line does not interfere
6955 * with the comms protocol. Note: For EBCDIC we need to write out
6956 * the escape sequences as ASCII codes!
6957 * Note 2: Char codes < 32 are identical in EBCDIC and ASCII AFAIK!
6958 */
6959 ga_append(&prt_ps_buffer, IF_EB('\\', 0134));
6960 switch (ch)
6961 {
6962 case BS: ga_append(&prt_ps_buffer, IF_EB('b', 0142)); break;
6963 case TAB: ga_append(&prt_ps_buffer, IF_EB('t', 0164)); break;
6964 case NL: ga_append(&prt_ps_buffer, IF_EB('n', 0156)); break;
6965 case FF: ga_append(&prt_ps_buffer, IF_EB('f', 0146)); break;
6966 case CAR: ga_append(&prt_ps_buffer, IF_EB('r', 0162)); break;
6967 case '(': ga_append(&prt_ps_buffer, IF_EB('(', 0050)); break;
6968 case ')': ga_append(&prt_ps_buffer, IF_EB(')', 0051)); break;
6969 case '\\': ga_append(&prt_ps_buffer, IF_EB('\\', 0134)); break;
6970
6971 default:
6972 sprintf((char *)ch_buff, "%03o", (unsigned int)ch);
6973#ifdef EBCDIC
6974 ebcdic2ascii(ch_buff, 3);
6975#endif
6976 ga_append(&prt_ps_buffer, ch_buff[0]);
6977 ga_append(&prt_ps_buffer, ch_buff[1]);
6978 ga_append(&prt_ps_buffer, ch_buff[2]);
6979 break;
6980 }
6981 }
6982 else
6983 ga_append(&prt_ps_buffer, ch);
6984 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006985
6986#ifdef FEAT_MBYTE
6987 /* Need to free any translated characters */
6988 if (prt_do_conv && (*p != NUL))
6989 vim_free(p);
6990#endif
6991
Bram Moolenaar8299df92004-07-10 09:47:34 +00006992 prt_text_run += char_width;
6993 prt_pos_x += char_width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006994
Bram Moolenaar8299df92004-07-10 09:47:34 +00006995 /* The downside of fp - use relative error on right margin check */
6996 next_pos = prt_pos_x + prt_char_width;
6997 need_break = (next_pos > prt_right_margin) &&
6998 ((next_pos - prt_right_margin) > (prt_right_margin*1e-5));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006999
7000 if (need_break)
7001 prt_flush_buffer();
7002
7003 return need_break;
7004}
7005
7006 void
7007mch_print_set_font(iBold, iItalic, iUnderline)
7008 int iBold;
7009 int iItalic;
7010 int iUnderline;
7011{
7012 int font = 0;
7013
7014 if (iBold)
7015 font |= 0x01;
7016 if (iItalic)
7017 font |= 0x02;
7018
7019 if (font != prt_font)
7020 {
7021 prt_font = font;
7022 prt_attribute_change = TRUE;
7023 prt_need_font = TRUE;
7024 }
7025 if (prt_underline != iUnderline)
7026 {
7027 prt_underline = iUnderline;
7028 prt_attribute_change = TRUE;
7029 prt_need_underline = TRUE;
7030 }
7031}
7032
7033 void
7034mch_print_set_bg(bgcol)
7035 long_u bgcol;
7036{
7037 prt_bgcol = bgcol;
7038 prt_attribute_change = TRUE;
7039 prt_need_bgcol = TRUE;
7040}
7041
7042 void
7043mch_print_set_fg(fgcol)
7044 long_u fgcol;
7045{
7046 if (fgcol != (long_u)prt_fgcol)
7047 {
7048 prt_fgcol = fgcol;
7049 prt_attribute_change = TRUE;
7050 prt_need_fgcol = TRUE;
7051 }
7052}
7053
7054# endif /*FEAT_POSTSCRIPT*/
7055#endif /*FEAT_PRINTER*/
7056
7057#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7058 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
7059static char *get_locale_val __ARGS((int what));
7060
7061 static char *
7062get_locale_val(what)
7063 int what;
7064{
7065 char *loc;
7066
7067 /* Obtain the locale value from the libraries. For DJGPP this is
7068 * redefined and it doesn't use the arguments. */
7069 loc = setlocale(what, NULL);
7070
7071# if defined(__BORLANDC__)
7072 if (loc != NULL)
7073 {
7074 char_u *p;
7075
7076 /* Borland returns something like "LC_CTYPE=<name>\n"
7077 * Let's try to fix that bug here... */
7078 p = vim_strchr(loc, '=');
7079 if (p != NULL)
7080 {
7081 loc = ++p;
7082 while (*p != NUL) /* remove trailing newline */
7083 {
7084 if (*p < ' ')
7085 {
7086 *p = NUL;
7087 break;
7088 }
7089 ++p;
7090 }
7091 }
7092 }
7093# endif
7094
7095 return loc;
7096}
7097#endif
7098
7099
7100#ifdef WIN32
7101/*
7102 * On MS-Windows locale names are strings like "German_Germany.1252", but
7103 * gettext expects "de". Try to translate one into another here for a few
7104 * supported languages.
7105 */
7106 static char_u *
7107gettext_lang(char_u *name)
7108{
7109 int i;
7110 static char *(mtable[]) = {
7111 "afrikaans", "af",
7112 "czech", "cs",
7113 "dutch", "nl",
7114 "german", "de",
7115 "english_united kingdom", "en_GB",
7116 "spanish", "es",
7117 "french", "fr",
7118 "italian", "it",
7119 "japanese", "ja",
7120 "korean", "ko",
7121 "norwegian", "no",
7122 "polish", "pl",
7123 "russian", "ru",
7124 "slovak", "sk",
7125 "swedish", "sv",
7126 "ukrainian", "uk",
7127 "chinese_china", "zh_CN",
7128 "chinese_taiwan", "zh_TW",
7129 NULL};
7130
7131 for (i = 0; mtable[i] != NULL; i += 2)
7132 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
7133 return mtable[i + 1];
7134 return name;
7135}
7136#endif
7137
7138#if defined(FEAT_MULTI_LANG) || defined(PROTO)
7139/*
7140 * Obtain the current messages language. Used to set the default for
7141 * 'helplang'. May return NULL or an empty string.
7142 */
7143 char_u *
7144get_mess_lang()
7145{
7146 char_u *p;
7147
7148# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE))
7149# if defined(LC_MESSAGES)
7150 p = (char_u *)get_locale_val(LC_MESSAGES);
7151# else
7152 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
7153 * may be set to the LCID number. */
7154 p = (char_u *)get_locale_val(LC_ALL);
7155# endif
7156# else
7157 p = mch_getenv((char_u *)"LC_ALL");
7158 if (p == NULL || *p == NUL)
7159 {
7160 p = mch_getenv((char_u *)"LC_MESSAGES");
7161 if (p == NULL || *p == NUL)
7162 p = mch_getenv((char_u *)"LANG");
7163 }
7164# endif
7165# ifdef WIN32
7166 p = gettext_lang(p);
7167# endif
7168 return p;
7169}
7170#endif
7171
Bram Moolenaardef9e822004-12-31 20:58:58 +00007172/* Complicated #if; matches with where get_mess_env() is used below. */
7173#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7174 && defined(LC_MESSAGES))) \
7175 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7176 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
7177 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007178static char_u *get_mess_env __ARGS((void));
7179
7180/*
7181 * Get the language used for messages from the environment.
7182 */
7183 static char_u *
7184get_mess_env()
7185{
7186 char_u *p;
7187
7188 p = mch_getenv((char_u *)"LC_ALL");
7189 if (p == NULL || *p == NUL)
7190 {
7191 p = mch_getenv((char_u *)"LC_MESSAGES");
7192 if (p == NULL || *p == NUL)
7193 {
7194 p = mch_getenv((char_u *)"LANG");
7195 if (p != NULL && VIM_ISDIGIT(*p))
7196 p = NULL; /* ignore something like "1043" */
7197# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7198 if (p == NULL || *p == NUL)
7199 p = (char_u *)get_locale_val(LC_CTYPE);
7200# endif
7201 }
7202 }
7203 return p;
7204}
7205#endif
7206
7207#if defined(FEAT_EVAL) || defined(PROTO)
7208
7209/*
7210 * Set the "v:lang" variable according to the current locale setting.
7211 * Also do "v:lc_time"and "v:ctype".
7212 */
7213 void
7214set_lang_var()
7215{
7216 char_u *loc;
7217
7218# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7219 loc = (char_u *)get_locale_val(LC_CTYPE);
7220# else
7221 /* setlocale() not supported: use the default value */
7222 loc = (char_u *)"C";
7223# endif
7224 set_vim_var_string(VV_CTYPE, loc, -1);
7225
7226 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
7227 * back to LC_CTYPE if it's empty. */
7228# if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) && defined(LC_MESSAGES)
7229 loc = (char_u *)get_locale_val(LC_MESSAGES);
7230# else
7231 loc = get_mess_env();
7232# endif
7233 set_vim_var_string(VV_LANG, loc, -1);
7234
7235# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
7236 loc = (char_u *)get_locale_val(LC_TIME);
7237# endif
7238 set_vim_var_string(VV_LC_TIME, loc, -1);
7239}
7240#endif
7241
7242#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
7243 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
7244/*
7245 * ":language": Set the language (locale).
7246 */
7247 void
7248ex_language(eap)
7249 exarg_T *eap;
7250{
7251 char *loc;
7252 char_u *p;
7253 char_u *name;
7254 int what = LC_ALL;
7255 char *whatstr = "";
7256#ifdef LC_MESSAGES
7257# define VIM_LC_MESSAGES LC_MESSAGES
7258#else
7259# define VIM_LC_MESSAGES 6789
7260#endif
7261
7262 name = eap->arg;
7263
7264 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
7265 * Allow abbreviation, but require at least 3 characters to avoid
7266 * confusion with a two letter language name "me" or "ct". */
7267 p = skiptowhite(eap->arg);
7268 if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3)
7269 {
7270 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
7271 {
7272 what = VIM_LC_MESSAGES;
7273 name = skipwhite(p);
7274 whatstr = "messages ";
7275 }
7276 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
7277 {
7278 what = LC_CTYPE;
7279 name = skipwhite(p);
7280 whatstr = "ctype ";
7281 }
7282 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
7283 {
7284 what = LC_TIME;
7285 name = skipwhite(p);
7286 whatstr = "time ";
7287 }
7288 }
7289
7290 if (*name == NUL)
7291 {
7292#ifndef LC_MESSAGES
7293 if (what == VIM_LC_MESSAGES)
7294 p = get_mess_env();
7295 else
7296#endif
7297 p = (char_u *)setlocale(what, NULL);
7298 if (p == NULL || *p == NUL)
7299 p = (char_u *)"Unknown";
7300 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
7301 }
7302 else
7303 {
7304#ifndef LC_MESSAGES
7305 if (what == VIM_LC_MESSAGES)
7306 loc = "";
7307 else
7308#endif
7309 loc = setlocale(what, (char *)name);
7310 if (loc == NULL)
7311 EMSG2(_("E197: Cannot set language to \"%s\""), name);
7312 else
7313 {
7314#ifdef HAVE_NL_MSG_CAT_CNTR
7315 /* Need to do this for GNU gettext, otherwise cached translations
7316 * will be used again. */
7317 extern int _nl_msg_cat_cntr;
7318
7319 ++_nl_msg_cat_cntr;
7320#endif
7321 /* Reset $LC_ALL, otherwise it would overrule everyting. */
7322 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
7323
7324 if (what != LC_TIME)
7325 {
7326 /* Tell gettext() what to translate to. It apparently doesn't
7327 * use the currently effective locale. Also do this when
7328 * FEAT_GETTEXT isn't defined, so that shell commands use this
7329 * value. */
7330 if (what == LC_ALL)
7331 vim_setenv((char_u *)"LANG", name);
7332 if (what != LC_CTYPE)
7333 {
7334 char_u *mname;
7335#ifdef WIN32
7336 mname = gettext_lang(name);
7337#else
7338 mname = name;
7339#endif
7340 vim_setenv((char_u *)"LC_MESSAGES", mname);
7341#ifdef FEAT_MULTI_LANG
7342 set_helplang_default(mname);
7343#endif
7344 }
7345
7346 /* Set $LC_CTYPE, because it overrules $LANG, and
7347 * gtk_set_locale() calls setlocale() again. gnome_init()
7348 * sets $LC_CTYPE to "en_US" (that's a bug!). */
7349 if (what != VIM_LC_MESSAGES)
7350 vim_setenv((char_u *)"LC_CTYPE", name);
7351# ifdef FEAT_GUI_GTK
7352 /* Let GTK know what locale we're using. Not sure this is
7353 * really needed... */
7354 if (gui.in_use)
7355 (void)gtk_set_locale();
7356# endif
7357 }
7358
7359# ifdef FEAT_EVAL
7360 /* Set v:lang, v:lc_time and v:ctype to the final result. */
7361 set_lang_var();
7362# endif
7363 }
7364 }
7365}
7366
7367# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
7368/*
7369 * Function given to ExpandGeneric() to obtain the possible arguments of the
7370 * ":language" command.
7371 */
7372/*ARGSUSED*/
7373 char_u *
7374get_lang_arg(xp, idx)
7375 expand_T *xp;
7376 int idx;
7377{
7378 if (idx == 0)
7379 return (char_u *)"messages";
7380 if (idx == 1)
7381 return (char_u *)"ctype";
7382 if (idx == 2)
7383 return (char_u *)"time";
7384 return NULL;
7385}
7386# endif
7387
7388#endif